graphql 实现自定义指令(Directive) @dateFormat

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 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();

    }

 

转载于:https://my.oschina.net/u/560755/blog/3030933

你可能感兴趣的:(graphql 实现自定义指令(Directive) @dateFormat)