Denis Khvan
Denis Khvan

Reputation: 11

Custom GraphQL directive for INPUT_FIELD_DEFINITION doesn't work

I want to create custom directive for input variables to check value in CMS. Application cannot register and find this directive. This global .graphqls file:

# ====================================================
# =                      SCALARS                     =
# ====================================================

scalar DateTime
scalar BigDecimal

# ====================================================
# =                    DIRECTIVES                    =
# ====================================================

directive @NotBlank(message : String = "graphql.validation.NotBlank.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION

directive @Size(min : Int = 0, max : Int = 2147483647, message : String = "graphql.validation.Size.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION

directive @ContainerNotEmpty(message : String = "graphql.validation.ContainerNotEmpty.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION

directive @Pattern(regexp : String! =".*", message : String = "graphql.validation.Pattern.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION

directive @Min(value : Int! = 0, message : String = "graphql.validation.Min.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION

directive @existsInCms(dictionary: String!) on INPUT_FIELD_DEFINITION

# ====================================================
# =             BASE QUERY AND MUTATION              =
# ====================================================

type Mutation {
}

#type Query {
#}

# ====================================================
# =                 FEDERATED TYPES                  =
# ====================================================

type countries @key(fields: "code") @extends {
    code: String!
}

This is a configuration:

@Slf4j
@Configuration
@RequiredArgsConstructor
public class GraphQLValidationConfig {

    private final CmsHelper cmsHelper;

    @Bean
    public RuntimeWiringConfigurer runtimeWiringConfigurer() {
        log.info("Registered @existsInCms directive");
        return builder -> builder.directiveWiring(new ExistsInCmsDirective(cmsHelper));
    }

}

This is a directive in Java:

@DgsDirective(name = "existsInCms")
@Component
@RequiredArgsConstructor
public class ExistsInCmsDirective implements SchemaDirectiveWiring {

    private final CmsHelper cmsHelper;

    @Override
    public GraphQLFieldDefinition onField(SchemaDirectiveWiringEnvironment<GraphQLFieldDefinition> environment) {
        var coordinates = FieldCoordinates.coordinates(
            environment.getFieldsContainer().getName(),
            environment.getElement().getName());

        var originalFetcher = environment.getCodeRegistry().getDataFetcher(coordinates, environment.getElement());
        var directive = environment.getDirective("existsInCms");
        if (directive == null) {
            throw new IllegalStateException("Directive @existsInCms not found");
        }

        String dictionary = directive.getArgument("dictionary").getArgumentValue().getValue().toString();

        DataFetcher<?> dataFetcher = dataEnv -> {
            List<String> input = dataEnv.getArgument(environment.getFieldDefinition().getName());

            if (!cmsHelper.existsByCodes(dictionary, input)) {
                throw new IllegalArgumentException("Invalid values in dictionary '" + dictionary + "': " + input);
            }

            return originalFetcher.get(dataEnv);
        };

        environment.getCodeRegistry().dataFetcher(coordinates, dataFetcher);

        return environment.getElement();
    }

    @Override
    public GraphQLInputObjectField onInputObjectField(SchemaDirectiveWiringEnvironment<GraphQLInputObjectField> environment) {
        return environment.getElement();
    }

}

And this is my CMS helper for check value in CMS:

@Component
@RequiredArgsConstructor
public class CmsHelper {

    private final RestTemplate restTemplate;
    private final HttpHeaders defaultHeaders = new HttpHeaders();

    @Value("${app-config.hasura.url}")
    private String cmsUrl;

    @Value("${app-config.hasura.access-token}")
    private String accessToken;

    public boolean existsByCodes(String dict, List<String> codes) {
        var graphqlQuery = String.format("""
            {
                "query": "query MyQuery { %s(where: {code: {_in: %s}}) { code } }",
                "operationName": "MyQuery"
            }
            """, dict, codes.toString());

        var defaultHeaders = new HttpHeaders();
        defaultHeaders.setContentType(MediaType.APPLICATION_JSON);

        var entity = new HttpEntity<>(graphqlQuery, defaultHeaders);
        var response = restTemplate.exchange(cmsUrl, HttpMethod.POST, entity, JsonNode.class);

        return Optional.ofNullable(response.getBody())
            .map(jsonNode -> jsonNode.path("data").path(dict))
            .filter(JsonNode::isArray)
            .map(arrayNode -> {
                List<String> responseCodes = new ArrayList<>();
                arrayNode.forEach(item -> responseCodes.add(item.path("code").asText()));

                return new HashSet<>(responseCodes).containsAll(codes);
            })
            .orElse(false);
    }

    @PostConstruct
    void initHeaders() {
        defaultHeaders.setContentType(MediaType.APPLICATION_JSON);
        defaultHeaders.set("x-hasura-admin-secret", accessToken);
    }

}

When launching the project it gives an error that the directive “@existsInCms not found”, and also when sending a request the directive does not work

16:44:53.524 [main           ] ERROR o.springframework.boot.SpringApplication  - Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'routerFunctionMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Error creating bean with name 'graphQlRouterFunction' defined in class path resource [org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQlRouterFunction' parameter 0: Error creating bean with name 'graphQlHttpHandler' defined in class path resource [org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQlHttpHandler' parameter 0: Error creating bean with name 'webGraphQlHandler' defined in class path resource [org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.class]: Unsatisfied dependency expressed through method 'webGraphQlHandler' parameter 0: Error creating bean with name 'executionGraphQlService' defined in class path resource [org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfiguration.class]: Failed to instantiate [org.springframework.graphql.ExecutionGraphQlService]: Factory method 'executionGraphQlService' threw exception with message: Directive @existsInCms not found
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1806)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:971)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:625)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:335)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352)
    at kz.tau.activityservice.ActivityServiceApplication.main(ActivityServiceApplication.java:10)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphQlRouterFunction' defined in class path resource [org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQlRouterFunction' parameter 0: Error creating bean with name 'graphQlHttpHandler' defined in class path resource [org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQlHttpHandler' parameter 0: Error creating bean with name 'webGraphQlHandler' defined in class path resource [org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.class]: Unsatisfied dependency expressed through method 'webGraphQlHandler' parameter 0: Error creating bean with name 'executionGraphQlService' defined in class path resource [org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfiguration.class]: Failed to instantiate [org.springframework.graphql.ExecutionGraphQlService]: Factory method 'executionGraphQlService' threw exception with message: Directive @existsInCms not found
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:795)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:542)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1355)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1185)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory$1.orderedStream(DefaultListableBeanFactory.java:473)
    at org.springframework.web.servlet.function.support.RouterFunctionMapping.initRouterFunctions(RouterFunctionMapping.java:152)
    at org.springframework.web.servlet.function.support.RouterFunctionMapping.afterPropertiesSet(RouterFunctionMapping.java:130)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1853)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1802)
    ... 16 common frames omitted
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphQlHttpHandler' defined in class path resource [org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQlHttpHandler' parameter 0: Error creating bean with name 'webGraphQlHandler' defined in class path resource [org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.class]: Unsatisfied dependency expressed through method 'webGraphQlHandler' parameter 0: Error creating bean with name 'executionGraphQlService' defined in class path resource [org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfiguration.class]: Failed to instantiate [org.springframework.graphql.ExecutionGraphQlService]: Factory method 'executionGraphQlService' threw exception with message: Directive @existsInCms not found
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:795)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:542)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1355)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1185)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1443)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1353)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:904)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:782)
    ... 30 common frames omitted
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'webGraphQlHandler' defined in class path resource [org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.class]: Unsatisfied dependency expressed through method 'webGraphQlHandler' parameter 0: Error creating bean with name 'executionGraphQlService' defined in class path resource [org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfiguration.class]: Failed to instantiate [org.springframework.graphql.ExecutionGraphQlService]: Factory method 'executionGraphQlService' threw exception with message: Directive @existsInCms not found
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:795)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:542)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1355)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1185)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1443)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1353)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:904)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:782)
    ... 44 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'executionGraphQlService' defined in class path resource [org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfiguration.class]: Failed to instantiate [org.springframework.graphql.ExecutionGraphQlService]: Factory method 'executionGraphQlService' threw exception with message: Directive @existsInCms not found
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:648)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:636)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1355)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1185)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1443)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1353)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:904)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:782)
    ... 58 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.graphql.ExecutionGraphQlService]: Factory method 'executionGraphQlService' threw exception with message: Directive @existsInCms not found
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:178)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:644)
    ... 72 common frames omitted
Caused by: java.lang.IllegalStateException: Directive @existsInCms not found
    at kz.tau.activityservice.core.annotation.ExistsInCmsDirective.onField(ExistsInCmsDirective.java:34)
    at graphql.schema.idl.SchemaGeneratorDirectiveHelper.invokeWiring(SchemaGeneratorDirectiveHelper.java:466)
    at graphql.schema.idl.SchemaGeneratorDirectiveHelper.wireDirectives(SchemaGeneratorDirectiveHelper.java:452)
    at graphql.schema.idl.SchemaGeneratorDirectiveHelper.onField(SchemaGeneratorDirectiveHelper.java:352)
    at graphql.schema.idl.SchemaGeneratorDirectiveHelper.lambda$wireFields$2(SchemaGeneratorDirectiveHelper.java:190)
    at graphql.collect.ImmutableKit.map(ImmutableKit.java:55)
    at graphql.schema.idl.SchemaGeneratorDirectiveHelper.wireFields(SchemaGeneratorDirectiveHelper.java:174)
    at graphql.schema.idl.SchemaGeneratorDirectiveHelper.onObject(SchemaGeneratorDirectiveHelper.java:197)
    at graphql.schema.idl.SchemaDirectiveWiringSchemaGeneratorPostProcessing$Visitor.visitGraphQLObjectType(SchemaDirectiveWiringSchemaGeneratorPostProcessing.java:95)
    at graphql.schema.GraphQLObjectType.accept(GraphQLObjectType.java:191)
    at graphql.schema.SchemaTransformer$2.enter(SchemaTransformer.java:214)
    at graphql.util.Traverser.traverse(Traverser.java:144)
    at graphql.util.Traverser.traverse(Traverser.java:87)
    at graphql.schema.SchemaTransformer.traverseAndTransform(SchemaTransformer.java:278)
    at graphql.schema.SchemaTransformer.transformImpl(SchemaTransformer.java:147)
    at graphql.schema.SchemaTransformer.transform(SchemaTransformer.java:121)
    at graphql.schema.SchemaTransformer.transformSchema(SchemaTransformer.java:89)
    at graphql.schema.idl.SchemaDirectiveWiringSchemaGeneratorPostProcessing.process(SchemaDirectiveWiringSchemaGeneratorPostProcessing.java:47)
    at graphql.schema.idl.SchemaGenerator.makeExecutableSchemaImpl(SchemaGenerator.java:155)
    at graphql.schema.idl.SchemaGenerator.makeExecutableSchema(SchemaGenerator.java:113)
    at com.apollographql.federation.graphqljava.Federation.transform(Federation.java:85)
    at com.netflix.graphql.dgs.internal.DgsSchemaProvider.computeSchema(DgsSchemaProvider.kt:229)
    at com.netflix.graphql.dgs.internal.DgsSchemaProvider.schema(DgsSchemaProvider.kt:131)
    at com.netflix.graphql.dgs.internal.DgsSchemaProvider.schema$default(DgsSchemaProvider.kt:125)
    at com.netflix.graphql.dgs.springgraphql.DgsGraphQLSourceBuilder.initGraphQlSchema(DgsGraphQLSourceBuilder.kt:54)
    at org.springframework.graphql.execution.AbstractGraphQlSourceBuilder.build(AbstractGraphQlSourceBuilder.java:106)
    at com.netflix.graphql.dgs.springgraphql.ReloadableGraphQLSource.getSource(ReloadableGraphQLSource.kt:39)
    at com.netflix.graphql.dgs.springgraphql.ReloadableGraphQLSource.graphQl(ReloadableGraphQLSource.kt:32)
    at org.springframework.graphql.execution.DefaultExecutionGraphQlService.<init>(DefaultExecutionGraphQlService.java:72)
    at org.springframework.boot.autoconfigure.graphql.GraphQlAutoConfiguration.executionGraphQlService(GraphQlAutoConfiguration.java:149)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:146)
    ... 73 common frames omitted

Upvotes: 0

Views: 61

Answers (0)

Related Questions