Compare commits

...

17 Commits

Author SHA1 Message Date
Maxim Khatskevich 89e8d109f3 Merge branch 'release/5.4.0' 2021-09-26 00:12:38 +02:00
Maxim Khatskevich 6f014c3ff6 [MAKE] request body/parameters encoding specified per request
plus few other improvements on request customizatons.
2021-09-26 00:12:19 +02:00
Maxim Khatskevich f270e16c6f [IMPROVE] `prepareRequest` method signature
with explicit separation of parameters vs. variables.
2021-09-25 17:34:45 +02:00
Maxim Khatskevich aed7af9dd3 Merge branch 'release/5.3.0' 2021-08-15 19:25:21 +02:00
Maxim Khatskevich 65a4a05ac0 Merge tag '5.3.0' into develop
[FIX] incorrect handling of optional parameters in request definition.
2021-08-15 19:25:21 +02:00
Maxim Khatskevich e228e4beb3 [FIX] incorrect handling of optional parameters in request definition 2021-08-15 19:24:47 +02:00
Maxim Khatskevich 792c159774 [ADD] unit tests to expose incorrect handling of optional parameters in request definition 2021-08-15 19:05:04 +02:00
Maxim Khatskevich 7e99ee5a65 Merge branch 'release/5.2.1' 2021-03-20 21:33:35 +01:00
Maxim Khatskevich 7f323ec75c Merge tag '5.2.1' into develop
Updated CI config with Xcode 12
2021-03-20 21:33:35 +01:00
Maxim Khatskevich 87fd31b2ea [UPDATE] CI config for Swift 5.3 (Xcode 12) 2021-03-20 21:33:09 +01:00
Maxim Khatskevich 79db02c391 Merge branch 'release/5.2.0' 2021-03-20 21:22:12 +01:00
Maxim Khatskevich c72de279d3 Merge tag '5.2.0' into develop
Update configuration with Swift 5.3
2021-03-20 21:22:12 +01:00
Maxim Khatskevich 22e8b5dfe4 [UPDATE] configuration with Swift 5.3 2021-03-20 21:20:38 +01:00
Maxim Khatskevich 1b9d653e57 Merge branch 'release/5.1.1' 2021-03-07 04:02:37 +01:00
Maxim Khatskevich bbaff73a60 Merge tag '5.1.1' into develop
Updated CI config - removed Xcode 10 and added Xcode 12.
2021-03-07 04:02:37 +01:00
Maxim Khatskevich c2245892ce [UPDATE] CI config for Xcode 12 2021-03-07 04:01:41 +01:00
Maxim Khatskevich b489bbd69b Merge tag '5.1.0' into develop
Various renamings, improvements and interface simplifications, plus switched to Swift 5 `Result` type instead of more generic `throw`.
2021-03-07 03:53:24 +01:00
12 changed files with 137 additions and 72 deletions

View File

@ -1,4 +1,4 @@
// swift-tools-version:5.0
// swift-tools-version:5.3
import PackageDescription
@ -8,8 +8,16 @@ let package = Package(
.macOS(.v10_11),
],
dependencies: [
.package(url: "https://github.com/kylef/PathKit", from: "1.0.0"),
.package(url: "https://github.com/XCEssentials/RepoConfigurator", from: "3.0.0")
.package(
name: "PathKit",
url: "https://github.com/kylef/PathKit",
from: "1.0.0"
),
.package(
name: "XCERepoConfigurator",
url: "https://github.com/XCEssentials/RepoConfigurator",
from: "3.0.0"
)
],
targets: [
.target(

View File

@ -11,7 +11,7 @@ print("--- BEGIN of '\(Executable.name)' script ---")
// MARK: Parameters
Spec.BuildSettings.swiftVersion.value = "5.0"
Spec.BuildSettings.swiftVersion.value = "5.3"
let localRepo = try Spec.LocalRepo.current()

View File

@ -16,8 +16,7 @@ git:
language: objective-c # fine for Swift as well
osx_image:
- xcode10.2
- xcode11
- xcode12
before_script:
# cd ./.setup && swift run && cd ./.. # RUN this manually!

View File

@ -1,4 +1,4 @@
// swift-tools-version:5.0
// swift-tools-version:5.3
import PackageDescription

View File

@ -1,7 +1,7 @@
[![GitHub License](https://img.shields.io/github/license/XCEssentials/APIClient.svg?longCache=true)](LICENSE)
[![GitHub Tag](https://img.shields.io/github/tag/XCEssentials/APIClient.svg?longCache=true)](https://github.com/XCEssentials/APIClient/tags)
[![Swift Package Manager Compatible](https://img.shields.io/badge/SPM-compatible-brightgreen.svg?longCache=true)](Package.swift)
[![Written in Swift](https://img.shields.io/badge/Swift-5.0-orange.svg?longCache=true)](https://swift.org)
[![Written in Swift](https://img.shields.io/badge/Swift-5.3-orange.svg?longCache=true)](https://swift.org)
[![Supported platforms](https://img.shields.io/badge/platforms-macOS%20%7C%20iOS%20%7C%20tvOS%20%7C%20watchOS%20%7C%20Linux-blue.svg?longCache=true)](Package.swift)
[![Build Status](https://travis-ci.com/XCEssentials/APIClient.svg?branch=master)](https://travis-ci.com/XCEssentials/APIClient)

View File

@ -39,20 +39,15 @@ struct BasicURLRequestFacilitator: URLRequestFacilitator
public
let sharedPrefixURL: URL
public
let onEncodeRequest: OnEncodeRequest
// MARK: - Initializers
public
init(
sharedPrefixURL: URL,
session: URLSession = .shared,
onConfigureRequest: @escaping OnEncodeRequest = URLEncoding.default.encode
session: URLSession = .shared
)
{
self.sharedPrefixURL = sharedPrefixURL
self.session = session
self.onEncodeRequest = onConfigureRequest
}
}

View File

@ -38,7 +38,10 @@ struct RequestEncodingIssue: URLRequestFacilitatorError
public
enum PrepareRequestIssue: URLRequestFacilitatorError
{
{
case conversionIntoDataFailed(Error)
case conversionDataIntoJSONObjectFailed(Error)
case conversionJSONObjectIntoDictionaryFailed(theObject: Any)
case invalidRelativePath(String)
case requestEncodingFailed(RequestEncodingIssue)
}

View File

@ -28,11 +28,6 @@ import Foundation
//---
public
typealias OnEncodeRequest = (URLRequest, Parameters?) -> Result<URLRequest, RequestEncodingIssue>
//---
public
enum HTTPHeaderFieldName: String
{

View File

@ -24,48 +24,37 @@
*/
import Foundation
//---
public
protocol RequestDefinition
protocol RequestDefinition: Encodable
{
static
var relativePath: String { get }
static
var method: HTTPMethod? { get }
static
var parameterEncoding: ParameterEncoding { get }
static
var toDataConverter: (Self) throws -> Data { get }
static
var dataToDictionaryConversionOptions: JSONSerialization.ReadingOptions { get }
}
//---
public
extension RequestDefinition {
/// Parameters for the corresponding URL request.
var parameters: Parameters {
extension RequestDefinition
{
static
var toDataConverter: (Self) throws -> Data { JSONEncoder().encode }
Mirror
.init(reflecting: self)
.children
.compactMap{
(child: (label: String?, value: Any)) -> (key: String, value: Any)? in
//---
if
let key = child.label
{
return (key, child.value)
}
else
{
return nil
}
}
.reduce(into: [:]) {
result, nextItem in
//---
result[nextItem.key] = nextItem.value
}
}
static
var dataToDictionaryConversionOptions: JSONSerialization.ReadingOptions { .init() }
}

View File

@ -34,18 +34,73 @@ protocol URLRequestFacilitator
var session: URLSession { get }
var sharedPrefixURL: URL { get }
var onEncodeRequest: OnEncodeRequest { get }
}
// MARK: - Internal methods
// MARK: - Commands - Public
public
extension URLRequestFacilitator
{
func prepareRequest<R: RequestDefinition>(
from definition: R
) -> Result<URLRequest, PrepareRequestIssue>
{
let parametersData: Data
do
{
parametersData = try R.toDataConverter(definition)
}
catch
{
return .failure(.conversionIntoDataFailed(error))
}
//---
let parametersObject: Any
do
{
parametersObject = try JSONSerialization
.jsonObject(
with: parametersData,
options: R.dataToDictionaryConversionOptions
)
}
catch
{
return .failure(.conversionDataIntoJSONObjectFailed(error))
}
//---
guard
let parameters = parametersObject as? Parameters
else
{
return .failure(.conversionJSONObjectIntoDictionaryFailed(theObject: parametersObject))
}
//---
return prepareRequest(
R.method,
relativePath: R.relativePath,
parameterEncoding: R.parameterEncoding,
parameters: parameters
)
}
}
// MARK: - Internal methods
extension URLRequestFacilitator
{
func prepareRequest(
_ method: HTTPMethod? = nil,
relativePath: String,
parameterEncoding: ParameterEncoding,
parameters: Parameters? = nil
) -> Result<URLRequest, PrepareRequestIssue>
{
@ -64,7 +119,7 @@ extension URLRequestFacilitator
var result = URLRequest(url: targetURL)
result.httpMethod = method?.rawValue
switch onEncodeRequest(result, parameters)
switch parameterEncoding.encode(result, with: parameters)
{
case .success(let output):
return .success(output)
@ -73,15 +128,4 @@ extension URLRequestFacilitator
return .failure(.requestEncodingFailed(error))
}
}
func prepareRequest(
for definition: RequestDefinition
) -> Result<URLRequest, PrepareRequestIssue>
{
prepareRequest(
type(of: definition).method,
relativePath: type(of: definition).relativePath,
parameters: definition.parameters
)
}
}

View File

@ -16,7 +16,24 @@ class RequestDefinitionTests: XCTestCase
static
let method: HTTPMethod? = .get
static
let parameterEncoding: ParameterEncoding = URLEncoding.default
let id: String
var optionalFlag: Int? = nil
}
var facilitator: URLRequestFacilitator!
override func setUp() {
super.setUp()
facilitator = BasicURLRequestFacilitator(sharedPrefixURL: .init(string: "host.com")!)
}
override func tearDown() {
facilitator = nil
super.tearDown()
}
}
@ -24,12 +41,26 @@ class RequestDefinitionTests: XCTestCase
extension RequestDefinitionTests
{
func test_buildParameters()
func test_buildParameters_withoutOptional()
{
let sut = UserRequestDefinition(id: "123").parameters
let definition = UserRequestDefinition(id: "123")
let request = try! facilitator.prepareRequest(from: definition).get()
let components = URLComponents(string: request.url!.absoluteString)!
let sut = components.queryItems!
XCTAssertEqual(sut.count, 1)
XCTAssertTrue(sut.contains(where: { $0.key == "id" }))
XCTAssertTrue(sut.contains(where: { ($0.value as? String) == "123" }))
XCTAssertTrue(sut.contains(where: { $0.name == "id" && $0.value == "123" }))
}
func test_buildParameters_withOptional()
{
let definition = UserRequestDefinition(id: "123", optionalFlag: 22)
let request = try! facilitator.prepareRequest(from: definition).get()
let components = URLComponents(string: request.url!.absoluteString)!
let sut = components.queryItems!
XCTAssertEqual(sut.count, 2)
XCTAssertTrue(sut.contains(where: { $0.name == "id" && $0.value == "123" }))
XCTAssertTrue(sut.contains(where: { $0.name == "optionalFlag" && $0.value == "22" }))
}
}

View File

@ -22,6 +22,7 @@ extension URLRequestFacilitatorTests
let sut = facilitator.prepareRequest(
.get,
relativePath: "user",
parameterEncoding: URLEncoding.default,
parameters: ["id": "123"]
)