cc.voox.graphql.GraphqlResolverFactory Maven / Gradle / Ivy
The newest version!
package cc.voox.graphql;
import cc.voox.graphql.annotation.ObjectType;
import cc.voox.graphql.annotation.Query;
import cc.voox.graphql.annotation.QueryField;
import cc.voox.graphql.annotation.QueryMethod;
import cc.voox.graphql.metadata.TypeArgument;
import cc.voox.graphql.metadata.TypeField;
import cc.voox.graphql.utils.AOPUtil;
import cc.voox.graphql.utils.BeanUtil;
import cc.voox.graphql.utils.GraphQLTypeUtils;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import graphql.GraphQLException;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import graphql.schema.GraphQLArgument;
import graphql.schema.GraphQLInputObjectType;
import graphql.schema.GraphQLList;
import graphql.schema.GraphQLNonNull;
import graphql.schema.GraphQLOutputType;
import graphql.schema.GraphQLScalarType;
import graphql.schema.GraphQLType;
import graphql.schema.GraphQLTypeReference;
import graphql.schema.idl.TypeRuntimeWiring;
import org.dataloader.DataLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import static cc.voox.graphql.utils.GraphQLTypeUtils.isGraphQLPrimitive;
import static graphql.schema.idl.TypeRuntimeWiring.newTypeWiring;
@Component
public class GraphqlResolverFactory implements ApplicationContextAware {
private Logger logger = LoggerFactory.getLogger(getClass());
private ApplicationContext context;
@Autowired
private GraphqlProperties graphqlProperties;
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.context = context;
}
@Bean
public ObjectMapper objectMapper() {
ObjectMapper responseMapper = new ObjectMapper();
responseMapper.registerModule(new ParameterNamesModule())
.registerModule(new Jdk8Module())
.registerModule(new JavaTimeModule());
responseMapper.addMixIn(Object.class, JsonIgnore.class);
responseMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return responseMapper;
}
@Autowired
private ObjectMapper objectMapper;
private final Map> dataLoaders = new ConcurrentHashMap();
private Map> wfResolvers = new HashMap<>();
private Map> typeFieldMap = new HashMap<>();
private Set scalarSet = new HashSet<>();
private Set directiveSet = new HashSet<>();
private Set interceptors = new HashSet<>();
private List> typeList = new ArrayList<>();
public Set getInterceptors() {
return interceptors;
}
protected Set getScalarSet() {
return scalarSet;
}
protected Set getDirectiveSet() {
return directiveSet;
}
protected Map> getDataLoaders() {
return dataLoaders;
}
public List> getTypeList() {
return typeList;
}
@PostConstruct
void init() {
if (graphqlProperties.isLog()) {
logger.info("init GraphQL start");
}
ClassPathScanningCandidateComponentProvider cp = new ClassPathScanningCandidateComponentProvider(false);
cp.addIncludeFilter(new AnnotationTypeFilter(Query.class));
cp.addIncludeFilter(new AssignableTypeFilter(IGraphQL.class));
cp.addIncludeFilter(new AssignableTypeFilter(IScalar.class));
cp.addIncludeFilter(new AssignableTypeFilter(IDirective.class));
cp.addIncludeFilter(new AssignableTypeFilter(IDataLoader.class));
cp.addIncludeFilter(new AssignableTypeFilter(GraphQLInterceptor.class));
if (graphqlProperties.isEnableCodeMode()) {
cp.addIncludeFilter(new AnnotationTypeFilter(ObjectType.class));
}
if (StringUtils.isEmpty(graphqlProperties.getScanPath())) {
throw new GraphQLException("Scan path is empty. please set scan path in GraphqlProperties bean.");
}
Set bd = cp.findCandidateComponents(graphqlProperties.getScanPath());
ClassLoader loader = GraphqlResolverFactory.class.getClassLoader();
Map> resolvers = new HashMap();
Map>> originClassResolvers = new HashMap();
Iterator var5 = bd.iterator();
while (var5.hasNext()) {
BeanDefinition b = (BeanDefinition) var5.next();
String beanName = b.getBeanClassName();
try {
Class> c = loader.loadClass(beanName);
Class>[] interfaces = c.getInterfaces();
Set> set = new HashSet<>();
for (Class> inter : interfaces) {
set.add(inter);
}
if (set.contains(IScalar.class) || IScalar.class.isAssignableFrom(c)) {
IScalar iScalar = null;
try {
iScalar = (IScalar) context.getBean(c);
} catch (Exception e) {
try {
iScalar = (IScalar) c.newInstance();
} catch (Exception ex) {
}
if (iScalar == null) {
throw new GraphQLException("Cannot initial scalar" + c);
}
}
scalarSet.add(iScalar);
continue;
} else if (set.contains(IDirective.class) || IDirective.class.isAssignableFrom(c)) {
IDirective directive = null;
try {
directive = (IDirective) this.context.getBean(c);
} catch (Exception e) {
try {
directive = (IDirective) c.newInstance();
} catch (Exception illegalAccessException) {
}
if (directive == null) {
throw new GraphQLException("Cannot initial directive" + c);
}
}
directiveSet.add(directive);
continue;
} else if (set.contains(IDataLoader.class) || IDataLoader.class.isAssignableFrom(c)) {
IDataLoader dataLoader = null;
try {
dataLoader = (IDataLoader) this.context.getBean(c);
} catch (Exception e) {
try {
dataLoader = (IDataLoader) c.newInstance();
} catch (Exception illegalAccessException) {
}
if (dataLoader == null) {
throw new GraphQLException("Cannot initial dataLoader" + c);
}
}
dataLoaders.put(c.getSimpleName(), dataLoader.useTryMode() ? dataLoader.getTry() : dataLoader.get());
continue;
} else if (set.contains(GraphQLInterceptor.class) || GraphQLInterceptor.class.isAssignableFrom(c)) {
try {
GraphQLInterceptor interceptor = (GraphQLInterceptor) this.context.getBean(c);
this.interceptors.add(interceptor);
} catch (Exception e) {
throw new GraphQLException("Cannot initial interceptor" + c);
}
continue;
} else if(c.isAnnotationPresent(ObjectType.class)) {
typeList.add(c);
// continue;
}
String queryValue = null;
if (c.isAnnotationPresent(Query.class)) {
Query q = c.getAnnotation(Query.class);
queryValue = q.value();
} else {
if (set.contains(IGraphQL.class) || IGraphQL.class.isAssignableFrom(c)) {
queryValue = c.getSimpleName();
} else {
if(c.isAnnotationPresent(ObjectType.class)) {
continue;
}
}
}
String value = queryValue;
if (StringUtils.isEmpty(value)) {
value = "Query";
}
Method[] methods = c.getDeclaredMethods();
for (Method method : methods) {
method.setAccessible(true);
if (method.isAnnotationPresent(QueryMethod.class)) {
String methodQueryValue = value;
QueryMethod methodAnnotation = method.getAnnotation(QueryMethod.class);
String type = methodAnnotation.type();
String methodValue = methodAnnotation.value();
if (!StringUtils.isEmpty(type)) {
methodQueryValue = type;
} else if (method.isAnnotationPresent(Query.class)) {
Query q = method.getAnnotation(Query.class);
methodQueryValue = StringUtils.isEmpty(q.value()) ? value : q.value();
} else {
methodQueryValue = value;
}
// final String resolverType = methodQueryValue;
Map resolverMap = resolvers.get(methodQueryValue);
Map> resolverClassMap = originClassResolvers.get(methodQueryValue);
if (resolverMap == null) {
resolverMap = new LinkedHashMap<>();
}
if (resolverClassMap == null) {
resolverClassMap = new LinkedHashMap<>();
}
if (StringUtils.isEmpty(methodValue)) {
methodValue = method.getName();
}
TypeField typeField = new TypeField();
typeField.setValue(methodValue);
typeField.setType(getFieldType(method));
Set typeFields = typeFieldMap.get(methodQueryValue);
if(typeFields == null) {
typeFields = new HashSet<>();
}
typeFields.add(typeField);
try {
Class>[] clsTypes = method.getParameterTypes();
int i = 0;
int index = 0;
List> directiveList = GraphQLTypeUtils.getDirectives(method);
List extends IDirective> collect = directiveList.stream().map(aClass -> this.context.getBean(aClass)).collect(Collectors.toList());
typeField.setDirectiveList(collect);
List typeArguments = GraphQLTypeUtils.getArguments(method);
for (Class> clsType : clsTypes) {
TypeArgument typeArgument = typeArguments.get(index);
if (typeArgument.isRoot()) {
continue;
}
if (clsType.isPrimitive() || GraphQLTypeUtils.isGraphQLPrimitive(clsType)) {
GraphQLScalarType scalarType = GraphQLTypeUtils.getType(clsType);
GraphQLArgument.Builder builder = GraphQLArgument.newArgument().name(typeArgument.getName());
builder.description(typeArgument.getDescription());
builder.type(typeArgument.isRequired()? GraphQLNonNull.nonNull(scalarType): scalarType);
typeField.addArgument(builder.build());
} else {
if (Collection.class.isAssignableFrom(clsType)) {
LinkedList typesFromIndex = GraphQLTypeUtils.getTypesFromIndex(method, i);
GraphQLArgument.Builder builder = GraphQLArgument.newArgument().name(typeArgument.getName());
if (typesFromIndex.size() > 0) {
String typeStr = typesFromIndex.get(0);
Class> typeClz = Class.forName(typeStr);
GraphQLList graphQLList = GraphQLList.list(GraphQLTypeReference.typeRef(typeClz.getSimpleName()));
builder.description(typeArgument.getDescription());
builder.type(typeArgument.isRequired()? GraphQLNonNull.nonNull(graphQLList): graphQLList);
typeField.addArgument(builder.build());
i++;
} else {
GraphQLList graphQLList = GraphQLList.list(GraphQLTypeReference.typeRef(clsType.getSimpleName()));
builder.description(typeArgument.getDescription());
builder.type(typeArgument.isRequired()? GraphQLNonNull.nonNull(graphQLList): graphQLList);
typeField.addArgument(builder.build());
}
} else {
GraphQLArgument.Builder builder = GraphQLArgument.newArgument().name(typeArgument.getName());
builder.description(typeArgument.getDescription());
builder.type(typeArgument.isRequired() ? GraphQLNonNull.nonNull(GraphQLTypeReference.typeRef(clsType.getSimpleName()))
: GraphQLTypeReference.typeRef(clsType.getSimpleName()));
typeField.addArgument(builder.build());
}
}
index++;
}
} catch (Exception e) {
e.printStackTrace();
}
typeFieldMap.put(methodQueryValue, typeFields);
DataFetcher df = dataFetchingEnvironment -> {
GraphQLContextUtil.add(dataFetchingEnvironment);
Class>[] clsTypes = method.getParameterTypes();
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy