buildkite-swift/Sources/Buildkite/Resources/GraphQL.swift

105 lines
3.1 KiB
Swift

//
// Emojis.swift
// Buildkite
//
// Created by Aaron Sky on 5/19/20.
// Copyright © 2020 Aaron Sky. All rights reserved.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
public struct GraphQL<T: Decodable>: Resource {
public struct Body: Encodable {
/// The query or mutation to be sent
public var query: String
/// The variables to be provided alongside the query or mutation
public var variables: JSONValue
}
// Making a design decision here to forbid supplying data and errors
// simultaneously. The GraphQL spec permits it but multiple implementations,
// including Buildkite's, choose not to.
public enum Content: Decodable {
case data(T)
case errors(Errors)
public init(
from decoder: Decoder
) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let errors = try container.decodeIfPresent([Error].self, forKey: .errors) {
let type = try container.decodeIfPresent(String.self, forKey: .type)
self = .errors(Errors(errors: errors, type: type))
} else if let data = try container.decodeIfPresent(T.self, forKey: .data) {
self = .data(data)
} else {
throw DecodingError.dataCorrupted(
DecodingError.Context(
codingPath: decoder.codingPath,
debugDescription:
"The GraphQL response does not contain either errors or data. One is required. If errors are present, they will be considered instead of any data that may have also been sent."
)
)
}
}
public func get() throws -> T {
switch self {
case let .data(data):
return data
case let .errors(errors):
throw errors
}
}
private enum CodingKeys: String, CodingKey {
case data
case type
case errors
}
}
public var body: Body
public var version: APIVersion {
APIVersion.GraphQL.v1
}
public let path = ""
public init(
rawQuery query: String,
variables: [String: JSONValue] = [:]
) {
self.body = Body(query: query, variables: .object(variables))
}
public func transformRequest(_ request: inout URLRequest) {
request.httpMethod = "POST"
}
public struct Errors: Swift.Error, Equatable, Hashable {
var errors: [Error]
var type: String?
}
public struct Error: Swift.Error, Equatable, Hashable, Decodable {
public var message: String
public var locations: [Location]?
public var path: [String]?
public var extensions: JSONValue?
public struct Location: Equatable, Hashable, Decodable {
public var line: Int
public var column: Int
}
}
}
extension GraphQL.Content: Equatable where T: Equatable {}
extension GraphQL.Content: Hashable where T: Hashable {}