Implemented the User Agent middleware #6
@@ -43,6 +43,7 @@ public struct AuthMiddleware {
|
|||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - method: A representation of an authentication method to use to authenticate requests.
|
/// - method: A representation of an authentication method to use to authenticate requests.
|
||||||
/// - transport: A representation of a transport option to send credentials in requests.
|
/// - transport: A representation of a transport option to send credentials in requests.
|
||||||
|
/// - Throws: An error of type ``InputValidationError`` in case an input failed any validation.
|
||||||
public init(
|
public init(
|
||||||
method: AuthMethod = .none,
|
method: AuthMethod = .none,
|
||||||
transport: AuthTransport
|
transport: AuthTransport
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
//
|
||||||
|
// This source file is part of the DiscogsService open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2025 Röck+Cöde VoF. and the DiscogsService project authors
|
||||||
|
// Licensed under Apache license v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE for license information
|
||||||
|
// See CONTRIBUTORS for the list of DiscogsService project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
|
||||||
|
import class OpenAPIRuntime.HTTPBody
|
||||||
|
|
||||||
|
import protocol OpenAPIRuntime.ClientMiddleware
|
||||||
|
|
||||||
|
import struct Foundation.URL
|
||||||
|
import struct HTTPTypes.HTTPField
|
||||||
|
import struct HTTPTypes.HTTPFields
|
||||||
|
import struct HTTPTypes.HTTPRequest
|
||||||
|
import struct HTTPTypes.HTTPResponse
|
||||||
|
|
||||||
|
/// A middleware that attaches the user agent header into the requests to the service.
|
||||||
|
///
|
||||||
|
/// Please refer to the [Discogs documentation](https://www.discogs.com/developers/#page:home,header:home-general-information) for further information.
|
||||||
|
public struct UserAgentMiddleware {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
/// A formatted value for the user agent header.
|
||||||
|
let agentField: HTTPField
|
||||||
|
|
||||||
|
// MARK: Initializers
|
||||||
|
|
||||||
|
/// Initializes this middleware.
|
||||||
|
/// - Parameter product: A product from which the user agent will be generated from.
|
||||||
|
/// - Throws: An error of type ``InputValidationError`` in case an input failed any validation.
|
||||||
|
public init(product: Product) throws {
|
||||||
|
let agentName = ValidateInputUseCase(rules: .notNil, .notEmpty, .camelCase)
|
||||||
|
let agentVersion = ValidateInputUseCase(rules: .notNil, .notEmpty, .semanticVersion)
|
||||||
|
let agentURL = ValidateInputUseCase(rules: .notNil, .notEmpty, .url)
|
||||||
|
|
||||||
|
try agentName(product.name)
|
||||||
|
try agentVersion(product.version)
|
||||||
|
try agentURL(product.url)
|
||||||
|
|
||||||
|
self.agentField = .init(
|
||||||
|
name: .userAgent,
|
||||||
|
value: .init(format: .Format.userAgent, product.name, product.version, product.url)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - ClientMiddleware
|
||||||
|
|
||||||
|
extension UserAgentMiddleware: ClientMiddleware {
|
||||||
|
|
||||||
|
// MARK: Functions
|
||||||
|
|
||||||
|
public func intercept(
|
||||||
|
_ request: HTTPRequest,
|
||||||
|
body: HTTPBody?,
|
||||||
|
baseURL: URL,
|
||||||
|
operationID: String,
|
||||||
|
next: @Sendable (HTTPRequest, HTTPBody?, URL) async throws -> (HTTPResponse, HTTPBody?)
|
||||||
|
) async throws -> (HTTPResponse, HTTPBody?) {
|
||||||
|
return try await next(
|
||||||
|
.init(
|
||||||
|
method: request.method,
|
||||||
|
scheme: request.scheme,
|
||||||
|
authority: request.authority,
|
||||||
|
path: request.path,
|
||||||
|
headerFields: userAgentHeader(request.headerFields)
|
||||||
|
),
|
||||||
|
body,
|
||||||
|
baseURL
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Helpers
|
||||||
|
|
||||||
|
private extension UserAgentMiddleware {
|
||||||
|
|
||||||
|
// MARK: Functions
|
||||||
|
|
||||||
|
/// Adds a user agent header to the existing header fields.
|
||||||
|
/// - Parameter fields: A set of header fields to update.
|
||||||
|
/// - Returns: An updated set of header fields including the user agent header.
|
||||||
|
func userAgentHeader(_ fields: HTTPFields) -> HTTPFields {
|
||||||
|
var fields = fields
|
||||||
|
|
||||||
|
fields.append(agentField)
|
||||||
|
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Constants
|
||||||
|
|
||||||
|
private extension String.Format {
|
||||||
|
/// A format for the user agent header.
|
||||||
|
static let userAgent = "%@/%@ +%@"
|
||||||
|
}
|
||||||
@@ -15,7 +15,6 @@
|
|||||||
import struct Foundation.URL
|
import struct Foundation.URL
|
||||||
import struct Foundation.URLComponents
|
import struct Foundation.URLComponents
|
||||||
import struct Foundation.URLQueryItem
|
import struct Foundation.URLQueryItem
|
||||||
import struct HTTPTypes.HTTPField
|
|
||||||
import struct HTTPTypes.HTTPFields
|
import struct HTTPTypes.HTTPFields
|
||||||
import struct HTTPTypes.HTTPRequest
|
import struct HTTPTypes.HTTPRequest
|
||||||
import struct HTTPTypes.HTTPResponse
|
import struct HTTPTypes.HTTPResponse
|
||||||
@@ -28,6 +27,7 @@ import Testing
|
|||||||
struct AuthMiddlewareTests {
|
struct AuthMiddlewareTests {
|
||||||
|
|
||||||
// MARK: Initializers tests
|
// MARK: Initializers tests
|
||||||
|
|
||||||
#if swift(>=6.2)
|
#if swift(>=6.2)
|
||||||
@Test(arguments: Input.authMethods)
|
@Test(arguments: Input.authMethods)
|
||||||
func `initialize`(
|
func `initialize`(
|
||||||
@@ -273,11 +273,11 @@ private extension AuthMiddlewareTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asserts the error throwing (if justified) during the initialization of the middleware.
|
/// Asserts the error throwing (if justified) during the initialization of a middleware.
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - authMethod: A representation of an authentication method.
|
/// - authMethod: A representation of an authentication method.
|
||||||
/// - authTransport: A representation of an authentication transport.
|
/// - authTransport: A representation of an authentication transport.
|
||||||
/// - error: An expected error of type ``InputValidationError`` during the initialization of the middleware.
|
/// - error: An expected error of type ``InputValidationError`` during the initialization of a middleware.
|
||||||
func assertInitThrows(
|
func assertInitThrows(
|
||||||
authMethod: AuthMethod,
|
authMethod: AuthMethod,
|
||||||
authTransport: AuthTransport,
|
authTransport: AuthTransport,
|
||||||
@@ -331,8 +331,8 @@ private extension AuthMiddlewareTests {
|
|||||||
try await middleware.intercept(
|
try await middleware.intercept(
|
||||||
request,
|
request,
|
||||||
body: nil,
|
body: nil,
|
||||||
baseURL: .baseURL,
|
baseURL: .Sample.baseURL,
|
||||||
operationID: .operationId
|
operationID: .Sample.operationId
|
||||||
) { request, _, _ in
|
) { request, _, _ in
|
||||||
// THEN
|
// THEN
|
||||||
switch (authMethod, authTransport) {
|
switch (authMethod, authTransport) {
|
||||||
@@ -430,31 +430,6 @@ private extension AuthMiddlewareTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension HTTPRequest {
|
|
||||||
|
|
||||||
// MARK: Initializers
|
|
||||||
|
|
||||||
/// Initializes a HTTP request conveniently.
|
|
||||||
/// - Parameters:
|
|
||||||
/// - method: A request method.
|
|
||||||
/// - path: A value of the “:path” pseudo header field.
|
|
||||||
/// - headerFields: A dictionary of request header fields.
|
|
||||||
init(
|
|
||||||
method: HTTPRequest.Method = .get,
|
|
||||||
path: String?,
|
|
||||||
headerFields: HTTPFields = [:]
|
|
||||||
) {
|
|
||||||
self.init(
|
|
||||||
method: method,
|
|
||||||
scheme: nil,
|
|
||||||
authority: nil,
|
|
||||||
path: path,
|
|
||||||
headerFields: headerFields
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Constants
|
// MARK: - Constants
|
||||||
|
|
||||||
private extension Input {
|
private extension Input {
|
||||||
@@ -464,7 +439,7 @@ private extension Input {
|
|||||||
.user(token: "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStT"),
|
.user(token: "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStT"),
|
||||||
.none
|
.none
|
||||||
]
|
]
|
||||||
/// A list of authentication methods to tests for the initialization throw test cases.
|
/// A list of authentication methods to use in the initialization throw test cases.
|
||||||
static let authMethodsThrows: [AuthMethod] = authMethods + [
|
static let authMethodsThrows: [AuthMethod] = authMethods + [
|
||||||
.consumer(key: .empty, secret: "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpP"),
|
.consumer(key: .empty, secret: "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpP"),
|
||||||
.consumer(key: "aAbBcCdDeEfFgGhHiI", secret: "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpP"),
|
.consumer(key: "aAbBcCdDeEfFgGhHiI", secret: "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpP"),
|
||||||
@@ -487,13 +462,3 @@ private extension Output {
|
|||||||
/// A list of expected boolean flags coming from the should authenticate test cases.
|
/// A list of expected boolean flags coming from the should authenticate test cases.
|
||||||
static let authMethodsShouldAuthenticate: [Bool] = [true, true, false]
|
static let authMethodsShouldAuthenticate: [Bool] = [true, true, false]
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension String {
|
|
||||||
/// An operation ID sample.
|
|
||||||
static let operationId = "SomeOperationId"
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension URL {
|
|
||||||
/// A base URL sample.
|
|
||||||
static let baseURL = URL(string: "https://sample.domain.com")!
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -12,14 +12,215 @@
|
|||||||
//
|
//
|
||||||
// ===----------------------------------------------------------------------===
|
// ===----------------------------------------------------------------------===
|
||||||
|
|
||||||
import DiscogsService
|
import struct HTTPTypes.HTTPField
|
||||||
|
import struct HTTPTypes.HTTPFields
|
||||||
|
import struct HTTPTypes.HTTPRequest
|
||||||
|
|
||||||
import Testing
|
import Testing
|
||||||
|
|
||||||
|
@testable import DiscogsService
|
||||||
|
|
||||||
@Suite("User Agent Middleware", .tags(.middleware))
|
@Suite("User Agent Middleware", .tags(.middleware))
|
||||||
struct UserAgentMiddlewareTests {
|
struct UserAgentMiddlewareTests {
|
||||||
|
|
||||||
// @Test func <#test function name#>() async throws {
|
// MARK: Initializers tests
|
||||||
// // Write your test here and use APIs like `#expect(...)` to check expected conditions.
|
|
||||||
// }
|
#if swift(>=6.2)
|
||||||
|
@Test(arguments: Input.userAgents)
|
||||||
|
func `initialize`(
|
||||||
|
product: Product
|
||||||
|
) throws {
|
||||||
|
try assertInit(product: product)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(arguments: zip(
|
||||||
|
Input.userAgentsThrows,
|
||||||
|
Output.userAgentsThrows
|
||||||
|
))
|
||||||
|
func `initialize throws`(
|
||||||
|
product: Product,
|
||||||
|
expect error: InputValidationError?
|
||||||
|
) {
|
||||||
|
assertInitThrows(
|
||||||
|
product: product,
|
||||||
|
expects: error
|
||||||
|
)
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
@Test("initialize", arguments: Input.userAgents)
|
||||||
|
func initialize(
|
||||||
|
product: Product
|
||||||
|
) throws {
|
||||||
|
try assertInit(product: product)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("initialize throws", arguments: zip(
|
||||||
|
Input.userAgentsThrows,
|
||||||
|
Output.userAgentsThrows
|
||||||
|
))
|
||||||
|
func initializeThrows(
|
||||||
|
product: Product,
|
||||||
|
expect error: InputValidationError?
|
||||||
|
) {
|
||||||
|
assertInitThrows(
|
||||||
|
product: product,
|
||||||
|
expects: error
|
||||||
|
)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// MARK: Functions tests
|
||||||
|
|
||||||
|
#if swift(>=6.2)
|
||||||
|
@Test(arguments: Input.userAgents)
|
||||||
|
func `intercept with user agent on headers`(
|
||||||
|
product: Product
|
||||||
|
) async throws {
|
||||||
|
try await assertIntercept(product: product)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(arguments: Input.userAgents)
|
||||||
|
func `intercept with user agent on headers when headers are populated`(
|
||||||
|
product: Product
|
||||||
|
) async throws {
|
||||||
|
try await assertIntercept(
|
||||||
|
product: product,
|
||||||
|
headerFields: [.accept: "*/*"]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
@Test("intercept with user agent on headers", arguments: Input.userAgents)
|
||||||
|
func intercept_withUserAgentOnHeaders(
|
||||||
|
product: Product
|
||||||
|
) async throws {
|
||||||
|
try await assertIntercept(product: product)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("intercept with user agent on headers when headers are populated", arguments: Input.userAgents)
|
||||||
|
func intercept_withUserAgentOnHeaders_whenHeadersPopulated(
|
||||||
|
product: Product
|
||||||
|
) async throws {
|
||||||
|
try await assertIntercept(
|
||||||
|
product: product,
|
||||||
|
headerFields: [.accept: "*/*"]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Assertions
|
||||||
|
|
||||||
|
private extension UserAgentMiddlewareTests {
|
||||||
|
|
||||||
|
// MARK: Functions
|
||||||
|
|
||||||
|
/// Asserts the initialization of the middleware , especially the assignments of its properties.
|
||||||
|
/// - Parameter product: A product to initialize a middleware.
|
||||||
|
/// - Throws: an error of type ``InputValidationError`` in case of an unexpected error occurs while running test cases.
|
||||||
|
func assertInit(
|
||||||
|
product: Product
|
||||||
|
) throws {
|
||||||
|
// GIVEN
|
||||||
|
// WHEN
|
||||||
|
let middleware = try UserAgentMiddleware(product: product)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
#expect(middleware.agentField == .init(
|
||||||
|
name: .userAgent,
|
||||||
|
value: "\(product.name)/\(product.version) +\(product.url)"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asserts the error throwing (if justified) during the initialization of the middleware.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - product: A product to initialize a middleware.
|
||||||
|
/// - error: An expected error of type ``InputValidationError`` during the initialization of a middleware.
|
||||||
|
func assertInitThrows(
|
||||||
|
product: Product,
|
||||||
|
expects error: InputValidationError?
|
||||||
|
) {
|
||||||
|
// GIVEN
|
||||||
|
// WHEN
|
||||||
|
// THEN
|
||||||
|
if let error {
|
||||||
|
#expect(throws: error) {
|
||||||
|
try UserAgentMiddleware(product: product)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#expect(throws: Never.self) {
|
||||||
|
try UserAgentMiddleware(product: product)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asserts the interception of a request to add the user agent in its header.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - product: A product to initialize a middleware.
|
||||||
|
/// - path: A URI path for a request.
|
||||||
|
/// - headerFields: A set of header fields for a request.
|
||||||
|
func assertIntercept(
|
||||||
|
product: Product,
|
||||||
|
path: String? = nil,
|
||||||
|
headerFields: HTTPFields = [:]
|
||||||
|
) async throws {
|
||||||
|
// GIVEN
|
||||||
|
let middleware = try UserAgentMiddleware(product: product)
|
||||||
|
let request = HTTPRequest(
|
||||||
|
path: path,
|
||||||
|
headerFields: headerFields
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
_ = try await confirmation { confirmation in
|
||||||
|
try await middleware.intercept(
|
||||||
|
request,
|
||||||
|
body: nil,
|
||||||
|
baseURL: .Sample.baseURL,
|
||||||
|
operationID: .Sample.operationId
|
||||||
|
) { request, _, _ in
|
||||||
|
// THEN
|
||||||
|
#expect(request.path == path)
|
||||||
|
#expect(request.headerFields != headerFields)
|
||||||
|
#expect(request.headerFields.count == headerFields.count + 1)
|
||||||
|
#expect(request.headerFields.contains(where: { $0.name == .userAgent }))
|
||||||
|
|
||||||
|
confirmation()
|
||||||
|
|
||||||
|
return (.init(status: .ok) , nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Constants
|
||||||
|
|
||||||
|
private extension Input {
|
||||||
|
/// A list of products to successfully initialize user agent middleware instances.
|
||||||
|
static let userAgents: [Product] = [
|
||||||
|
.init(name: "SomeApp", version: "0.0.1", url: "http://www.some.app"),
|
||||||
|
.init(name: "SomeOther4pp", version: "1.2.3-b1", url: "https://some-other.app"),
|
||||||
|
.init(name: "Yet4notherApp", version: "0.8.8+alpha", url: "https://yet.another.app")
|
||||||
|
]
|
||||||
|
/// A list of products to use in the initialization throw test cases.
|
||||||
|
static let userAgentsThrows: [Product] = userAgents + [
|
||||||
|
.init(name: "Some App", version: "0.0.1", url: "http://www.some.app"),
|
||||||
|
.init(name: "Some-App", version: "0.0.1", url: "http://www.some.app"),
|
||||||
|
.init(name: .empty, version: "0.0.1", url: "http://www.some.app"),
|
||||||
|
.init(name: "SomeApp", version: "v0.0.1", url: "http://www.some.app"),
|
||||||
|
.init(name: "SomeApp", version: "0.1", url: "http://www.some.app"),
|
||||||
|
.init(name: "SomeApp", version: .empty, url: "http://www.some.app"),
|
||||||
|
.init(name: "SomeApp", version: "0.0.1", url: "www.some.app"),
|
||||||
|
.init(name: "SomeApp", version: "0.0.1", url: "some.app"),
|
||||||
|
.init(name: "SomeApp", version: "0.0.1", url: .empty),
|
||||||
|
.init(name: "Some App", version: "v0.0.1", url: "www.some.app"),
|
||||||
|
.init(name: "SomeApp", version: "v0.0.1", url: "www.some.app"),
|
||||||
|
.init(name: "Some App", version: "0.0.1", url: "www.some.app"),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension Output {
|
||||||
|
/// A list of expected input validation errors (if thrown) coming from the initialization throw test cases.
|
||||||
|
static let userAgentsThrows: [InputValidationError?] = [nil, nil, nil, .inputNotCamelCase, .inputNotCamelCase, .inputIsEmpty, .inputNotSemanticVersion, .inputNotSemanticVersion, .inputIsEmpty, .inputNotURL, .inputNotURL, .inputIsEmpty, .inputNotCamelCase, .inputNotSemanticVersion, .inputNotCamelCase]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
//
|
||||||
|
// This source file is part of the DiscogsService open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2025 Röck+Cöde VoF. and the DiscogsService project authors
|
||||||
|
// Licensed under Apache license v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE for license information
|
||||||
|
// See CONTRIBUTORS for the list of DiscogsService project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
|
||||||
|
import struct HTTPTypes.HTTPFields
|
||||||
|
import struct HTTPTypes.HTTPRequest
|
||||||
|
|
||||||
|
extension HTTPRequest {
|
||||||
|
|
||||||
|
// MARK: Initializers
|
||||||
|
|
||||||
|
/// Initializes a HTTP request conveniently.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - method: A request method.
|
||||||
|
/// - path: A value of the “:path” pseudo header field.
|
||||||
|
/// - headerFields: A dictionary of request header fields.
|
||||||
|
init(
|
||||||
|
method: HTTPRequest.Method = .get,
|
||||||
|
path: String?,
|
||||||
|
headerFields: HTTPFields = [:]
|
||||||
|
) {
|
||||||
|
self.init(
|
||||||
|
method: method,
|
||||||
|
scheme: nil,
|
||||||
|
authority: nil,
|
||||||
|
path: path,
|
||||||
|
headerFields: headerFields
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
//
|
||||||
|
// This source file is part of the DiscogsService open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2025 Röck+Cöde VoF. and the DiscogsService project authors
|
||||||
|
// Licensed under Apache license v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE for license information
|
||||||
|
// See CONTRIBUTORS for the list of DiscogsService project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
/// A namespace assigned for string samples on test cases.
|
||||||
|
enum Sample {
|
||||||
|
/// An operation ID sample.
|
||||||
|
static let operationId = "SomeOperationId"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
//
|
||||||
|
// This source file is part of the DiscogsService open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2025 Röck+Cöde VoF. and the DiscogsService project authors
|
||||||
|
// Licensed under Apache license v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE for license information
|
||||||
|
// See CONTRIBUTORS for the list of DiscogsService project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// ===----------------------------------------------------------------------===
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension URL {
|
||||||
|
/// A namespace assigned for URL samples on test cases.
|
||||||
|
enum Sample {
|
||||||
|
/// A base URL sample.
|
||||||
|
static let baseURL = URL(string: "https://sample.domain.com")!
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user