leangen / graphql-spqr

Build a GraphQL service in seconds
Apache License 2.0
1.09k stars 179 forks source link

How to use @GraphQLNonNull annotation in DTO model from library jar file #424

Closed yangli2014 closed 1 year ago

yangli2014 commented 1 year ago

I am using graphql-spqr-spring-boot-starter and annotating @GraphQLApi and @GraphQLMutation @GraphQLQuery in the service layer. However, the DTO data models come from another library project. How can I define some filed as mandatory in this situation? Thanks.

kaqqao commented 1 year ago

You could implement a custom TypeMapper that detects the desired DTOs and wraps them with GraphQLNonNull.

E.g. for an externally-defined class such as:

public class BookDTO { .. }

You could a mapper like this:

public class BookMapper implements TypeMapper {

        @Override
        public GraphQLNonNull toGraphQLType(AnnotatedType javaType, Set<Class<? extends TypeMapper>> mappersToSkip, TypeMappingEnvironment env) {
            GraphQLOutputType wrappedType = env.operationMapper.toGraphQLType(javaType, singleton(getClass()), env);
            return GraphQLNonNull.nonNull(wrappedType);
        }

        @Override
        public GraphQLNonNull toGraphQLInputType(AnnotatedType javaType, Set<Class<? extends TypeMapper>> mappersToSkip, TypeMappingEnvironment env) {
            GraphQLInputType wrappedType = env.operationMapper.toGraphQLInputType(javaType, singleton(getClass()), env);
            return GraphQLNonNull.nonNull(wrappedType);
        }

        @Override
        public boolean supports(AnnotatedElement element, AnnotatedType type) {
            return type.getType().equals(BookDTO.class);
            // or GenericTypeReflector.isSuperType(BookDTO.class, type.getType()) to support subtypes as well
            // You can also use the `element` to detect a specific method or class where this type is being used,
           // in case BookDTO should only sometimes be non-null
        }
    }

Since you say you're using Spring, you can register the custom TypeMapper like this:

@Bean
public ExtensionProvider<GeneratorConfiguration, TypeMapper> customMappers() {
       // The order of mappers matters, but a generally good place to insert customizations is after `IdAdapter`
       return (config, mappers) -> mappers.insertAfter(IdAdapter.class, new BookMapper());
}

P.S. Sorry for the long response time, the project was in maintenance-only mode for a couple of months.