Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 47 additions & 3 deletions Sources/HTTPEngine/HTTPEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,23 @@ public typealias ResponseValidationClosure = (Int) -> ValidResponse
public struct HTTPEngine {
public init() {}


/// Creates a URLRequest Object
/// - Parameters:
/// - method: HTTPMethod - `.get, .put. post` etc.,
/// - url: A URL Object
/// - body: Data?: The body data to send with a request
/// - header: A dictionary of HTTP Request Headers - `["Content-Type": "text", "Some Key": "Some Value"]`
/// - Returns: A fully constructed URLRequest
///
/// -- Headers
///
/// By default all requests have the `["Accept-Encoding": "gzip;q=1.0,compress;q=0.5"]` header included.
///
/// All `.post, .put, & .patch` requests also contain `["Content-Type": "application/json"]` by default.
///
/// These values can be overridden by including those headers as arguments when calling this function
///
public func buildRequest(
method: HTTPMethod,
url: URL,
Expand All @@ -32,7 +49,33 @@ public struct HTTPEngine {

return request
}



/// Makes a request via HTTP
/// - Parameters:
/// - method: HTTPMethod - `.get, .put. post` etc.,
/// - urlString: URL domain + path as a string: `"abc.com/some/path"`
/// - body: Data?: The body data to send with a request
/// - header: A dictionary of HTTP Request Headers - `["Content-Type": "text", "Some Key": "Some Value"]`
/// - validator: `(Int) -> Bool` - A function to validate the response code of the request. By default, makeRequest() will fail if the status code does not fall within the 200 - 299 range. To override this, pass in a function that compares the status code and returns a boolean. True == success, False == failure. Upon failure an error will be thrown that contains the HTTPURLResponse for inspection.
///
/// - Returns: AnyPubliser<Data, Error>
///
/// -- Headers
///
/// By default all requests have the `["Accept-Encoding": "gzip;q=1.0,compress;q=0.5"]` header included.
///
/// All `.post, .put, & .patch` requests also contain `["Content-Type": "application/json"]` by default.
///
/// These values can be overridden by including those headers as arguments when calling this function
///
/// -- Validation
///
/// By default the validation checks for a 200-299 status code and fails if the code is out of bounds
/// ```swift
/// // example validator
/// validator: { $0 == 202 }
/// ```
public func makeRequest(
method: HTTPMethod,
url urlString: String,
Expand All @@ -54,8 +97,9 @@ public struct HTTPEngine {
}
.eraseToAnyPublisher()
}

func validateResponse(_ response: URLResponse?, validator: ResponseValidationClosure? = nil) throws {


private func validateResponse(_ response: URLResponse?, validator: ResponseValidationClosure? = nil) throws {
let response = try response as? HTTPURLResponse ??? Errors.Response.couldNotRetrieveStatusCode

guard let validator = validator else {
Expand Down
16 changes: 16 additions & 0 deletions Sources/HTTPEngine/Utilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ public extension URLRequest {
}
}


/// A publisher that immediately throws
/// - Parameters:
/// - type: The expected type of the publisher
/// - error: The error to throw
/// - Returns: AnyPublisher<Type, Error> That fails immediately
///
/// -- Use case
/// Sometimes a function returns a publisher, but we need to unwap a value or perform a try catch before a publisher can be created. In this instance we can return this publisher instead to allow the publisher chain to handle those errors.
public func ThrowingPublisher<T>(forType type: T.Type, throws error: Error) -> AnyPublisher<T, Error> {
Result<T?, Error> { nil }
.publisher
Expand All @@ -19,7 +28,14 @@ public func ThrowingPublisher<T>(forType type: T.Type, throws error: Error) -> A
}.eraseToAnyPublisher()
}


infix operator ??? : TernaryPrecedence
/// Unwrap or throw
/// - Parameters:
/// - left: Any Optional
/// - right: Error
/// - Throws: The error from the right
/// - Returns: The unwrapped optional from the left
public func ???<T>(_ left: Optional<T>, right: Error) throws -> T {
guard let value = left else { throw right }
return value
Expand Down