leangen / graphql-spqr

Build a GraphQL service in seconds
Apache License 2.0
1.1k stars 181 forks source link

@GraphQLNonNull by default for lists (many-to-one relations) #345

Open alexandru-constantinescu opened 4 years ago

alexandru-constantinescu commented 4 years ago

Hi,

We are just starting to use SPQR, which really rocks. It's very time saving and DRY!

We have a bunch of Hibernate entities. They have one-to-many relations. For now we need to do:

class MyEntity { private List<@GraphQLNonNull OtherEntity> myOtherEntities; }

Is there a way to configure some kind of default NonNull for all one-to-many relations? So that we don't need to annotate each list/set this way?

Thanks a lot!

kaqqao commented 4 years ago

I suppose you could register a custom SchemaTransformer in a way similar to NonNullMapper (but likely simpler). That doesn't work as a general solution, but I think it can solve your case. I'm away from the computer (typing this on the phone), so I can't really provide an example, you'll have to experiment on your own. In the next release, a lot more contextual info is provided to TypeMappers, enabling them to deal with many advanced cases more generally. Your case would also fit well.

alexandru-constantinescu commented 4 years ago

Thank you for your time.

kaqqao commented 4 years ago

No worries. Please post back with your findings or if you encounter any trouble.

IceBlizz6 commented 4 years ago

Hey

I realize this thread is a bit old now but has there been some progress on this? I believe this approach can also be very helpful to Kotlin users as annotating @GraphQLNonNull does not work at the moment, and even if it did it would be somewhat unnecessary as Kotlin already has a way to define fields that are nullable.

So if you did figure this out then could you please provide a sample on how to use SchemaTransformer to make fields (and elements in lists) non-nullable?

alexandru-constantinescu commented 4 years ago

Hi. Sorry for the delay, I forgot to come back with the solution I found. In our case, we needed this transformation only for OneToMany relations.

public class GraphQLNonNullListEntities implements SchemaTransformer  {

    @Override
    public GraphQLFieldDefinition transformField(GraphQLFieldDefinition field, Operation operation, OperationMapper operationMapper, BuildContext buildContext) {

        //only for OneToMany annotated relations in entities
        if (operation.getTypedElement().isAnnotationPresent(javax.persistence.OneToMany.class)) {
            /* checking if type is List and the entity is not already annotated as non null
             * List<Entity> => [Entity!]
             */
            if (field.getType() instanceof GraphQLList && !(((GraphQLList) field.getType()).getWrappedType() instanceof GraphQLNonNull)) {
                return field.transform(builder -> builder.type(new GraphQLList(new GraphQLNonNull(((GraphQLList) field.getType()).getWrappedType()))));
            } else {
                /* checking if type is non null List and the entity is not already annotated as non null
                 * @NotNull List<Entity> => [Entity!]!
                 */
                if (field.getType() instanceof GraphQLNonNull && (((GraphQLNonNull) field.getType()).getWrappedType() instanceof GraphQLList)
                        && !(((GraphQLList)((GraphQLNonNull) field.getType()).getWrappedType()).getWrappedType() instanceof GraphQLNonNull)) {
                    return field.transform(builder -> builder.type(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(((GraphQLList)((GraphQLNonNull) field.getType()).getWrappedType()).getWrappedType())))));
                }
            }
        }

        return SchemaTransformer.super.transformField(field, operation, operationMapper, buildContext);
    }
}
@Bean
public ExtensionProvider<GeneratorConfiguration, SchemaTransformer> schemaTransformerExtensionProvider() {
    return (config, current) -> {
        current.add(new GraphQLNonNullListEntities());
        return current;
    };      
}