Tests updates and other fixes (#27)
This PR contains the work done to update the live test data to its latest version. Plus other improvements have been made: * conformed the `AmiiboServiceError` error to the `LocalizableError` protocol, * improved the `ISODateTimeTranscoder` transcoder to handle the decoding of both ISO dates and timestamps, * removed a force-unwrapped from the `AmiiboLiveClient` type. Reviewed-on: #27 Co-authored-by: Javier Cicchelli <javier@rock-n-code.com> Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
This commit was merged in pull request #27.
This commit is contained in:
@@ -18,17 +18,29 @@ extension DateFormatter {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// An ISO date formatter.
|
||||
///
|
||||
/// This formatter implements the `yyyy-MM-dd` date format.
|
||||
static let isoDate: DateFormatter = {
|
||||
let formatter = DateFormatter()
|
||||
|
||||
formatter.dateFormat = "yyyy-MM-dd"
|
||||
formatter.timeZone = .init(secondsFromGMT: 0)
|
||||
|
||||
return formatter
|
||||
}()
|
||||
|
||||
/// An ISO timestamp formatter.
|
||||
///
|
||||
/// This formatter implements the `yyyy-MM-dd'T'HH:mm:ss.SSSSSS` custom date format.
|
||||
/// Within the context of this library, this formatter is solely used to decode a date formatted as a timestamp that is returned by the ``AmiiboService/getLastUpdated()`` function.
|
||||
static var isoTimestamp: DateFormatter {
|
||||
static let isoTimestamp: DateFormatter = {
|
||||
let formatter = DateFormatter()
|
||||
|
||||
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS"
|
||||
formatter.timeZone = .init(secondsFromGMT: 0)
|
||||
|
||||
return formatter
|
||||
}
|
||||
}()
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
// ===----------------------------------------------------------------------===
|
||||
//
|
||||
// This source file is part of the Amiibo Service open source project
|
||||
//
|
||||
// Copyright (c) 2026 Röck+Cöde VoF. and the Amiibo Service project authors
|
||||
// Licensed under Apache license v2.0
|
||||
//
|
||||
// See LICENSE for license information
|
||||
// See CONTRIBUTORS for the list of Amiibo Service project authors
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// ===----------------------------------------------------------------------===
|
||||
|
||||
import Foundation
|
||||
import OpenAPIRuntime
|
||||
|
||||
/// A type that allows the decoding and encoding of ISO dates, supporting both the `yyyy-MM-dd'T'HH:mm:ss.SSSSSS` timestamp format and the `yyyy-MM-dd` date-only format.
|
||||
struct ISODateTimeTranscoder {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// A formatter to use to decode and encode ISO timestamp dates.
|
||||
private let timestampFormatter: DateFormatter = .isoTimestamp
|
||||
|
||||
/// A formatter to use to decode and encode ISO date-only strings.
|
||||
private let dateFormatter: DateFormatter = .isoDate
|
||||
|
||||
}
|
||||
|
||||
// MARK: - DateTranscoder
|
||||
|
||||
extension ISODateTimeTranscoder: DateTranscoder {
|
||||
|
||||
// MARK: Functions
|
||||
|
||||
/// Encodes a date into an ISO timestamp string.
|
||||
/// - Parameter date: A date to encode.
|
||||
/// - Returns: A string representation of the date in `yyyy-MM-dd'T'HH:mm:ss.SSSSSS` format.
|
||||
func encode(_ date: Date) throws -> String {
|
||||
timestampFormatter.string(from: date)
|
||||
}
|
||||
|
||||
/// Decodes an ISO date string into a date, trying the timestamp format first and falling back to the date-only format.
|
||||
/// - Parameter string: A string to decode.
|
||||
/// - Returns: A date parsed from the string.
|
||||
/// - Throws: A `DecodingError` if the string cannot be parsed into a valid date.
|
||||
func decode(_ string: String) throws -> Date {
|
||||
if let date = timestampFormatter.date(from: string) {
|
||||
return date
|
||||
}
|
||||
|
||||
if let date = dateFormatter.date(from: string) {
|
||||
return date
|
||||
}
|
||||
|
||||
throw DecodingError.dataCorrupted(.init(
|
||||
codingPath: [],
|
||||
debugDescription: "Expected an ISO date with format 'yyyy-MM-dd'T'HH:mm:ss.SSSSSS' or 'yyyy-MM-dd', but found '\(string)' instead."
|
||||
))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
// ===----------------------------------------------------------------------===
|
||||
//
|
||||
// This source file is part of the Amiibo Service open source project
|
||||
//
|
||||
// Copyright (c) 2026 Röck+Cöde VoF. and the Amiibo Service project authors
|
||||
// Licensed under Apache license v2.0
|
||||
//
|
||||
// See LICENSE for license information
|
||||
// See CONTRIBUTORS for the list of Amiibo Service project authors
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// ===----------------------------------------------------------------------===
|
||||
|
||||
import Foundation
|
||||
import OpenAPIRuntime
|
||||
|
||||
/// A type that allows the decoding and encoding of ISO timestamp dates, defined by the `yyyy-MM-dd'T'HH:mm:ss.SSSSSS` custom date format.
|
||||
struct ISOTimestampTranscoder {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// A formatter to use to decode and encode ISO timestamps dates.
|
||||
private let dateFormatter: DateFormatter = .isoTimestamp
|
||||
|
||||
}
|
||||
|
||||
// MARK: - DateTranscoder
|
||||
|
||||
extension ISOTimestampTranscoder: DateTranscoder {
|
||||
|
||||
// MARK: Functions
|
||||
|
||||
/// Encodes a date into an ISO timestamp string.
|
||||
/// - Parameter date: A date to encode.
|
||||
/// - Returns: A string representation of the date in `yyyy-MM-dd'T'HH:mm:ss.SSSSSS` format.
|
||||
func encode(_ date: Date) throws -> String {
|
||||
dateFormatter.string(from: date)
|
||||
}
|
||||
|
||||
/// Decodes an ISO timestamp string into a date.
|
||||
/// - Parameter string: A string to decode.
|
||||
/// - Returns: A date parsed from the string, or the Unix epoch if the string cannot be parsed.
|
||||
func decode(_ string: String) throws -> Date {
|
||||
dateFormatter.date(from: string) ?? .init()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,10 +29,13 @@ public struct AmiiboLiveClient: Sendable {
|
||||
/// Initializes this client with a transport for performing HTTP operations.
|
||||
/// - Parameter transport: A transport that performs HTTP operations. Defaults to a `URLSessionTransport` using the shared session.
|
||||
public init(transport: any ClientTransport = URLSessionTransport()) {
|
||||
guard let serverURL = try? Servers.Server1.url() else {
|
||||
fatalError("The server URL defined in the OpenAPI specification could not be resolved. Verify that the 'openapi.yaml' server definition is valid.")
|
||||
}
|
||||
|
||||
self.client = .init(
|
||||
// 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()),
|
||||
serverURL: serverURL,
|
||||
configuration: .init(dateTranscoder: ISODateTimeTranscoder()),
|
||||
transport: transport
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
// ===----------------------------------------------------------------------===
|
||||
//
|
||||
//
|
||||
// This source file is part of the Amiibo Service open source project
|
||||
//
|
||||
//
|
||||
// Copyright (c) 2026 Röck+Cöde VoF. and the Amiibo Service project authors
|
||||
// Licensed under Apache license v2.0
|
||||
//
|
||||
//
|
||||
// See LICENSE for license information
|
||||
// See CONTRIBUTORS for the list of Amiibo Service project authors
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
//
|
||||
// ===----------------------------------------------------------------------===
|
||||
|
||||
import Foundation
|
||||
|
||||
/// A representation of all the possible errors that the ``AmiiboService`` service could throw.
|
||||
public enum AmiiboServiceError: Error {
|
||||
/// The request was malformed or contained invalid filter parameters.
|
||||
@@ -33,3 +35,23 @@ public enum AmiiboServiceError: Error {
|
||||
// MARK: - Equatable
|
||||
|
||||
extension AmiiboServiceError: Equatable {}
|
||||
|
||||
// MARK: - LocalizedError
|
||||
|
||||
extension AmiiboServiceError: LocalizedError {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
public var errorDescription: String? {
|
||||
switch self {
|
||||
case .badRequest: "The request was malformed or contained invalid filter parameters."
|
||||
case .cancelled: "The request was cancelled before a response was received."
|
||||
case .decoding: "The response body could not be decoded into the expected model."
|
||||
case .notAvailable: "The backend service is currently unreachable due to a network or server issue."
|
||||
case .notFound: "No results were found matching the given filter criteria."
|
||||
case .undocumented(let statusCode): "The server returned an undocumented HTTP status code: \(statusCode)."
|
||||
case .unknown: "An unexpected error occurred."
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user