Skip to content

Commit

Permalink
Fix internal data type (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
MykolaVasyk authored Jun 21, 2024
1 parent a8fc4f5 commit db31730
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 46 deletions.
32 changes: 19 additions & 13 deletions Sources/AppStoreConnectClient/AppStoreConnectClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import OpenAPIURLSession
import Foundation

/// The client for interacting with the App Store Connect API.
public struct AppStoreConnectClient {
public struct AppStoreConnectClient: AppStoreConnectClientProtocol {
private let client: any APIProtocol

/// Initializes the client with a custom API protocol implementation.
Expand Down Expand Up @@ -124,20 +124,26 @@ public struct AppStoreConnectClient {
/// - Throws: An error of type `AppStoreConnectError` if the response indicates an error.
public func fetchBuilds(
for app: Application,
with query: BuildsQuery
with query: BuildsQuery? = nil
) async throws -> [Build] {
let response = try await client.builds_hyphen_get_collection(
query: .init(
filter_lbrack_app_rbrack_: [app.id],
sort: type(of: query.sort).init(arrayLiteral: ._hyphen_version),
fields_lbrack_builds_rbrack_: type(of: query.fields).init(
arrayLiteral:
.version,
.minOsVersion,
.uploadedDate
var response: Operations.builds_hyphen_get_collection.Output
if let query = query {
let sort = query.convert(from: query.sort)
let fields = query.convert(from: query.fields)
response = try await client.builds_hyphen_get_collection(
query: .init(
filter_lbrack_app_rbrack_: [app.id],
sort: sort,
fields_lbrack_builds_rbrack_: fields
)
)
)
} else {
response = try await client.builds_hyphen_get_collection(
query: .init(
filter_lbrack_app_rbrack_: [app.id]
)
)
}
switch response {
case .ok(let okResponse):
return try okResponse.body.json.data.compactMap { Build(schema: $0) }
Expand All @@ -164,7 +170,7 @@ public struct AppStoreConnectClient {
/// - Parameter id: The build-id for which to fetch the pre-release version.
/// - Returns: A `PreReleaseVersion` object.
/// - Throws: An error of type `AppStoreConnectError` if the response indicates an error.
public func fetchPreReleaseVersion(for build: Build) async throws -> PreReleaseVersion {
public func fetchPreReleaseVersion(by build: Build) async throws -> PreReleaseVersion {
let id = build.id
let response = try await client.builds_hyphen_preReleaseVersion_hyphen_get_to_one_related(
path: .init(id: id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public protocol AppStoreConnectClientProtocol {
/// - Note: Builds are specific versions of an app that have been uploaded to App Store Connect.
func fetchBuilds(
for app: Application,
with query: BuildsQuery
with query: BuildsQuery?
) async throws -> [Build]

/// Fetches the pre-release testFlight's version associated with a specific build from App Store Connect.
Expand Down
2 changes: 1 addition & 1 deletion Sources/AppStoreConnectClient/Models/Build.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import Foundation
/// - minOsVersion: The minimum required OS version for the build.
/// - Note: A build is a specific version of an app that has been uploaded to App Store Connect.
/// It contains metadata such as the build version, upload date, and minimum supported OS version.
public struct Build {
public struct Build: Equatable {
/// The unique identifier of the build.
public let id: String
/// The version string of the build.
Expand Down
60 changes: 58 additions & 2 deletions Sources/AppStoreConnectClient/Models/BuildsQuery.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,63 @@ import Foundation
/// A structure representing a query for builds with specified sort and fields parameters.
public struct BuildsQuery {
/// The sort payload for the query.
let sort: Operations.builds_hyphen_get_collection.Input.Query.sortPayload
public var sort: [Sort] = []
/// The fields payload for the query.
let fields: Operations.builds_hyphen_get_collection.Input.Query.fields_lbrack_builds_rbrack_Payload
public var fields: [Fields] = []

/// Converts the Sort enum array to the appropriate payload type used in the query.
/// - Parameter from: The Sort enum value to convert.
/// - Returns: The converted sort payload.
func convert(from sorts: [Sort]) -> [Operations.builds_hyphen_get_collection.Input.Query.sortPayloadPayload] {
return sorts.compactMap { sort in
return Operations.builds_hyphen_get_collection.Input.Query.sortPayloadPayload(rawValue: sort.rawValue)
}
}

/// Converts the Fields enum array to the appropriate payload type used in the query.
/// - Parameter from: The array of Fields enum values to convert.
/// - Returns: An array of converted field payloads.
func convert(from fields: [Fields]) -> [Operations.builds_hyphen_get_collection.Input.Query.fields_lbrack_builds_rbrack_PayloadPayload] {
return fields.compactMap { field in
return Operations.builds_hyphen_get_collection.Input.Query.fields_lbrack_builds_rbrack_PayloadPayload(rawValue: field.rawValue)
}
}

/// An enumeration representing the possible sort options for the query.
public enum Sort: String {
case preReleaseVersion
case hyphenPreReleaseVersion = "-preReleaseVersion"
case uploadedDate
case hyphenUploadedDate = "-uploadedDate"
case version
case hyphenVersion = "-version"
}

/// An enumeration representing the possible fields that can be included in the query.
public enum Fields: String {
case app
case appEncryptionDeclaration
case appStoreVersion
case betaAppReviewSubmission
case betaBuildLocalizations
case betaGroups
case buildAudienceType
case buildBetaDetail
case buildBundles
case computedMinMacOsVersion
case diagnosticSignatures
case expirationDate
case expired
case iconAssetToken
case icons
case individualTesters
case lsMinimumSystemVersion
case minOsVersion
case perfPowerMetrics
case preReleaseVersion
case processingState
case uploadedDate
case usesNonExemptEncryption
case version
}
}
103 changes: 74 additions & 29 deletions Tests/AppStoreConnectClientTests/AppStoreConnectClientTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -199,15 +199,12 @@ final class AppStoreConnectClientTests: XCTestCase {
let expectedBuilds = [Build(schema: MockObjects.build)]
let build = try await client.fetchBuilds(
for: app,
with: BuildsQuery.init(
sort: .init(arrayLiteral: ._hyphen_version),
fields: .init(arrayLiteral: .version, .minOsVersion, .uploadedDate)
with: BuildsQuery(
sort: [.hyphenVersion],
fields: [.version, .minOsVersion, .uploadedDate]
)
)
XCTAssertEqual(build.first?.id, expectedBuilds.first?.id)
XCTAssertEqual(build.first?.version, expectedBuilds.first?.version)
XCTAssertEqual(build.first?.uploadedDate, expectedBuilds.first?.uploadedDate)
XCTAssertEqual(build.first?.minOsVersion, expectedBuilds.first?.minOsVersion)
XCTAssertEqual(build, expectedBuilds)
}

func testFetchBuildsBadRequest() async throws {
Expand All @@ -219,12 +216,12 @@ final class AppStoreConnectClientTests: XCTestCase {
do {
let result = try await client.fetchBuilds(
for: app,
with: BuildsQuery.init(
sort: .init(arrayLiteral: ._hyphen_version),
fields: .init(arrayLiteral: .version, .minOsVersion, .uploadedDate)
with: BuildsQuery(
sort: [.hyphenVersion, .uploadedDate],
fields: [.version, .minOsVersion, .uploadedDate]
)
)
XCTFail("Expected error not thrown, got result: \(result)")
XCTFail("Expected error not thrown, got result: \(String(describing: result))")
} catch AppStoreConnectError.badRequest(let error) {
XCTAssertEqual(error, expectedError)
} catch {
Expand All @@ -241,12 +238,12 @@ final class AppStoreConnectClientTests: XCTestCase {
do {
let result = try await client.fetchBuilds(
for: app,
with: BuildsQuery.init(
sort: .init(arrayLiteral: ._hyphen_version),
fields: .init(arrayLiteral: .version, .minOsVersion, .uploadedDate)
with: BuildsQuery(
sort: [.hyphenVersion, .uploadedDate],
fields: [.version, .minOsVersion, .uploadedDate]
)
)
XCTFail("Expected error not thrown, got result: \(result)")
XCTFail("Expected error not thrown, got result: \(String(describing: result))")
} catch AppStoreConnectError.unauthorized(let error) {
XCTAssertEqual(error, expectedError)
} catch {
Expand All @@ -263,12 +260,12 @@ final class AppStoreConnectClientTests: XCTestCase {
do {
let result = try await client.fetchBuilds(
for: app,
with: BuildsQuery.init(
sort: .init(arrayLiteral: ._hyphen_version),
fields: .init(arrayLiteral: .version, .minOsVersion, .uploadedDate)
with: BuildsQuery(
sort: [.hyphenVersion, .uploadedDate],
fields: [.version, .minOsVersion, .uploadedDate]
)
)
XCTFail("Expected error not thrown, got result: \(result)")
XCTFail("Expected error not thrown, got result: \(String(describing: result))")
} catch AppStoreConnectError.forbidden(let error) {
XCTAssertEqual(error, expectedError)
} catch {
Expand All @@ -285,12 +282,12 @@ final class AppStoreConnectClientTests: XCTestCase {
do {
let result = try await client.fetchBuilds(
for: app,
with: BuildsQuery.init(
sort: .init(arrayLiteral: ._hyphen_version),
fields: .init(arrayLiteral: .version, .minOsVersion, .uploadedDate)
with: BuildsQuery(
sort: [.hyphenVersion, .uploadedDate],
fields: [.version, .minOsVersion, .uploadedDate]
)
)
XCTFail("Expected error not thrown, got result: \(result)")
XCTFail("Expected error not thrown, got result: \(String(describing: result))")
} catch AppStoreConnectError.serverError(let error) {
XCTAssertEqual(error, expectedError)
} catch {
Expand All @@ -303,7 +300,7 @@ final class AppStoreConnectClientTests: XCTestCase {
mockClient.result = .ok
let client = AppStoreConnectClient(client: mockClient)
let build = Build(schema: MockObjects.build)
let preReleaseVersion = try await client.fetchPreReleaseVersion(for: build)
let preReleaseVersion = try await client.fetchPreReleaseVersion(by: build)
XCTAssertEqual(preReleaseVersion.id, "FooBarId")
XCTAssertEqual(preReleaseVersion.version, "Foo")
XCTAssertEqual(preReleaseVersion.platform, "IOS")
Expand All @@ -317,7 +314,7 @@ final class AppStoreConnectClientTests: XCTestCase {
let build = Build(schema: MockObjects.build)
let expectedError = "\nFailed with: \(badRequest), Foo, Bar, Baz."
do {
let result = try await client.fetchPreReleaseVersion(for: build)
let result = try await client.fetchPreReleaseVersion(by: build)
XCTFail("Expected error not thrown, got result: \(result)")
} catch AppStoreConnectError.badRequest(let error) {
XCTAssertEqual(error, expectedError)
Expand All @@ -333,7 +330,7 @@ final class AppStoreConnectClientTests: XCTestCase {
let build = Build(schema: MockObjects.build)
let expectedError = "\nFailed with: \(unauthorized), Foo, Bar, Baz."
do {
let result = try await client.fetchPreReleaseVersion(for: build)
let result = try await client.fetchPreReleaseVersion(by: build)
XCTFail("Expected error not thrown, got result: \(result)")
} catch AppStoreConnectError.unauthorized(let error) {
XCTAssertEqual(error, expectedError)
Expand All @@ -349,7 +346,7 @@ final class AppStoreConnectClientTests: XCTestCase {
let build = Build(schema: MockObjects.build)
let expectedError = "\nFailed with: \(notFound), Foo, Bar, Baz."
do {
let result = try await client.fetchPreReleaseVersion(for: build)
let result = try await client.fetchPreReleaseVersion(by: build)
XCTFail("Expected error not thrown, got result: \(result)")
} catch AppStoreConnectError.notFound(let error) {
XCTAssertEqual(error, expectedError)
Expand All @@ -365,7 +362,7 @@ final class AppStoreConnectClientTests: XCTestCase {
let build = Build(schema: MockObjects.build)
let expectedError = "\nFailed with: \(forbidden), Foo, Bar, Baz."
do {
let result = try await client.fetchPreReleaseVersion(for: build)
let result = try await client.fetchPreReleaseVersion(by: build)
XCTFail("Expected error not thrown, got result: \(result)")
} catch AppStoreConnectError.forbidden(let error) {
XCTAssertEqual(error, expectedError)
Expand All @@ -381,12 +378,60 @@ final class AppStoreConnectClientTests: XCTestCase {
let build = Build(schema: MockObjects.build)
let expectedError = 501
do {
let result = try await client.fetchPreReleaseVersion(for: build)
let result = try await client.fetchPreReleaseVersion(by: build)
XCTFail("Expected error not thrown, got result: \(result)")
} catch AppStoreConnectError.serverError(let error) {
XCTAssertEqual(error, expectedError)
} catch {
XCTFail("Unexpected error: \(error)")
}
}

func testFetchBuildsSuccessWithEmptySort() async throws {
var mockClient = MockAPIClient()
mockClient.result = .ok
let client = AppStoreConnectClient(client: mockClient)
let app = Application(id: "Foo", bundleId: "Bar")
let expectedBuilds = [Build(schema: MockObjects.build)]
let build = try await client.fetchBuilds(
for: app,
with: BuildsQuery(
sort: [],
fields: [.version, .minOsVersion, .uploadedDate]
)
)
XCTAssertEqual(build, expectedBuilds)
}

func testFetchBuildsSuccessWithEmptyFields() async throws {
var mockClient = MockAPIClient()
mockClient.result = .ok
let client = AppStoreConnectClient(client: mockClient)
let app = Application(id: "Foo", bundleId: "Bar")
let expectedBuilds = [Build(schema: MockObjects.build)]
let build = try await client.fetchBuilds(
for: app,
with: BuildsQuery(
sort: [.hyphenVersion, .uploadedDate],
fields: []
)
)
XCTAssertEqual(build, expectedBuilds)
}

func testFetchBuildsSuccessWithoutQuery() async throws {
var mockClient = MockAPIClient()
mockClient.result = .ok
let client = AppStoreConnectClient(client: mockClient)
let app = Application(id: "Foo", bundleId: "Bar")
let expectedBuilds = [Build(schema: MockObjects.build)]
let build = try await client.fetchBuilds(for: app)
XCTAssertEqual(build, expectedBuilds)
}

func testBuildsQueryInitialization() {
let query = BuildsQuery()
XCTAssertEqual(query.sort, [])
XCTAssertEqual(query.fields, [])
}
}

0 comments on commit db31730

Please sign in to comment.