如何使用graphql-java扩展响应?

vlurs2pr  于 2023-09-29  发布在  Java
关注(0)|答案(4)|浏览(122)

我的GraphQL响应必须遵循特定的格式

{
    data:{}
    errors:[{}]
    extensions:{}
}

然而,我不确定如何从我的方法扩展响应。我使用的是graphql-spring- Boot ,它可以拉入graphql-java、graphql-java-tools和graphql-java-servlet。
我知道查询/变异方法的结果将被 Package 在数据对象中,如果抛出任何异常,它们将被 Package 在错误中。
如果我有一个GraphQL Schema定义为

type Query {
    someQuery(input: String!) : String!
}

和相应的Java方法

public String someQuery(String input) {
    return "Hello, world!";
}

GraphQL响应将是

{
    data: { "Hello, world!"}
}

我想知道如何向GraphQL响应添加扩展,以便输出如下:

{
    data: {"Hello, world!"}
    extensions: { <something>}
}
6ioyuze2

6ioyuze21#

我发现返回extensions的最好方法是实现SimpleInstrumentation的子类,它覆盖instrumentExecutionResult(代码部分来自graphql-java的TracingInstrumentation):

@Override
public CompletableFuture<ExecutionResult> instrumentExecutionResult(
        ExecutionResult executionResult,
        InstrumentationExecutionParameters parameters) {
    Map<Object, Object> currentExt = executionResult.getExtensions();
    Map<Object, Object> newExtensionMap = new LinkedHashMap<>();
    newExtensionMap.putAll(currentExt == null ? Collections.emptyMap() : currentExt);
    newExtensionMap.put("MyExtensionKey", myExtensionValue);

    return CompletableFuture.completedFuture(
        new ExecutionResultImpl(
            executionResult.getData(), 
            executionResult.getErrors(), 
            newExtensionMap));
}

在设置GraphQL示例时,然后将instrumentation类的示例传入:

GraphQL graphQL = GraphQL
        .newGraphQL(schema)
        .instrumentation(new MyInstrumentation())
        .build()

(Not完全确定graphql-spring-boot是如何处理的,但可以想象有一些方法可以配置@AutowireGraphQL示例?graphql-java-servlet中的InstrumentationProvider可能就是您要使用的)

kokeuurv

kokeuurv2#

好吧,在这上面花了20个小时。终于想通了。需要大量的试验和错误。此外,我们肯定没有适当的/足够的文件。
虽然我的解决方案回答了这个问题,但它也回答了一个额外的问题,即->如何使扩展对每个查询都是动态的?
这意味着,所有3个(数据,错误和扩展)在响应中都是动态的。
我的用例-如何在扩展中为每个查询添加日志?
答案-(注意-下面的代码包含lombok注解)
第一步-创建您的插装类插装状态类

//This is the state class . 
//State is the first thing created whenever a graphql query is run . 
//We will embed our Logs object here .
@Builder
class LogInstrumentationState implements InstrumentationState {
    @Getter
    @Setter
    public LogsDto logsDto;
}

//This is the instrumentation class that will be used to create the graphql object .
//The overridden methods are different stages in the graphql query execution 
@Builder
public class LogsInstrumentation extends SimpleInstrumentation {

  //First stage in graphql query execution .
  //Object for our custom state class object is created here . 
  @Override
  public InstrumentationState createState() {
    return LogInstrumentationState.builder().build();
  }

   //Second Stage in graphql query execution
   //Reference of initialized Logs object in the main code flow is passed here . 
   //This reference is stored in our custom state class's object .
  @Override
  public ExecutionInput instrumentExecutionInput(ExecutionInput executionInput,
                                                 InstrumentationExecutionParameters parameters) {
    LogsDto logsDto = (LogsDto) executionInput.getExtensions().get("logs");

    LogInstrumentationState logInstrumentationState = parameters.getInstrumentationState();
    logInstrumentationState.setLogsDto(logsDto);
    return super.instrumentExecutionInput(executionInput, parameters);
  }

  //This is the last stage in the graphql query execution .
  //Logs are taken from the custom container and added into extensions . 
  @Override
  public CompletableFuture<ExecutionResult> instrumentExecutionResult(
      ExecutionResult executionResult, InstrumentationExecutionParameters parameters) {

    Map<Object, Object> newExtensionMap = getExtensionsMap(executionResult,parameters);

    return CompletableFuture.completedFuture(
        new ExecutionResultImpl(
            executionResult.getData(),
            executionResult.getErrors(),
            newExtensionMap));
  }

  //Helper function
  public Map<Object, Object> getExtensionsMap(ExecutionResult executionResult, InstrumentationExecutionParameters parameters) {
    Map<Object, Object> currentExt = executionResult.getExtensions();
    Map<Object, Object> newExtensionMap = new LinkedHashMap<>();
    newExtensionMap.putAll(currentExt == null ? Collections.emptyMap() : currentExt);
    LogsDto logsDto =
        ((LogInstrumentationState)parameters.getInstrumentationState()).getLogsDto();
    newExtensionMap.put(ControllerConstants.LOGS, logsDto);
    return newExtensionMap;
  }

}

步骤2 -创建graphql对象-

GraphQL graphQl = GraphQL.newGraphQL(graphqlSchema).instrumentation(LogsInstrumentation.builder().build())
        .build();

步骤3 -创建执行输入。这是将动态Log对象传递到LogsInstrumentation类的地方。

var executionInput = ExecutionInput.newExecutionInput()
          .query(...)
          .variables(...)
          .operationName(...)
          .extensions(Map.of("logs",logsDto))
          .dataLoaderRegistry(...)
          .graphQLContext(graphqlContext).build();

      ExecutionResult executionResult = graphQl.execute(executionInput);

步骤4 -这是如何在查询完成后获取扩展名。

Map<Object, Object> extensions = executionResult.getExtensions();
LogsDto logsDto = (LogsDto) extensions.get("logs");

My source

mrzz3bfm

mrzz3bfm3#

从graphql-java 21.0开始,可以直接在DataFetcherResult上设置extensions:https://github.com/graphql-java/graphql-java/blob/v21.0/src/main/java/graphql/execution/DataFetcherResult.java#L205
添加了here。如果你还在使用旧版本的graphql-java,你可以从DataFetchingEnvironment中得到它,如下所示:

ExtensionsBuilder extensionsBuilder = dfe.getGraphQlContext().get<ExtensionsBuilder>(ExtensionsBuilder.class);
if (extensionsBuilder != null) {
    extensionsBuilder.addValue("yourKey", yourValue);
}
dgiusagp

dgiusagp4#

你可以实现GraphQLError,其中可以添加额外的错误属性。

相关问题