Basic pagination support

This commit is contained in:
Aaron Sky 2020-05-05 16:52:20 -04:00
parent fa81cfdf2c
commit 1b2c7472fa
19 changed files with 432 additions and 206 deletions

View File

@ -34,20 +34,6 @@
ReferencedContainer = "container:"> ReferencedContainer = "container:">
</BuildableReference> </BuildableReference>
</BuildActionEntry> </BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "NO"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BuildkiteIntegrationTests"
BuildableName = "BuildkiteIntegrationTests"
BlueprintName = "BuildkiteIntegrationTests"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries> </BuildActionEntries>
</BuildAction> </BuildAction>
<TestAction <TestAction
@ -67,16 +53,6 @@
ReferencedContainer = "container:"> ReferencedContainer = "container:">
</BuildableReference> </BuildableReference>
</TestableReference> </TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BuildkiteIntegrationTests"
BuildableName = "BuildkiteIntegrationTests"
BlueprintName = "BuildkiteIntegrationTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables> </Testables>
</TestAction> </TestAction>
<LaunchAction <LaunchAction

View File

@ -12,7 +12,7 @@ import Buildkite
let client = Buildkite() let client = Buildkite()
client.token = "..." client.token = "..."
var cancellables: Set<AnyCancellable> = [] var cancellables: Set<AnyCancellable> = []
client.sendPublisher(Build.Resources.ListAll()) client.sendPublisher(Build.Resources.Get(organization: "wayfair", pipeline: "merge-train-ci", build: 162))
.map(\.content) .map(\.content)
.sink(receiveCompletion: { result in .sink(receiveCompletion: { result in
if case let .failure(error) = result { if case let .failure(error) = result {

View File

@ -32,7 +32,7 @@ public struct Artifact: Codable, Equatable {
public var fileSize: Int public var fileSize: Int
public var sha1sum: String public var sha1sum: String
public struct URLs: Codable { public struct URLs: Codable, Equatable {
public var url: URL public var url: URL
} }
} }

View File

@ -12,29 +12,72 @@ import Foundation
import FoundationNetworking import FoundationNetworking
#endif #endif
public struct Job: Codable, Equatable { public enum Job: Codable, Equatable {
public struct Agent: Codable, Equatable { case script(Command)
public var id: UUID case waiter(Wait)
public var url: URL case manual(Block)
public var name: String case trigger(Trigger)
private enum Unassociated: String, Codable {
case script
case waiter
case manual
case trigger
} }
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 {
case .script:
self = .script(try Command(from: decoder))
case .waiter:
self = .waiter(try Wait(from: decoder))
case .manual:
self = .manual(try Block(from: decoder))
case .trigger:
self = .trigger(try Trigger(from: decoder))
}
}
public func encode(to encoder: Encoder) throws {
switch self {
case .script(let step):
try step.encode(to: encoder)
case .waiter(let step):
try step.encode(to: encoder)
case .manual(let step):
try step.encode(to: encoder)
case .trigger(let step):
try step.encode(to: encoder)
}
}
private enum CodingKeys: String, CodingKey {
case type
}
public struct Command: Codable, Equatable {
public struct AgentRef: Codable, Equatable {
public var id: UUID public var id: UUID
public var type: String public var name: String
public var name: String? public var url: URL
public var stepKey: String? }
public var agentQueryRules: [String] public let type = "script"
public var state: String? public var id: UUID
public var buildUrl: URL? public var name: String
public var webUrl: URL?
public var logUrl: URL?
public var rawLogUrl: URL?
public var artifactsUrl: URL?
public var command: String? public var command: String?
public var stepKey: String?
public var buildUrl: URL
public var webUrl: URL
public var logUrl: URL
public var rawLogUrl: URL
public var artifactsUrl: URL
public var softFailed: Bool public var softFailed: Bool
public var exitStatus: Int? public var exitStatus: Int
public var artifactPaths: String? public var artifactPaths: String?
public var agent: Agent? public var agentQueryRules: [String]
public var agent: AgentRef?
public var createdAt: Date public var createdAt: Date
public var scheduledAt: Date public var scheduledAt: Date
public var runnableAt: Date? public var runnableAt: Date?
@ -45,10 +88,16 @@ public struct Job: Codable, Equatable {
public var retriesCount: Int? public var retriesCount: Int?
public var parallelGroupIndex: Int? public var parallelGroupIndex: Int?
public var parallelGroupTotal: Int? public var parallelGroupTotal: Int?
}
public struct Unblocked: Codable, Equatable { public struct Wait: Codable, Equatable {
public let type = "waiter"
public var id: UUID
}
public struct Block: Codable, Equatable {
public let type = "manual"
public var id: UUID public var id: UUID
public var type: String
public var label: String public var label: String
public var state: String public var state: String
public var webUrl: URL? public var webUrl: URL?
@ -58,6 +107,26 @@ public struct Job: Codable, Equatable {
public var unblockUrl: URL public var unblockUrl: URL
} }
public struct Trigger: Codable, Equatable {
public let type = "trigger"
public var name: String
public var state: String
public var buildUrl: URL
public var webUrl: URL
public var createdAt: Date
public var scheduledAt: Date
public var finishedAt: Date?
public var runnableAt: Date?
public var triggeredBuild: TriggeredBuild
public struct TriggeredBuild: Codable, Equatable {
public var id: UUID
public var number: Int
public var url: URL
public var webUrl: URL
}
}
public struct LogOutput: Codable { public struct LogOutput: Codable {
public var url: URL public var url: URL
public var content: String public var content: String

View File

@ -159,7 +159,6 @@ extension Pipeline {
public var triggerBranch: String? public var triggerBranch: String?
public var triggerAsync: Bool? public var triggerAsync: Bool?
} }
} }
} }

View File

@ -1,5 +1,5 @@
// //
// Constants.swift // Formatters.swift
// //
// //
// Created by Aaron Sky on 3/23/20. // Created by Aaron Sky on 3/23/20.

View File

@ -0,0 +1,77 @@
//
// Pagination.swift
//
//
// Created by Aaron Sky on 5/5/20.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
public struct Page {
var nextPage: Int?
var previousPage: Int?
var firstPage: Int?
var lastPage: Int?
init?(for header: String) {
guard !header.isEmpty else {
return nil
}
for link in header.split(separator: ",") {
let segments = link
.trimmingCharacters(in: .whitespacesAndNewlines)
.split(separator: ";")
guard
segments.count <= 2,
let urlString = segments.first,
urlString.hasPrefix("<") && urlString.hasSuffix(">"),
let url = URLComponents(string: String(urlString.dropFirst().dropLast())),
let pageString = url.queryItems?.first(where: { $0.name == "page" })?.value,
let page = Int(pageString) else {
continue
}
for segment in segments.dropFirst() {
switch segment.trimmingCharacters(in: .whitespacesAndNewlines) {
case "rel=\"next\"":
nextPage = page
case "rel=\"prev\"":
previousPage = page
case "rel=\"first\"":
firstPage = page
case "rel=\"last\"":
lastPage = page
default:
continue
}
}
}
}
}
public struct PageOptions {
public var page: Int
public var perPage: Int
public init(page: Int, perPage: Int) {
self.page = page
self.perPage = perPage
}
}
extension Array where Element == URLQueryItem {
init(options: PageOptions) {
self.init()
append(URLQueryItem(name: "page", value: String(options.page)))
append(URLQueryItem(name: "per_page", value: String(options.perPage)))
}
}

View File

@ -32,11 +32,6 @@ extension Resource {
} }
extension URLRequest { extension URLRequest {
init<R: Resource & HasRequestBody>(_ resource: R, configuration: Configuration, encoder: JSONEncoder) throws {
self.init(resource, configuration: configuration)
httpBody = try encoder.encode(resource.body)
}
init<R: Resource>(_ resource: R, configuration: Configuration) { init<R: Resource>(_ resource: R, configuration: Configuration) {
let url = configuration.url(for: resource.path) let url = configuration.url(for: resource.path)
var request = URLRequest(url: url) var request = URLRequest(url: url)
@ -44,4 +39,9 @@ extension URLRequest {
resource.transformRequest(&request) resource.transformRequest(&request)
self = request self = request
} }
init<R: Resource & HasRequestBody>(_ resource: R, configuration: Configuration, encoder: JSONEncoder) throws {
self.init(resource, configuration: configuration)
httpBody = try encoder.encode(resource.body)
}
} }

View File

@ -19,9 +19,15 @@ enum ResponseError: Error {
public struct Response<T> { public struct Response<T> {
public let content: T public let content: T
public let response: URLResponse public let response: URLResponse
public let page: Page?
init(content: T, response: URLResponse) { init(content: T, response: URLResponse) {
self.content = content self.content = content
self.response = response self.response = response
if let response = response as? HTTPURLResponse, let link = response.allHeaderFields["Link"] as? String {
page = Page(for: link)
} else {
page = nil
}
} }
} }

View File

@ -24,6 +24,8 @@ extension Agent.Resources {
/// organization slug /// organization slug
public var organization: String public var organization: String
public var pageOptions: PageOptions?
public var path: String { public var path: String {
"organizations/\(organization)/agents" "organizations/\(organization)/agents"
} }
@ -31,6 +33,17 @@ extension Agent.Resources {
public init(organization: String) { public init(organization: String) {
self.organization = organization self.organization = organization
} }
public func transformRequest(_ request: inout URLRequest) {
guard let url = request.url,
var components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
return
}
if let options = pageOptions {
components.queryItems = [URLQueryItem](options: options)
}
request.url = components.url
}
} }
public struct Get: Resource, HasResponseBody { public struct Get: Resource, HasResponseBody {

View File

@ -28,6 +28,8 @@ extension Annotation.Resources {
/// build number /// build number
public var build: Int public var build: Int
public var pageOptions: PageOptions?
public var path: String { public var path: String {
"organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/annotations" "organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/annotations"
} }
@ -37,5 +39,16 @@ extension Annotation.Resources {
self.pipeline = pipeline self.pipeline = pipeline
self.build = build self.build = build
} }
public func transformRequest(_ request: inout URLRequest) {
guard let url = request.url,
var components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
return
}
if let options = pageOptions {
components.queryItems = [URLQueryItem](options: options)
}
request.url = components.url
}
} }
} }

View File

@ -12,23 +12,25 @@ import FoundationNetworking
#endif #endif
extension Artifact { extension Artifact {
enum Resources { } public enum Resources { }
} }
extension Artifact.Resources { extension Artifact.Resources {
/// List artifacts for a build /// List artifacts for a build
/// ///
/// Returns a paginated list of a builds artifacts across all of its jobs. /// Returns a paginated list of a builds artifacts across all of its jobs.
struct ListByBuild: Resource, HasResponseBody { public struct ListByBuild: Resource, HasResponseBody {
typealias Content = [Artifact] public typealias Content = [Artifact]
/// organization slug /// organization slug
var organization: String public var organization: String
/// pipeline slug /// pipeline slug
var pipeline: String public var pipeline: String
/// build number /// build number
var build: Int public var build: Int
var path: String { public var pageOptions: PageOptions?
public var path: String {
"organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/artifacts" "organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/artifacts"
} }
@ -37,23 +39,36 @@ extension Artifact.Resources {
self.pipeline = pipeline self.pipeline = pipeline
self.build = build self.build = build
} }
public func transformRequest(_ request: inout URLRequest) {
guard let url = request.url,
var components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
return
}
if let options = pageOptions {
components.queryItems = [URLQueryItem](options: options)
}
request.url = components.url
}
} }
/// List artifacts for a job /// List artifacts for a job
/// ///
/// Returns a paginated list of a jobs artifacts. /// Returns a paginated list of a jobs artifacts.
struct ListByJob: Resource, HasResponseBody { public struct ListByJob: Resource, HasResponseBody {
typealias Content = [Artifact] public typealias Content = [Artifact]
/// organization slug /// organization slug
var organization: String public var organization: String
/// pipeline slug /// pipeline slug
var pipeline: String public var pipeline: String
/// build number /// build number
var build: Int public var build: Int
/// job ID /// job ID
var jobId: UUID public var jobId: UUID
var path: String { public var pageOptions: PageOptions?
public var path: String {
"organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/jobs/\(jobId)/artifacts" "organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/jobs/\(jobId)/artifacts"
} }
@ -63,23 +78,34 @@ extension Artifact.Resources {
self.build = build self.build = build
self.jobId = jobId self.jobId = jobId
} }
public func transformRequest(_ request: inout URLRequest) {
guard let url = request.url,
var components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
return
}
if let options = pageOptions {
components.queryItems = [URLQueryItem](options: options)
}
request.url = components.url
}
} }
/// Get an artifact /// Get an artifact
struct Get: Resource, HasResponseBody { public struct Get: Resource, HasResponseBody {
typealias Content = Artifact public typealias Content = Artifact
/// organization slug /// organization slug
var organization: String public var organization: String
/// pipeline slug /// pipeline slug
var pipeline: String public var pipeline: String
/// build number /// build number
var build: Int public var build: Int
/// job ID /// job ID
var jobId: UUID public var jobId: UUID
/// artifact ID /// artifact ID
var artifactId: UUID public var artifactId: UUID
var path: String { public var path: String {
"organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/jobs/\(jobId)/artifacts/\(artifactId)" "organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/jobs/\(jobId)/artifacts/\(artifactId)"
} }
@ -95,20 +121,20 @@ extension Artifact.Resources {
/// Download an artifact /// Download an artifact
/// ///
/// ///
struct Download: Resource { public struct Download: Resource, HasResponseBody {
typealias Content = Artifact.URLs public typealias Content = Artifact.URLs
/// organization slug /// organization slug
var organization: String public var organization: String
/// pipeline slug /// pipeline slug
var pipeline: String public var pipeline: String
/// build number /// build number
var build: Int public var build: Int
/// job ID /// job ID
var jobId: UUID public var jobId: UUID
/// artifact ID /// artifact ID
var artifactId: UUID public var artifactId: UUID
var path: String { public var path: String {
"organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/jobs/\(jobId)/artifacts/\(artifactId)/download" "organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/jobs/\(jobId)/artifacts/\(artifactId)/download"
} }
@ -124,27 +150,23 @@ extension Artifact.Resources {
/// Delete an artifact /// Delete an artifact
/// ///
/// ///
struct Delete: Resource { public struct Delete: Resource {
typealias Content = Void public typealias Content = Void
/// organization slug /// organization slug
var organization: String public var organization: String
/// pipeline slug /// pipeline slug
var pipeline: String public var pipeline: String
/// build number /// build number
var build: Int public var build: Int
/// job ID /// job ID
var jobId: UUID public var jobId: UUID
/// artifact ID /// artifact ID
var artifactId: UUID public var artifactId: UUID
var path: String { public var path: String {
"organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/jobs/\(jobId)/artifacts/\(artifactId)" "organizations/\(organization)/pipelines/\(pipeline)/builds/\(build)/jobs/\(jobId)/artifacts/\(artifactId)"
} }
func transformRequest(_ request: inout URLRequest) {
request.httpMethod = "DELETE"
}
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.organization = organization
self.pipeline = pipeline self.pipeline = pipeline
@ -152,5 +174,9 @@ extension Artifact.Resources {
self.jobId = jobId self.jobId = jobId
self.artifactId = artifactId self.artifactId = artifactId
} }
public func transformRequest(_ request: inout URLRequest) {
request.httpMethod = "DELETE"
}
} }
} }

View File

@ -24,10 +24,13 @@ extension Build.Resources {
public typealias Content = [Build] public typealias Content = [Build]
public let path = "builds" public let path = "builds"
public var options: QueryOptions? public var queryOptions: QueryOptions?
public init(options: Build.Resources.QueryOptions? = nil) { public var pageOptions: PageOptions?
self.options = options
public init(queryOptions: Build.Resources.QueryOptions? = nil, pageOptions: PageOptions? = nil) {
self.queryOptions = queryOptions
self.pageOptions = pageOptions
} }
public func transformRequest(_ request: inout URLRequest) { public func transformRequest(_ request: inout URLRequest) {
@ -35,9 +38,14 @@ extension Build.Resources {
var components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { var components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
return return
} }
if let options = options { var queryItems: [URLQueryItem] = []
components.queryItems = [URLQueryItem](options: options) if let options = queryOptions {
queryItems.append(contentsOf: [URLQueryItem](options: options))
} }
if let options = pageOptions {
queryItems.append(contentsOf: [URLQueryItem](options: options))
}
components.queryItems = queryItems
request.url = components.url request.url = components.url
} }
} }
@ -51,15 +59,18 @@ extension Build.Resources {
/// organization slug /// organization slug
public var organization: String public var organization: String
public var options: QueryOptions? public var queryOptions: QueryOptions?
public var pageOptions: PageOptions?
public var path: String { public var path: String {
"organizations/\(organization)/builds" "organizations/\(organization)/builds"
} }
public init(organization: String, options: Build.Resources.QueryOptions? = nil) { public init(organization: String, queryOptions: Build.Resources.QueryOptions? = nil, pageOptions: PageOptions? = nil) {
self.organization = organization self.organization = organization
self.options = options self.queryOptions = queryOptions
self.pageOptions = pageOptions
} }
public func transformRequest(_ request: inout URLRequest) { public func transformRequest(_ request: inout URLRequest) {
@ -67,9 +78,14 @@ extension Build.Resources {
var components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { var components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
return return
} }
if let options = options { var queryItems: [URLQueryItem] = []
components.queryItems = [URLQueryItem](options: options) if let options = queryOptions {
queryItems.append(contentsOf: [URLQueryItem](options: options))
} }
if let options = pageOptions {
queryItems.append(contentsOf: [URLQueryItem](options: options))
}
components.queryItems = queryItems
request.url = components.url request.url = components.url
} }
} }
@ -84,16 +100,19 @@ extension Build.Resources {
/// pipeline slug /// pipeline slug
public var pipeline: String public var pipeline: String
public var options: QueryOptions? public var queryOptions: QueryOptions?
public var pageOptions: PageOptions?
public var path: String { public var path: String {
"organizations/\(organization)/pipelines/\(pipeline)/builds" "organizations/\(organization)/pipelines/\(pipeline)/builds"
} }
public init(organization: String, pipeline: String, options: Build.Resources.QueryOptions? = nil) { public init(organization: String, pipeline: String, queryOptions: Build.Resources.QueryOptions? = nil, pageOptions: PageOptions? = nil) {
self.organization = organization self.organization = organization
self.pipeline = pipeline self.pipeline = pipeline
self.options = options self.queryOptions = queryOptions
self.pageOptions = pageOptions
} }
public func transformRequest(_ request: inout URLRequest) { public func transformRequest(_ request: inout URLRequest) {
@ -101,9 +120,14 @@ extension Build.Resources {
var components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { var components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
return return
} }
if let options = options { var queryItems: [URLQueryItem] = []
components.queryItems = [URLQueryItem](options: options) if let options = queryOptions {
queryItems.append(contentsOf: [URLQueryItem](options: options))
} }
if let options = pageOptions {
queryItems.append(contentsOf: [URLQueryItem](options: options))
}
components.queryItems = queryItems
request.url = components.url request.url = components.url
} }
} }
@ -260,18 +284,6 @@ extension Build.Resources {
} }
public struct QueryOptions { public struct QueryOptions {
internal 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: String? = nil) {
self.branches = branches
self.commit = commit
self.createdFrom = createdFrom
self.createdTo = createdTo
self.creator = creator
self.finishedFrom = finishedFrom
self.includeRetriedJobs = includeRetriedJobs
self.metadata = metadata
self.state = state
}
/// Filters the results by the given branch or branches. /// Filters the results by the given branch or branches.
public var branches: [String] = [] public var branches: [String] = []
/// Filters the results by the commit (only works for full sha, not for shortened ones). /// Filters the results by the commit (only works for full sha, not for shortened ones).
@ -290,6 +302,18 @@ extension Build.Resources {
public var metadata: [String: String] = [:] public var metadata: [String: String] = [:]
/// 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. /// 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: String? public var state: String?
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: String? = nil) {
self.branches = branches
self.commit = commit
self.createdFrom = createdFrom
self.createdTo = createdTo
self.creator = creator
self.finishedFrom = finishedFrom
self.includeRetriedJobs = includeRetriedJobs
self.metadata = metadata
self.state = state
}
} }
} }

View File

@ -23,7 +23,22 @@ extension Organization.Resources {
public typealias Content = [Organization] public typealias Content = [Organization]
public let path = "organizations" public let path = "organizations"
public init() {} public var pageOptions: PageOptions?
public init(pageOptions: PageOptions? = nil) {
self.pageOptions = pageOptions
}
public func transformRequest(_ request: inout URLRequest) {
guard let url = request.url,
var components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
return
}
if let options = pageOptions {
components.queryItems = [URLQueryItem](options: options)
}
request.url = components.url
}
} }
/// Get an organization /// Get an organization

View File

@ -24,12 +24,26 @@ extension Pipeline.Resources {
/// organization slug /// organization slug
public var organization: String public var organization: String
public var pageOptions: PageOptions?
public var path: String { public var path: String {
"organizations/\(organization)/pipelines" "organizations/\(organization)/pipelines"
} }
public init(organization: String) { public init(organization: String, pageOptions: PageOptions? = nil) {
self.organization = organization self.organization = organization
self.pageOptions = pageOptions
}
public func transformRequest(_ request: inout URLRequest) {
guard let url = request.url,
var components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
return
}
if let options = pageOptions {
components.queryItems = [URLQueryItem](options: options)
}
request.url = components.url
} }
} }

View File

@ -26,7 +26,30 @@ extension Agent {
version: "3.20.0", version: "3.20.0",
creator: User(), creator: User(),
createdAt: Date(timeIntervalSince1970: 1000), createdAt: Date(timeIntervalSince1970: 1000),
job: Job(), job: Job.script(Job.Command(id: UUID(),
name: "📦",
command: nil,
stepKey: nil,
buildUrl: URL(),
webUrl: URL(),
logUrl: URL(),
rawLogUrl: URL(),
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) ),
lastJobFinishedAt: nil, lastJobFinishedAt: nil,
priority: nil, priority: nil,
metaData: []) metaData: [])

View File

@ -85,13 +85,15 @@ class ArtifactsTests: XCTestCase {
} }
func testArtifactsDownload() throws { func testArtifactsDownload() throws {
let context = MockContext() let expected = Artifact.URLs(url: URL())
let context = try MockContext(content: expected)
let expectation = XCTestExpectation() let expectation = XCTestExpectation()
context.client.send(Artifact.Resources.Download(organization: "buildkite", pipeline: "my-pipeline", build: 1, jobId: UUID(), artifactId: UUID())) { result in context.client.send(Artifact.Resources.Download(organization: "buildkite", pipeline: "my-pipeline", build: 1, jobId: UUID(), artifactId: UUID())) { result in
do { do {
_ = try result.get() let response = try result.get()
XCTAssertEqual(expected, response.content)
} catch { } catch {
XCTFail(error.localizedDescription) XCTFail(error.localizedDescription)
} }

View File

@ -61,7 +61,7 @@ class BuildsTests: XCTestCase {
let expected = [Build(), Build()] let expected = [Build(), Build()]
let context = try MockContext(content: expected) let context = try MockContext(content: expected)
let resource = Build.Resources.ListAll(options: Build.Resources.QueryOptions(branches: ["master"], let resource = Build.Resources.ListAll(queryOptions: Build.Resources.QueryOptions(branches: ["master"],
commit: "HEAD", commit: "HEAD",
createdFrom: Date(timeIntervalSince1970: 1000), createdFrom: Date(timeIntervalSince1970: 1000),
createdTo: Date(timeIntervalSince1970: 1000), createdTo: Date(timeIntervalSince1970: 1000),

View File

@ -13,40 +13,9 @@ import XCTest
import FoundationNetworking import FoundationNetworking
#endif #endif
extension Job {
init() {
self.init(id: UUID(),
type: "script",
name: "📦",
stepKey: "package",
agentQueryRules: [],
state: "finished",
buildUrl: URL(),
webUrl: URL(),
logUrl: URL(),
rawLogUrl: URL(),
artifactsUrl: URL(),
command: "echo 1",
softFailed: false,
exitStatus: 0,
artifactPaths: "",
agent: nil,
createdAt: Date(timeIntervalSince1970: 1000),
scheduledAt: Date(timeIntervalSince1970: 1000),
runnableAt: nil,
startedAt: nil,
finishedAt: nil,
retried: false,
retriedInJobId: UUID(),
retriesCount: 0,
parallelGroupIndex: nil,
parallelGroupTotal: nil)
}
}
class JobsTests: XCTestCase { class JobsTests: XCTestCase {
func testJobsRetry() throws { func testJobsRetry() throws {
let expected = Job() let expected = Job.waiter(Job.Wait(id: UUID()))
let context = try MockContext(content: expected) let context = try MockContext(content: expected)
let expectation = XCTestExpectation() let expectation = XCTestExpectation()
@ -64,7 +33,7 @@ class JobsTests: XCTestCase {
} }
func testJobsUnblock() throws { func testJobsUnblock() throws {
let expected = Job() let expected = Job.waiter(Job.Wait(id: UUID()))
let context = try MockContext(content: expected) let context = try MockContext(content: expected)
let body = Job.Resources.Unblock.Body() let body = Job.Resources.Unblock.Body()