Skip to content

Commit ab87ffd

Browse files
committed
DO NOT MERGE: initial conversion of swift toolchain release to OpenAPI
1 parent 726ea90 commit ab87ffd

File tree

5 files changed

+105
-70
lines changed

5 files changed

+105
-70
lines changed

Sources/SwiftlyCore/HTTPClient.swift

Lines changed: 78 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ extension Components.Schemas.SwiftlyReleasePlatformArtifacts {
5353
public protocol HTTPRequestExecutor {
5454
func execute(_ request: HTTPClientRequest, timeout: TimeAmount) async throws -> HTTPClientResponse
5555
func getCurrentSwiftlyRelease() async throws -> Components.Schemas.SwiftlyRelease
56+
func getReleaseToolchains(
57+
platform: PlatformDefinition,
58+
arch a: Components.Schemas.Architecture?,
59+
limit: Int?,
60+
filter: ((ToolchainVersion.StableRelease) -> Bool)?
61+
) async throws -> [ToolchainVersion.StableRelease]
5662
}
5763

5864
internal struct SwiftlyUserAgentMiddleware: ClientMiddleware {
@@ -128,6 +134,61 @@ internal class HTTPRequestExecutorImpl: HTTPRequestExecutor {
128134
let response = try await client.getCurrentSwiftlyRelease()
129135
return try response.ok.body.json
130136
}
137+
138+
public func getReleaseToolchains(
139+
platform: PlatformDefinition,
140+
arch a: Components.Schemas.Architecture? = nil,
141+
limit: Int? = nil,
142+
filter: ((ToolchainVersion.StableRelease) -> Bool)? = nil
143+
) async throws -> [ToolchainVersion.StableRelease] {
144+
let arch = a ?? cpuArch
145+
146+
let config = AsyncHTTPClientTransport.Configuration(client: self.httpClient, timeout: .seconds(30))
147+
let swiftlyUserAgent = SwiftlyUserAgentMiddleware()
148+
149+
let client = Client(
150+
serverURL: try Servers.Server1.url(),
151+
transport: AsyncHTTPClientTransport(configuration: config),
152+
middlewares: [swiftlyUserAgent]
153+
)
154+
155+
let response = try await client.listReleases()
156+
157+
var swiftOrgFiltered: [ToolchainVersion.StableRelease] = try response.ok.body.json.compactMap { swiftOrgRelease in
158+
if platform.name != PlatformDefinition.macOS.name {
159+
// If the platform isn't xcode then verify that there is an offering for this platform name and arch
160+
guard let swiftOrgPlatform = swiftOrgRelease.platforms.first(where: { $0.matches(platform) }) else {
161+
return nil
162+
}
163+
164+
guard case let archs = swiftOrgPlatform.archs, archs.contains(arch) else {
165+
return nil
166+
}
167+
}
168+
169+
guard let version = try? ToolchainVersion(parsing: swiftOrgRelease.stableName),
170+
case let .stable(release) = version
171+
else {
172+
throw SwiftlyError(message: "error parsing swift.org release version: \(swiftOrgRelease.stableName)")
173+
}
174+
175+
if let filter {
176+
guard filter(release) else {
177+
return nil
178+
}
179+
}
180+
181+
return release
182+
}
183+
184+
swiftOrgFiltered.sort(by: >)
185+
186+
return if let limit = limit {
187+
Array(swiftOrgFiltered.prefix(limit))
188+
} else {
189+
swiftOrgFiltered
190+
}
191+
}
131192
}
132193

133194
private func makeRequest(url: String) -> HTTPClientRequest {
@@ -136,13 +197,21 @@ private func makeRequest(url: String) -> HTTPClientRequest {
136197
return request
137198
}
138199

139-
struct SwiftOrgPlatform: Codable {
140-
var name: String
141-
var archs: [String]?
200+
extension Components.Schemas.Release {
201+
var stableName: String {
202+
let components = self.name.components(separatedBy: ".")
203+
if components.count == 2 {
204+
return self.name + ".0"
205+
} else {
206+
return self.name
207+
}
208+
}
209+
}
142210

143-
/// platform is a mapping from the 'name' field of the swift.org platform object
211+
extension Components.Schemas.Platform {
212+
/// platformDef is a mapping from the 'name' field of the swift.org platform object
144213
/// to swiftly's PlatformDefinition, if possible.
145-
var platform: PlatformDefinition? {
214+
var platformDef: PlatformDefinition? {
146215
// NOTE: some of these platforms are represented on swift.org metadata, but not supported by swiftly and so they don't have constants in PlatformDefinition
147216
switch self.name {
148217
case "Ubuntu 14.04":
@@ -181,28 +250,14 @@ struct SwiftOrgPlatform: Codable {
181250
}
182251

183252
func matches(_ platform: PlatformDefinition) -> Bool {
184-
guard let myPlatform = self.platform else {
253+
guard let myPlatform = self.platformDef else {
185254
return false
186255
}
187256

188257
return myPlatform.name == platform.name
189258
}
190259
}
191260

192-
public struct SwiftOrgRelease: Codable {
193-
var name: String
194-
var platforms: [SwiftOrgPlatform]
195-
196-
var stableName: String {
197-
let components = self.name.components(separatedBy: ".")
198-
if components.count == 2 {
199-
return self.name + ".0"
200-
} else {
201-
return self.name
202-
}
203-
}
204-
}
205-
206261
public struct SwiftOrgSnapshotList: Codable {
207262
var aarch64: [SwiftOrgSnapshot]?
208263
var x86_64: [SwiftOrgSnapshot]?
@@ -291,49 +346,11 @@ public struct SwiftlyHTTPClient {
291346
/// limit (default unlimited).
292347
public func getReleaseToolchains(
293348
platform: PlatformDefinition,
294-
arch a: String? = nil,
349+
arch a: Components.Schemas.Architecture? = nil,
295350
limit: Int? = nil,
296351
filter: ((ToolchainVersion.StableRelease) -> Bool)? = nil
297352
) async throws -> [ToolchainVersion.StableRelease] {
298-
let arch = a ?? cpuArch
299-
300-
let url = "https://www.swift.org/api/v1/install/releases.json"
301-
let swiftOrgReleases: [SwiftOrgRelease] = try await self.getFromJSON(url: url, type: [SwiftOrgRelease].self)
302-
303-
var swiftOrgFiltered: [ToolchainVersion.StableRelease] = try swiftOrgReleases.compactMap { swiftOrgRelease in
304-
if platform.name != PlatformDefinition.macOS.name {
305-
// If the platform isn't xcode then verify that there is an offering for this platform name and arch
306-
guard let swiftOrgPlatform = swiftOrgRelease.platforms.first(where: { $0.matches(platform) }) else {
307-
return nil
308-
}
309-
310-
guard let archs = swiftOrgPlatform.archs, archs.contains(arch) else {
311-
return nil
312-
}
313-
}
314-
315-
guard let version = try? ToolchainVersion(parsing: swiftOrgRelease.stableName),
316-
case let .stable(release) = version
317-
else {
318-
throw SwiftlyError(message: "error parsing swift.org release version: \(swiftOrgRelease.stableName)")
319-
}
320-
321-
if let filter {
322-
guard filter(release) else {
323-
return nil
324-
}
325-
}
326-
327-
return release
328-
}
329-
330-
swiftOrgFiltered.sort(by: >)
331-
332-
return if let limit = limit {
333-
Array(swiftOrgFiltered.prefix(limit))
334-
} else {
335-
swiftOrgFiltered
336-
}
353+
try await SwiftlyCore.httpRequestExecutor.getReleaseToolchains(platform: platform, arch: a, limit: limit, filter: filter)
337354
}
338355

339356
public struct SnapshotBranchNotFoundError: LocalizedError {
@@ -349,7 +366,7 @@ public struct SwiftlyHTTPClient {
349366
limit: Int? = nil,
350367
filter: ((ToolchainVersion.Snapshot) -> Bool)? = nil
351368
) async throws -> [ToolchainVersion.Snapshot] {
352-
let arch = a ?? cpuArch
369+
let arch = a ?? String(describing: cpuArch)
353370

354371
let platformName = if platform.name == PlatformDefinition.macOS.name {
355372
"macos"

Sources/SwiftlyCore/SwiftlyCore.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ public func readLine(prompt: String) -> String? {
5353
}
5454

5555
#if arch(x86_64)
56-
public let cpuArch = "x86_64"
56+
public let cpuArch = Components.Schemas.Architecture(value1: Components.Schemas.KnownArchitecture.x8664)
5757
#elseif arch(arm64)
58-
public let cpuArch = "aarch64"
58+
public let cpuArch = Components.Schemas.Architecture(value1: Components.Schemas.KnownArchitecture.aarch64)
5959
#else
6060
#error("Unsupported processor architecture")
6161
#endif

Sources/SwiftlyCore/openapi-generator-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ generate:
44
namingStrategy: idiomatic
55
accessModifier: public
66
filter:
7-
tags: ["Swiftly"]
7+
tags: ["Swiftly", "Toolchains"]

Tests/SwiftlyTests/HTTPClientTests.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ final class HTTPClientTests: SwiftlyTests {
66
func testGet() async throws {
77
// GIVEN: we have a swiftly http client
88
// WHEN: we make get request for a particular type of JSON
9-
var releases: [SwiftOrgRelease] = try await SwiftlyCore.httpClient.getFromJSON(
9+
var releases: [Components.Schemas.Release] = try await SwiftlyCore.httpClient.getFromJSON(
1010
url: "https://www.swift.org/api/v1/install/releases.json",
11-
type: [SwiftOrgRelease].self,
11+
type: [Components.Schemas.Release].self,
1212
headers: [:]
1313
)
1414
// THEN: we get a decoded JSON response
@@ -20,7 +20,7 @@ final class HTTPClientTests: SwiftlyTests {
2020
do {
2121
releases = try await SwiftlyCore.httpClient.getFromJSON(
2222
url: "https://www.swift.org/api/v1/install/releases-invalid.json",
23-
type: [SwiftOrgRelease].self,
23+
type: [Components.Schemas.Release].self,
2424
headers: [:]
2525
)
2626
} catch {
@@ -35,7 +35,7 @@ final class HTTPClientTests: SwiftlyTests {
3535
do {
3636
releases = try await SwiftlyCore.httpClient.getFromJSON(
3737
url: "https://invalid.swift.org/api/v1/install/releases.json",
38-
type: [SwiftOrgRelease].self,
38+
type: [Components.Schemas.Release].self,
3939
headers: [:]
4040
)
4141
} catch {
@@ -74,7 +74,7 @@ final class HTTPClientTests: SwiftlyTests {
7474
.release(major: 6, minor: 0), // This is available in swift.org API
7575
]
7676

77-
for arch in ["x86_64", "aarch64"] {
77+
for arch in [Components.Schemas.Architecture(value1: Components.Schemas.KnownArchitecture.x8664), Components.Schemas.Architecture(value1: Components.Schemas.KnownArchitecture.aarch64)] {
7878
for platform in supportedPlatforms {
7979
// GIVEN: we have a swiftly http client with swift.org metadata capability
8080
// WHEN: we ask for the first five releases of a supported platform in a supported arch
@@ -87,7 +87,7 @@ final class HTTPClientTests: SwiftlyTests {
8787
for branch in branches {
8888
// GIVEN: we have a swiftly http client with swift.org metadata capability
8989
// WHEN: we ask for the first five snapshots on a branch for a supported platform and arch
90-
let snapshots = try await SwiftlyCore.httpClient.getSnapshotToolchains(platform: platform, arch: arch, branch: branch, limit: 5)
90+
let snapshots = try await SwiftlyCore.httpClient.getSnapshotToolchains(platform: platform, arch: String(describing: arch), branch: branch, limit: 5)
9191
// THEN: we get at least 3 releases
9292
XCTAssertTrue(3 <= snapshots.count)
9393
}

Tests/SwiftlyTests/SwiftlyTests.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,15 @@ private struct MockHTTPRequestExecutor: HTTPRequestExecutor {
467467
public func getCurrentSwiftlyRelease() async throws -> Components.Schemas.SwiftlyRelease {
468468
throw SwiftlyTestError(message: "Mocking of fetching the current swiftly release is not implemented in MockHTTPRequestExecutor.")
469469
}
470+
471+
public func getReleaseToolchains(
472+
platform _: PlatformDefinition,
473+
arch _: Components.Schemas.Architecture?,
474+
limit _: Int?,
475+
filter _: ((ToolchainVersion.StableRelease) -> Bool)?
476+
) async throws -> [ToolchainVersion.StableRelease] {
477+
throw SwiftlyTestError(message: "Mocking of fetching the release toolchains is not implemented in MockHTTPRequestExecutor.")
478+
}
470479
}
471480

472481
/// An `HTTPRequestExecutor` which will return a mocked response to any toolchain download requests.
@@ -506,6 +515,15 @@ public class MockToolchainDownloader: HTTPRequestExecutor {
506515
return release
507516
}
508517

518+
public func getReleaseToolchains(
519+
platform _: PlatformDefinition,
520+
arch _: Components.Schemas.Architecture?,
521+
limit _: Int?,
522+
filter _: ((ToolchainVersion.StableRelease) -> Bool)?
523+
) async throws -> [ToolchainVersion.StableRelease] {
524+
throw SwiftlyTestError(message: "Mocking of the fetching of release toolchains is not implemented by the MockToolchainDownloader")
525+
}
526+
509527
public func execute(_ request: HTTPClientRequest, timeout: TimeAmount) async throws -> HTTPClientResponse {
510528
guard let url = URL(string: request.url) else {
511529
throw SwiftlyTestError(message: "invalid request URL: \(request.url)")

0 commit comments

Comments
 (0)