[Bugfix] Communications (#7)

This PR contains the work done to implement some bugfixes to the existing source code related to the `Communications` library of this package.

To provide further details about the work done:
- [x] removed an unnecessary function from the `Client` public protocol;
- [x] moved the `MakeURLRequestError` public error to its own file;
- [x] added the `parameters` property to the `Endpoint` public protocol;
- [x] added support for the parameters handling in the `MakeURLRequestUseCase` use case.

Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Reviewed-on: #7
This commit is contained in:
Javier Cicchelli 2023-04-18 22:55:25 +00:00
parent 6e785b4255
commit d8163ab0de
5 changed files with 63 additions and 23 deletions

View File

@ -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
}

View File

@ -27,9 +27,4 @@ public protocol Client {
as model: Model.Type as model: Model.Type
) async throws -> Model ) 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
} }

View File

@ -15,6 +15,11 @@ import Foundation
/// This protocol defines an endpoint to be used in a remote call. /// This protocol defines an endpoint to be used in a remote call.
public protocol Endpoint { public protocol Endpoint {
// MARK: Type aliases
typealias Parameters = [String : String?]
typealias Headers = [String : String]
// MARK: Properties // MARK: Properties
/// The scheme subcomponent for the endpoint. /// The scheme subcomponent for the endpoint.
@ -29,11 +34,14 @@ public protocol Endpoint {
/// The path subcomponent for the endpoint. /// The path subcomponent for the endpoint.
var path: String { get } var path: String { get }
/// The query parameter subcomponents for the endpoint.
var parameters: Parameters { get }
/// The HTTP request method for the endpoint. /// The HTTP request method for the endpoint.
var method: HTTPRequestMethod { get } var method: HTTPRequestMethod { get }
/// The HTTP header fields as a dictionary for the endpoint. /// 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. /// The message body as data for a request.
var body: Data? { get } var body: Data? { get }

View File

@ -35,6 +35,12 @@ public struct MakeURLRequestUseCase {
urlComponents.port = port 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 { guard let url = urlComponents.url else {
throw MakeURLRequestError.urlNotCreated throw MakeURLRequestError.urlNotCreated
} }
@ -49,10 +55,3 @@ public struct MakeURLRequestUseCase {
} }
} }
// MARK: - Errors
enum MakeURLRequestError: Error {
case urlNotCreated
}

View File

@ -52,6 +52,25 @@ final class MakeURLRequestUseCaseTests: XCTestCase {
XCTAssertNil(result.httpBody) 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 { func test_withEndpoint_initialisedWithHeaders() throws {
// GIVEN // GIVEN
let endpoint = TestEndpoint(headers: [ let endpoint = TestEndpoint(headers: [
@ -100,23 +119,25 @@ private struct TestEndpoint: Endpoint {
let scheme: String = "http" let scheme: String = "http"
let host: String = "www.something.com" let host: String = "www.something.com"
let port: Int?
let path: String = "/path/to/endpoint" let path: String = "/path/to/endpoint"
let parameters: Parameters
let method: HTTPRequestMethod = .get let method: HTTPRequestMethod = .get
let headers: Headers
var port: Int? let body: Data?
var headers: [String : String]
var body: Data?
// MARK: Initialisers // MARK: Initialisers
init( init(
port: Int? = nil, port: Int? = nil,
headers: [String : String] = [:], parameters: Parameters = [:],
headers: Headers = [:],
body: Data? = nil body: Data? = nil
) { ) {
self.port = port self.port = port
self.body = body self.parameters = parameters
self.headers = headers self.headers = headers
self.body = body
} }
} }