diff --git a/.gitignore b/.gitignore index 4a519ee..5cd0309 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ .DS_Store /.build -/Example/.build +/.vscode +/Examples/.build /Packages /*.xcodeproj xcuserdata/ +Package.resolved diff --git a/.swift-format.json b/.swift-format.json new file mode 100644 index 0000000..f83fc91 --- /dev/null +++ b/.swift-format.json @@ -0,0 +1,56 @@ +{ + "fileScopedDeclarationPrivacy": { + "accessLevel": "private" + }, + "indentation": { + "spaces": 4 + }, + "indentConditionalCompilationBlocks": false, + "indentSwitchCaseLabels": false, + "lineBreakAroundMultilineExpressionChainComponents": true, + "lineBreakBeforeControlFlowKeywords": false, + "lineBreakBeforeEachArgument": true, + "lineBreakBeforeEachGenericRequirement": false, + "lineLength": 120, + "maximumBlankLines": 1, + "prioritizeKeepingFunctionOutputTogether": true, + "respectsExistingLineBreaks": true, + "rules": { + "AllPublicDeclarationsHaveDocumentation": false, + "AlwaysUseLowerCamelCase": true, + "AmbiguousTrailingClosureOverload": true, + "BeginDocumentationCommentWithOneLineSummary": false, + "DoNotUseSemicolons": true, + "DontRepeatTypeInStaticProperties": true, + "FileScopedDeclarationPrivacy": true, + "FullyIndirectEnum": true, + "GroupNumericLiterals": true, + "IdentifiersMustBeASCII": true, + "NeverForceUnwrap": false, + "NeverUseForceTry": false, + "NeverUseImplicitlyUnwrappedOptionals": false, + "NoAccessLevelOnExtensionDeclaration": true, + "NoBlockComments": true, + "NoCasesWithOnlyFallthrough": true, + "NoEmptyTrailingClosureParentheses": true, + "NoLabelsInCasePatterns": true, + "NoLeadingUnderscores": false, + "NoParensAroundConditions": true, + "NoVoidReturnOnFunctionSignature": true, + "OneCasePerLine": true, + "OneVariableDeclarationPerLine": true, + "OnlyOneTrailingClosureArgument": true, + "OrderedImports": true, + "ReturnVoidInsteadOfEmptyTuple": true, + "UseEarlyExits": true, + "UseLetInEveryBoundCaseVariable": true, + "UseShorthandTypeNames": true, + "UseSingleLinePropertyGetter": true, + "UseSynthesizedInitializer": true, + "UseTripleSlashForDocumentationComments": true, + "UseWhereClausesInForLoops": true, + "ValidateDocumentationComments": true + }, + "tabWidth": 8, + "version": 1 +} diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata index 706eede..919434a 100644 --- a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/Examples/graphql/GraphQLExample.swift b/Examples/graphql/GraphQLExample.swift new file mode 100644 index 0000000..9ffe641 --- /dev/null +++ b/Examples/graphql/GraphQLExample.swift @@ -0,0 +1,69 @@ +// +// main.swift +// graphql +// +// Created by Aaron Sky on 5/5/20. +// + +#if compiler(>=5.5.2) && canImport(_Concurrency) + +import Foundation +import Combine +import Buildkite + +struct MyPipeline: Codable { + var organization: Organization? + + struct Organization: Codable { + var pipelines: Pipelines + + struct Pipelines: Codable { + var edges: [PipelineEdge] + + struct PipelineEdge: Codable { + var node: Pipeline + + struct Pipeline: Codable { + var name: String + var uuid: UUID + } + } + } + } +} + +@available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) +@main struct GraphQLExample { + static func main() async { + let client = BuildkiteClient() + client.token = "..." + + let query = """ + query MyPipelines($first: Int!) { + organization(slug: "buildkite") { + pipelines(first: $first) { + edges { + node { + name + uuid + } + } + } + } + } + """ + + do { + let pipelines = try await client.sendQuery(GraphQL( + rawQuery: query, + variables: ["first": 30] + )) + print(pipelines) + } catch { + print(error) + exit(1) + } + } +} + +#endif diff --git a/Examples/graphql/main.swift b/Examples/graphql/main.swift deleted file mode 100644 index a0c657b..0000000 --- a/Examples/graphql/main.swift +++ /dev/null @@ -1,62 +0,0 @@ -// -// main.swift -// graphql -// -// Created by Aaron Sky on 5/5/20. -// - -import Foundation -import Combine -import Buildkite - -let client = BuildkiteClient() -client.token = "..." - -let query = """ -query MyPipelines($first: Int!) { - organization(slug: "buildkite") { - pipelines(first: $first) { - edges { - node { - name - uuid - } - } - } - } -} -""" - -struct MyPipeline: Codable { - var organization: Organization? - - struct Organization: Codable { - var pipelines: Pipelines - - struct Pipelines: Codable { - var edges: [PipelineEdge] - - struct PipelineEdge: Codable { - var node: Pipeline - - struct Pipeline: Codable { - var name: String - var uuid: UUID - } - } - } - } -} - -client.sendQuery(GraphQL(rawQuery: query, variables: ["first": 30])) { result in - do { - let pipelines = try result.get() - print(pipelines) - exit(0) - } catch { - print(error) - exit(1) - } -} - -RunLoop.main.run() diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..74c8d27 --- /dev/null +++ b/Makefile @@ -0,0 +1,30 @@ +SWIFT_FORMAT_BIN := swift format +GIT_REPO_TOPLEVEL := $(shell git rev-parse --show-toplevel) +SWIFT_FORMAT_CONFIG_FILE := $(GIT_REPO_TOPLEVEL)/.swift-format.json + +format: + $(SWIFT_FORMAT_BIN) \ + --configuration $(SWIFT_FORMAT_CONFIG_FILE) \ + --ignore-unparsable-files \ + --in-place \ + --recursive \ + $(GIT_REPO_TOPLEVEL)/Package.swift $(GIT_REPO_TOPLEVEL)/Sources $(GIT_REPO_TOPLEVEL)/Tests + +lint: + $(SWIFT_FORMAT_BIN) lint \ + --configuration $(SWIFT_FORMAT_CONFIG_FILE) \ + --ignore-unparsable-files \ + --recursive \ + $(GIT_REPO_TOPLEVEL)/Package.swift $(GIT_REPO_TOPLEVEL)/Sources $(GIT_REPO_TOPLEVEL)/Tests + +test: + swift test --parallel --enable-code-coverage + +coverage: test + xcrun llvm-cov export \ + -format=lcov \ + -instr-profile=.build/arm64-apple-macosx/debug/codecov/default.profdata \ + .build/arm64-apple-macosx/debug/BuildkitePackageTests.xctest/Contents/MacOS/BuildkitePackageTests \ + > lcov.info + +.PHONY: format lint test coverage diff --git a/Package.swift b/Package.swift index 7ed40c6..3b64a58 100644 --- a/Package.swift +++ b/Package.swift @@ -8,12 +8,13 @@ let package = Package( .iOS(.v10), .macOS(.v10_12), .tvOS(.v10), - .watchOS(.v3) + .watchOS(.v3), ], products: [ .library( name: "Buildkite", - targets: ["Buildkite"]) + targets: ["Buildkite"] + ) ], targets: [ .target( @@ -34,6 +35,13 @@ let package = Package( .testTarget( name: "BuildkiteTests", dependencies: ["Buildkite"] - ) + ), ] ) + +#if swift(>=5.6) +// Add the documentation compiler plugin if possible +package.dependencies.append( + .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0") +) +#endif diff --git a/Sources/Buildkite/Buildkite.docc/Buildkite.md b/Sources/Buildkite/Buildkite.docc/Buildkite.md index b6c3362..da98d35 100644 --- a/Sources/Buildkite/Buildkite.docc/Buildkite.md +++ b/Sources/Buildkite/Buildkite.docc/Buildkite.md @@ -22,6 +22,7 @@ The entire publicly documented REST API surface is supported by this package. - ``Build/Resources`` - ``Emoji/Resources`` - ``Job/Resources`` +- ``Meta/Resources`` - ``Organization/Resources`` - ``Pipeline/Resources`` - ``Team/Resources`` @@ -50,6 +51,7 @@ The entire publicly documented REST API surface is supported by this package. - ``Build`` - ``Emoji`` - ``Job`` +- ``Meta`` - ``Organization`` - ``Pipeline`` - ``Team`` diff --git a/Sources/Buildkite/BuildkiteClient.swift b/Sources/Buildkite/BuildkiteClient.swift index 35bf593..364a23c 100644 --- a/Sources/Buildkite/BuildkiteClient.swift +++ b/Sources/Buildkite/BuildkiteClient.swift @@ -47,12 +47,17 @@ public final class BuildkiteClient { /// - Parameters: /// - configuration: Configures supported API versions and the access token. Uses the latest supported API versions by default. See ``token`` for setting the client access token if using the default configuration. /// - transport: Transport layer used for API communication. Uses the shared URLSession by default. - public init(configuration: Configuration = .default, transport: Transport = URLSession.shared) { + public init( + configuration: Configuration = .default, + transport: Transport = URLSession.shared + ) { self.configuration = configuration self.transport = transport } - private func handleContentfulResponse(completion: @escaping (Result, Error>) -> Void) -> Transport.Completion { + private func handleContentfulResponse( + completion: @escaping (Result, Error>) -> Void + ) -> Transport.Completion { return { [weak self] result in guard let self = self else { return @@ -72,7 +77,9 @@ public final class BuildkiteClient { } } - private func handleEmptyResponse(completion: @escaping (Result, Error>) -> Void) -> Transport.Completion { + private func handleEmptyResponse( + completion: @escaping (Result, Error>) -> Void + ) -> Transport.Completion { return { [weak self] result in guard let self = self else { return @@ -91,14 +98,16 @@ public final class BuildkiteClient { private func checkResponseForIssues(_ response: URLResponse, data: Data? = nil) throws { guard let httpResponse = response as? HTTPURLResponse, - let statusCode = StatusCode(rawValue: httpResponse.statusCode) else { - throw ResponseError.incompatibleResponse(response) - } + let statusCode = StatusCode(rawValue: httpResponse.statusCode) + else { + throw ResponseError.incompatibleResponse(response) + } if !statusCode.isSuccess { guard let data = data, - let errorIntermediary = try? decoder.decode(BuildkiteError.Intermediary.self, from: data) else { - throw statusCode - } + let errorIntermediary = try? decoder.decode(BuildkiteError.Intermediary.self, from: data) + else { + throw statusCode + } throw BuildkiteError(statusCode: statusCode, intermediary: errorIntermediary) } } @@ -106,12 +115,13 @@ public final class BuildkiteClient { // MARK: - Closure API -public extension BuildkiteClient { +extension BuildkiteClient { /// Performs the given resource asynchronously, then calls a handler upon completion. /// - Parameters: /// - resource:A resource. /// - completion:The completion handler to call when the operation has completed. This handler is called on whatever queue the transport layer is implemented to use. You should generally assume this is happening on a global background queue, such as the case when using the shared URLSession. - func send(_ resource: R, completion: @escaping (Result, Error>) -> Void) where R: Resource, R.Content: Decodable { + public func send(_ resource: R, completion: @escaping (Result, Error>) -> Void) + where R: Resource, R.Content: Decodable { do { let request = try URLRequest(resource, configuration: configuration) transport.send(request: request, completion: handleContentfulResponse(completion: completion)) @@ -125,7 +135,11 @@ public extension BuildkiteClient { /// - resource:A resource. /// - pageOptions: Page options to perform pagination. /// - completion:The completion handler to call when the operation has completed. This handler is called on whatever queue the transport layer is implemented to use. You should generally assume this is happening on a global background queue, such as the case when using the shared URLSession. - func send(_ resource: R, pageOptions: PageOptions? = nil, completion: @escaping (Result, Error>) -> Void) where R: PaginatedResource { + public func send( + _ resource: R, + pageOptions: PageOptions? = nil, + completion: @escaping (Result, Error>) -> Void + ) where R: PaginatedResource { do { let request = try URLRequest(resource, configuration: configuration, pageOptions: pageOptions) transport.send(request: request, completion: handleContentfulResponse(completion: completion)) @@ -138,7 +152,8 @@ public extension BuildkiteClient { /// - Parameters: /// - resource:A resource. /// - completion:The completion handler to call when the operation has completed. This handler is called on whatever queue the transport layer is implemented to use. You should generally assume this is happening on a global background queue, such as the case when using the shared URLSession. - func send(_ resource: R, completion: @escaping (Result, Error>) -> Void) where R: Resource, R.Body: Encodable, R.Content: Decodable { + public func send(_ resource: R, completion: @escaping (Result, Error>) -> Void) + where R: Resource, R.Body: Encodable, R.Content: Decodable { do { let request = try URLRequest(resource, configuration: configuration, encoder: encoder) transport.send(request: request, completion: handleContentfulResponse(completion: completion)) @@ -152,9 +167,18 @@ public extension BuildkiteClient { /// - resource:A resource. /// - pageOptions: Page options to perform pagination. /// - completion:The completion handler to call when the operation has completed. This handler is called on whatever queue the transport layer is implemented to use. You should generally assume this is happening on a global background queue, such as the case when using the shared URLSession. - func send(_ resource: R, pageOptions: PageOptions? = nil, completion: @escaping (Result, Error>) -> Void) where R: PaginatedResource, R.Body: Encodable { + public func send( + _ resource: R, + pageOptions: PageOptions? = nil, + completion: @escaping (Result, Error>) -> Void + ) where R: PaginatedResource, R.Body: Encodable { do { - let request = try URLRequest(resource, configuration: configuration, encoder: encoder, pageOptions: pageOptions) + let request = try URLRequest( + resource, + configuration: configuration, + encoder: encoder, + pageOptions: pageOptions + ) transport.send(request: request, completion: handleContentfulResponse(completion: completion)) } catch { completion(.failure(error)) @@ -165,7 +189,8 @@ public extension BuildkiteClient { /// - Parameters: /// - resource:A resource. /// - completion:The completion handler to call when the operation has completed. This handler is called on whatever queue the transport layer is implemented to use. You should generally assume this is happening on a global background queue, such as the case when using the shared URLSession. - func send(_ resource: R, completion: @escaping (Result, Error>) -> Void) where R: Resource, R.Content == Void { + public func send(_ resource: R, completion: @escaping (Result, Error>) -> Void) + where R: Resource, R.Content == Void { do { let request = try URLRequest(resource, configuration: configuration) transport.send(request: request, completion: handleEmptyResponse(completion: completion)) @@ -178,7 +203,8 @@ public extension BuildkiteClient { /// - Parameters: /// - resource:A resource. /// - completion:The completion handler to call when the operation has completed. This handler is called on whatever queue the transport layer is implemented to use. You should generally assume this is happening on a global background queue, such as the case when using the shared URLSession. - func send(_ resource: R, completion: @escaping (Result, Error>) -> Void) where R: Resource, R.Body: Encodable, R.Content == Void { + public func send(_ resource: R, completion: @escaping (Result, Error>) -> Void) + where R: Resource, R.Body: Encodable, R.Content == Void { do { let request = try URLRequest(resource, configuration: configuration, encoder: encoder) transport.send(request: request, completion: handleEmptyResponse(completion: completion)) @@ -191,7 +217,7 @@ public extension BuildkiteClient { /// - Parameters: /// - resource:A resource. /// - completion:The completion handler to call when the operation has completed. This handler is called on whatever queue the transport layer is implemented to use. You should generally assume this is happening on a global background queue, such as the case when using the shared URLSession. - func sendQuery(_ resource: GraphQL, completion: @escaping (Result) -> Void) { + public func sendQuery(_ resource: GraphQL, completion: @escaping (Result) -> Void) { send(resource) { result in do { switch (try result.get()).content { @@ -213,20 +239,21 @@ public extension BuildkiteClient { import Combine @available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) -public extension BuildkiteClient { +extension BuildkiteClient { /// Performs the given resource and publishes the response asynchronously. /// - Parameter resource: A resource. /// - Returns: The publisher publishes the response when the operation completes, or terminates if the operation fails with an error. - func sendPublisher(_ resource: R) -> AnyPublisher, Error> where R: Resource, R.Content: Decodable { + public func sendPublisher(_ resource: R) -> AnyPublisher, Error> + where R: Resource, R.Content: Decodable { Result { try URLRequest(resource, configuration: configuration) } - .publisher - .flatMap(transport.sendPublisher) - .tryMap { - try self.checkResponseForIssues($0.response, data: $0.data) - let content = try self.decoder.decode(R.Content.self, from: $0.data) - return Response(content: content, response: $0.response) - } - .eraseToAnyPublisher() + .publisher + .flatMap(transport.sendPublisher) + .tryMap { + try self.checkResponseForIssues($0.response, data: $0.data) + let content = try self.decoder.decode(R.Content.self, from: $0.data) + return Response(content: content, response: $0.response) + } + .eraseToAnyPublisher() } /// Performs the given resource and publishes the response asynchronously. @@ -234,31 +261,35 @@ public extension BuildkiteClient { /// - resource: A resource. /// - pageOptions: Page options to perform pagination. /// - Returns: The publisher publishes the response when the operation completes, or terminates if the operation fails with an error. - func sendPublisher(_ resource: R, pageOptions: PageOptions? = nil) -> AnyPublisher, Error> where R: PaginatedResource { + public func sendPublisher( + _ resource: R, + pageOptions: PageOptions? = nil + ) -> AnyPublisher, Error> where R: PaginatedResource { Result { try URLRequest(resource, configuration: configuration, pageOptions: pageOptions) } - .publisher - .flatMap(transport.sendPublisher) - .tryMap { - try self.checkResponseForIssues($0.response, data: $0.data) - let content = try self.decoder.decode(R.Content.self, from: $0.data) - return Response(content: content, response: $0.response) - } - .eraseToAnyPublisher() + .publisher + .flatMap(transport.sendPublisher) + .tryMap { + try self.checkResponseForIssues($0.response, data: $0.data) + let content = try self.decoder.decode(R.Content.self, from: $0.data) + return Response(content: content, response: $0.response) + } + .eraseToAnyPublisher() } /// Performs the given resource and publishes the response asynchronously. /// - Parameter resource: A resource. /// - Returns: The publisher publishes the response when the operation completes, or terminates if the operation fails with an error. - func sendPublisher(_ resource: R) -> AnyPublisher, Error> where R: Resource, R.Body: Encodable, R.Content: Decodable { + public func sendPublisher(_ resource: R) -> AnyPublisher, Error> + where R: Resource, R.Body: Encodable, R.Content: Decodable { Result { try URLRequest(resource, configuration: configuration, encoder: encoder) } - .publisher - .flatMap(transport.sendPublisher) - .tryMap { - try self.checkResponseForIssues($0.response, data: $0.data) - let content = try self.decoder.decode(R.Content.self, from: $0.data) - return Response(content: content, response: $0.response) - } - .eraseToAnyPublisher() + .publisher + .flatMap(transport.sendPublisher) + .tryMap { + try self.checkResponseForIssues($0.response, data: $0.data) + let content = try self.decoder.decode(R.Content.self, from: $0.data) + return Response(content: content, response: $0.response) + } + .eraseToAnyPublisher() } /// Performs the given resource and publishes the response asynchronously. @@ -266,50 +297,55 @@ public extension BuildkiteClient { /// - resource: A resource. /// - pageOptions: Page options to perform pagination. /// - Returns: The publisher publishes the response when the operation completes, or terminates if the operation fails with an error. - func sendPublisher(_ resource: R, pageOptions: PageOptions? = nil) -> AnyPublisher, Error> where R: PaginatedResource, R.Body: Encodable { + public func sendPublisher( + _ resource: R, + pageOptions: PageOptions? = nil + ) -> AnyPublisher, Error> where R: PaginatedResource, R.Body: Encodable { Result { try URLRequest(resource, configuration: configuration, encoder: encoder, pageOptions: pageOptions) } - .publisher - .flatMap(transport.sendPublisher) - .tryMap { - try self.checkResponseForIssues($0.response, data: $0.data) - let content = try self.decoder.decode(R.Content.self, from: $0.data) - return Response(content: content, response: $0.response) - } - .eraseToAnyPublisher() + .publisher + .flatMap(transport.sendPublisher) + .tryMap { + try self.checkResponseForIssues($0.response, data: $0.data) + let content = try self.decoder.decode(R.Content.self, from: $0.data) + return Response(content: content, response: $0.response) + } + .eraseToAnyPublisher() } /// Performs the given resource and publishes the response asynchronously. /// - Parameter resource: A resource. /// - Returns: The publisher publishes the response when the operation completes, or terminates if the operation fails with an error. - func sendPublisher(_ resource: R) -> AnyPublisher, Error> where R: Resource, R.Content == Void { + public func sendPublisher(_ resource: R) -> AnyPublisher, Error> + where R: Resource, R.Content == Void { Result { try URLRequest(resource, configuration: configuration) } - .publisher - .flatMap(transport.sendPublisher) - .tryMap { - try self.checkResponseForIssues($0.response) - return Response(content: (), response: $0.response) - } - .eraseToAnyPublisher() + .publisher + .flatMap(transport.sendPublisher) + .tryMap { + try self.checkResponseForIssues($0.response) + return Response(content: (), response: $0.response) + } + .eraseToAnyPublisher() } /// Performs the given resource and publishes the response asynchronously. /// - Parameter resource: A resource. /// - Returns: The publisher publishes the response when the operation completes, or terminates if the operation fails with an error. - func sendPublisher(_ resource: R) -> AnyPublisher, Error> where R: Resource, R.Body: Encodable, R.Content == Void { + public func sendPublisher(_ resource: R) -> AnyPublisher, Error> + where R: Resource, R.Body: Encodable, R.Content == Void { Result { try URLRequest(resource, configuration: configuration, encoder: encoder) } - .publisher - .flatMap(transport.sendPublisher) - .tryMap { - try self.checkResponseForIssues($0.response) - return Response(content: (), response: $0.response) - } - .eraseToAnyPublisher() + .publisher + .flatMap(transport.sendPublisher) + .tryMap { + try self.checkResponseForIssues($0.response) + return Response(content: (), response: $0.response) + } + .eraseToAnyPublisher() } /// Performs the given GraphQL query or mutation and publishes the content asynchronously. /// - Parameter resource: A resource. /// - Returns: The publisher publishes the content when the operation completes, or terminates if the operation fails with an error. - func sendQueryPublisher(_ resource: GraphQL) -> AnyPublisher { + public func sendQueryPublisher(_ resource: GraphQL) -> AnyPublisher { sendPublisher(resource) .map(\.content) .tryMap { try $0.get() } @@ -323,11 +359,11 @@ public extension BuildkiteClient { // MARK: - Async/Await API @available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) -public extension BuildkiteClient { +extension BuildkiteClient { /// Performs the given resource asynchronously. /// - Parameter resource: A resource. /// - Returns: A response containing the content of the response body, as well as other information about the HTTP operation. - func send(_ resource: R) async throws -> Response where R: Resource, R.Content: Decodable { + public func send(_ resource: R) async throws -> Response where R: Resource, R.Content: Decodable { let request = try URLRequest(resource, configuration: configuration) let (data, response) = try await transport.send(request: request) try checkResponseForIssues(response, data: data) @@ -340,7 +376,8 @@ public extension BuildkiteClient { /// - resource: A resource. /// - pageOptions: Page options to perform pagination. /// - Returns: A response containing the content of the response body, as well as other information about the HTTP operation. - func send(_ resource: R, pageOptions: PageOptions? = nil) async throws -> Response where R: PaginatedResource { + public func send(_ resource: R, pageOptions: PageOptions? = nil) async throws -> Response + where R: PaginatedResource { let request = try URLRequest(resource, configuration: configuration, pageOptions: pageOptions) let (data, response) = try await transport.send(request: request) try checkResponseForIssues(response, data: data) @@ -351,7 +388,8 @@ public extension BuildkiteClient { /// Performs the given resource asynchronously. /// - Parameter resource: A resource. /// - Returns: A response containing the content of the response body, as well as other information about the HTTP operation. - func send(_ resource: R) async throws -> Response where R: Resource, R.Body: Encodable, R.Content: Decodable { + public func send(_ resource: R) async throws -> Response + where R: Resource, R.Body: Encodable, R.Content: Decodable { let request = try URLRequest(resource, configuration: configuration, encoder: encoder) let (data, response) = try await transport.send(request: request) try checkResponseForIssues(response, data: data) @@ -364,7 +402,8 @@ public extension BuildkiteClient { /// - resource: A resource. /// - pageOptions: Page options to perform pagination. /// - Returns: A response containing the content of the response body, as well as other information about the HTTP operation. - func send(_ resource: R, pageOptions: PageOptions? = nil) async throws -> Response where R: PaginatedResource, R.Body: Encodable { + public func send(_ resource: R, pageOptions: PageOptions? = nil) async throws -> Response + where R: PaginatedResource, R.Body: Encodable { let request = try URLRequest(resource, configuration: configuration, encoder: encoder, pageOptions: pageOptions) let (data, response) = try await transport.send(request: request) @@ -376,7 +415,7 @@ public extension BuildkiteClient { /// Performs the given resource asynchronously. /// - Parameter resource: A resource. /// - Returns: A response containing the content of the response body, as well as other information about the HTTP operation. - func send(_ resource: R) async throws -> Response where R: Resource, R.Content == Void { + public func send(_ resource: R) async throws -> Response where R: Resource, R.Content == Void { let request = try URLRequest(resource, configuration: configuration) let (data, response) = try await transport.send(request: request) try checkResponseForIssues(response, data: data) @@ -386,7 +425,8 @@ public extension BuildkiteClient { /// Performs the given resource asynchronously. /// - Parameter resource: A resource. /// - Returns: A response containing information about the HTTP operation, and no content. - func send(_ resource: R) async throws -> Response where R: Resource, R.Body: Encodable, R.Content == Void { + public func send(_ resource: R) async throws -> Response + where R: Resource, R.Body: Encodable, R.Content == Void { let request = try URLRequest(resource, configuration: configuration, encoder: encoder) let (data, response) = try await transport.send(request: request) try checkResponseForIssues(response, data: data) @@ -397,7 +437,7 @@ public extension BuildkiteClient { /// - Parameter resource: A resource. /// - Returns: Content of the resolved GraphQL operation. /// - Throws: An error either of type ``BuildkiteError`` or ``GraphQL/Errors``. - func sendQuery(_ resource: GraphQL) async throws -> T { + public func sendQuery(_ resource: GraphQL) async throws -> T { let response = try await send(resource) return try response.content.get() } diff --git a/Sources/Buildkite/Models/Job.swift b/Sources/Buildkite/Models/Job.swift index bd85e40..75e1e8a 100644 --- a/Sources/Buildkite/Models/Job.swift +++ b/Sources/Buildkite/Models/Job.swift @@ -25,7 +25,9 @@ public enum Job: Codable, Equatable { case trigger } - public init(from decoder: Decoder) throws { + public init( + from decoder: Decoder + ) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let type = try container.decode(Unassociated.self, forKey: .type) switch type { diff --git a/Sources/Buildkite/Models/Meta.swift b/Sources/Buildkite/Models/Meta.swift new file mode 100644 index 0000000..d2f97ff --- /dev/null +++ b/Sources/Buildkite/Models/Meta.swift @@ -0,0 +1,29 @@ +// +// Meta.swift +// Buildkite +// +// Created by Aaron Sky on 5/29/22. +// Copyright © 2022 Aaron Sky. All rights reserved. +// + +import Foundation + +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif + +public struct Meta: Codable, Equatable { + /// A list of IP addresses in CIDR notation that Buildkite uses to + /// send outbound traffic such as webhooks and commit statuses. + /// These are subject to change from time to time. + /// + /// Buildkite recommends checking for new addresses daily, and + /// will try to advertise new addresses for at least 7 days + /// before they are used. + public var webhookIPRanges: [String] + + private enum CodingKeys: String, CodingKey { + // This corresponds to the key "webhook_ips" from the Buildkite payload. + case webhookIPRanges = "webhookIps" + } +} diff --git a/Sources/Buildkite/Models/Pipeline.swift b/Sources/Buildkite/Models/Pipeline.swift index 88915bc..c5dce68 100644 --- a/Sources/Buildkite/Models/Pipeline.swift +++ b/Sources/Buildkite/Models/Pipeline.swift @@ -93,7 +93,9 @@ extension Pipeline { case trigger } - public init(from decoder: Decoder) throws { + public init( + from decoder: Decoder + ) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let type = try container.decode(Unassociated.self, forKey: .type) switch type { diff --git a/Sources/Buildkite/Networking/APIVersion.swift b/Sources/Buildkite/Networking/APIVersion.swift index 9586551..50d88ad 100644 --- a/Sources/Buildkite/Networking/APIVersion.swift +++ b/Sources/Buildkite/Networking/APIVersion.swift @@ -26,7 +26,10 @@ public struct APIVersion: Equatable { public let baseURL: URL public let version: String - init(baseURL: URL, version: String) { + init( + baseURL: URL, + version: String + ) { self.baseURL = baseURL self.version = version } diff --git a/Sources/Buildkite/Networking/Configuration.swift b/Sources/Buildkite/Networking/Configuration.swift index 01c0235..a97bb8c 100644 --- a/Sources/Buildkite/Networking/Configuration.swift +++ b/Sources/Buildkite/Networking/Configuration.swift @@ -18,11 +18,16 @@ public struct Configuration { public var graphQLVersion: APIVersion public static var `default`: Configuration { - .init(version: APIVersion.REST.v2, - graphQLVersion: APIVersion.GraphQL.v1) + .init( + version: APIVersion.REST.v2, + graphQLVersion: APIVersion.GraphQL.v1 + ) } - public init(version: APIVersion = APIVersion.REST.v2, graphQLVersion: APIVersion = APIVersion.GraphQL.v1) { + public init( + version: APIVersion = APIVersion.REST.v2, + graphQLVersion: APIVersion = APIVersion.GraphQL.v1 + ) { self.version = version self.graphQLVersion = graphQLVersion } diff --git a/Sources/Buildkite/Networking/Formatters.swift b/Sources/Buildkite/Networking/Formatters.swift index cd9dbc8..63e0850 100644 --- a/Sources/Buildkite/Networking/Formatters.swift +++ b/Sources/Buildkite/Networking/Formatters.swift @@ -49,7 +49,12 @@ enum Formatters { let container = try decoder.singleValueContainer() let dateString = try container.decode(String.self) guard let date = dateIfPossible(fromISO8601: dateString) else { - throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: container.codingPath, debugDescription: "Expected date string to be ISO8601-formatted.")) + throw DecodingError.dataCorrupted( + DecodingError.Context( + codingPath: container.codingPath, + debugDescription: "Expected date string to be ISO8601-formatted." + ) + ) } return date } diff --git a/Sources/Buildkite/Networking/JSONValue.swift b/Sources/Buildkite/Networking/JSONValue.swift index d251e16..f34a102 100644 --- a/Sources/Buildkite/Networking/JSONValue.swift +++ b/Sources/Buildkite/Networking/JSONValue.swift @@ -24,12 +24,12 @@ public enum JSONValue { indirect case object([String: JSONValue]) } -public extension JSONValue { - subscript(dynamicMember key: JSONValue) -> JSONValue? { +extension JSONValue { + public subscript(dynamicMember key: JSONValue) -> JSONValue? { self[key] } - subscript(_ key: JSONValue) -> JSONValue? { + public subscript(_ key: JSONValue) -> JSONValue? { if case let .number(key) = key { return self[Int(key)] } else if case let .string(key) = key { @@ -38,14 +38,14 @@ public extension JSONValue { return nil } - subscript(_ index: Int) -> JSONValue? { + public subscript(_ index: Int) -> JSONValue? { guard case let .array(array) = self else { return nil } return array[index] } - subscript(_ key: String) -> JSONValue? { + public subscript(_ key: String) -> JSONValue? { guard case let .object(object) = self else { return nil } @@ -76,7 +76,9 @@ extension JSONValue: Encodable { } extension JSONValue: Decodable { - public init(from decoder: Decoder) throws { + public init( + from decoder: Decoder + ) throws { let singleValueContainer = try decoder.singleValueContainer() if singleValueContainer.decodeNil() { @@ -94,49 +96,64 @@ extension JSONValue: Decodable { } else { throw DecodingError.dataCorruptedError( in: singleValueContainer, - debugDescription: "invalid JSON structure or the input was not JSON") + debugDescription: "invalid JSON structure or the input was not JSON" + ) } } } extension JSONValue: ExpressibleByNilLiteral { - public init(nilLiteral: Void) { + public init( + nilLiteral: Void + ) { self = .null } } extension JSONValue: ExpressibleByBooleanLiteral { - public init(booleanLiteral value: BooleanLiteralType) { + public init( + booleanLiteral value: BooleanLiteralType + ) { self = .bool(value) } } extension JSONValue: ExpressibleByIntegerLiteral { - public init(integerLiteral value: IntegerLiteralType) { + public init( + integerLiteral value: IntegerLiteralType + ) { self = .number(Double(value)) } } extension JSONValue: ExpressibleByFloatLiteral { - public init(floatLiteral value: FloatLiteralType) { + public init( + floatLiteral value: FloatLiteralType + ) { self = .number(value) } } extension JSONValue: ExpressibleByStringLiteral { - public init(stringLiteral value: StringLiteralType) { + public init( + stringLiteral value: StringLiteralType + ) { self = .string(value) } } extension JSONValue: ExpressibleByArrayLiteral { - public init(arrayLiteral elements: JSONValue...) { + public init( + arrayLiteral elements: JSONValue... + ) { self = .array(elements) } } extension JSONValue: ExpressibleByDictionaryLiteral { - public init(dictionaryLiteral elements: (String, JSONValue)...) { + public init( + dictionaryLiteral elements: (String, JSONValue)... + ) { self = .object(Dictionary(uniqueKeysWithValues: elements)) } } diff --git a/Sources/Buildkite/Networking/Pagination.swift b/Sources/Buildkite/Networking/Pagination.swift index c380560..f01e78c 100644 --- a/Sources/Buildkite/Networking/Pagination.swift +++ b/Sources/Buildkite/Networking/Pagination.swift @@ -18,13 +18,16 @@ public struct Page { public var firstPage: Int? public var lastPage: Int? - init?(for header: String) { + init?( + for header: String + ) { guard !header.isEmpty else { return nil } for link in header.split(separator: ",") { - let segments = link + let segments = + link .trimmingCharacters(in: .whitespacesAndNewlines) .split(separator: ";") guard @@ -36,8 +39,9 @@ public struct Page { let url = URLComponents(string: String(urlString.dropFirst().dropLast())), let pageString = url.queryItems?.first(where: { $0.name == "page" })?.value, - let page = Int(pageString) else { - continue + let page = Int(pageString) + else { + continue } for segment in segments.dropFirst() { @@ -62,7 +66,10 @@ public struct PageOptions { public var page: Int public var perPage: Int - public init(page: Int, perPage: Int) { + public init( + page: Int, + perPage: Int + ) { self.page = page self.perPage = perPage } @@ -71,8 +78,9 @@ public struct PageOptions { extension URLRequest { mutating func appendPageOptions(_ options: PageOptions) { guard let url = self.url, - var components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { - return + var components = URLComponents(url: url, resolvingAgainstBaseURL: true) + else { + return } var queryItems = components.queryItems ?? [] queryItems.append(pageOptions: options) @@ -81,8 +89,8 @@ extension URLRequest { } } -private extension Array where Element == URLQueryItem { - mutating func append(pageOptions: PageOptions) { +extension Array where Element == URLQueryItem { + fileprivate mutating func append(pageOptions: PageOptions) { append(URLQueryItem(name: "page", value: String(pageOptions.page))) append(URLQueryItem(name: "per_page", value: String(pageOptions.perPage))) } diff --git a/Sources/Buildkite/Networking/Resource.swift b/Sources/Buildkite/Networking/Resource.swift index 0d4bb6c..76b9832 100644 --- a/Sources/Buildkite/Networking/Resource.swift +++ b/Sources/Buildkite/Networking/Resource.swift @@ -43,11 +43,16 @@ extension Resource where Body == Void { public protocol PaginatedResource: Resource where Content: Decodable {} extension URLRequest { - init(_ resource: R, configuration: Configuration) throws { + init( + _ resource: R, + configuration: Configuration + ) throws { let version = resource.version - guard version == configuration.version - || version == configuration.graphQLVersion else { - throw ResourceError.incompatibleVersion(version) + guard + version == configuration.version + || version == configuration.graphQLVersion + else { + throw ResourceError.incompatibleVersion(version) } let url = version.url(for: resource.path) @@ -58,19 +63,32 @@ extension URLRequest { self = request } - init(_ resource: R, configuration: Configuration, encoder: JSONEncoder) throws where R.Body: Encodable { + init( + _ resource: R, + configuration: Configuration, + encoder: JSONEncoder + ) throws where R.Body: Encodable { try self.init(resource, configuration: configuration) httpBody = try encoder.encode(resource.body) } - init(_ resource: R, configuration: Configuration, pageOptions: PageOptions? = nil) throws { + init( + _ resource: R, + configuration: Configuration, + pageOptions: PageOptions? = nil + ) throws { try self.init(resource, configuration: configuration) if let options = pageOptions { appendPageOptions(options) } } - init(_ resource: R, configuration: Configuration, encoder: JSONEncoder, pageOptions: PageOptions? = nil) throws where R.Body: Encodable { + init( + _ resource: R, + configuration: Configuration, + encoder: JSONEncoder, + pageOptions: PageOptions? = nil + ) throws where R.Body: Encodable { try self.init(resource, configuration: configuration, encoder: encoder) if let options = pageOptions { appendPageOptions(options) diff --git a/Sources/Buildkite/Networking/Response.swift b/Sources/Buildkite/Networking/Response.swift index d6eb7e5..388e57f 100644 --- a/Sources/Buildkite/Networking/Response.swift +++ b/Sources/Buildkite/Networking/Response.swift @@ -17,7 +17,10 @@ public struct Response { public let response: URLResponse public let page: Page? - init(content: T, response: URLResponse) { + init( + content: T, + response: URLResponse + ) { self.content = content self.response = response if let response = response as? HTTPURLResponse, let link = response.allHeaderFields["Link"] as? String { @@ -37,7 +40,10 @@ public struct BuildkiteError: Error { public var message: String public var errors: [String] - init(statusCode: StatusCode, intermediary: Intermediary) { + init( + statusCode: StatusCode, + intermediary: Intermediary + ) { self.statusCode = statusCode self.message = intermediary.message ?? "" self.errors = intermediary.errors ?? [] diff --git a/Sources/Buildkite/Networking/Transport.swift b/Sources/Buildkite/Networking/Transport.swift index 8f16135..7eea91d 100644 --- a/Sources/Buildkite/Networking/Transport.swift +++ b/Sources/Buildkite/Networking/Transport.swift @@ -26,15 +26,15 @@ public protocol Transport { func send(request: URLRequest, completion: @escaping Completion) -#if canImport(Combine) + #if canImport(Combine) @available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) func sendPublisher(request: URLRequest) -> AnyPublisher -#endif + #endif -#if compiler(>=5.5.2) && canImport(_Concurrency) + #if compiler(>=5.5.2) && canImport(_Concurrency) @available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) func send(request: URLRequest) async throws -> Output -#endif + #endif } extension URLSession: Transport { @@ -53,31 +53,30 @@ extension URLSession: Transport { task.resume() } -#if canImport(Combine) + #if canImport(Combine) @available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) public func sendPublisher(request: URLRequest) -> AnyPublisher { dataTaskPublisher(for: request) .mapError { $0 as Error } .eraseToAnyPublisher() } -#endif + #endif -#if compiler(>=5.5.2) && canImport(_Concurrency) + #if compiler(>=5.5.2) && canImport(_Concurrency) @available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) public func send(request: URLRequest) async throws -> Output { - if #available(iOS 15, macOS 12, tvOS 15, watchOS 8, *) { - return try await data(for: request) - } else { + guard #available(iOS 15, macOS 12, tvOS 15, watchOS 8, *) else { return try await withCheckedThrowingContinuation { continuation in let task = dataTask(with: request) { data, response, error in if let error = error { continuation.resume(throwing: error) return } - + guard let data = data, - let response = response else { - continuation.resume(throwing: TransportError.noResponse) + let response = response + else { + continuation.resume(throwing: TransportError.noResponse) return } @@ -86,6 +85,7 @@ extension URLSession: Transport { task.resume() } } + return try await data(for: request) } -#endif + #endif } diff --git a/Sources/Buildkite/Networking/URL+Buildkite.swift b/Sources/Buildkite/Networking/URL+Buildkite.swift index c8dfcd8..9d02df0 100644 --- a/Sources/Buildkite/Networking/URL+Buildkite.swift +++ b/Sources/Buildkite/Networking/URL+Buildkite.swift @@ -40,25 +40,36 @@ extension Array where Element == URLQueryItem { } else if items.count == 1 { appendIfNeeded(items.first, forKey: key) } else { - append(contentsOf: items - .enumerated() - .map { - URLQueryItem(name: "\(key)\(arrayFormat.format(for: $0.offset))", - value: $0.element) - }) + append( + contentsOf: + items + .enumerated() + .map { + URLQueryItem( + name: "\(key)\(arrayFormat.format(for: $0.offset))", + value: $0.element + ) + } + ) } } mutating func append(_ items: [String: String], forKey key: String) { - append(contentsOf: items.map { - URLQueryItem(name: "\(key)[\($0.key)]", - value: $0.value) - }) + append( + contentsOf: items.map { + URLQueryItem( + name: "\(key)[\($0.key)]", + value: $0.value + ) + } + ) } } extension Date: LosslessStringConvertible { - public init?(_ description: String) { + public init?( + _ description: String + ) { guard let date = Formatters.dateIfPossible(fromISO8601: description) else { return nil } @@ -67,7 +78,9 @@ extension Date: LosslessStringConvertible { } extension UUID: LosslessStringConvertible { - public init?(_ description: String) { + public init?( + _ description: String + ) { guard let id = UUID(uuidString: description) else { return nil } diff --git a/Sources/Buildkite/Resources/Agents.swift b/Sources/Buildkite/Resources/Agents.swift index cf4c2ce..5844d05 100644 --- a/Sources/Buildkite/Resources/Agents.swift +++ b/Sources/Buildkite/Resources/Agents.swift @@ -37,14 +37,17 @@ extension Agent.Resources { "organizations/\(organization)/agents" } - public init(organization: String) { + public init( + organization: String + ) { self.organization = organization } public func transformRequest(_ request: inout URLRequest) { guard let url = request.url, - var components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { - return + var components = URLComponents(url: url, resolvingAgainstBaseURL: true) + else { + return } var queryItems: [URLQueryItem] = [] queryItems.appendIfNeeded(name, forKey: "name") @@ -67,7 +70,10 @@ extension Agent.Resources { "organizations/\(organization)/agents/\(agentId)" } - public init(organization: String, agentId: UUID) { + public init( + organization: String, + agentId: UUID + ) { self.organization = organization self.agentId = agentId } @@ -88,7 +94,9 @@ extension Agent.Resources { /// If the agent is currently processing a job, the job and the build will be canceled. public var force: Bool? - public init(force: Bool? = nil) { + public init( + force: Bool? = nil + ) { self.force = force } } @@ -97,7 +105,11 @@ extension Agent.Resources { "organizations/\(organization)/agents/\(agentId)/stop" } - public init(organization: String, agentId: UUID, force: Bool? = nil) { + public init( + organization: String, + agentId: UUID, + force: Bool? = nil + ) { self.organization = organization self.agentId = agentId self.body = Body(force: force) diff --git a/Sources/Buildkite/Resources/Annotations.swift b/Sources/Buildkite/Resources/Annotations.swift index ffd49d0..3d2db1b 100644 --- a/Sources/Buildkite/Resources/Annotations.swift +++ b/Sources/Buildkite/Resources/Annotations.swift @@ -33,7 +33,11 @@ extension Annotation.Resources { "organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/annotations" } - public init(organization: String, pipeline: String, build: Int) { + public init( + organization: String, + pipeline: String, + build: Int + ) { self.organization = organization self.pipeline = pipeline self.build = build diff --git a/Sources/Buildkite/Resources/Artifacts.swift b/Sources/Buildkite/Resources/Artifacts.swift index 614070a..d579751 100644 --- a/Sources/Buildkite/Resources/Artifacts.swift +++ b/Sources/Buildkite/Resources/Artifacts.swift @@ -33,7 +33,11 @@ extension Artifact.Resources { "organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/artifacts" } - public init(organization: String, pipeline: String, build: Int) { + public init( + organization: String, + pipeline: String, + build: Int + ) { self.organization = organization self.pipeline = pipeline self.build = build @@ -58,7 +62,12 @@ extension Artifact.Resources { "organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/jobs/\(jobId)/artifacts" } - public init(organization: String, pipeline: String, build: Int, jobId: UUID) { + public init( + organization: String, + pipeline: String, + build: Int, + jobId: UUID + ) { self.organization = organization self.pipeline = pipeline self.build = build @@ -84,7 +93,13 @@ extension Artifact.Resources { "organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/jobs/\(jobId)/artifacts/\(artifactId)" } - public init(organization: String, pipeline: String, build: Int, jobId: UUID, artifactId: UUID) { + public init( + organization: String, + pipeline: String, + build: Int, + jobId: UUID, + artifactId: UUID + ) { self.organization = organization self.pipeline = pipeline self.build = build @@ -113,7 +128,13 @@ extension Artifact.Resources { "organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/jobs/\(jobId)/artifacts/\(artifactId)/download" } - public init(organization: String, pipeline: String, build: Int, jobId: UUID, artifactId: UUID) { + public init( + organization: String, + pipeline: String, + build: Int, + jobId: UUID, + artifactId: UUID + ) { self.organization = organization self.pipeline = pipeline self.build = build @@ -141,7 +162,13 @@ extension Artifact.Resources { "organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/jobs/\(jobId)/artifacts/\(artifactId)" } - public init(organization: String, pipeline: String, build: Int, jobId: UUID, artifactId: UUID) { + public init( + organization: String, + pipeline: String, + build: Int, + jobId: UUID, + artifactId: UUID + ) { self.organization = organization self.pipeline = pipeline self.build = build @@ -174,13 +201,25 @@ extension Resource where Self == Artifact.Resources.Get { } extension Resource where Self == Artifact.Resources.Download { - public static func downloadArtifact(_ id: UUID, in organization: String, pipeline: String, build: Int, job: UUID) -> Self { + public static func downloadArtifact( + _ id: UUID, + in organization: String, + pipeline: String, + build: Int, + job: UUID + ) -> Self { Self(organization: organization, pipeline: pipeline, build: build, jobId: job, artifactId: id) } } extension Resource where Self == Artifact.Resources.Delete { - public static func deleteArtifact(_ id: UUID, in organization: String, pipeline: String, build: Int, job: UUID) -> Self { + public static func deleteArtifact( + _ id: UUID, + in organization: String, + pipeline: String, + build: Int, + job: UUID + ) -> Self { Self(organization: organization, pipeline: pipeline, build: build, jobId: job, artifactId: id) } } diff --git a/Sources/Buildkite/Resources/Builds.swift b/Sources/Buildkite/Resources/Builds.swift index 40adc9d..23c0cdc 100644 --- a/Sources/Buildkite/Resources/Builds.swift +++ b/Sources/Buildkite/Resources/Builds.swift @@ -27,14 +27,17 @@ extension Build.Resources { public var queryOptions: QueryOptions? - public init(queryOptions: Build.Resources.QueryOptions? = nil) { + public init( + queryOptions: Build.Resources.QueryOptions? = nil + ) { self.queryOptions = queryOptions } public func transformRequest(_ request: inout URLRequest) { guard let url = request.url, - var components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { - return + var components = URLComponents(url: url, resolvingAgainstBaseURL: true) + else { + return } var queryItems: [URLQueryItem] = [] if let options = queryOptions { @@ -60,15 +63,19 @@ extension Build.Resources { "organizations/\(organization)/builds" } - public init(organization: String, queryOptions: Build.Resources.QueryOptions? = nil) { + public init( + organization: String, + queryOptions: Build.Resources.QueryOptions? = nil + ) { self.organization = organization self.queryOptions = queryOptions } public func transformRequest(_ request: inout URLRequest) { guard let url = request.url, - var components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { - return + var components = URLComponents(url: url, resolvingAgainstBaseURL: true) + else { + return } var queryItems: [URLQueryItem] = [] if let options = queryOptions { @@ -95,7 +102,11 @@ extension Build.Resources { "organizations/\(organization)/pipelines/\(pipeline)/builds" } - public init(organization: String, pipeline: String, queryOptions: Build.Resources.QueryOptions? = nil) { + public init( + organization: String, + pipeline: String, + queryOptions: Build.Resources.QueryOptions? = nil + ) { self.organization = organization self.pipeline = pipeline self.queryOptions = queryOptions @@ -103,8 +114,9 @@ extension Build.Resources { public func transformRequest(_ request: inout URLRequest) { guard let url = request.url, - var components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { - return + var components = URLComponents(url: url, resolvingAgainstBaseURL: true) + else { + return } var queryItems: [URLQueryItem] = [] if let options = queryOptions { @@ -129,7 +141,11 @@ extension Build.Resources { "organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)" } - public init(organization: String, pipeline: String, build: Int) { + public init( + organization: String, + pipeline: String, + build: Int + ) { self.organization = organization self.pipeline = pipeline self.build = build @@ -151,7 +167,10 @@ extension Build.Resources { public var name: String public var email: String - public init(name: String, email: String) { + public init( + name: String, + email: String + ) { self.name = name self.email = email } @@ -181,7 +200,19 @@ extension Build.Resources { /// For a pull request build, the git repository of the pull request. public var pullRequestRepository: String? - public init(commit: String, branch: String, author: Author? = nil, cleanCheckout: Bool? = nil, env: [String: String]? = nil, ignorePipelineBranchFilters: Bool? = nil, message: String? = nil, metaData: [String: String]? = nil, pullRequestBaseBranch: String? = nil, pullRequestId: Int? = nil, pullRequestRepository: String? = nil) { + public init( + commit: String, + branch: String, + author: Author? = nil, + cleanCheckout: Bool? = nil, + env: [String: String]? = nil, + ignorePipelineBranchFilters: Bool? = nil, + message: String? = nil, + metaData: [String: String]? = nil, + pullRequestBaseBranch: String? = nil, + pullRequestId: Int? = nil, + pullRequestRepository: String? = nil + ) { self.commit = commit self.branch = branch self.author = author @@ -201,7 +232,11 @@ extension Build.Resources { "organizations/\(organization)/pipelines/\(pipeline)/builds" } - public init(organization: String, pipeline: String, body: Body) { + public init( + organization: String, + pipeline: String, + body: Body + ) { self.organization = organization self.pipeline = pipeline self.body = body @@ -228,7 +263,11 @@ extension Build.Resources { "organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/cancel" } - public init(organization: String, pipeline: String, build: Int) { + public init( + organization: String, + pipeline: String, + build: Int + ) { self.organization = organization self.pipeline = pipeline self.build = build @@ -255,7 +294,11 @@ extension Build.Resources { "organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/rebuild" } - public init(organization: String, pipeline: String, build: Int) { + public init( + organization: String, + pipeline: String, + build: Int + ) { self.organization = organization self.pipeline = pipeline self.build = build @@ -286,7 +329,17 @@ extension Build.Resources { /// Filters the results by the given build state. The finished state is a shortcut to automatically search for builds with passed, failed, blocked, canceled states. public var state: [Build.State] - public init(branches: [String] = [], commit: String? = nil, createdFrom: Date? = nil, createdTo: Date? = nil, creator: UUID? = nil, finishedFrom: Date? = nil, includeRetriedJobs: Bool? = nil, metadata: [String: String] = [:], state: [Build.State] = []) { + public init( + branches: [String] = [], + commit: String? = nil, + createdFrom: Date? = nil, + createdTo: Date? = nil, + creator: UUID? = nil, + finishedFrom: Date? = nil, + includeRetriedJobs: Bool? = nil, + metadata: [String: String] = [:], + state: [Build.State] = [] + ) { self.branches = branches self.commit = commit self.createdFrom = createdFrom @@ -300,8 +353,10 @@ extension Build.Resources { } } -private extension Array where Element == URLQueryItem { - init(queryOptions: Build.Resources.QueryOptions) { +extension Array where Element == URLQueryItem { + fileprivate init( + queryOptions: Build.Resources.QueryOptions + ) { self.init() append(queryOptions.branches, forKey: "branch") appendIfNeeded(queryOptions.commit, forKey: "commit") @@ -322,13 +377,18 @@ extension Resource where Self == Build.Resources.ListAll { } extension Resource where Self == Build.Resources.ListForOrganization { - public static func builds(inOrganization organization: String, options: Build.Resources.QueryOptions? = nil) -> Self { + public static func builds(inOrganization organization: String, options: Build.Resources.QueryOptions? = nil) -> Self + { Self(organization: organization, queryOptions: options) } } extension Resource where Self == Build.Resources.ListForPipeline { - public static func builds(forPipeline pipeline: String, in organization: String, options: Build.Resources.QueryOptions? = nil) -> Self { + public static func builds( + forPipeline pipeline: String, + in organization: String, + options: Build.Resources.QueryOptions? = nil + ) -> Self { Self(organization: organization, pipeline: pipeline, queryOptions: options) } } diff --git a/Sources/Buildkite/Resources/Emojis.swift b/Sources/Buildkite/Resources/Emojis.swift index bd9d395..8b9c16a 100644 --- a/Sources/Buildkite/Resources/Emojis.swift +++ b/Sources/Buildkite/Resources/Emojis.swift @@ -29,7 +29,9 @@ extension Emoji.Resources { "organizations/\(organization)/emojis" } - public init(organization: String) { + public init( + organization: String + ) { self.organization = organization } } diff --git a/Sources/Buildkite/Resources/Followable.swift b/Sources/Buildkite/Resources/Followable.swift index 9b75634..aaadc01 100644 --- a/Sources/Buildkite/Resources/Followable.swift +++ b/Sources/Buildkite/Resources/Followable.swift @@ -21,11 +21,15 @@ public struct Followable: Codable, Equatable, Resource { private var url: URL - init(url: URL) { + init( + url: URL + ) { self.url = url } - public init(from decoder: Decoder) throws { + public init( + from decoder: Decoder + ) throws { let container = try decoder.singleValueContainer() let url = try container.decode(URL.self) self.init(url: url) diff --git a/Sources/Buildkite/Resources/GraphQL.swift b/Sources/Buildkite/Resources/GraphQL.swift index 49b4b6f..89c4d3d 100644 --- a/Sources/Buildkite/Resources/GraphQL.swift +++ b/Sources/Buildkite/Resources/GraphQL.swift @@ -27,7 +27,9 @@ public struct GraphQL: Resource { case data(T) case errors(Errors) - public init(from decoder: Decoder) throws { + 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) @@ -38,7 +40,8 @@ public struct GraphQL: Resource { 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." + 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." ) ) } @@ -68,7 +71,10 @@ public struct GraphQL: Resource { public let path = "" - public init(rawQuery query: String, variables: [String: JSONValue] = [:]) { + public init( + rawQuery query: String, + variables: [String: JSONValue] = [:] + ) { self.body = Body(query: query, variables: .object(variables)) } diff --git a/Sources/Buildkite/Resources/Jobs.swift b/Sources/Buildkite/Resources/Jobs.swift index 20e26bc..9a62c48 100644 --- a/Sources/Buildkite/Resources/Jobs.swift +++ b/Sources/Buildkite/Resources/Jobs.swift @@ -35,7 +35,12 @@ extension Job.Resources { "organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/jobs/\(job)/retry" } - public init(organization: String, pipeline: String, build: Int, job: UUID) { + public init( + organization: String, + pipeline: String, + build: Int, + job: UUID + ) { self.organization = organization self.pipeline = pipeline self.build = build @@ -67,7 +72,10 @@ extension Job.Resources { public var unblocker: UUID? public var fields: [String: String] - public init(unblocker: UUID? = nil, fields: [String: String] = [:]) { + public init( + unblocker: UUID? = nil, + fields: [String: String] = [:] + ) { self.unblocker = unblocker self.fields = fields } @@ -77,7 +85,13 @@ extension Job.Resources { "organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/jobs/\(job)/unblock" } - public init(organization: String, pipeline: String, build: Int, job: UUID, body: Body) { + public init( + organization: String, + pipeline: String, + build: Int, + job: UUID, + body: Body + ) { self.organization = organization self.pipeline = pipeline self.build = build @@ -106,7 +120,12 @@ extension Job.Resources { "organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/jobs/\(job)/log" } - public init(organization: String, pipeline: String, build: Int, job: UUID) { + public init( + organization: String, + pipeline: String, + build: Int, + job: UUID + ) { self.organization = organization self.pipeline = pipeline self.build = build @@ -129,7 +148,12 @@ extension Job.Resources { "organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/jobs/\(job)/log" } - public init(organization: String, pipeline: String, build: Int, job: UUID) { + public init( + organization: String, + pipeline: String, + build: Int, + job: UUID + ) { self.organization = organization self.pipeline = pipeline self.build = build @@ -157,7 +181,12 @@ extension Job.Resources { "organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/jobs/\(job)/env" } - public init(organization: String, pipeline: String, build: Int, job: UUID) { + public init( + organization: String, + pipeline: String, + build: Int, + job: UUID + ) { self.organization = organization self.pipeline = pipeline self.build = build @@ -189,7 +218,13 @@ extension Job.Resources.LogOutput { "organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/jobs/\(job)/log.\(format)" } - public init(organization: String, pipeline: String, build: Int, job: UUID, format: Format) { + public init( + organization: String, + pipeline: String, + build: Int, + job: UUID, + format: Format + ) { self.organization = organization self.pipeline = pipeline self.build = build @@ -206,7 +241,13 @@ extension Resource where Self == Job.Resources.Retry { } extension Resource where Self == Job.Resources.Unblock { - public static func unblockJob(_ job: UUID, in organization: String, pipeline: String, build: Int, with body: Self.Body) -> Self { + public static func unblockJob( + _ job: UUID, + in organization: String, + pipeline: String, + build: Int, + with body: Self.Body + ) -> Self { Self(organization: organization, pipeline: pipeline, build: build, job: job, body: body) } } @@ -224,13 +265,24 @@ extension Resource where Self == Job.Resources.DeleteLogOutput { } extension Resource where Self == Job.Resources.EnvironmentVariables { - public static func environmentVariables(for job: UUID, in organization: String, pipeline: String, build: Int) -> Self { + public static func environmentVariables( + for job: UUID, + in organization: String, + pipeline: String, + build: Int + ) -> Self { Self(organization: organization, pipeline: pipeline, build: build, job: job) } } extension Resource where Self == Job.Resources.LogOutput.Alternative { - public static func logOutput(_ format: Job.Resources.LogOutput.Alternative.Format, for job: UUID, in organization: String, pipeline: String, build: Int) -> Self { + public static func logOutput( + _ format: Job.Resources.LogOutput.Alternative.Format, + for job: UUID, + in organization: String, + pipeline: String, + build: Int + ) -> Self { Self(organization: organization, pipeline: pipeline, build: build, job: job, format: format) } } diff --git a/Sources/Buildkite/Resources/Metas.swift b/Sources/Buildkite/Resources/Metas.swift new file mode 100644 index 0000000..448437b --- /dev/null +++ b/Sources/Buildkite/Resources/Metas.swift @@ -0,0 +1,39 @@ +// +// Metas.swift +// Buildkite +// +// Created by Aaron Sky on 5/29/22. +// Copyright © 2022 Aaron Sky. All rights reserved. +// + +import Foundation + +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif + +extension Meta { + /// Resources for requesting meta information about Buildkite. + public enum Resources {} +} + +extension Meta.Resources { + /// Get an object with properties describing Buildkite + /// + /// Returns meta information about Buildkite. + public struct Get: Resource { + public typealias Content = Meta + public let path = "meta" + + public init() {} + } +} + +extension Resource where Self == Meta.Resources.Get { + /// Get an object with properties describing Buildkite + /// + /// Returns meta information about Buildkite. + public static var meta: Self { + Self() + } +} diff --git a/Sources/Buildkite/Resources/Organizations.swift b/Sources/Buildkite/Resources/Organizations.swift index d18eacc..5c6b740 100644 --- a/Sources/Buildkite/Resources/Organizations.swift +++ b/Sources/Buildkite/Resources/Organizations.swift @@ -38,7 +38,9 @@ extension Organization.Resources { "organizations/\(organization)" } - public init(organization: String) { + public init( + organization: String + ) { self.organization = organization } } diff --git a/Sources/Buildkite/Resources/Pipelines.swift b/Sources/Buildkite/Resources/Pipelines.swift index 7cb56c1..b2c9ffa 100644 --- a/Sources/Buildkite/Resources/Pipelines.swift +++ b/Sources/Buildkite/Resources/Pipelines.swift @@ -30,7 +30,9 @@ extension Pipeline.Resources { "organizations/\(organization)/pipelines" } - public init(organization: String) { + public init( + organization: String + ) { self.organization = organization } } @@ -47,7 +49,10 @@ extension Pipeline.Resources { "organizations/\(organization)/pipelines/\(pipeline)" } - public init(organization: String, pipeline: String) { + public init( + organization: String, + pipeline: String + ) { self.organization = organization self.pipeline = pipeline } @@ -88,7 +93,20 @@ extension Pipeline.Resources { /// An array of team UUIDs to add this pipeline to. You can find your team’s UUID either via the GraphQL API, or on the settings page for a team. This property is only available if your organization has enabled Teams. public var teamUUIDs: [UUID]? - public init(name: String, repository: URL, configuration: String, branchConfiguration: String? = nil, cancelRunningBranchBuilds: Bool? = nil, cancelRunningBranchBuildsFilter: String? = nil, defaultBranch: String? = nil, description: String? = nil, providerSettings: Pipeline.Provider.Settings? = nil, skipQueuedBranchBuilds: Bool? = nil, skipQueuedBranchBuildsFilter: String? = nil, teamUUIDs: [UUID]? = nil) { + public init( + name: String, + repository: URL, + configuration: String, + branchConfiguration: String? = nil, + cancelRunningBranchBuilds: Bool? = nil, + cancelRunningBranchBuildsFilter: String? = nil, + defaultBranch: String? = nil, + description: String? = nil, + providerSettings: Pipeline.Provider.Settings? = nil, + skipQueuedBranchBuilds: Bool? = nil, + skipQueuedBranchBuildsFilter: String? = nil, + teamUUIDs: [UUID]? = nil + ) { self.name = name self.repository = repository self.configuration = configuration @@ -108,7 +126,10 @@ extension Pipeline.Resources { "organizations/\(organization)/pipelines" } - public init(organization: String, body: Body) { + public init( + organization: String, + body: Body + ) { self.organization = organization self.body = body } @@ -155,7 +176,21 @@ extension Pipeline.Resources { /// An array of team UUIDs to add this pipeline to. You can find your team’s UUID either via the GraphQL API, or on the settings page for a team. This property is only available if your organization has enabled Teams. public var teamUUIDs: [UUID]? - public init(name: String, repository: URL, steps: [Pipeline.Step], branchConfiguration: String? = nil, cancelRunningBranchBuilds: Bool? = nil, cancelRunningBranchBuildsFilter: String? = nil, defaultBranch: String? = nil, description: String? = nil, env: [String: String]? = nil, providerSettings: Pipeline.Provider.Settings? = nil, skipQueuedBranchBuilds: Bool? = nil, skipQueuedBranchBuildsFilter: String? = nil, teamUUIDs: [UUID]? = nil) { + public init( + name: String, + repository: URL, + steps: [Pipeline.Step], + branchConfiguration: String? = nil, + cancelRunningBranchBuilds: Bool? = nil, + cancelRunningBranchBuildsFilter: String? = nil, + defaultBranch: String? = nil, + description: String? = nil, + env: [String: String]? = nil, + providerSettings: Pipeline.Provider.Settings? = nil, + skipQueuedBranchBuilds: Bool? = nil, + skipQueuedBranchBuildsFilter: String? = nil, + teamUUIDs: [UUID]? = nil + ) { self.name = name self.repository = repository self.steps = steps @@ -176,7 +211,10 @@ extension Pipeline.Resources { "organizations/\(organization)/pipelines" } - public init(organization: String, body: Body) { + public init( + organization: String, + body: Body + ) { self.organization = organization self.body = body } @@ -226,7 +264,21 @@ extension Pipeline.Resources { /// Whether the pipeline is visible to everyone, including users outside this organization. public var visibility: String? - public init(branchConfiguration: String? = nil, cancelRunningBranchBuilds: Bool? = nil, cancelRunningBranchBuildsFilter: String? = nil, defaultBranch: String? = nil, description: String? = nil, env: [String: String]? = nil, name: String? = nil, providerSettings: Pipeline.Provider.Settings? = nil, repository: URL? = nil, steps: [Pipeline.Step]? = nil, skipQueuedBranchBuilds: Bool? = nil, skipQueuedBranchBuildsFilter: String? = nil, visibility: String? = nil) { + public init( + branchConfiguration: String? = nil, + cancelRunningBranchBuilds: Bool? = nil, + cancelRunningBranchBuildsFilter: String? = nil, + defaultBranch: String? = nil, + description: String? = nil, + env: [String: String]? = nil, + name: String? = nil, + providerSettings: Pipeline.Provider.Settings? = nil, + repository: URL? = nil, + steps: [Pipeline.Step]? = nil, + skipQueuedBranchBuilds: Bool? = nil, + skipQueuedBranchBuildsFilter: String? = nil, + visibility: String? = nil + ) { self.branchConfiguration = branchConfiguration self.cancelRunningBranchBuilds = cancelRunningBranchBuilds self.cancelRunningBranchBuildsFilter = cancelRunningBranchBuildsFilter @@ -247,7 +299,11 @@ extension Pipeline.Resources { "organizations/\(organization)/pipelines/\(pipeline)" } - public init(organization: String, pipeline: String, body: Body) { + public init( + organization: String, + pipeline: String, + body: Body + ) { self.organization = organization self.pipeline = pipeline self.body = body @@ -266,7 +322,10 @@ extension Pipeline.Resources { "organizations/\(organization)/pipelines/\(pipeline)/archive" } - public init(organization: String, pipeline: String) { + public init( + organization: String, + pipeline: String + ) { self.organization = organization self.pipeline = pipeline } @@ -288,7 +347,10 @@ extension Pipeline.Resources { "organizations/\(organization)/pipelines/\(pipeline)/unarchive" } - public init(organization: String, pipeline: String) { + public init( + organization: String, + pipeline: String + ) { self.organization = organization self.pipeline = pipeline } @@ -309,7 +371,10 @@ extension Pipeline.Resources { "organizations/\(organization)/pipelines/\(pipeline)" } - public init(organization: String, pipeline: String) { + public init( + organization: String, + pipeline: String + ) { self.organization = organization self.pipeline = pipeline } @@ -326,7 +391,10 @@ extension Pipeline.Resources { "organizations/\(organization)/pipelines/\(pipeline)/webhook" } - public init(organization: String, pipeline: String) { + public init( + organization: String, + pipeline: String + ) { self.organization = organization self.pipeline = pipeline } diff --git a/Sources/Buildkite/Resources/Teams.swift b/Sources/Buildkite/Resources/Teams.swift index 90b0a29..91391ab 100644 --- a/Sources/Buildkite/Resources/Teams.swift +++ b/Sources/Buildkite/Resources/Teams.swift @@ -30,15 +30,19 @@ extension Team.Resources { "organizations/\(organization)/teams" } - public init(organization: String, userId: UUID? = nil) { + public init( + organization: String, + userId: UUID? = nil + ) { self.organization = organization self.userId = userId } public func transformRequest(_ request: inout URLRequest) { guard let url = request.url, - var components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { - return + var components = URLComponents(url: url, resolvingAgainstBaseURL: true) + else { + return } var queryItems: [URLQueryItem] = [] queryItems.appendIfNeeded(userId, forKey: "user_id") diff --git a/Tests/BuildkiteTests/BuildkiteClientTests.swift b/Tests/BuildkiteTests/BuildkiteClientTests.swift index 1aba578..c92f1fc 100644 --- a/Tests/BuildkiteTests/BuildkiteClientTests.swift +++ b/Tests/BuildkiteTests/BuildkiteClientTests.swift @@ -8,6 +8,7 @@ import Foundation import XCTest + @testable import Buildkite #if canImport(FoundationNetworking) @@ -33,24 +34,37 @@ class BuildkiteClientTests: XCTestCase { var client: BuildkiteClient var resources = MockResources() - init(testCase: Case = .success) throws { + init( + testCase: Case = .success + ) throws { let responses: [(Data, URLResponse)] switch testCase { case .success: responses = [try MockData.mockingSuccess(with: resources.content, url: configuration.version.baseURL)] case .successPaginated: - responses = [try MockData.mockingSuccess(with: resources.paginatedContent, url: configuration.version.baseURL)] + responses = [ + try MockData.mockingSuccess(with: resources.paginatedContent, url: configuration.version.baseURL) + ] case .successNoContent: responses = [MockData.mockingSuccessNoContent(url: configuration.version.baseURL)] case .successHasBody: responses = [MockData.mockingSuccessNoContent(url: configuration.version.baseURL)] case .successHasBodyAndContent: - responses = [try MockData.mockingSuccess(with: resources.bodyAndContent, url: configuration.version.baseURL)] + responses = [ + try MockData.mockingSuccess(with: resources.bodyAndContent, url: configuration.version.baseURL) + ] case .successHasBodyPaginated: - responses = [try MockData.mockingSuccess(with: resources.bodyAndPaginatedContent, url: configuration.version.baseURL)] + responses = [ + try MockData.mockingSuccess( + with: resources.bodyAndPaginatedContent, + url: configuration.version.baseURL + ) + ] case .successGraphQL: - responses = [try MockData.mockingSuccess(with: resources.graphQLIntermediary, url: configuration.version.baseURL)] + responses = [ + try MockData.mockingSuccess(with: resources.graphQLIntermediary, url: configuration.version.baseURL) + ] case .badResponse: responses = [MockData.mockingIncompatibleResponse(for: configuration.version.baseURL)] case .unsuccessfulResponse: @@ -59,8 +73,10 @@ class BuildkiteClientTests: XCTestCase { responses = [] } - client = BuildkiteClient(configuration: configuration, - transport: MockTransport(responses: responses)) + client = BuildkiteClient( + configuration: configuration, + transport: MockTransport(responses: responses) + ) client.token = "a valid token, i guess" XCTAssertEqual(client.token, client.configuration.token) } @@ -108,7 +124,10 @@ extension BuildkiteClientTests { let testData = try TestData(testCase: .success) let expectation = XCTestExpectation() - testData.client.send(testData.resources.paginatedContentResource, pageOptions: PageOptions(page: 1, perPage: 30)) { result in + testData.client.send( + testData.resources.paginatedContentResource, + pageOptions: PageOptions(page: 1, perPage: 30) + ) { result in do { let response = try result.get() XCTAssertEqual(testData.resources.paginatedContent, response.content) @@ -161,7 +180,10 @@ extension BuildkiteClientTests { let testData = try TestData(testCase: .successHasBodyPaginated) let expectation = XCTestExpectation() - testData.client.send(testData.resources.bodyAndPaginatedResource, pageOptions: PageOptions(page: 1, perPage: 30)) { result in + testData.client.send( + testData.resources.bodyAndPaginatedResource, + pageOptions: PageOptions(page: 1, perPage: 30) + ) { result in do { let response = try result.get() XCTAssertEqual(testData.resources.bodyAndPaginatedContent, response.content) @@ -246,13 +268,15 @@ extension BuildkiteClientTests { let expectation = XCTestExpectation() var cancellables: Set = [] testData.client.sendPublisher(testData.resources.contentResource) - .sink(receiveCompletion: { - if case let .failure(error) = $0 { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - }, - receiveValue: { XCTAssertEqual(testData.resources.content, $0.content) }) + .sink( + receiveCompletion: { + if case let .failure(error) = $0 { + XCTFail(error.localizedDescription) + } + expectation.fulfill() + }, + receiveValue: { XCTAssertEqual(testData.resources.content, $0.content) } + ) .store(in: &cancellables) wait(for: [expectation]) } @@ -261,17 +285,20 @@ extension BuildkiteClientTests { let testData = try TestData(testCase: .success) let expectation = XCTestExpectation() var cancellables: Set = [] - testData.client.sendPublisher(testData.resources.paginatedContentResource, pageOptions: PageOptions(page: 1, perPage: 30)) - .sink(receiveCompletion: { - if case let .failure(error) = $0 { - XCTFail(error.localizedDescription) + testData.client + .sendPublisher(testData.resources.paginatedContentResource, pageOptions: PageOptions(page: 1, perPage: 30)) + .sink( + receiveCompletion: { + if case let .failure(error) = $0 { + XCTFail(error.localizedDescription) + } + expectation.fulfill() + }, + receiveValue: { + XCTAssertEqual(testData.resources.paginatedContent, $0.content) + XCTAssertNotNil($0.page) } - expectation.fulfill() - }, - receiveValue: { - XCTAssertEqual(testData.resources.paginatedContent, $0.content) - XCTAssertNotNil($0.page) - }) + ) .store(in: &cancellables) wait(for: [expectation]) } @@ -281,12 +308,15 @@ extension BuildkiteClientTests { let expectation = XCTestExpectation() var cancellables: Set = [] testData.client.sendPublisher(testData.resources.noContentNoBodyResource) - .sink(receiveCompletion: { - if case let .failure(error) = $0 { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - }, receiveValue: { _ in }) + .sink( + receiveCompletion: { + if case let .failure(error) = $0 { + XCTFail(error.localizedDescription) + } + expectation.fulfill() + }, + receiveValue: { _ in } + ) .store(in: &cancellables) wait(for: [expectation]) } @@ -296,12 +326,15 @@ extension BuildkiteClientTests { let expectation = XCTestExpectation() var cancellables: Set = [] testData.client.sendPublisher(testData.resources.bodyResource) - .sink(receiveCompletion: { - if case let .failure(error) = $0 { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - }, receiveValue: { _ in }) + .sink( + receiveCompletion: { + if case let .failure(error) = $0 { + XCTFail(error.localizedDescription) + } + expectation.fulfill() + }, + receiveValue: { _ in } + ) .store(in: &cancellables) wait(for: [expectation]) } @@ -311,15 +344,17 @@ extension BuildkiteClientTests { let expectation = XCTestExpectation() var cancellables: Set = [] testData.client.sendPublisher(testData.resources.bodyAndContentResource) - .sink(receiveCompletion: { - if case let .failure(error) = $0 { - XCTFail(error.localizedDescription) + .sink( + receiveCompletion: { + if case let .failure(error) = $0 { + XCTFail(error.localizedDescription) + } + expectation.fulfill() + }, + receiveValue: { + XCTAssertEqual(testData.resources.bodyAndContent, $0.content) } - expectation.fulfill() - }, - receiveValue: { - XCTAssertEqual(testData.resources.bodyAndContent, $0.content) - }) + ) .store(in: &cancellables) wait(for: [expectation]) } @@ -328,17 +363,20 @@ extension BuildkiteClientTests { let testData = try TestData(testCase: .successHasBodyPaginated) let expectation = XCTestExpectation() var cancellables: Set = [] - testData.client.sendPublisher(testData.resources.bodyAndPaginatedResource, pageOptions: PageOptions(page: 1, perPage: 30)) - .sink(receiveCompletion: { - if case let .failure(error) = $0 { - XCTFail(error.localizedDescription) + testData.client + .sendPublisher(testData.resources.bodyAndPaginatedResource, pageOptions: PageOptions(page: 1, perPage: 30)) + .sink( + receiveCompletion: { + if case let .failure(error) = $0 { + XCTFail(error.localizedDescription) + } + expectation.fulfill() + }, + receiveValue: { + XCTAssertEqual(testData.resources.bodyAndPaginatedContent, $0.content) + XCTAssertNotNil($0.page) } - expectation.fulfill() - }, - receiveValue: { - XCTAssertEqual(testData.resources.bodyAndPaginatedContent, $0.content) - XCTAssertNotNil($0.page) - }) + ) .store(in: &cancellables) wait(for: [expectation]) } @@ -348,14 +386,17 @@ extension BuildkiteClientTests { let expectation = XCTestExpectation() var cancellables: Set = [] testData.client.sendQueryPublisher(testData.resources.graphQLResource) - .sink(receiveCompletion: { - if case let .failure(error) = $0 { - XCTFail(error.localizedDescription) + .sink( + receiveCompletion: { + if case let .failure(error) = $0 { + XCTFail(error.localizedDescription) + } + expectation.fulfill() + }, + receiveValue: { + XCTAssertEqual(testData.resources.graphQLContent, $0) } - expectation.fulfill() - }, receiveValue: { - XCTAssertEqual(testData.resources.graphQLContent, $0) - }) + ) .store(in: &cancellables) wait(for: [expectation]) } @@ -365,12 +406,15 @@ extension BuildkiteClientTests { let expectation = XCTestExpectation() var cancellables: Set = [] testData.client.sendPublisher(testData.resources.contentResource) - .sink(receiveCompletion: { - if case .finished = $0 { - XCTFail("Expected to have failed with an error, but publisher fulfilled normally") - } - expectation.fulfill() - }, receiveValue: { _ in }) + .sink( + receiveCompletion: { + if case .finished = $0 { + XCTFail("Expected to have failed with an error, but publisher fulfilled normally") + } + expectation.fulfill() + }, + receiveValue: { _ in } + ) .store(in: &cancellables) wait(for: [expectation]) } @@ -380,12 +424,15 @@ extension BuildkiteClientTests { let expectation = XCTestExpectation() var cancellables: Set = [] testData.client.sendPublisher(testData.resources.contentResource) - .sink(receiveCompletion: { - if case .finished = $0 { - XCTFail("Expected to have failed with an error, but publisher fulfilled normally") - } - expectation.fulfill() - }, receiveValue: { _ in }) + .sink( + receiveCompletion: { + if case .finished = $0 { + XCTFail("Expected to have failed with an error, but publisher fulfilled normally") + } + expectation.fulfill() + }, + receiveValue: { _ in } + ) .store(in: &cancellables) wait(for: [expectation]) } @@ -405,8 +452,10 @@ extension BuildkiteClientTests { func testAsyncBasedRequestWithPagination() async throws { let testData = try TestData(testCase: .success) - let response = try await testData.client.send(testData.resources.paginatedContentResource, - pageOptions: PageOptions(page: 1, perPage: 30)) + let response = try await testData.client.send( + testData.resources.paginatedContentResource, + pageOptions: PageOptions(page: 1, perPage: 30) + ) XCTAssertEqual(testData.resources.paginatedContent, response.content) XCTAssertNotNil(response.page) } @@ -429,8 +478,10 @@ extension BuildkiteClientTests { func testAsyncBasedRequestHasBodyWithPagination() async throws { let testData = try TestData(testCase: .successHasBodyPaginated) - let response = try await testData.client.send(testData.resources.bodyAndPaginatedResource, - pageOptions: PageOptions(page: 1, perPage: 30)) + let response = try await testData.client.send( + testData.resources.bodyAndPaginatedResource, + pageOptions: PageOptions(page: 1, perPage: 30) + ) XCTAssertEqual(testData.resources.bodyAndPaginatedContent, response.content) XCTAssertNotNil(response.page) } diff --git a/Tests/BuildkiteTests/Networking/JSONValueTests.swift b/Tests/BuildkiteTests/Networking/JSONValueTests.swift index 2b22a35..87452f7 100644 --- a/Tests/BuildkiteTests/Networking/JSONValueTests.swift +++ b/Tests/BuildkiteTests/Networking/JSONValueTests.swift @@ -10,6 +10,7 @@ import Foundation import XCTest + @testable import Buildkite #if canImport(FoundationNetworking) @@ -19,40 +20,40 @@ import FoundationNetworking final class JSONValueTests: XCTestCase { func testDecodeArray() throws { let json = """ -{ "fooKey": [true, 1] } -""" + { "fooKey": [true, 1] } + """ let expected = JSONValue.object(["fooKey": .array([.bool(true), .number(1)])]) try decodingTest(json, expected) } func testDecodeBool() throws { let json = """ -{ "fooKey": true } -""" + { "fooKey": true } + """ let expected = JSONValue.object(["fooKey": .bool(true)]) try decodingTest(json, expected) } func testDecodeDouble() throws { let json = """ -{ "fooKey": 1.2345 } -""" + { "fooKey": 1.2345 } + """ let expected = JSONValue.object(["fooKey": .number(1.2345)]) try decodingTest(json, expected) } func testDecodeNull() throws { let json = """ -{ "fooKey": null } -""" + { "fooKey": null } + """ let expected = JSONValue.object(["fooKey": .null]) try decodingTest(json, expected) } func testDecodeString() throws { let json = """ -{ "fooKey": "fooVal" } -""" + { "fooKey": "fooVal" } + """ let expected = JSONValue.object(["fooKey": .string("fooVal")]) let data = try XCTUnwrap(json.data(using: .utf8)) let actual = try JSONDecoder().decode(JSONValue.self, from: data) @@ -94,16 +95,15 @@ final class JSONValueTests: XCTestCase { 123, [ "abc": false, - "qqq": [:] - ] + "qqq": [:], + ], - ] - , + ], "qux": [ "1": nil, "2": "2", - "3": 33333333.0 - ] + "3": 33333333.0, + ], ]) } diff --git a/Tests/BuildkiteTests/Networking/StatusCodeTests.swift b/Tests/BuildkiteTests/Networking/StatusCodeTests.swift index 9810144..c4da158 100644 --- a/Tests/BuildkiteTests/Networking/StatusCodeTests.swift +++ b/Tests/BuildkiteTests/Networking/StatusCodeTests.swift @@ -8,6 +8,7 @@ import Foundation import XCTest + @testable import Buildkite #if canImport(FoundationNetworking) diff --git a/Tests/BuildkiteTests/Networking/TransportTests.swift b/Tests/BuildkiteTests/Networking/TransportTests.swift index 09cfda0..40ae349 100644 --- a/Tests/BuildkiteTests/Networking/TransportTests.swift +++ b/Tests/BuildkiteTests/Networking/TransportTests.swift @@ -6,10 +6,11 @@ // Copyright © 2020 Aaron Sky. All rights reserved. // -@testable import Buildkite import Foundation import XCTest +@testable import Buildkite + #if canImport(FoundationNetworking) import FoundationNetworking #endif @@ -68,24 +69,26 @@ extension TransportTests { func testURLSessionSendClosureBasedRequest() { let request = URLRequest(url: URL()) let expectation = XCTestExpectation() - createSession().send(request: request) { - if case let .failure(error) = $0 { - XCTFail(error.localizedDescription) + createSession() + .send(request: request) { + if case let .failure(error) = $0 { + XCTFail(error.localizedDescription) + } + expectation.fulfill() } - expectation.fulfill() - } wait(for: [expectation]) } func testURLSessionSendClosureBasedRequestFailure() { let request = URLRequest(url: URL()) let expectation = XCTestExpectation() - createSession(testCase: .error).send(request: request) { - if case .success(_) = $0 { - XCTFail("Expected to have failed with an error, but closure fulfilled normally") + createSession(testCase: .error) + .send(request: request) { + if case .success(_) = $0 { + XCTFail("Expected to have failed with an error, but closure fulfilled normally") + } + expectation.fulfill() } - expectation.fulfill() - } wait(for: [expectation]) } } @@ -103,14 +106,17 @@ extension TransportTests { var cancellables: Set = [] createSession() .sendPublisher(request: request) - .sink(receiveCompletion: { - if case let .failure(error) = $0 { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - }, receiveValue: { _ in }) + .sink( + receiveCompletion: { + if case let .failure(error) = $0 { + XCTFail(error.localizedDescription) + } + expectation.fulfill() + }, + receiveValue: { _ in } + ) .store(in: &cancellables) - wait(for: [expectation]) + wait(for: [expectation]) } func testURLSessionSendPublisherBasedRequestFailure() { @@ -119,14 +125,17 @@ extension TransportTests { var cancellables: Set = [] createSession(testCase: .error) .sendPublisher(request: request) - .sink(receiveCompletion: { - if case .finished = $0 { - XCTFail("Expected to have failed with an error, but publisher fulfilled normally") - } - expectation.fulfill() - }, receiveValue: { _ in }) + .sink( + receiveCompletion: { + if case .finished = $0 { + XCTFail("Expected to have failed with an error, but publisher fulfilled normally") + } + expectation.fulfill() + }, + receiveValue: { _ in } + ) .store(in: &cancellables) - wait(for: [expectation]) + wait(for: [expectation]) } } #endif diff --git a/Tests/BuildkiteTests/Resources/AccessTokensTests.swift b/Tests/BuildkiteTests/Resources/AccessTokensTests.swift index 8e29c71..686df2a 100644 --- a/Tests/BuildkiteTests/Resources/AccessTokensTests.swift +++ b/Tests/BuildkiteTests/Resources/AccessTokensTests.swift @@ -8,6 +8,7 @@ import Foundation import XCTest + @testable import Buildkite #if canImport(FoundationNetworking) @@ -15,37 +16,17 @@ import FoundationNetworking #endif class AccessTokensTests: XCTestCase { - func testAccessTokenGet() throws { + func testAccessTokenGet() async throws { let expected = AccessToken(uuid: UUID(), scopes: []) let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send(.getAccessToken) - context.client.send(.getAccessToken) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } - func testAccessTokenDelete() throws { + func testAccessTokenDelete() async throws { let context = MockContext() - - let expectation = XCTestExpectation() - - context.client.send(.revokeAccessToken) { result in - do { - _ = try result.get() - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + _ = try await context.client.send(.revokeAccessToken) } } diff --git a/Tests/BuildkiteTests/Resources/AgentsTests.swift b/Tests/BuildkiteTests/Resources/AgentsTests.swift index c8b924a..2a101de 100644 --- a/Tests/BuildkiteTests/Resources/AgentsTests.swift +++ b/Tests/BuildkiteTests/Resources/AgentsTests.swift @@ -8,6 +8,7 @@ import Foundation import XCTest + @testable import Buildkite #if canImport(FoundationNetworking) @@ -16,99 +17,76 @@ import FoundationNetworking extension Agent { init() { - let job = Job.script(Job.Command(id: UUID(), - name: "📦", - state: "passed", - command: nil, - stepKey: nil, - buildUrl: URL(), - webUrl: URL(), - logUrl: Followable(), - rawLogUrl: Followable(), - artifactsUrl: URL(), - softFailed: false, - exitStatus: 0, - artifactPaths: nil, - agentQueryRules: [], - agent: nil, - createdAt: Date(timeIntervalSince1970: 1000), - scheduledAt: Date(timeIntervalSince1970: 1000), - runnableAt: nil, - startedAt: nil, - finishedAt: nil, - retried: false, - retriedInJobId: nil, - retriesCount: nil, - parallelGroupIndex: nil, - parallelGroupTotal: nil)) + let job = Job.script( + Job.Command( + id: UUID(), + name: "📦", + state: "passed", + command: nil, + stepKey: nil, + buildUrl: URL(), + webUrl: URL(), + logUrl: Followable(), + rawLogUrl: Followable(), + artifactsUrl: URL(), + softFailed: false, + exitStatus: 0, + artifactPaths: nil, + agentQueryRules: [], + agent: nil, + createdAt: Date(timeIntervalSince1970: 1000), + scheduledAt: Date(timeIntervalSince1970: 1000), + runnableAt: nil, + startedAt: nil, + finishedAt: nil, + retried: false, + retriedInJobId: nil, + retriesCount: nil, + parallelGroupIndex: nil, + parallelGroupTotal: nil + ) + ) - self.init(id: UUID(), - url: Followable(), - webUrl: URL(), - name: "jeffrey", - connectionState: "connected", - hostname: "jeffrey", - ipAddress: "192.168.1.1", - userAgent: "buildkite/host", - version: "3.20.0", - creator: User(), - createdAt: Date(timeIntervalSince1970: 1000), - job: job, - lastJobFinishedAt: nil, - priority: nil, - metaData: []) + self.init( + id: UUID(), + url: Followable(), + webUrl: URL(), + name: "jeffrey", + connectionState: "connected", + hostname: "jeffrey", + ipAddress: "192.168.1.1", + userAgent: "buildkite/host", + version: "3.20.0", + creator: User(), + createdAt: Date(timeIntervalSince1970: 1000), + job: job, + lastJobFinishedAt: nil, + priority: nil, + metaData: [] + ) } } class AgentsTests: XCTestCase { - func testAgentsList() throws { + func testAgentsList() async throws { let expected = [Agent(), Agent()] let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send(.agents(in: "buildkite")) - context.client.send(.agents(in: "buildkite")) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } - func testAgentsGet() throws { + func testAgentsGet() async throws { let expected = Agent() let context = try MockContext(content: expected) - let expectation = XCTestExpectation() - context.client.send(.agent(UUID(), in: "buildkite")) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + let response = try await context.client.send(.agent(UUID(), in: "buildkite")) + XCTAssertEqual(expected, response.content) } - func testAgentsStop() throws { + func testAgentsStop() async throws { let context = MockContext() - - let expectation = XCTestExpectation() - - context.client.send(.stopAgent(UUID(), in: "buildkite", force: true)) { result in - do { - _ = try result.get() - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + _ = try await context.client.send(.stopAgent(UUID(), in: "buildkite", force: true)) } } diff --git a/Tests/BuildkiteTests/Resources/AnnotationsTests.swift b/Tests/BuildkiteTests/Resources/AnnotationsTests.swift index df89e6d..fdff682 100644 --- a/Tests/BuildkiteTests/Resources/AnnotationsTests.swift +++ b/Tests/BuildkiteTests/Resources/AnnotationsTests.swift @@ -8,6 +8,7 @@ import Foundation import XCTest + @testable import Buildkite #if canImport(FoundationNetworking) @@ -16,31 +17,24 @@ import FoundationNetworking extension Annotation { init() { - self.init(id: UUID(), - context: "message", - style: .info, - bodyHtml: "
", - createdAt: Date(timeIntervalSince1970: 1000), - updatedAt: Date(timeIntervalSince1970: 1001)) + self.init( + id: UUID(), + context: "message", + style: .info, + bodyHtml: "
", + createdAt: Date(timeIntervalSince1970: 1000), + updatedAt: Date(timeIntervalSince1970: 1001) + ) } } class AnnotationsTests: XCTestCase { - func testAnnotationsList() throws { + func testAnnotationsList() async throws { let expected = [Annotation(), Annotation()] let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send(.annotations(in: "buildkite", pipeline: "my-pipeline", build: 1)) - context.client.send(.annotations(in: "buildkite", pipeline: "my-pipeline", build: 1)) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } } diff --git a/Tests/BuildkiteTests/Resources/ArtifactsTests.swift b/Tests/BuildkiteTests/Resources/ArtifactsTests.swift index 5fd13b1..1aec292 100644 --- a/Tests/BuildkiteTests/Resources/ArtifactsTests.swift +++ b/Tests/BuildkiteTests/Resources/ArtifactsTests.swift @@ -8,6 +8,7 @@ import Foundation import XCTest + @testable import Buildkite #if canImport(FoundationNetworking) @@ -16,106 +17,70 @@ import FoundationNetworking extension Artifact { init() { - self.init(id: UUID(), - jobId: UUID(), - url: Followable(), - downloadUrl: Followable(), - state: .new, - path: "", - dirname: "", - filename: "", - mimeType: "", - fileSize: 0, - sha1sum: "") + self.init( + id: UUID(), + jobId: UUID(), + url: Followable(), + downloadUrl: Followable(), + state: .new, + path: "", + dirname: "", + filename: "", + mimeType: "", + fileSize: 0, + sha1sum: "" + ) } } class ArtifactsTests: XCTestCase { - func testArtifactsListByBuild() throws { + func testArtifactsListByBuild() async throws { let expected = [Artifact(), Artifact()] let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send(.artifacts(byBuild: 1, in: "buildkite", pipeline: "my-pipeline")) - context.client.send(.artifacts(byBuild: 1, in: "buildkite", pipeline: "my-pipeline")) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } - func testArtifactsListByJob() throws { + func testArtifactsListByJob() async throws { let expected = [Artifact(), Artifact()] let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send( + .artifacts(byJob: UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1) + ) - context.client.send(.artifacts(byJob: UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1)) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } - func testArtifactsGet() throws { + func testArtifactsGet() async throws { let expected = Artifact() let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send( + .artifact(UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1, job: UUID()) + ) - context.client.send(.artifact(UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1, job: UUID())) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } - func testArtifactsDownload() throws { + func testArtifactsDownload() async throws { let expected = Artifact.URLs(url: URL()) let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send( + .downloadArtifact(UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1, job: UUID()) + ) - context.client.send(.downloadArtifact(UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1, job: UUID())) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } - func testArtifactsDelete() throws { + func testArtifactsDelete() async throws { let context = MockContext() - let expectation = XCTestExpectation() - - context.client.send(.deleteArtifact(UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1, job: UUID())) { result in - do { - _ = try result.get() - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + _ = try await context.client.send( + .deleteArtifact(UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1, job: UUID()) + ) } } diff --git a/Tests/BuildkiteTests/Resources/BuildsTests.swift b/Tests/BuildkiteTests/Resources/BuildsTests.swift index 77f3475..1fcc39e 100644 --- a/Tests/BuildkiteTests/Resources/BuildsTests.swift +++ b/Tests/BuildkiteTests/Resources/BuildsTests.swift @@ -8,6 +8,7 @@ import Foundation import XCTest + @testable import Buildkite #if canImport(FoundationNetworking) @@ -16,189 +17,135 @@ import FoundationNetworking extension Build { init() { - self.init(id: UUID(), - url: Followable(), - webUrl: URL(), - number: 1, - state: .passed, - blocked: false, - message: "a commit", - commit: "HEAD", - branch: "master", - env: [:], - source: "webhook", - creator: User(), - jobs: [], - createdAt: Date(timeIntervalSince1970: 1000), - scheduledAt: Date(timeIntervalSince1970: 1000), - startedAt: Date(timeIntervalSince1970: 1000), - finishedAt: Date(timeIntervalSince1970: 1001), - metaData: [:], - pullRequest: [:], - pipeline: Pipeline()) + self.init( + id: UUID(), + url: Followable(), + webUrl: URL(), + number: 1, + state: .passed, + blocked: false, + message: "a commit", + commit: "HEAD", + branch: "master", + env: [:], + source: "webhook", + creator: User(), + jobs: [], + createdAt: Date(timeIntervalSince1970: 1000), + scheduledAt: Date(timeIntervalSince1970: 1000), + startedAt: Date(timeIntervalSince1970: 1000), + finishedAt: Date(timeIntervalSince1970: 1001), + metaData: [:], + pullRequest: [:], + pipeline: Pipeline() + ) } } class BuildsTests: XCTestCase { - func testBuildsListAllDefaultQuery() throws { + func testBuildsListAllDefaultQuery() async throws { let expected = [Build(), Build()] let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send(.builds()) - context.client.send(.builds()) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } - func testBuildsListAllSpecializedQuery() throws { + func testBuildsListAllSpecializedQuery() async throws { let expected = [Build(), Build()] let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send( + .builds( + options: .init( + branches: ["master"], + commit: "HEAD", + createdFrom: Date(timeIntervalSince1970: 1000), + createdTo: Date(timeIntervalSince1970: 1000), + creator: UUID(), + finishedFrom: Date(timeIntervalSince1970: 1000), + includeRetriedJobs: true, + metadata: ["buildkite": "is cool"], + state: [.passed, .blocked, .failed] + ) + ) + ) - context.client.send(.builds(options: .init(branches: ["master"], - commit: "HEAD", - createdFrom: Date(timeIntervalSince1970: 1000), - createdTo: Date(timeIntervalSince1970: 1000), - creator: UUID(), - finishedFrom: Date(timeIntervalSince1970: 1000), - includeRetriedJobs: true, - metadata: ["buildkite": "is cool"], - state: [.passed, .blocked, .failed]))) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } - func testBuildsListForOrganization() throws { + func testBuildsListForOrganization() async throws { let expected = [Build(), Build()] let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send(.builds(inOrganization: "buildkite", options: .init())) - context.client.send(.builds(inOrganization: "buildkite", options: .init())) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } - func testBuildsListForPipeline() throws { + func testBuildsListForPipeline() async throws { let expected = [Build(), Build()] let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send( + .builds(forPipeline: "my-pipeline", in: "buildkite", options: .init()) + ) - context.client.send(.builds(forPipeline: "my-pipeline", in: "buildkite", options: .init())) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } - func testBuildsGet() throws { + func testBuildsGet() async throws { let expected = Build() let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send(.build(1, in: "buildkite", pipeline: "my-pipeline")) - context.client.send(.build(1, in: "buildkite", pipeline: "my-pipeline")) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } - func testBuildsCreate() throws { + func testBuildsCreate() async throws { let expected = Build() let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send( + .createBuild( + in: "buildkite", + pipeline: "my-pipeline", + with: .init( + commit: "HEAD", + branch: "master", + author: Build.Resources.Create.Body.Author(name: "", email: ""), + cleanCheckout: nil, + env: nil, + ignorePipelineBranchFilters: nil, + message: nil, + metaData: nil, + pullRequestBaseBranch: nil, + pullRequestId: nil, + pullRequestRepository: nil + ) + ) + ) - context.client.send(.createBuild(in: "buildkite", pipeline: "my-pipeline", with: .init(commit: "HEAD", - branch: "master", - author: Build.Resources.Create.Body.Author(name: "", email: ""), - cleanCheckout: nil, - env: nil, - ignorePipelineBranchFilters: nil, - message: nil, - metaData: nil, - pullRequestBaseBranch: nil, - pullRequestId: nil, - pullRequestRepository: nil))) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } - func testBuildsCancel() throws { + func testBuildsCancel() async throws { let expected = Build() let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send(.cancelBuild(1, in: "buildkite", pipeline: "my-pipeline")) - context.client.send(.cancelBuild(1, in: "buildkite", pipeline: "my-pipeline")) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } - func testBuildsRebuild() throws { + func testBuildsRebuild() async throws { let expected = Build() let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send(.rebuild(1, in: "buildkite", pipeline: "my-pipeline")) - context.client.send(.rebuild(1, in: "buildkite", pipeline: "my-pipeline")) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } } diff --git a/Tests/BuildkiteTests/Resources/EmojisTests.swift b/Tests/BuildkiteTests/Resources/EmojisTests.swift index 509a3f9..cc32031 100644 --- a/Tests/BuildkiteTests/Resources/EmojisTests.swift +++ b/Tests/BuildkiteTests/Resources/EmojisTests.swift @@ -8,6 +8,7 @@ import Foundation import XCTest + @testable import Buildkite #if canImport(FoundationNetworking) @@ -16,27 +17,20 @@ import FoundationNetworking extension Emoji { init() { - self.init(name: "jeff", - url: URL()) + self.init( + name: "jeff", + url: URL() + ) } } class EmojisTests: XCTestCase { - func testEmojisList() throws { + func testEmojisList() async throws { let expected = [Emoji(), Emoji()] let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send(.emojis(in: "buildkite")) - context.client.send(.emojis(in: "buildkite")) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } } diff --git a/Tests/BuildkiteTests/Resources/FollowableTests.swift b/Tests/BuildkiteTests/Resources/FollowableTests.swift index b0005b3..50c4d6a 100644 --- a/Tests/BuildkiteTests/Resources/FollowableTests.swift +++ b/Tests/BuildkiteTests/Resources/FollowableTests.swift @@ -8,6 +8,7 @@ import Foundation import XCTest + @testable import Buildkite #if canImport(FoundationNetworking) @@ -21,29 +22,13 @@ extension Followable { } class FollowableTests: XCTestCase { - func testFollowable() throws { + func testFollowable() async throws { let expected = Organization() let context = try MockContext(content: Organization(), expected) - let expectation = XCTestExpectation() + let organizationResponse = try await context.client.send(.organization("buildkite")) + let followableResponse = try await context.client.send(organizationResponse.content.url) - context.client.send(.organization("buildkite")) { result in - do { - let response = try result.get() - context.client.send(response.content.url) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - } catch { - XCTFail(error.localizedDescription) - } - } - - wait(for: [expectation]) + XCTAssertEqual(expected, followableResponse.content) } } diff --git a/Tests/BuildkiteTests/Resources/GraphQLTests.swift b/Tests/BuildkiteTests/Resources/GraphQLTests.swift index d7b45ff..90be896 100644 --- a/Tests/BuildkiteTests/Resources/GraphQLTests.swift +++ b/Tests/BuildkiteTests/Resources/GraphQLTests.swift @@ -8,6 +8,7 @@ import Foundation import XCTest + @testable import Buildkite #if canImport(FoundationNetworking) @@ -15,91 +16,88 @@ import FoundationNetworking #endif class GraphQLTests: XCTestCase { - func testGraphQLSuccess() throws { + func testGraphQLSuccess() async throws { let expected: JSONValue = ["jeff": [1, 2, 3], "horses": false] let content: JSONValue = ["data": expected] let context = try MockContext(content: content) - let expectation = XCTestExpectation() + let response = try await context.client.sendQuery( + GraphQL(rawQuery: "query MyQuery{jeff,horses}", variables: [:]) + ) - context.client.sendQuery(GraphQL(rawQuery: "query MyQuery{jeff,horses}", variables: [:])) { result in - do { - let content = try result.get() - XCTAssertEqual(expected, content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response) } - func testGraphQLErrors() throws { - let expected: GraphQL.Errors = .init(errors: [ - .init(message: "Field 'id' doesn't exist on type 'Query'", - locations: [.init(line: 2, column: 3)], - path: ["query SimpleQuery", "id"], - extensions: [ - "code": "undefinedField", - "typeName": "Query", - "fieldName": "id" - ]) - ], type: nil) + func testGraphQLErrors() async throws { + let expected: GraphQL.Errors = .init( + errors: [ + .init( + message: "Field 'id' doesn't exist on type 'Query'", + locations: [.init(line: 2, column: 3)], + path: ["query SimpleQuery", "id"], + extensions: [ + "code": "undefinedField", + "typeName": "Query", + "fieldName": "id", + ] + ) + ], + type: nil + ) let content: JSONValue = [ - "errors": .array(expected.errors.map { error in - let messageJSON: JSONValue = .string(error.message) - let locationsJSON: JSONValue - if let locations = error.locations { - locationsJSON = .array(locations.map { .object(["line": .number(Double($0.line)), "column": .number(Double($0.column))]) }) - } else { - locationsJSON = .null - } - let pathJSON: JSONValue - if let path = error.path { - pathJSON = .array(path.map { .string($0) }) - } else { - pathJSON = .null - } - let extensionsJSON = error.extensions ?? .null + "errors": .array( + expected.errors.map { error in + let messageJSON: JSONValue = .string(error.message) + let locationsJSON: JSONValue + if let locations = error.locations { + locationsJSON = .array( + locations.map { + .object(["line": .number(Double($0.line)), "column": .number(Double($0.column))]) + } + ) + } else { + locationsJSON = .null + } + let pathJSON: JSONValue + if let path = error.path { + pathJSON = .array(path.map { .string($0) }) + } else { + pathJSON = .null + } + let extensionsJSON = error.extensions ?? .null - return [ - "message": messageJSON, - "locations": locationsJSON, - "path": pathJSON, - "extensions": extensionsJSON - ] - }) + return [ + "message": messageJSON, + "locations": locationsJSON, + "path": pathJSON, + "extensions": extensionsJSON, + ] + } + ) ] let context = try MockContext(content: content) - let expectation = XCTestExpectation() - - context.client.sendQuery(GraphQL(rawQuery: "query MyQuery{jeff,horses}", variables: [:])) { result in - do { - _ = try result.get() - XCTFail("Expected to have failed with an error, but closure fulfilled normally") - } catch let error as GraphQL.Errors { - XCTAssertEqual(expected, error) - } catch { - XCTFail("Expected to have failed with an error, but closure failed with unexpected error type") - } - expectation.fulfill() + do { + _ = try await context.client.sendQuery( + GraphQL(rawQuery: "query MyQuery{jeff,horses}", variables: [:]) + ) + XCTFail("Expected to have failed with an error, but closure fulfilled normally") + } catch let error as GraphQL.Errors { + XCTAssertEqual(expected, error) + } catch { + XCTFail("Expected to have failed with an error, but closure failed with unexpected error type") } - wait(for: [expectation]) } - func testGraphQLIncompatibleResponse() throws { + func testGraphQLIncompatibleResponse() async throws { let content: JSONValue = [:] let context = try MockContext(content: content) - let expectation = XCTestExpectation() - - context.client.sendQuery(GraphQL(rawQuery: "", variables: [:])) { - try? XCTAssertThrowsError($0.get(), "Expected to have failed with an error, but closure fulfilled normally") - expectation.fulfill() - } - wait(for: [expectation]) + do { + _ = try await context.client.sendQuery(GraphQL(rawQuery: "", variables: [:])) + XCTFail("Expected to have failed with an error, but closure fulfilled normally") + } catch {} } func testGraphQLContentGet() throws { diff --git a/Tests/BuildkiteTests/Resources/JobsTests.swift b/Tests/BuildkiteTests/Resources/JobsTests.swift index 3e23a21..7bf22a5 100644 --- a/Tests/BuildkiteTests/Resources/JobsTests.swift +++ b/Tests/BuildkiteTests/Resources/JobsTests.swift @@ -8,6 +8,7 @@ import Foundation import XCTest + @testable import Buildkite #if canImport(FoundationNetworking) @@ -15,172 +16,127 @@ import FoundationNetworking #endif class JobsTests: XCTestCase { - func testJobsRetryWaiter() throws { + func testJobsRetryWaiter() async throws { let expected: Job = .waiter(Job.Wait(id: UUID())) let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send( + .retryJob(UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1) + ) - context.client.send(.retryJob(UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1)) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } - func testJobsRetryTrigger() throws { - let expected: Job = .trigger(Job.Trigger(name: nil, - state: nil, - buildUrl: URL(), - webUrl: URL(), - createdAt: Date(timeIntervalSince1970: 1000), - scheduledAt: nil, - finishedAt: nil, - runnableAt: nil, - triggeredBuild: Job.Trigger.TriggeredBuild(id: UUID(), - number: 0, - url: URL(), - webUrl: URL()))) + func testJobsRetryTrigger() async throws { + let expected: Job = .trigger( + Job.Trigger( + name: nil, + state: nil, + buildUrl: URL(), + webUrl: URL(), + createdAt: Date(timeIntervalSince1970: 1000), + scheduledAt: nil, + finishedAt: nil, + runnableAt: nil, + triggeredBuild: Job.Trigger.TriggeredBuild( + id: UUID(), + number: 0, + url: URL(), + webUrl: URL() + ) + ) + ) let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send( + .retryJob(UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1) + ) - context.client.send(.retryJob(UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1)) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } - func testJobsUnblock() throws { - let expected: Job = .manual(Job.Block(id: UUID(), - label: "", - state: "", - webUrl: nil, - unblockedBy: User(), - unblockedAt: Date(timeIntervalSince1970: 1000), - unblockable: true, - unblockUrl: URL())) + func testJobsUnblock() async throws { + let expected: Job = .manual( + Job.Block( + id: UUID(), + label: "", + state: "", + webUrl: nil, + unblockedBy: User(), + unblockedAt: Date(timeIntervalSince1970: 1000), + unblockable: true, + unblockUrl: URL() + ) + ) let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send( + .unblockJob(UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1, with: .init()) + ) - context.client.send(.unblockJob(UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1, with: .init())) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } - func testJobsLogOutput() throws { + func testJobsLogOutput() async throws { let expected = Job.LogOutput() let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send( + .logOutput(for: UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1) + ) - context.client.send(.logOutput(for: UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1)) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } - func testJobsLogOutputAlternativePlainText() throws { + func testJobsLogOutputAlternativePlainText() async throws { let expected = "hello friends" let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send( + .logOutput(.plainText, for: UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1) + ) - context.client.send(.logOutput(.plainText, for: UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1)) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } - func testJobsLogOutputAlternativeHTML() throws { + func testJobsLogOutputAlternativeHTML() async throws { let expected = "hello friends" let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send( + .logOutput(.html, for: UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1) + ) - context.client.send(.logOutput(.html, for: UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1)) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } - func testJobsDeleteLogOutput() throws { + func testJobsDeleteLogOutput() async throws { let context = MockContext() - let expectation = XCTestExpectation() - - context.client.send(.deleteLogOutput(for: UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1)) { result in - do { - _ = try result.get() - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + _ = try await context.client.send( + .deleteLogOutput(for: UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1) + ) } - func testJobsEnvironmentVariables() throws { + func testJobsEnvironmentVariables() async throws { let expected = Job.EnvironmentVariables(env: [:]) let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send( + .environmentVariables(for: UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1) + ) - context.client.send(.environmentVariables(for: UUID(), in: "buildkite", pipeline: "my-pipeline", build: 1)) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } } extension Job.LogOutput { init() { - self.init(url: Followable(), - content: "hello friends", - size: 13, - headerTimes: []) + self.init( + url: Followable(), + content: "hello friends", + size: 13, + headerTimes: [] + ) } } diff --git a/Tests/BuildkiteTests/Resources/MetasTests.swift b/Tests/BuildkiteTests/Resources/MetasTests.swift new file mode 100644 index 0000000..38a6881 --- /dev/null +++ b/Tests/BuildkiteTests/Resources/MetasTests.swift @@ -0,0 +1,27 @@ +// +// MetasTests.swift +// Buildkite +// +// Created by Aaron Sky on 5/29/22. +// Copyright © 2022 Aaron Sky. All rights reserved. +// + +import Foundation +import XCTest + +@testable import Buildkite + +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif + +class MetasTests: XCTestCase { + func testMetaGet() async throws { + let expected = Meta(webhookIPRanges: ["1.1.1.1/32"]) + let context = try MockContext(content: expected) + + let response = try await context.client.send(.meta) + + XCTAssertEqual(expected, response.content) + } +} diff --git a/Tests/BuildkiteTests/Resources/OrganizationsTests.swift b/Tests/BuildkiteTests/Resources/OrganizationsTests.swift index 52cb01a..8493ba1 100644 --- a/Tests/BuildkiteTests/Resources/OrganizationsTests.swift +++ b/Tests/BuildkiteTests/Resources/OrganizationsTests.swift @@ -8,6 +8,7 @@ import Foundation import XCTest + @testable import Buildkite #if canImport(FoundationNetworking) @@ -16,52 +17,36 @@ import FoundationNetworking extension Organization { init() { - self.init(id: UUID(), - url: Followable(), - webUrl: URL(), - name: "Buildkite", - slug: "buildkite", - pipelinesUrl: Followable(), - agentsUrl: Followable(), - emojisUrl: Followable(), - createdAt: Date(timeIntervalSince1970: 1000)) + self.init( + id: UUID(), + url: Followable(), + webUrl: URL(), + name: "Buildkite", + slug: "buildkite", + pipelinesUrl: Followable(), + agentsUrl: Followable(), + emojisUrl: Followable(), + createdAt: Date(timeIntervalSince1970: 1000) + ) } } class OrganizationsTests: XCTestCase { - func testOrganizationsList() throws { + func testOrganizationsList() async throws { let expected = [Organization()] let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send(.organizations) - context.client.send(.organizations) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } - func testOrganizationsGet() throws { + func testOrganizationsGet() async throws { let expected = Organization() let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send(.organization("buildkite")) - context.client.send(.organization("buildkite")) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } } diff --git a/Tests/BuildkiteTests/Resources/PipelinesTests.swift b/Tests/BuildkiteTests/Resources/PipelinesTests.swift index d4f4bf0..5fd5823 100644 --- a/Tests/BuildkiteTests/Resources/PipelinesTests.swift +++ b/Tests/BuildkiteTests/Resources/PipelinesTests.swift @@ -8,6 +8,7 @@ import Foundation import XCTest + @testable import Buildkite #if canImport(FoundationNetworking) @@ -15,207 +16,194 @@ import FoundationNetworking #endif extension Pipeline { - init(steps: [Step] = []) { - self.init(id: UUID(), - url: Followable(), - webUrl: URL(), - name: "My Pipeline", - slug: "my-pipeline", - repository: "git@github.com:buildkite/agent.git", - branchConfiguration: nil, - defaultBranch: "master", - provider: Provider(id: "github", - webhookUrl: URL(), - settings: Provider.Settings(repository: nil, - buildPullRequests: nil, - pullRequestBranchFilterEnabled: nil, - pullRequestBranchFilterConfiguration: nil, - skipPullRequestBuildsForExistingCommits: nil, - buildTags: nil, - publishCommitStatus: nil, - publishCommitStatusPerStep: nil, - triggerMode: nil, - filterEnabled: nil, - filterCondition: nil, - buildPullRequestForks: nil, - prefixPullRequestForkBranchNames: nil, - separatePullRequestStatuses: nil, - publishBlockedAsPending: nil)), - skipQueuedBranchBuilds: false, - skipQueuedBranchBuildsFilter: nil, - cancelRunningBranchBuilds: false, - cancelRunningBranchBuildsFilter: nil, - buildsUrl: Followable(), - badgeUrl: URL(), - createdAt: Date(timeIntervalSince1970: 1000), - scheduledBuildsCount: 0, - runningBuildsCount: 0, - scheduledJobsCount: 0, - runningJobsCount: 0, - waitingJobsCount: 0, - visibility: "private", - steps: steps, - env: [:]) + init( + steps: [Step] = [] + ) { + self.init( + id: UUID(), + url: Followable(), + webUrl: URL(), + name: "My Pipeline", + slug: "my-pipeline", + repository: "git@github.com:buildkite/agent.git", + branchConfiguration: nil, + defaultBranch: "master", + provider: Provider( + id: "github", + webhookUrl: URL(), + settings: Provider.Settings( + repository: nil, + buildPullRequests: nil, + pullRequestBranchFilterEnabled: nil, + pullRequestBranchFilterConfiguration: nil, + skipPullRequestBuildsForExistingCommits: nil, + buildTags: nil, + publishCommitStatus: nil, + publishCommitStatusPerStep: nil, + triggerMode: nil, + filterEnabled: nil, + filterCondition: nil, + buildPullRequestForks: nil, + prefixPullRequestForkBranchNames: nil, + separatePullRequestStatuses: nil, + publishBlockedAsPending: nil + ) + ), + skipQueuedBranchBuilds: false, + skipQueuedBranchBuildsFilter: nil, + cancelRunningBranchBuilds: false, + cancelRunningBranchBuildsFilter: nil, + buildsUrl: Followable(), + badgeUrl: URL(), + createdAt: Date(timeIntervalSince1970: 1000), + scheduledBuildsCount: 0, + runningBuildsCount: 0, + scheduledJobsCount: 0, + runningJobsCount: 0, + waitingJobsCount: 0, + visibility: "private", + steps: steps, + env: [:] + ) } } class PipelinesTests: XCTestCase { - func testPipelinesList() throws { + func testPipelinesList() async throws { let expected = [Pipeline(), Pipeline()] let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send(.pipelines(in: "buildkite")) - context.client.send(.pipelines(in: "buildkite")) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } - func testPipelinesGet() throws { + func testPipelinesGet() async throws { let expected = Pipeline() let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send(.pipeline("buildkite", in: "organization")) - context.client.send(.pipeline("buildkite", in: "organization")) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } - func testPipelinesCreate() throws { + func testPipelinesCreate() async throws { let steps: [Pipeline.Step] = [ - .script(Pipeline.Step.Command(name: "📦", - command: "echo true", - label: "📦", - env: [:], - agentQueryRules: [])) + .script( + Pipeline.Step.Command( + name: "📦", + command: "echo true", + label: "📦", + env: [:], + agentQueryRules: [] + ) + ) ] let expected = Pipeline(steps: steps) let context = try MockContext(content: expected) - let expectation = XCTestExpectation() - context.client.send(.createPipeline(.init(name: "My Pipeline", - repository: URL(), - configuration: "steps:\n\t- label: \"📦\"\n\t command: \"echo true\""), - in: "buildkite")) { result in - do { - _ = try result.get() - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + _ = try await context.client.send( + .createPipeline( + .init( + name: "My Pipeline", + repository: URL(), + configuration: "steps:\n\t- label: \"📦\"\n\t command: \"echo true\"" + ), + in: "buildkite" + ) + ) } - func testPipelinesCreateVisualSteps() throws { + func testPipelinesCreateVisualSteps() async throws { let steps: [Pipeline.Step] = [ - .script(Pipeline.Step.Command(name: "📦", - command: "echo true", - label: "📦", - artifactPaths: "*", - branchConfiguration: nil, - env: [:], - timeoutInMinutes: nil, - agentQueryRules: [], - async: nil, - concurrency: nil, - parallelism: nil)), - .waiter(Pipeline.Step.Wait(label: "wait", - continueAfterFailure: true)), + .script( + Pipeline.Step.Command( + name: "📦", + command: "echo true", + label: "📦", + artifactPaths: "*", + branchConfiguration: nil, + env: [:], + timeoutInMinutes: nil, + agentQueryRules: [], + async: nil, + concurrency: nil, + parallelism: nil + ) + ), + .waiter( + Pipeline.Step.Wait( + label: "wait", + continueAfterFailure: true + ) + ), .manual(Pipeline.Step.Block(label: "manual")), - .trigger(Pipeline.Step.Trigger(triggerProjectSlug: "my-other-pipeline", - label: "trigger", - triggerCommit: nil, - triggerBranch: nil, - triggerAsync: nil)) + .trigger( + Pipeline.Step.Trigger( + triggerProjectSlug: "my-other-pipeline", + label: "trigger", + triggerCommit: nil, + triggerBranch: nil, + triggerAsync: nil + ) + ), ] let expected = Pipeline(steps: steps) let context = try MockContext(content: expected) - let expectation = XCTestExpectation() - context.client.send(.createVisualStepsPipeline(.init(name: "My Pipeline", - repository: URL(), - steps: steps, - branchConfiguration: nil, - cancelRunningBranchBuilds: nil, - cancelRunningBranchBuildsFilter: nil, - defaultBranch: nil, - description: nil, - env: nil, - providerSettings: nil, - skipQueuedBranchBuilds: nil, - skipQueuedBranchBuildsFilter: nil, - teamUUIDs: nil), - in: "buildkite")) { result in - do { - _ = try result.get() - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + _ = try await context.client.send( + .createVisualStepsPipeline( + .init( + name: "My Pipeline", + repository: URL(), + steps: steps, + branchConfiguration: nil, + cancelRunningBranchBuilds: nil, + cancelRunningBranchBuildsFilter: nil, + defaultBranch: nil, + description: nil, + env: nil, + providerSettings: nil, + skipQueuedBranchBuilds: nil, + skipQueuedBranchBuildsFilter: nil, + teamUUIDs: nil + ), + in: "buildkite" + ) + ) } - func testPipelinesUpdate() throws { + func testPipelinesUpdate() async throws { let expected = Pipeline() let context = try MockContext(content: expected) - let expectation = XCTestExpectation() - context.client.send(.updatePipeline("my-pipeline", - in: "buildkite", - with: .init(branchConfiguration: nil, - cancelRunningBranchBuilds: nil, - cancelRunningBranchBuildsFilter: nil, - defaultBranch: nil, - description: nil, - env: nil, - name: nil, - providerSettings: nil, - repository: nil, - steps: nil, - skipQueuedBranchBuilds: nil, - skipQueuedBranchBuildsFilter: nil, - visibility: nil))) { result in - do { - _ = try result.get() - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + _ = try await context.client.send( + .updatePipeline( + "my-pipeline", + in: "buildkite", + with: .init( + branchConfiguration: nil, + cancelRunningBranchBuilds: nil, + cancelRunningBranchBuildsFilter: nil, + defaultBranch: nil, + description: nil, + env: nil, + name: nil, + providerSettings: nil, + repository: nil, + steps: nil, + skipQueuedBranchBuilds: nil, + skipQueuedBranchBuildsFilter: nil, + visibility: nil + ) + ) + ) } - func testPipelinesDelete() throws { + func testPipelinesDelete() async throws { let context = MockContext() - let expectation = XCTestExpectation() - - context.client.send(.deletePipeline("my-pipeline", in: "buildkite")) { result in - do { - _ = try result.get() - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + _ = try await context.client.send(.deletePipeline("my-pipeline", in: "buildkite")) } } diff --git a/Tests/BuildkiteTests/Resources/TeamsTests.swift b/Tests/BuildkiteTests/Resources/TeamsTests.swift index ffd042e..0f53f83 100644 --- a/Tests/BuildkiteTests/Resources/TeamsTests.swift +++ b/Tests/BuildkiteTests/Resources/TeamsTests.swift @@ -8,41 +8,35 @@ import Foundation import XCTest + @testable import Buildkite #if canImport(FoundationNetworking) import FoundationNetworking #endif -private extension Team { - init() { - self.init(id: UUID(), - name: "", - slug: "", - description: "", - privacy: .visible, - default: true, - createdAt: Date(timeIntervalSince1970: 1000), - createdBy: User()) +extension Team { + fileprivate init() { + self.init( + id: UUID(), + name: "", + slug: "", + description: "", + privacy: .visible, + default: true, + createdAt: Date(timeIntervalSince1970: 1000), + createdBy: User() + ) } } class TeamsTests: XCTestCase { - func testTeamsList() throws { + func testTeamsList() async throws { let expected = [Team(), Team()] let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send(.teams(in: "buildkite", byUser: UUID())) - context.client.send(.teams(in: "buildkite", byUser: UUID())) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } } diff --git a/Tests/BuildkiteTests/Resources/UsersTests.swift b/Tests/BuildkiteTests/Resources/UsersTests.swift index 34844e2..784f578 100644 --- a/Tests/BuildkiteTests/Resources/UsersTests.swift +++ b/Tests/BuildkiteTests/Resources/UsersTests.swift @@ -8,6 +8,7 @@ import Foundation import XCTest + @testable import Buildkite #if canImport(FoundationNetworking) @@ -16,30 +17,23 @@ import FoundationNetworking extension User { init() { - self.init(id: UUID(), - name: "Jeff", - email: "jeff@buildkite.com", - avatarUrl: URL(), - createdAt: Date(timeIntervalSince1970: 1000)) + self.init( + id: UUID(), + name: "Jeff", + email: "jeff@buildkite.com", + avatarUrl: URL(), + createdAt: Date(timeIntervalSince1970: 1000) + ) } } class UsersTests: XCTestCase { - func testUserMe() throws { + func testUserMe() async throws { let expected = User() let context = try MockContext(content: expected) - let expectation = XCTestExpectation() + let response = try await context.client.send(.me) - context.client.send(.me) { result in - do { - let response = try result.get() - XCTAssertEqual(expected, response.content) - } catch { - XCTFail(error.localizedDescription) - } - expectation.fulfill() - } - wait(for: [expectation]) + XCTAssertEqual(expected, response.content) } } diff --git a/Tests/BuildkiteTests/Utilities/MockContext.swift b/Tests/BuildkiteTests/Utilities/MockContext.swift index ab255d8..5b9949a 100644 --- a/Tests/BuildkiteTests/Utilities/MockContext.swift +++ b/Tests/BuildkiteTests/Utilities/MockContext.swift @@ -6,8 +6,8 @@ // Copyright © 2020 Aaron Sky. All rights reserved. // -import Foundation import Buildkite +import Foundation #if canImport(FoundationNetworking) import FoundationNetworking @@ -19,21 +19,34 @@ struct MockContext { init() { let configuration = Configuration.default - self.init(configuration: configuration, responses: [ - MockData.mockingSuccessNoContent(url: configuration.version.baseURL) - ]) + self.init( + configuration: configuration, + responses: [ + MockData.mockingSuccessNoContent(url: configuration.version.baseURL) + ] + ) } - init(content: Content...) throws { + init( + content: Content... + ) throws { let configuration = Configuration.default - try self.init(configuration: configuration, responses: content.map { - try MockData.mockingSuccess(with: $0, url: configuration.version.baseURL) - }) + try self.init( + configuration: configuration, + responses: content.map { + try MockData.mockingSuccess(with: $0, url: configuration.version.baseURL) + } + ) } - private init(configuration: Configuration, responses: [(Data, URLResponse)]) { + private init( + configuration: Configuration, + responses: [(Data, URLResponse)] + ) { let transport = MockTransport(responses: responses) - client = BuildkiteClient(configuration: configuration, - transport: transport) + client = BuildkiteClient( + configuration: configuration, + transport: transport + ) } } diff --git a/Tests/BuildkiteTests/Utilities/MockData.swift b/Tests/BuildkiteTests/Utilities/MockData.swift index 888f70d..b39819a 100644 --- a/Tests/BuildkiteTests/Utilities/MockData.swift +++ b/Tests/BuildkiteTests/Utilities/MockData.swift @@ -7,6 +7,7 @@ // import Foundation + @testable import Buildkite #if canImport(FoundationNetworking) @@ -143,9 +144,14 @@ extension MockData { } private static func urlResponse(for url: URL, rawStatus status: Int) -> URLResponse { - HTTPURLResponse(url: url, - statusCode: status, - httpVersion: "HTTP/1.1", - headerFields: ["Link": #"; rel="prev",; rel="next",; rel="first", ; rel="last""#])! + HTTPURLResponse( + url: url, + statusCode: status, + httpVersion: "HTTP/1.1", + headerFields: [ + "Link": + #"; rel="prev",; rel="next",; rel="first", ; rel="last""# + ] + )! } } diff --git a/Tests/BuildkiteTests/Utilities/MockTransport.swift b/Tests/BuildkiteTests/Utilities/MockTransport.swift index f7f28ed..bb91afd 100644 --- a/Tests/BuildkiteTests/Utilities/MockTransport.swift +++ b/Tests/BuildkiteTests/Utilities/MockTransport.swift @@ -6,8 +6,8 @@ // Copyright © 2020 Aaron Sky. All rights reserved. // -import Foundation import Buildkite +import Foundation #if canImport(FoundationNetworking) import FoundationNetworking @@ -25,7 +25,9 @@ final class MockTransport { var history: [URLRequest] = [] var responses: [Transport.Output] - init(responses: [Transport.Output]) { + init( + responses: [Transport.Output] + ) { self.responses = responses } } @@ -40,7 +42,7 @@ extension MockTransport: Transport { completion(.success(responses.removeFirst())) } -#if canImport(Combine) + #if canImport(Combine) @available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) func sendPublisher(request: URLRequest) -> AnyPublisher { history.append(request) @@ -53,11 +55,12 @@ extension MockTransport: Transport { return } promise(.success(self.responses.removeFirst())) - }.eraseToAnyPublisher() + } + .eraseToAnyPublisher() } -#endif + #endif -#if compiler(>=5.5.2) && canImport(_Concurrency) + #if compiler(>=5.5.2) && canImport(_Concurrency) @available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) func send(request: URLRequest) async throws -> Output { history.append(request) @@ -66,5 +69,5 @@ extension MockTransport: Transport { } return responses.removeFirst() } -#endif + #endif }