Overall update and improvements (#4)
This PR contains all the work done to update the App Store Connect OpenAPI specification document to its latest version, plus the implementation of the `BearerAuthMiddleware` middleware, and several other improvements to the setup of the library, the `Makefile` file, and the documentation. Reviewed-on: #4 Co-authored-by: Javier Cicchelli <javier@rock-n-code.com> Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
This commit was merged in pull request #4.
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
##
|
##
|
||||||
## This source file is part of the App Store Connect Service open source project
|
## This source file is part of the App Store Connect Service open source project
|
||||||
##
|
##
|
||||||
## Copyright (c) 2025 Röck+Cöde VoF. and the App Store Connect Service project authors
|
## Copyright (c) 2026 Röck+Cöde VoF. and the App Store Connect Service project authors
|
||||||
## Licensed under Apache license v2.0
|
## Licensed under Apache license v2.0
|
||||||
##
|
##
|
||||||
## See LICENSE for license information
|
## See LICENSE for license information
|
||||||
@@ -19,6 +19,14 @@ 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
|
||||||
|
|
||||||
|
# --- OPEN API SPECIFICATION ---
|
||||||
|
|
||||||
|
OAS_DOWNLOAD_FOLDER=./oas-download
|
||||||
|
OAS_DOWNLOAD_FILE=$(OAS_DOWNLOAD_FOLDER)/openapi.oas.json
|
||||||
|
OAS_DOWNLOAD_INPUT=$(OAS_DOWNLOAD_FOLDER)/asconnect-openapi-specification.zip
|
||||||
|
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
|
||||||
|
|
||||||
# -- SWIFT PACKAGE MANAGER ---
|
# -- SWIFT PACKAGE MANAGER ---
|
||||||
|
|
||||||
SPM_LIBRARY_TARGET=ASConnectService
|
SPM_LIBRARY_TARGET=ASConnectService
|
||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
##
|
##
|
||||||
## This source file is part of the App Store Connect Service open source project
|
## This source file is part of the App Store Connect Service open source project
|
||||||
##
|
##
|
||||||
## Copyright (c) 2025 Röck+Cöde VoF. and the App Store Connect Service project authors
|
## Copyright (c) 2026 Röck+Cöde VoF. and the App Store Connect Service project authors
|
||||||
## Licensed under Apache license v2.0
|
## Licensed under Apache license v2.0
|
||||||
##
|
##
|
||||||
## See LICENSE for license information
|
## See LICENSE for license information
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
{
|
||||||
|
"fileScopedDeclarationPrivacy" : {
|
||||||
|
"accessLevel" : "private"
|
||||||
|
},
|
||||||
|
"indentBlankLines" : false,
|
||||||
|
"indentConditionalCompilationBlocks" : true,
|
||||||
|
"indentSwitchCaseLabels" : true,
|
||||||
|
"indentation" : {
|
||||||
|
"spaces" : 4
|
||||||
|
},
|
||||||
|
"lineBreakAroundMultilineExpressionChainComponents" : true,
|
||||||
|
"lineBreakBeforeControlFlowKeywords" : false,
|
||||||
|
"lineBreakBeforeEachArgument" : true,
|
||||||
|
"lineBreakBeforeEachGenericRequirement" : true,
|
||||||
|
"lineBreakBetweenDeclarationAttributes" : true,
|
||||||
|
"lineLength" : 120,
|
||||||
|
"maximumBlankLines" : 1,
|
||||||
|
"multiElementCollectionTrailingCommas" : true,
|
||||||
|
"noAssignmentInExpressions" : {
|
||||||
|
"allowedFunctions" : [
|
||||||
|
"XCTAssertNoThrow"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"orderedImports" : {
|
||||||
|
"includeConditionalImports" : true
|
||||||
|
},
|
||||||
|
"prioritizeKeepingFunctionOutputTogether" : true,
|
||||||
|
"reflowMultilineStringLiterals" : "never",
|
||||||
|
"respectsExistingLineBreaks" : true,
|
||||||
|
"rules" : {
|
||||||
|
"AllPublicDeclarationsHaveDocumentation" : true,
|
||||||
|
"AlwaysUseLiteralForEmptyCollectionInit" : true,
|
||||||
|
"AlwaysUseLowerCamelCase" : true,
|
||||||
|
"AmbiguousTrailingClosureOverload" : true,
|
||||||
|
"AvoidRetroactiveConformances" : true,
|
||||||
|
"BeginDocumentationCommentWithOneLineSummary" : true,
|
||||||
|
"DoNotUseSemicolons" : true,
|
||||||
|
"DontRepeatTypeInStaticProperties" : true,
|
||||||
|
"FileScopedDeclarationPrivacy" : true,
|
||||||
|
"FullyIndirectEnum" : true,
|
||||||
|
"GroupNumericLiterals" : true,
|
||||||
|
"IdentifiersMustBeASCII" : true,
|
||||||
|
"NeverForceUnwrap" : true,
|
||||||
|
"NeverUseForceTry" : true,
|
||||||
|
"NeverUseImplicitlyUnwrappedOptionals" : true,
|
||||||
|
"NoAccessLevelOnExtensionDeclaration" : false,
|
||||||
|
"NoAssignmentInExpressions" : true,
|
||||||
|
"NoBlockComments" : true,
|
||||||
|
"NoCasesWithOnlyFallthrough" : true,
|
||||||
|
"NoEmptyLinesOpeningClosingBraces" : true,
|
||||||
|
"NoEmptyTrailingClosureParentheses" : true,
|
||||||
|
"NoLabelsInCasePatterns" : true,
|
||||||
|
"NoLeadingUnderscores" : true,
|
||||||
|
"NoParensAroundConditions" : true,
|
||||||
|
"NoPlaygroundLiterals" : true,
|
||||||
|
"NoVoidReturnOnFunctionSignature" : true,
|
||||||
|
"OmitExplicitReturns" : true,
|
||||||
|
"OneCasePerLine" : true,
|
||||||
|
"OneVariableDeclarationPerLine" : true,
|
||||||
|
"OnlyOneTrailingClosureArgument" : true,
|
||||||
|
"OrderedImports" : true,
|
||||||
|
"ReplaceForEachWithForLoop" : true,
|
||||||
|
"ReturnVoidInsteadOfEmptyTuple" : true,
|
||||||
|
"TypeNamesShouldBeCapitalized" : true,
|
||||||
|
"UseEarlyExits" : true,
|
||||||
|
"UseExplicitNilCheckInConditions" : true,
|
||||||
|
"UseLetInEveryBoundCaseVariable" : true,
|
||||||
|
"UseShorthandTypeNames" : true,
|
||||||
|
"UseSingleLinePropertyGetter" : true,
|
||||||
|
"UseSynthesizedInitializer" : true,
|
||||||
|
"UseTripleSlashForDocumentationComments" : true,
|
||||||
|
"UseWhereClausesInForLoops" : true,
|
||||||
|
"ValidateDocumentationComments" : true
|
||||||
|
},
|
||||||
|
"spacesAroundRangeFormationOperators" : true,
|
||||||
|
"spacesBeforeEndOfLineComments" : 2,
|
||||||
|
"tabWidth" : 8,
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
##
|
##
|
||||||
## This source file is part of the App Store Connect Service open source project
|
## This source file is part of the App Store Connect Service open source project
|
||||||
##
|
##
|
||||||
## Copyright (c) 2025 Röck+Cöde VoF. and the App Store Connect Service project authors
|
## Copyright (c) 2026 Röck+Cöde VoF. and the App Store Connect Service project authors
|
||||||
## Licensed under Apache license v2.0
|
## Licensed under Apache license v2.0
|
||||||
##
|
##
|
||||||
## See LICENSE for license information
|
## See LICENSE for license information
|
||||||
@@ -86,6 +86,17 @@ doc-preview: ## Previews the library documentation in Safari
|
|||||||
--include-extended-types \
|
--include-extended-types \
|
||||||
--enable-inherited-docs
|
--enable-inherited-docs
|
||||||
|
|
||||||
|
# OPEN API SPECIFICATION
|
||||||
|
|
||||||
|
oas-download: ## Downloads the latest App Store Connect OpenAPI specification document
|
||||||
|
@mkdir -p $(OAS_DOWNLOAD_FOLDER)
|
||||||
|
@curl -fsSL \
|
||||||
|
"$(OAS_DOWNLOAD_URL)" \
|
||||||
|
-o "$(OAS_DOWNLOAD_INPUT)"
|
||||||
|
@unzip -q "$(OAS_DOWNLOAD_INPUT)" -d "$(OAS_DOWNLOAD_FOLDER)"
|
||||||
|
@mv "$(OAS_DOWNLOAD_FILE)" "$(OAS_DOWNLOAD_OUTPUT)"
|
||||||
|
@rm -rf "$(OAS_DOWNLOAD_FOLDER)"
|
||||||
|
|
||||||
# IDE
|
# IDE
|
||||||
|
|
||||||
ide-xcode: ## Opens this package with Xcode
|
ide-xcode: ## Opens this package with Xcode
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Please visit the App Store Connect Service web site for more information:
|
|||||||
|
|
||||||
* https://github.com/rock-n-code/asconnect-service
|
* https://github.com/rock-n-code/asconnect-service
|
||||||
|
|
||||||
Copyright 2025 Röck+Cöde VoF
|
Copyright 2026 Röck+Cöde VoF
|
||||||
|
|
||||||
The Marvel Service Project licenses this file to you under the Apache License,
|
The Marvel Service Project licenses this file to you under the Apache License,
|
||||||
version 2.0 (the "License"); you may not use this file except in compliance
|
version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
|||||||
+2
-2
@@ -4,7 +4,7 @@
|
|||||||
//
|
//
|
||||||
// This source file is part of the ASConnectService open source project
|
// This source file is part of the ASConnectService open source project
|
||||||
//
|
//
|
||||||
// Copyright (c) 2025 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 the EUPL 1.2 or later.
|
||||||
//
|
//
|
||||||
// See LICENSE for license information
|
// See LICENSE for license information
|
||||||
@@ -30,7 +30,7 @@ let package = Package(
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.package(url: "https://github.com/apple/swift-openapi-generator.git", from: "1.3.0"),
|
.package(url: "https://github.com/apple/swift-openapi-generator.git", exact: "1.11.0"),
|
||||||
.package(url: "https://github.com/apple/swift-openapi-runtime", from: "1.5.0"),
|
.package(url: "https://github.com/apple/swift-openapi-runtime", from: "1.5.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")
|
.package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.1.0")
|
||||||
|
|||||||
@@ -1 +1,113 @@
|
|||||||
# App Store Connect Service (ASConnectService)
|
# App Store Connect Service (ASConnectService)
|
||||||
|
|
||||||
|
A Swift client library for the App Store Connect API, generated from the official OpenAPI specification.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Add `ASConnectService` as a dependency in your `Package.swift` file:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
dependencies: [
|
||||||
|
.package(url: "https://github.com/rock-n-code/asconnect-service.git", from: "1.0.0")
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
Then add it to your target dependencies:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
.target(
|
||||||
|
name: "YourTarget",
|
||||||
|
dependencies: [
|
||||||
|
"ASConnectService"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### 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).
|
||||||
|
|
||||||
|
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
|
||||||
|
import ASConnectService
|
||||||
|
import OpenAPIURLSession
|
||||||
|
|
||||||
|
let client = Client(
|
||||||
|
serverURL: try Servers.server1(),
|
||||||
|
transport: URLSessionTransport(),
|
||||||
|
middlewares: [
|
||||||
|
BearerAuthMiddleware(token: yourJWTToken)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Making API Calls
|
||||||
|
|
||||||
|
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
|
||||||
|
let response = try await client.appsGetCollection(.init())
|
||||||
|
```
|
||||||
|
|
||||||
|
## Supported Platforms
|
||||||
|
|
||||||
|
- iOS 13.0+
|
||||||
|
- macOS 10.15+
|
||||||
|
- tvOS 13.0+
|
||||||
|
- visionOS 1.0+
|
||||||
|
- watchOS 6.0+
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### Building
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make lib-build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make lib-test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Generating Documentation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate documentation archive (for Xcode)
|
||||||
|
make doc-generate-archive
|
||||||
|
|
||||||
|
# Generate documentation for static hosting (e.g. GitHub Pages)
|
||||||
|
make doc-generate-github
|
||||||
|
|
||||||
|
# Preview documentation locally in Safari
|
||||||
|
make doc-preview
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- [swift-openapi-generator](https://github.com/apple/swift-openapi-generator)
|
||||||
|
- [swift-openapi-runtime](https://github.com/apple/swift-openapi-runtime)
|
||||||
|
- [swift-openapi-urlsession](https://github.com/apple/swift-openapi-urlsession)
|
||||||
|
- [swift-docc-plugin](https://github.com/swiftlang/swift-docc-plugin)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for details.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
See [CONTRIBUTORS](CONTRIBUTORS) for the list of project authors.
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
|||||||
|
# ``Client``
|
||||||
|
|
||||||
|
The API client for performing HTTP operations against the App Store Connect API.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
### Creating a Client
|
||||||
|
|
||||||
|
Create a ``Client`` by providing a server URL, a transport, and optionally a list of middlewares for authentication or request customization.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
import ASConnectService
|
||||||
|
import OpenAPIURLSession
|
||||||
|
|
||||||
|
let client = Client(
|
||||||
|
serverURL: try Servers.server1(),
|
||||||
|
transport: URLSessionTransport(),
|
||||||
|
middlewares: [
|
||||||
|
BearerAuthMiddleware(token: yourJWTToken)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Making API Calls
|
||||||
|
|
||||||
|
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
|
||||||
|
let response = try await client.appsGetCollection(.init())
|
||||||
|
```
|
||||||
|
|
||||||
|
## Topics
|
||||||
|
|
||||||
|
### Creating a Client
|
||||||
|
|
||||||
|
- ``init(serverURL:configuration:transport:middlewares:)``
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
|||||||
|
# ``Servers``
|
||||||
|
|
||||||
|
Server URLs defined in the OpenAPI document.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let client = Client(
|
||||||
|
serverURL: try Servers.Server1.url(),
|
||||||
|
transport: URLSessionTransport(),
|
||||||
|
middlewares: [
|
||||||
|
BearerAuthMiddleware(token: yourJWTToken)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Topics
|
||||||
|
|
||||||
|
### Server URLs
|
||||||
|
|
||||||
|
- ``Servers/Server1``
|
||||||
|
- ``Servers/server1()``
|
||||||
@@ -1,13 +1,51 @@
|
|||||||
# ``ASConnectService``
|
# ``ASConnectService``
|
||||||
|
|
||||||
<!--@START_MENU_TOKEN@-->Summary<!--@END_MENU_TOKEN@-->
|
A Swift client library for the App Store Connect API, generated from the official OpenAPI specification.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
<!--@START_MENU_TOKEN@-->Text<!--@END_MENU_TOKEN@-->
|
``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.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Create a ``Client`` instance by providing a server URL and transport. Use ``BearerAuthMiddleware`` to authenticate requests with a JSON Web Token (JWT).
|
||||||
|
|
||||||
|
```swift
|
||||||
|
import ASConnectService
|
||||||
|
import OpenAPIURLSession
|
||||||
|
|
||||||
|
let client = Client(
|
||||||
|
serverURL: try Servers.server1(),
|
||||||
|
transport: URLSessionTransport(),
|
||||||
|
middlewares: [
|
||||||
|
BearerAuthMiddleware(token: yourJWTToken)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Making API Calls
|
||||||
|
|
||||||
|
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
|
||||||
|
let response = try await client.appsGetCollection(.init())
|
||||||
|
```
|
||||||
|
|
||||||
## Topics
|
## Topics
|
||||||
|
|
||||||
### <!--@START_MENU_TOKEN@-->Group<!--@END_MENU_TOKEN@-->
|
### API Client
|
||||||
|
|
||||||
- <!--@START_MENU_TOKEN@-->``Symbol``<!--@END_MENU_TOKEN@-->
|
- ``Client``
|
||||||
|
- ``APIProtocol``
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
|
||||||
|
- ``BearerAuthMiddleware``
|
||||||
|
|
||||||
|
### Generated Types
|
||||||
|
|
||||||
|
- ``Components``
|
||||||
|
- ``Operations``
|
||||||
|
- ``Servers``
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
//
|
||||||
|
// 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 struct Foundation.URLComponents
|
||||||
|
import struct Foundation.URLQueryItem
|
||||||
|
import struct HTTPTypes.HTTPField
|
||||||
|
import struct HTTPTypes.HTTPFields
|
||||||
|
import struct HTTPTypes.HTTPRequest
|
||||||
|
import struct HTTPTypes.HTTPResponse
|
||||||
|
import protocol OpenAPIRuntime.ClientMiddleware
|
||||||
|
import class OpenAPIRuntime.HTTPBody
|
||||||
|
|
||||||
|
/// A client middleware that injects a Bearer authentication token into outgoing HTTP requests.
|
||||||
|
///
|
||||||
|
/// This middleware appends an `Authorization` header with a Bearer token to every request
|
||||||
|
/// before forwarding it to the next handler in the middleware chain. It is intended for use
|
||||||
|
/// with the App Store Connect API, which requires JSON Web Token (JWT) authentication.
|
||||||
|
///
|
||||||
|
/// ## Usage
|
||||||
|
///
|
||||||
|
/// ```swift
|
||||||
|
/// let middleware = BearerAuthMiddleware(token: "your-jwt-token")
|
||||||
|
/// ```
|
||||||
|
public struct BearerAuthMiddleware {
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
/// The Bearer token to include in the `Authorization` header of each request.
|
||||||
|
private let token: String
|
||||||
|
|
||||||
|
// MARK: Initializers
|
||||||
|
|
||||||
|
/// Creates a new middleware instance with the given Bearer token.
|
||||||
|
/// - Parameter token: A JSON Web Token (JWT) string used to authenticate requests to the App Store Connect API.
|
||||||
|
init(token: String) {
|
||||||
|
self.token = token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - ClientMiddleware
|
||||||
|
|
||||||
|
extension BearerAuthMiddleware: ClientMiddleware {
|
||||||
|
// MARK: Methods
|
||||||
|
|
||||||
|
/// Intercepts an outgoing HTTP request and adds a Bearer authentication token to its headers.
|
||||||
|
/// - 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.
|
||||||
|
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 request = request
|
||||||
|
request.headerFields[.authorization] = "Bearer \(token)"
|
||||||
|
|
||||||
|
return try await next(
|
||||||
|
request,
|
||||||
|
body,
|
||||||
|
baseURL
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
// The Swift Programming Language
|
|
||||||
// https://docs.swift.org/swift-book
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
##
|
##
|
||||||
## This source file is part of the App Store Connect Service open source project
|
## This source file is part of the App Store Connect Service open source project
|
||||||
##
|
##
|
||||||
## Copyright (c) 2025 Röck+Cöde VoF. and the App Store Connect Service project authors
|
## Copyright (c) 2026 Röck+Cöde VoF. and the App Store Connect Service project authors
|
||||||
## Licensed under Apache license v2.0
|
## Licensed under Apache license v2.0
|
||||||
##
|
##
|
||||||
## See LICENSE for license information
|
## See LICENSE for license information
|
||||||
@@ -15,5 +15,5 @@
|
|||||||
generate:
|
generate:
|
||||||
- types
|
- types
|
||||||
- client
|
- client
|
||||||
namingStrategy: defensive
|
namingStrategy: idiomatic
|
||||||
accessModifier: public
|
accessModifier: public
|
||||||
|
|||||||
+230191
-208663
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,140 @@
|
|||||||
|
import Foundation
|
||||||
|
import HTTPTypes
|
||||||
|
import OpenAPIRuntime
|
||||||
|
import Testing
|
||||||
|
|
||||||
|
@testable import ASConnectService
|
||||||
|
|
||||||
|
@Suite("BearerAuthMiddleware")
|
||||||
|
struct BearerAuthMiddlewareTests {
|
||||||
|
// MARK: Tests
|
||||||
|
|
||||||
|
@Test("Adds the Authorization header with the Bearer token to the request")
|
||||||
|
func addsAuthorizationHeader() async throws {
|
||||||
|
let middleware = BearerAuthMiddleware(token: .Token.jwt)
|
||||||
|
|
||||||
|
let (response, _) = try await middleware.intercept(
|
||||||
|
.get,
|
||||||
|
body: nil,
|
||||||
|
baseURL: .base,
|
||||||
|
operationID: "listApps",
|
||||||
|
next: { interceptedRequest, body, baseURL in
|
||||||
|
#expect(interceptedRequest.headerFields[.authorization] == "Bearer test-jwt-token")
|
||||||
|
return (HTTPResponse(status: .ok), nil)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
#expect(response.status == .ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Forwards the request body to the next handler")
|
||||||
|
func forwardsRequestBody() async throws {
|
||||||
|
let middleware = BearerAuthMiddleware(token: .Token.jwt)
|
||||||
|
let requestBody: HTTPBody = HTTPBody("request-body")
|
||||||
|
|
||||||
|
var receivedBody: HTTPBody?
|
||||||
|
|
||||||
|
_ = try await middleware.intercept(
|
||||||
|
.get,
|
||||||
|
body: requestBody,
|
||||||
|
baseURL: .base,
|
||||||
|
operationID: "createApp",
|
||||||
|
next: { _, body, _ in
|
||||||
|
receivedBody = body
|
||||||
|
|
||||||
|
return (HTTPResponse(status: .created), nil)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
let bodyData = try await Data(collecting: try #require(receivedBody), upTo: .max)
|
||||||
|
|
||||||
|
#expect(bodyData == Data("request-body".utf8))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Forwards the base URL to the next handler")
|
||||||
|
func forwardsBaseURL() async throws {
|
||||||
|
let middleware = BearerAuthMiddleware(token: .Token.jwt)
|
||||||
|
let expectedBaseURL = URL.base
|
||||||
|
|
||||||
|
_ = try await middleware.intercept(
|
||||||
|
.get,
|
||||||
|
body: nil,
|
||||||
|
baseURL: expectedBaseURL,
|
||||||
|
operationID: "listApps",
|
||||||
|
next: { _, _, baseURL in
|
||||||
|
#expect(baseURL == expectedBaseURL)
|
||||||
|
|
||||||
|
return (HTTPResponse(status: .ok), nil)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Returns the response from the next handler")
|
||||||
|
func returnsNextHandlerResponse() async throws {
|
||||||
|
let middleware = BearerAuthMiddleware(token: .Token.jwt)
|
||||||
|
let expectedBody: HTTPBody = HTTPBody("response-body")
|
||||||
|
|
||||||
|
let (response, body) = try await middleware.intercept(
|
||||||
|
.get,
|
||||||
|
body: nil,
|
||||||
|
baseURL: .base,
|
||||||
|
operationID: "listApps",
|
||||||
|
next: { _, _, _ in
|
||||||
|
(HTTPResponse(status: .notFound), expectedBody)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
#expect(response.status == .notFound)
|
||||||
|
|
||||||
|
let bodyData = try await Data(collecting: try #require(body), upTo: .max)
|
||||||
|
|
||||||
|
#expect(bodyData == Data("response-body".utf8))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Preserves existing request headers")
|
||||||
|
func preservesExistingHeaders() async throws {
|
||||||
|
let middleware = BearerAuthMiddleware(token: .Token.jwt)
|
||||||
|
|
||||||
|
var request = HTTPRequest.get
|
||||||
|
|
||||||
|
request.headerFields[.contentType] = "application/json"
|
||||||
|
|
||||||
|
_ = try await middleware.intercept(
|
||||||
|
request,
|
||||||
|
body: nil,
|
||||||
|
baseURL: .base,
|
||||||
|
operationID: "listApps",
|
||||||
|
next: { interceptedRequest, _, _ in
|
||||||
|
#expect(interceptedRequest.headerFields[.contentType] == "application/json")
|
||||||
|
#expect(interceptedRequest.headerFields[.authorization] == "Bearer \(String.Token.jwt)")
|
||||||
|
|
||||||
|
return (HTTPResponse(status: .ok), nil)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - HTTPRequest+Samples
|
||||||
|
|
||||||
|
private extension HTTPRequest {
|
||||||
|
static let get = HTTPRequest(
|
||||||
|
method: .get,
|
||||||
|
scheme: "https",
|
||||||
|
authority: "api.appstoreconnect.apple.com",
|
||||||
|
path: "/v1/apps"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - String+Samples
|
||||||
|
|
||||||
|
private extension String {
|
||||||
|
enum Token {
|
||||||
|
static let jwt: String = "test-jwt-token"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - URL+Samples
|
||||||
|
|
||||||
|
private extension URL {
|
||||||
|
static let base = URL(string: "https://api.appstoreconnect.apple.com")!
|
||||||
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import Testing
|
|
||||||
@testable import asconnect_service
|
|
||||||
|
|
||||||
@Test func example() async throws {
|
|
||||||
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user