Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0431fb5133 |
No files matched your search
@@ -15,6 +15,7 @@
|
|||||||
# --- DOCUMENTATION ---
|
# --- DOCUMENTATION ---
|
||||||
|
|
||||||
DOCC_ARCHIVE_OUTPUT=./${SPM_LIBRARY_TARGET}.doccarchive
|
DOCC_ARCHIVE_OUTPUT=./${SPM_LIBRARY_TARGET}.doccarchive
|
||||||
|
DOCC_CATALOG=Sources/$(SPM_LIBRARY_TARGET)/Catalogs/$(SPM_LIBRARY_TARGET).docc
|
||||||
DOCC_GITHUB_OUTPUT=./docs
|
DOCC_GITHUB_OUTPUT=./docs
|
||||||
DOCC_GITHUB_BASE_PATH=asconnect-service
|
DOCC_GITHUB_BASE_PATH=asconnect-service
|
||||||
DOCC_PREVIEW_URL=http://localhost:8080/documentation/asconnectservice
|
DOCC_PREVIEW_URL=http://localhost:8080/documentation/asconnectservice
|
||||||
@@ -26,6 +27,9 @@ OAS_DOWNLOAD_FILE=$(OAS_DOWNLOAD_FOLDER)/openapi.oas.json
|
|||||||
OAS_DOWNLOAD_INPUT=$(OAS_DOWNLOAD_FOLDER)/asconnect-openapi-specification.zip
|
OAS_DOWNLOAD_INPUT=$(OAS_DOWNLOAD_FOLDER)/asconnect-openapi-specification.zip
|
||||||
OAS_DOWNLOAD_OUTPUT=Sources/$(SPM_LIBRARY_TARGET)/openapi.json
|
OAS_DOWNLOAD_OUTPUT=Sources/$(SPM_LIBRARY_TARGET)/openapi.json
|
||||||
OAS_DOWNLOAD_URL=https://developer.apple.com/sample-code/app-store-connect/app-store-connect-openapi-specification.zip
|
OAS_DOWNLOAD_URL=https://developer.apple.com/sample-code/app-store-connect/app-store-connect-openapi-specification.zip
|
||||||
|
OAS_GENERATE_CONFIG=Sources/$(SPM_LIBRARY_TARGET)/openapi-generator-config.yaml
|
||||||
|
OAS_GENERATE_OUTPUT=Sources/$(SPM_LIBRARY_TARGET)/Sources/Public/Generated
|
||||||
|
OAS_GENERATE_TOOLS=./Tools
|
||||||
|
|
||||||
# -- SWIFT PACKAGE MANAGER ---
|
# -- SWIFT PACKAGE MANAGER ---
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ environment ?= .env
|
|||||||
include $(environment)
|
include $(environment)
|
||||||
export $(shell sed 's/=.*//' $(environment))
|
export $(shell sed 's/=.*//' $(environment))
|
||||||
|
|
||||||
|
# Uses the DocC executable from Xcode's default toolchain, as some Swift toolchains (e.g. 6.4.0) ship an x86_64-only one.
|
||||||
|
DOCC_EXEC ?= $(shell xcrun --toolchain com.apple.dt.toolchain.XcodeDefault --find docc 2>/dev/null)
|
||||||
|
|
||||||
|
export DOCC_EXEC
|
||||||
|
|
||||||
# LIBRARY
|
# LIBRARY
|
||||||
|
|
||||||
lib-build: ## Builds the library
|
lib-build: ## Builds the library
|
||||||
@@ -57,7 +62,7 @@ pkg-update: ## Updates the SPM package dependencies
|
|||||||
doc-generate: doc-generate-archive doc-generate-github ## Generates the library documentation for both Github and Xcode
|
doc-generate: doc-generate-archive doc-generate-github ## Generates the library documentation for both Github and Xcode
|
||||||
|
|
||||||
doc-generate-archive: ## Generates the library documentation archive for Xcode
|
doc-generate-archive: ## Generates the library documentation archive for Xcode
|
||||||
@swift package \
|
@ENABLE_DOCC_PLUGIN=1 swift package \
|
||||||
--allow-writing-to-directory $(DOCC_ARCHIVE_OUTPUT) \
|
--allow-writing-to-directory $(DOCC_ARCHIVE_OUTPUT) \
|
||||||
generate-documentation \
|
generate-documentation \
|
||||||
--target $(SPM_LIBRARY_TARGET) \
|
--target $(SPM_LIBRARY_TARGET) \
|
||||||
@@ -66,7 +71,7 @@ doc-generate-archive: ## Generates the library documentation archive for Xcode
|
|||||||
--output-path $(DOCC_ARCHIVE_OUTPUT)
|
--output-path $(DOCC_ARCHIVE_OUTPUT)
|
||||||
|
|
||||||
doc-generate-github: ## Generates the library documentation for Github
|
doc-generate-github: ## Generates the library documentation for Github
|
||||||
@swift package \
|
@ENABLE_DOCC_PLUGIN=1 swift package \
|
||||||
--allow-writing-to-directory $(DOCC_GITHUB_OUTPUT) \
|
--allow-writing-to-directory $(DOCC_GITHUB_OUTPUT) \
|
||||||
generate-documentation \
|
generate-documentation \
|
||||||
--target $(SPM_LIBRARY_TARGET) \
|
--target $(SPM_LIBRARY_TARGET) \
|
||||||
@@ -77,9 +82,12 @@ doc-generate-github: ## Generates the library documentation for Github
|
|||||||
--hosting-base-path $(DOCC_GITHUB_BASE_PATH) \
|
--hosting-base-path $(DOCC_GITHUB_BASE_PATH) \
|
||||||
--output-path $(DOCC_GITHUB_OUTPUT)
|
--output-path $(DOCC_GITHUB_OUTPUT)
|
||||||
|
|
||||||
|
doc-check: ## Checks the DocC catalog covers every generated operation and schema
|
||||||
|
@python3 -I Scripts/check-docc.py "$(OAS_GENERATE_OUTPUT)" "$(DOCC_CATALOG)"
|
||||||
|
|
||||||
doc-preview: ## Previews the library documentation in Safari
|
doc-preview: ## Previews the library documentation in Safari
|
||||||
@open -a safari $(DOCC_PREVIEW_URL)
|
@open -a safari $(DOCC_PREVIEW_URL)
|
||||||
@swift package \
|
@ENABLE_DOCC_PLUGIN=1 swift package \
|
||||||
--disable-sandbox \
|
--disable-sandbox \
|
||||||
preview-documentation \
|
preview-documentation \
|
||||||
--target $(SPM_LIBRARY_TARGET) \
|
--target $(SPM_LIBRARY_TARGET) \
|
||||||
@@ -96,6 +104,35 @@ oas-download: ## Downloads the latest App Store Connect OpenAPI specification do
|
|||||||
@unzip -q "$(OAS_DOWNLOAD_INPUT)" -d "$(OAS_DOWNLOAD_FOLDER)"
|
@unzip -q "$(OAS_DOWNLOAD_INPUT)" -d "$(OAS_DOWNLOAD_FOLDER)"
|
||||||
@mv "$(OAS_DOWNLOAD_FILE)" "$(OAS_DOWNLOAD_OUTPUT)"
|
@mv "$(OAS_DOWNLOAD_FILE)" "$(OAS_DOWNLOAD_OUTPUT)"
|
||||||
@rm -rf "$(OAS_DOWNLOAD_FOLDER)"
|
@rm -rf "$(OAS_DOWNLOAD_FOLDER)"
|
||||||
|
@# Generating the code also validates the document.
|
||||||
|
@$(MAKE) --no-print-directory oas-patch-document
|
||||||
|
@$(MAKE) --no-print-directory oas-generate
|
||||||
|
@$(MAKE) --no-print-directory doc-check
|
||||||
|
|
||||||
|
oas-patch: oas-patch-document oas-validate # Patches and validates the OpenAPI specification document
|
||||||
|
|
||||||
|
oas-patch-document: # Patches the OpenAPI specification document with Scripts/patch-oas.jq
|
||||||
|
@jq --from-file Scripts/patch-oas.jq "$(OAS_DOWNLOAD_OUTPUT)" > "$(OAS_DOWNLOAD_OUTPUT).tmp"
|
||||||
|
@mv "$(OAS_DOWNLOAD_OUTPUT).tmp" "$(OAS_DOWNLOAD_OUTPUT)"
|
||||||
|
|
||||||
|
oas-validate: ## Validates the OpenAPI specification document
|
||||||
|
@swift run \
|
||||||
|
--configuration release \
|
||||||
|
--package-path "$(OAS_GENERATE_TOOLS)" \
|
||||||
|
swift-openapi-generator generate \
|
||||||
|
--config "$(OAS_GENERATE_CONFIG)" \
|
||||||
|
--dry-run \
|
||||||
|
"$(OAS_DOWNLOAD_OUTPUT)"
|
||||||
|
|
||||||
|
oas-generate: ## Generates the library source code from the OpenAPI specification document
|
||||||
|
@mkdir -p "$(OAS_GENERATE_OUTPUT)"
|
||||||
|
@swift run \
|
||||||
|
--configuration release \
|
||||||
|
--package-path "$(OAS_GENERATE_TOOLS)" \
|
||||||
|
swift-openapi-generator generate \
|
||||||
|
--config "$(OAS_GENERATE_CONFIG)" \
|
||||||
|
--output-directory "$(OAS_GENERATE_OUTPUT)" \
|
||||||
|
"$(OAS_DOWNLOAD_OUTPUT)"
|
||||||
|
|
||||||
# IDE
|
# IDE
|
||||||
|
|
||||||
|
|||||||
+24
-14
@@ -1,27 +1,30 @@
|
|||||||
// swift-tools-version: 5.10
|
// swift-tools-version: 6.0
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===
|
// ===----------------------------------------------------------------------===
|
||||||
//
|
//
|
||||||
// This source file is part of the ASConnectService open source project
|
// This source file is part of the ASConnectService open source project
|
||||||
//
|
//
|
||||||
// Copyright (c) 2026 Röck+Cöde VoF. and the ASConnectService project authors
|
// Copyright (c) 2026 Röck+Cöde VoF. and the ASConnectService project authors
|
||||||
// Licensed under the EUPL 1.2 or later.
|
// Licensed under Apache license v2.0
|
||||||
//
|
//
|
||||||
// See LICENSE for license information
|
// See LICENSE for license information
|
||||||
// See CONTRIBUTORS for the list of ASConnectService project authors
|
// See CONTRIBUTORS for the list of ASConnectService project authors
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
|
||||||
|
import class Foundation.ProcessInfo
|
||||||
import PackageDescription
|
import PackageDescription
|
||||||
|
|
||||||
let package = Package(
|
let package = Package(
|
||||||
name: AppStoreConnectService.package,
|
name: AppStoreConnectService.package,
|
||||||
platforms: [
|
platforms: [
|
||||||
.iOS(.v13),
|
.iOS(.v14),
|
||||||
.macOS(.v10_15),
|
.macOS(.v11),
|
||||||
.tvOS(.v13),
|
.tvOS(.v14),
|
||||||
.visionOS(.v1),
|
.visionOS(.v1),
|
||||||
.watchOS(.v6)
|
.watchOS(.v7)
|
||||||
],
|
],
|
||||||
products: [
|
products: [
|
||||||
.library(
|
.library(
|
||||||
@@ -30,10 +33,9 @@ let package = Package(
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.package(url: "https://github.com/apple/swift-openapi-generator.git", exact: "1.11.0"),
|
// Must be at least the runtime version required by the generator in `Tools`.
|
||||||
.package(url: "https://github.com/apple/swift-openapi-runtime", from: "1.5.0"),
|
.package(url: "https://github.com/apple/swift-openapi-runtime", from: "1.11.0"),
|
||||||
.package(url: "https://github.com/apple/swift-openapi-urlsession", from: "1.0.2"),
|
.package(url: "https://github.com/apple/swift-openapi-urlsession", from: "1.0.2")
|
||||||
.package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.1.0")
|
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.target(
|
.target(
|
||||||
@@ -43,8 +45,9 @@ let package = Package(
|
|||||||
.product(name: "OpenAPIURLSession", package: "swift-openapi-urlsession")
|
.product(name: "OpenAPIURLSession", package: "swift-openapi-urlsession")
|
||||||
],
|
],
|
||||||
path: "Sources/ASConnectService",
|
path: "Sources/ASConnectService",
|
||||||
plugins: [
|
exclude: [
|
||||||
.plugin(name: "OpenAPIGenerator", package: "swift-openapi-generator")
|
"openapi.json",
|
||||||
|
"openapi-generator-config.yaml"
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
@@ -57,6 +60,13 @@ let package = Package(
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Only added by the documentation targets, so consumers don't resolve it.
|
||||||
|
if ProcessInfo.processInfo.environment["ENABLE_DOCC_PLUGIN"] != nil {
|
||||||
|
package.dependencies.append(
|
||||||
|
.package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.1.0")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Constants
|
// MARK: - Constants
|
||||||
|
|
||||||
enum AppStoreConnectService {
|
enum AppStoreConnectService {
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ A Swift client library for the App Store Connect API, generated from the officia
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
`ASConnectService` provides a type-safe, Swift-native interface to Apple's App Store Connect API. This package enables developers to programmatically interact with App Store Connect services for managing apps, builds, reviews, sales reports, and more.
|
`ASConnectService` exposes every endpoint of Apple's App Store Connect API as a Swift method with typed requests and responses. It's generated from the official OpenAPI specification with [Swift OpenAPI Generator](https://github.com/apple/swift-openapi-generator).
|
||||||
|
|
||||||
The library is automatically generated from the official App Store Connect API OpenAPI specification using Apple's [swift-openapi-generator](https://github.com/apple/swift-openapi-generator), ensuring complete API coverage and type safety.
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -14,7 +12,7 @@ Add `ASConnectService` as a dependency in your `Package.swift` file:
|
|||||||
|
|
||||||
```swift
|
```swift
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.package(url: "https://github.com/rock-n-code/asconnect-service.git", from: "1.0.0")
|
.package(url: "https://github.com/rock-n-code/asconnect-service.git", from: "2.0.0")
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -33,42 +31,81 @@ Then add it to your target dependencies:
|
|||||||
|
|
||||||
### Creating a Client
|
### Creating a Client
|
||||||
|
|
||||||
Create a `Client` instance by providing a server URL and transport. Use `BearerAuthMiddleware` to authenticate requests with a JSON Web Token (JWT).
|
Create a `Client` with a server URL, a transport, and a `BearerAuthMiddleware`. With an API key from App Store Connect, `APIKeyTokenProvider` signs the JSON Web Tokens (JWT) the API requires, and renews them before they expire:
|
||||||
|
|
||||||
The App Store Connect API requires authentication using API keys. You'll need to:
|
|
||||||
|
|
||||||
1. Create an API key in App Store Connect
|
|
||||||
2. Generate a signed JWT token using your key ID, issuer ID, and private key
|
|
||||||
3. Pass the token to the built-in `BearerAuthMiddleware` when creating the client
|
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
import ASConnectService
|
import ASConnectService
|
||||||
import OpenAPIURLSession
|
import OpenAPIURLSession
|
||||||
|
|
||||||
|
let tokenProvider = try APIKeyTokenProvider(
|
||||||
|
issuerID: yourIssuerID,
|
||||||
|
keyID: yourKeyID,
|
||||||
|
privateKey: yourP8FileContents
|
||||||
|
)
|
||||||
|
|
||||||
let client = Client(
|
let client = Client(
|
||||||
serverURL: try Servers.server1(),
|
serverURL: try Servers.Server1.url(),
|
||||||
transport: URLSessionTransport(),
|
transport: URLSessionTransport(),
|
||||||
middlewares: [
|
middlewares: [
|
||||||
BearerAuthMiddleware(token: yourJWTToken)
|
BearerAuthMiddleware { try await tokenProvider.token() }
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Making API Calls
|
To use a token you signed yourself, pass it directly. It expires after 20 minutes at most:
|
||||||
|
|
||||||
The `Client` conforms to `APIProtocol`, which defines a method for every endpoint in the App Store Connect API. Each method accepts an `Input` value and returns an `Output` value with the response.
|
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
let response = try await client.appsGetCollection(.init())
|
BearerAuthMiddleware(token: yourJWTToken)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Supported Platforms
|
### Making API Calls
|
||||||
|
|
||||||
- iOS 13.0+
|
Each endpoint is a method on `Client`, declared by `APIProtocol`. It returns an `Output` enum with a case for each documented response:
|
||||||
- macOS 10.15+
|
|
||||||
- tvOS 13.0+
|
```swift
|
||||||
|
let response = try await client.appsGetCollection(query: .init(limit: 10))
|
||||||
|
let apps = try response.ok.body.json.data
|
||||||
|
```
|
||||||
|
|
||||||
|
The `ok` property throws for any other response. To handle those, switch over the `Output` cases, such as `.unauthorized` or `.undocumented(statusCode:_:)`.
|
||||||
|
|
||||||
|
### Fetching More Pages
|
||||||
|
|
||||||
|
Lists are paged. Iterate a `PagedSequence` to fetch each page in turn:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let pages = PagedSequence(links: \.links) { cursor in
|
||||||
|
try await client.appsGetCollection(query: .init(limit: 200, cursor: cursor)).ok.body.json
|
||||||
|
}
|
||||||
|
|
||||||
|
var apps: [Components.Schemas.App] = []
|
||||||
|
|
||||||
|
for try await page in pages {
|
||||||
|
apps += page.data
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To fetch pages yourself, pass a page's `links.nextCursor` as the `cursor` of the next request.
|
||||||
|
|
||||||
|
### Handling Rate Limits
|
||||||
|
|
||||||
|
Add `RateLimitMiddleware` before the authentication middleware to retry `429 Too Many Requests` responses:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
middlewares: [
|
||||||
|
RateLimitMiddleware(),
|
||||||
|
BearerAuthMiddleware { try await tokenProvider.token() }
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Swift 6.0+ (Xcode 16+)
|
||||||
|
- iOS 14.0+
|
||||||
|
- macOS 11.0+
|
||||||
|
- tvOS 14.0+
|
||||||
- visionOS 1.0+
|
- visionOS 1.0+
|
||||||
- watchOS 6.0+
|
- watchOS 7.0+
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
@@ -95,13 +132,34 @@ make doc-generate-github
|
|||||||
|
|
||||||
# Preview documentation locally in Safari
|
# Preview documentation locally in Safari
|
||||||
make doc-preview
|
make doc-preview
|
||||||
|
|
||||||
|
# Check the catalog covers the generated code
|
||||||
|
make doc-check
|
||||||
|
```
|
||||||
|
|
||||||
|
### Updating the API
|
||||||
|
|
||||||
|
The generated code is committed in `Sources/ASConnectService/Sources/Public/Generated`, so consumers don't run the generator.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Update to the latest specification
|
||||||
|
make oas-download
|
||||||
|
|
||||||
|
# Regenerate the code only
|
||||||
|
make oas-generate
|
||||||
|
|
||||||
|
# Validate the specification only
|
||||||
|
make oas-validate
|
||||||
```
|
```
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
- [swift-openapi-generator](https://github.com/apple/swift-openapi-generator)
|
|
||||||
- [swift-openapi-runtime](https://github.com/apple/swift-openapi-runtime)
|
- [swift-openapi-runtime](https://github.com/apple/swift-openapi-runtime)
|
||||||
- [swift-openapi-urlsession](https://github.com/apple/swift-openapi-urlsession)
|
- [swift-openapi-urlsession](https://github.com/apple/swift-openapi-urlsession)
|
||||||
|
|
||||||
|
Development only:
|
||||||
|
|
||||||
|
- [swift-openapi-generator](https://github.com/apple/swift-openapi-generator), in `Tools`
|
||||||
- [swift-docc-plugin](https://github.com/swiftlang/swift-docc-plugin)
|
- [swift-docc-plugin](https://github.com/swiftlang/swift-docc-plugin)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# ===------------------------------------------------------------------------------------===
|
||||||
|
#
|
||||||
|
# This source file is part of the App Store Connect Service open source project
|
||||||
|
#
|
||||||
|
# Copyright (c) 2026 Röck+Cöde VoF. and the App Store Connect Service project authors
|
||||||
|
# Licensed under Apache license v2.0
|
||||||
|
#
|
||||||
|
# See LICENSE for license information
|
||||||
|
# See CONTRIBUTORS for the list of App Store Connect Service project authors
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
# ===------------------------------------------------------------------------------------===
|
||||||
|
|
||||||
|
"""Checks that the DocC catalog lists every generated operation and schema, and nothing else.
|
||||||
|
|
||||||
|
Usage: python3 check-docc.py <generated sources folder> <DocC catalog folder>
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def generated_symbols(types_path):
|
||||||
|
"""Returns the methods, operations, and schemas in the generated `Types.swift` file."""
|
||||||
|
lines = open(types_path).read().split('\n')
|
||||||
|
|
||||||
|
def start(prefix):
|
||||||
|
return next(index for index, line in enumerate(lines) if line.startswith(prefix))
|
||||||
|
|
||||||
|
protocol = lines[start('public protocol APIProtocol'):start('extension APIProtocol {')]
|
||||||
|
components = lines[start('public enum Components {'):start('public enum Operations {')]
|
||||||
|
operations = lines[start('public enum Operations {'):]
|
||||||
|
|
||||||
|
schema = re.compile(r' (?:@\w+ )*public (?:indirect )?(?:struct|enum|typealias) (\w+)')
|
||||||
|
|
||||||
|
return {
|
||||||
|
'APIProtocol.md': {m.group(1) for line in protocol if (m := re.match(r' func (\w+)\(', line))},
|
||||||
|
'Operations.md': {m.group(1) for line in operations if (m := re.match(r' public enum (\w+)', line))},
|
||||||
|
'Components.md': {m.group(1) for line in components if (m := schema.match(line))},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def documented_symbols(catalog_path):
|
||||||
|
"""Returns the symbols listed in the DocC catalog topics."""
|
||||||
|
patterns = {
|
||||||
|
'APIProtocol.md': r'``(\w+)\(_:\)``',
|
||||||
|
'Operations.md': r'``Operations/(\w+)``',
|
||||||
|
'Components.md': r'``Components/Schemas/(\w+)``',
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: set(re.findall(pattern, open(f'{catalog_path}/Extensions/{name}').read()))
|
||||||
|
for name, pattern in patterns.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def main(generated_path, catalog_path):
|
||||||
|
generated = generated_symbols(f'{generated_path}/Types.swift')
|
||||||
|
documented = documented_symbols(catalog_path)
|
||||||
|
is_up_to_date = True
|
||||||
|
|
||||||
|
for name in generated:
|
||||||
|
missing = sorted(generated[name] - documented[name])
|
||||||
|
stale = sorted(documented[name] - generated[name])
|
||||||
|
|
||||||
|
for title, symbols in [('Missing from', missing), ('Stale in', stale)]:
|
||||||
|
if symbols:
|
||||||
|
is_up_to_date = False
|
||||||
|
print(f'{title} {name} ({len(symbols)}):')
|
||||||
|
print('\n'.join(f' - {symbol}' for symbol in symbols))
|
||||||
|
|
||||||
|
if is_up_to_date:
|
||||||
|
print('The DocC catalog lists every generated operation and schema.')
|
||||||
|
|
||||||
|
return 0 if is_up_to_date else 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if len(sys.argv) != 3:
|
||||||
|
sys.exit(__doc__)
|
||||||
|
|
||||||
|
sys.exit(main(sys.argv[1], sys.argv[2]))
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# ===------------------------------------------------------------------------------------===
|
||||||
|
#
|
||||||
|
# This source file is part of the App Store Connect Service open source project
|
||||||
|
#
|
||||||
|
# Copyright (c) 2026 Röck+Cöde VoF. and the App Store Connect Service project authors
|
||||||
|
# Licensed under Apache license v2.0
|
||||||
|
#
|
||||||
|
# See LICENSE for license information
|
||||||
|
# See CONTRIBUTORS for the list of App Store Connect Service project authors
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
# ===------------------------------------------------------------------------------------===
|
||||||
|
|
||||||
|
# Patches the App Store Connect OpenAPI specification document. Each patch is safe to apply more than once.
|
||||||
|
|
||||||
|
# Removes empty enums (e.g. `fields[appKeywords]`), as they generate case-less Swift enums that do not compile.
|
||||||
|
(.. | objects | select(.enum == [])) |= del(.enum)
|
||||||
|
|
||||||
|
# Adds the undocumented `cursor` query parameter, used by `next` links, to the operations that return paged responses.
|
||||||
|
| ([
|
||||||
|
.components.schemas
|
||||||
|
| to_entries[]
|
||||||
|
| select(.value.properties.links["$ref"] == "#/components/schemas/PagedDocumentLinks")
|
||||||
|
| "#/components/schemas/" + .key
|
||||||
|
]) as $paged
|
||||||
|
| .paths[][] |= (
|
||||||
|
if type == "object"
|
||||||
|
and (.responses["200"].content["application/json"].schema["$ref"] as $ref | any($paged[]; . == $ref))
|
||||||
|
and (any(.parameters[]?; .name == "cursor") | not)
|
||||||
|
then .parameters += [{
|
||||||
|
"name": "cursor",
|
||||||
|
"in": "query",
|
||||||
|
"description": "cursor of the page to fetch, from the next link of the previous page",
|
||||||
|
"schema": { "type": "string" },
|
||||||
|
"style": "form"
|
||||||
|
}]
|
||||||
|
else . end
|
||||||
|
)
|
||||||
+116
-10
@@ -1,20 +1,28 @@
|
|||||||
# ``APIProtocol``
|
# ``APIProtocol``
|
||||||
|
|
||||||
A protocol defining the available operations for the App Store Connect API.
|
The operations of the App Store Connect API.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The ``APIProtocol`` protocol declares the complete set of operations available through the App Store Connect API. Each method corresponds to an HTTP endpoint and follows a consistent naming pattern:
|
Each method calls one endpoint, and is named after its resource followed by its action:
|
||||||
|
|
||||||
- `<resource>GetCollection` — List resources (HTTP `GET` on a collection)
|
| Suffix | HTTP method | Action |
|
||||||
- `<resource>CreateInstance` — Create a resource (HTTP `POST`)
|
| --- | --- | --- |
|
||||||
- `<resource>GetInstance` — Get a single resource by ID (HTTP `GET`)
|
| `GetCollection` | `GET` | List resources |
|
||||||
- `<resource>UpdateInstance` — Update a resource (HTTP `PATCH`)
|
| `GetInstance` | `GET` | Get a resource by ID |
|
||||||
- `<resource>DeleteInstance` — Delete a resource (HTTP `DELETE`)
|
| `CreateInstance` | `POST` | Create a resource |
|
||||||
- `<resource>GetToManyRelated` — Fetch related resources
|
| `UpdateInstance` | `PATCH` | Update a resource |
|
||||||
- `<resource>GetToOneRelated` — Fetch a related resource
|
| `DeleteInstance` | `DELETE` | Delete a resource |
|
||||||
|
| `GetToOneRelated`, `GetToManyRelated` | `GET` | Get related resources |
|
||||||
|
| `GetToOneRelationship`, `GetToManyRelationship` | `GET` | Get the IDs of related resources |
|
||||||
|
| `CreateToManyRelationship` | `POST` | Add relationships |
|
||||||
|
| `UpdateToOneRelationship`, `ReplaceToManyRelationship` | `PATCH` | Replace relationships |
|
||||||
|
| `DeleteToManyRelationship` | `DELETE` | Remove relationships |
|
||||||
|
| `GetMetrics` | `GET` | Get metrics |
|
||||||
|
|
||||||
The ``Client`` type conforms to this protocol and provides the concrete implementation for making HTTP requests.
|
For example, ``appsGetCollection(_:)`` lists apps, and ``appsBuildsGetToManyRelated(_:)`` lists the builds of an app.
|
||||||
|
|
||||||
|
Every method also has an overload that takes the `path`, `query`, `headers`, and `body` of its `Input` as separate arguments. ``Client`` provides the implementation.
|
||||||
|
|
||||||
## Topics
|
## Topics
|
||||||
|
|
||||||
@@ -106,6 +114,9 @@ The ``Client`` type conforms to this protocol and provides the concrete implemen
|
|||||||
- ``appsSubscriptionGracePeriodGetToOneRelated(_:)``
|
- ``appsSubscriptionGracePeriodGetToOneRelated(_:)``
|
||||||
- ``appsSubscriptionGroupsGetToManyRelated(_:)``
|
- ``appsSubscriptionGroupsGetToManyRelated(_:)``
|
||||||
- ``appsWebhooksGetToManyRelated(_:)``
|
- ``appsWebhooksGetToManyRelated(_:)``
|
||||||
|
- ``appsAssetLibraryGetToOneRelationship(_:)``
|
||||||
|
- ``appsAssetLibraryGetToOneRelated(_:)``
|
||||||
|
- ``appsPerformanceOverviewsGetToManyRelated(_:)``
|
||||||
- ``appInfosGetInstance(_:)``
|
- ``appInfosGetInstance(_:)``
|
||||||
- ``appInfosUpdateInstance(_:)``
|
- ``appInfosUpdateInstance(_:)``
|
||||||
- ``appInfosAgeRatingDeclarationGetToOneRelated(_:)``
|
- ``appInfosAgeRatingDeclarationGetToOneRelated(_:)``
|
||||||
@@ -207,6 +218,8 @@ The ``Client`` type conforms to this protocol and provides the concrete implemen
|
|||||||
- ``appStoreVersionLocalizationsSearchKeywordsCreateToManyRelationship(_:)``
|
- ``appStoreVersionLocalizationsSearchKeywordsCreateToManyRelationship(_:)``
|
||||||
- ``appStoreVersionLocalizationsSearchKeywordsDeleteToManyRelationship(_:)``
|
- ``appStoreVersionLocalizationsSearchKeywordsDeleteToManyRelationship(_:)``
|
||||||
- ``appStoreVersionLocalizationsSearchKeywordsGetToManyRelated(_:)``
|
- ``appStoreVersionLocalizationsSearchKeywordsGetToManyRelated(_:)``
|
||||||
|
- ``appStoreVersionLocalizationsPlacementsGetToManyRelationship(_:)``
|
||||||
|
- ``appStoreVersionLocalizationsPlacementsGetToManyRelated(_:)``
|
||||||
- ``appStoreVersionPhasedReleasesCreateInstance(_:)``
|
- ``appStoreVersionPhasedReleasesCreateInstance(_:)``
|
||||||
- ``appStoreVersionPhasedReleasesUpdateInstance(_:)``
|
- ``appStoreVersionPhasedReleasesUpdateInstance(_:)``
|
||||||
- ``appStoreVersionPhasedReleasesDeleteInstance(_:)``
|
- ``appStoreVersionPhasedReleasesDeleteInstance(_:)``
|
||||||
@@ -241,6 +254,8 @@ The ``Client`` type conforms to this protocol and provides the concrete implemen
|
|||||||
- ``appStoreVersionExperimentTreatmentLocalizationsAppScreenshotSetsGetToManyRelated(_:)``
|
- ``appStoreVersionExperimentTreatmentLocalizationsAppScreenshotSetsGetToManyRelated(_:)``
|
||||||
- ``appStoreVersionExperimentTreatmentLocalizationsAppPreviewSetsGetToManyRelationship(_:)``
|
- ``appStoreVersionExperimentTreatmentLocalizationsAppPreviewSetsGetToManyRelationship(_:)``
|
||||||
- ``appStoreVersionExperimentTreatmentLocalizationsAppScreenshotSetsGetToManyRelationship(_:)``
|
- ``appStoreVersionExperimentTreatmentLocalizationsAppScreenshotSetsGetToManyRelationship(_:)``
|
||||||
|
- ``appStoreVersionExperimentTreatmentLocalizationsPlacementsGetToManyRelationship(_:)``
|
||||||
|
- ``appStoreVersionExperimentTreatmentLocalizationsPlacementsGetToManyRelated(_:)``
|
||||||
|
|
||||||
### App Custom Product Pages
|
### App Custom Product Pages
|
||||||
|
|
||||||
@@ -267,6 +282,8 @@ The ``Client`` type conforms to this protocol and provides the concrete implemen
|
|||||||
- ``appCustomProductPageLocalizationsSearchKeywordsCreateToManyRelationship(_:)``
|
- ``appCustomProductPageLocalizationsSearchKeywordsCreateToManyRelationship(_:)``
|
||||||
- ``appCustomProductPageLocalizationsSearchKeywordsDeleteToManyRelationship(_:)``
|
- ``appCustomProductPageLocalizationsSearchKeywordsDeleteToManyRelationship(_:)``
|
||||||
- ``appCustomProductPageLocalizationsSearchKeywordsGetToManyRelated(_:)``
|
- ``appCustomProductPageLocalizationsSearchKeywordsGetToManyRelated(_:)``
|
||||||
|
- ``appCustomProductPageLocalizationsPlacementsGetToManyRelationship(_:)``
|
||||||
|
- ``appCustomProductPageLocalizationsPlacementsGetToManyRelated(_:)``
|
||||||
|
|
||||||
### App Clips
|
### App Clips
|
||||||
|
|
||||||
@@ -322,6 +339,8 @@ The ``Client`` type conforms to this protocol and provides the concrete implemen
|
|||||||
- ``appEventLocalizationsAppEventVideoClipsGetToManyRelated(_:)``
|
- ``appEventLocalizationsAppEventVideoClipsGetToManyRelated(_:)``
|
||||||
- ``appEventLocalizationsAppEventScreenshotsGetToManyRelationship(_:)``
|
- ``appEventLocalizationsAppEventScreenshotsGetToManyRelationship(_:)``
|
||||||
- ``appEventLocalizationsAppEventVideoClipsGetToManyRelationship(_:)``
|
- ``appEventLocalizationsAppEventVideoClipsGetToManyRelationship(_:)``
|
||||||
|
- ``appEventLocalizationsPlacementsGetToManyRelationship(_:)``
|
||||||
|
- ``appEventLocalizationsPlacementsGetToManyRelated(_:)``
|
||||||
- ``appEventScreenshotsCreateInstance(_:)``
|
- ``appEventScreenshotsCreateInstance(_:)``
|
||||||
- ``appEventScreenshotsGetInstance(_:)``
|
- ``appEventScreenshotsGetInstance(_:)``
|
||||||
- ``appEventScreenshotsUpdateInstance(_:)``
|
- ``appEventScreenshotsUpdateInstance(_:)``
|
||||||
@@ -358,6 +377,32 @@ The ``Client`` type conforms to this protocol and provides the concrete implemen
|
|||||||
- ``routingAppCoveragesUpdateInstance(_:)``
|
- ``routingAppCoveragesUpdateInstance(_:)``
|
||||||
- ``routingAppCoveragesDeleteInstance(_:)``
|
- ``routingAppCoveragesDeleteInstance(_:)``
|
||||||
|
|
||||||
|
### App Asset Library
|
||||||
|
|
||||||
|
- ``appAssetLibrariesGetInstance(_:)``
|
||||||
|
- ``appAssetLibrariesImagesGetToManyRelationship(_:)``
|
||||||
|
- ``appAssetLibrariesImagesGetToManyRelated(_:)``
|
||||||
|
- ``appAssetLibrariesVideosGetToManyRelationship(_:)``
|
||||||
|
- ``appAssetLibrariesVideosGetToManyRelated(_:)``
|
||||||
|
- ``appAssetLibraryImagesCreateInstance(_:)``
|
||||||
|
- ``appAssetLibraryImagesGetInstance(_:)``
|
||||||
|
- ``appAssetLibraryImagesUpdateInstance(_:)``
|
||||||
|
- ``appAssetLibraryImagesDeleteInstance(_:)``
|
||||||
|
- ``appAssetLibraryImagesPlacementsGetToManyRelationship(_:)``
|
||||||
|
- ``appAssetLibraryImagesPlacementsGetToManyRelated(_:)``
|
||||||
|
- ``appAssetLibraryVideosCreateInstance(_:)``
|
||||||
|
- ``appAssetLibraryVideosGetInstance(_:)``
|
||||||
|
- ``appAssetLibraryVideosUpdateInstance(_:)``
|
||||||
|
- ``appAssetLibraryVideosDeleteInstance(_:)``
|
||||||
|
- ``appAssetLibraryVideosPlacementsGetToManyRelationship(_:)``
|
||||||
|
- ``appAssetLibraryVideosPlacementsGetToManyRelated(_:)``
|
||||||
|
- ``appAssetLibraryPlacementsCreateInstance(_:)``
|
||||||
|
- ``appAssetLibraryPlacementsGetInstance(_:)``
|
||||||
|
- ``appAssetLibraryPlacementsDeleteInstance(_:)``
|
||||||
|
- ``appAssetLibraryPlacementOrderingRequestsCreateInstance(_:)``
|
||||||
|
- ``appAssetLibraryRefDataGetCollection(_:)``
|
||||||
|
- ``appAssetLibraryRefDataGetInstance(_:)``
|
||||||
|
|
||||||
### App Encryption
|
### App Encryption
|
||||||
|
|
||||||
- ``appEncryptionDeclarationsGetCollection(_:)``
|
- ``appEncryptionDeclarationsGetCollection(_:)``
|
||||||
@@ -581,15 +626,33 @@ The ``Client`` type conforms to this protocol and provides the concrete implemen
|
|||||||
- ``inAppPurchasesV2OfferCodesGetToManyRelationship(_:)``
|
- ``inAppPurchasesV2OfferCodesGetToManyRelationship(_:)``
|
||||||
- ``inAppPurchasesV2PricePointsGetToManyRelationship(_:)``
|
- ``inAppPurchasesV2PricePointsGetToManyRelationship(_:)``
|
||||||
- ``inAppPurchasesV2PromotedPurchaseGetToOneRelationship(_:)``
|
- ``inAppPurchasesV2PromotedPurchaseGetToOneRelationship(_:)``
|
||||||
|
- ``inAppPurchasesV2VersionsGetToManyRelationship(_:)``
|
||||||
|
- ``inAppPurchasesV2VersionsGetToManyRelated(_:)``
|
||||||
|
- ``inAppPurchaseVersionsCreateInstance(_:)``
|
||||||
|
- ``inAppPurchaseVersionsGetInstance(_:)``
|
||||||
|
- ``inAppPurchaseVersionsImageGetToOneRelationship(_:)``
|
||||||
|
- ``inAppPurchaseVersionsImageGetToOneRelated(_:)``
|
||||||
|
- ``inAppPurchaseVersionsImagesGetToManyRelationship(_:)``
|
||||||
|
- ``inAppPurchaseVersionsImagesGetToManyRelated(_:)``
|
||||||
|
- ``inAppPurchaseVersionsLocalizationsGetToManyRelationship(_:)``
|
||||||
|
- ``inAppPurchaseVersionsLocalizationsGetToManyRelated(_:)``
|
||||||
- ``inAppPurchaseLocalizationsCreateInstance(_:)``
|
- ``inAppPurchaseLocalizationsCreateInstance(_:)``
|
||||||
- ``inAppPurchaseLocalizationsGetInstance(_:)``
|
- ``inAppPurchaseLocalizationsGetInstance(_:)``
|
||||||
- ``inAppPurchaseLocalizationsUpdateInstance(_:)``
|
- ``inAppPurchaseLocalizationsUpdateInstance(_:)``
|
||||||
- ``inAppPurchaseLocalizationsDeleteInstance(_:)``
|
- ``inAppPurchaseLocalizationsDeleteInstance(_:)``
|
||||||
|
- ``inAppPurchaseLocalizationsV2CreateInstance(_:)``
|
||||||
|
- ``inAppPurchaseLocalizationsV2GetInstance(_:)``
|
||||||
|
- ``inAppPurchaseLocalizationsV2UpdateInstance(_:)``
|
||||||
|
- ``inAppPurchaseLocalizationsV2DeleteInstance(_:)``
|
||||||
- ``inAppPurchaseContentsGetInstance(_:)``
|
- ``inAppPurchaseContentsGetInstance(_:)``
|
||||||
- ``inAppPurchaseImagesCreateInstance(_:)``
|
- ``inAppPurchaseImagesCreateInstance(_:)``
|
||||||
- ``inAppPurchaseImagesGetInstance(_:)``
|
- ``inAppPurchaseImagesGetInstance(_:)``
|
||||||
- ``inAppPurchaseImagesUpdateInstance(_:)``
|
- ``inAppPurchaseImagesUpdateInstance(_:)``
|
||||||
- ``inAppPurchaseImagesDeleteInstance(_:)``
|
- ``inAppPurchaseImagesDeleteInstance(_:)``
|
||||||
|
- ``inAppPurchaseImagesV2CreateInstance(_:)``
|
||||||
|
- ``inAppPurchaseImagesV2GetInstance(_:)``
|
||||||
|
- ``inAppPurchaseImagesV2UpdateInstance(_:)``
|
||||||
|
- ``inAppPurchaseImagesV2DeleteInstance(_:)``
|
||||||
- ``inAppPurchaseAppStoreReviewScreenshotsCreateInstance(_:)``
|
- ``inAppPurchaseAppStoreReviewScreenshotsCreateInstance(_:)``
|
||||||
- ``inAppPurchaseAppStoreReviewScreenshotsGetInstance(_:)``
|
- ``inAppPurchaseAppStoreReviewScreenshotsGetInstance(_:)``
|
||||||
- ``inAppPurchaseAppStoreReviewScreenshotsUpdateInstance(_:)``
|
- ``inAppPurchaseAppStoreReviewScreenshotsUpdateInstance(_:)``
|
||||||
@@ -660,6 +723,18 @@ The ``Client`` type conforms to this protocol and provides the concrete implemen
|
|||||||
- ``subscriptionsSubscriptionAvailabilityGetToOneRelated(_:)``
|
- ``subscriptionsSubscriptionAvailabilityGetToOneRelated(_:)``
|
||||||
- ``subscriptionsSubscriptionLocalizationsGetToManyRelated(_:)``
|
- ``subscriptionsSubscriptionLocalizationsGetToManyRelated(_:)``
|
||||||
- ``subscriptionsWinBackOffersGetToManyRelated(_:)``
|
- ``subscriptionsWinBackOffersGetToManyRelated(_:)``
|
||||||
|
- ``subscriptionsPlanAvailabilitiesGetToManyRelationship(_:)``
|
||||||
|
- ``subscriptionsPlanAvailabilitiesGetToManyRelated(_:)``
|
||||||
|
- ``subscriptionsVersionsGetToManyRelationship(_:)``
|
||||||
|
- ``subscriptionsVersionsGetToManyRelated(_:)``
|
||||||
|
- ``subscriptionVersionsCreateInstance(_:)``
|
||||||
|
- ``subscriptionVersionsGetInstance(_:)``
|
||||||
|
- ``subscriptionVersionsImageGetToOneRelationship(_:)``
|
||||||
|
- ``subscriptionVersionsImageGetToOneRelated(_:)``
|
||||||
|
- ``subscriptionVersionsImagesGetToManyRelationship(_:)``
|
||||||
|
- ``subscriptionVersionsImagesGetToManyRelated(_:)``
|
||||||
|
- ``subscriptionVersionsLocalizationsGetToManyRelationship(_:)``
|
||||||
|
- ``subscriptionVersionsLocalizationsGetToManyRelated(_:)``
|
||||||
- ``subscriptionGroupsCreateInstance(_:)``
|
- ``subscriptionGroupsCreateInstance(_:)``
|
||||||
- ``subscriptionGroupsGetInstance(_:)``
|
- ``subscriptionGroupsGetInstance(_:)``
|
||||||
- ``subscriptionGroupsUpdateInstance(_:)``
|
- ``subscriptionGroupsUpdateInstance(_:)``
|
||||||
@@ -668,19 +743,37 @@ The ``Client`` type conforms to this protocol and provides the concrete implemen
|
|||||||
- ``subscriptionGroupsSubscriptionsGetToManyRelationship(_:)``
|
- ``subscriptionGroupsSubscriptionsGetToManyRelationship(_:)``
|
||||||
- ``subscriptionGroupsSubscriptionGroupLocalizationsGetToManyRelated(_:)``
|
- ``subscriptionGroupsSubscriptionGroupLocalizationsGetToManyRelated(_:)``
|
||||||
- ``subscriptionGroupsSubscriptionsGetToManyRelated(_:)``
|
- ``subscriptionGroupsSubscriptionsGetToManyRelated(_:)``
|
||||||
|
- ``subscriptionGroupsVersionsGetToManyRelationship(_:)``
|
||||||
|
- ``subscriptionGroupsVersionsGetToManyRelated(_:)``
|
||||||
|
- ``subscriptionGroupVersionsCreateInstance(_:)``
|
||||||
|
- ``subscriptionGroupVersionsGetInstance(_:)``
|
||||||
|
- ``subscriptionGroupVersionsLocalizationsGetToManyRelationship(_:)``
|
||||||
|
- ``subscriptionGroupVersionsLocalizationsGetToManyRelated(_:)``
|
||||||
- ``subscriptionGroupLocalizationsCreateInstance(_:)``
|
- ``subscriptionGroupLocalizationsCreateInstance(_:)``
|
||||||
- ``subscriptionGroupLocalizationsGetInstance(_:)``
|
- ``subscriptionGroupLocalizationsGetInstance(_:)``
|
||||||
- ``subscriptionGroupLocalizationsUpdateInstance(_:)``
|
- ``subscriptionGroupLocalizationsUpdateInstance(_:)``
|
||||||
- ``subscriptionGroupLocalizationsDeleteInstance(_:)``
|
- ``subscriptionGroupLocalizationsDeleteInstance(_:)``
|
||||||
|
- ``subscriptionGroupLocalizationsV2CreateInstance(_:)``
|
||||||
|
- ``subscriptionGroupLocalizationsV2GetInstance(_:)``
|
||||||
|
- ``subscriptionGroupLocalizationsV2UpdateInstance(_:)``
|
||||||
|
- ``subscriptionGroupLocalizationsV2DeleteInstance(_:)``
|
||||||
- ``subscriptionGroupSubmissionsCreateInstance(_:)``
|
- ``subscriptionGroupSubmissionsCreateInstance(_:)``
|
||||||
- ``subscriptionLocalizationsCreateInstance(_:)``
|
- ``subscriptionLocalizationsCreateInstance(_:)``
|
||||||
- ``subscriptionLocalizationsGetInstance(_:)``
|
- ``subscriptionLocalizationsGetInstance(_:)``
|
||||||
- ``subscriptionLocalizationsUpdateInstance(_:)``
|
- ``subscriptionLocalizationsUpdateInstance(_:)``
|
||||||
- ``subscriptionLocalizationsDeleteInstance(_:)``
|
- ``subscriptionLocalizationsDeleteInstance(_:)``
|
||||||
|
- ``subscriptionLocalizationsV2CreateInstance(_:)``
|
||||||
|
- ``subscriptionLocalizationsV2GetInstance(_:)``
|
||||||
|
- ``subscriptionLocalizationsV2UpdateInstance(_:)``
|
||||||
|
- ``subscriptionLocalizationsV2DeleteInstance(_:)``
|
||||||
- ``subscriptionImagesCreateInstance(_:)``
|
- ``subscriptionImagesCreateInstance(_:)``
|
||||||
- ``subscriptionImagesGetInstance(_:)``
|
- ``subscriptionImagesGetInstance(_:)``
|
||||||
- ``subscriptionImagesUpdateInstance(_:)``
|
- ``subscriptionImagesUpdateInstance(_:)``
|
||||||
- ``subscriptionImagesDeleteInstance(_:)``
|
- ``subscriptionImagesDeleteInstance(_:)``
|
||||||
|
- ``subscriptionImagesV2CreateInstance(_:)``
|
||||||
|
- ``subscriptionImagesV2GetInstance(_:)``
|
||||||
|
- ``subscriptionImagesV2UpdateInstance(_:)``
|
||||||
|
- ``subscriptionImagesV2DeleteInstance(_:)``
|
||||||
- ``subscriptionAppStoreReviewScreenshotsCreateInstance(_:)``
|
- ``subscriptionAppStoreReviewScreenshotsCreateInstance(_:)``
|
||||||
- ``subscriptionAppStoreReviewScreenshotsGetInstance(_:)``
|
- ``subscriptionAppStoreReviewScreenshotsGetInstance(_:)``
|
||||||
- ``subscriptionAppStoreReviewScreenshotsUpdateInstance(_:)``
|
- ``subscriptionAppStoreReviewScreenshotsUpdateInstance(_:)``
|
||||||
@@ -689,6 +782,12 @@ The ``Client`` type conforms to this protocol and provides the concrete implemen
|
|||||||
- ``subscriptionAvailabilitiesGetInstance(_:)``
|
- ``subscriptionAvailabilitiesGetInstance(_:)``
|
||||||
- ``subscriptionAvailabilitiesAvailableTerritoriesGetToManyRelated(_:)``
|
- ``subscriptionAvailabilitiesAvailableTerritoriesGetToManyRelated(_:)``
|
||||||
- ``subscriptionAvailabilitiesAvailableTerritoriesGetToManyRelationship(_:)``
|
- ``subscriptionAvailabilitiesAvailableTerritoriesGetToManyRelationship(_:)``
|
||||||
|
- ``subscriptionPlanAvailabilitiesCreateInstance(_:)``
|
||||||
|
- ``subscriptionPlanAvailabilitiesGetInstance(_:)``
|
||||||
|
- ``subscriptionPlanAvailabilitiesUpdateInstance(_:)``
|
||||||
|
- ``subscriptionPlanAvailabilitiesAvailableTerritoriesGetToManyRelationship(_:)``
|
||||||
|
- ``subscriptionPlanAvailabilitiesAvailableTerritoriesReplaceToManyRelationship(_:)``
|
||||||
|
- ``subscriptionPlanAvailabilitiesAvailableTerritoriesGetToManyRelated(_:)``
|
||||||
- ``subscriptionGracePeriodsGetInstance(_:)``
|
- ``subscriptionGracePeriodsGetInstance(_:)``
|
||||||
- ``subscriptionGracePeriodsUpdateInstance(_:)``
|
- ``subscriptionGracePeriodsUpdateInstance(_:)``
|
||||||
- ``subscriptionIntroductoryOffersCreateInstance(_:)``
|
- ``subscriptionIntroductoryOffersCreateInstance(_:)``
|
||||||
@@ -713,6 +812,7 @@ The ``Client`` type conforms to this protocol and provides the concrete implemen
|
|||||||
- ``subscriptionPricePointsGetInstance(_:)``
|
- ``subscriptionPricePointsGetInstance(_:)``
|
||||||
- ``subscriptionPricePointsEqualizationsGetToManyRelated(_:)``
|
- ``subscriptionPricePointsEqualizationsGetToManyRelated(_:)``
|
||||||
- ``subscriptionPricePointsEqualizationsGetToManyRelationship(_:)``
|
- ``subscriptionPricePointsEqualizationsGetToManyRelationship(_:)``
|
||||||
|
- ``subscriptionPricePointsAdjustedEqualizationsGetToManyRelated(_:)``
|
||||||
- ``subscriptionPricesCreateInstance(_:)``
|
- ``subscriptionPricesCreateInstance(_:)``
|
||||||
- ``subscriptionPricesDeleteInstance(_:)``
|
- ``subscriptionPricesDeleteInstance(_:)``
|
||||||
- ``subscriptionPromotionalOffersCreateInstance(_:)``
|
- ``subscriptionPromotionalOffersCreateInstance(_:)``
|
||||||
@@ -773,6 +873,9 @@ The ``Client`` type conforms to this protocol and provides the concrete implemen
|
|||||||
- ``gameCenterDetailsGameCenterLeaderboardsV2ReplaceToManyRelationship(_:)``
|
- ``gameCenterDetailsGameCenterLeaderboardsV2ReplaceToManyRelationship(_:)``
|
||||||
- ``gameCenterDetailsLeaderboardReleasesGetToManyRelationship(_:)``
|
- ``gameCenterDetailsLeaderboardReleasesGetToManyRelationship(_:)``
|
||||||
- ``gameCenterDetailsLeaderboardSetReleasesGetToManyRelationship(_:)``
|
- ``gameCenterDetailsLeaderboardSetReleasesGetToManyRelationship(_:)``
|
||||||
|
- ``gameCenterDetailsBlockedPlayersGetToManyRelationship(_:)``
|
||||||
|
- ``gameCenterDetailsBlockedPlayersGetToManyRelated(_:)``
|
||||||
|
- ``gameCenterDetailPlayersUpdateInstance(_:)``
|
||||||
- ``gameCenterGroupsGetCollection(_:)``
|
- ``gameCenterGroupsGetCollection(_:)``
|
||||||
- ``gameCenterGroupsCreateInstance(_:)``
|
- ``gameCenterGroupsCreateInstance(_:)``
|
||||||
- ``gameCenterGroupsGetInstance(_:)``
|
- ``gameCenterGroupsGetInstance(_:)``
|
||||||
@@ -871,6 +974,9 @@ The ``Client`` type conforms to this protocol and provides the concrete implemen
|
|||||||
- ``gameCenterLeaderboardsV2ChallengeUpdateToOneRelationship(_:)``
|
- ``gameCenterLeaderboardsV2ChallengeUpdateToOneRelationship(_:)``
|
||||||
- ``gameCenterLeaderboardsV2VersionsGetToManyRelationship(_:)``
|
- ``gameCenterLeaderboardsV2VersionsGetToManyRelationship(_:)``
|
||||||
- ``gameCenterLeaderboardsV2VersionsGetToManyRelated(_:)``
|
- ``gameCenterLeaderboardsV2VersionsGetToManyRelated(_:)``
|
||||||
|
- ``gameCenterLeaderboardsV2GameCenterScoreModerationsGetToManyRelationship(_:)``
|
||||||
|
- ``gameCenterLeaderboardsV2GameCenterScoreModerationsGetToManyRelated(_:)``
|
||||||
|
- ``gameCenterScoreModerationsUpdateInstance(_:)``
|
||||||
- ``gameCenterLeaderboardVersionsV2CreateInstance(_:)``
|
- ``gameCenterLeaderboardVersionsV2CreateInstance(_:)``
|
||||||
- ``gameCenterLeaderboardVersionsV2GetInstance(_:)``
|
- ``gameCenterLeaderboardVersionsV2GetInstance(_:)``
|
||||||
- ``gameCenterLeaderboardVersionsV2LocalizationsGetToManyRelated(_:)``
|
- ``gameCenterLeaderboardVersionsV2LocalizationsGetToManyRelated(_:)``
|
||||||
|
|||||||
@@ -1,21 +1,19 @@
|
|||||||
# ``Client``
|
# ``Client``
|
||||||
|
|
||||||
The API client for performing HTTP operations against the App Store Connect API.
|
The App Store Connect API client.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The ``Client`` struct is the main entry point for interacting with the App Store Connect API. It conforms to ``APIProtocol`` and provides concrete implementations for all available API operations.
|
``Client`` implements every ``APIProtocol`` method by sending the request through its transport, after passing it through its middlewares.
|
||||||
|
|
||||||
### Creating a Client
|
Include ``BearerAuthMiddleware`` in `middlewares` to authenticate requests:
|
||||||
|
|
||||||
Create a ``Client`` by providing a server URL, a transport, and optionally a list of middlewares for authentication or request customization.
|
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
import ASConnectService
|
import ASConnectService
|
||||||
import OpenAPIURLSession
|
import OpenAPIURLSession
|
||||||
|
|
||||||
let client = Client(
|
let client = Client(
|
||||||
serverURL: try Servers.server1(),
|
serverURL: try Servers.Server1.url(),
|
||||||
transport: URLSessionTransport(),
|
transport: URLSessionTransport(),
|
||||||
middlewares: [
|
middlewares: [
|
||||||
BearerAuthMiddleware(token: yourJWTToken)
|
BearerAuthMiddleware(token: yourJWTToken)
|
||||||
@@ -23,12 +21,10 @@ let client = Client(
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Making API Calls
|
Each method takes either a single `Input` value or its `path`, `query`, `headers`, and `body` as separate arguments:
|
||||||
|
|
||||||
Each method on the ``Client`` corresponds to an HTTP endpoint defined in the App Store Connect API OpenAPI specification. Methods accept an `Input` value and return an `Output` value representing the response.
|
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
let response = try await client.appsGetCollection(.init())
|
let response = try await client.appsGetInstance(path: .init(id: appID))
|
||||||
```
|
```
|
||||||
|
|
||||||
## Topics
|
## Topics
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
# ``Components``
|
# ``Components``
|
||||||
|
|
||||||
Types generated from the components section of the OpenAPI document.
|
The data models of the App Store Connect API.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The ``Components`` namespace contains all the data models defined in the App Store Connect API OpenAPI specification. These types are organized within the ``Components/Schemas`` namespace and are used as request and response bodies across all API operations.
|
The models are in ``Components/Schemas``, and are used as request and response bodies. Each resource typically has:
|
||||||
|
|
||||||
Each resource type typically includes:
|
- A model, such as ``Components/Schemas/App``.
|
||||||
|
- A single-resource response, such as ``Components/Schemas/AppResponse``.
|
||||||
- A **model type** (e.g., `Components.Schemas.App`) representing the resource
|
- A collection response, such as ``Components/Schemas/AppsResponse``.
|
||||||
- A **response type** (e.g., `Components.Schemas.AppResponse`) for single-resource responses
|
- Create and update requests, such as ``Components/Schemas/AppUpdateRequest``.
|
||||||
- A **collection response type** (e.g., `Components.Schemas.AppsResponse`) for list responses
|
- Linkage responses for its relationships, such as ``Components/Schemas/AppBuildsLinkagesResponse``.
|
||||||
- **Request types** for create and update operations (e.g., `Components.Schemas.AppUpdateRequest`)
|
|
||||||
|
|
||||||
## Topics
|
## Topics
|
||||||
|
|
||||||
@@ -94,6 +93,7 @@ Each resource type typically includes:
|
|||||||
- ``Components/Schemas/AppMediaStateError``
|
- ``Components/Schemas/AppMediaStateError``
|
||||||
- ``Components/Schemas/AppMediaVideoState``
|
- ``Components/Schemas/AppMediaVideoState``
|
||||||
- ``Components/Schemas/AppPerfPowerMetricsLinkagesResponse``
|
- ``Components/Schemas/AppPerfPowerMetricsLinkagesResponse``
|
||||||
|
- ``Components/Schemas/AppPerformanceOverviewsLinkagesResponse``
|
||||||
- ``Components/Schemas/AppPreReleaseVersionsLinkagesResponse``
|
- ``Components/Schemas/AppPreReleaseVersionsLinkagesResponse``
|
||||||
- ``Components/Schemas/AppPricePointV3``
|
- ``Components/Schemas/AppPricePointV3``
|
||||||
- ``Components/Schemas/AppPricePointV3EqualizationsLinkagesResponse``
|
- ``Components/Schemas/AppPricePointV3EqualizationsLinkagesResponse``
|
||||||
@@ -156,6 +156,7 @@ Each resource type typically includes:
|
|||||||
- ``Components/Schemas/AppStoreVersionLocalizationAppPreviewSetsLinkagesResponse``
|
- ``Components/Schemas/AppStoreVersionLocalizationAppPreviewSetsLinkagesResponse``
|
||||||
- ``Components/Schemas/AppStoreVersionLocalizationAppScreenshotSetsLinkagesResponse``
|
- ``Components/Schemas/AppStoreVersionLocalizationAppScreenshotSetsLinkagesResponse``
|
||||||
- ``Components/Schemas/AppStoreVersionLocalizationCreateRequest``
|
- ``Components/Schemas/AppStoreVersionLocalizationCreateRequest``
|
||||||
|
- ``Components/Schemas/AppStoreVersionLocalizationPlacementsLinkagesResponse``
|
||||||
- ``Components/Schemas/AppStoreVersionLocalizationResponse``
|
- ``Components/Schemas/AppStoreVersionLocalizationResponse``
|
||||||
- ``Components/Schemas/AppStoreVersionLocalizationSearchKeywordsLinkagesRequest``
|
- ``Components/Schemas/AppStoreVersionLocalizationSearchKeywordsLinkagesRequest``
|
||||||
- ``Components/Schemas/AppStoreVersionLocalizationSearchKeywordsLinkagesResponse``
|
- ``Components/Schemas/AppStoreVersionLocalizationSearchKeywordsLinkagesResponse``
|
||||||
@@ -194,6 +195,7 @@ Each resource type typically includes:
|
|||||||
- ``Components/Schemas/AppStoreVersionExperimentTreatmentLocalizationAppPreviewSetsLinkagesResponse``
|
- ``Components/Schemas/AppStoreVersionExperimentTreatmentLocalizationAppPreviewSetsLinkagesResponse``
|
||||||
- ``Components/Schemas/AppStoreVersionExperimentTreatmentLocalizationAppScreenshotSetsLinkagesResponse``
|
- ``Components/Schemas/AppStoreVersionExperimentTreatmentLocalizationAppScreenshotSetsLinkagesResponse``
|
||||||
- ``Components/Schemas/AppStoreVersionExperimentTreatmentLocalizationCreateRequest``
|
- ``Components/Schemas/AppStoreVersionExperimentTreatmentLocalizationCreateRequest``
|
||||||
|
- ``Components/Schemas/AppStoreVersionExperimentTreatmentLocalizationPlacementsLinkagesResponse``
|
||||||
- ``Components/Schemas/AppStoreVersionExperimentTreatmentLocalizationResponse``
|
- ``Components/Schemas/AppStoreVersionExperimentTreatmentLocalizationResponse``
|
||||||
- ``Components/Schemas/AppStoreVersionExperimentTreatmentLocalizationsResponse``
|
- ``Components/Schemas/AppStoreVersionExperimentTreatmentLocalizationsResponse``
|
||||||
- ``Components/Schemas/AppStoreVersionExperimentTreatmentResponse``
|
- ``Components/Schemas/AppStoreVersionExperimentTreatmentResponse``
|
||||||
@@ -218,6 +220,7 @@ Each resource type typically includes:
|
|||||||
- ``Components/Schemas/AppCustomProductPageLocalizationAppScreenshotSetsLinkagesResponse``
|
- ``Components/Schemas/AppCustomProductPageLocalizationAppScreenshotSetsLinkagesResponse``
|
||||||
- ``Components/Schemas/AppCustomProductPageLocalizationCreateRequest``
|
- ``Components/Schemas/AppCustomProductPageLocalizationCreateRequest``
|
||||||
- ``Components/Schemas/AppCustomProductPageLocalizationInlineCreate``
|
- ``Components/Schemas/AppCustomProductPageLocalizationInlineCreate``
|
||||||
|
- ``Components/Schemas/AppCustomProductPageLocalizationPlacementsLinkagesResponse``
|
||||||
- ``Components/Schemas/AppCustomProductPageLocalizationResponse``
|
- ``Components/Schemas/AppCustomProductPageLocalizationResponse``
|
||||||
- ``Components/Schemas/AppCustomProductPageLocalizationSearchKeywordsLinkagesRequest``
|
- ``Components/Schemas/AppCustomProductPageLocalizationSearchKeywordsLinkagesRequest``
|
||||||
- ``Components/Schemas/AppCustomProductPageLocalizationSearchKeywordsLinkagesResponse``
|
- ``Components/Schemas/AppCustomProductPageLocalizationSearchKeywordsLinkagesResponse``
|
||||||
@@ -289,6 +292,7 @@ Each resource type typically includes:
|
|||||||
- ``Components/Schemas/AppEventLocalizationAppEventScreenshotsLinkagesResponse``
|
- ``Components/Schemas/AppEventLocalizationAppEventScreenshotsLinkagesResponse``
|
||||||
- ``Components/Schemas/AppEventLocalizationAppEventVideoClipsLinkagesResponse``
|
- ``Components/Schemas/AppEventLocalizationAppEventVideoClipsLinkagesResponse``
|
||||||
- ``Components/Schemas/AppEventLocalizationCreateRequest``
|
- ``Components/Schemas/AppEventLocalizationCreateRequest``
|
||||||
|
- ``Components/Schemas/AppEventLocalizationPlacementsLinkagesResponse``
|
||||||
- ``Components/Schemas/AppEventLocalizationResponse``
|
- ``Components/Schemas/AppEventLocalizationResponse``
|
||||||
- ``Components/Schemas/AppEventLocalizationUpdateRequest``
|
- ``Components/Schemas/AppEventLocalizationUpdateRequest``
|
||||||
- ``Components/Schemas/AppEventLocalizationsLinkagesResponse``
|
- ``Components/Schemas/AppEventLocalizationsLinkagesResponse``
|
||||||
@@ -337,6 +341,71 @@ Each resource type typically includes:
|
|||||||
- ``Components/Schemas/RoutingAppCoverageResponse``
|
- ``Components/Schemas/RoutingAppCoverageResponse``
|
||||||
- ``Components/Schemas/RoutingAppCoverageUpdateRequest``
|
- ``Components/Schemas/RoutingAppCoverageUpdateRequest``
|
||||||
|
|
||||||
|
### App Asset Library
|
||||||
|
|
||||||
|
- ``Components/Schemas/AppAssetLibrary``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryAssetCategory``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryAssetState``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryDisplayClass``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryFeature``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryImage``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryImageAcceptedAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryImageApprovedAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryImageArchivedAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryImageAwaitingUploadAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryImageCommonAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryImageCreateRequest``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryImageFailedAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryImageInReviewAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryImagePlacementsLinkagesResponse``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryImageReadyForReviewAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryImageRejectedAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryImageResponse``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryImageUpdateRequest``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryImageUploadCompleteAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryImageWaitingForReviewAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryImagesLinkagesResponse``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryImagesResponse``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryLinkageResponse``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryMediaType``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryPlacement``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryPlacementCommonAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryPlacementCommonRelationships``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryPlacementCreateRequest``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryPlacementImageRelationships``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryPlacementOrderingRequest``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryPlacementOrderingRequestCreateRequest``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryPlacementOrderingRequestResponse``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryPlacementPlatform``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryPlacementResponse``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryPlacementState``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryPlacementType``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryPlacementVideoAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryPlacementVideoRelationships``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryPlacementsResponse``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryRefDataResponse``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryRefDatum``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryRefDatumResponse``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryResponse``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryVideo``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryVideoAcceptedAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryVideoApprovedAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryVideoArchivedAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryVideoAwaitingUploadAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryVideoCommonAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryVideoCreateRequest``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryVideoFailedAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryVideoInReviewAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryVideoPlacementsLinkagesResponse``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryVideoReadyForReviewAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryVideoRejectedAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryVideoResponse``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryVideoUpdateRequest``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryVideoUploadCompleteAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryVideoWaitingForReviewAttributes``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryVideosLinkagesResponse``
|
||||||
|
- ``Components/Schemas/AppAssetLibraryVideosResponse``
|
||||||
|
|
||||||
### App Encryption
|
### App Encryption
|
||||||
|
|
||||||
- ``Components/Schemas/AppEncryptionDeclaration``
|
- ``Components/Schemas/AppEncryptionDeclaration``
|
||||||
@@ -573,12 +642,22 @@ Each resource type typically includes:
|
|||||||
- ``Components/Schemas/InAppPurchaseImageCreateRequest``
|
- ``Components/Schemas/InAppPurchaseImageCreateRequest``
|
||||||
- ``Components/Schemas/InAppPurchaseImageResponse``
|
- ``Components/Schemas/InAppPurchaseImageResponse``
|
||||||
- ``Components/Schemas/InAppPurchaseImageUpdateRequest``
|
- ``Components/Schemas/InAppPurchaseImageUpdateRequest``
|
||||||
|
- ``Components/Schemas/InAppPurchaseImageV2``
|
||||||
|
- ``Components/Schemas/InAppPurchaseImageV2CreateRequest``
|
||||||
|
- ``Components/Schemas/InAppPurchaseImageV2Response``
|
||||||
|
- ``Components/Schemas/InAppPurchaseImageV2UpdateRequest``
|
||||||
- ``Components/Schemas/InAppPurchaseImagesResponse``
|
- ``Components/Schemas/InAppPurchaseImagesResponse``
|
||||||
|
- ``Components/Schemas/InAppPurchaseImagesV2Response``
|
||||||
- ``Components/Schemas/InAppPurchaseLocalization``
|
- ``Components/Schemas/InAppPurchaseLocalization``
|
||||||
- ``Components/Schemas/InAppPurchaseLocalizationCreateRequest``
|
- ``Components/Schemas/InAppPurchaseLocalizationCreateRequest``
|
||||||
- ``Components/Schemas/InAppPurchaseLocalizationResponse``
|
- ``Components/Schemas/InAppPurchaseLocalizationResponse``
|
||||||
- ``Components/Schemas/InAppPurchaseLocalizationUpdateRequest``
|
- ``Components/Schemas/InAppPurchaseLocalizationUpdateRequest``
|
||||||
|
- ``Components/Schemas/InAppPurchaseLocalizationV2``
|
||||||
|
- ``Components/Schemas/InAppPurchaseLocalizationV2CreateRequest``
|
||||||
|
- ``Components/Schemas/InAppPurchaseLocalizationV2Response``
|
||||||
|
- ``Components/Schemas/InAppPurchaseLocalizationV2UpdateRequest``
|
||||||
- ``Components/Schemas/InAppPurchaseLocalizationsResponse``
|
- ``Components/Schemas/InAppPurchaseLocalizationsResponse``
|
||||||
|
- ``Components/Schemas/InAppPurchaseLocalizationsV2Response``
|
||||||
- ``Components/Schemas/InAppPurchaseOfferCode``
|
- ``Components/Schemas/InAppPurchaseOfferCode``
|
||||||
- ``Components/Schemas/InAppPurchaseOfferCodeCreateRequest``
|
- ``Components/Schemas/InAppPurchaseOfferCodeCreateRequest``
|
||||||
- ``Components/Schemas/InAppPurchaseOfferCodeCustomCode``
|
- ``Components/Schemas/InAppPurchaseOfferCodeCustomCode``
|
||||||
@@ -633,6 +712,14 @@ Each resource type typically includes:
|
|||||||
- ``Components/Schemas/InAppPurchaseV2PromotedPurchaseLinkageResponse``
|
- ``Components/Schemas/InAppPurchaseV2PromotedPurchaseLinkageResponse``
|
||||||
- ``Components/Schemas/InAppPurchaseV2Response``
|
- ``Components/Schemas/InAppPurchaseV2Response``
|
||||||
- ``Components/Schemas/InAppPurchaseV2UpdateRequest``
|
- ``Components/Schemas/InAppPurchaseV2UpdateRequest``
|
||||||
|
- ``Components/Schemas/InAppPurchaseV2VersionsLinkagesResponse``
|
||||||
|
- ``Components/Schemas/InAppPurchaseVersion``
|
||||||
|
- ``Components/Schemas/InAppPurchaseVersionCreateRequest``
|
||||||
|
- ``Components/Schemas/InAppPurchaseVersionImageLinkageResponse``
|
||||||
|
- ``Components/Schemas/InAppPurchaseVersionImagesLinkagesResponse``
|
||||||
|
- ``Components/Schemas/InAppPurchaseVersionLocalizationsLinkagesResponse``
|
||||||
|
- ``Components/Schemas/InAppPurchaseVersionResponse``
|
||||||
|
- ``Components/Schemas/InAppPurchaseVersionsResponse``
|
||||||
- ``Components/Schemas/InAppPurchasesResponse``
|
- ``Components/Schemas/InAppPurchasesResponse``
|
||||||
- ``Components/Schemas/InAppPurchasesV2Response``
|
- ``Components/Schemas/InAppPurchasesV2Response``
|
||||||
- ``Components/Schemas/OfferCodeEnvironment``
|
- ``Components/Schemas/OfferCodeEnvironment``
|
||||||
@@ -666,7 +753,12 @@ Each resource type typically includes:
|
|||||||
- ``Components/Schemas/SubscriptionGroupLocalizationCreateRequest``
|
- ``Components/Schemas/SubscriptionGroupLocalizationCreateRequest``
|
||||||
- ``Components/Schemas/SubscriptionGroupLocalizationResponse``
|
- ``Components/Schemas/SubscriptionGroupLocalizationResponse``
|
||||||
- ``Components/Schemas/SubscriptionGroupLocalizationUpdateRequest``
|
- ``Components/Schemas/SubscriptionGroupLocalizationUpdateRequest``
|
||||||
|
- ``Components/Schemas/SubscriptionGroupLocalizationV2``
|
||||||
|
- ``Components/Schemas/SubscriptionGroupLocalizationV2CreateRequest``
|
||||||
|
- ``Components/Schemas/SubscriptionGroupLocalizationV2Response``
|
||||||
|
- ``Components/Schemas/SubscriptionGroupLocalizationV2UpdateRequest``
|
||||||
- ``Components/Schemas/SubscriptionGroupLocalizationsResponse``
|
- ``Components/Schemas/SubscriptionGroupLocalizationsResponse``
|
||||||
|
- ``Components/Schemas/SubscriptionGroupLocalizationsV2Response``
|
||||||
- ``Components/Schemas/SubscriptionGroupResponse``
|
- ``Components/Schemas/SubscriptionGroupResponse``
|
||||||
- ``Components/Schemas/SubscriptionGroupSubmission``
|
- ``Components/Schemas/SubscriptionGroupSubmission``
|
||||||
- ``Components/Schemas/SubscriptionGroupSubmissionCreateRequest``
|
- ``Components/Schemas/SubscriptionGroupSubmissionCreateRequest``
|
||||||
@@ -674,13 +766,24 @@ Each resource type typically includes:
|
|||||||
- ``Components/Schemas/SubscriptionGroupSubscriptionGroupLocalizationsLinkagesResponse``
|
- ``Components/Schemas/SubscriptionGroupSubscriptionGroupLocalizationsLinkagesResponse``
|
||||||
- ``Components/Schemas/SubscriptionGroupSubscriptionsLinkagesResponse``
|
- ``Components/Schemas/SubscriptionGroupSubscriptionsLinkagesResponse``
|
||||||
- ``Components/Schemas/SubscriptionGroupUpdateRequest``
|
- ``Components/Schemas/SubscriptionGroupUpdateRequest``
|
||||||
|
- ``Components/Schemas/SubscriptionGroupVersion``
|
||||||
|
- ``Components/Schemas/SubscriptionGroupVersionCreateRequest``
|
||||||
|
- ``Components/Schemas/SubscriptionGroupVersionLocalizationsLinkagesResponse``
|
||||||
|
- ``Components/Schemas/SubscriptionGroupVersionResponse``
|
||||||
|
- ``Components/Schemas/SubscriptionGroupVersionsLinkagesResponse``
|
||||||
|
- ``Components/Schemas/SubscriptionGroupVersionsResponse``
|
||||||
- ``Components/Schemas/SubscriptionGroupsResponse``
|
- ``Components/Schemas/SubscriptionGroupsResponse``
|
||||||
- ``Components/Schemas/SubscriptionImage``
|
- ``Components/Schemas/SubscriptionImage``
|
||||||
- ``Components/Schemas/SubscriptionImageCreateRequest``
|
- ``Components/Schemas/SubscriptionImageCreateRequest``
|
||||||
- ``Components/Schemas/SubscriptionImageResponse``
|
- ``Components/Schemas/SubscriptionImageResponse``
|
||||||
- ``Components/Schemas/SubscriptionImageUpdateRequest``
|
- ``Components/Schemas/SubscriptionImageUpdateRequest``
|
||||||
|
- ``Components/Schemas/SubscriptionImageV2``
|
||||||
|
- ``Components/Schemas/SubscriptionImageV2CreateRequest``
|
||||||
|
- ``Components/Schemas/SubscriptionImageV2Response``
|
||||||
|
- ``Components/Schemas/SubscriptionImageV2UpdateRequest``
|
||||||
- ``Components/Schemas/SubscriptionImagesLinkagesResponse``
|
- ``Components/Schemas/SubscriptionImagesLinkagesResponse``
|
||||||
- ``Components/Schemas/SubscriptionImagesResponse``
|
- ``Components/Schemas/SubscriptionImagesResponse``
|
||||||
|
- ``Components/Schemas/SubscriptionImagesV2Response``
|
||||||
- ``Components/Schemas/SubscriptionIntroductoryOffer``
|
- ``Components/Schemas/SubscriptionIntroductoryOffer``
|
||||||
- ``Components/Schemas/SubscriptionIntroductoryOfferCreateRequest``
|
- ``Components/Schemas/SubscriptionIntroductoryOfferCreateRequest``
|
||||||
- ``Components/Schemas/SubscriptionIntroductoryOfferInlineCreate``
|
- ``Components/Schemas/SubscriptionIntroductoryOfferInlineCreate``
|
||||||
@@ -693,7 +796,12 @@ Each resource type typically includes:
|
|||||||
- ``Components/Schemas/SubscriptionLocalizationCreateRequest``
|
- ``Components/Schemas/SubscriptionLocalizationCreateRequest``
|
||||||
- ``Components/Schemas/SubscriptionLocalizationResponse``
|
- ``Components/Schemas/SubscriptionLocalizationResponse``
|
||||||
- ``Components/Schemas/SubscriptionLocalizationUpdateRequest``
|
- ``Components/Schemas/SubscriptionLocalizationUpdateRequest``
|
||||||
|
- ``Components/Schemas/SubscriptionLocalizationV2``
|
||||||
|
- ``Components/Schemas/SubscriptionLocalizationV2CreateRequest``
|
||||||
|
- ``Components/Schemas/SubscriptionLocalizationV2Response``
|
||||||
|
- ``Components/Schemas/SubscriptionLocalizationV2UpdateRequest``
|
||||||
- ``Components/Schemas/SubscriptionLocalizationsResponse``
|
- ``Components/Schemas/SubscriptionLocalizationsResponse``
|
||||||
|
- ``Components/Schemas/SubscriptionLocalizationsV2Response``
|
||||||
- ``Components/Schemas/SubscriptionOfferCode``
|
- ``Components/Schemas/SubscriptionOfferCode``
|
||||||
- ``Components/Schemas/SubscriptionOfferCodeCreateRequest``
|
- ``Components/Schemas/SubscriptionOfferCodeCreateRequest``
|
||||||
- ``Components/Schemas/SubscriptionOfferCodeCustomCode``
|
- ``Components/Schemas/SubscriptionOfferCodeCustomCode``
|
||||||
@@ -721,6 +829,15 @@ Each resource type typically includes:
|
|||||||
- ``Components/Schemas/SubscriptionOfferDuration``
|
- ``Components/Schemas/SubscriptionOfferDuration``
|
||||||
- ``Components/Schemas/SubscriptionOfferEligibility``
|
- ``Components/Schemas/SubscriptionOfferEligibility``
|
||||||
- ``Components/Schemas/SubscriptionOfferMode``
|
- ``Components/Schemas/SubscriptionOfferMode``
|
||||||
|
- ``Components/Schemas/SubscriptionPlanAvailabilitiesLinkagesResponse``
|
||||||
|
- ``Components/Schemas/SubscriptionPlanAvailabilitiesResponse``
|
||||||
|
- ``Components/Schemas/SubscriptionPlanAvailability``
|
||||||
|
- ``Components/Schemas/SubscriptionPlanAvailabilityAvailableTerritoriesLinkagesRequest``
|
||||||
|
- ``Components/Schemas/SubscriptionPlanAvailabilityAvailableTerritoriesLinkagesResponse``
|
||||||
|
- ``Components/Schemas/SubscriptionPlanAvailabilityCreateRequest``
|
||||||
|
- ``Components/Schemas/SubscriptionPlanAvailabilityResponse``
|
||||||
|
- ``Components/Schemas/SubscriptionPlanAvailabilityUpdateRequest``
|
||||||
|
- ``Components/Schemas/SubscriptionPlanType``
|
||||||
- ``Components/Schemas/SubscriptionPrice``
|
- ``Components/Schemas/SubscriptionPrice``
|
||||||
- ``Components/Schemas/SubscriptionPriceCreateRequest``
|
- ``Components/Schemas/SubscriptionPriceCreateRequest``
|
||||||
- ``Components/Schemas/SubscriptionPriceInlineCreate``
|
- ``Components/Schemas/SubscriptionPriceInlineCreate``
|
||||||
@@ -754,6 +871,14 @@ Each resource type typically includes:
|
|||||||
- ``Components/Schemas/SubscriptionSubscriptionAvailabilityLinkageResponse``
|
- ``Components/Schemas/SubscriptionSubscriptionAvailabilityLinkageResponse``
|
||||||
- ``Components/Schemas/SubscriptionSubscriptionLocalizationsLinkagesResponse``
|
- ``Components/Schemas/SubscriptionSubscriptionLocalizationsLinkagesResponse``
|
||||||
- ``Components/Schemas/SubscriptionUpdateRequest``
|
- ``Components/Schemas/SubscriptionUpdateRequest``
|
||||||
|
- ``Components/Schemas/SubscriptionVersion``
|
||||||
|
- ``Components/Schemas/SubscriptionVersionCreateRequest``
|
||||||
|
- ``Components/Schemas/SubscriptionVersionImageLinkageResponse``
|
||||||
|
- ``Components/Schemas/SubscriptionVersionImagesLinkagesResponse``
|
||||||
|
- ``Components/Schemas/SubscriptionVersionLocalizationsLinkagesResponse``
|
||||||
|
- ``Components/Schemas/SubscriptionVersionResponse``
|
||||||
|
- ``Components/Schemas/SubscriptionVersionsLinkagesResponse``
|
||||||
|
- ``Components/Schemas/SubscriptionVersionsResponse``
|
||||||
- ``Components/Schemas/SubscriptionWinBackOffersLinkagesResponse``
|
- ``Components/Schemas/SubscriptionWinBackOffersLinkagesResponse``
|
||||||
- ``Components/Schemas/SubscriptionsResponse``
|
- ``Components/Schemas/SubscriptionsResponse``
|
||||||
- ``Components/Schemas/WinBackOffer``
|
- ``Components/Schemas/WinBackOffer``
|
||||||
@@ -888,6 +1013,7 @@ Each resource type typically includes:
|
|||||||
- ``Components/Schemas/GameCenterDetail``
|
- ``Components/Schemas/GameCenterDetail``
|
||||||
- ``Components/Schemas/GameCenterDetailAchievementReleasesLinkagesResponse``
|
- ``Components/Schemas/GameCenterDetailAchievementReleasesLinkagesResponse``
|
||||||
- ``Components/Schemas/GameCenterDetailActivityReleasesLinkagesResponse``
|
- ``Components/Schemas/GameCenterDetailActivityReleasesLinkagesResponse``
|
||||||
|
- ``Components/Schemas/GameCenterDetailBlockedPlayersLinkagesResponse``
|
||||||
- ``Components/Schemas/GameCenterDetailChallengeReleasesLinkagesResponse``
|
- ``Components/Schemas/GameCenterDetailChallengeReleasesLinkagesResponse``
|
||||||
- ``Components/Schemas/GameCenterDetailChallengesMinimumPlatformVersionsLinkagesRequest``
|
- ``Components/Schemas/GameCenterDetailChallengesMinimumPlatformVersionsLinkagesRequest``
|
||||||
- ``Components/Schemas/GameCenterDetailCreateRequest``
|
- ``Components/Schemas/GameCenterDetailCreateRequest``
|
||||||
@@ -909,6 +1035,10 @@ Each resource type typically includes:
|
|||||||
- ``Components/Schemas/GameCenterDetailGameCenterLeaderboardsV2LinkagesResponse``
|
- ``Components/Schemas/GameCenterDetailGameCenterLeaderboardsV2LinkagesResponse``
|
||||||
- ``Components/Schemas/GameCenterDetailLeaderboardReleasesLinkagesResponse``
|
- ``Components/Schemas/GameCenterDetailLeaderboardReleasesLinkagesResponse``
|
||||||
- ``Components/Schemas/GameCenterDetailLeaderboardSetReleasesLinkagesResponse``
|
- ``Components/Schemas/GameCenterDetailLeaderboardSetReleasesLinkagesResponse``
|
||||||
|
- ``Components/Schemas/GameCenterDetailPlayer``
|
||||||
|
- ``Components/Schemas/GameCenterDetailPlayerResponse``
|
||||||
|
- ``Components/Schemas/GameCenterDetailPlayerUpdateRequest``
|
||||||
|
- ``Components/Schemas/GameCenterDetailPlayersResponse``
|
||||||
- ``Components/Schemas/GameCenterDetailResponse``
|
- ``Components/Schemas/GameCenterDetailResponse``
|
||||||
- ``Components/Schemas/GameCenterDetailUpdateRequest``
|
- ``Components/Schemas/GameCenterDetailUpdateRequest``
|
||||||
- ``Components/Schemas/GameCenterDetailsResponse``
|
- ``Components/Schemas/GameCenterDetailsResponse``
|
||||||
@@ -1034,6 +1164,7 @@ Each resource type typically includes:
|
|||||||
- ``Components/Schemas/GameCenterLeaderboardV2ActivityLinkageRequest``
|
- ``Components/Schemas/GameCenterLeaderboardV2ActivityLinkageRequest``
|
||||||
- ``Components/Schemas/GameCenterLeaderboardV2ChallengeLinkageRequest``
|
- ``Components/Schemas/GameCenterLeaderboardV2ChallengeLinkageRequest``
|
||||||
- ``Components/Schemas/GameCenterLeaderboardV2CreateRequest``
|
- ``Components/Schemas/GameCenterLeaderboardV2CreateRequest``
|
||||||
|
- ``Components/Schemas/GameCenterLeaderboardV2GameCenterScoreModerationsLinkagesResponse``
|
||||||
- ``Components/Schemas/GameCenterLeaderboardV2Response``
|
- ``Components/Schemas/GameCenterLeaderboardV2Response``
|
||||||
- ``Components/Schemas/GameCenterLeaderboardV2UpdateRequest``
|
- ``Components/Schemas/GameCenterLeaderboardV2UpdateRequest``
|
||||||
- ``Components/Schemas/GameCenterLeaderboardV2VersionsLinkagesResponse``
|
- ``Components/Schemas/GameCenterLeaderboardV2VersionsLinkagesResponse``
|
||||||
@@ -1086,6 +1217,10 @@ Each resource type typically includes:
|
|||||||
- ``Components/Schemas/GameCenterPlayerAchievementSubmission``
|
- ``Components/Schemas/GameCenterPlayerAchievementSubmission``
|
||||||
- ``Components/Schemas/GameCenterPlayerAchievementSubmissionCreateRequest``
|
- ``Components/Schemas/GameCenterPlayerAchievementSubmissionCreateRequest``
|
||||||
- ``Components/Schemas/GameCenterPlayerAchievementSubmissionResponse``
|
- ``Components/Schemas/GameCenterPlayerAchievementSubmissionResponse``
|
||||||
|
- ``Components/Schemas/GameCenterScoreModeration``
|
||||||
|
- ``Components/Schemas/GameCenterScoreModerationResponse``
|
||||||
|
- ``Components/Schemas/GameCenterScoreModerationUpdateRequest``
|
||||||
|
- ``Components/Schemas/GameCenterScoreModerationsResponse``
|
||||||
- ``Components/Schemas/GameCenterVersionState``
|
- ``Components/Schemas/GameCenterVersionState``
|
||||||
|
|
||||||
### Users and Roles
|
### Users and Roles
|
||||||
@@ -1270,14 +1405,17 @@ Each resource type typically includes:
|
|||||||
- ``Components/Schemas/DiagnosticInsightType``
|
- ``Components/Schemas/DiagnosticInsightType``
|
||||||
- ``Components/Schemas/DiagnosticLog``
|
- ``Components/Schemas/DiagnosticLog``
|
||||||
- ``Components/Schemas/DiagnosticLogCallStackNode``
|
- ``Components/Schemas/DiagnosticLogCallStackNode``
|
||||||
|
- ``Components/Schemas/DiagnosticLogs``
|
||||||
- ``Components/Schemas/DiagnosticSignature``
|
- ``Components/Schemas/DiagnosticSignature``
|
||||||
- ``Components/Schemas/DiagnosticSignatureLogsLinkagesResponse``
|
- ``Components/Schemas/DiagnosticSignatureLogsLinkagesResponse``
|
||||||
- ``Components/Schemas/DiagnosticSignaturesResponse``
|
- ``Components/Schemas/DiagnosticSignaturesResponse``
|
||||||
- ``Components/Schemas/MetricCategory``
|
- ``Components/Schemas/MetricCategory``
|
||||||
- ``Components/Schemas/MetricsInsight``
|
- ``Components/Schemas/MetricsInsight``
|
||||||
- ``Components/Schemas/PerfPowerMetric``
|
- ``Components/Schemas/PerfPowerMetric``
|
||||||
- ``Components/Schemas/diagnosticLogs``
|
- ``Components/Schemas/PerformanceOverview``
|
||||||
- ``Components/Schemas/xcodeMetrics``
|
- ``Components/Schemas/PerformanceSignature``
|
||||||
|
- ``Components/Schemas/XcodeMetrics``
|
||||||
|
- ``Components/Schemas/XcodeOverview``
|
||||||
|
|
||||||
### Sandbox Testing
|
### Sandbox Testing
|
||||||
|
|
||||||
@@ -1410,6 +1548,7 @@ Each resource type typically includes:
|
|||||||
- ``Components/Schemas/BrazilAgeRating``
|
- ``Components/Schemas/BrazilAgeRating``
|
||||||
- ``Components/Schemas/ChecksumAlgorithm``
|
- ``Components/Schemas/ChecksumAlgorithm``
|
||||||
- ``Components/Schemas/Checksums``
|
- ``Components/Schemas/Checksums``
|
||||||
|
- ``Components/Schemas/Csv``
|
||||||
- ``Components/Schemas/DeliveryFileUploadOperation``
|
- ``Components/Schemas/DeliveryFileUploadOperation``
|
||||||
- ``Components/Schemas/DocumentLinks``
|
- ``Components/Schemas/DocumentLinks``
|
||||||
- ``Components/Schemas/ErrorLinks``
|
- ``Components/Schemas/ErrorLinks``
|
||||||
@@ -1417,6 +1556,7 @@ Each resource type typically includes:
|
|||||||
- ``Components/Schemas/ErrorSourceParameter``
|
- ``Components/Schemas/ErrorSourceParameter``
|
||||||
- ``Components/Schemas/ErrorSourcePointer``
|
- ``Components/Schemas/ErrorSourcePointer``
|
||||||
- ``Components/Schemas/FileLocation``
|
- ``Components/Schemas/FileLocation``
|
||||||
|
- ``Components/Schemas/Gzip``
|
||||||
- ``Components/Schemas/HttpHeader``
|
- ``Components/Schemas/HttpHeader``
|
||||||
- ``Components/Schemas/IconAssetType``
|
- ``Components/Schemas/IconAssetType``
|
||||||
- ``Components/Schemas/ImageAsset``
|
- ``Components/Schemas/ImageAsset``
|
||||||
@@ -1434,5 +1574,3 @@ Each resource type typically includes:
|
|||||||
- ``Components/Schemas/StateDetail``
|
- ``Components/Schemas/StateDetail``
|
||||||
- ``Components/Schemas/StringToStringMap``
|
- ``Components/Schemas/StringToStringMap``
|
||||||
- ``Components/Schemas/UploadOperation``
|
- ``Components/Schemas/UploadOperation``
|
||||||
- ``Components/Schemas/csv``
|
|
||||||
- ``Components/Schemas/gzip``
|
|
||||||
@@ -1,18 +1,14 @@
|
|||||||
# ``Operations``
|
# ``Operations``
|
||||||
|
|
||||||
API operations, with input and output types, generated from the paths section of the OpenAPI document.
|
The request and response types of each API operation.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The ``Operations`` namespace contains a nested enum for each API operation defined in the App Store Connect API OpenAPI specification. Each operation enum encapsulates the `Input` and `Output` types for that specific endpoint.
|
Each operation is an enum, named after its ``APIProtocol`` method, that contains:
|
||||||
|
|
||||||
Each operation typically provides:
|
- `Input`: The request, split into `path`, `query`, `headers`, and `body` as the endpoint requires.
|
||||||
|
- `Output`: The response, with a case for each documented status, such as `ok`, and `undocumented(statusCode:_:)` for any other.
|
||||||
- An **`Input`** struct containing the request `Path`, `Query`, `Headers`, and `Body` types
|
- `id`: The operation identifier in the OpenAPI document, such as `apps_getCollection`.
|
||||||
- An **`Output`** enum with cases for each possible HTTP response status code
|
|
||||||
- An **`id`** string constant with the operation identifier
|
|
||||||
|
|
||||||
These types are used by the ``APIProtocol`` methods and the ``Client`` implementation.
|
|
||||||
|
|
||||||
## Topics
|
## Topics
|
||||||
|
|
||||||
@@ -104,6 +100,9 @@ These types are used by the ``APIProtocol`` methods and the ``Client`` implement
|
|||||||
- ``Operations/AppsSubscriptionGracePeriodGetToOneRelated``
|
- ``Operations/AppsSubscriptionGracePeriodGetToOneRelated``
|
||||||
- ``Operations/AppsSubscriptionGroupsGetToManyRelated``
|
- ``Operations/AppsSubscriptionGroupsGetToManyRelated``
|
||||||
- ``Operations/AppsWebhooksGetToManyRelated``
|
- ``Operations/AppsWebhooksGetToManyRelated``
|
||||||
|
- ``Operations/AppsAssetLibraryGetToOneRelationship``
|
||||||
|
- ``Operations/AppsAssetLibraryGetToOneRelated``
|
||||||
|
- ``Operations/AppsPerformanceOverviewsGetToManyRelated``
|
||||||
- ``Operations/AppInfosGetInstance``
|
- ``Operations/AppInfosGetInstance``
|
||||||
- ``Operations/AppInfosUpdateInstance``
|
- ``Operations/AppInfosUpdateInstance``
|
||||||
- ``Operations/AppInfosAgeRatingDeclarationGetToOneRelated``
|
- ``Operations/AppInfosAgeRatingDeclarationGetToOneRelated``
|
||||||
@@ -205,6 +204,8 @@ These types are used by the ``APIProtocol`` methods and the ``Client`` implement
|
|||||||
- ``Operations/AppStoreVersionLocalizationsSearchKeywordsCreateToManyRelationship``
|
- ``Operations/AppStoreVersionLocalizationsSearchKeywordsCreateToManyRelationship``
|
||||||
- ``Operations/AppStoreVersionLocalizationsSearchKeywordsDeleteToManyRelationship``
|
- ``Operations/AppStoreVersionLocalizationsSearchKeywordsDeleteToManyRelationship``
|
||||||
- ``Operations/AppStoreVersionLocalizationsSearchKeywordsGetToManyRelated``
|
- ``Operations/AppStoreVersionLocalizationsSearchKeywordsGetToManyRelated``
|
||||||
|
- ``Operations/AppStoreVersionLocalizationsPlacementsGetToManyRelationship``
|
||||||
|
- ``Operations/AppStoreVersionLocalizationsPlacementsGetToManyRelated``
|
||||||
- ``Operations/AppStoreVersionPhasedReleasesCreateInstance``
|
- ``Operations/AppStoreVersionPhasedReleasesCreateInstance``
|
||||||
- ``Operations/AppStoreVersionPhasedReleasesUpdateInstance``
|
- ``Operations/AppStoreVersionPhasedReleasesUpdateInstance``
|
||||||
- ``Operations/AppStoreVersionPhasedReleasesDeleteInstance``
|
- ``Operations/AppStoreVersionPhasedReleasesDeleteInstance``
|
||||||
@@ -239,6 +240,8 @@ These types are used by the ``APIProtocol`` methods and the ``Client`` implement
|
|||||||
- ``Operations/AppStoreVersionExperimentTreatmentLocalizationsAppScreenshotSetsGetToManyRelated``
|
- ``Operations/AppStoreVersionExperimentTreatmentLocalizationsAppScreenshotSetsGetToManyRelated``
|
||||||
- ``Operations/AppStoreVersionExperimentTreatmentLocalizationsAppPreviewSetsGetToManyRelationship``
|
- ``Operations/AppStoreVersionExperimentTreatmentLocalizationsAppPreviewSetsGetToManyRelationship``
|
||||||
- ``Operations/AppStoreVersionExperimentTreatmentLocalizationsAppScreenshotSetsGetToManyRelationship``
|
- ``Operations/AppStoreVersionExperimentTreatmentLocalizationsAppScreenshotSetsGetToManyRelationship``
|
||||||
|
- ``Operations/AppStoreVersionExperimentTreatmentLocalizationsPlacementsGetToManyRelationship``
|
||||||
|
- ``Operations/AppStoreVersionExperimentTreatmentLocalizationsPlacementsGetToManyRelated``
|
||||||
|
|
||||||
### App Custom Product Pages
|
### App Custom Product Pages
|
||||||
|
|
||||||
@@ -265,6 +268,8 @@ These types are used by the ``APIProtocol`` methods and the ``Client`` implement
|
|||||||
- ``Operations/AppCustomProductPageLocalizationsSearchKeywordsCreateToManyRelationship``
|
- ``Operations/AppCustomProductPageLocalizationsSearchKeywordsCreateToManyRelationship``
|
||||||
- ``Operations/AppCustomProductPageLocalizationsSearchKeywordsDeleteToManyRelationship``
|
- ``Operations/AppCustomProductPageLocalizationsSearchKeywordsDeleteToManyRelationship``
|
||||||
- ``Operations/AppCustomProductPageLocalizationsSearchKeywordsGetToManyRelated``
|
- ``Operations/AppCustomProductPageLocalizationsSearchKeywordsGetToManyRelated``
|
||||||
|
- ``Operations/AppCustomProductPageLocalizationsPlacementsGetToManyRelationship``
|
||||||
|
- ``Operations/AppCustomProductPageLocalizationsPlacementsGetToManyRelated``
|
||||||
|
|
||||||
### App Clips
|
### App Clips
|
||||||
|
|
||||||
@@ -320,6 +325,8 @@ These types are used by the ``APIProtocol`` methods and the ``Client`` implement
|
|||||||
- ``Operations/AppEventLocalizationsAppEventVideoClipsGetToManyRelated``
|
- ``Operations/AppEventLocalizationsAppEventVideoClipsGetToManyRelated``
|
||||||
- ``Operations/AppEventLocalizationsAppEventScreenshotsGetToManyRelationship``
|
- ``Operations/AppEventLocalizationsAppEventScreenshotsGetToManyRelationship``
|
||||||
- ``Operations/AppEventLocalizationsAppEventVideoClipsGetToManyRelationship``
|
- ``Operations/AppEventLocalizationsAppEventVideoClipsGetToManyRelationship``
|
||||||
|
- ``Operations/AppEventLocalizationsPlacementsGetToManyRelationship``
|
||||||
|
- ``Operations/AppEventLocalizationsPlacementsGetToManyRelated``
|
||||||
- ``Operations/AppEventScreenshotsCreateInstance``
|
- ``Operations/AppEventScreenshotsCreateInstance``
|
||||||
- ``Operations/AppEventScreenshotsGetInstance``
|
- ``Operations/AppEventScreenshotsGetInstance``
|
||||||
- ``Operations/AppEventScreenshotsUpdateInstance``
|
- ``Operations/AppEventScreenshotsUpdateInstance``
|
||||||
@@ -356,6 +363,32 @@ These types are used by the ``APIProtocol`` methods and the ``Client`` implement
|
|||||||
- ``Operations/RoutingAppCoveragesUpdateInstance``
|
- ``Operations/RoutingAppCoveragesUpdateInstance``
|
||||||
- ``Operations/RoutingAppCoveragesDeleteInstance``
|
- ``Operations/RoutingAppCoveragesDeleteInstance``
|
||||||
|
|
||||||
|
### App Asset Library
|
||||||
|
|
||||||
|
- ``Operations/AppAssetLibrariesGetInstance``
|
||||||
|
- ``Operations/AppAssetLibrariesImagesGetToManyRelationship``
|
||||||
|
- ``Operations/AppAssetLibrariesImagesGetToManyRelated``
|
||||||
|
- ``Operations/AppAssetLibrariesVideosGetToManyRelationship``
|
||||||
|
- ``Operations/AppAssetLibrariesVideosGetToManyRelated``
|
||||||
|
- ``Operations/AppAssetLibraryImagesCreateInstance``
|
||||||
|
- ``Operations/AppAssetLibraryImagesGetInstance``
|
||||||
|
- ``Operations/AppAssetLibraryImagesUpdateInstance``
|
||||||
|
- ``Operations/AppAssetLibraryImagesDeleteInstance``
|
||||||
|
- ``Operations/AppAssetLibraryImagesPlacementsGetToManyRelationship``
|
||||||
|
- ``Operations/AppAssetLibraryImagesPlacementsGetToManyRelated``
|
||||||
|
- ``Operations/AppAssetLibraryVideosCreateInstance``
|
||||||
|
- ``Operations/AppAssetLibraryVideosGetInstance``
|
||||||
|
- ``Operations/AppAssetLibraryVideosUpdateInstance``
|
||||||
|
- ``Operations/AppAssetLibraryVideosDeleteInstance``
|
||||||
|
- ``Operations/AppAssetLibraryVideosPlacementsGetToManyRelationship``
|
||||||
|
- ``Operations/AppAssetLibraryVideosPlacementsGetToManyRelated``
|
||||||
|
- ``Operations/AppAssetLibraryPlacementsCreateInstance``
|
||||||
|
- ``Operations/AppAssetLibraryPlacementsGetInstance``
|
||||||
|
- ``Operations/AppAssetLibraryPlacementsDeleteInstance``
|
||||||
|
- ``Operations/AppAssetLibraryPlacementOrderingRequestsCreateInstance``
|
||||||
|
- ``Operations/AppAssetLibraryRefDataGetCollection``
|
||||||
|
- ``Operations/AppAssetLibraryRefDataGetInstance``
|
||||||
|
|
||||||
### App Encryption
|
### App Encryption
|
||||||
|
|
||||||
- ``Operations/AppEncryptionDeclarationsGetCollection``
|
- ``Operations/AppEncryptionDeclarationsGetCollection``
|
||||||
@@ -579,15 +612,33 @@ These types are used by the ``APIProtocol`` methods and the ``Client`` implement
|
|||||||
- ``Operations/InAppPurchasesV2OfferCodesGetToManyRelationship``
|
- ``Operations/InAppPurchasesV2OfferCodesGetToManyRelationship``
|
||||||
- ``Operations/InAppPurchasesV2PricePointsGetToManyRelationship``
|
- ``Operations/InAppPurchasesV2PricePointsGetToManyRelationship``
|
||||||
- ``Operations/InAppPurchasesV2PromotedPurchaseGetToOneRelationship``
|
- ``Operations/InAppPurchasesV2PromotedPurchaseGetToOneRelationship``
|
||||||
|
- ``Operations/InAppPurchasesV2VersionsGetToManyRelationship``
|
||||||
|
- ``Operations/InAppPurchasesV2VersionsGetToManyRelated``
|
||||||
|
- ``Operations/InAppPurchaseVersionsCreateInstance``
|
||||||
|
- ``Operations/InAppPurchaseVersionsGetInstance``
|
||||||
|
- ``Operations/InAppPurchaseVersionsImageGetToOneRelationship``
|
||||||
|
- ``Operations/InAppPurchaseVersionsImageGetToOneRelated``
|
||||||
|
- ``Operations/InAppPurchaseVersionsImagesGetToManyRelationship``
|
||||||
|
- ``Operations/InAppPurchaseVersionsImagesGetToManyRelated``
|
||||||
|
- ``Operations/InAppPurchaseVersionsLocalizationsGetToManyRelationship``
|
||||||
|
- ``Operations/InAppPurchaseVersionsLocalizationsGetToManyRelated``
|
||||||
- ``Operations/InAppPurchaseLocalizationsCreateInstance``
|
- ``Operations/InAppPurchaseLocalizationsCreateInstance``
|
||||||
- ``Operations/InAppPurchaseLocalizationsGetInstance``
|
- ``Operations/InAppPurchaseLocalizationsGetInstance``
|
||||||
- ``Operations/InAppPurchaseLocalizationsUpdateInstance``
|
- ``Operations/InAppPurchaseLocalizationsUpdateInstance``
|
||||||
- ``Operations/InAppPurchaseLocalizationsDeleteInstance``
|
- ``Operations/InAppPurchaseLocalizationsDeleteInstance``
|
||||||
|
- ``Operations/InAppPurchaseLocalizationsV2CreateInstance``
|
||||||
|
- ``Operations/InAppPurchaseLocalizationsV2GetInstance``
|
||||||
|
- ``Operations/InAppPurchaseLocalizationsV2UpdateInstance``
|
||||||
|
- ``Operations/InAppPurchaseLocalizationsV2DeleteInstance``
|
||||||
- ``Operations/InAppPurchaseContentsGetInstance``
|
- ``Operations/InAppPurchaseContentsGetInstance``
|
||||||
- ``Operations/InAppPurchaseImagesCreateInstance``
|
- ``Operations/InAppPurchaseImagesCreateInstance``
|
||||||
- ``Operations/InAppPurchaseImagesGetInstance``
|
- ``Operations/InAppPurchaseImagesGetInstance``
|
||||||
- ``Operations/InAppPurchaseImagesUpdateInstance``
|
- ``Operations/InAppPurchaseImagesUpdateInstance``
|
||||||
- ``Operations/InAppPurchaseImagesDeleteInstance``
|
- ``Operations/InAppPurchaseImagesDeleteInstance``
|
||||||
|
- ``Operations/InAppPurchaseImagesV2CreateInstance``
|
||||||
|
- ``Operations/InAppPurchaseImagesV2GetInstance``
|
||||||
|
- ``Operations/InAppPurchaseImagesV2UpdateInstance``
|
||||||
|
- ``Operations/InAppPurchaseImagesV2DeleteInstance``
|
||||||
- ``Operations/InAppPurchaseAppStoreReviewScreenshotsCreateInstance``
|
- ``Operations/InAppPurchaseAppStoreReviewScreenshotsCreateInstance``
|
||||||
- ``Operations/InAppPurchaseAppStoreReviewScreenshotsGetInstance``
|
- ``Operations/InAppPurchaseAppStoreReviewScreenshotsGetInstance``
|
||||||
- ``Operations/InAppPurchaseAppStoreReviewScreenshotsUpdateInstance``
|
- ``Operations/InAppPurchaseAppStoreReviewScreenshotsUpdateInstance``
|
||||||
@@ -658,6 +709,18 @@ These types are used by the ``APIProtocol`` methods and the ``Client`` implement
|
|||||||
- ``Operations/SubscriptionsSubscriptionAvailabilityGetToOneRelated``
|
- ``Operations/SubscriptionsSubscriptionAvailabilityGetToOneRelated``
|
||||||
- ``Operations/SubscriptionsSubscriptionLocalizationsGetToManyRelated``
|
- ``Operations/SubscriptionsSubscriptionLocalizationsGetToManyRelated``
|
||||||
- ``Operations/SubscriptionsWinBackOffersGetToManyRelated``
|
- ``Operations/SubscriptionsWinBackOffersGetToManyRelated``
|
||||||
|
- ``Operations/SubscriptionsPlanAvailabilitiesGetToManyRelationship``
|
||||||
|
- ``Operations/SubscriptionsPlanAvailabilitiesGetToManyRelated``
|
||||||
|
- ``Operations/SubscriptionsVersionsGetToManyRelationship``
|
||||||
|
- ``Operations/SubscriptionsVersionsGetToManyRelated``
|
||||||
|
- ``Operations/SubscriptionVersionsCreateInstance``
|
||||||
|
- ``Operations/SubscriptionVersionsGetInstance``
|
||||||
|
- ``Operations/SubscriptionVersionsImageGetToOneRelationship``
|
||||||
|
- ``Operations/SubscriptionVersionsImageGetToOneRelated``
|
||||||
|
- ``Operations/SubscriptionVersionsImagesGetToManyRelationship``
|
||||||
|
- ``Operations/SubscriptionVersionsImagesGetToManyRelated``
|
||||||
|
- ``Operations/SubscriptionVersionsLocalizationsGetToManyRelationship``
|
||||||
|
- ``Operations/SubscriptionVersionsLocalizationsGetToManyRelated``
|
||||||
- ``Operations/SubscriptionGroupsCreateInstance``
|
- ``Operations/SubscriptionGroupsCreateInstance``
|
||||||
- ``Operations/SubscriptionGroupsGetInstance``
|
- ``Operations/SubscriptionGroupsGetInstance``
|
||||||
- ``Operations/SubscriptionGroupsUpdateInstance``
|
- ``Operations/SubscriptionGroupsUpdateInstance``
|
||||||
@@ -666,19 +729,37 @@ These types are used by the ``APIProtocol`` methods and the ``Client`` implement
|
|||||||
- ``Operations/SubscriptionGroupsSubscriptionsGetToManyRelationship``
|
- ``Operations/SubscriptionGroupsSubscriptionsGetToManyRelationship``
|
||||||
- ``Operations/SubscriptionGroupsSubscriptionGroupLocalizationsGetToManyRelated``
|
- ``Operations/SubscriptionGroupsSubscriptionGroupLocalizationsGetToManyRelated``
|
||||||
- ``Operations/SubscriptionGroupsSubscriptionsGetToManyRelated``
|
- ``Operations/SubscriptionGroupsSubscriptionsGetToManyRelated``
|
||||||
|
- ``Operations/SubscriptionGroupsVersionsGetToManyRelationship``
|
||||||
|
- ``Operations/SubscriptionGroupsVersionsGetToManyRelated``
|
||||||
|
- ``Operations/SubscriptionGroupVersionsCreateInstance``
|
||||||
|
- ``Operations/SubscriptionGroupVersionsGetInstance``
|
||||||
|
- ``Operations/SubscriptionGroupVersionsLocalizationsGetToManyRelationship``
|
||||||
|
- ``Operations/SubscriptionGroupVersionsLocalizationsGetToManyRelated``
|
||||||
- ``Operations/SubscriptionGroupLocalizationsCreateInstance``
|
- ``Operations/SubscriptionGroupLocalizationsCreateInstance``
|
||||||
- ``Operations/SubscriptionGroupLocalizationsGetInstance``
|
- ``Operations/SubscriptionGroupLocalizationsGetInstance``
|
||||||
- ``Operations/SubscriptionGroupLocalizationsUpdateInstance``
|
- ``Operations/SubscriptionGroupLocalizationsUpdateInstance``
|
||||||
- ``Operations/SubscriptionGroupLocalizationsDeleteInstance``
|
- ``Operations/SubscriptionGroupLocalizationsDeleteInstance``
|
||||||
|
- ``Operations/SubscriptionGroupLocalizationsV2CreateInstance``
|
||||||
|
- ``Operations/SubscriptionGroupLocalizationsV2GetInstance``
|
||||||
|
- ``Operations/SubscriptionGroupLocalizationsV2UpdateInstance``
|
||||||
|
- ``Operations/SubscriptionGroupLocalizationsV2DeleteInstance``
|
||||||
- ``Operations/SubscriptionGroupSubmissionsCreateInstance``
|
- ``Operations/SubscriptionGroupSubmissionsCreateInstance``
|
||||||
- ``Operations/SubscriptionLocalizationsCreateInstance``
|
- ``Operations/SubscriptionLocalizationsCreateInstance``
|
||||||
- ``Operations/SubscriptionLocalizationsGetInstance``
|
- ``Operations/SubscriptionLocalizationsGetInstance``
|
||||||
- ``Operations/SubscriptionLocalizationsUpdateInstance``
|
- ``Operations/SubscriptionLocalizationsUpdateInstance``
|
||||||
- ``Operations/SubscriptionLocalizationsDeleteInstance``
|
- ``Operations/SubscriptionLocalizationsDeleteInstance``
|
||||||
|
- ``Operations/SubscriptionLocalizationsV2CreateInstance``
|
||||||
|
- ``Operations/SubscriptionLocalizationsV2GetInstance``
|
||||||
|
- ``Operations/SubscriptionLocalizationsV2UpdateInstance``
|
||||||
|
- ``Operations/SubscriptionLocalizationsV2DeleteInstance``
|
||||||
- ``Operations/SubscriptionImagesCreateInstance``
|
- ``Operations/SubscriptionImagesCreateInstance``
|
||||||
- ``Operations/SubscriptionImagesGetInstance``
|
- ``Operations/SubscriptionImagesGetInstance``
|
||||||
- ``Operations/SubscriptionImagesUpdateInstance``
|
- ``Operations/SubscriptionImagesUpdateInstance``
|
||||||
- ``Operations/SubscriptionImagesDeleteInstance``
|
- ``Operations/SubscriptionImagesDeleteInstance``
|
||||||
|
- ``Operations/SubscriptionImagesV2CreateInstance``
|
||||||
|
- ``Operations/SubscriptionImagesV2GetInstance``
|
||||||
|
- ``Operations/SubscriptionImagesV2UpdateInstance``
|
||||||
|
- ``Operations/SubscriptionImagesV2DeleteInstance``
|
||||||
- ``Operations/SubscriptionAppStoreReviewScreenshotsCreateInstance``
|
- ``Operations/SubscriptionAppStoreReviewScreenshotsCreateInstance``
|
||||||
- ``Operations/SubscriptionAppStoreReviewScreenshotsGetInstance``
|
- ``Operations/SubscriptionAppStoreReviewScreenshotsGetInstance``
|
||||||
- ``Operations/SubscriptionAppStoreReviewScreenshotsUpdateInstance``
|
- ``Operations/SubscriptionAppStoreReviewScreenshotsUpdateInstance``
|
||||||
@@ -687,6 +768,12 @@ These types are used by the ``APIProtocol`` methods and the ``Client`` implement
|
|||||||
- ``Operations/SubscriptionAvailabilitiesGetInstance``
|
- ``Operations/SubscriptionAvailabilitiesGetInstance``
|
||||||
- ``Operations/SubscriptionAvailabilitiesAvailableTerritoriesGetToManyRelated``
|
- ``Operations/SubscriptionAvailabilitiesAvailableTerritoriesGetToManyRelated``
|
||||||
- ``Operations/SubscriptionAvailabilitiesAvailableTerritoriesGetToManyRelationship``
|
- ``Operations/SubscriptionAvailabilitiesAvailableTerritoriesGetToManyRelationship``
|
||||||
|
- ``Operations/SubscriptionPlanAvailabilitiesCreateInstance``
|
||||||
|
- ``Operations/SubscriptionPlanAvailabilitiesGetInstance``
|
||||||
|
- ``Operations/SubscriptionPlanAvailabilitiesUpdateInstance``
|
||||||
|
- ``Operations/SubscriptionPlanAvailabilitiesAvailableTerritoriesGetToManyRelationship``
|
||||||
|
- ``Operations/SubscriptionPlanAvailabilitiesAvailableTerritoriesReplaceToManyRelationship``
|
||||||
|
- ``Operations/SubscriptionPlanAvailabilitiesAvailableTerritoriesGetToManyRelated``
|
||||||
- ``Operations/SubscriptionGracePeriodsGetInstance``
|
- ``Operations/SubscriptionGracePeriodsGetInstance``
|
||||||
- ``Operations/SubscriptionGracePeriodsUpdateInstance``
|
- ``Operations/SubscriptionGracePeriodsUpdateInstance``
|
||||||
- ``Operations/SubscriptionIntroductoryOffersCreateInstance``
|
- ``Operations/SubscriptionIntroductoryOffersCreateInstance``
|
||||||
@@ -711,6 +798,7 @@ These types are used by the ``APIProtocol`` methods and the ``Client`` implement
|
|||||||
- ``Operations/SubscriptionPricePointsGetInstance``
|
- ``Operations/SubscriptionPricePointsGetInstance``
|
||||||
- ``Operations/SubscriptionPricePointsEqualizationsGetToManyRelated``
|
- ``Operations/SubscriptionPricePointsEqualizationsGetToManyRelated``
|
||||||
- ``Operations/SubscriptionPricePointsEqualizationsGetToManyRelationship``
|
- ``Operations/SubscriptionPricePointsEqualizationsGetToManyRelationship``
|
||||||
|
- ``Operations/SubscriptionPricePointsAdjustedEqualizationsGetToManyRelated``
|
||||||
- ``Operations/SubscriptionPricesCreateInstance``
|
- ``Operations/SubscriptionPricesCreateInstance``
|
||||||
- ``Operations/SubscriptionPricesDeleteInstance``
|
- ``Operations/SubscriptionPricesDeleteInstance``
|
||||||
- ``Operations/SubscriptionPromotionalOffersCreateInstance``
|
- ``Operations/SubscriptionPromotionalOffersCreateInstance``
|
||||||
@@ -771,6 +859,9 @@ These types are used by the ``APIProtocol`` methods and the ``Client`` implement
|
|||||||
- ``Operations/GameCenterDetailsGameCenterLeaderboardsV2ReplaceToManyRelationship``
|
- ``Operations/GameCenterDetailsGameCenterLeaderboardsV2ReplaceToManyRelationship``
|
||||||
- ``Operations/GameCenterDetailsLeaderboardReleasesGetToManyRelationship``
|
- ``Operations/GameCenterDetailsLeaderboardReleasesGetToManyRelationship``
|
||||||
- ``Operations/GameCenterDetailsLeaderboardSetReleasesGetToManyRelationship``
|
- ``Operations/GameCenterDetailsLeaderboardSetReleasesGetToManyRelationship``
|
||||||
|
- ``Operations/GameCenterDetailsBlockedPlayersGetToManyRelationship``
|
||||||
|
- ``Operations/GameCenterDetailsBlockedPlayersGetToManyRelated``
|
||||||
|
- ``Operations/GameCenterDetailPlayersUpdateInstance``
|
||||||
- ``Operations/GameCenterGroupsGetCollection``
|
- ``Operations/GameCenterGroupsGetCollection``
|
||||||
- ``Operations/GameCenterGroupsCreateInstance``
|
- ``Operations/GameCenterGroupsCreateInstance``
|
||||||
- ``Operations/GameCenterGroupsGetInstance``
|
- ``Operations/GameCenterGroupsGetInstance``
|
||||||
@@ -869,6 +960,9 @@ These types are used by the ``APIProtocol`` methods and the ``Client`` implement
|
|||||||
- ``Operations/GameCenterLeaderboardsV2ChallengeUpdateToOneRelationship``
|
- ``Operations/GameCenterLeaderboardsV2ChallengeUpdateToOneRelationship``
|
||||||
- ``Operations/GameCenterLeaderboardsV2VersionsGetToManyRelationship``
|
- ``Operations/GameCenterLeaderboardsV2VersionsGetToManyRelationship``
|
||||||
- ``Operations/GameCenterLeaderboardsV2VersionsGetToManyRelated``
|
- ``Operations/GameCenterLeaderboardsV2VersionsGetToManyRelated``
|
||||||
|
- ``Operations/GameCenterLeaderboardsV2GameCenterScoreModerationsGetToManyRelationship``
|
||||||
|
- ``Operations/GameCenterLeaderboardsV2GameCenterScoreModerationsGetToManyRelated``
|
||||||
|
- ``Operations/GameCenterScoreModerationsUpdateInstance``
|
||||||
- ``Operations/GameCenterLeaderboardVersionsV2CreateInstance``
|
- ``Operations/GameCenterLeaderboardVersionsV2CreateInstance``
|
||||||
- ``Operations/GameCenterLeaderboardVersionsV2GetInstance``
|
- ``Operations/GameCenterLeaderboardVersionsV2GetInstance``
|
||||||
- ``Operations/GameCenterLeaderboardVersionsV2LocalizationsGetToManyRelated``
|
- ``Operations/GameCenterLeaderboardVersionsV2LocalizationsGetToManyRelated``
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
# ``Servers``
|
# ``Servers``
|
||||||
|
|
||||||
Server URLs defined in the OpenAPI document.
|
The App Store Connect API server URLs.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The ``Servers`` namespace provides access to the base URLs defined in the App Store Connect API OpenAPI specification. Use these URLs when creating a ``Client`` instance.
|
The specification defines a single server, `https://api.appstoreconnect.apple.com/`. Pass its URL to ``Client``:
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
let client = Client(
|
let client = Client(
|
||||||
serverURL: try Servers.Server1.url(),
|
serverURL: try Servers.Server1.url(),
|
||||||
transport: URLSessionTransport(),
|
transport: URLSessionTransport()
|
||||||
middlewares: [
|
|
||||||
BearerAuthMiddleware(token: yourJWTToken)
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -21,4 +18,7 @@ let client = Client(
|
|||||||
### Server URLs
|
### Server URLs
|
||||||
|
|
||||||
- ``Servers/Server1``
|
- ``Servers/Server1``
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
|
||||||
- ``Servers/server1()``
|
- ``Servers/server1()``
|
||||||
@@ -1,36 +1,78 @@
|
|||||||
# ``ASConnectService``
|
# ``ASConnectService``
|
||||||
|
|
||||||
A Swift client library for the App Store Connect API, generated from the official OpenAPI specification.
|
A type-safe Swift client for the App Store Connect API.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
``ASConnectService`` provides a type-safe, Swift-native interface to Apple's App Store Connect API. This package enables developers to programmatically interact with App Store Connect services for managing apps, builds, reviews, sales reports, and more.
|
ASConnectService exposes every endpoint of Apple's App Store Connect API as a Swift method with typed requests and responses. It's generated from the official OpenAPI specification with [Swift OpenAPI Generator](https://github.com/apple/swift-openapi-generator).
|
||||||
|
|
||||||
The library is automatically generated from the official App Store Connect API OpenAPI specification using Apple's [swift-openapi-generator](https://github.com/apple/swift-openapi-generator), ensuring complete API coverage and type safety.
|
|
||||||
|
|
||||||
### Creating a Client
|
### Creating a Client
|
||||||
|
|
||||||
Create a ``Client`` instance by providing a server URL and transport. Use ``BearerAuthMiddleware`` to authenticate requests with a JSON Web Token (JWT).
|
Create a ``Client`` with a server URL, a transport, and a ``BearerAuthMiddleware``. With an API key from App Store Connect, ``APIKeyTokenProvider`` signs the JSON Web Tokens (JWT) the API requires, and renews them before they expire:
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
import ASConnectService
|
import ASConnectService
|
||||||
import OpenAPIURLSession
|
import OpenAPIURLSession
|
||||||
|
|
||||||
|
let tokenProvider = try APIKeyTokenProvider(
|
||||||
|
issuerID: yourIssuerID,
|
||||||
|
keyID: yourKeyID,
|
||||||
|
privateKey: yourP8FileContents
|
||||||
|
)
|
||||||
|
|
||||||
let client = Client(
|
let client = Client(
|
||||||
serverURL: try Servers.server1(),
|
serverURL: try Servers.Server1.url(),
|
||||||
transport: URLSessionTransport(),
|
transport: URLSessionTransport(),
|
||||||
middlewares: [
|
middlewares: [
|
||||||
BearerAuthMiddleware(token: yourJWTToken)
|
BearerAuthMiddleware { try await tokenProvider.token() }
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Making API Calls
|
To use a token you signed yourself, pass it directly. It expires after 20 minutes at most:
|
||||||
|
|
||||||
The ``Client`` conforms to ``APIProtocol``, which defines a method for every endpoint in the App Store Connect API. Each method accepts an `Input` value and returns an `Output` value with the response.
|
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
let response = try await client.appsGetCollection(.init())
|
BearerAuthMiddleware(token: yourJWTToken)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Making API Calls
|
||||||
|
|
||||||
|
Each endpoint is a method on ``Client``, declared by ``APIProtocol``. It returns an `Output` enum with a case for each documented response:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let response = try await client.appsGetCollection(query: .init(limit: 10))
|
||||||
|
let apps = try response.ok.body.json.data
|
||||||
|
```
|
||||||
|
|
||||||
|
The `ok` property throws for any other response. To handle those, switch over the `Output` cases, such as `.unauthorized` or `.undocumented(statusCode:_:)`.
|
||||||
|
|
||||||
|
### Fetching More Pages
|
||||||
|
|
||||||
|
Lists are paged. Iterate a ``PagedSequence`` to fetch each page in turn:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let pages = PagedSequence(links: \.links) { cursor in
|
||||||
|
try await client.appsGetCollection(query: .init(limit: 200, cursor: cursor)).ok.body.json
|
||||||
|
}
|
||||||
|
|
||||||
|
var apps: [Components.Schemas.App] = []
|
||||||
|
|
||||||
|
for try await page in pages {
|
||||||
|
apps += page.data
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To fetch pages yourself, pass a page's ``Components/Schemas/PagedDocumentLinks/nextCursor`` as the `cursor` of the next request.
|
||||||
|
|
||||||
|
### Handling Rate Limits
|
||||||
|
|
||||||
|
Add ``RateLimitMiddleware`` before the authentication middleware to retry `429 Too Many Requests` responses:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
middlewares: [
|
||||||
|
RateLimitMiddleware(),
|
||||||
|
BearerAuthMiddleware { try await tokenProvider.token() }
|
||||||
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Topics
|
## Topics
|
||||||
@@ -43,6 +85,15 @@ let response = try await client.appsGetCollection(.init())
|
|||||||
### Authentication
|
### Authentication
|
||||||
|
|
||||||
- ``BearerAuthMiddleware``
|
- ``BearerAuthMiddleware``
|
||||||
|
- ``APIKeyTokenProvider``
|
||||||
|
|
||||||
|
### Pagination
|
||||||
|
|
||||||
|
- ``PagedSequence``
|
||||||
|
|
||||||
|
### Rate Limiting
|
||||||
|
|
||||||
|
- ``RateLimitMiddleware``
|
||||||
|
|
||||||
### Generated Types
|
### Generated Types
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
//
|
||||||
|
// This source file is part of the ASConnectService open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2026 Röck+Cöde VoF. and the ASConnectService project authors
|
||||||
|
// Licensed under Apache license v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE for license information
|
||||||
|
// See CONTRIBUTORS for the list of ASConnectService project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
|
||||||
|
import struct Foundation.URLComponents
|
||||||
|
|
||||||
|
extension Components.Schemas.PagedDocumentLinks {
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
/// The cursor of the next page, or `nil` on the last page.
|
||||||
|
///
|
||||||
|
/// Pass it as the `cursor` query parameter of the same operation to fetch the next page.
|
||||||
|
public var nextCursor: String? {
|
||||||
|
guard let next else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return URLComponents(string: next)?
|
||||||
|
.queryItems?
|
||||||
|
.first { $0.name == "cursor" }?
|
||||||
|
.value
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -13,38 +13,56 @@
|
|||||||
// ===----------------------------------------------------------------------===
|
// ===----------------------------------------------------------------------===
|
||||||
|
|
||||||
import struct Foundation.URL
|
import struct Foundation.URL
|
||||||
import struct Foundation.URLComponents
|
|
||||||
import struct Foundation.URLQueryItem
|
|
||||||
import struct HTTPTypes.HTTPField
|
|
||||||
import struct HTTPTypes.HTTPFields
|
|
||||||
import struct HTTPTypes.HTTPRequest
|
import struct HTTPTypes.HTTPRequest
|
||||||
import struct HTTPTypes.HTTPResponse
|
import struct HTTPTypes.HTTPResponse
|
||||||
import protocol OpenAPIRuntime.ClientMiddleware
|
import protocol OpenAPIRuntime.ClientMiddleware
|
||||||
import class OpenAPIRuntime.HTTPBody
|
import class OpenAPIRuntime.HTTPBody
|
||||||
|
|
||||||
/// A client middleware that injects a Bearer authentication token into outgoing HTTP requests.
|
/// A client middleware that authenticates requests with a Bearer token.
|
||||||
///
|
///
|
||||||
/// This middleware appends an `Authorization` header with a Bearer token to every request
|
/// This middleware sets the `Authorization` header of every request to `Bearer <token>`, as the
|
||||||
/// before forwarding it to the next handler in the middleware chain. It is intended for use
|
/// App Store Connect API requires JSON Web Token (JWT) authentication.
|
||||||
/// with the App Store Connect API, which requires JSON Web Token (JWT) authentication.
|
|
||||||
///
|
///
|
||||||
/// ## Usage
|
/// ## Usage
|
||||||
///
|
///
|
||||||
|
/// Provide a fixed token:
|
||||||
|
///
|
||||||
/// ```swift
|
/// ```swift
|
||||||
/// let middleware = BearerAuthMiddleware(token: "your-jwt-token")
|
/// let middleware = BearerAuthMiddleware(token: "your-jwt-token")
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// Tokens expire after 20 minutes at most, so long-lived clients should provide a token provider, such as
|
||||||
|
/// ``APIKeyTokenProvider``:
|
||||||
|
///
|
||||||
|
/// ```swift
|
||||||
|
/// let middleware = BearerAuthMiddleware {
|
||||||
|
/// try await tokenProvider.token()
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
public struct BearerAuthMiddleware {
|
public struct BearerAuthMiddleware {
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
|
||||||
/// The Bearer token to include in the `Authorization` header of each request.
|
/// The closure that provides the Bearer token for each request.
|
||||||
private let token: String
|
private let tokenProvider: @Sendable () async throws -> String
|
||||||
|
|
||||||
// MARK: Initializers
|
// MARK: Initializers
|
||||||
|
|
||||||
/// Creates a new middleware instance with the given Bearer token.
|
/// Creates a new middleware instance with a fixed Bearer token.
|
||||||
/// - Parameter token: A JSON Web Token (JWT) string used to authenticate requests to the App Store Connect API.
|
/// - Parameter token: A JSON Web Token (JWT) used to authenticate requests to the App Store Connect API.
|
||||||
init(token: String) {
|
public init(
|
||||||
self.token = token
|
token: String
|
||||||
|
) {
|
||||||
|
self.init { token }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new middleware instance that asks for a Bearer token on every request.
|
||||||
|
///
|
||||||
|
/// If the provider throws, the error is rethrown and the request is not sent.
|
||||||
|
/// - Parameter tokenProvider: A closure that returns a valid JSON Web Token (JWT).
|
||||||
|
public init(
|
||||||
|
tokenProvider: @escaping @Sendable () async throws -> String
|
||||||
|
) {
|
||||||
|
self.tokenProvider = tokenProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +86,8 @@ extension BearerAuthMiddleware: ClientMiddleware {
|
|||||||
operationID: String,
|
operationID: String,
|
||||||
next: @Sendable (HTTPRequest, HTTPBody?, URL) async throws -> (HTTPResponse, HTTPBody?)
|
next: @Sendable (HTTPRequest, HTTPBody?, URL) async throws -> (HTTPResponse, HTTPBody?)
|
||||||
) async throws -> (HTTPResponse, HTTPBody?) {
|
) async throws -> (HTTPResponse, HTTPBody?) {
|
||||||
|
let token = try await tokenProvider()
|
||||||
|
|
||||||
var request = request
|
var request = request
|
||||||
request.headerFields[.authorization] = "Bearer \(token)"
|
request.headerFields[.authorization] = "Bearer \(token)"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,149 @@
|
|||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
//
|
||||||
|
// This source file is part of the ASConnectService open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2026 Röck+Cöde VoF. and the ASConnectService project authors
|
||||||
|
// Licensed under Apache license v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE for license information
|
||||||
|
// See CONTRIBUTORS for the list of ASConnectService project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
|
||||||
|
import struct Foundation.URL
|
||||||
|
import typealias Foundation.TimeInterval
|
||||||
|
import struct HTTPTypes.HTTPRequest
|
||||||
|
import struct HTTPTypes.HTTPResponse
|
||||||
|
import protocol OpenAPIRuntime.ClientMiddleware
|
||||||
|
import class OpenAPIRuntime.HTTPBody
|
||||||
|
|
||||||
|
/// A client middleware that retries rate-limited requests.
|
||||||
|
///
|
||||||
|
/// On a `429 Too Many Requests` response, it waits for the `Retry-After` seconds, or a delay that doubles from
|
||||||
|
/// `baseDelay`, and retries. It returns the `429` response instead when the retries run out, the delay exceeds
|
||||||
|
/// `maximumDelay`, or the request body can only be sent once.
|
||||||
|
///
|
||||||
|
/// ## Usage
|
||||||
|
///
|
||||||
|
/// Place it before ``BearerAuthMiddleware``, so retries get a fresh token:
|
||||||
|
///
|
||||||
|
/// ```swift
|
||||||
|
/// middlewares: [
|
||||||
|
/// RateLimitMiddleware(),
|
||||||
|
/// BearerAuthMiddleware { try await tokenProvider.token() }
|
||||||
|
/// ]
|
||||||
|
/// ```
|
||||||
|
public struct RateLimitMiddleware {
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
/// The maximum number of times a request is retried.
|
||||||
|
private let maximumRetries: Int
|
||||||
|
|
||||||
|
/// The delay before the first retry, when the response has no `Retry-After` header.
|
||||||
|
private let baseDelay: TimeInterval
|
||||||
|
|
||||||
|
/// The longest delay to wait before a retry.
|
||||||
|
private let maximumDelay: TimeInterval
|
||||||
|
|
||||||
|
/// The closure that waits for a given number of seconds.
|
||||||
|
private let sleep: @Sendable (TimeInterval) async throws -> Void
|
||||||
|
|
||||||
|
// MARK: Initializers
|
||||||
|
|
||||||
|
/// Creates a new middleware instance.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - maximumRetries: The maximum number of times a request is retried.
|
||||||
|
/// - baseDelay: The delay, in seconds, before the first retry when the response has no `Retry-After` header.
|
||||||
|
/// - maximumDelay: The longest delay, in seconds, to wait before a retry.
|
||||||
|
public init(
|
||||||
|
maximumRetries: Int = 3,
|
||||||
|
baseDelay: TimeInterval = 1,
|
||||||
|
maximumDelay: TimeInterval = 60
|
||||||
|
) {
|
||||||
|
self.init(
|
||||||
|
maximumRetries: maximumRetries,
|
||||||
|
baseDelay: baseDelay,
|
||||||
|
maximumDelay: maximumDelay,
|
||||||
|
sleep: { try await Task.sleep(nanoseconds: UInt64($0 * 1_000_000_000)) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new middleware instance that waits with `sleep`, for testing.
|
||||||
|
package init(
|
||||||
|
maximumRetries: Int,
|
||||||
|
baseDelay: TimeInterval,
|
||||||
|
maximumDelay: TimeInterval,
|
||||||
|
sleep: @escaping @Sendable (TimeInterval) async throws -> Void
|
||||||
|
) {
|
||||||
|
self.maximumRetries = maximumRetries
|
||||||
|
self.baseDelay = baseDelay
|
||||||
|
self.maximumDelay = maximumDelay
|
||||||
|
self.sleep = sleep
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - ClientMiddleware
|
||||||
|
|
||||||
|
extension RateLimitMiddleware: ClientMiddleware {
|
||||||
|
// MARK: Methods
|
||||||
|
|
||||||
|
/// Sends an HTTP request, and retries it while its response is `429 Too Many Requests`.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - request: The original HTTP request.
|
||||||
|
/// - body: The optional body of the request.
|
||||||
|
/// - baseURL: The base URL for the request.
|
||||||
|
/// - operationID: The identifier of the API operation being performed.
|
||||||
|
/// - next: The next handler in the middleware chain.
|
||||||
|
/// - Returns: The HTTP response and optional body returned by the next handler.
|
||||||
|
/// - Throws: An error if the next handler throws, or the task is cancelled while waiting.
|
||||||
|
public func intercept(
|
||||||
|
_ request: HTTPRequest,
|
||||||
|
body: HTTPBody?,
|
||||||
|
baseURL: URL,
|
||||||
|
operationID: String,
|
||||||
|
next: @Sendable (HTTPRequest, HTTPBody?, URL) async throws -> (HTTPResponse, HTTPBody?)
|
||||||
|
) async throws -> (HTTPResponse, HTTPBody?) {
|
||||||
|
var retries = 0
|
||||||
|
var backoff = baseDelay
|
||||||
|
|
||||||
|
while true {
|
||||||
|
let (response, responseBody) = try await next(
|
||||||
|
request,
|
||||||
|
body,
|
||||||
|
baseURL
|
||||||
|
)
|
||||||
|
|
||||||
|
guard
|
||||||
|
response.status == .tooManyRequests,
|
||||||
|
retries < maximumRetries,
|
||||||
|
body?.iterationBehavior != .single
|
||||||
|
else {
|
||||||
|
return (response, responseBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
let delay = response.retryAfter ?? backoff
|
||||||
|
|
||||||
|
guard delay <= maximumDelay else {
|
||||||
|
return (response, responseBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
try await sleep(delay)
|
||||||
|
|
||||||
|
retries += 1
|
||||||
|
backoff *= 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - HTTPResponse+RetryAfter
|
||||||
|
|
||||||
|
private extension HTTPResponse {
|
||||||
|
/// The seconds in the `Retry-After` header, if any, and never negative.
|
||||||
|
var retryAfter: TimeInterval? {
|
||||||
|
headerFields[.retryAfter]
|
||||||
|
.flatMap { TimeInterval($0) }
|
||||||
|
.map { max($0, 0) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
//
|
||||||
|
// This source file is part of the ASConnectService open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2026 Röck+Cöde VoF. and the ASConnectService project authors
|
||||||
|
// Licensed under Apache license v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE for license information
|
||||||
|
// See CONTRIBUTORS for the list of ASConnectService project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
|
||||||
|
#if canImport(CryptoKit)
|
||||||
|
import enum CryptoKit.P256
|
||||||
|
import struct Foundation.Data
|
||||||
|
import struct Foundation.Date
|
||||||
|
import class Foundation.JSONEncoder
|
||||||
|
import typealias Foundation.TimeInterval
|
||||||
|
|
||||||
|
/// A token provider that signs App Store Connect API tokens with an API key.
|
||||||
|
///
|
||||||
|
/// Each JSON Web Token (JWT) is reused until a minute before it expires:
|
||||||
|
///
|
||||||
|
/// ```swift
|
||||||
|
/// let tokenProvider = try APIKeyTokenProvider(
|
||||||
|
/// issuerID: "57246542-96fe-1a63-e053-0824d011072a",
|
||||||
|
/// keyID: "2X9R4HXF34",
|
||||||
|
/// privateKey: privateKey
|
||||||
|
/// )
|
||||||
|
///
|
||||||
|
/// let middleware = BearerAuthMiddleware {
|
||||||
|
/// try await tokenProvider.token()
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
public actor APIKeyTokenProvider {
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
/// The issuer ID of a team key, or `nil` for an individual key.
|
||||||
|
private let issuerID: String?
|
||||||
|
|
||||||
|
/// The ID of the API key.
|
||||||
|
private let keyID: String
|
||||||
|
|
||||||
|
/// The private key that signs the tokens.
|
||||||
|
private let privateKey: P256.Signing.PrivateKey
|
||||||
|
|
||||||
|
/// The closure that provides the current date.
|
||||||
|
private let now: @Sendable () -> Date
|
||||||
|
|
||||||
|
/// The last signed token, and its expiration date.
|
||||||
|
private var current: (token: String, expiresAt: Date)?
|
||||||
|
|
||||||
|
// MARK: Initializers
|
||||||
|
|
||||||
|
/// Creates a token provider with an App Store Connect API key.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - issuerID: The issuer ID of a team key, or `nil` for an individual key.
|
||||||
|
/// - keyID: The ID of the key.
|
||||||
|
/// - privateKey: The contents of the key's `.p8` file.
|
||||||
|
/// - Throws: An error if `privateKey` is not a P-256 private key in PEM format.
|
||||||
|
public init(
|
||||||
|
issuerID: String?,
|
||||||
|
keyID: String,
|
||||||
|
privateKey: String
|
||||||
|
) throws {
|
||||||
|
try self.init(
|
||||||
|
issuerID: issuerID,
|
||||||
|
keyID: keyID,
|
||||||
|
privateKey: privateKey,
|
||||||
|
now: { Date() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a token provider that reads the current date from `now`, for testing.
|
||||||
|
package init(
|
||||||
|
issuerID: String?,
|
||||||
|
keyID: String,
|
||||||
|
privateKey: String,
|
||||||
|
now: @escaping @Sendable () -> Date
|
||||||
|
) throws {
|
||||||
|
self.issuerID = issuerID
|
||||||
|
self.keyID = keyID
|
||||||
|
self.privateKey = try P256.Signing.PrivateKey(pemRepresentation: privateKey)
|
||||||
|
self.now = now
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Methods
|
||||||
|
|
||||||
|
/// Returns the current token, or signs a new one if it expires within a minute.
|
||||||
|
/// - Returns: A JSON Web Token (JWT) valid for 20 minutes at most.
|
||||||
|
/// - Throws: An error if the token cannot be signed.
|
||||||
|
public func token() throws -> String {
|
||||||
|
let date = now()
|
||||||
|
|
||||||
|
if let current, current.expiresAt.timeIntervalSince(date) > .refreshMargin {
|
||||||
|
return current.token
|
||||||
|
}
|
||||||
|
|
||||||
|
let expiresAt = date.addingTimeInterval(.lifetime)
|
||||||
|
let token = try sign(
|
||||||
|
issuedAt: date,
|
||||||
|
expiresAt: expiresAt
|
||||||
|
)
|
||||||
|
|
||||||
|
current = (token, expiresAt)
|
||||||
|
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Helpers
|
||||||
|
|
||||||
|
extension APIKeyTokenProvider {
|
||||||
|
// MARK: Functions
|
||||||
|
|
||||||
|
/// Signs a new token.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - issuedAt: The date the token is issued at.
|
||||||
|
/// - expiresAt: The date the token expires at.
|
||||||
|
/// - Returns: A JSON Web Token (JWT) signed with the ES256 algorithm.
|
||||||
|
/// - Throws: An error if the token cannot be encoded or signed.
|
||||||
|
private func sign(
|
||||||
|
issuedAt: Date,
|
||||||
|
expiresAt: Date
|
||||||
|
) throws -> String {
|
||||||
|
let encoder = JSONEncoder()
|
||||||
|
encoder.outputFormatting = .sortedKeys
|
||||||
|
|
||||||
|
let header = try encoder.encode(Header(kid: keyID))
|
||||||
|
let payload = try encoder.encode(
|
||||||
|
Payload(
|
||||||
|
iss: issuerID,
|
||||||
|
sub: issuerID == nil ? "user" : nil,
|
||||||
|
iat: Int(issuedAt.timeIntervalSince1970),
|
||||||
|
exp: Int(expiresAt.timeIntervalSince1970)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let message = "\(header.base64URLEncoded).\(payload.base64URLEncoded)"
|
||||||
|
let signature = try privateKey.signature(for: Data(message.utf8))
|
||||||
|
|
||||||
|
return "\(message).\(signature.rawRepresentation.base64URLEncoded)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Header
|
||||||
|
|
||||||
|
/// The header of a token.
|
||||||
|
private struct Header: Encodable {
|
||||||
|
let alg = "ES256"
|
||||||
|
let kid: String
|
||||||
|
let typ = "JWT"
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Payload
|
||||||
|
|
||||||
|
/// The payload of a token.
|
||||||
|
private struct Payload: Encodable {
|
||||||
|
let iss: String?
|
||||||
|
let sub: String?
|
||||||
|
let iat: Int
|
||||||
|
let exp: Int
|
||||||
|
let aud = "appstoreconnect-v1"
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Data+Base64URL
|
||||||
|
|
||||||
|
private extension Data {
|
||||||
|
/// The Base64URL encoding of the data, without padding.
|
||||||
|
var base64URLEncoded: String {
|
||||||
|
base64EncodedString()
|
||||||
|
.replacingOccurrences(of: "+", with: "-")
|
||||||
|
.replacingOccurrences(of: "/", with: "_")
|
||||||
|
.replacingOccurrences(of: "=", with: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - TimeInterval+Constants
|
||||||
|
|
||||||
|
private extension TimeInterval {
|
||||||
|
/// The lifetime of a token, which App Store Connect limits to 20 minutes.
|
||||||
|
static let lifetime: TimeInterval = 20 * 60
|
||||||
|
|
||||||
|
/// The time before the expiration of a token when a new one is signed.
|
||||||
|
static let refreshMargin: TimeInterval = 60
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
//
|
||||||
|
// This source file is part of the ASConnectService open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2026 Röck+Cöde VoF. and the ASConnectService project authors
|
||||||
|
// Licensed under Apache license v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE for license information
|
||||||
|
// See CONTRIBUTORS for the list of ASConnectService project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
|
||||||
|
/// An asynchronous sequence of the pages of a paged operation.
|
||||||
|
///
|
||||||
|
/// Each iteration fetches the next page, until a page has no next link:
|
||||||
|
///
|
||||||
|
/// ```swift
|
||||||
|
/// let pages = PagedSequence(links: \.links) { cursor in
|
||||||
|
/// try await client.appsGetCollection(query: .init(limit: 200, cursor: cursor)).ok.body.json
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// var apps: [Components.Schemas.App] = []
|
||||||
|
///
|
||||||
|
/// for try await page in pages {
|
||||||
|
/// apps += page.data
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
public struct PagedSequence<Page: Sendable>: AsyncSequence, Sendable {
|
||||||
|
// MARK: Type aliases
|
||||||
|
|
||||||
|
public typealias Element = Page
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
/// The closure that returns the links of a page.
|
||||||
|
private let links: @Sendable (Page) -> Components.Schemas.PagedDocumentLinks
|
||||||
|
|
||||||
|
/// The closure that fetches the page at a cursor.
|
||||||
|
private let fetch: @Sendable (String?) async throws -> Page
|
||||||
|
|
||||||
|
// MARK: Initializers
|
||||||
|
|
||||||
|
/// Creates a sequence of the pages of a paged operation.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - links: A closure that returns the links of a page.
|
||||||
|
/// - fetch: A closure that fetches the page at a cursor, or the first page if the cursor is `nil`.
|
||||||
|
public init(
|
||||||
|
links: @escaping @Sendable (Page) -> Components.Schemas.PagedDocumentLinks,
|
||||||
|
fetch: @escaping @Sendable (_ cursor: String?) async throws -> Page
|
||||||
|
) {
|
||||||
|
self.links = links
|
||||||
|
self.fetch = fetch
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Functions
|
||||||
|
|
||||||
|
public func makeAsyncIterator() -> Iterator {
|
||||||
|
Iterator(
|
||||||
|
links: links,
|
||||||
|
fetch: fetch
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Iterator
|
||||||
|
|
||||||
|
extension PagedSequence {
|
||||||
|
/// An iterator that fetches one page at a time.
|
||||||
|
public struct Iterator: AsyncIteratorProtocol {
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
/// The closure that returns the links of a page.
|
||||||
|
private let links: @Sendable (Page) -> Components.Schemas.PagedDocumentLinks
|
||||||
|
|
||||||
|
/// The closure that fetches the page at a cursor.
|
||||||
|
private let fetch: @Sendable (String?) async throws -> Page
|
||||||
|
|
||||||
|
/// The cursor of the next page, or `nil` for the first page.
|
||||||
|
private var cursor: String?
|
||||||
|
|
||||||
|
/// A Boolean value that indicates whether there are no more pages to fetch.
|
||||||
|
private var isFinished = false
|
||||||
|
|
||||||
|
// MARK: Initializers
|
||||||
|
|
||||||
|
init(
|
||||||
|
links: @escaping @Sendable (Page) -> Components.Schemas.PagedDocumentLinks,
|
||||||
|
fetch: @escaping @Sendable (String?) async throws -> Page
|
||||||
|
) {
|
||||||
|
self.links = links
|
||||||
|
self.fetch = fetch
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Functions
|
||||||
|
|
||||||
|
/// Fetches the next page.
|
||||||
|
/// - Returns: The next page, or `nil` after the last page.
|
||||||
|
/// - Throws: The error thrown by the fetch closure, after which the iterator returns `nil`.
|
||||||
|
public mutating func next() async throws -> Page? {
|
||||||
|
guard !isFinished else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finishes the iteration if fetching the page throws.
|
||||||
|
isFinished = true
|
||||||
|
|
||||||
|
let page = try await fetch(cursor)
|
||||||
|
|
||||||
|
cursor = links(page).nextCursor
|
||||||
|
isFinished = cursor == nil
|
||||||
|
|
||||||
|
return page
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,3 +17,6 @@ generate:
|
|||||||
- client
|
- client
|
||||||
namingStrategy: idiomatic
|
namingStrategy: idiomatic
|
||||||
accessModifier: public
|
accessModifier: public
|
||||||
|
# Stops swift-format from reformatting the generated code.
|
||||||
|
additionalFileComments:
|
||||||
|
- swift-format-ignore-file
|
||||||
+32269
-165
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,65 @@
|
|||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
//
|
||||||
|
// This source file is part of the ASConnectService open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2026 Röck+Cöde VoF. and the ASConnectService project authors
|
||||||
|
// Licensed under Apache license v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE for license information
|
||||||
|
// See CONTRIBUTORS for the list of ASConnectService project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
|
||||||
|
import Testing
|
||||||
|
|
||||||
|
// A non-testable import ensures that the extension can be used from outside the module.
|
||||||
|
import ASConnectService
|
||||||
|
|
||||||
|
@Suite("PagedDocumentLinks")
|
||||||
|
struct PagedDocumentLinksTests {
|
||||||
|
// MARK: Tests
|
||||||
|
|
||||||
|
@Test("Returns the cursor from the next link")
|
||||||
|
func returnsNextCursor() {
|
||||||
|
let links = Components.Schemas.PagedDocumentLinks(
|
||||||
|
_self: .Link.current,
|
||||||
|
next: "https://api.appstoreconnect.apple.com/v1/apps?cursor=Mg.ANmP3sY&limit=2"
|
||||||
|
)
|
||||||
|
|
||||||
|
#expect(links.nextCursor == "Mg.ANmP3sY")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Returns nil when there is no next link")
|
||||||
|
func returnsNilWithoutNextLink() {
|
||||||
|
let links = Components.Schemas.PagedDocumentLinks(_self: .Link.current)
|
||||||
|
|
||||||
|
#expect(links.nextCursor == nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Returns nil when the next link has no cursor")
|
||||||
|
func returnsNilWithoutCursor() {
|
||||||
|
let links = Components.Schemas.PagedDocumentLinks(
|
||||||
|
_self: .Link.current,
|
||||||
|
next: "https://api.appstoreconnect.apple.com/v1/apps?limit=2"
|
||||||
|
)
|
||||||
|
|
||||||
|
#expect(links.nextCursor == nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Passes the cursor to the query of a paged operation")
|
||||||
|
func passesCursorToQuery() {
|
||||||
|
let query = Operations.AppsGetCollection.Input.Query(limit: 2, cursor: "Mg.ANmP3sY")
|
||||||
|
|
||||||
|
#expect(query.cursor == "Mg.ANmP3sY")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - String+Samples
|
||||||
|
|
||||||
|
private extension String {
|
||||||
|
enum Link {
|
||||||
|
static let current: String = "https://api.appstoreconnect.apple.com/v1/apps?limit=2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,24 @@
|
|||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
//
|
||||||
|
// This source file is part of the ASConnectService open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2026 Röck+Cöde VoF. and the ASConnectService project authors
|
||||||
|
// Licensed under Apache license v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE for license information
|
||||||
|
// See CONTRIBUTORS for the list of ASConnectService project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import HTTPTypes
|
import HTTPTypes
|
||||||
import OpenAPIRuntime
|
import OpenAPIRuntime
|
||||||
import Testing
|
import Testing
|
||||||
|
|
||||||
@testable import ASConnectService
|
// A non-testable import ensures that the middleware can be created from outside the module.
|
||||||
|
import ASConnectService
|
||||||
|
|
||||||
@Suite("BearerAuthMiddleware")
|
@Suite("BearerAuthMiddleware")
|
||||||
struct BearerAuthMiddlewareTests {
|
struct BearerAuthMiddlewareTests {
|
||||||
@@ -32,17 +47,14 @@ struct BearerAuthMiddlewareTests {
|
|||||||
let middleware = BearerAuthMiddleware(token: .Token.jwt)
|
let middleware = BearerAuthMiddleware(token: .Token.jwt)
|
||||||
let requestBody: HTTPBody = HTTPBody("request-body")
|
let requestBody: HTTPBody = HTTPBody("request-body")
|
||||||
|
|
||||||
var receivedBody: HTTPBody?
|
// The next handler echoes the body back, so the test can read it.
|
||||||
|
let (_, receivedBody) = try await middleware.intercept(
|
||||||
_ = try await middleware.intercept(
|
|
||||||
.get,
|
.get,
|
||||||
body: requestBody,
|
body: requestBody,
|
||||||
baseURL: .base,
|
baseURL: .base,
|
||||||
operationID: "createApp",
|
operationID: "createApp",
|
||||||
next: { _, body, _ in
|
next: { _, body, _ in
|
||||||
receivedBody = body
|
(HTTPResponse(status: .created), body)
|
||||||
|
|
||||||
return (HTTPResponse(status: .created), nil)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -112,6 +124,67 @@ struct BearerAuthMiddlewareTests {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test("Asks the token provider for a token on every request")
|
||||||
|
func asksTokenProviderOnEveryRequest() async throws {
|
||||||
|
let tokens = TokenSequence()
|
||||||
|
let middleware = BearerAuthMiddleware {
|
||||||
|
await tokens.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
for expectedToken in ["token-1", "token-2"] {
|
||||||
|
_ = try await middleware.intercept(
|
||||||
|
.get,
|
||||||
|
body: nil,
|
||||||
|
baseURL: .base,
|
||||||
|
operationID: "listApps",
|
||||||
|
next: { interceptedRequest, _, _ in
|
||||||
|
#expect(interceptedRequest.headerFields[.authorization] == "Bearer \(expectedToken)")
|
||||||
|
|
||||||
|
return (HTTPResponse(status: .ok), nil)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Rethrows the token provider error without calling the next handler")
|
||||||
|
func rethrowsTokenProviderError() async throws {
|
||||||
|
let middleware = BearerAuthMiddleware {
|
||||||
|
throw TokenError.expired
|
||||||
|
}
|
||||||
|
|
||||||
|
await #expect(throws: TokenError.expired) {
|
||||||
|
_ = try await middleware.intercept(
|
||||||
|
.get,
|
||||||
|
body: nil,
|
||||||
|
baseURL: .base,
|
||||||
|
operationID: "listApps",
|
||||||
|
next: { _, _, _ in
|
||||||
|
Issue.record("The next handler should not be called when the token provider throws")
|
||||||
|
|
||||||
|
return (HTTPResponse(status: .ok), nil)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Helpers
|
||||||
|
|
||||||
|
/// An actor that returns a new token every time it is asked for one.
|
||||||
|
private actor TokenSequence {
|
||||||
|
private var count = 0
|
||||||
|
|
||||||
|
func next() -> String {
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
return "token-\(count)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An error thrown by a token provider.
|
||||||
|
private enum TokenError: Error {
|
||||||
|
case expired
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - HTTPRequest+Samples
|
// MARK: - HTTPRequest+Samples
|
||||||
|
|||||||
@@ -0,0 +1,165 @@
|
|||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
//
|
||||||
|
// This source file is part of the ASConnectService open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2026 Röck+Cöde VoF. and the ASConnectService project authors
|
||||||
|
// Licensed under Apache license v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE for license information
|
||||||
|
// See CONTRIBUTORS for the list of ASConnectService project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import HTTPTypes
|
||||||
|
import OpenAPIRuntime
|
||||||
|
import Testing
|
||||||
|
|
||||||
|
// A non-testable import ensures that the middleware can be created from outside the module.
|
||||||
|
import ASConnectService
|
||||||
|
|
||||||
|
@Suite("RateLimitMiddleware")
|
||||||
|
struct RateLimitMiddlewareTests {
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let recorder = Recorder()
|
||||||
|
|
||||||
|
// MARK: Tests
|
||||||
|
|
||||||
|
@Test("Returns a response that is not rate limited without retrying")
|
||||||
|
func returnsResponseWithoutRetrying() async throws {
|
||||||
|
let response = try await intercept(responses: [.ok])
|
||||||
|
|
||||||
|
#expect(response.status == .ok)
|
||||||
|
#expect(await recorder.calls == 1)
|
||||||
|
#expect(await recorder.delays.isEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Retries rate-limited requests with an exponential backoff")
|
||||||
|
func retriesWithBackoff() async throws {
|
||||||
|
let response = try await intercept(responses: [.tooManyRequests, .tooManyRequests, .ok])
|
||||||
|
|
||||||
|
#expect(response.status == .ok)
|
||||||
|
#expect(await recorder.calls == 3)
|
||||||
|
#expect(await recorder.delays == [1, 2])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Waits for the seconds in the Retry-After header")
|
||||||
|
func waitsForRetryAfter() async throws {
|
||||||
|
let response = try await intercept(responses: [.tooManyRequests(retryAfter: "5"), .ok])
|
||||||
|
|
||||||
|
#expect(response.status == .ok)
|
||||||
|
#expect(await recorder.delays == [5])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Returns the rate-limited response when the retries run out")
|
||||||
|
func returnsResponseWhenRetriesRunOut() async throws {
|
||||||
|
let response = try await intercept(
|
||||||
|
responses: [.tooManyRequests, .tooManyRequests, .tooManyRequests],
|
||||||
|
maximumRetries: 2
|
||||||
|
)
|
||||||
|
|
||||||
|
#expect(response.status == .tooManyRequests)
|
||||||
|
#expect(await recorder.calls == 3)
|
||||||
|
#expect(await recorder.delays == [1, 2])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Returns the rate-limited response when the delay exceeds the maximum")
|
||||||
|
func returnsResponseWhenDelayExceedsMaximum() async throws {
|
||||||
|
let response = try await intercept(responses: [.tooManyRequests(retryAfter: "3600"), .ok])
|
||||||
|
|
||||||
|
#expect(response.status == .tooManyRequests)
|
||||||
|
#expect(await recorder.calls == 1)
|
||||||
|
#expect(await recorder.delays.isEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Does not retry a request whose body can only be sent once")
|
||||||
|
func doesNotRetrySingleIterationBody() async throws {
|
||||||
|
let body = HTTPBody(
|
||||||
|
AsyncStream<HTTPBody.ByteChunk> { $0.finish() },
|
||||||
|
length: .unknown,
|
||||||
|
iterationBehavior: .single
|
||||||
|
)
|
||||||
|
|
||||||
|
let response = try await intercept(responses: [.tooManyRequests, .ok], body: body)
|
||||||
|
|
||||||
|
#expect(response.status == .tooManyRequests)
|
||||||
|
#expect(await recorder.calls == 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Helpers
|
||||||
|
|
||||||
|
private extension RateLimitMiddlewareTests {
|
||||||
|
func intercept(
|
||||||
|
responses: [HTTPResponse],
|
||||||
|
body: HTTPBody? = nil,
|
||||||
|
maximumRetries: Int = 3
|
||||||
|
) async throws -> HTTPResponse {
|
||||||
|
let recorder = recorder
|
||||||
|
let middleware = RateLimitMiddleware(
|
||||||
|
maximumRetries: maximumRetries,
|
||||||
|
baseDelay: 1,
|
||||||
|
maximumDelay: 60,
|
||||||
|
sleep: { await recorder.record(delay: $0) }
|
||||||
|
)
|
||||||
|
|
||||||
|
let (response, _) = try await middleware.intercept(
|
||||||
|
.get,
|
||||||
|
body: body,
|
||||||
|
baseURL: .base,
|
||||||
|
operationID: "apps_getCollection",
|
||||||
|
next: { _, _, _ in
|
||||||
|
(await recorder.response(from: responses), nil)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An actor that records the calls to the next handler, and the delays the middleware waits for.
|
||||||
|
private actor Recorder {
|
||||||
|
private(set) var calls = 0
|
||||||
|
private(set) var delays: [TimeInterval] = []
|
||||||
|
|
||||||
|
func response(from responses: [HTTPResponse]) -> HTTPResponse {
|
||||||
|
defer { calls += 1 }
|
||||||
|
|
||||||
|
return responses[calls]
|
||||||
|
}
|
||||||
|
|
||||||
|
func record(delay: TimeInterval) {
|
||||||
|
delays.append(delay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - HTTPRequest+Samples
|
||||||
|
|
||||||
|
private extension HTTPRequest {
|
||||||
|
static let get = HTTPRequest(
|
||||||
|
method: .get,
|
||||||
|
scheme: "https",
|
||||||
|
authority: "api.appstoreconnect.apple.com",
|
||||||
|
path: "/v1/apps"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - HTTPResponse+Samples
|
||||||
|
|
||||||
|
private extension HTTPResponse {
|
||||||
|
static let ok = HTTPResponse(status: .ok)
|
||||||
|
static let tooManyRequests = HTTPResponse(status: .tooManyRequests)
|
||||||
|
|
||||||
|
static func tooManyRequests(retryAfter: String) -> HTTPResponse {
|
||||||
|
HTTPResponse(status: .tooManyRequests, headerFields: [.retryAfter: retryAfter])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - URL+Samples
|
||||||
|
|
||||||
|
private extension URL {
|
||||||
|
static let base = URL(string: "https://api.appstoreconnect.apple.com")!
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
//
|
||||||
|
// This source file is part of the ASConnectService open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2026 Röck+Cöde VoF. and the ASConnectService project authors
|
||||||
|
// Licensed under Apache license v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE for license information
|
||||||
|
// See CONTRIBUTORS for the list of ASConnectService project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
|
||||||
|
#if canImport(CryptoKit)
|
||||||
|
import CryptoKit
|
||||||
|
import Foundation
|
||||||
|
import Testing
|
||||||
|
|
||||||
|
// A non-testable import ensures that the provider can be created from outside the module.
|
||||||
|
import ASConnectService
|
||||||
|
|
||||||
|
@Suite("APIKeyTokenProvider")
|
||||||
|
struct APIKeyTokenProviderTests {
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let key = P256.Signing.PrivateKey()
|
||||||
|
private let clock = TestClock()
|
||||||
|
|
||||||
|
// MARK: Tests
|
||||||
|
|
||||||
|
@Test("Signs a token for a team key")
|
||||||
|
func signsTeamKeyToken() async throws {
|
||||||
|
let token = try await makeProvider(issuerID: .Sample.issuerID).token()
|
||||||
|
let (header, payload) = try decode(token)
|
||||||
|
|
||||||
|
#expect(header == ["alg": "ES256", "kid": String.Sample.keyID, "typ": "JWT"])
|
||||||
|
#expect(payload["iss"] as? String == .Sample.issuerID)
|
||||||
|
#expect(payload["sub"] == nil)
|
||||||
|
#expect(payload["aud"] as? String == "appstoreconnect-v1")
|
||||||
|
#expect(payload["iat"] as? Int == Int(clock.date.timeIntervalSince1970))
|
||||||
|
#expect(payload["exp"] as? Int == Int(clock.date.timeIntervalSince1970) + 20 * 60)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Signs a token for an individual key")
|
||||||
|
func signsIndividualKeyToken() async throws {
|
||||||
|
let token = try await makeProvider(issuerID: nil).token()
|
||||||
|
let (_, payload) = try decode(token)
|
||||||
|
|
||||||
|
#expect(payload["iss"] == nil)
|
||||||
|
#expect(payload["sub"] as? String == "user")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Signs the token with the private key")
|
||||||
|
func signsWithPrivateKey() async throws {
|
||||||
|
let token = try await makeProvider(issuerID: .Sample.issuerID).token()
|
||||||
|
let parts = token.split(separator: ".").map(String.init)
|
||||||
|
let signature = try P256.Signing.ECDSASignature(rawRepresentation: try #require(Data(base64URL: parts[2])))
|
||||||
|
|
||||||
|
#expect(key.publicKey.isValidSignature(signature, for: Data("\(parts[0]).\(parts[1])".utf8)))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Returns the current token until it is about to expire")
|
||||||
|
func returnsCurrentToken() async throws {
|
||||||
|
let provider = try makeProvider(issuerID: .Sample.issuerID)
|
||||||
|
let token = try await provider.token()
|
||||||
|
|
||||||
|
clock.advance(by: 18 * 60)
|
||||||
|
|
||||||
|
#expect(try await provider.token() == token)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Signs a new token when the current one is about to expire")
|
||||||
|
func signsNewToken() async throws {
|
||||||
|
let provider = try makeProvider(issuerID: .Sample.issuerID)
|
||||||
|
let token = try await provider.token()
|
||||||
|
|
||||||
|
clock.advance(by: 19 * 60 + 1)
|
||||||
|
|
||||||
|
#expect(try await provider.token() != token)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Throws when the private key is not valid")
|
||||||
|
func throwsWithInvalidPrivateKey() {
|
||||||
|
#expect(throws: (any Error).self) {
|
||||||
|
try APIKeyTokenProvider(
|
||||||
|
issuerID: .Sample.issuerID,
|
||||||
|
keyID: .Sample.keyID,
|
||||||
|
privateKey: "not-a-private-key"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Helpers
|
||||||
|
|
||||||
|
private extension APIKeyTokenProviderTests {
|
||||||
|
func makeProvider(issuerID: String?) throws -> APIKeyTokenProvider {
|
||||||
|
try APIKeyTokenProvider(
|
||||||
|
issuerID: issuerID,
|
||||||
|
keyID: .Sample.keyID,
|
||||||
|
privateKey: key.pemRepresentation,
|
||||||
|
now: { [clock] in clock.date }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decode(_ token: String) throws -> (header: [String: String], payload: [String: Any]) {
|
||||||
|
let parts = token.split(separator: ".").map(String.init)
|
||||||
|
|
||||||
|
try #require(parts.count == 3)
|
||||||
|
|
||||||
|
let header = try JSONDecoder().decode([String: String].self, from: try #require(Data(base64URL: parts[0])))
|
||||||
|
let payloadData = try #require(Data(base64URL: parts[1]))
|
||||||
|
let payload = try #require(try JSONSerialization.jsonObject(with: payloadData) as? [String: Any])
|
||||||
|
|
||||||
|
return (header, payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A clock whose date only changes when it is advanced.
|
||||||
|
private final class TestClock: @unchecked Sendable {
|
||||||
|
private(set) var date = Date(timeIntervalSince1970: 1_800_000_000)
|
||||||
|
|
||||||
|
func advance(by interval: TimeInterval) {
|
||||||
|
date = date.addingTimeInterval(interval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Data+Base64URL
|
||||||
|
|
||||||
|
private extension Data {
|
||||||
|
init?(base64URL: String) {
|
||||||
|
var base64 = base64URL
|
||||||
|
.replacingOccurrences(of: "-", with: "+")
|
||||||
|
.replacingOccurrences(of: "_", with: "/")
|
||||||
|
|
||||||
|
base64 += String(repeating: "=", count: (4 - base64.count % 4) % 4)
|
||||||
|
|
||||||
|
self.init(base64Encoded: base64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - String+Samples
|
||||||
|
|
||||||
|
private extension String {
|
||||||
|
enum Sample {
|
||||||
|
static let issuerID: String = "57246542-96fe-1a63-e053-0824d011072a"
|
||||||
|
static let keyID: String = "2X9R4HXF34"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
//
|
||||||
|
// This source file is part of the ASConnectService open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2026 Röck+Cöde VoF. and the ASConnectService project authors
|
||||||
|
// Licensed under Apache license v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE for license information
|
||||||
|
// See CONTRIBUTORS for the list of ASConnectService project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import HTTPTypes
|
||||||
|
import OpenAPIRuntime
|
||||||
|
import Testing
|
||||||
|
|
||||||
|
// A non-testable import ensures that the sequence can be created from outside the module.
|
||||||
|
import ASConnectService
|
||||||
|
|
||||||
|
@Suite("PagedSequence")
|
||||||
|
struct PagedSequenceTests {
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let server = Server(pages: 3)
|
||||||
|
|
||||||
|
// MARK: Tests
|
||||||
|
|
||||||
|
@Test("Fetches every page, passing the cursor of each next link")
|
||||||
|
func fetchesEveryPage() async throws {
|
||||||
|
var pages: [Int] = []
|
||||||
|
|
||||||
|
for try await page in makeSequence() {
|
||||||
|
pages.append(page.number)
|
||||||
|
}
|
||||||
|
|
||||||
|
#expect(pages == [1, 2, 3])
|
||||||
|
#expect(await server.cursors == [nil, "2", "3"])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Does not fetch any page until the sequence is iterated")
|
||||||
|
func fetchesLazily() async throws {
|
||||||
|
_ = makeSequence()
|
||||||
|
|
||||||
|
#expect(await server.cursors.isEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Finishes the iteration after a fetch throws")
|
||||||
|
func finishesAfterError() async throws {
|
||||||
|
await server.fail(atPage: 2)
|
||||||
|
|
||||||
|
var iterator = makeSequence().makeAsyncIterator()
|
||||||
|
|
||||||
|
#expect(try await iterator.next()?.number == 1)
|
||||||
|
|
||||||
|
do {
|
||||||
|
_ = try await iterator.next()
|
||||||
|
|
||||||
|
Issue.record("Fetching the second page should throw")
|
||||||
|
} catch {
|
||||||
|
#expect(error is ServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
#expect(try await iterator.next() == nil)
|
||||||
|
#expect(await server.cursors == [nil, "2"])
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Pages through a generated operation, sending the cursor as a query parameter")
|
||||||
|
func pagesThroughGeneratedOperation() async throws {
|
||||||
|
let transport = PagedTransport()
|
||||||
|
let client = Client(
|
||||||
|
serverURL: try Servers.Server1.url(),
|
||||||
|
transport: transport
|
||||||
|
)
|
||||||
|
|
||||||
|
let pages = PagedSequence(links: \.links) { cursor in
|
||||||
|
try await client.appsGetCollection(query: .init(limit: 200, cursor: cursor)).ok.body.json
|
||||||
|
}
|
||||||
|
|
||||||
|
var count = 0
|
||||||
|
|
||||||
|
for try await _ in pages {
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#expect(count == 2)
|
||||||
|
#expect(await transport.paths == ["/v1/apps?limit=200", "/v1/apps?limit=200&cursor=Mg"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Helpers
|
||||||
|
|
||||||
|
private extension PagedSequenceTests {
|
||||||
|
func makeSequence() -> PagedSequence<TestPage> {
|
||||||
|
let server = server
|
||||||
|
|
||||||
|
return PagedSequence(links: \.links) { cursor in
|
||||||
|
try await server.page(at: cursor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A page of a paged operation.
|
||||||
|
private struct TestPage: Sendable {
|
||||||
|
let number: Int
|
||||||
|
let links: Components.Schemas.PagedDocumentLinks
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An actor that serves a number of pages, and records the cursors it is asked for.
|
||||||
|
private actor Server {
|
||||||
|
private let pages: Int
|
||||||
|
private var failingPage: Int?
|
||||||
|
|
||||||
|
private(set) var cursors: [String?] = []
|
||||||
|
|
||||||
|
init(pages: Int) {
|
||||||
|
self.pages = pages
|
||||||
|
}
|
||||||
|
|
||||||
|
func fail(atPage page: Int) {
|
||||||
|
failingPage = page
|
||||||
|
}
|
||||||
|
|
||||||
|
func page(at cursor: String?) throws -> TestPage {
|
||||||
|
cursors.append(cursor)
|
||||||
|
|
||||||
|
let number = cursor.flatMap { Int($0) } ?? 1
|
||||||
|
|
||||||
|
if number == failingPage {
|
||||||
|
throw ServerError.unavailable
|
||||||
|
}
|
||||||
|
|
||||||
|
let base = "https://api.appstoreconnect.apple.com/v1/apps"
|
||||||
|
|
||||||
|
return TestPage(
|
||||||
|
number: number,
|
||||||
|
links: .init(
|
||||||
|
_self: base,
|
||||||
|
next: number < pages ? "\(base)?cursor=\(number + 1)" : nil
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An error thrown by the server.
|
||||||
|
private enum ServerError: Error {
|
||||||
|
case unavailable
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A transport that serves two pages of apps, and records the paths of the requests it sends.
|
||||||
|
private actor PagedTransport: ClientTransport {
|
||||||
|
private(set) var paths: [String?] = []
|
||||||
|
|
||||||
|
func send(
|
||||||
|
_ request: HTTPRequest,
|
||||||
|
body: HTTPBody?,
|
||||||
|
baseURL: URL,
|
||||||
|
operationID: String
|
||||||
|
) async throws -> (HTTPResponse, HTTPBody?) {
|
||||||
|
paths.append(request.path)
|
||||||
|
|
||||||
|
let isFirstPage = paths.count == 1
|
||||||
|
let next = isFirstPage ? #", "next": "https://api.appstoreconnect.apple.com/v1/apps?cursor=Mg&limit=200""# : ""
|
||||||
|
let json = #"{ "data": [], "links": { "self": "https://api.appstoreconnect.apple.com/v1/apps"\#(next) } }"#
|
||||||
|
|
||||||
|
return (
|
||||||
|
HTTPResponse(status: .ok, headerFields: [.contentType: "application/json"]),
|
||||||
|
HTTPBody(json)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// swift-tools-version: 6.0
|
||||||
|
|
||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
//
|
||||||
|
// This source file is part of the ASConnectService open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2026 Röck+Cöde VoF. and the ASConnectService project authors
|
||||||
|
// Licensed under Apache license v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE for license information
|
||||||
|
// See CONTRIBUTORS for the list of ASConnectService project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
// Development tools, kept out of the main package so consumers don't resolve them.
|
||||||
|
let package = Package(
|
||||||
|
name: "asconnect-service-tools",
|
||||||
|
platforms: [
|
||||||
|
.macOS(.v10_15)
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
.package(url: "https://github.com/apple/swift-openapi-generator.git", exact: "1.11.0")
|
||||||
|
]
|
||||||
|
)
|
||||||
Reference in new issue
Block a user