Skip to content

Commit edcba6e

Browse files
feat: Adds oneOf input object directive
1 parent 3f15434 commit edcba6e

File tree

4 files changed

+97
-6
lines changed

4 files changed

+97
-6
lines changed

Package.resolved

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ let package = Package(
77
.library(name: "Graphiti", targets: ["Graphiti"]),
88
],
99
dependencies: [
10-
.package(url: "https://github.com/GraphQLSwift/GraphQL.git", from: "2.9.2"),
10+
.package(url: "https://github.com/GraphQLSwift/GraphQL.git", from: "2.10.0"),
1111
],
1212
targets: [
1313
.target(name: "Graphiti", dependencies: ["GraphQL"]),

Sources/Graphiti/Input/Input.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ public final class Input<
88
Resolver,
99
Context
1010
> {
11+
let isOneOf: Bool
1112
let fields: [InputFieldComponent<InputObjectType, Context>]
1213

1314
override func update(typeProvider: SchemaTypeProvider, coders _: Coders) throws {
1415
let inputObjectType = try GraphQLInputObjectType(
1516
name: name,
1617
description: description,
17-
fields: fields(typeProvider: typeProvider)
18+
fields: fields(typeProvider: typeProvider),
19+
isOneOf: isOneOf
1820
)
1921

2022
try typeProvider.add(type: InputObjectType.self, as: inputObjectType)
@@ -38,8 +40,10 @@ public final class Input<
3840
init(
3941
type _: InputObjectType.Type,
4042
name: String?,
43+
isOneOf: Bool,
4144
fields: [InputFieldComponent<InputObjectType, Context>]
4245
) {
46+
self.isOneOf = isOneOf
4347
self.fields = fields
4448
super.init(
4549
name: name ?? Reflection.name(for: InputObjectType.self),
@@ -52,18 +56,20 @@ public extension Input {
5256
convenience init(
5357
_ type: InputObjectType.Type,
5458
as name: String? = nil,
59+
isOneOf: Bool = false,
5560
@InputFieldComponentBuilder<InputObjectType, Context> _ fields: ()
5661
-> InputFieldComponent<InputObjectType, Context>
5762
) {
58-
self.init(type: type, name: name, fields: [fields()])
63+
self.init(type: type, name: name, isOneOf: isOneOf, fields: [fields()])
5964
}
6065

6166
convenience init(
6267
_ type: InputObjectType.Type,
6368
as name: String? = nil,
69+
isOneOf: Bool = false,
6470
@InputFieldComponentBuilder<InputObjectType, Context> _ fields: ()
6571
-> [InputFieldComponent<InputObjectType, Context>]
6672
) {
67-
self.init(type: type, name: name, fields: fields())
73+
self.init(type: type, name: name, isOneOf: isOneOf, fields: fields())
6874
}
6975
}

Tests/GraphitiTests/DirectiveTests/DirectiveTests.swift

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,89 @@ class DirectiveTests: XCTestCase {
7474

7575
XCTAssertEqual(response, expected)
7676
}
77+
78+
func testOneOfAcceptsGoodValue() throws {
79+
try XCTAssertEqual(
80+
OneOfAPI().execute(
81+
request: """
82+
query {
83+
test(input: {a: "abc"}) {
84+
a
85+
b
86+
}
87+
}
88+
""",
89+
context: NoContext(),
90+
on: group
91+
).wait(),
92+
GraphQLResult(
93+
data: [
94+
"test": [
95+
"a": "abc",
96+
"b": .null,
97+
],
98+
]
99+
)
100+
)
101+
}
102+
103+
func testOneOfRejectsBadValue() throws {
104+
try XCTAssertEqual(
105+
OneOfAPI().execute(
106+
request: """
107+
query {
108+
test(input: {a: "abc", b: 123}) {
109+
a
110+
b
111+
}
112+
}
113+
""",
114+
context: NoContext(),
115+
on: group
116+
).wait().errors[0].message,
117+
#"OneOf Input Object "TestInputObject" must specify exactly one key."#
118+
)
119+
}
120+
121+
struct OneOfAPI: API {
122+
struct TestObject: Codable {
123+
let a: String?
124+
let b: Int?
125+
}
126+
127+
struct TestInputObject: Codable {
128+
let a: String?
129+
let b: Int?
130+
}
131+
132+
struct TestArguments: Codable {
133+
let input: TestInputObject
134+
}
135+
136+
struct OneOfResolver {
137+
func test(context _: NoContext, arguments: TestArguments) -> TestObject {
138+
return TestObject(a: arguments.input.a, b: arguments.input.b)
139+
}
140+
}
141+
142+
let resolver = OneOfResolver()
143+
144+
let schema = try! Schema<OneOfResolver, NoContext> {
145+
Type(TestObject.self) {
146+
Field("a", at: \.a)
147+
Field("b", at: \.b)
148+
}
149+
150+
Input(TestInputObject.self, isOneOf: true) {
151+
InputField("a", at: \.a)
152+
InputField("b", at: \.b)
153+
}
154+
155+
Query {
156+
Field("test", at: OneOfResolver.test, as: TestObject.self) {
157+
Argument("input", at: \.input)
158+
}
159+
}
160+
}
161+
}
77162
}

0 commit comments

Comments
 (0)