1.先看下原生效果java
query:nginx
{ mediaInfoList(limit: 1) { createTime dateArray } }
result:git
{ "data": { "mediaInfoList": [ { "createTime": "2019-01-24T22:52:45", "dateArray": [ "2019-04-01T10:03:00.394Z", "111111", 99999 ] } ] } }
2.實現效果github
query:apache
{ mediaInfoList(limit: 1) { createTime @dateFormat(pattern: "yyyy-MM-dd HH:mm:ss") dateArray @dateFormat(pattern: "yyyy-MM") } }
result:json
{ "data": { "mediaInfoList": [ { "createTime": "2019-01-24 22:52:45", "dateArray": [ "2019-04", "111111", 99999 ] } ] } }
3.關鍵代碼 app
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<Object, Map<String, Annotation>> 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<String, Annotation> getDirectiveAnnotation(GraphQLFieldDefinition fieldDefinition) { Map<String, Annotation> 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<String, Annotation> 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<ExecutionResult> completeValueForScalar(ExecutionContext executionContext, ExecutionStrategyParameters parameters, GraphQLScalarType scalarType, Object result) { Object serialized = null; Field field = parameters.getExecutionStepInfo().getField(); GraphQLFieldDefinition fieldDefinition = parameters.getExecutionStepInfo().getFieldDefinition(); Map<String, Annotation> directives = getDirectiveAnnotation(fieldDefinition); List<Directive> list = field.getDirectives(); AtomicReference reference = new AtomicReference(null); boolean completion = false; /** * client directive 優先 */ if (CollectionUtils.isNotEmpty(list)) { try { for (Directive directive : list) { Map<String, Object> 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<String, Annotation> 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<Directive> 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<String, Object> 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<String, Object> 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<String, Object> getDirectiveArgs(Directive directive) { Map<String, Object> result = new HashMap<>(); if (directive == null) return result; List<Argument> 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(); }