Skip to content

Commit

Permalink
Investigate having support for union (#10)
Browse files Browse the repository at this point in the history
* Investigate having support for union

* Add some extra union<Type> functions

* Update StarWarsTests to include a working Union example
  • Loading branch information
williambailey authored and paulofaria committed May 27, 2017
1 parent 4b50a6d commit 5db60d7
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 1 deletion.
45 changes: 45 additions & 0 deletions Sources/Graphiti/Schema/Schema.swift
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,50 @@ public final class SchemaBuilder<Root, Context> {
map(Type.self, to: interfaceType)
}

public func union<Type>(
type: Type.Type,
members: [Any.Type]
) throws {
let name = fixName(String(describing: Type.self))
try union(name: name, type: type, members: members)
}

public func union<Type>(
name: String,
type: Type.Type,
members: [Any.Type]
) throws {
try union(name: name, type: type) { builder in
builder.types = members
}
}

public func union<Type>(
type: Type.Type,
build: (UnionTypeBuilder<Type>) throws -> Void
) throws {
let name = fixName(String(describing: Type.self))
try union(name: name, type: type, build: build)
}

public func union<Type>(
name: String,
type: Type.Type,
build: (UnionTypeBuilder<Type>) throws -> Void
) throws {
let builder = UnionTypeBuilder<Type>()
try build(builder)

let interfaceType = try GraphQLUnionType(
name: name,
description: builder.description,
resolveType: builder.resolveType,
types: builder.types.map { try getObjectType(from: $0) }
)

map(Type.self, to: interfaceType)
}

public func `enum`<Type : OutputType>(
type: Type.Type,
build: (EnumTypeBuilder<Type>) throws -> Void
Expand Down Expand Up @@ -517,3 +561,4 @@ public struct Schema<Root, Context> {
)
}
}

8 changes: 8 additions & 0 deletions Sources/Graphiti/Types/UnionType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import GraphQL

public final class UnionTypeBuilder<Type> {
public var description: String? = nil
public var resolveType: GraphQLTypeResolve? = nil
public var types: [Any.Type] = []
}

37 changes: 37 additions & 0 deletions Tests/GraphitiTests/StarWarsTests/StarWarsData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,19 @@ struct Droid : Character {
let primaryFunction: String
}

protocol SearchResult {}
extension Planet: SearchResult {}
extension Human: SearchResult {}
extension Droid: SearchResult {}

var tatooine = Planet(id:"10001", name: "Tatooine", diameter: 10465, rotationPeriod: 23, orbitalPeriod: 304,residents: [Human]() )
var alderaan = Planet(id: "10002", name: "Alderaan", diameter: 12500, rotationPeriod: 24, orbitalPeriod: 364, residents: [Human]())

let planetData: [String: Planet] = [
"10001": tatooine,
"10002": alderaan,
]

let luke = Human(
id: "1000",
name: "Luke Skywalker",
Expand Down Expand Up @@ -193,3 +203,30 @@ func getSecretBackStory() throws -> String? {
throw Secret(description: "secretBackstory is secret.")
}

/**
* Allos us to query for either a Human, Droid, or Planet.
*/
func search(for value: String) -> [SearchResult] {
let value = value.lowercased()
var result: [SearchResult] = []

result.append(contentsOf:
planetData.filter({
return $1.name.lowercased().range(of:value) != nil
}).map({ $1 })
)

result.append(contentsOf:
humanData.filter({
return $1.name.lowercased().range(of:value) != nil
}).map({ $1 })
)

result.append(contentsOf:
droidData.filter({
return $1.name.lowercased().range(of:value) != nil
}).map({ $1 })
)

return result
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@ class StarWarsIntrospectionTests : XCTestCase {
[
"name": "Query",
],
[
"name": "SearchResult",
],
[
"name": "String",
],

[
"name": "__Directive",
],
Expand Down Expand Up @@ -125,6 +127,9 @@ class StarWarsIntrospectionTests : XCTestCase {
[
"name": "Query",
],
[
"name": "SearchResult",
],
[
"name": "String",
],
Expand Down Expand Up @@ -467,6 +472,24 @@ class StarWarsIntrospectionTests : XCTestCase {
],
],
],
[
"name": "search",
"args": [
[
"name": "query",
"description": "text to find",
"type": [
"name": nil,
"kind": "NON_NULL",
"ofType": [
"name": "String",
"kind": "SCALAR",
]
],
"defaultValue": nil,
],
],
],
],
],
],
Expand Down
32 changes: 32 additions & 0 deletions Tests/GraphitiTests/StarWarsTests/StarWarsQueryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,38 @@ class StarWarsQueryTests : XCTestCase {
let result = try schema.execute(request: query)
XCTAssertEqual(result, expected)
}

func testSearchQuery() throws {
let query = "query {" +
" search(query: \"o\") {" +
" ... on Planet {" +
" name " +
" diameter " +
" }" +
" ... on Human {" +
" name " +
" }" +
" ... on Droid {" +
" name " +
" primaryFunction " +
" }" +
" }" +
"}"

let expected: Map = [
"data": [
"search": [
[ "name": "Tatooine", "diameter": 10465 ],
[ "name": "Han Solo" ],
[ "name": "Leia Organa" ],
[ "name": "C-3PO", "primaryFunction": "Protocol" ],
],
],
]

let result = try starWarsSchema.execute(request: query)
XCTAssertEqual(result, expected)
}
}

extension StarWarsQueryTests {
Expand Down
20 changes: 20 additions & 0 deletions Tests/GraphitiTests/StarWarsTests/StarWarsSchema.swift
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,15 @@ let starWarsSchema = try! Schema<NoRoot, NoContext> { schema in
)
}

/**
* A search result can be a include a number of different types.
*
* This implements the following type system shorthand:
*
* union SearchResult = Planet | Human | Droid
*/
try schema.union(type: SearchResult.self, members: [ Planet.self, Human.self, Droid.self ])

/**
* This is the type that will be the root of our query, and the
* entry point into our schema. It gives us the ability to fetch
Expand All @@ -264,6 +273,7 @@ let starWarsSchema = try! Schema<NoRoot, NoContext> { schema in
* hero(episode: Episode): Character
* human(id: String!): Human
* droid(id: String!): Droid
* search(query: String!): SearchResult
* }
*/
try schema.query { query in
Expand Down Expand Up @@ -298,6 +308,16 @@ let starWarsSchema = try! Schema<NoRoot, NoContext> { schema in
try query.field(name: "droid") { (_, arguments: DroidArguments, _, _) in
getDroid(id: arguments.id)
}

struct SearchArguments : Arguments {
let query: String
static let descriptions = ["query": "text to find"]
}

try query.field(name: "search") { (_, arguments: SearchArguments, _, _) in
search(for: arguments.query)
}

}

schema.types = [Human.self, Droid.self]
Expand Down

0 comments on commit 5db60d7

Please sign in to comment.