io.leangen.graphql.spqr.spring.autoconfigure.BaseAutoConfiguration Maven / Gradle / Ivy
The newest version!
package io.leangen.graphql.spqr.spring.autoconfigure;
import graphql.GraphQL;
import graphql.execution.instrumentation.Instrumentation;
import graphql.schema.GraphQLSchema;
import io.leangen.geantyref.GenericTypeReflector;
import io.leangen.graphql.*;
import io.leangen.graphql.execution.ResolverInterceptorFactory;
import io.leangen.graphql.generator.mapping.ArgumentInjector;
import io.leangen.graphql.generator.mapping.InputConverter;
import io.leangen.graphql.generator.mapping.OutputConverter;
import io.leangen.graphql.generator.mapping.SchemaTransformer;
import io.leangen.graphql.generator.mapping.TypeMapper;
import io.leangen.graphql.generator.mapping.strategy.AbstractInputHandler;
import io.leangen.graphql.generator.mapping.strategy.InterfaceMappingStrategy;
import io.leangen.graphql.metadata.messages.MessageBundle;
import io.leangen.graphql.metadata.strategy.InclusionStrategy;
import io.leangen.graphql.metadata.strategy.query.AbstractResolverBuilder;
import io.leangen.graphql.metadata.strategy.query.AnnotatedResolverBuilder;
import io.leangen.graphql.metadata.strategy.query.BeanResolverBuilder;
import io.leangen.graphql.metadata.strategy.query.MethodInvokerFactory;
import io.leangen.graphql.metadata.strategy.query.PublicResolverBuilder;
import io.leangen.graphql.metadata.strategy.query.ResolverBuilder;
import io.leangen.graphql.metadata.strategy.type.TypeInfoGenerator;
import io.leangen.graphql.metadata.strategy.value.InputFieldBuilder;
import io.leangen.graphql.metadata.strategy.value.ValueMapperFactory;
import io.leangen.graphql.module.Module;
import io.leangen.graphql.spqr.spring.annotations.GraphQLApi;
import io.leangen.graphql.spqr.spring.annotations.WithResolverBuilder;
import io.leangen.graphql.util.Utils;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.type.StandardMethodMetadata;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedType;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@AutoConfiguration
@ConditionalOnClass(GraphQLSchemaGenerator.class)
@EnableConfigurationProperties(SpqrProperties.class)
@SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
public class BaseAutoConfiguration {
private final ConfigurableApplicationContext context;
private final MethodInvokerFactory aopAwareFactory = new AopAwareMethodInvokerFactory();
@Autowired(required = false)
private ExtensionProvider globalResolverBuilderExtensionProvider;
@Autowired(required = false)
private ExtensionProvider typeMapperExtensionProvider;
@Autowired(required = false)
private ExtensionProvider inputConverterExtensionProvider;
@Autowired(required = false)
private ExtensionProvider outputConverterExtensionProvider;
@Autowired(required = false)
private ExtensionProvider argumentInjectorExtensionProvider;
@Autowired(required = false)
private ExtensionProvider schemaTransformerExtensionProvider;
@Autowired(required = false)
private ExtensionProvider resolverInterceptorFactoryExtensionProvider;
@Autowired(required = false)
private ValueMapperFactory valueMapperFactory;
@Autowired(required = false)
private ExtensionProvider inputFieldBuilderProvider;
@Autowired(required = false)
private TypeInfoGenerator typeInfoGenerator;
@Autowired(required = false)
private AbstractInputHandler abstractInputHandler;
@Autowired(required = false)
private InclusionStrategy inclusionStrategy;
@Autowired(required = false)
private InterfaceMappingStrategy interfaceMappingStrategy;
@Autowired(required = false)
private Set messageBundles;
@Autowired(required = false)
private ExtensionProvider moduleExtensionProvider;
@Autowired(required = false)
private List> internalModules;
@Autowired
public BaseAutoConfiguration(ConfigurableApplicationContext context) {
this.context = context;
}
@Bean
@ConditionalOnMissingBean
public AnnotatedResolverBuilder defaultAnnotatedResolverBuilder() {
return (AnnotatedResolverBuilder) new AnnotatedResolverBuilder().withMethodInvokerFactory(aopAwareFactory);
}
@Bean
@ConditionalOnMissingBean
public BeanResolverBuilder defaultBeanResolverBuilder() {
return (BeanResolverBuilder) new BeanResolverBuilder().withMethodInvokerFactory(aopAwareFactory);
}
@Bean
@ConditionalOnMissingBean
public PublicResolverBuilder defaultPublicResolverBuilder() {
return (PublicResolverBuilder) new PublicResolverBuilder().withMethodInvokerFactory(aopAwareFactory);
}
@Bean
@ConditionalOnMissingBean
public GraphQLSchemaGenerator graphQLSchemaGenerator(SpqrProperties spqrProperties) {
GraphQLSchemaGenerator schemaGenerator = new GraphQLSchemaGenerator();
schemaGenerator.withBasePackages(spqrProperties.getBasePackages());
if (spqrProperties.getRelay().isEnabled()) {
if (Utils.isNotEmpty(spqrProperties.getRelay().getMutationWrapper())) {
schemaGenerator.withRelayCompliantMutations(
spqrProperties.getRelay().getMutationWrapper(), spqrProperties.getRelay().getMutationWrapperDescription()
);
} else {
schemaGenerator.withRelayCompliantMutations();
}
}
Map apiComponents = findGraphQLApiComponents();
addOperationSources(schemaGenerator, apiComponents.values());
// Modules should be registered first, so that extension providers have a chance to override what they need
// Built-in modules must go before the user-provided ones for similar reasons
if (internalModules != null) {
internalModules.forEach(module -> schemaGenerator.withModules(module.get()));
}
if (moduleExtensionProvider != null) {
schemaGenerator.withModules(moduleExtensionProvider);
}
if (globalResolverBuilderExtensionProvider != null) {
schemaGenerator.withResolverBuilders(globalResolverBuilderExtensionProvider);
} else {
schemaGenerator.withResolverBuilders(defaultAnnotatedResolverBuilder());
}
if (typeMapperExtensionProvider != null) {
schemaGenerator.withTypeMappers(typeMapperExtensionProvider);
}
if (inputConverterExtensionProvider != null) {
schemaGenerator.withInputConverters(inputConverterExtensionProvider);
}
if (outputConverterExtensionProvider != null) {
schemaGenerator.withOutputConverters(outputConverterExtensionProvider);
}
if (argumentInjectorExtensionProvider != null) {
schemaGenerator.withArgumentInjectors(argumentInjectorExtensionProvider);
}
if (schemaTransformerExtensionProvider != null) {
schemaGenerator.withSchemaTransformers(schemaTransformerExtensionProvider);
}
if (resolverInterceptorFactoryExtensionProvider != null) {
schemaGenerator.withResolverInterceptorFactories(resolverInterceptorFactoryExtensionProvider);
}
if (valueMapperFactory != null) {
schemaGenerator.withValueMapperFactory(valueMapperFactory);
}
if (inputFieldBuilderProvider != null) {
schemaGenerator.withInputFieldBuilders(inputFieldBuilderProvider);
}
if (typeInfoGenerator != null) {
schemaGenerator.withTypeInfoGenerator(typeInfoGenerator);
}
if (spqrProperties.isAbstractInputTypeResolution()) {
schemaGenerator.withAbstractInputTypeResolution();
}
if (abstractInputHandler != null) {
schemaGenerator.withAbstractInputHandler(abstractInputHandler);
}
if (messageBundles != null && !messageBundles.isEmpty()) {
schemaGenerator.withStringInterpolation(messageBundles.toArray(new MessageBundle[0]));
}
if (inclusionStrategy != null) {
schemaGenerator.withInclusionStrategy(inclusionStrategy);
}
if (interfaceMappingStrategy != null) {
schemaGenerator.withInterfaceMappingStrategy(interfaceMappingStrategy);
}
if (spqrProperties.getRelay().isConnectionCheckRelaxed()) {
schemaGenerator.withRelayConnectionCheckRelaxed();
}
return schemaGenerator;
}
private void addOperationSources(GraphQLSchemaGenerator schemaGenerator, Collection spqrBeans) {
spqrBeans.forEach(spqrBean ->
schemaGenerator.withOperationsFromBean(
spqrBean.beanSupplier,
spqrBean.type,
spqrBean.exposedType,
spqrBean.resolverBuilders.stream()
.map(criteria -> findQualifiedBeanByType(
criteria.getResolverType(),
criteria.getValue(),
criteria.getQualifierType()))
.peek(resolverBuilder -> {
if (resolverBuilder instanceof AbstractResolverBuilder) {
((AbstractResolverBuilder) resolverBuilder).withMethodInvokerFactory(aopAwareFactory);
}
})
.toArray(ResolverBuilder[]::new)
)
);
}
@Bean
@ConditionalOnMissingBean
public ExecutableSchema graphQLExecutableSchema(GraphQLSchemaGenerator schemaGenerator) {
return schemaGenerator.generateExecutable();
}
@Bean
@ConditionalOnMissingBean
public GraphQLSchema graphQLSchema(ExecutableSchema executableSchema) {
return executableSchema.getSchema();
}
@Bean
@ConditionalOnMissingBean
public GraphQL graphQL(ExecutableSchema schema, SpqrProperties spqrProperties, List instrumentations) {
GraphQLRuntime.Builder builder = GraphQLRuntime.newGraphQL(schema);
instrumentations.forEach(builder::instrumentation);
if (spqrProperties.getMaxComplexity() > 1) {
builder.maximumQueryComplexity(spqrProperties.getMaxComplexity());
}
return builder.build();
}
private T findQualifiedBeanByType(Class extends T> type, String qualifierValue, Class extends Annotation> qualifierType) {
final NoSuchBeanDefinitionException noSuchBeanDefinitionException = new NoSuchBeanDefinitionException(qualifierValue, "No matching " + type.getSimpleName() +
" bean found for qualifier " + qualifierValue + " of type " + qualifierType.getSimpleName() + " !");
try {
if (Utils.isEmpty(qualifierValue)) {
if (qualifierType.equals(Qualifier.class)) {
return Optional.of(
context.getBean(type))
.orElseThrow(() -> noSuchBeanDefinitionException);
}
return context.getBean(
Arrays.stream(context.getBeanNamesForAnnotation(qualifierType))
.filter(beanName -> type.isInstance(context.getBean(beanName)))
.findFirst()
.orElseThrow(() -> noSuchBeanDefinitionException),
type);
}
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(context.getBeanFactory(), type, qualifierValue);
} catch (NoSuchBeanDefinitionException noBeanException) {
ConfigurableListableBeanFactory factory = context.getBeanFactory();
for (String name : factory.getBeanDefinitionNames()) {
BeanDefinition bd = factory.getBeanDefinition(name);
if (bd.getSource() instanceof StandardMethodMetadata) {
StandardMethodMetadata metadata = (StandardMethodMetadata) bd.getSource();
if (metadata.getReturnTypeName().equals(type.getName())) {
Map attributes = metadata.getAnnotationAttributes(qualifierType.getName());
if (null != attributes) {
if (qualifierType.equals(Qualifier.class)) {
if (qualifierValue.equals(attributes.get("value"))) {
return context.getBean(name, type);
}
}
return context.getBean(name, type);
}
}
}
}
throw noSuchBeanDefinitionException;
}
}
private Map findGraphQLApiComponents() {
final String[] apiBeanNames = context.getBeanNamesForAnnotation(GraphQLApi.class);
final ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
Map result = new HashMap<>();
for (String beanName : apiBeanNames) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
AnnotatedType beanType;
Set resolverBuilders;
if (beanDefinition.getSource() instanceof StandardMethodMetadata) {
StandardMethodMetadata metadata = (StandardMethodMetadata) beanDefinition.getSource();
beanType = metadata.getIntrospectedMethod().getAnnotatedReturnType();
resolverBuilders = AnnotatedElementUtils.findMergedRepeatableAnnotations(metadata.getIntrospectedMethod(), WithResolverBuilder.class);
} else {
BeanDefinition current = beanDefinition;
BeanDefinition originatingBeanDefinition = current;
while (current != null) {
originatingBeanDefinition = current;
current = current.getOriginatingBeanDefinition();
}
ResolvableType resolvableType = originatingBeanDefinition.getResolvableType();
if (resolvableType != ResolvableType.NONE && Utils.isNotEmpty(originatingBeanDefinition.getBeanClassName())
//Sanity check only -- should never happen
&& !originatingBeanDefinition.getBeanClassName().startsWith("org.springframework.")) {
beanType = GenericTypeReflector.annotate(resolvableType.getType());
} else {
beanType = GenericTypeReflector.annotate(AopUtils.getTargetClass(context.getBean(beanName)));
}
resolverBuilders = AnnotatedElementUtils.findMergedRepeatableAnnotations(beanType, WithResolverBuilder.class);
}
List builders = resolverBuilders.stream()
.map(builder -> new ResolverBuilderBeanCriteria(builder.value(), builder.qualifierValue(), builder.qualifierType()))
.collect(Collectors.toList());
result.put(beanName, new SpqrBean(context, beanName, beanType, builders));
}
return result;
}
private static class SpqrBean {
final BeanScope scope;
final Supplier
© 2015 - 2024 Weber Informatics LLC | Privacy Policy