我可以安全地从GraphQLResolver回调GraphQLQueryResolver吗? 示例1:查询示例2:部分更新的变异解决方案

问题描述

我的问题是:实现 resolution 方法时最好的是什么?是直接调用数据存储库还是调用主解析器又称一个实现GraphQLQueryResolver的方法(只要它具有适当的方法)?换句话说(请参见下面的示例),在回调主解析器 时,DataFetchingEnvironment是否已正确调整/设置?

注意:如果您不熟悉Resolvers与GraphQL Java Tools的工作方式,那么让我看看@ https://www.graphql-java-kickstart.com/tools/schema-definition/

现在是示例。

在带有GraphQL Java工具(具有graphql-spring-boot-starter依赖项)的Spring Boot应用程序中,让我们拥有以下架构:

type User {
  id: ID
  name: String
  company: Company
}

type Company {
  id: ID
  name: String
}

具有匹配的POJO或实体(省略了getter / setter):

class User {

  private Long id;
  private String name;
  private Long idCompany;

}

class Company {

  private Long id;
  private String name;

}

和这些 resolvers (注意:UserRepository和CompanyRepository是您常用的DAO / Repository类,由Spring Data(JPA)支持,其他某种东西或您自己的自定义实现,无论...):

QueryResolver implements GraphQLQueryResolver {

  @Autowired
  private UserRepository userRepository;

  @Autowired
  private CompanyRepository companyRepository;

  public User user(String id) {
    return userRepository.findById(id);
  }

  public Company company(String idCompany) {
    return companyRepository.findById(idCompany);
  }

}

UserResolver implements GraphQLResolver<User> {

  @Autowired
  private CompanyRepository companyRepository;

  public Company company(User user) {
    return companyRepository.findById(user.getIdCompany());
  }

  // ...or should I do:

  @Autowired
  private QueryResolver queryResolver;

  public Company company(User user) {
    return queryResolver.company(user.getIdCompany());
  }

}

在每种方法的末尾添加DataFetchingEnvironment environment并在执行对各种(数据)存储库的调用之前使用DataFetchingEnvironment时,这(更)有意义。

继续上面的示例,这样做是否正确(即UserResolver implements GraphQLResolver<User> { @Autowired private QueryResolver queryResolver; public Company company(User user,DataFetchingEnvironment environment) { return queryResolver.company(user.getIdCompany(),environment); } } 在再次传输到主QueryResolver时是否正确填充)?

credential = ManagedIdentityCredential()
service_client = DataLakeServiceClient(account_url="{}://{}.dfs.core.windows.net".format("https",storage_account_name),credential=credential)

解决方法

简短回答

您可以将解析程序调用委派给服务层,但不要在解析程序/服务之间传递DataFecthingEnvironment。无法正确填充。

长答案

这是不安全的,并且可能导致难以确定的错误和数据丢失。

从正在执行的graphql查询/突变中填充了DataFetchingEnvironment,并且您希望您的resolver方法中的DataFetchingEnvironment与所调用的resolver方法一致。

考虑以下架构:

type Movie {
  id: ID!
  title: String!
  rating: String
  actors: [Actor]
}

type Actor {
  id: ID!
  name: String!
  role: String
}

input ActorUpdateInput {
  id: ID!
  name: String
  role: String
}

type Query {
  #Search movies with a specified Rating
  searchMovie(name: movieTitle,rating: String): Book
  #Search R-rated movies
  searchRRatedMovie(name: movieTitle): Book
}

type Mutation {
  #Update a movie and its actors
  updateMovie(id:Id!,title: String,actors: [ActorUpdateInput]): Movie
  #Update an actor
  updateActor(input: ActorUpdateInput!): Actor
}

示例1:查询

query {
  searchRRatedMovie(name: "NotRRatedMovie") {
    title
  }
}

电影“ NotRRatedMovie”未获得R级评级,我们可以期望此查询返回空数据。

现在,下面的实现将DataFetchingEnvironment从searchRRatedMovie传递到searchMovie查询解析器实现。

public class QueryResolver  {

  @Autowired
  MovieRepository repository;

  public Movie searchRRatedMovie(String title,DataFetchingEnvironment environment) {
    return this.searchMovie(name,"R",environment);
  }

  public Movie searchMovie(String title,String rating,DataFetchingEnvironment environment) {
    if(!environment.containsArgument("rating")) {
      //if the rating argument was omitted from the query
      return repository.findByTitle(title);
    } else if(rating == null) {
      //rating is an argument but was set to null (ie. the user wants to retrieve all the movies without any rating)
      return repository.findByTitleAndRating(title,null);
    } else {
      repository.findByNameAndTitle(name,rating);
    }
  }

}

这看起来不错,但查询不会返回null。

第一个解析器将呼叫searchRRatedMovie("NotRRatedMovie",environment)。该环境不包含"rating"参数。到达该行时:if(!environment.containsArgument("rating")) { "rating"参数不存在,它将进入if语句,返回repository.findByTitle("NotRRatedMovie")而不是预期的repository.findByTitleAndRating("NotRRatedMovie","R")

示例2:部分更新的变异

我们可以使用DataFetchingEnvironment参数在突变中实现部分更新:如果参数为null,则需要DataFetchingEnvironment参数来告诉我们参数是否为null,因为它被设置为{{ 1}}(即,该突变应将基础值更新为null),或者因为根本没有设置它(即,该突变不应将基础值更新)。

null

在这里,updateActor解析器期望输入参数(它将与updateActor突变定义匹配)。因为我们通过了一个错误填充的环境,所以实现失败了。

解决方案

没有DataFetchinEnvironment的部分更新

如果要实现部分更新,则可以不使用DataFecthingEnvironment来实现,就像我在此注释中所做的:https://github.com/graphql-java-kickstart/graphql-java-tools/issues/141#issuecomment-560938020

在将DataFetchingEnvironment传递给下一个解析器之前,先对其进行重建

如果您确实需要DataFetchingEnvironment,则仍可以构建一个新的传递给下一个解析器。这可能会更加困难且容易出错,但是您可以看看如何在ExecutionStrategy.java https://github.com/graphql-java/graphql-java/blob/master/src/main/java/graphql/execution/ExecutionStrategy.java#L246

中创建原始DataFetchingEnvironment。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...