Skip to content

Commit

Permalink
🚨 ✨ Adds ParametersAdaptor. Polishes tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
connor-ricks committed Jul 25, 2023
1 parent fe559f3 commit 8ed6335
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 3 deletions.
42 changes: 42 additions & 0 deletions Sources/HTTPNetworking/Plugins/Adaptors/ParametersAdaptor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import Foundation

/// An ``HTTPRequestAdaptor`` that can be used to append query parameters to a request before it is sent out over the network.
///
/// > Warning: This adaptor will **not** overwrite existing parameters, instead it will append to existing parameters.
public struct ParametersAdaptor: HTTPRequestAdaptor {

// MARK: Properties

let items: [URLQueryItem]

// MARK: Initializers

/// Creates a ``ParametersAdaptor`` from the provided query parameters.
///
/// - Parameter items: The query parameters to append to incoming requests.
public init(items: [URLQueryItem]) {
self.items = items
}

// MARK: HTTPRequestAdaptor

public func adapt(_ request: URLRequest, for session: URLSession) async throws -> URLRequest {
var request = request
guard let url = request.url else {
preconditionFailure("URLRequest should contain a valid URL at this point.")
}

request.url = url.appending(queryItems: items)
return request
}
}

// MARK: - HTTPRequest + Adaptor

extension HTTPRequest {
/// Applies a ``ParametersAdaptor`` that appends the provided query parameters to the request.
@discardableResult
public func adapt(queryItems: [URLQueryItem]) -> Self {
adapt(with: ParametersAdaptor(items: queryItems))
}
}
15 changes: 15 additions & 0 deletions Tests/HTTPNetworkingTests/Plugins/Adaptors/AdaptorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@
import XCTest

class AdaptorTests: XCTestCase {
func test_adaptor_withProvidedHandler_callsHandlerOnAdaptation() async throws {
let url = URL(string: "https://api.com")!
let client = HTTPClient()
let request = client.request(for: .get, to: url, expecting: String.self)
let expectation = expectation(description: "Expected handler to be called.")

let adaptor = Adaptor { request, _ in
expectation.fulfill()
return request
}

_ = try await adaptor.adapt(request.request, for: .shared)
await fulfillment(of: [expectation])
}

func test_request_adaptorConvenience_isAddedToRequestAdaptors() async throws {
let url = URL(string: "https://api.com")!
let client = HTTPClient()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
@testable import HTTPNetworking
import XCTest

class ParametersAdaptorTests: XCTestCase {
func test_parametersAdaptor_withPrameters_addsParametersToRequest() async throws {
let adaptor = ParametersAdaptor(items: [
.init(name: "one-name", value: "one-value"),
.init(name: "two-name", value: "two-value"),
])

let cleanUrlRequest = URLRequest(url: URL(string: "https://api.com")!)
let adaptedCleanUrlRequest = try await adaptor.adapt(cleanUrlRequest, for: .shared)
XCTAssertEqual(
adaptedCleanUrlRequest.url,
URL(string: "https://api.com?one-name=one-value&two-name=two-value")!
)

let dirtyUrlRequest = URLRequest(url: URL(string: "https://api.com?")!)
let adaptedDirtyUrlRequest = try await adaptor.adapt(dirtyUrlRequest, for: .shared)
XCTAssertEqual(
adaptedDirtyUrlRequest.url,
URL(string: "https://api.com?one-name=one-value&two-name=two-value")!
)

let existingParametersUrlRequest = URLRequest(url: URL(string: "https://api.com?two-name=original-two-value&other-name=other-value")!)
let adaptedExistingParametersUrlRequest = try await adaptor.adapt(existingParametersUrlRequest, for: .shared)
XCTAssertEqual(
adaptedExistingParametersUrlRequest.url,
URL(string: "https://api.com?two-name=original-two-value&other-name=other-value&one-name=one-value&two-name=two-value")!
)
}

func test_request_adaptorConvenience_isAddedToRequestAdaptors() async throws {
let url = URL(string: "https://api.com")!
let client = HTTPClient()
let request = client.request(for: .get, to: url, expecting: String.self)

let items: [URLQueryItem] = [
.init(name: "one-name", value: "one-value"),
.init(name: "two-name", value: "two-value"),
]
request.adapt(queryItems: items)

guard let adaptor = request.adaptors.first as? ParametersAdaptor else {
XCTFail("Expected request to container PrametersAdaptor.")
return
}

XCTAssertEqual(adaptor.items, items)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import XCTest

class ZipAdaptorTests: XCTestCase {
func test_zippedAdaptor_withAdaptors_containsAdaptorsInOrder() {
func test_zipAdaptor_withAdaptors_containsAdaptorsInOrder() {
struct TestAdaptor: HTTPRequestAdaptor, Equatable {
let id: Int
func adapt(_ request: URLRequest, for session: URLSession) async throws -> URLRequest {
Expand Down
16 changes: 16 additions & 0 deletions Tests/HTTPNetworkingTests/Plugins/Retriers/RetrierTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@
import XCTest

class RetrierTests: XCTestCase {
func test_retrier_withProvidedHandler_callsHandlerOnRetry() async throws {

let url = URL(string: "https://api.com")!
let client = HTTPClient()
let request = client.request(for: .get, to: url, expecting: String.self)
let expectation = expectation(description: "Expected handler to be called.")

let retrier = Retrier { _, _, _ in
expectation.fulfill()
return .concede
}

_ = await retrier.retry(request.request, for: .shared, dueTo: URLError(.cannotParseResponse))
await fulfillment(of: [expectation])
}

func test_request_retryConvenience_isAddedToRequestRetriers() async {
struct MockError: Error {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import XCTest

class ZipRetrierTests: XCTestCase {
func test_zippedRetrier_withRetriers_containsRetriersInOrder() {
func test_zipRetrier_withRetriers_containsRetriersInOrder() {
struct TestRetrier: HTTPRequestRetrier, Equatable {
let id: Int
func retry(_ request: URLRequest, for session: URLSession, dueTo error: Error) async -> HTTPNetworking.RetryStrategy {
Expand Down
16 changes: 16 additions & 0 deletions Tests/HTTPNetworkingTests/Plugins/Validators/ValidatorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@
import XCTest

class ValidatorTests: XCTestCase {
func test_validator_withProvidedHandler_callsHandlerOnValidation() async throws {

let url = URL(string: "https://api.com")!
let client = HTTPClient()
let request = client.request(for: .get, to: url, expecting: String.self)
let expectation = expectation(description: "Expected handler to be called.")

let validator = Validator { _, _, _ in
expectation.fulfill()
return .success
}

_ = await validator.validate(HTTPURLResponse(), for: request.request, with: Data())
await fulfillment(of: [expectation])
}

func test_request_validatorConvenience_isAddedToRequestValidators() async throws {
let url = URL(string: "https://api.com")!
let client = HTTPClient()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import XCTest

class ZipValidatorTests: XCTestCase {
func test_zippedValidator_withValidators_containsValidatorsInOrder() {
func test_zipValidator_withValidators_containsValidatorsInOrder() {
struct TestValidator: HTTPResponseValidator, Equatable {
let id: Int
func validate(_ response: HTTPURLResponse, for request: URLRequest, with data: Data) async -> ValidationResult {
Expand Down

0 comments on commit 8ed6335

Please sign in to comment.