calculator.engine.ExecutionEngineStateParser Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package calculator.engine;
import calculator.engine.annotation.Internal;
import calculator.engine.metadata.Directives;
import calculator.engine.metadata.FetchSourceTask;
import graphql.analysis.QueryVisitor;
import graphql.analysis.QueryVisitorFieldEnvironment;
import graphql.analysis.QueryVisitorFragmentSpreadEnvironment;
import graphql.analysis.QueryVisitorInlineFragmentEnvironment;
import graphql.com.google.common.base.Objects;
import graphql.language.Directive;
import graphql.util.TraverserContext;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import static calculator.common.CommonUtil.getArgumentFromDirective;
import static calculator.common.GraphQLUtil.isInList;
import static calculator.common.GraphQLUtil.isListNode;
import static calculator.common.GraphQLUtil.parentPathList;
import static calculator.common.GraphQLUtil.pathForTraverse;
import static calculator.engine.metadata.Directives.INCLUDE_BY;
import static calculator.engine.metadata.Directives.SKIP_BY;
@Internal
public class ExecutionEngineStateParser implements QueryVisitor {
private final ExecutionEngineState.Builder engineStateBuilder = new ExecutionEngineState.Builder();
public ExecutionEngineState getExecutionEngineState() {
return engineStateBuilder.build();
}
@Override
public void visitField(QueryVisitorFieldEnvironment environment) {
if (environment.getTraverserContext().getPhase() != TraverserContext.Phase.ENTER) {
return;
}
determineContainSkipByOrIncludeBy(environment.getField().getDirectives());
List directives = environment.getField().getDirectives(Directives.FETCH_SOURCE.getName());
if (directives != null && !directives.isEmpty()) {
// non-repeatable directive
Directive nodeDir = directives.get(0);
String sourceName = getArgumentFromDirective(nodeDir, "name");
String sourceConvert = getArgumentFromDirective(nodeDir, "sourceConvert");
ArrayList topTaskPathList = new ArrayList<>();
ArrayList queryTaskPathList = new ArrayList<>();
parseFetchSourceInfo(sourceName, true, sourceConvert, environment, topTaskPathList, queryTaskPathList);
// traverserContext is shared in a visitor-operation.
environment.getTraverserContext().setAccumulate(null);
engineStateBuilder.topTaskList(sourceName, topTaskPathList);
engineStateBuilder.queryTaskList(sourceName, queryTaskPathList);
}
}
/**
* 获取 @fetchSource 注释的节点相关数据保存在 ExecutionEngineState 中:
* 1. 代表该节点的 FetchSourceTask;
* 2. 该节点所代表的异步任务结束所依赖的父节点列表 topTaskPathList ;
* 3. topTask节点的父亲节点列表——这些节点失败则topTaskPathList中所有的异步任务都不会执行。
*
* @param isAnnotatedNode 是否是递归调用该方法,即是否是被 @fetchSource 注解的节点
* @param sourceConvert 获取 fetchSource 后进行数据转换的表达式
* @param visitorEnv 请求变量
* @param topTaskPathList @fetchSource 注解的节点完成所依赖的顶层节点。
* @param queryTaskPathList 顶层节点的父亲节点。这部分节点如果解析失败,则获取@fetchSource值的异步任务不会执行。
*/
private void parseFetchSourceInfo(String sourceName,
boolean isAnnotatedNode,
String sourceConvert,
QueryVisitorFieldEnvironment visitorEnv,
ArrayList topTaskPathList,
ArrayList queryTaskPathList) {
String fieldFullPath = pathForTraverse(visitorEnv);
// step_1边界条件,如果是topTaskNode,则直接包装、返回
if (!isInList(visitorEnv)) {
topTaskPathList.add(fieldFullPath);
// 对于已经解析过、放到 taskByPath 的任务不可以在重复创建任务
if (visitorEnv.getTraverserContext().getNewAccumulate() == null) {
FetchSourceTask task = FetchSourceTask.newFetchSourceTask()
.sourceName(sourceName)
.isAnnotatedNode(isAnnotatedNode)
.isListType(isListNode(visitorEnv))
.isInList(false)
.isTopTask(true)
.taskFuture(new CompletableFuture<>())
.mapper(sourceConvert)
.resultKey(visitorEnv.getField().getResultKey())
.build();
visitorEnv.getTraverserContext().setAccumulate(task);
engineStateBuilder.fetchSourceTask(fieldFullPath, task);
}
ArrayList queryPathList = parentPathList(visitorEnv);
for (String queryPath : queryPathList) {
queryTaskPathList.add(queryPath);
Supplier queryTaskSupplier = () -> FetchSourceTask.newFetchSourceTask()
.sourceName(null)
.isAnnotatedNode(false)
.isListType(false)
.isInList(false)
.isTopTask(false)
.taskFuture(new CompletableFuture<>())
.build();
engineStateBuilder.taskByPathIfAbsent(queryPath, queryTaskSupplier);
}
return;
}
// 先递归解析父节点的原因:在创建自节点对应的NodeTask时需要设置parentTask,
// 并将当前节点代表的任务设置为parentTask的子任务。
parseFetchSourceInfo(
null, false, null,
visitorEnv.getParentEnvironment(), topTaskPathList, queryTaskPathList
);
// 递归执行该逻辑,因此 topTaskPathList 中的节点顺序也是从上到下的
topTaskPathList.add(fieldFullPath);
FetchSourceTask parentTask = visitorEnv.getParentEnvironment().getTraverserContext().getNewAccumulate();
FetchSourceTask currentTask;
// 对于 list 中父子字段都有 @fetchSource 的情况,是否会判断为null
if (visitorEnv.getTraverserContext().getNewAccumulate() == null) {
currentTask = FetchSourceTask.newFetchSourceTask()
.sourceName(sourceName)
.isAnnotatedNode(isAnnotatedNode)
.isListType(isListNode(visitorEnv))
.isInList(true)
.isTopTask(false)
.taskFuture(new CompletableFuture<>())
.resultKey(visitorEnv.getField().getResultKey())
.mapper(sourceConvert)
.build();
visitorEnv.getTraverserContext().setAccumulate(currentTask);
engineStateBuilder.taskByPathIfAbsent(fieldFullPath, currentTask);
} else {
// 对于 [list-a,[b,[c,d]]] 这种情况,先解析c、然后解析d的时候递归会执行到这里
currentTask = visitorEnv.getTraverserContext().getNewAccumulate();
}
parentTask.addChildrenTaskList(currentTask);
}
@Override
public void visitInlineFragment(QueryVisitorInlineFragmentEnvironment visitorEnvironment) {
if (visitorEnvironment.getTraverserContext().getPhase() != TraverserContext.Phase.ENTER) {
return;
}
determineContainSkipByOrIncludeBy(visitorEnvironment.getInlineFragment().getDirectives());
}
@Override
public void visitFragmentSpread(QueryVisitorFragmentSpreadEnvironment visitorEnvironment) {
if (visitorEnvironment.getTraverserContext().getPhase() != TraverserContext.Phase.ENTER) {
return;
}
determineContainSkipByOrIncludeBy(visitorEnvironment.getFragmentSpread().getDirectives());
}
/**
* Determine whether the directives contain skipBy or includeBy.
*/
private void determineContainSkipByOrIncludeBy(List directives) {
if (engineStateBuilder.isContainSkipByOrIncludeBy()) {
return;
}
boolean containSkipByOrIncludeBy = directives.stream().anyMatch(
directive -> Objects.equal(SKIP_BY.getName(), directive.getName()) || Objects.equal(INCLUDE_BY.getName(), directive.getName())
);
if (containSkipByOrIncludeBy) {
engineStateBuilder.containSkipByOrIncludeBy();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy