diff --git a/Sources/Communications/Errors/MakeURLRequestError.swift b/Sources/Communications/Errors/MakeURLRequestError.swift new file mode 100644 index 0000000..13db6f2 --- /dev/null +++ b/Sources/Communications/Errors/MakeURLRequestError.swift @@ -0,0 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftLibs open source project +// +// Copyright (c) 2023 Röck+Cöde VoF. and the SwiftLibs project authors +// Licensed under the EUPL 1.2 or later. +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftLibs project authors +// +//===----------------------------------------------------------------------===// + +/// Errors generated out of the `MakeURLRequestUseCase` use case. +public enum MakeURLRequestError: Error { + /// An expected URL was not created. + case urlNotCreated +} diff --git a/Sources/Communications/Protocols/Client.swift b/Sources/Communications/Protocols/Client.swift index 3c1f556..bc641c7 100644 --- a/Sources/Communications/Protocols/Client.swift +++ b/Sources/Communications/Protocols/Client.swift @@ -27,9 +27,4 @@ public protocol Client { as model: Model.Type ) async throws -> Model - /// Makes a request to a remote location based on a given endpoint and expects to return an original, uncasted response. - /// - Parameter endpoint: The endpoint for which to make a remote call. - /// - Returns: An original data response from a call to a remote endpoint. - @discardableResult func request(endpoint: some Endpoint) async throws -> Data - } diff --git a/Sources/Communications/Protocols/Endpoint.swift b/Sources/Communications/Protocols/Endpoint.swift index 79b79fd..b373dce 100644 --- a/Sources/Communications/Protocols/Endpoint.swift +++ b/Sources/Communications/Protocols/Endpoint.swift @@ -15,27 +15,35 @@ import Foundation /// This protocol defines an endpoint to be used in a remote call. public protocol Endpoint { + // MARK: Type aliases + + typealias Parameters = [String : String?] + typealias Headers = [String : String] + // MARK: Properties - + /// The scheme subcomponent for the endpoint. var scheme: String { get } /// The host subcomponent for the endpoint. var host: String { get } - + /// The port subcomponent for the component. var port: Int? { get } - + /// The path subcomponent for the endpoint. var path: String { get } + /// The query parameter subcomponents for the endpoint. + var parameters: Parameters { get } + /// The HTTP request method for the endpoint. var method: HTTPRequestMethod { get } /// The HTTP header fields as a dictionary for the endpoint. - var headers: [String: String] { get } + var headers: Headers { get } /// The message body as data for a request. var body: Data? { get } - + } diff --git a/Sources/Communications/Use Cases/MakeURLRequestUseCase.swift b/Sources/Communications/Use Cases/MakeURLRequestUseCase.swift index e81e639..099fec1 100644 --- a/Sources/Communications/Use Cases/MakeURLRequestUseCase.swift +++ b/Sources/Communications/Use Cases/MakeURLRequestUseCase.swift @@ -35,6 +35,12 @@ public struct MakeURLRequestUseCase { urlComponents.port = port } + if !endpoint.parameters.isEmpty { + urlComponents.queryItems = endpoint.parameters + .map(URLQueryItem.init) + .sorted(by: { $0.name < $1.name }) + } + guard let url = urlComponents.url else { throw MakeURLRequestError.urlNotCreated } @@ -49,10 +55,3 @@ public struct MakeURLRequestUseCase { } } - -// MARK: - Errors - -enum MakeURLRequestError: Error { - case urlNotCreated -} - diff --git a/Tests/Communications/Use Cases/MakeURLRequestUseCaseTests.swift b/Tests/Communications/Use Cases/MakeURLRequestUseCaseTests.swift index 6f8cf7c..7499bb1 100644 --- a/Tests/Communications/Use Cases/MakeURLRequestUseCaseTests.swift +++ b/Tests/Communications/Use Cases/MakeURLRequestUseCaseTests.swift @@ -52,6 +52,25 @@ final class MakeURLRequestUseCaseTests: XCTestCase { XCTAssertNil(result.httpBody) } + func test_withEndpoint_initialisedWithParameters() throws { + // GIVEN + let endpoint = TestEndpoint(parameters: [ + "someParameter": "someValue", + "anotherParameter": nil, + "otherParameter": "yetAnotherValue" + ]) + + // WHEN + let result = try makeURLRequest(endpoint: endpoint) + + // THEN + XCTAssertNotNil(result) + XCTAssertEqual(result.url?.absoluteString, "http://www.something.com/path/to/endpoint?anotherParameter&otherParameter=yetAnotherValue&someParameter=someValue") + XCTAssertEqual(result.httpMethod, HTTPRequestMethod.get.rawValue) + XCTAssertEqual(result.allHTTPHeaderFields, [:]) + XCTAssertNil(result.httpBody) + } + func test_withEndpoint_initialisedWithHeaders() throws { // GIVEN let endpoint = TestEndpoint(headers: [ @@ -100,23 +119,25 @@ private struct TestEndpoint: Endpoint { let scheme: String = "http" let host: String = "www.something.com" + let port: Int? let path: String = "/path/to/endpoint" + let parameters: Parameters let method: HTTPRequestMethod = .get - - var port: Int? - var headers: [String : String] - var body: Data? + let headers: Headers + let body: Data? // MARK: Initialisers init( port: Int? = nil, - headers: [String : String] = [:], + parameters: Parameters = [:], + headers: Headers = [:], body: Data? = nil ) { self.port = port - self.body = body + self.parameters = parameters self.headers = headers + self.body = body } }