-
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding standardconnection protocol API for paging
- Loading branch information
1 parent
bca3a09
commit a6bc373
Showing
7 changed files
with
391 additions
and
0 deletions.
There are no files selected for viewing
50 changes: 50 additions & 0 deletions
50
...GraphZahl/Resolution/OutputResolvable/Implementations/Connection/ConnectionProtocol.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
|
||
import Foundation | ||
import NIO | ||
import GraphQL | ||
import ContextKit | ||
|
||
public protocol ConnectionProtocol: OutputResolvable, ConcreteResolvable { | ||
associatedtype Node | ||
associatedtype Edge: EdgeProtocol = StandardEdge<Node> where Edge.Node == Node | ||
|
||
func totalCount() -> EventLoopFuture<Int> | ||
func pageInfo(first: Int?, after: String?, last: Int?, before: String?, eventLoop: EventLoopGroup) -> EventLoopFuture<PageInfo> | ||
func edges(first: Int?, after: String?, last: Int?, before: String?, eventLoop: EventLoopGroup) -> EventLoopFuture<[Edge?]?> | ||
} | ||
|
||
extension ConnectionProtocol { | ||
|
||
public static var concreteTypeName: String { | ||
return "\(Node.concreteTypeName)\(String(describing: Self.self))" | ||
} | ||
|
||
public static var additionalArguments: [String : InputResolvable.Type] { | ||
return ConnectionWrapper<Self>.additionalArguments | ||
} | ||
|
||
public static func resolve(using context: inout Resolution.Context) throws -> GraphQLOutputType { | ||
return try context.resolve(type: ConnectionWrapper<Self>.self) | ||
} | ||
|
||
public func resolve(source: Any, | ||
arguments: [String : Map], | ||
context: MutableContext, | ||
eventLoop: EventLoopGroup) throws -> EventLoopFuture<Any?> { | ||
|
||
let first = try arguments["first"]?.intValue(converting: true) | ||
let after = try arguments["after"]?.stringValue(converting: true) | ||
let last = try arguments["last"]?.intValue(converting: true) | ||
let before = try arguments["before"]?.stringValue(converting: true) | ||
|
||
let connection = ConnectionWrapper(connection: self, | ||
eventLoop: eventLoop, | ||
first: first, | ||
after: after, | ||
last: last, | ||
before: before) | ||
|
||
return eventLoop.next().makeSucceededFuture(connection) | ||
} | ||
|
||
} |
97 changes: 97 additions & 0 deletions
97
.../GraphZahl/Resolution/OutputResolvable/Implementations/Connection/ConnectionWrapper.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
|
||
import Foundation | ||
import NIO | ||
import GraphQL | ||
import ContextKit | ||
|
||
class ConnectionWrapper<Connection: ConnectionProtocol> { | ||
private let connection: Connection | ||
private let eventLoop: EventLoopGroup | ||
|
||
private let first: Int? | ||
private let after: String? | ||
private let last: Int? | ||
private let before: String? | ||
|
||
init(connection: Connection, | ||
eventLoop: EventLoopGroup, | ||
first: Int?, | ||
after: String?, | ||
last: Int?, | ||
before: String?) { | ||
|
||
self.connection = connection | ||
self.eventLoop = eventLoop | ||
self.first = first | ||
self.after = after | ||
self.last = last | ||
self.before = before | ||
} | ||
} | ||
|
||
extension ConnectionWrapper { | ||
|
||
private func pageInfo() -> EventLoopFuture<PageInfo> { | ||
return connection.pageInfo(first: first, after: after, last: last, before: before, eventLoop: eventLoop) | ||
} | ||
|
||
private func edges() -> EventLoopFuture<[Connection.Edge?]?> { | ||
return connection.edges(first: first, after: after, last: last, before: before, eventLoop: eventLoop) | ||
} | ||
|
||
private func totalCount() -> EventLoopFuture<Int> { | ||
return connection.totalCount() | ||
} | ||
|
||
} | ||
|
||
extension ConnectionWrapper: ConcreteResolvable { | ||
|
||
static var concreteTypeName: String { | ||
return Connection.concreteTypeName | ||
} | ||
|
||
} | ||
|
||
extension ConnectionWrapper: OutputResolvable { | ||
|
||
static var additionalArguments: [String : InputResolvable.Type] { | ||
return [ | ||
"first" : Int?.self, | ||
"after" : String?.self, | ||
"last" : Int?.self, | ||
"before" : String?.self, | ||
] | ||
} | ||
|
||
static func resolve(using context: inout Resolution.Context) throws -> GraphQLOutputType { | ||
context.append(type: GraphQLNonNull(GraphQLTypeReference(concreteTypeName)), as: concreteTypeName) | ||
|
||
let fields = [ | ||
"pageInfo" : GraphQLField(type: try context.resolve(type: PageInfo.self)) { (receiver, args, context, eventLoop, _) -> Future<Any?> in | ||
return (receiver as! ConnectionWrapper<Connection>) | ||
.pageInfo() | ||
.resolve(source: receiver, arguments: try args.dictionaryValue(), context: context as! MutableContext, eventLoop: eventLoop) | ||
}, | ||
"edges" : GraphQLField(type: try context.resolve(type: [Connection.Edge?]?.self)) { (receiver, args, context, eventLoop, _) -> Future<Any?> in | ||
return (receiver as! ConnectionWrapper<Connection>) | ||
.edges() | ||
.resolve(source: receiver, arguments: try args.dictionaryValue(), context: context as! MutableContext, eventLoop: eventLoop) | ||
}, | ||
"totalCount" : GraphQLField(type: try context.resolve(type: Int.self)) { (receiver, args, context, eventLoop, _) -> Future<Any?> in | ||
return (receiver as! ConnectionWrapper<Connection>) | ||
.totalCount() | ||
.resolve(source: receiver, arguments: try args.dictionaryValue(), context: context as! MutableContext, eventLoop: eventLoop) | ||
}, | ||
] | ||
|
||
return GraphQLNonNull( | ||
try GraphQLObjectType(name: concreteTypeName, fields: fields) | ||
) | ||
} | ||
|
||
func resolve(source: Any, arguments: [String : Map], context: MutableContext, eventLoop: EventLoopGroup) throws -> EventLoopFuture<Any?> { | ||
return eventLoop.next().makeSucceededFuture(self) | ||
} | ||
|
||
} |
52 changes: 52 additions & 0 deletions
52
...hZahl/Resolution/OutputResolvable/Implementations/Connection/ContextBasedConnection.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
|
||
import Foundation | ||
import NIO | ||
import GraphQL | ||
import ContextKit | ||
|
||
public protocol ContextBasedConnection: class, OutputResolvable, ConcreteResolvable { | ||
associatedtype Node | ||
associatedtype Edge: EdgeProtocol = StandardEdge<Node> where Edge.Node == Node | ||
associatedtype Context | ||
|
||
func context(first: Int?, after: String?, last: Int?, before: String?, eventLoop: EventLoopGroup) -> EventLoopFuture<Context> | ||
func totalCount(context: Context, eventLoop: EventLoopGroup) -> EventLoopFuture<Int> | ||
func pageInfo(context: Context, eventLoop: EventLoopGroup) -> EventLoopFuture<PageInfo> | ||
func edges(context: Context, eventLoop: EventLoopGroup) -> EventLoopFuture<[Edge?]?> | ||
} | ||
|
||
extension ContextBasedConnection { | ||
|
||
public static var concreteTypeName: String { | ||
return "\(Node.concreteTypeName)\(String(describing: Self.self))" | ||
} | ||
|
||
public static var additionalArguments: [String : InputResolvable.Type] { | ||
return ContextBasedConnectionWrapper<Self>.additionalArguments | ||
} | ||
|
||
public static func resolve(using context: inout Resolution.Context) throws -> GraphQLOutputType { | ||
return try context.resolve(type: ContextBasedConnectionWrapper<Self>.self) | ||
} | ||
|
||
public func resolve(source: Any, | ||
arguments: [String : Map], | ||
context: MutableContext, | ||
eventLoop: EventLoopGroup) throws -> EventLoopFuture<Any?> { | ||
|
||
let first = try arguments["first"]?.intValue(converting: true) | ||
let after = try arguments["after"]?.stringValue(converting: true) | ||
let last = try arguments["last"]?.intValue(converting: true) | ||
let before = try arguments["before"]?.stringValue(converting: true) | ||
|
||
let connection = ContextBasedConnectionWrapper(connection: self, | ||
eventLoop: eventLoop, | ||
first: first, | ||
after: after, | ||
last: last, | ||
before: before) | ||
|
||
return eventLoop.next().makeSucceededFuture(connection) | ||
} | ||
|
||
} |
108 changes: 108 additions & 0 deletions
108
...esolution/OutputResolvable/Implementations/Connection/ContextBasedConnectionWrapper.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
|
||
import Foundation | ||
import NIO | ||
import GraphQL | ||
import ContextKit | ||
|
||
class ContextBasedConnectionWrapper<Connection: ContextBasedConnection> { | ||
private let connection: Connection | ||
private let eventLoop: EventLoopGroup | ||
|
||
private var cachedContext: EventLoopFuture<Connection.Context>? | ||
private let first: Int? | ||
private let after: String? | ||
private let last: Int? | ||
private let before: String? | ||
|
||
init(connection: Connection, | ||
eventLoop: EventLoopGroup, | ||
first: Int?, | ||
after: String?, | ||
last: Int?, | ||
before: String?) { | ||
|
||
self.connection = connection | ||
self.eventLoop = eventLoop | ||
self.first = first | ||
self.after = after | ||
self.last = last | ||
self.before = before | ||
} | ||
} | ||
|
||
extension ContextBasedConnectionWrapper { | ||
|
||
private func context() -> EventLoopFuture<Connection.Context> { | ||
if let context = cachedContext { | ||
return context | ||
} | ||
|
||
let context = connection.context(first: first, after: after, last: last, before: before, eventLoop: eventLoop) | ||
cachedContext = context | ||
return context | ||
} | ||
|
||
private func pageInfo() -> EventLoopFuture<PageInfo> { | ||
return context().flatMap { self.connection.pageInfo(context: $0, eventLoop: self.eventLoop) } | ||
} | ||
|
||
private func edges() -> EventLoopFuture<[Connection.Edge?]?> { | ||
return context().flatMap { self.connection.edges(context: $0, eventLoop: self.eventLoop) } | ||
} | ||
|
||
private func totalCount() -> EventLoopFuture<Int> { | ||
return context().flatMap { self.connection.totalCount(context: $0, eventLoop: self.eventLoop) } | ||
} | ||
|
||
} | ||
|
||
extension ContextBasedConnectionWrapper: ConcreteResolvable { | ||
|
||
static var concreteTypeName: String { | ||
return Connection.concreteTypeName | ||
} | ||
|
||
} | ||
|
||
extension ContextBasedConnectionWrapper: OutputResolvable { | ||
|
||
static var additionalArguments: [String : InputResolvable.Type] { | ||
return [ | ||
"first" : Int?.self, | ||
"after" : String?.self, | ||
"last" : Int?.self, | ||
"before" : String?.self, | ||
] | ||
} | ||
|
||
static func resolve(using context: inout Resolution.Context) throws -> GraphQLOutputType { | ||
context.append(type: GraphQLNonNull(GraphQLTypeReference(concreteTypeName)), as: concreteTypeName) | ||
|
||
let fields = [ | ||
"pageInfo" : GraphQLField(type: try context.resolve(type: PageInfo.self)) { (receiver, args, context, eventLoop, _) -> Future<Any?> in | ||
return (receiver as! ContextBasedConnectionWrapper<Connection>) | ||
.pageInfo() | ||
.resolve(source: receiver, arguments: try args.dictionaryValue(), context: context as! MutableContext, eventLoop: eventLoop) | ||
}, | ||
"edges" : GraphQLField(type: try context.resolve(type: [Connection.Edge?]?.self)) { (receiver, args, context, eventLoop, _) -> Future<Any?> in | ||
return (receiver as! ContextBasedConnectionWrapper<Connection>) | ||
.edges() | ||
.resolve(source: receiver, arguments: try args.dictionaryValue(), context: context as! MutableContext, eventLoop: eventLoop) | ||
}, | ||
"totalCount" : GraphQLField(type: try context.resolve(type: Int.self)) { (receiver, args, context, eventLoop, _) -> Future<Any?> in | ||
return (receiver as! ContextBasedConnectionWrapper<Connection>) | ||
.totalCount() | ||
.resolve(source: receiver, arguments: try args.dictionaryValue(), context: context as! MutableContext, eventLoop: eventLoop) | ||
}, | ||
] | ||
|
||
return GraphQLNonNull( | ||
try GraphQLObjectType(name: concreteTypeName, fields: fields) | ||
) | ||
} | ||
|
||
func resolve(source: Any, arguments: [String : Map], context: MutableContext, eventLoop: EventLoopGroup) throws -> EventLoopFuture<Any?> { | ||
return eventLoop.next().makeSucceededFuture(self) | ||
} | ||
|
||
} |
9 changes: 9 additions & 0 deletions
9
Sources/GraphZahl/Resolution/OutputResolvable/Implementations/Connection/EdgeProtocol.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
|
||
import Foundation | ||
|
||
public protocol EdgeProtocol: OutputResolvable { | ||
associatedtype Node: OutputResolvable & ConcreteResolvable | ||
|
||
var node: Node? { get } | ||
var cursor: String { get } | ||
} |
21 changes: 21 additions & 0 deletions
21
Sources/GraphZahl/Resolution/OutputResolvable/Implementations/Connection/PageInfo.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
|
||
import Foundation | ||
|
||
public class PageInfo: GraphQLObject { | ||
let hasNextPage: Bool | ||
let hasPreviousPage: Bool | ||
|
||
let startCursor: String? | ||
let endCursor: String? | ||
|
||
public init(hasNextPage: Bool, | ||
hasPreviousPage: Bool, | ||
startCursor: String?, | ||
endCursor: String?) { | ||
|
||
self.hasNextPage = hasNextPage | ||
self.hasPreviousPage = hasPreviousPage | ||
self.startCursor = startCursor | ||
self.endCursor = endCursor | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
Sources/GraphZahl/Resolution/OutputResolvable/Implementations/Connection/StandardEdge.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
|
||
import Foundation | ||
import NIO | ||
import GraphQL | ||
import ContextKit | ||
|
||
public struct StandardEdge<Node: OutputResolvable & ConcreteResolvable>: EdgeProtocol { | ||
public let node: Node? | ||
public let cursor: String | ||
|
||
public init(node: Node?, cursor: String) { | ||
self.node = node | ||
self.cursor = cursor | ||
} | ||
} | ||
|
||
extension StandardEdge { | ||
public static var concreteTypeName: String { | ||
return "\(Node.concreteTypeName)Edge" | ||
} | ||
} | ||
|
||
extension StandardEdge { | ||
|
||
public static var additionalArguments: [String : InputResolvable.Type] { | ||
return Node?.additionalArguments | ||
} | ||
|
||
public static func resolve(using context: inout Resolution.Context) throws -> GraphQLOutputType { | ||
context.append(type: GraphQLNonNull(GraphQLTypeReference(concreteTypeName)), as: concreteTypeName) | ||
|
||
let fields = [ | ||
"node" : GraphQLField(type: try context.resolve(type: Optional<Node>.self)) { (receiver, args, context, eventLoop, _) -> Future<Any?> in | ||
return try (receiver as! StandardEdge<Node>) | ||
.node | ||
.resolve(source: receiver, arguments: try args.dictionaryValue(), context: context as! MutableContext, eventLoop: eventLoop) | ||
}, | ||
"cursor" : GraphQLField(type: try context.resolve(type: String.self)) { (receiver, args, context, eventLoop, _) -> Future<Any?> in | ||
return (receiver as! StandardEdge<Node>) | ||
.cursor | ||
.resolve(source: receiver, arguments: try args.dictionaryValue(), context: context as! MutableContext,eventLoop: eventLoop) | ||
}, | ||
] | ||
|
||
return GraphQLNonNull( | ||
try GraphQLObjectType(name: concreteTypeName, fields: fields) | ||
) | ||
} | ||
|
||
public func resolve(source: Any, arguments: [String : Map], context: MutableContext, eventLoop: EventLoopGroup) throws -> EventLoopFuture<Any?> { | ||
return eventLoop.next().makeSucceededFuture(self) | ||
} | ||
|
||
} |