Skip to content

Commit 5db60d7

Browse files
williambaileypaulofaria
authored andcommitted
Investigate having support for union (#10)
* Investigate having support for union * Add some extra union<Type> functions * Update StarWarsTests to include a working Union example
1 parent 4b50a6d commit 5db60d7

File tree

6 files changed

+166
-1
lines changed

6 files changed

+166
-1
lines changed

Sources/Graphiti/Schema/Schema.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,50 @@ public final class SchemaBuilder<Root, Context> {
117117
map(Type.self, to: interfaceType)
118118
}
119119

120+
public func union<Type>(
121+
type: Type.Type,
122+
members: [Any.Type]
123+
) throws {
124+
let name = fixName(String(describing: Type.self))
125+
try union(name: name, type: type, members: members)
126+
}
127+
128+
public func union<Type>(
129+
name: String,
130+
type: Type.Type,
131+
members: [Any.Type]
132+
) throws {
133+
try union(name: name, type: type) { builder in
134+
builder.types = members
135+
}
136+
}
137+
138+
public func union<Type>(
139+
type: Type.Type,
140+
build: (UnionTypeBuilder<Type>) throws -> Void
141+
) throws {
142+
let name = fixName(String(describing: Type.self))
143+
try union(name: name, type: type, build: build)
144+
}
145+
146+
public func union<Type>(
147+
name: String,
148+
type: Type.Type,
149+
build: (UnionTypeBuilder<Type>) throws -> Void
150+
) throws {
151+
let builder = UnionTypeBuilder<Type>()
152+
try build(builder)
153+
154+
let interfaceType = try GraphQLUnionType(
155+
name: name,
156+
description: builder.description,
157+
resolveType: builder.resolveType,
158+
types: builder.types.map { try getObjectType(from: $0) }
159+
)
160+
161+
map(Type.self, to: interfaceType)
162+
}
163+
120164
public func `enum`<Type : OutputType>(
121165
type: Type.Type,
122166
build: (EnumTypeBuilder<Type>) throws -> Void
@@ -517,3 +561,4 @@ public struct Schema<Root, Context> {
517561
)
518562
}
519563
}
564+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import GraphQL
2+
3+
public final class UnionTypeBuilder<Type> {
4+
public var description: String? = nil
5+
public var resolveType: GraphQLTypeResolve? = nil
6+
public var types: [Any.Type] = []
7+
}
8+

Tests/GraphitiTests/StarWarsTests/StarWarsData.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,19 @@ struct Droid : Character {
6060
let primaryFunction: String
6161
}
6262

63+
protocol SearchResult {}
64+
extension Planet: SearchResult {}
65+
extension Human: SearchResult {}
66+
extension Droid: SearchResult {}
67+
6368
var tatooine = Planet(id:"10001", name: "Tatooine", diameter: 10465, rotationPeriod: 23, orbitalPeriod: 304,residents: [Human]() )
6469
var alderaan = Planet(id: "10002", name: "Alderaan", diameter: 12500, rotationPeriod: 24, orbitalPeriod: 364, residents: [Human]())
6570

71+
let planetData: [String: Planet] = [
72+
"10001": tatooine,
73+
"10002": alderaan,
74+
]
75+
6676
let luke = Human(
6777
id: "1000",
6878
name: "Luke Skywalker",
@@ -193,3 +203,30 @@ func getSecretBackStory() throws -> String? {
193203
throw Secret(description: "secretBackstory is secret.")
194204
}
195205

206+
/**
207+
* Allos us to query for either a Human, Droid, or Planet.
208+
*/
209+
func search(for value: String) -> [SearchResult] {
210+
let value = value.lowercased()
211+
var result: [SearchResult] = []
212+
213+
result.append(contentsOf:
214+
planetData.filter({
215+
return $1.name.lowercased().range(of:value) != nil
216+
}).map({ $1 })
217+
)
218+
219+
result.append(contentsOf:
220+
humanData.filter({
221+
return $1.name.lowercased().range(of:value) != nil
222+
}).map({ $1 })
223+
)
224+
225+
result.append(contentsOf:
226+
droidData.filter({
227+
return $1.name.lowercased().range(of:value) != nil
228+
}).map({ $1 })
229+
)
230+
231+
return result
232+
}

Tests/GraphitiTests/StarWarsTests/StarWarsIntrospectionTests.swift

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,12 @@ class StarWarsIntrospectionTests : XCTestCase {
4040
[
4141
"name": "Query",
4242
],
43+
[
44+
"name": "SearchResult",
45+
],
4346
[
4447
"name": "String",
4548
],
46-
4749
[
4850
"name": "__Directive",
4951
],
@@ -125,6 +127,9 @@ class StarWarsIntrospectionTests : XCTestCase {
125127
[
126128
"name": "Query",
127129
],
130+
[
131+
"name": "SearchResult",
132+
],
128133
[
129134
"name": "String",
130135
],
@@ -467,6 +472,24 @@ class StarWarsIntrospectionTests : XCTestCase {
467472
],
468473
],
469474
],
475+
[
476+
"name": "search",
477+
"args": [
478+
[
479+
"name": "query",
480+
"description": "text to find",
481+
"type": [
482+
"name": nil,
483+
"kind": "NON_NULL",
484+
"ofType": [
485+
"name": "String",
486+
"kind": "SCALAR",
487+
]
488+
],
489+
"defaultValue": nil,
490+
],
491+
],
492+
],
470493
],
471494
],
472495
],

Tests/GraphitiTests/StarWarsTests/StarWarsQueryTests.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,38 @@ class StarWarsQueryTests : XCTestCase {
490490
let result = try schema.execute(request: query)
491491
XCTAssertEqual(result, expected)
492492
}
493+
494+
func testSearchQuery() throws {
495+
let query = "query {" +
496+
" search(query: \"o\") {" +
497+
" ... on Planet {" +
498+
" name " +
499+
" diameter " +
500+
" }" +
501+
" ... on Human {" +
502+
" name " +
503+
" }" +
504+
" ... on Droid {" +
505+
" name " +
506+
" primaryFunction " +
507+
" }" +
508+
" }" +
509+
"}"
510+
511+
let expected: Map = [
512+
"data": [
513+
"search": [
514+
[ "name": "Tatooine", "diameter": 10465 ],
515+
[ "name": "Han Solo" ],
516+
[ "name": "Leia Organa" ],
517+
[ "name": "C-3PO", "primaryFunction": "Protocol" ],
518+
],
519+
],
520+
]
521+
522+
let result = try starWarsSchema.execute(request: query)
523+
XCTAssertEqual(result, expected)
524+
}
493525
}
494526

495527
extension StarWarsQueryTests {

Tests/GraphitiTests/StarWarsTests/StarWarsSchema.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,15 @@ let starWarsSchema = try! Schema<NoRoot, NoContext> { schema in
252252
)
253253
}
254254

255+
/**
256+
* A search result can be a include a number of different types.
257+
*
258+
* This implements the following type system shorthand:
259+
*
260+
* union SearchResult = Planet | Human | Droid
261+
*/
262+
try schema.union(type: SearchResult.self, members: [ Planet.self, Human.self, Droid.self ])
263+
255264
/**
256265
* This is the type that will be the root of our query, and the
257266
* entry point into our schema. It gives us the ability to fetch
@@ -264,6 +273,7 @@ let starWarsSchema = try! Schema<NoRoot, NoContext> { schema in
264273
* hero(episode: Episode): Character
265274
* human(id: String!): Human
266275
* droid(id: String!): Droid
276+
* search(query: String!): SearchResult
267277
* }
268278
*/
269279
try schema.query { query in
@@ -298,6 +308,16 @@ let starWarsSchema = try! Schema<NoRoot, NoContext> { schema in
298308
try query.field(name: "droid") { (_, arguments: DroidArguments, _, _) in
299309
getDroid(id: arguments.id)
300310
}
311+
312+
struct SearchArguments : Arguments {
313+
let query: String
314+
static let descriptions = ["query": "text to find"]
315+
}
316+
317+
try query.field(name: "search") { (_, arguments: SearchArguments, _, _) in
318+
search(for: arguments.query)
319+
}
320+
301321
}
302322

303323
schema.types = [Human.self, Droid.self]

0 commit comments

Comments
 (0)