Resources now declare their required API versions, making GraphQL support a little cleaner
This commit is contained in:
parent
e4ae58b14b
commit
135510dcb9
|
@ -101,12 +101,24 @@ public final class Buildkite {
|
||||||
|
|
||||||
public extension Buildkite {
|
public extension Buildkite {
|
||||||
func send<R: Resource & HasResponseBody>(_ resource: R, completion: @escaping (Result<Response<R.Content>, Error>) -> Void) {
|
func send<R: Resource & HasResponseBody>(_ resource: R, completion: @escaping (Result<Response<R.Content>, Error>) -> Void) {
|
||||||
let request = URLRequest(resource, configuration: configuration)
|
let request: URLRequest
|
||||||
|
do {
|
||||||
|
request = try URLRequest(resource, configuration: configuration)
|
||||||
|
} catch {
|
||||||
|
completion(.failure(error))
|
||||||
|
return
|
||||||
|
}
|
||||||
transport.send(request: request, completion: handleContentfulResponse(completion: completion))
|
transport.send(request: request, completion: handleContentfulResponse(completion: completion))
|
||||||
}
|
}
|
||||||
|
|
||||||
func send<R: Resource & Paginated>(_ resource: R, pageOptions: PageOptions? = nil, completion: @escaping (Result<Response<R.Content>, Error>) -> Void) {
|
func send<R: Resource & Paginated>(_ resource: R, pageOptions: PageOptions? = nil, completion: @escaping (Result<Response<R.Content>, Error>) -> Void) {
|
||||||
let request = URLRequest(resource, configuration: configuration, pageOptions: pageOptions)
|
let request: URLRequest
|
||||||
|
do {
|
||||||
|
request = try URLRequest(resource, configuration: configuration, pageOptions: pageOptions)
|
||||||
|
} catch {
|
||||||
|
completion(.failure(error))
|
||||||
|
return
|
||||||
|
}
|
||||||
transport.send(request: request, completion: handleContentfulResponse(completion: completion))
|
transport.send(request: request, completion: handleContentfulResponse(completion: completion))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +145,13 @@ public extension Buildkite {
|
||||||
}
|
}
|
||||||
|
|
||||||
func send<R: Resource>(_ resource: R, completion: @escaping (Result<Response<Void>, Error>) -> Void) {
|
func send<R: Resource>(_ resource: R, completion: @escaping (Result<Response<Void>, Error>) -> Void) {
|
||||||
let request = URLRequest(resource, configuration: configuration)
|
let request: URLRequest
|
||||||
|
do {
|
||||||
|
request = try URLRequest(resource, configuration: configuration)
|
||||||
|
} catch {
|
||||||
|
completion(.failure(error))
|
||||||
|
return
|
||||||
|
}
|
||||||
transport.send(request: request, completion: handleEmptyResponse(completion: completion))
|
transport.send(request: request, completion: handleEmptyResponse(completion: completion))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +175,9 @@ import Combine
|
||||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
|
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
|
||||||
extension Buildkite {
|
extension Buildkite {
|
||||||
public func sendPublisher<R: Resource & HasResponseBody>(_ resource: R) -> AnyPublisher<Response<R.Content>, Error> {
|
public func sendPublisher<R: Resource & HasResponseBody>(_ resource: R) -> AnyPublisher<Response<R.Content>, Error> {
|
||||||
transport.sendPublisher(request: URLRequest(resource, configuration: configuration))
|
Result { try URLRequest(resource, configuration: configuration) }
|
||||||
|
.publisher
|
||||||
|
.flatMap(transport.sendPublisher)
|
||||||
.tryMap {
|
.tryMap {
|
||||||
try self.checkResponseForIssues($0.response, data: $0.data)
|
try self.checkResponseForIssues($0.response, data: $0.data)
|
||||||
let content = try self.decoder.decode(R.Content.self, from: $0.data)
|
let content = try self.decoder.decode(R.Content.self, from: $0.data)
|
||||||
|
@ -167,7 +187,9 @@ extension Buildkite {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func sendPublisher<R: Resource & HasResponseBody & Paginated>(_ resource: R, pageOptions: PageOptions? = nil) -> AnyPublisher<Response<R.Content>, Error> {
|
public func sendPublisher<R: Resource & HasResponseBody & Paginated>(_ resource: R, pageOptions: PageOptions? = nil) -> AnyPublisher<Response<R.Content>, Error> {
|
||||||
transport.sendPublisher(request: URLRequest(resource, configuration: configuration, pageOptions: pageOptions))
|
Result { try URLRequest(resource, configuration: configuration, pageOptions: pageOptions) }
|
||||||
|
.publisher
|
||||||
|
.flatMap(transport.sendPublisher)
|
||||||
.tryMap {
|
.tryMap {
|
||||||
try self.checkResponseForIssues($0.response, data: $0.data)
|
try self.checkResponseForIssues($0.response, data: $0.data)
|
||||||
let content = try self.decoder.decode(R.Content.self, from: $0.data)
|
let content = try self.decoder.decode(R.Content.self, from: $0.data)
|
||||||
|
@ -201,7 +223,9 @@ extension Buildkite {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func sendPublisher<R: Resource>(_ resource: R) -> AnyPublisher<Response<Void>, Error> {
|
public func sendPublisher<R: Resource>(_ resource: R) -> AnyPublisher<Response<Void>, Error> {
|
||||||
transport.sendPublisher(request: URLRequest(resource, configuration: configuration))
|
Result { try URLRequest(resource, configuration: configuration) }
|
||||||
|
.publisher
|
||||||
|
.flatMap(transport.sendPublisher)
|
||||||
.tryMap {
|
.tryMap {
|
||||||
try self.checkResponseForIssues($0.response)
|
try self.checkResponseForIssues($0.response)
|
||||||
return Response(content: (), response: $0.response)
|
return Response(content: (), response: $0.response)
|
||||||
|
|
|
@ -12,7 +12,7 @@ import Foundation
|
||||||
import FoundationNetworking
|
import FoundationNetworking
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public struct APIVersion {
|
public struct APIVersion: Equatable {
|
||||||
public enum REST {
|
public enum REST {
|
||||||
private static let baseURL = URL(string: "https://api.buildkite.com")!
|
private static let baseURL = URL(string: "https://api.buildkite.com")!
|
||||||
public static let v2 = APIVersion(baseURL: baseURL, version: "v2")
|
public static let v2 = APIVersion(baseURL: baseURL, version: "v2")
|
||||||
|
|
|
@ -12,18 +12,18 @@ import Foundation
|
||||||
import FoundationNetworking
|
import FoundationNetworking
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
let libraryVersion = "0.0.1"
|
|
||||||
|
|
||||||
public struct Configuration {
|
public struct Configuration {
|
||||||
public let userAgent = "buildkite-swift/\(libraryVersion)"
|
public let userAgent = "buildkite-swift"
|
||||||
public var version: APIVersion
|
public var version: APIVersion
|
||||||
|
public var graphQLVersion: APIVersion
|
||||||
|
|
||||||
public static var `default`: Configuration {
|
public static var `default`: Configuration {
|
||||||
.init(version: APIVersion.REST.v2)
|
.init(version: APIVersion.REST.v2, graphQLVersion: APIVersion.GraphQL.v1)
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(version: APIVersion) {
|
public init(version: APIVersion = APIVersion.REST.v2, graphQLVersion: APIVersion = APIVersion.GraphQL.v1) {
|
||||||
self.version = version
|
self.version = version
|
||||||
|
self.graphQLVersion = graphQLVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
var token: String?
|
var token: String?
|
||||||
|
|
|
@ -14,11 +14,16 @@ import FoundationNetworking
|
||||||
|
|
||||||
public protocol Resource {
|
public protocol Resource {
|
||||||
associatedtype Content
|
associatedtype Content
|
||||||
|
var version: APIVersion { get }
|
||||||
var path: String { get }
|
var path: String { get }
|
||||||
func transformRequest(_ request: inout URLRequest)
|
func transformRequest(_ request: inout URLRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Resource {
|
extension Resource {
|
||||||
|
public var version: APIVersion {
|
||||||
|
APIVersion.REST.v2
|
||||||
|
}
|
||||||
|
|
||||||
public func transformRequest(_ request: inout URLRequest) {}
|
public func transformRequest(_ request: inout URLRequest) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,8 +39,14 @@ public protocol HasResponseBody {
|
||||||
public protocol Paginated: HasResponseBody {}
|
public protocol Paginated: HasResponseBody {}
|
||||||
|
|
||||||
extension URLRequest {
|
extension URLRequest {
|
||||||
init<R: Resource>(_ resource: R, configuration: Configuration) {
|
init<R: Resource>(_ resource: R, configuration: Configuration) throws {
|
||||||
let url = configuration.version.url(for: resource.path)
|
let version = resource.version
|
||||||
|
guard version == configuration.version
|
||||||
|
|| version == configuration.graphQLVersion else {
|
||||||
|
throw ResponseError.incompatibleVersion
|
||||||
|
}
|
||||||
|
let url = version.url(for: resource.path)
|
||||||
|
|
||||||
var request = URLRequest(url: url)
|
var request = URLRequest(url: url)
|
||||||
configuration.transformRequest(&request)
|
configuration.transformRequest(&request)
|
||||||
resource.transformRequest(&request)
|
resource.transformRequest(&request)
|
||||||
|
@ -43,12 +54,12 @@ extension URLRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
init<R: Resource & HasRequestBody>(_ resource: R, configuration: Configuration, encoder: JSONEncoder) throws {
|
init<R: Resource & HasRequestBody>(_ resource: R, configuration: Configuration, encoder: JSONEncoder) throws {
|
||||||
self.init(resource, configuration: configuration)
|
try self.init(resource, configuration: configuration)
|
||||||
httpBody = try encoder.encode(resource.body)
|
httpBody = try encoder.encode(resource.body)
|
||||||
}
|
}
|
||||||
|
|
||||||
init<R: Resource & Paginated>(_ resource: R, configuration: Configuration, pageOptions: PageOptions? = nil) {
|
init<R: Resource & Paginated>(_ resource: R, configuration: Configuration, pageOptions: PageOptions? = nil) throws {
|
||||||
self.init(resource, configuration: configuration)
|
try self.init(resource, configuration: configuration)
|
||||||
if let options = pageOptions {
|
if let options = pageOptions {
|
||||||
appendPageOptions(options)
|
appendPageOptions(options)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import FoundationNetworking
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum ResponseError: Error {
|
enum ResponseError: Error {
|
||||||
|
case incompatibleVersion
|
||||||
case missingResponse
|
case missingResponse
|
||||||
case unexpectedlyNoContent
|
case unexpectedlyNoContent
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,10 @@ public struct GraphQL<T: Codable>: Resource, HasResponseBody, HasRequestBody {
|
||||||
|
|
||||||
public var body: Body
|
public var body: Body
|
||||||
|
|
||||||
|
public var version: APIVersion {
|
||||||
|
APIVersion.GraphQL.v1
|
||||||
|
}
|
||||||
|
|
||||||
public let path = ""
|
public let path = ""
|
||||||
|
|
||||||
public init(rawQuery query: String, variables: [String: JSONValue] = [:]) {
|
public init(rawQuery query: String, variables: [String: JSONValue] = [:]) {
|
||||||
|
@ -47,7 +51,6 @@ public struct GraphQL<T: Codable>: Resource, HasResponseBody, HasRequestBody {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func transformRequest(_ request: inout URLRequest) {
|
public func transformRequest(_ request: inout URLRequest) {
|
||||||
request.url = APIVersion.GraphQL.v1.url(for: path)
|
|
||||||
request.httpMethod = "POST"
|
request.httpMethod = "POST"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue