Skip to content

Commit 90c61c9

Browse files
Merge pull request #8 from GraphQLSwift/feat/macro
Adds a `@graphQLResolver` Macro, which reduces boilerplate for simple Swift property resolvers.
2 parents d0855eb + 755ce72 commit 90c61c9

15 files changed

Lines changed: 661 additions & 117 deletions

File tree

Examples/HelloWorldServer/Package.resolved

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

Examples/HelloWorldServer/Package.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ let package = Package(
1616
name: "HelloWorldServer",
1717
dependencies: [
1818
.product(name: "GraphQL", package: "GraphQL"),
19+
.product(name: "GraphQLGeneratorMacros", package: "graphql-generator"),
1920
.product(name: "GraphQLGeneratorRuntime", package: "graphql-generator"),
2021
],
2122
plugins: [

Examples/HelloWorldServer/Sources/HelloWorldServer/Resolvers.swift

Lines changed: 19 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Foundation
22
import GraphQL
3+
import GraphQLGeneratorMacros
34
import GraphQLGeneratorRuntime
45

56
/// Must be created by user and named `GraphQLContext`.
@@ -76,64 +77,42 @@ struct Resolvers: GraphQLGenerated.Resolvers {
7677

7778
struct User: GraphQLGenerated.User {
7879
// User can choose structure
79-
let id: String
80-
let name: String
81-
let email: String
82-
let age: Int?
83-
let role: GraphQLGenerated.Role?
8480

85-
/// Required implementations
86-
func id(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> String {
87-
return id
88-
}
81+
// Properties with auto-generated GraphQL resolvers.
8982

90-
func name(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> String {
91-
return name
92-
}
83+
@graphQLResolver let id: String
84+
@graphQLResolver let name: String
85+
@graphQLResolver let age: Int?
86+
@graphQLResolver let role: GraphQLGenerated.Role?
87+
88+
// Required implementations
89+
// Can't use @graphQLResolver macro because we must convert from String to GraphQLScalars.EmailAddress
9390

91+
let email: String
9492
func email(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> GraphQLScalars.EmailAddress {
9593
return .init(email: email)
9694
}
97-
98-
func age(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> Int? {
99-
return age
100-
}
101-
102-
func role(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> GraphQLGenerated.Role? {
103-
return role
104-
}
10595
}
10696

10797
struct Contact: GraphQLGenerated.Contact {
108-
/// User can choose structure
109-
let email: String
98+
// User can choose structure
11099

111100
/// Required implementations
101+
let email: String
112102
func email(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> GraphQLScalars.EmailAddress {
113103
return .init(email: email)
114104
}
115105
}
116106

117107
struct Post: GraphQLGenerated.Post {
118108
// User can choose structure
119-
let id: String
120-
let title: String
121-
let content: String
122-
let authorId: String
123109

124-
/// Required implementations
125-
func id(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> String {
126-
return id
127-
}
128-
129-
func title(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> String {
130-
return title
131-
}
132-
133-
func content(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> String {
134-
return content
135-
}
110+
@graphQLResolver let id: String
111+
@graphQLResolver let title: String
112+
@graphQLResolver let content: String
136113

114+
/// Required implementations
115+
let authorId: String
137116
func author(context: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> any GraphQLGenerated.User {
138117
return context.users[authorId]!
139118
}
@@ -168,9 +147,9 @@ struct Mutation: GraphQLGenerated.Mutation {
168147
let user = User(
169148
id: userInfo.id,
170149
name: userInfo.name,
171-
email: userInfo.email.email,
172150
age: userInfo.age,
173-
role: userInfo.role
151+
role: userInfo.role,
152+
email: userInfo.email.email
174153
)
175154
context.users[userInfo.id] = user
176155
return user

Examples/HelloWorldServer/Sources/HelloWorldServer/schema.graphql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ union UserOrPost = User | Post
1212
input UserInfo {
1313
id: ID!
1414
name: String!
15-
email: EmailAddress!
1615
age: Int
1716
role: Role = USER
17+
email: EmailAddress!
1818
}
1919

2020
"""

Examples/HelloWorldServer/Tests/HelloWorldServerTests/HelloWorldServerTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ struct HelloWorldServerTests {
77
@Test func query() async throws {
88
let schema = try buildGraphQLSchema(resolvers: Resolvers.self)
99
let context = GraphQLContext(
10-
users: ["1": .init(id: "1", name: "John", email: "john@example.com", age: 18, role: .user)],
10+
users: ["1": .init(id: "1", name: "John", age: 18, role: .user, email: "john@example.com")],
1111
posts: ["1": .init(id: "1", title: "Foo", content: "bar", authorId: "1")]
1212
)
1313
let actual = try await graphql(
@@ -89,7 +89,7 @@ struct HelloWorldServerTests {
8989
@Test func subscription() async throws {
9090
let schema = try buildGraphQLSchema(resolvers: Resolvers.self)
9191
let context = GraphQLContext(
92-
users: ["1": .init(id: "1", name: "John", email: "john@example.com", age: 18, role: .user)],
92+
users: ["1": .init(id: "1", name: "John", age: 18, role: .user, email: "john@example.com")],
9393
posts: [:]
9494
)
9595
let stream = try await graphqlSubscribe(

Examples/StarWars/Package.resolved

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

Examples/StarWars/Package.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ let package = Package(
2626
.product(name: "AsyncDataLoader", package: "DataLoader"),
2727
.product(name: "AsyncHTTPClient", package: "async-http-client"),
2828
.product(name: "GraphQL", package: "GraphQL"),
29+
.product(name: "GraphQLGeneratorMacros", package: "graphql-generator"),
2930
.product(name: "GraphQLGeneratorRuntime", package: "graphql-generator"),
3031
],
3132
plugins: [

Examples/StarWars/Sources/StarWars/GraphQL/Relay.swift

Lines changed: 9 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Foundation
22
import GraphQL
3+
import GraphQLGeneratorMacros
34

45
/// Encodes a SWAPI type and ID into a Relay Node ID. This is the base64-encoded string `<type>:<id>`
56
/// - Parameters:
@@ -26,33 +27,17 @@ func urlToID(_ url: String) -> String {
2627
}
2728

2829
struct PageInfo: GraphQLGenerated.PageInfo {
29-
let hasNextPage: Bool
30-
let hasPreviousPage: Bool
31-
let startCursor: String?
32-
let endCursor: String?
33-
34-
func hasNextPage(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> Bool {
35-
return hasNextPage
36-
}
37-
38-
func hasPreviousPage(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> Bool {
39-
return hasPreviousPage
40-
}
41-
42-
func startCursor(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> String? {
43-
return startCursor
44-
}
45-
46-
func endCursor(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> String? {
47-
return endCursor
48-
}
30+
@graphQLResolver let hasNextPage: Bool
31+
@graphQLResolver let hasPreviousPage: Bool
32+
@graphQLResolver let startCursor: String?
33+
@graphQLResolver let endCursor: String?
4934
}
5035

5136
/// A generalized Relay connection.
5237
struct Connection<T: GraphQLGenerated.Node>: Sendable {
53-
let pageInfo: PageInfo
54-
let edges: [Edge<T>]
55-
let totalCount: Int
38+
@graphQLResolver let pageInfo: any GraphQLGenerated.PageInfo
39+
@graphQLResolver let edges: [Edge<T>]
40+
@graphQLResolver let totalCount: Int?
5641

5742
/// Create a connection by passing a total list of the available IDs in order.
5843
init(ids: [String], after: String?, first: Int?, before: String?, last: Int?) {
@@ -93,18 +78,6 @@ struct Connection<T: GraphQLGenerated.Node>: Sendable {
9378
edges = pageIds.map { Edge<T>(cursor: $0) }
9479
totalCount = ids.count
9580
}
96-
97-
func pageInfo(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> any GraphQLGenerated.PageInfo {
98-
return pageInfo
99-
}
100-
101-
func edges(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> [Edge<T>]? {
102-
return edges
103-
}
104-
105-
func totalCount(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> Int? {
106-
return totalCount
107-
}
10881
}
10982

11083
extension Connection:
@@ -338,11 +311,7 @@ extension Connection:
338311
}
339312

340313
struct Edge<T: GraphQLGenerated.Node>: Sendable {
341-
let cursor: String
342-
343-
func cursor(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> String {
344-
return cursor
345-
}
314+
@graphQLResolver let cursor: String
346315
}
347316

348317
extension Edge:

Examples/StarWars/Sources/StarWars/GraphQL/Resolvers.swift

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import AsyncHTTPClient
22
import Foundation
33
import GraphQL
4+
import GraphQLGeneratorMacros
45
import GraphQLGeneratorRuntime
56

67
struct GraphQLContext {
@@ -130,7 +131,7 @@ struct Root: GraphQLGenerated.Root {
130131
}
131132

132133
extension Film: GraphQLGenerated.Film {
133-
var id: String {
134+
@graphQLResolver var id: String {
134135
return encodeID(type: Film.self, id: urlToID(url))
135136
}
136137

@@ -166,10 +167,6 @@ extension Film: GraphQLGenerated.Film {
166167
return edited
167168
}
168169

169-
func id(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> String {
170-
return id
171-
}
172-
173170
func speciesConnection(after: String?, first: Int?, before: String?, last: Int?, context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> (any GraphQLGenerated.FilmSpeciesConnection)? {
174171
let filteredIDs = species.map { encodeID(type: Film.self, id: urlToID($0)) }
175172
return Connection<Species>(ids: filteredIDs, after: after, first: first, before: before, last: last)
@@ -197,7 +194,7 @@ extension Film: GraphQLGenerated.Film {
197194
}
198195

199196
extension Person: GraphQLGenerated.Person {
200-
var id: String {
197+
@graphQLResolver var id: String {
201198
return encodeID(type: Person.self, id: urlToID(url))
202199
}
203200

@@ -266,14 +263,10 @@ extension Person: GraphQLGenerated.Person {
266263
func edited(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> String? {
267264
return edited
268265
}
269-
270-
func id(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> String {
271-
return id
272-
}
273266
}
274267

275268
extension Planet: GraphQLGenerated.Planet {
276-
var id: String {
269+
@graphQLResolver var id: String {
277270
return encodeID(type: Planet.self, id: urlToID(url))
278271
}
279272

@@ -330,14 +323,10 @@ extension Planet: GraphQLGenerated.Planet {
330323
func edited(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> String? {
331324
return edited
332325
}
333-
334-
func id(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> String {
335-
return id
336-
}
337326
}
338327

339328
extension Species: GraphQLGenerated.Species {
340-
var id: String {
329+
@graphQLResolver var id: String {
341330
return encodeID(type: Species.self, id: urlToID(url))
342331
}
343332

@@ -398,14 +387,10 @@ extension Species: GraphQLGenerated.Species {
398387
func edited(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> String? {
399388
return edited
400389
}
401-
402-
func id(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> String {
403-
return id
404-
}
405390
}
406391

407392
extension Starship: GraphQLGenerated.Starship {
408-
var id: String {
393+
@graphQLResolver var id: String {
409394
return encodeID(type: Starship.self, id: urlToID(url))
410395
}
411396

@@ -478,14 +463,10 @@ extension Starship: GraphQLGenerated.Starship {
478463
func edited(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> String? {
479464
return edited
480465
}
481-
482-
func id(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> String {
483-
return id
484-
}
485466
}
486467

487468
extension Vehicle: GraphQLGenerated.Vehicle {
488-
var id: String {
469+
@graphQLResolver var id: String {
489470
return encodeID(type: Vehicle.self, id: urlToID(url))
490471
}
491472

@@ -550,8 +531,4 @@ extension Vehicle: GraphQLGenerated.Vehicle {
550531
func edited(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> String? {
551532
return edited
552533
}
553-
554-
func id(context _: GraphQLContext, info _: GraphQL.GraphQLResolveInfo) async throws -> String {
555-
return id
556-
}
557534
}

0 commit comments

Comments
 (0)