在GraphQL中,通过@oneOf指令来表示input类型的union

@oneOf 指令是什么

@oneOf 是一个指令,用于表达 input 类型中的字段必须选择其中一个的要求。

如果你努力实现定制指令的话,是可以实现这个的……我想问问你!其实这个正好是 GraphQL 官方规范中即将合并的指令。

 

最初,GraphQL 中原本就有 union,可以在 API 上表示只返回“任意一种类型”的输出,但对于输入却没有表达的方法。
通过 @oneOf 指令,这成为可能!

使用示例

以下是应用了 @oneOf 的用户搜索API的示例。
当在搜索条件中使用 input 指定 FindUserCondition 时,表示 id 字段或 nameContaining 字段必须指定非空值。

directive @oneOf on INPUT_OBJECT

input FindUserCondition @oneOf { # <--- ここで "oneOf" 指定
  # ユーザを ID 指定で検索する場合に指定する
  id: ID

  # ユーザを、名前の部分一致で検索する場合に指定する
  nameContaining: String
}

type Query {
  # ユーザを検索する
  findUser(by: FindUserCondition!): [User!]!
}

一个有效的查询示例。

query q1 {
  findUser(by: {
    id: "u1"
  }) {
    id
    name
  }
}

以下的规定将引发错误。
关于无效的规定模式,在前述的 PR 中已经列出。

query error1 {
  findUser(by: {
    # NG: 2 つ以上のフィールドを同時に指定している
    id: "1"
    nameContaining: "foo"
  }) {
    id
    name
  }
}

query error2 {
  findUser(by: {
    # NG: 指定したフィールドは 1 つだが値が null
    id: null
  }) {
    id
    name
  }
}

query error3 {
  findUser(by: {
    # NG: 一見 nameContaining が有効そうに見えるが、この指定方法はダメ
    id: null
    nameContaining: "foo"
  }) {
    id
    name
  }
}

你能用了吗?

这一次我正在升级 spring-graphql 的版本时注意到,根据我的观察范围,至少以下的实现中(有些甚至是从相当早之前开始),都包含了 @oneOf。

    • graphql-java (v21.2)

 

    • graphql-ruby (v2.0.14)

 

    graphql-js (v17.0.0-alpha.3)

尝试在 spring-graphql 中使用 @oneOf

为了做到这一点,我打算使用 Spring GraphQL 实现 API,并使用 @oneOf 指令进行编码。
完整的代码可以在 GitHub 上找到。

首先,从SDL开始。

directive @oneOf on INPUT_OBJECT

type User {
  id: ID!
  name: String!
}
input FindUserCondition @oneOf {
  id: ID
  nameContaining: String
}
type Query {
  findUser(by: FindUserCondition!): [User!]!
}

接下来是控制器方面。

@Controller
public class UserController {

  private final List<User> users;

  public UserController() {
    List<User> users = new ArrayList<>();
    users.add(new User("u1", "taro"));
    users.add(new User("u2", "jiro"));
    users.add(new User("u3", "saburo"));
    this.users = users;
  }

  @QueryMapping
  public Flux<User> findUser(@Argument("by") FindUserCondition condition) {
    Predicate<User> filter;
    if (condition.id() != null) {
      filter = u -> u.id().equals(condition.id());
    } else if (condition.nameContaining() != null) {
      filter = u -> u.name().contains(condition.nameContaining());
    } else {
      throw new IllegalStateException();
    }
    return Flux.fromStream(users.stream().filter(filter));
  }
}

让我动一动试试

正常系 xì)

    • リクエスト

 

    • query findUser {

 

    • findUser(by: {

 

    • id: “u1”

 

    • }) {

 

    • id

 

    • name

 

    • }

 

    • }

レスポンス
{
“data”: {
“findUser”: [
{
“id”: “u1”,
“name”: “taro”
}
]
}
}

异常系

    • リクエスト

 

    • query findUser {

 

    • findUser(by: {

 

    • id: “u1”

 

    • nameContaining: “x”

 

    • }) {

 

    • id

 

    • name

 

    • }

 

    • }

レスポンス
{
“errors”: [
{
“message”: “graphql.execution.OneOfTooManyKeysException: Exactly one key must be specified for OneOf type ‘FindUserCondition’.”,
“locations”: [
{
“line”: 2,
“column”: 3
}
],
“path”: [
“findUser”
],
“extensions”: {
“classification”: “INTERNAL_ERROR”
}
}
],
“data”: null
}

入力チェックは何も実装していませんが、ちゃんとエラーになってくれました!

作者的话

GraphQL 的可表达性通过指令而不是预留字或语法进行扩展是很有趣的。
这样一来,我希望在文档中也能输出指令的信息。
目前我在使用 SpectaQL 生成 GraphQL API 文档时不支持输出自定义指令,但如果这个趋势加速发展的话,我希望能够得到对应的支持。
(不仅仅是 @oneOf,为了认证也可以创建自定义指令,比如 @auth,并进行相应的设置,但这些都不能在文档中输出,感觉有点遗憾)

广告
将在 10 秒后关闭
bannerAds