diff --git a/Sources/Public/Clients/AmiiboLiveClient.swift b/Sources/Public/Clients/AmiiboLiveClient.swift index 49ef078..d087300 100644 --- a/Sources/Public/Clients/AmiiboLiveClient.swift +++ b/Sources/Public/Clients/AmiiboLiveClient.swift @@ -14,55 +14,61 @@ import Foundation import OpenAPIRuntime import OpenAPIURLSession +/// A type that implements a live client to the online service. public struct AmiiboLiveClient { // MARK: Properties + /// A client generated by the `OpenAPIRuntime` library. private let client: Client - // MARK: Initialisers + // MARK: Initializers - public init() throws { + /// Initializes this client. + public init() { self.client = .init( - serverURL: try Servers.Server1.url(), - configuration: .init(dateTranscoder: ISODateTranscoder()), + // The force unwrapping implemented below assumes that the server definition from the OpenAPI specification is correct. + serverURL: try! Servers.Server1.url(), + configuration: .init(dateTranscoder: ISOTimestampTranscoder()), transport: URLSessionTransport() ) } } -// MARK: - APIProtocol +// MARK: - APIClient extension AmiiboLiveClient: APIClient { // MARK: Functions - public func getAmiibos(by filter: AmiiboFilter) async throws -> [Amiibo] { - let response = try await { - do { - return try await client.getAmiibos( - .init(query: .init( - amiiboSeries: filter.series, - character: filter.gameCharacter, - gameseries: filter.gameSeries, - id: filter.identifier, - name: filter.name, - showgames: filter.showGames, - showusage: filter.showUsage, - _type: filter.type - )) - ) - } catch let error as ClientError { - guard let _ = error.underlyingError as? DecodingError else { - throw AmiiboServiceError.unknown - } + public func getAmiibos( + by filter: AmiiboFilter + ) async throws(AmiiboServiceError) -> [Amiibo] { + let response: Operations.getAmiibos.Output + do { + response = try await client.getAmiibos( + .init(query: .init( + amiiboSeries: filter.series, + character: filter.gameCharacter, + gameseries: filter.gameSeries, + id: filter.identifier, + name: filter.name, + showgames: filter.showGames, + showusage: filter.showUsage, + _type: filter.type + )) + ) + } catch let error as ClientError { + if error.underlyingError is DecodingError { throw AmiiboServiceError.decoding - } catch { + } else { throw AmiiboServiceError.unknown } - }() + } catch { + throw AmiiboServiceError.unknown + } switch response { case let .ok(ok): @@ -79,13 +85,21 @@ extension AmiiboLiveClient: APIClient { } } - public func getAmiiboSeries(by filter: AmiiboSeriesFilter) async throws -> [AmiiboSeries] { - let response = try await client.getAmiiboSeries( - .init(query: .init( - key: filter.key, - name: filter.name - )) - ) + public func getAmiiboSeries( + by filter: AmiiboSeriesFilter + ) async throws(AmiiboServiceError) -> [AmiiboSeries] { + let response: Operations.getAmiiboSeries.Output + + do { + response = try await client.getAmiiboSeries( + .init(query: .init( + key: filter.key, + name: filter.name + )) + ) + } catch { + throw AmiiboServiceError.unknown + } switch response { case let .ok(ok): @@ -108,13 +122,21 @@ extension AmiiboLiveClient: APIClient { } } - public func getAmiiboTypes(by filter: AmiiboTypeFilter) async throws -> [AmiiboType] { - let response = try await client.getAmiiboTypes( - .init(query: .init( - key: filter.key, - name: filter.name - )) - ) + public func getAmiiboTypes( + by filter: AmiiboTypeFilter + ) async throws(AmiiboServiceError) -> [AmiiboType] { + let response: Operations.getAmiiboTypes.Output + + do { + response = try await client.getAmiiboTypes( + .init(query: .init( + key: filter.key, + name: filter.name + )) + ) + } catch { + throw AmiiboServiceError.unknown + } switch response { case let .ok(ok): @@ -137,13 +159,21 @@ extension AmiiboLiveClient: APIClient { } } - public func getGameCharacters(by filter: GameCharacterFilter) async throws -> [GameCharacter] { - let response = try await client.getGameCharacters( - .init(query: .init( - key: filter.key, - name: filter.name - )) - ) + public func getGameCharacters( + by filter: GameCharacterFilter + ) async throws(AmiiboServiceError) -> [GameCharacter] { + let response: Operations.getGameCharacters.Output + + do { + response = try await client.getGameCharacters( + .init(query: .init( + key: filter.key, + name: filter.name + )) + ) + } catch { + throw AmiiboServiceError.unknown + } switch response { case let .ok(ok): @@ -166,13 +196,21 @@ extension AmiiboLiveClient: APIClient { } } - public func getGameSeries(by filter: GameSeriesFilter) async throws -> [GameSeries] { - let response = try await client.getGameSeries( - .init(query: .init( - key: filter.key, - name: filter.name - )) - ) + public func getGameSeries( + by filter: GameSeriesFilter + ) async throws(AmiiboServiceError) -> [GameSeries] { + let response: Operations.getGameSeries.Output + + do { + response = try await client.getGameSeries( + .init(query: .init( + key: filter.key, + name: filter.name + )) + ) + } catch { + throw AmiiboServiceError.unknown + } switch response { case let .ok(ok): @@ -195,8 +233,14 @@ extension AmiiboLiveClient: APIClient { } } - public func getLastUpdated() async throws -> Date { - let response = try await client.getLastUpdated() + public func getLastUpdated() async throws(AmiiboServiceError) -> Date { + let response: Operations.getLastUpdated.Output + + do { + response = try await client.getLastUpdated() + } catch { + throw AmiiboServiceError.unknown + } switch response { case let .ok(ok): @@ -218,7 +262,12 @@ private extension AmiiboLiveClient { // MARK: Functions - func map(_ wrapper: Components.Schemas.AmiiboWrapper) -> [Amiibo] { + /// Retrieves a list of amiibo items from a wrapper container. + /// - Parameter wrapper: A wrapper container that either has an object or a list of items. + /// - Returns: A list of amiibo items, sorted by identifiers. + func map( + _ wrapper: Components.Schemas.AmiiboWrapper + ) -> [Amiibo] { switch wrapper.amiibo { case let .Amiibo(object): return [.init(object)] @@ -230,6 +279,11 @@ private extension AmiiboLiveClient { } } + /// Retrieves a list of items that conforms to the `KeyNameModel` protocol from a wrapper container. + /// - Parameters: + /// - wrapper: A wrapper container that either has an object or a list of items. + /// - as: a model type that conforms to the `KeyNameModel` protocol. + /// - Returns: A list of items that conforms to the `KeyNameModel` protocol, sorted by keys. func map( _ wrapper: Components.Schemas.TupleWrapper, as: Model.Type diff --git a/Sources/Public/Clients/AmiiboMockClient.swift b/Sources/Public/Clients/AmiiboMockClient.swift index e3bb572..86394af 100644 --- a/Sources/Public/Clients/AmiiboMockClient.swift +++ b/Sources/Public/Clients/AmiiboMockClient.swift @@ -12,20 +12,43 @@ import Foundation +/// A type that implements a mock client, for testing purposes. public struct AmiiboMockClient { // MARK: Properties + /// A list of amiibo items to return, if any. private let amiibos: [Amiibo]? + + /// A list of amiibo series to return, if any. private let amiiboSeries: [AmiiboSeries]? + + /// A list of amiibo types to return, if any. private let amiiboTypes: [AmiiboType]? + + /// An error to throw, if any. private let error: AmiiboServiceError? + + /// A list of game characters to return, if any. private let gameCharacters: [GameCharacter]? + + /// A list of game series to return, if any. private let gameSeries: [GameSeries]? + + /// A last updated date to return, if any. private let lastUpdated: Date? - // MARK: Initialisers + // MARK: Initializers + /// Initializes this client. + /// - Parameters: + /// - amiibos: A list of amiibo items to return, if any. + /// - amiiboSeries: A list of amiibo series to return, if any. + /// - amiiboTypes: A list of amiibo types to return, if any. + /// - gameCharacters: A list of game characters to return, if any. + /// - gameSeries: A list of game series to return, if any. + /// - lastUpdated: A last updated date to return, if any. + /// - error: An error to throw, if any. public init( amiibos: [Amiibo]? = nil, amiiboSeries: [AmiiboSeries]? = nil, @@ -52,7 +75,7 @@ extension AmiiboMockClient: APIClient { // MARK: Functions - public func getAmiibos(by filter: AmiiboFilter) async throws -> [Amiibo] { + public func getAmiibos(by filter: AmiiboFilter) async throws(AmiiboServiceError) -> [Amiibo] { try throwErrorIfExists() guard let amiibos else { @@ -62,7 +85,7 @@ extension AmiiboMockClient: APIClient { return amiibos } - public func getAmiiboSeries(by filter: AmiiboSeriesFilter) async throws -> [AmiiboSeries] { + public func getAmiiboSeries(by filter: AmiiboSeriesFilter) async throws(AmiiboServiceError) -> [AmiiboSeries] { try throwErrorIfExists() guard let amiiboSeries else { @@ -72,7 +95,7 @@ extension AmiiboMockClient: APIClient { return amiiboSeries } - public func getAmiiboTypes(by filter: AmiiboTypeFilter) async throws -> [AmiiboType] { + public func getAmiiboTypes(by filter: AmiiboTypeFilter) async throws(AmiiboServiceError) -> [AmiiboType] { try throwErrorIfExists() guard let amiiboTypes else { @@ -82,7 +105,7 @@ extension AmiiboMockClient: APIClient { return amiiboTypes } - public func getGameCharacters(by filter: GameCharacterFilter) async throws -> [GameCharacter] { + public func getGameCharacters(by filter: GameCharacterFilter) async throws(AmiiboServiceError) -> [GameCharacter] { try throwErrorIfExists() guard let gameCharacters else { @@ -92,7 +115,7 @@ extension AmiiboMockClient: APIClient { return gameCharacters } - public func getGameSeries(by filter: GameSeriesFilter) async throws -> [GameSeries] { + public func getGameSeries(by filter: GameSeriesFilter) async throws(AmiiboServiceError) -> [GameSeries] { try throwErrorIfExists() guard let gameSeries else { @@ -102,7 +125,7 @@ extension AmiiboMockClient: APIClient { return gameSeries } - public func getLastUpdated() async throws -> Date { + public func getLastUpdated() async throws(AmiiboServiceError) -> Date { try throwErrorIfExists() guard let lastUpdated else { @@ -121,7 +144,9 @@ private extension AmiiboMockClient { // MARK: Functions - func throwErrorIfExists() throws { + /// Throws an error if it has been provided, + /// - Throws: An ``AmiiboServiceError`` error in case an error has been provided. + func throwErrorIfExists() throws(AmiiboServiceError) { if let error { throw error }