1.先看下原生效果
query:
{
mediaInfoList(limit: 1) {
createTime
dateArray
}
}
result:
{
"data": {
"mediaInfoList": [
{
"createTime": "2019-01-24T22:52:45",
"dateArray": [
"2019-04-01T10:03:00.394Z",
"111111",
99999
]
}
]
}
}
2.实现效果
query:
{
mediaInfoList(limit: 1) {
createTime @dateFormat(pattern: "yyyy-MM-dd HH:mm:ss")
dateArray @dateFormat(pattern: "yyyy-MM")
}
}
result:
{
"data": {
"mediaInfoList": [
{
"createTime": "2019-01-24 22:52:45",
"dateArray": [
"2019-04",
"111111",
99999
]
}
]
}
}
3.关键代码
import graphql.introspection.Introspection;
import io.leangen.graphql.annotations.types.GraphQLDirective;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 支持client 日期格式化
* sample : createTime @dateFormat(pattern: "yyyy-MM-dd ")
*/
@GraphQLDirective(name = "dateFormat", locations = {Introspection.DirectiveLocation.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface DateFormat {
String pattern();
}
import graphql.*;
import graphql.execution.*;
import graphql.language.*;
import graphql.schema.*;
import io.leangen.graphql.annotations.types.GraphQLDirective;
import io.leangen.graphql.util.Directives;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import static java.util.concurrent.CompletableFuture.completedFuture;
/**
* 处理日期格式化逻辑,支持client提交和schema 注解处理
*/
public class DirectiveAsyncExecutionStrategy extends graphql.execution.AsyncExecutionStrategy {
private static final Logger log = LoggerFactory.getLogger(DirectiveAsyncExecutionStrategy.class);
Map> directiveAnnotationCache = Collections.synchronizedMap(new HashMap<>());
/**
* 获取指令名称
* @param annotation
* @return
*/
protected String getDirectiveName(Annotation annotation) {
return getDirectiveName(annotation.annotationType());
}
/**
* 获取指令名称
* @param clazz
* @return
*/
protected String getDirectiveName(Class extends Annotation> clazz) {
GraphQLDirective directiveAnnotation = clazz.getAnnotation(GraphQLDirective.class);
if (directiveAnnotation != null) return directiveAnnotation.name();
return null;
}
/**
*查找指令注解
*/
public Map getDirectiveAnnotation(GraphQLFieldDefinition fieldDefinition) {
Map result = null;
Object delegate = Directives.getMappedOperation(fieldDefinition).get().getResolvers().stream().map(it -> it.getExecutable().getDelegate()).findFirst().orElse(null);
if (delegate != null) {
result = directiveAnnotationCache.get(delegate);
if (result == null) {
result = new HashMap<>();
directiveAnnotationCache.put(delegate, result);
Map map = result;
Method method = null;
java.lang.reflect.Field fieldRef = null;
if (delegate instanceof Method) {
method = (Method) delegate;
} else if (delegate instanceof java.lang.reflect.Field) {
fieldRef = (java.lang.reflect.Field) delegate;
}
if (method != null) {
if (fieldRef == null) {
fieldRef = Arrays.stream(FieldUtils.getAllFields(method.getDeclaringClass())).filter(it -> StringUtils.equals(it.getName(), fieldDefinition.getName())).findFirst().orElse(null);
}
Arrays.stream(method.getAnnotations()).filter(it -> StringUtils.isNotBlank(getDirectiveName(it))).forEach(it -> {
String name = getDirectiveName(it);
map.put(name, it);
});
}
if (fieldRef != null) {
Arrays.stream(fieldRef.getAnnotations()).filter(it -> StringUtils.isNotBlank(getDirectiveName(it))).forEach(it -> {
String name = getDirectiveName(it);
map.put(name, it);
});
}
}
}
return result;
}
protected CompletableFuture completeValueForScalar(ExecutionContext executionContext, ExecutionStrategyParameters parameters, GraphQLScalarType scalarType, Object result) {
Object serialized = null;
Field field = parameters.getExecutionStepInfo().getField();
GraphQLFieldDefinition fieldDefinition = parameters.getExecutionStepInfo().getFieldDefinition();
Map directives = getDirectiveAnnotation(fieldDefinition);
List list = field.getDirectives();
AtomicReference reference = new AtomicReference(null);
boolean completion = false;
/**
* client directive 优先
*/
if (CollectionUtils.isNotEmpty(list)) {
try {
for (Directive directive : list) {
Map args = getDirectiveArgs(directive);
if (StringUtils.equals(directive.getName(), getDirectiveName(DateFormat.class))) {
String pattern = (String) args.get("pattern");
completion = format(pattern, result, reference);
if (completion) {
break;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/***
* 如果没有client指令,那么则处理服务端指令注解
* 例示:
* @GraphQLQuery(description = "创建时间")
* @DateFormat(pattern = "yyyy-MM-dd HH:mm:ss")
* private Date createTime;
*/
if (MapUtils.isNotEmpty(directives) && !completion) {
try {
for (Map.Entry entry : directives.entrySet()) {
Annotation value = entry.getValue();
if (value instanceof DateFormat) {
DateFormat annotation = (DateFormat) value;
String pattern = annotation.pattern();
completion = format(pattern, result, reference);
if (completion) {
break;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
serialized = reference.get();
if (!completion) {
try {
serialized = scalarType.getCoercing().serialize(result);
} catch (CoercingSerializeException e) {
serialized = handleCoercionProblem(executionContext, parameters, e);
}
// TODO: fix that: this should not be handled here
//6.6.1 http://facebook.github.io/graphql/#sec-Field-entries
if (serialized instanceof Double && ((Double) serialized).isNaN()) {
serialized = null;
}
}
try {
serialized = parameters.getNonNullFieldValidator().validate(parameters.getPath(), serialized);
} catch (NonNullableFieldWasNullException e) {
return Async.exceptionallyCompletedFuture(e);
}
return completedFuture(new ExecutionResultImpl(serialized, null));
}
@SuppressWarnings("SameReturnValue")
private Object handleCoercionProblem(ExecutionContext context, ExecutionStrategyParameters parameters, CoercingSerializeException e) {
SerializationError error = new SerializationError(parameters.getPath(), e);
log.warn(error.getMessage(), e);
context.addError(error);
parameters.deferredErrorSupport().onError(error);
return null;
}
protected boolean format(String pattern, Object value, AtomicReference reference) {
try {
if (value instanceof Date) {
Date date = (Date) value;
Object result = DateFormatUtils.format(date, pattern);
reference.set(result);
return true;
}
} catch (Exception e) {
}
return false;
}
protected Directive getDirective(List directives, String name) {
if (CollectionUtils.isNotEmpty(directives)) {
for (Directive directive : directives) {
if (StringUtils.equals(name, directive.getName())) {
return directive;
}
}
}
return null;
}
private Object parseObjectValue(Value value, Map variables) {
if (value instanceof StringValue) {
return ((StringValue) value).getValue();
}
if (value instanceof IntValue) {
return ((IntValue) value).getValue();
}
if (value instanceof FloatValue) {
return ((FloatValue) value).getValue();
}
if (value instanceof BooleanValue) {
return ((BooleanValue) value).isValue();
}
if (value instanceof EnumValue) {
return ((EnumValue) value).getName();
}
if (value instanceof NullValue) {
return null;
}
if (value instanceof ArrayValue) {
return ((ArrayValue) value).getValues().stream()
.map(v -> parseObjectValue(v, variables))
.collect(Collectors.toList());
}
if (value instanceof VariableReference) {
return variables.get(((VariableReference) value).getName());
}
if (value instanceof ObjectValue) {
Map map = new LinkedHashMap<>();
((ObjectValue) value).getObjectFields().forEach(field ->
map.put(field.getName(), parseObjectValue(field.getValue(), variables)));
return map;
}
//Should never happen
throw new CoercingParseLiteralException("Unknown scalar AST type: " + value.getClass().getName());
}
protected Map getDirectiveArgs(Directive directive) {
Map result = new HashMap<>();
if (directive == null) return result;
List arguments = directive.getArguments();
for (Argument argument : arguments) {
if (argument != null) {
Value value = argument.getValue();
if (value != null) {
result.put(argument.getName(), parseObjectValue(value, null));
}
}
}
return result;
}
}
public void setBuilder(GraphQLSchemaGenerator generator) {
generator.withAdditionalDirectives(DateFormat.class);
GraphQLSchema schema = generator.generate();
graphQL = GraphQL.newGraphQL(schema).queryExecutionStrategy(new DirectiveAsyncExecutionStrategy()).build();
GraphQLQueryInvoker queryInvoker = GraphQLQueryInvoker.newBuilder().withExecutionStrategyProvider(new DefaultExecutionStrategyProvider(new DirectiveAsyncExecutionStrategy())).build();
servlet = SimpleGraphQLHttpServlet.newBuilder(schema).withQueryInvoker(queryInvoker).build();
}