Several fixes and optimizations all over the library (#28)
This PR contains the work done to cleans up the library after the Swift 6.2 migration: drops the legacy conditional-compilation paths, hardens the OpenAPI specification, removes boilerplate from the live client, and makes the live test suite resilient to changes in the upstream service data.
* Swift 6.2 baseline
* Adopted swift-tools-version: 6.2, which allowed removing every #if swift(>=6.0) / #if swift(>=6.2) branch and natural-language test names are now the only code paths.
* `AmiiboClient` client now refines `Sendable`, and both `AmiiboLiveClient` and `AmiiboService` conform to it, so instances can be shared across concurrency domains.
* Live client
* Extracted a generic `perform(_:)` helper that wraps every generated API call and funnels transport errors through the existing error mapper — replaces six repeated do/catch blocks and lets the private fetch* methods use typed throws end-to-end.
* Added the internal `KeyNamePayload` protocol and a generic makeModels(from:), collapsing the four near-identical mapping/sorting blocks for amiibo series, types, game characters and game series into one.
* The server URL is now resolved once into a static let constant instead of on every initialization.
* Improved error mapping: URLError.cancelled → .cancelled, .cannotParseResponse → .decoding, and .dataNotAllowed, .internationalRoamingOff, .secureConnectionFailed now map to .notAvailable.
* Simplified the Amiibo.Platform initializer ([Amiibo+Platform.swift](vscode-webview://1m6rk4uhpaukb4hrbp3j54p77iq693fq2ql7o9oup2pca1omf7f6/Sources/AmiiboService/Public/Models/Amiibo/Amiibo+Platform.swift)) using optional chaining and ?? [] instead of nested guards and immediately-invoked closures.
* Set the en_US_POSIX locale on both fixed-format date formatters so the user's locale or 12/24-hour setting can no longer break parsing.
* OpenAPI specification
* Replaced the shared Tuple schema with explicit per-type schemas (AmiiboSeries, AmiiboType, GameCharacter, GameSeries), so the generated code exposes real properties instead of allOf wrappers with .value1 accessors.
* Extracted named list schemas (AmiiboList, AmiiboSeriesList, …) for the wrapper payloads, which turns the generated .case2 enum cases into readable .AmiiboSeriesList cases.
* Added validation patterns and length bounds to the id, head, tail and key query parameters, and collapsed the redundant pattern/minLength/maxLength triplets on head/tail into ^[0-9a-fA-F]{8}$.
* Extracted the shared InternalServerError response, replacing the five inline 500 descriptions.
* Documented the two non-standard date/time decoding behaviours (date-only strings for release dates, offset-less timestamps for lastUpdated) and why the optional amiibo wrapper property must stay optional.
* Corrected the info.version value from v1.0.0 to 1.0.0.
* ⚠️ Breaking changes
* Minimum Swift version raised from 5.10 to 6.2.
* `AmiiboServiceError.unknown` now carries a String description of the underlying error.
* `AmiiboClient` requires Sendable conformance, so existing custom mock clients must be Sendable.
Reviewed-on: #28
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 #28.
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
# ``AmiiboService/AmiiboService``
|
||||
|
||||
## Topics
|
||||
|
||||
### Initializers
|
||||
|
||||
- ``AmiiboService/AmiiboService/init(client:)``
|
||||
|
||||
### Amiibo endpoints
|
||||
|
||||
- ``AmiiboService/AmiiboService/getAmiibos(_:)``
|
||||
- ``AmiiboService/AmiiboService/getAmiiboSeries(_:)``
|
||||
- ``AmiiboService/AmiiboService/getAmiiboTypes(_:)``
|
||||
|
||||
### Game endpoints
|
||||
|
||||
- ``AmiiboService/AmiiboService/getGameCharacters(_:)``
|
||||
- ``AmiiboService/AmiiboService/getGameSeries(_:)``
|
||||
|
||||
### System endpoints
|
||||
|
||||
- ``AmiiboService/AmiiboService/getLastUpdated()``
|
||||
@@ -16,4 +16,4 @@
|
||||
|
||||
- ``AmiiboServiceError/notAvailable``
|
||||
- ``AmiiboServiceError/undocumented(_:)``
|
||||
- ``AmiiboServiceError/unknown``
|
||||
- ``AmiiboServiceError/unknown(_:)``
|
||||
|
||||
@@ -18,7 +18,7 @@ To use the `AmiiboService` library with your package, then add it as a dependenc
|
||||
let package = Package(
|
||||
// name, platforms, products, etc.
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/rock-n-code/amiibo-service", from: "1.4.1"),
|
||||
.package(url: "https://github.com/rock-n-code/amiibo-service", from: "2.0.0"),
|
||||
// other dependencies
|
||||
],
|
||||
targets: [
|
||||
@@ -35,7 +35,32 @@ let package = Package(
|
||||
|
||||
It is also possible to use the `AmiiboService` library with your app in Xcode, then add it as a dependency in your Xcode project.
|
||||
|
||||
> important: Swift 5.10 or higher is required in order to compile this library.
|
||||
> important: Swift 6.2 or higher is required in order to compile this library.
|
||||
|
||||
## Usage
|
||||
|
||||
Create an ``AmiiboService`` instance and call any of its endpoints. Each endpoint accepts an optional filter and, when omitted, returns the full set of results:
|
||||
|
||||
```swift
|
||||
import AmiiboService
|
||||
|
||||
let service = AmiiboService()
|
||||
|
||||
// Fetch all amiibos
|
||||
let amiibos = try await service.getAmiibos()
|
||||
|
||||
// Fetch amiibos filtered by name
|
||||
let zeldaAmiibos = try await service.getAmiibos(.init(name: "zelda"))
|
||||
|
||||
// Fetch amiibo series, types, game characters, and game series
|
||||
let series = try await service.getAmiiboSeries()
|
||||
let types = try await service.getAmiiboTypes()
|
||||
let characters = try await service.getGameCharacters()
|
||||
let gameSeries = try await service.getGameSeries()
|
||||
|
||||
// Fetch the last updated timestamp
|
||||
let lastUpdated = try await service.getLastUpdated()
|
||||
```
|
||||
|
||||
## Caching
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ extension DateFormatter {
|
||||
static let isoDate: DateFormatter = {
|
||||
let formatter = DateFormatter()
|
||||
|
||||
// A fixed-format date requires the POSIX locale, as the user's locale or 12/24-hour setting could otherwise alter the parsing.
|
||||
formatter.locale = .init(identifier: "en_US_POSIX")
|
||||
formatter.dateFormat = "yyyy-MM-dd"
|
||||
formatter.timeZone = .init(secondsFromGMT: 0)
|
||||
|
||||
@@ -37,6 +39,8 @@ extension DateFormatter {
|
||||
static let isoTimestamp: DateFormatter = {
|
||||
let formatter = DateFormatter()
|
||||
|
||||
// A fixed-format date requires the POSIX locale, as the user's locale or 12/24-hour setting could otherwise alter the parsing.
|
||||
formatter.locale = .init(identifier: "en_US_POSIX")
|
||||
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS"
|
||||
formatter.timeZone = .init(secondsFromGMT: 0)
|
||||
|
||||
|
||||
@@ -27,6 +27,6 @@ protocol KeyNameModel: Sendable, Hashable {
|
||||
|
||||
/// Initializes this model from a given payload.
|
||||
/// - Parameter payload: A payload that contains the values for the model.
|
||||
init(_ payload: Components.Schemas.Tuple)
|
||||
init(_ payload: some KeyNamePayload)
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// ===----------------------------------------------------------------------===
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// ===----------------------------------------------------------------------===
|
||||
|
||||
/// A protocol that unifies the generated payload types containing a `key` and `name` pair.
|
||||
protocol KeyNamePayload {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// A hexadecimal key that uniquely identifies this payload.
|
||||
var key: String { get }
|
||||
|
||||
/// A display name for this payload.
|
||||
var name: String { get }
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Conformances
|
||||
|
||||
extension Components.Schemas.AmiiboSeries: KeyNamePayload {}
|
||||
extension Components.Schemas.AmiiboType: KeyNamePayload {}
|
||||
extension Components.Schemas.GameCharacter: KeyNamePayload {}
|
||||
extension Components.Schemas.GameSeries: KeyNamePayload {}
|
||||
@@ -17,8 +17,23 @@ import OpenAPIRuntime
|
||||
import OpenAPIURLSession
|
||||
|
||||
/// A type that implements a live client to the [Amiibo API](https://www.amiiboapi.org) online service.
|
||||
///
|
||||
/// This client maps any transport error or unsuccessful HTTP response to an ``AmiiboServiceError`` error, and sorts the items of the list responses in ascending order by identifier or key.
|
||||
public struct AmiiboLiveClient: Sendable {
|
||||
|
||||
// MARK: Constants
|
||||
|
||||
/// The base URL of the live service, resolved once from the server defined in the OpenAPI specification.
|
||||
///
|
||||
/// The validity of the URL is guaranteed by the bundled `openapi.yaml` specification and enforced by a unit test, so resolution is not expected to fail at runtime.
|
||||
static let serverURL: URL = {
|
||||
guard let url = 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.")
|
||||
}
|
||||
|
||||
return url
|
||||
}()
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// A client generated by the OpenAPI Runtime library to perform API calls.
|
||||
@@ -29,12 +44,8 @@ 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(
|
||||
serverURL: serverURL,
|
||||
serverURL: Self.serverURL,
|
||||
configuration: .init(dateTranscoder: ISODateTimeTranscoder()),
|
||||
transport: transport
|
||||
)
|
||||
@@ -49,7 +60,6 @@ extension AmiiboLiveClient: AmiiboClient {
|
||||
|
||||
// MARK: Functions
|
||||
|
||||
#if swift(>=6.0)
|
||||
/// Gets a list of amiibo items based on a given filter.
|
||||
/// - Parameter filter: A filter to remove unwanted items from the result.
|
||||
/// - Returns: A list of filtered amiibo items.
|
||||
@@ -101,69 +111,11 @@ extension AmiiboLiveClient: AmiiboClient {
|
||||
}
|
||||
|
||||
/// Gets the date when the data was last updated.
|
||||
/// - Returns: A last updated date.
|
||||
/// - Returns: A last updated date, decoded as UTC.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
public func getLastUpdated() async throws(AmiiboServiceError) -> Date {
|
||||
try await fetchLastUpdated()
|
||||
}
|
||||
#else
|
||||
/// Gets a list of amiibo items based on a given filter.
|
||||
/// - Parameter filter: A filter to remove unwanted items from the result.
|
||||
/// - Returns: A list of filtered amiibo items.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
public func getAmiibos(
|
||||
by filter: AmiiboFilter
|
||||
) async throws -> [Amiibo] {
|
||||
try await fetchAmiibos(filter)
|
||||
}
|
||||
|
||||
/// Gets a list of amiibo series based on a given filter.
|
||||
/// - Parameter filter: A filter to remove unwanted items from the result.
|
||||
/// - Returns: A list of filtered amiibo series.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
public func getAmiiboSeries(
|
||||
by filter: AmiiboSeriesFilter
|
||||
) async throws -> [AmiiboSeries] {
|
||||
try await fetchAmiiboSeries(filter)
|
||||
}
|
||||
|
||||
/// Gets a list of amiibo types based on a given filter.
|
||||
/// - Parameter filter: A filter to remove unwanted items from the result.
|
||||
/// - Returns: A list of filtered amiibo types.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
public func getAmiiboTypes(
|
||||
by filter: AmiiboTypeFilter
|
||||
) async throws -> [AmiiboType] {
|
||||
try await fetchAmiiboTypes(filter)
|
||||
}
|
||||
|
||||
/// Gets a list of game characters based on a given filter.
|
||||
/// - Parameter filter: A filter to remove unwanted items from the result.
|
||||
/// - Returns: A list of filtered game characters.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
public func getGameCharacters(
|
||||
by filter: GameCharacterFilter
|
||||
) async throws -> [GameCharacter] {
|
||||
try await fetchGameCharacters(filter)
|
||||
}
|
||||
|
||||
/// Gets a list of game series based on a given filter.
|
||||
/// - Parameter filter: A filter to remove unwanted items from the result.
|
||||
/// - Returns: A list of filtered game series.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
public func getGameSeries(
|
||||
by filter: GameSeriesFilter
|
||||
) async throws -> [GameSeries] {
|
||||
try await fetchGameSeries(filter)
|
||||
}
|
||||
|
||||
/// Gets the date when the data was last updated.
|
||||
/// - Returns: A last updated date.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
public func getLastUpdated() async throws -> Date {
|
||||
try await fetchLastUpdated()
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -179,11 +131,9 @@ private extension AmiiboLiveClient {
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
func fetchAmiibos(
|
||||
_ filter: AmiiboFilter
|
||||
) async throws -> [Amiibo] {
|
||||
let response: Operations.getAmiibos.Output
|
||||
|
||||
do {
|
||||
response = try await client.getAmiibos(.init(query: .init(
|
||||
) async throws(AmiiboServiceError) -> [Amiibo] {
|
||||
let response = try await perform {
|
||||
try await client.getAmiibos(.init(query: .init(
|
||||
id: filter.identifier,
|
||||
head: filter.head,
|
||||
tail: filter.tail,
|
||||
@@ -195,8 +145,6 @@ private extension AmiiboLiveClient {
|
||||
showgames: filter.showGames,
|
||||
showusage: filter.showUsage
|
||||
)))
|
||||
} catch {
|
||||
try handle(error: error)
|
||||
}
|
||||
|
||||
switch response {
|
||||
@@ -206,11 +154,12 @@ private extension AmiiboLiveClient {
|
||||
switch output.amiibo {
|
||||
case let .Amiibo(object):
|
||||
return [Amiibo(object)]
|
||||
case let .case2(list):
|
||||
case let .AmiiboList(list):
|
||||
return list
|
||||
.map { Amiibo($0) }
|
||||
.sorted { $0.identifier < $1.identifier }
|
||||
case .none:
|
||||
// The service returns `"amiibo": null` when an `id` filter matches nothing.
|
||||
return []
|
||||
}
|
||||
}
|
||||
@@ -231,16 +180,12 @@ private extension AmiiboLiveClient {
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
func fetchAmiiboSeries(
|
||||
_ filter: AmiiboSeriesFilter
|
||||
) async throws -> [AmiiboSeries] {
|
||||
let response: Operations.getAmiiboSeries.Output
|
||||
|
||||
do {
|
||||
response = try await client.getAmiiboSeries(.init(query: .init(
|
||||
) async throws(AmiiboServiceError) -> [AmiiboSeries] {
|
||||
let response = try await perform {
|
||||
try await client.getAmiiboSeries(.init(query: .init(
|
||||
key: filter.key,
|
||||
name: filter.name
|
||||
)))
|
||||
} catch {
|
||||
try handle(error: error)
|
||||
}
|
||||
|
||||
switch response {
|
||||
@@ -249,11 +194,9 @@ private extension AmiiboLiveClient {
|
||||
case let .json(output):
|
||||
switch output.amiibo {
|
||||
case let .AmiiboSeries(payload):
|
||||
return [AmiiboSeries(payload.value1)]
|
||||
case let .case2(list):
|
||||
return list
|
||||
.map { AmiiboSeries($0.value1) }
|
||||
.sorted { $0.key < $1.key }
|
||||
return makeModels(from: [payload])
|
||||
case let .AmiiboSeriesList(list):
|
||||
return makeModels(from: list)
|
||||
}
|
||||
}
|
||||
case .badRequest:
|
||||
@@ -273,16 +216,12 @@ private extension AmiiboLiveClient {
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
func fetchAmiiboTypes(
|
||||
_ filter: AmiiboTypeFilter
|
||||
) async throws -> [AmiiboType] {
|
||||
let response: Operations.getAmiiboTypes.Output
|
||||
|
||||
do {
|
||||
response = try await client.getAmiiboTypes(.init(query: .init(
|
||||
) async throws(AmiiboServiceError) -> [AmiiboType] {
|
||||
let response = try await perform {
|
||||
try await client.getAmiiboTypes(.init(query: .init(
|
||||
key: filter.key,
|
||||
name: filter.name
|
||||
)))
|
||||
} catch {
|
||||
try handle(error: error)
|
||||
}
|
||||
|
||||
switch response {
|
||||
@@ -291,11 +230,9 @@ private extension AmiiboLiveClient {
|
||||
case let .json(output):
|
||||
switch output.amiibo {
|
||||
case let .AmiiboType(payload):
|
||||
return [AmiiboType(payload.value1)]
|
||||
case let .case2(list):
|
||||
return list
|
||||
.map { AmiiboType($0.value1) }
|
||||
.sorted { $0.key < $1.key }
|
||||
return makeModels(from: [payload])
|
||||
case let .AmiiboTypeList(list):
|
||||
return makeModels(from: list)
|
||||
}
|
||||
}
|
||||
case .badRequest:
|
||||
@@ -315,16 +252,12 @@ private extension AmiiboLiveClient {
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
func fetchGameCharacters(
|
||||
_ filter: GameCharacterFilter
|
||||
) async throws -> [GameCharacter] {
|
||||
let response: Operations.getGameCharacters.Output
|
||||
|
||||
do {
|
||||
response = try await client.getGameCharacters(.init(query: .init(
|
||||
) async throws(AmiiboServiceError) -> [GameCharacter] {
|
||||
let response = try await perform {
|
||||
try await client.getGameCharacters(.init(query: .init(
|
||||
key: filter.key,
|
||||
name: filter.name
|
||||
)))
|
||||
} catch {
|
||||
try handle(error: error)
|
||||
}
|
||||
|
||||
switch response {
|
||||
@@ -333,11 +266,9 @@ private extension AmiiboLiveClient {
|
||||
case let .json(output):
|
||||
switch output.amiibo {
|
||||
case let .GameCharacter(payload):
|
||||
return [GameCharacter(payload.value1)]
|
||||
case let .case2(list):
|
||||
return list
|
||||
.map { GameCharacter($0.value1) }
|
||||
.sorted { $0.key < $1.key }
|
||||
return makeModels(from: [payload])
|
||||
case let .GameCharacterList(list):
|
||||
return makeModels(from: list)
|
||||
}
|
||||
}
|
||||
case .badRequest:
|
||||
@@ -357,16 +288,12 @@ private extension AmiiboLiveClient {
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
func fetchGameSeries(
|
||||
_ filter: GameSeriesFilter
|
||||
) async throws -> [GameSeries] {
|
||||
let response: Operations.getGameSeries.Output
|
||||
|
||||
do {
|
||||
response = try await client.getGameSeries(.init(query: .init(
|
||||
) async throws(AmiiboServiceError) -> [GameSeries] {
|
||||
let response = try await perform {
|
||||
try await client.getGameSeries(.init(query: .init(
|
||||
key: filter.key,
|
||||
name: filter.name
|
||||
)))
|
||||
} catch {
|
||||
try handle(error: error)
|
||||
}
|
||||
|
||||
switch response {
|
||||
@@ -375,11 +302,9 @@ private extension AmiiboLiveClient {
|
||||
case let .json(output):
|
||||
switch output.amiibo {
|
||||
case let .GameSeries(payload):
|
||||
return [GameSeries(payload.value1)]
|
||||
case let .case2(list):
|
||||
return list
|
||||
.map { GameSeries($0.value1) }
|
||||
.sorted { $0.key < $1.key }
|
||||
return makeModels(from: [payload])
|
||||
case let .GameSeriesList(list):
|
||||
return makeModels(from: list)
|
||||
}
|
||||
}
|
||||
case .badRequest:
|
||||
@@ -394,15 +319,11 @@ private extension AmiiboLiveClient {
|
||||
}
|
||||
|
||||
/// Fetches the date when the data was last updated.
|
||||
/// - Returns: A fetched last updated date.
|
||||
/// - Returns: A fetched last updated date, decoded as UTC.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
func fetchLastUpdated() async throws -> Date {
|
||||
let response: Operations.getLastUpdated.Output
|
||||
|
||||
do {
|
||||
response = try await client.getLastUpdated()
|
||||
} catch {
|
||||
try handle(error: error)
|
||||
func fetchLastUpdated() async throws(AmiiboServiceError) -> Date {
|
||||
let response = try await perform {
|
||||
try await client.getLastUpdated()
|
||||
}
|
||||
|
||||
switch response {
|
||||
@@ -418,10 +339,33 @@ private extension AmiiboLiveClient {
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs an API call, mapping any error thrown by the underlying client to an ``AmiiboServiceError`` error.
|
||||
/// - Parameter operation: A closure that performs the API call.
|
||||
/// - Returns: The output of the performed API call.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case the API call failed.
|
||||
func perform<Output>(
|
||||
_ operation: () async throws -> Output
|
||||
) async throws(AmiiboServiceError) -> Output {
|
||||
do {
|
||||
return try await operation()
|
||||
} catch {
|
||||
try handle(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps a list of key-name payloads into a sorted list of models.
|
||||
/// - Parameter payloads: A list of payloads to map into models.
|
||||
/// - Returns: A list of models sorted by their keys in ascending order.
|
||||
func makeModels<Model: KeyNameModel>(from payloads: [some KeyNamePayload]) -> [Model] {
|
||||
payloads
|
||||
.map { Model($0) }
|
||||
.sorted { $0.key < $1.key }
|
||||
}
|
||||
|
||||
/// Maps a given error to an ``AmiiboServiceError`` error.
|
||||
/// - Parameter error: An error to map.
|
||||
/// - Throws: An ``AmiiboServiceError`` error that corresponds to the given error.
|
||||
func handle(error: any Error) throws -> Never {
|
||||
func handle(error: any Error) throws(AmiiboServiceError) -> Never {
|
||||
switch error {
|
||||
case is CancellationError:
|
||||
throw AmiiboServiceError.cancelled
|
||||
@@ -431,21 +375,28 @@ private extension AmiiboLiveClient {
|
||||
throw AmiiboServiceError.decoding
|
||||
case let urlError as URLError:
|
||||
switch urlError.code {
|
||||
case .cancelled:
|
||||
throw AmiiboServiceError.cancelled
|
||||
case .cannotParseResponse:
|
||||
throw AmiiboServiceError.decoding
|
||||
case .cannotFindHost,
|
||||
.cannotConnectToHost,
|
||||
.dataNotAllowed,
|
||||
.dnsLookupFailed,
|
||||
.internationalRoamingOff,
|
||||
.networkConnectionLost,
|
||||
.notConnectedToInternet,
|
||||
.secureConnectionFailed,
|
||||
.timedOut:
|
||||
throw AmiiboServiceError.notAvailable
|
||||
default:
|
||||
throw AmiiboServiceError.unknown
|
||||
throw AmiiboServiceError.unknown(String(describing: urlError))
|
||||
}
|
||||
default:
|
||||
throw AmiiboServiceError.unknown
|
||||
throw AmiiboServiceError.unknown(String(describing: clientError.underlyingError))
|
||||
}
|
||||
default:
|
||||
throw AmiiboServiceError.unknown
|
||||
throw AmiiboServiceError.unknown(String(describing: error))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ public enum AmiiboServiceError: Error {
|
||||
case notFound
|
||||
/// The server returned an undocumented HTTP status code.
|
||||
case undocumented(_ statusCode: Int)
|
||||
/// An unexpected error that does not fall into any other category.
|
||||
case unknown
|
||||
/// An unexpected error that does not fall into any other category, with a description of the underlying error.
|
||||
case unknown(_ description: String)
|
||||
}
|
||||
|
||||
// MARK: - Equatable
|
||||
@@ -50,7 +50,7 @@ extension AmiiboServiceError: LocalizedError {
|
||||
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."
|
||||
case .unknown(let description): "An unexpected error occurred: \(description)"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,30 +47,18 @@ extension Amiibo {
|
||||
_ threeDS: [Components.Schemas.AmiiboGame]?,
|
||||
_ wiiU: [Components.Schemas.AmiiboGame]?
|
||||
) {
|
||||
guard (`switch` != nil && `switch`?.isEmpty == false)
|
||||
|| (switch2 != nil && switch2?.isEmpty == false)
|
||||
|| (threeDS != nil && threeDS?.isEmpty == false)
|
||||
|| (wiiU != nil && wiiU?.isEmpty == false)
|
||||
guard `switch`?.isEmpty == false
|
||||
|| switch2?.isEmpty == false
|
||||
|| threeDS?.isEmpty == false
|
||||
|| wiiU?.isEmpty == false
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.switch = {
|
||||
guard let `switch` else { return [] }
|
||||
return `switch`.map { .init($0) }
|
||||
}()
|
||||
self.switch2 = {
|
||||
guard let switch2 else { return [] }
|
||||
return switch2.map { .init($0) }
|
||||
}()
|
||||
self.threeDS = {
|
||||
guard let threeDS else { return [] }
|
||||
return threeDS.map { .init($0) }
|
||||
}()
|
||||
self.wiiU = {
|
||||
guard let wiiU else { return [] }
|
||||
return wiiU.map { .init($0) }
|
||||
}()
|
||||
self.switch = `switch`?.map { .init($0) } ?? []
|
||||
self.switch2 = switch2?.map { .init($0) } ?? []
|
||||
self.threeDS = threeDS?.map { .init($0) } ?? []
|
||||
self.wiiU = wiiU?.map { .init($0) } ?? []
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ import Foundation
|
||||
|
||||
extension Amiibo {
|
||||
/// A model that represents the regional release dates of an amiibo.
|
||||
///
|
||||
/// The service provides these dates as date-only values, which are decoded as midnight UTC.
|
||||
public struct Release: Sendable, Hashable {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
@@ -27,7 +27,7 @@ public struct AmiiboSeries: KeyNameModel {
|
||||
|
||||
/// Initializes this model from a given payload.
|
||||
/// - Parameter payload: A payload that contains the values for the model.
|
||||
init(_ payload: Components.Schemas.Tuple) {
|
||||
init(_ payload: some KeyNamePayload) {
|
||||
self.key = payload.key
|
||||
self.name = payload.name
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public struct AmiiboType: KeyNameModel {
|
||||
|
||||
/// Initializes this model from a given payload.
|
||||
/// - Parameter payload: A payload that contains the values for the model.
|
||||
init(_ payload: Components.Schemas.Tuple) {
|
||||
init(_ payload: some KeyNamePayload) {
|
||||
self.key = payload.key
|
||||
self.name = payload.name
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public struct GameCharacter: KeyNameModel {
|
||||
|
||||
/// Initializes this model from a given payload.
|
||||
/// - Parameter payload: A payload that contains the values for the model.
|
||||
init(_ payload: Components.Schemas.Tuple) {
|
||||
init(_ payload: some KeyNamePayload) {
|
||||
self.key = payload.key
|
||||
self.name = payload.name
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public struct GameSeries: KeyNameModel {
|
||||
|
||||
/// Initializes this model from a given payload.
|
||||
/// - Parameter payload: A payload that contains the values for the model.
|
||||
init(_ payload: Components.Schemas.Tuple) {
|
||||
init(_ payload: some KeyNamePayload) {
|
||||
self.key = payload.key
|
||||
self.name = payload.name
|
||||
}
|
||||
|
||||
@@ -15,11 +15,12 @@
|
||||
import Foundation
|
||||
|
||||
/// A protocol that defines API clients containing all available endpoints to interact with.
|
||||
public protocol AmiiboClient {
|
||||
///
|
||||
/// Conforming types must be `Sendable`, as clients are expected to be used safely across concurrency domains.
|
||||
public protocol AmiiboClient: Sendable {
|
||||
|
||||
// MARK: Functions
|
||||
|
||||
#if swift(>=6.0)
|
||||
/// Gets a list of amiibo items based on a given filter.
|
||||
/// - Parameter filter: A filter to remove unwanted items from the result.
|
||||
/// - Returns: A list of filtered amiibo items.
|
||||
@@ -51,44 +52,8 @@ public protocol AmiiboClient {
|
||||
func getGameSeries(by filter: GameSeriesFilter) async throws(AmiiboServiceError) -> [GameSeries]
|
||||
|
||||
/// Gets the date when the data was last updated.
|
||||
/// - Returns: A last updated date.
|
||||
/// - Returns: A last updated date, decoded as UTC.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
func getLastUpdated() async throws(AmiiboServiceError) -> Date
|
||||
#else
|
||||
/// Gets a list of amiibo items based on a given filter.
|
||||
/// - Parameter filter: A filter to remove unwanted items from the result.
|
||||
/// - Returns: A list of filtered amiibo items.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
func getAmiibos(by filter: AmiiboFilter) async throws -> [Amiibo]
|
||||
|
||||
/// Gets a list of amiibo series based on a given filter.
|
||||
/// - Parameter filter: A filter to remove unwanted items from the result.
|
||||
/// - Returns: A list of filtered amiibo series.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
func getAmiiboSeries(by filter: AmiiboSeriesFilter) async throws -> [AmiiboSeries]
|
||||
|
||||
/// Gets a list of amiibo types based on a given filter.
|
||||
/// - Parameter filter: A filter to remove unwanted items from the result.
|
||||
/// - Returns: A list of filtered amiibo types.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
func getAmiiboTypes(by filter: AmiiboTypeFilter) async throws -> [AmiiboType]
|
||||
|
||||
/// Gets a list of game characters based on a given filter.
|
||||
/// - Parameter filter: A filter to remove unwanted items from the result.
|
||||
/// - Returns: A list of filtered game characters.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
func getGameCharacters(by filter: GameCharacterFilter) async throws -> [GameCharacter]
|
||||
|
||||
/// Gets a list of game series based on a given filter.
|
||||
/// - Parameter filter: A filter to remove unwanted items from the result.
|
||||
/// - Returns: A list of filtered game series.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
func getGameSeries(by filter: GameSeriesFilter) async throws -> [GameSeries]
|
||||
|
||||
/// Gets the date when the data was last updated.
|
||||
/// - Returns: A last updated date.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
func getLastUpdated() async throws -> Date
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
import Foundation
|
||||
|
||||
/// A type that implements the service that uses a client to make calls.
|
||||
public struct AmiiboService {
|
||||
///
|
||||
/// This service forwards every call to the ``AmiiboClient`` client injected during initialization, which defaults to an ``AmiiboLiveClient`` instance. This type is `Sendable`, so an instance can be safely shared across concurrency domains.
|
||||
public struct AmiiboService: Sendable {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
@@ -32,7 +34,6 @@ public struct AmiiboService {
|
||||
|
||||
// MARK: Functions
|
||||
|
||||
#if swift(>=6.0)
|
||||
/// Gets a list of amiibo items based on a given filter.
|
||||
/// - Parameter filter: A filter to remove unwanted items from the result.
|
||||
/// - Returns: A list of filtered amiibo items.
|
||||
@@ -84,68 +85,10 @@ public struct AmiiboService {
|
||||
}
|
||||
|
||||
/// Gets the date when the data was last updated.
|
||||
/// - Returns: A last updated date.
|
||||
/// - Returns: A last updated date, decoded as UTC.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
public func getLastUpdated() async throws(AmiiboServiceError) -> Date {
|
||||
try await client.getLastUpdated()
|
||||
}
|
||||
#else
|
||||
/// Gets a list of amiibo items based on a given filter.
|
||||
/// - Parameter filter: A filter to remove unwanted items from the result.
|
||||
/// - Returns: A list of filtered amiibo items.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
public func getAmiibos(
|
||||
_ filter: AmiiboFilter = .init()
|
||||
) async throws -> [Amiibo] {
|
||||
try await client.getAmiibos(by: filter)
|
||||
}
|
||||
|
||||
/// Gets a list of amiibo series based on a given filter.
|
||||
/// - Parameter filter: A filter to remove unwanted items from the result.
|
||||
/// - Returns: A list of filtered amiibo series.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
public func getAmiiboSeries(
|
||||
_ filter: AmiiboSeriesFilter = .init()
|
||||
) async throws -> [AmiiboSeries] {
|
||||
try await client.getAmiiboSeries(by: filter)
|
||||
}
|
||||
|
||||
/// Gets a list of amiibo types based on a given filter.
|
||||
/// - Parameter filter: A filter to remove unwanted items from the result.
|
||||
/// - Returns: A list of filtered amiibo types.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
public func getAmiiboTypes(
|
||||
_ filter: AmiiboTypeFilter = .init()
|
||||
) async throws -> [AmiiboType] {
|
||||
try await client.getAmiiboTypes(by: filter)
|
||||
}
|
||||
|
||||
/// Gets a list of game characters based on a given filter.
|
||||
/// - Parameter filter: A filter to remove unwanted items from the result.
|
||||
/// - Returns: A list of filtered game characters.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
public func getGameCharacters(
|
||||
_ filter: GameCharacterFilter = .init()
|
||||
) async throws -> [GameCharacter] {
|
||||
try await client.getGameCharacters(by: filter)
|
||||
}
|
||||
|
||||
/// Gets a list of game series based on a given filter.
|
||||
/// - Parameter filter: A filter to remove unwanted items from the result.
|
||||
/// - Returns: A list of filtered game series.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
public func getGameSeries(
|
||||
_ filter: GameSeriesFilter = .init()
|
||||
) async throws -> [GameSeries] {
|
||||
try await client.getGameSeries(by: filter)
|
||||
}
|
||||
|
||||
/// Gets the date when the data was last updated.
|
||||
/// - Returns: A last updated date.
|
||||
/// - Throws: An ``AmiiboServiceError`` error in case some issue is encountered while generating the result.
|
||||
public func getLastUpdated() async throws -> Date {
|
||||
try await client.getLastUpdated()
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ info:
|
||||
* *You will require your end users to comply with (and not knowingly enable them to violate) applicable law, regulation, and the Terms.*
|
||||
* *You will comply with all applicable law, regulation, and third party rights (including without limitation laws regarding the import or export of data or software, privacy, and local laws). You will not use the APIs to encourage or promote illegal activity or violation of third party rights.*
|
||||
* *These Terms and Conditions are subject to change without notice, from time to time in our sole discretion.*
|
||||
version: v1.0.0
|
||||
version: 1.0.0
|
||||
termsOfService: https://www.amiiboapi.org/docs/#termscondition
|
||||
contact:
|
||||
name: FAQ
|
||||
@@ -91,7 +91,7 @@ paths:
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'500':
|
||||
description: The service is currently unavailable.
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
/amiiboseries:
|
||||
get:
|
||||
tags:
|
||||
@@ -119,7 +119,7 @@ paths:
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'500':
|
||||
description: The service is currently unavailable.
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
/character:
|
||||
get:
|
||||
tags:
|
||||
@@ -147,7 +147,7 @@ paths:
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'500':
|
||||
description: The service is currently unavailable.
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
/gameseries:
|
||||
get:
|
||||
tags:
|
||||
@@ -175,7 +175,7 @@ paths:
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'500':
|
||||
description: The service is currently unavailable.
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
/type:
|
||||
get:
|
||||
tags:
|
||||
@@ -203,7 +203,7 @@ paths:
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'500':
|
||||
description: The service is currently unavailable.
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
/lastupdated:
|
||||
get:
|
||||
tags:
|
||||
@@ -222,7 +222,7 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/LastUpdated'
|
||||
'500':
|
||||
description: The service is currently unavailable.
|
||||
$ref: '#/components/responses/InternalServerError'
|
||||
components:
|
||||
parameters:
|
||||
AmiiboSeries:
|
||||
@@ -254,36 +254,44 @@ components:
|
||||
schema:
|
||||
type: string
|
||||
Identifier:
|
||||
description: The full 16-character hexadecimal identifier of an amiibo to include in the response.
|
||||
description: The full 16-character hexadecimal identifier of an amiibo to include in the response, with an optional `0x` prefix. An empty value is rejected with a `400` response.
|
||||
name: id
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
pattern: "^(0x)?[0-9a-fA-F]{16}$"
|
||||
minLength: 16
|
||||
maxLength: 18
|
||||
IdentifierHead:
|
||||
description: The first 8 hexadecimal characters of an amiibo identifier to include in the response.
|
||||
description: The first 8 hexadecimal characters of an amiibo identifier to include in the response, with an optional `0x` prefix. A shorter value matches as a prefix, while an empty value is rejected with a `400` response.
|
||||
name: head
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
pattern: "^(0x)?[0-9a-fA-F]+$"
|
||||
minLength: 1
|
||||
maxLength: 10
|
||||
IdentifierTail:
|
||||
description: The last 8 hexadecimal characters of an amiibo identifier to include in the response.
|
||||
description: The last 8 hexadecimal characters of an amiibo identifier to include in the response, with an optional `0x` prefix. A shorter value matches as a prefix, while an empty value is rejected with a `400` response.
|
||||
name: tail
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
pattern: "^(0x)?[0-9a-fA-F]+$"
|
||||
minLength: 1
|
||||
maxLength: 10
|
||||
Key:
|
||||
description: A hexadecimal key to filter the results by.
|
||||
description: A hexadecimal key to filter the results by, in the `0x`-prefixed format used by the `key` property of the resources. A value without hexadecimal digits after the prefix, or an empty value, is rejected with a `400` response.
|
||||
name: key
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
pattern: "^0x[0-9a-fA-F]+$"
|
||||
minLength: 3
|
||||
Name:
|
||||
description: A name to filter the results by.
|
||||
name: name
|
||||
@@ -312,6 +320,8 @@ components:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ServiceError'
|
||||
InternalServerError:
|
||||
description: The service is currently unavailable.
|
||||
NotFound:
|
||||
description: No results were found matching the given filter criteria.
|
||||
content:
|
||||
@@ -362,9 +372,7 @@ components:
|
||||
|
||||
The positions 0 to 7 of the hexadecimal string.
|
||||
type: string
|
||||
pattern: "^[0-9a-fA-F]+$"
|
||||
minLength: 8
|
||||
maxLength: 8
|
||||
pattern: "^[0-9a-fA-F]{8}$"
|
||||
image:
|
||||
description: A URL pointing to an image of this amiibo.
|
||||
type: string
|
||||
@@ -381,9 +389,7 @@ components:
|
||||
|
||||
The positions 8 to 15 of the hexadecimal string.
|
||||
type: string
|
||||
pattern: "^[0-9a-fA-F]+$"
|
||||
minLength: 8
|
||||
maxLength: 8
|
||||
pattern: "^[0-9a-fA-F]{8}$"
|
||||
type:
|
||||
description: The type of this amiibo (e.g., Figure, Card, Yarn, Band).
|
||||
type: string
|
||||
@@ -418,7 +424,10 @@ components:
|
||||
- gameID
|
||||
- gameName
|
||||
AmiiboRelease:
|
||||
description: A type that contains the regional release dates of an amiibo.
|
||||
description: |
|
||||
A type that contains the regional release dates of an amiibo.
|
||||
|
||||
Note: The service returns these dates as date-only strings (`yyyy-MM-dd`) without a time component, even though the properties declare the `date-time` format. The `date-time` format is kept deliberately so the generated code produces `Date` values, and the date-only strings are decoded by the custom transcoder configured in the live client, which interprets them as midnight UTC.
|
||||
type: object
|
||||
properties:
|
||||
au:
|
||||
@@ -439,24 +448,34 @@ components:
|
||||
format: date-time
|
||||
AmiiboSeries:
|
||||
description: A type that represents an amiibo series.
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Tuple'
|
||||
- type: object
|
||||
properties:
|
||||
key:
|
||||
description: The hexadecimal key that uniquely identifies this amiibo series.
|
||||
name:
|
||||
description: The name of this amiibo series.
|
||||
type: object
|
||||
properties:
|
||||
key:
|
||||
description: The hexadecimal key that uniquely identifies this amiibo series.
|
||||
type: string
|
||||
pattern: "^0x[0-9a-fA-F]+$"
|
||||
minLength: 3
|
||||
name:
|
||||
description: The name of this amiibo series.
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- name
|
||||
AmiiboType:
|
||||
description: A type that represents an amiibo type (e.g., Figure, Card, Yarn, Band).
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Tuple'
|
||||
- type: object
|
||||
properties:
|
||||
key:
|
||||
description: The hexadecimal key that uniquely identifies this amiibo type.
|
||||
name:
|
||||
description: The name of this amiibo type.
|
||||
type: object
|
||||
properties:
|
||||
key:
|
||||
description: The hexadecimal key that uniquely identifies this amiibo type.
|
||||
type: string
|
||||
pattern: "^0x[0-9a-fA-F]+$"
|
||||
minLength: 3
|
||||
name:
|
||||
description: The name of this amiibo type.
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- name
|
||||
AmiiboUsage:
|
||||
description: A type that represents how an amiibo is used within a game.
|
||||
type: object
|
||||
@@ -472,26 +491,39 @@ components:
|
||||
- write
|
||||
GameCharacter:
|
||||
description: A type that represents a game character.
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Tuple'
|
||||
- type: object
|
||||
properties:
|
||||
key:
|
||||
description: The hexadecimal key that uniquely identifies this game character.
|
||||
name:
|
||||
description: The name of this game character.
|
||||
type: object
|
||||
properties:
|
||||
key:
|
||||
description: The hexadecimal key that uniquely identifies this game character.
|
||||
type: string
|
||||
pattern: "^0x[0-9a-fA-F]+$"
|
||||
minLength: 3
|
||||
name:
|
||||
description: The name of this game character.
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- name
|
||||
GameSeries:
|
||||
description: A type that represents a game series.
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Tuple'
|
||||
- type: object
|
||||
properties:
|
||||
key:
|
||||
description: The hexadecimal key that uniquely identifies this game series.
|
||||
name:
|
||||
description: The name of this game series.
|
||||
type: object
|
||||
properties:
|
||||
key:
|
||||
description: The hexadecimal key that uniquely identifies this game series.
|
||||
type: string
|
||||
pattern: "^0x[0-9a-fA-F]+$"
|
||||
minLength: 3
|
||||
name:
|
||||
description: The name of this game series.
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- name
|
||||
LastUpdated:
|
||||
description: A type that contains the date and time when the service data was last updated.
|
||||
description: |
|
||||
A type that contains the date and time when the service data was last updated.
|
||||
|
||||
Note: The service returns this timestamp in the `yyyy-MM-dd'T'HH:mm:ss.SSSSSS` format without a timezone offset, so it is not a strictly valid RFC 3339 `date-time` value. The `date-time` format is kept deliberately so the generated code produces a `Date` value, and the timestamp is decoded by the custom transcoder configured in the live client, which interprets it as UTC.
|
||||
type: object
|
||||
properties:
|
||||
lastUpdated:
|
||||
@@ -500,27 +532,38 @@ components:
|
||||
format: date-time
|
||||
required:
|
||||
- lastUpdated
|
||||
Tuple:
|
||||
description: |
|
||||
A base type composed of a `key` and `name` pair.
|
||||
|
||||
This type is the base schema for the `AmiiboSeries`, `AmiiboType`, `GameCharacter`, and `GameSeries` types.
|
||||
type: object
|
||||
properties:
|
||||
key:
|
||||
description: A hexadecimal key that uniquely identifies this resource.
|
||||
type: string
|
||||
pattern: "^0x[0-9a-fA-F]+$"
|
||||
minLength: 3
|
||||
name:
|
||||
description: A display name for this resource.
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- name
|
||||
# List Entities
|
||||
AmiiboList:
|
||||
description: A list that contains amiibos.
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Amiibo'
|
||||
AmiiboSeriesList:
|
||||
description: A list that contains amiibo series.
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/AmiiboSeries'
|
||||
AmiiboTypeList:
|
||||
description: A list that contains amiibo types.
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/AmiiboType'
|
||||
GameCharacterList:
|
||||
description: A list that contains game characters.
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/GameCharacter'
|
||||
GameSeriesList:
|
||||
description: A list that contains game series.
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/GameSeries'
|
||||
# Wrapper Entities
|
||||
AmiiboWrapper:
|
||||
description: A response wrapper that contains zero, one, or more amiibos.
|
||||
description: |
|
||||
A response wrapper that contains zero, one, or more amiibos.
|
||||
|
||||
Note: Unlike the other response wrappers, the `amiibo` property is deliberately not required: the service returns `"amiibo": null` when an `id` filter matches nothing, and an empty list when other filters match nothing. The property must therefore remain optional for decoding to succeed in both cases.
|
||||
type: object
|
||||
properties:
|
||||
amiibo:
|
||||
@@ -528,10 +571,8 @@ components:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/Amiibo'
|
||||
description: A certain amiibo.
|
||||
- type: array
|
||||
- $ref: '#/components/schemas/AmiiboList'
|
||||
description: A list that contains amiibos.
|
||||
items:
|
||||
$ref: '#/components/schemas/Amiibo'
|
||||
AmiiboSeriesWrapper:
|
||||
description: A response wrapper that contains one or more amiibo series.
|
||||
type: object
|
||||
@@ -541,10 +582,8 @@ components:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/AmiiboSeries'
|
||||
description: A certain amiibo series.
|
||||
- type: array
|
||||
- $ref: '#/components/schemas/AmiiboSeriesList'
|
||||
description: A list that contains amiibo series.
|
||||
items:
|
||||
$ref: '#/components/schemas/AmiiboSeries'
|
||||
required:
|
||||
- amiibo
|
||||
AmiiboTypeWrapper:
|
||||
@@ -556,10 +595,8 @@ components:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/AmiiboType'
|
||||
description: A certain amiibo type.
|
||||
- type: array
|
||||
- $ref: '#/components/schemas/AmiiboTypeList'
|
||||
description: A list that contains amiibo types.
|
||||
items:
|
||||
$ref: '#/components/schemas/AmiiboType'
|
||||
required:
|
||||
- amiibo
|
||||
GameCharacterWrapper:
|
||||
@@ -571,10 +608,8 @@ components:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/GameCharacter'
|
||||
description: A certain game character.
|
||||
- type: array
|
||||
- $ref: '#/components/schemas/GameCharacterList'
|
||||
description: A list that contains game characters.
|
||||
items:
|
||||
$ref: '#/components/schemas/GameCharacter'
|
||||
required:
|
||||
- amiibo
|
||||
GameSeriesWrapper:
|
||||
@@ -586,10 +621,8 @@ components:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/GameSeries'
|
||||
description: A certain game series.
|
||||
- type: array
|
||||
- $ref: '#/components/schemas/GameSeriesList'
|
||||
description: A list that contains game series.
|
||||
items:
|
||||
$ref: '#/components/schemas/GameSeries'
|
||||
required:
|
||||
- amiibo
|
||||
# Error Entities
|
||||
|
||||
Reference in New Issue
Block a user