在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,并进行相应的设置,但这些都不能在文档中输出,感觉有点遗憾)