Files
discogs-service/Sources/DiscogsService/Public/Middlewares/UserAgentMiddleware.swift
T
javier b14a9fa816 Documentation improvements (#16)
This PR contains the work done to improve the documentation efforts in the package, aiming at improving the documentation of the source code as well as the OpenAPI specification document. In addition, a breaking bug has been fixed.

Reviewed-on: #16
Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
2026-03-24 01:22:53 +00:00

118 lines
3.8 KiB
Swift

// ===----------------------------------------------------------------------===
//
// This source file is part of the DiscogsService open source project
//
// Copyright (c) 2026 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
/// Intercepts an outgoing HTTP request and attaches the `User-Agent` header.
/// - Parameters:
/// - request: The outgoing HTTP request to modify.
/// - body: The optional body of the HTTP request.
/// - baseURL: The base URL of the service.
/// - operationID: The identifier of the API operation being called.
/// - next: The next middleware or transport to call in the chain.
/// - Returns: The HTTP response and optional response body from the service.
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 = "%@/%@ +%@"
}