All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.spring.boxes.dollar.support.graphql.GraphqlFetchers Maven / Gradle / Ivy

The newest version!
package com.spring.boxes.dollar.support.graphql;

import static com.spring.boxes.dollar.JSONUtils.toJSON;
import static com.spring.boxes.dollar.support.graphql.GraphqlArguments.*;
import static com.spring.boxes.dollar.support.graphql.GraphqlUtils.*;
import static graphql.schema.DataFetchingEnvironmentImpl.newDataFetchingEnvironment;
import static java.util.stream.Collectors.toList;

import java.math.BigInteger;
import java.util.*;

import com.google.common.collect.Lists;
import com.netflix.graphql.dgs.DgsDataFetchingEnvironment;
import com.spring.boxes.dollar.JSONUtils;
import com.spring.boxes.dollar.enums.FetchTypeEnum;
import com.spring.boxes.dollar.support.graphql.model.FetchDirectiveArgument;
import graphql.language.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.collections4.MapUtils;

import com.google.common.collect.Maps;
import com.spring.boxes.dollar.StringUtils;
import com.spring.boxes.dollar.support.MoreStream;
import com.spring.boxes.dollar.support.aviator.AviatorInstance;
import com.spring.boxes.dollar.support.graphql.directive.DefaultDirective;
import com.spring.boxes.dollar.support.graphql.instrument.NestFutureTask;
import com.spring.boxes.dollar.support.graphql.instrument.NestInstrumentationState;

import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import org.springframework.context.ApplicationContext;

@Slf4j
public class GraphqlFetchers {

    // 组装Directive指令
    public static Directive toAviatorDirective(String supply) {
        if (StringUtils.isBlank(supply)) {
            return null;
        }
        return Directive.newDirective()
                .name(DefaultDirective.aviator.getName())
                .argument(Argument.newArgument("supply", StringValue.newStringValue(supply).build()).build())
                .build();
    }

    // 组装数据Fetch指令
    public static Directive toFetcherDirective(FetchDirectiveArgument fetchDirectiveArgument) {
        if (Objects.isNull(fetchDirectiveArgument)) {
            return null;
        }
        List arguments = Lists.newArrayList(
                Argument.newArgument("type",
                        IntValue.newIntValue().value(BigInteger.valueOf(fetchDirectiveArgument.getType())).build())
                        .build(),
                Argument.newArgument("service",
                        StringValue.newStringValue(fetchDirectiveArgument.getService()).build())
                        .build(),
                Argument.newArgument("supply",
                        StringValue.newStringValue(toJSON(fetchDirectiveArgument.getSupply())).build())
                        .build(),
                Argument.newArgument("aviator",
                        BooleanValue.newBooleanValue(fetchDirectiveArgument.isAviator()).build())
                        .build(),
                Argument.newArgument("extract",
                        StringValue.newStringValue(toJSON(fetchDirectiveArgument.getExtract())).build())
                        .build()
        );
        return Directive.newDirective().name(DefaultDirective.fetcher.getName()).arguments(arguments).build();
    }

    @SuppressWarnings("unchecked")
    public static DataFetcher getAviatorDataFetcher(DataFetcher defaultDataFetcher, InstrumentationFieldFetchParameters parameters) {
        DataFetchingEnvironment dataFetchingEnvironment = parameters.getEnvironment();
        if (Objects.isNull(dataFetchingEnvironment)) {
            return defaultDataFetcher;
        }
        Field field = dataFetchingEnvironment.getField();
        String expression = getAviatorDirectiveArgument(dataFetchingEnvironment);
        if (StringUtils.isBlank(expression)) {
            return defaultDataFetcher;
        }
        return environment -> {
            Object oriVal = defaultDataFetcher.get(environment);
            Map variable = sourceToMap(environment);
            variable.put(environment.getField().getResultKey(), oriVal);
            log.debug("[@aviator] [{}] supply:{} argument:{}]", field.getName(), expression, variable);
            return AviatorInstance.execute(expression, variable);
        };
    }

    // 模型函数
    @SuppressWarnings("unchecked")
    public static DataFetcher getFetcherDataFetcher(ApplicationContext applicationContext, DataFetcher defaultDataFetcher, InstrumentationFieldFetchParameters parameters) {
        DataFetchingEnvironment dataFetchingEnvironment = parameters.getEnvironment();
        if (Objects.isNull(dataFetchingEnvironment)) {
            return defaultDataFetcher;
        }
        Field field = dataFetchingEnvironment.getField();
        FetchDirectiveArgument directiveArgument = getFetcherDirectiveArgument(dataFetchingEnvironment);
        if (Objects.isNull(directiveArgument)) {
            return defaultDataFetcher;
        }
        return environment -> {
            Map variables = dataFetchingEnvironment.getVariables();
            Map arguments =
                    sourceAviFetcherNewArgument(dataFetchingEnvironment, directiveArgument.getSupply(), directiveArgument.isAviator());
            log.debug("[@fetcher] [{}] variables:{}, directiveArgs:{}, fetcherArgs:{}", field.getName(), toJSON(variables), toJSON(directiveArgument), toJSON(arguments));
            DataFetchingEnvironment newEnvironment = newDataFetchingEnvironment(dataFetchingEnvironment)
                    .arguments(arguments)
                    .variables(variables).build();
            return getDataFetcherWithNewEnvironment(applicationContext, defaultDataFetcher, newEnvironment, directiveArgument);
        };
    }

    public static Object getDataFetcherWithNewEnvironment(ApplicationContext applicationContext, DataFetcher defaultDataFetcher,
                                                          DataFetchingEnvironment newEnvironment,
                                                          FetchDirectiveArgument directiveArgument) {
        DgsDataFetchingEnvironment dgsDataFetchingEnvironment = new DgsDataFetchingEnvironment(newEnvironment);

        if (FetchTypeEnum.JAVA_EXECUTOR_BEAN.getValue() == directiveArgument.getType()) {
            return GraphqlInvoker.invokeWithSpringContext(applicationContext, directiveArgument.getService(), dgsDataFetchingEnvironment);
        }
        if (FetchTypeEnum.JAVA_STANDARD_CLASS.getValue() == directiveArgument.getType()) {
            return GraphqlInvoker.invokeWithClassName(directiveArgument.getService(), dgsDataFetchingEnvironment);
        }
        return defaultDataFetcher;
    }


    @SuppressWarnings("unchecked")
    // 组装@auto标记的Fetcher并动态获取其值
    @Deprecated
    public static DataFetcher autoDirectiveDataFetcher(DataFetcher defaultDataFetcher, InstrumentationFieldFetchParameters parameters) {
        DataFetchingEnvironment dataFetchingEnvironment = parameters.getEnvironment();

        Field field = dataFetchingEnvironment.getField();
        if (Objects.isNull(field)) {
            return defaultDataFetcher;
        }
        Directive directive = MoreStream.getFirst(field.getDirectives(DefaultDirective.auto.getName()));
        if (Objects.isNull(directive)) {
            return defaultDataFetcher;
        }

        String supply = getArgumentStringValue(directive.getArgument("supply"),
                "@auto directive illegal config: @auto(supply:'')");
        Map argsMapping = StringUtils.isBlank(supply) ?
                Maps.newHashMap() : JSONUtils.fromJSON(supply, Map.class, String.class, String.class);
        ;

        NestInstrumentationState scheduleState = parameters.getInstrumentationState();
        Map> sequenceTaskByNode = scheduleState.getSequenceTaskByNode();
        Map> taskByPath = scheduleState.getTaskByPath();

        return environment -> {
            Map newArguments = new HashMap<>(dataFetchingEnvironment.getArguments());
            log.debug("[@auto] field:{}, argumentMapping:{}", field.getName(), argsMapping);

            List arguments = field.getArguments();
            ListUtils.emptyIfNull(arguments).forEach(arg -> {
                // 获取参数映射的结点
                String nodeName = MapUtils.getString(argsMapping, arg.getName());
                // 抽取依赖参数真实值
                List taskNameForNode = sequenceTaskByNode.get(nodeName);
                List> taskList = taskNameForNode.stream().map(taskByPath::get).collect(toList());
                NestFutureTask valueTask = GraphqlFuture.getValueFromTasks(taskList);
                if (valueTask.getFuture().isCompletedExceptionally()) {
                    throw new IllegalArgumentException(String.format("dataFetcher error: %s", field.getName()));
                } else {
                    Object val = valueTask.getFuture().join();
                    newArguments.put(arg.getName(), val);
                }
            });

            log.debug("[@auto] [{}] supply:{}, argument:{}", field.getName(), argsMapping, newArguments);
            // 重新赋值模板参数.
            DataFetchingEnvironment
                    newEnvironment = newDataFetchingEnvironment(dataFetchingEnvironment)
                    .arguments(newArguments).variables(newArguments).build();

            return defaultDataFetcher.get(new DgsDataFetchingEnvironment(newEnvironment));
        };
    }

    @SuppressWarnings("unchecked")
    public static Map sourceAviFetcherNewArgument(DataFetchingEnvironment defaultEnvironment,
                                                                  Map argMapping, boolean aviFun) {
        Map arguments = new HashMap<>();
        Map mappingVal = new HashMap<>();
        if (MapUtils.isNotEmpty(defaultEnvironment.getVariables())) {
            arguments.putAll(defaultEnvironment.getVariables());
            mappingVal.putAll(defaultEnvironment.getVariables());
        }
        if (MapUtils.isNotEmpty(defaultEnvironment.getArguments())) {
            arguments.putAll(defaultEnvironment.getArguments());
        }
        Map sourceMap = sourceToMap(defaultEnvironment);
        if (MapUtils.isNotEmpty(sourceMap)) {
            mappingVal.putAll(sourceMap);
        }
        // 基于表达式的参数构建
        MapUtils.emptyIfNull(argMapping).forEach((k, v) -> {
            if (v instanceof String) {
                String val = ((String) v);
                if (!val.startsWith("\\$")) {
                    val = org.apache.commons.lang3.StringUtils.substring(val, 1);
                    if (aviFun) {
                        Object argumentVal = AviatorInstance.execute(val, mappingVal);
                        arguments.put(k, argumentVal);
                    } else {
                        Object sourceVal = MapUtils.getObject(mappingVal, val);
                        arguments.put(k, sourceVal);
                    }
                } else {
                    arguments.put(k, v);
                }
            } else {
                arguments.put(k, v);
            }
        });
        return arguments;
    }


}