Add more stuff to api route
This commit is contained in:
parent
2fbcc30ee2
commit
caa93fc529
|
@ -1,12 +1,13 @@
|
|||
# Release notes
|
||||
|
||||
I will bump revision by revision, until SwiftKit has all functionality that it should have from iExtra. I will then bump it to `1.0.0`.
|
||||
Until 1.0, breaking changes can occur in minor versions.
|
||||
|
||||
|
||||
## 0.5.1
|
||||
## 0.6.0
|
||||
|
||||
### ✨ New features
|
||||
|
||||
* `ApiRoute` has more explicit properties for working with post data.
|
||||
* `iCloudDocumentSync` is a new protocol for syncing iCloud document changes.
|
||||
* `String+Slugify` is a new extension that can convert a string to a slugified version.
|
||||
* `StandardiCloudDocumentSync` is a new class for syncing iCloud document changes.
|
||||
|
|
|
@ -10,41 +10,60 @@ import Foundation
|
|||
|
||||
/**
|
||||
This protocol represents an external api route, e.g. `login`
|
||||
or `user`. The `path` will be appended to the environment's
|
||||
url when performing requests.
|
||||
|
||||
The `queryParams` dictionary is a collection of string data.
|
||||
When the route is handled with `GET`
|
||||
You can url encode any query param with `urlEncode`, if you
|
||||
plan on sending it with GET.
|
||||
or `user`. Each route is a separate action that defines all
|
||||
information required to perform an api request.
|
||||
*/
|
||||
public protocol ApiRoute {
|
||||
|
||||
/**
|
||||
The route's environment-relative path, that is appended
|
||||
to the environment's url when performing a request.
|
||||
*/
|
||||
var path: String { get }
|
||||
|
||||
/**
|
||||
The route's optional post data, that should be added as
|
||||
`httpBody` when performing a request. When defined, the
|
||||
property takes precedence over `postParams`.
|
||||
*/
|
||||
var postData: Data? { get }
|
||||
|
||||
/**
|
||||
The route's optional post data dictionary, which should
|
||||
be added as a .utf8 encoded `httpBody` data string when
|
||||
performing a request.
|
||||
*/
|
||||
var postParams: [String: String] { get }
|
||||
|
||||
/**
|
||||
The route's optional query data dictionary, that should
|
||||
be added as a .utf8 encoded `httpBody` data string when
|
||||
performing a request.
|
||||
|
||||
*/
|
||||
var queryParams: [String: String] { get }
|
||||
}
|
||||
|
||||
public extension ApiRoute {
|
||||
|
||||
/**
|
||||
Convert the route's `queryItems` collection to a string
|
||||
that can be used for form data requests.
|
||||
Convert the route `formDataParams` to `.utf8` data that
|
||||
can be used in form data requests.
|
||||
*/
|
||||
var formDataString: String {
|
||||
queryItems.map { "\($0.name)=\($0.value ?? "")" }.joined(separator: "&")
|
||||
var postParamsData: Data? {
|
||||
postParamsString?.data(using: .utf8)
|
||||
}
|
||||
|
||||
/**
|
||||
This function returns a `URLRequest` that is configured
|
||||
with `application/x-www-form-urlencoded` `Content-Type`
|
||||
and the query params of the route applied as `httpBody`,
|
||||
using `POST` as `httpMethod`.
|
||||
Convert the route `formDataParams` to a string that can
|
||||
be used in form data requests.
|
||||
*/
|
||||
func formDataRequest(for env: ApiEnvironment) -> URLRequest {
|
||||
var req = request(for: env, httpMethod: .post)
|
||||
req.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
|
||||
req.httpBody = formDataString.data(using: .utf8)
|
||||
return req
|
||||
var postParamsString: String? {
|
||||
var params = URLComponents()
|
||||
params.queryItems = postParams
|
||||
.map { URLQueryItem(name: $0.key, value: urlEncode($0.value)) }
|
||||
.sorted { $0.name < $1.name }
|
||||
return params.query
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -52,10 +71,21 @@ public extension ApiRoute {
|
|||
*/
|
||||
var queryItems: [URLQueryItem] {
|
||||
queryParams
|
||||
.map { URLQueryItem(name: $0.key, value: $0.value) }
|
||||
.map { URLQueryItem(name: $0.key, value: urlEncode($0.value)) }
|
||||
.sorted { $0.name < $1.name }
|
||||
}
|
||||
|
||||
/**
|
||||
Create a `URLRequest` that is configured for being used
|
||||
with `application/x-www-form-urlencoded` `Content-Type`.
|
||||
*/
|
||||
func formDataRequest(for env: ApiEnvironment) -> URLRequest {
|
||||
var req = request(for: env, httpMethod: .post)
|
||||
req.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
|
||||
req.httpBody = postParamsData
|
||||
return req
|
||||
}
|
||||
|
||||
/**
|
||||
This function returns a `URLRequest` that is configured
|
||||
for the given `httpMethod` and the route's `queryItems`.
|
||||
|
@ -75,6 +105,7 @@ public extension ApiRoute {
|
|||
guard let requestUrl = components.url else { fatalError("Could not create URLRequest for \(url.absoluteString)") }
|
||||
var request = URLRequest(url: requestUrl)
|
||||
request.httpMethod = httpMethod
|
||||
request.httpBody = postData ?? postParamsData
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
return request
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ class Bundle_BundleInformationTests: QuickSpec {
|
|||
|
||||
it("implements BundleInformation (empty due to SPM") {
|
||||
let bundle = Bundle.main
|
||||
expect(bundle.buildNumber).to(equal("17501"))
|
||||
expect(Int(bundle.buildNumber)).to(beGreaterThan(17501))
|
||||
expect(bundle.versionNumber).to(equal("0.0.0"))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,16 +18,11 @@ class ApiRouteTests: QuickSpec {
|
|||
let env = TestEnvironment()
|
||||
let route = TestRoute()
|
||||
|
||||
describe("form data string") {
|
||||
describe("post params string") {
|
||||
|
||||
it("is correctly configured") {
|
||||
let str = route.formDataString
|
||||
expect(str).to(equal("anyone?=there?&hello=world"))
|
||||
}
|
||||
|
||||
it("handles ampersands") {
|
||||
let str = route.formDataString
|
||||
expect(str).to(equal("anyone?=there?&hello=world"))
|
||||
it("url encodes values") {
|
||||
let str = route.postParamsString
|
||||
expect(str).to(equal("baz?=BAM%3F&foo&=bar%26"))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,8 +30,8 @@ class ApiRouteTests: QuickSpec {
|
|||
|
||||
it("is correctly configured") {
|
||||
let req = route.formDataRequest(for: env)
|
||||
let expectedData = "anyone?=there?&hello=world".data(using: .utf8)
|
||||
expect(req.url?.absoluteString).to(equal("http://example.com/1/2/3?anyone?=there?&hello=world"))
|
||||
let expectedData = "baz?=BAM%3F&foo&=bar%26".data(using: .utf8)
|
||||
expect(req.url?.absoluteString).to(equal("http://example.com/1/2/3?anyone?=there%253F&hello%26=world%2526"))
|
||||
expect(req.allHTTPHeaderFields?["Content-Type"]).to(equal("application/x-www-form-urlencoded"))
|
||||
expect(req.httpBody).to(equal(expectedData))
|
||||
}
|
||||
|
@ -48,9 +43,9 @@ class ApiRouteTests: QuickSpec {
|
|||
let items = route.queryItems.sorted { $0.name < $1.name }
|
||||
expect(items.count).to(equal(2))
|
||||
expect(items[0].name).to(equal("anyone?"))
|
||||
expect(items[0].value).to(equal("there?"))
|
||||
expect(items[1].name).to(equal("hello"))
|
||||
expect(items[1].value).to(equal("world"))
|
||||
expect(items[0].value).to(equal("there%3F"))
|
||||
expect(items[1].name).to(equal("hello&"))
|
||||
expect(items[1].value).to(equal("world%26"))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,7 +53,7 @@ class ApiRouteTests: QuickSpec {
|
|||
|
||||
it("is correctly configured") {
|
||||
let req = route.request(for: env)
|
||||
expect(req.url?.absoluteString).to(equal("http://example.com/1/2/3?anyone?=there?&hello=world"))
|
||||
expect(req.url?.absoluteString).to(equal("http://example.com/1/2/3?anyone?=there%253F&hello%26=world%2526"))
|
||||
expect(req.allHTTPHeaderFields?["Content-Type"]).to(equal("application/json"))
|
||||
}
|
||||
}
|
||||
|
@ -86,11 +81,7 @@ private struct TestEnvironment: ApiEnvironment {
|
|||
private struct TestRoute: ApiRoute {
|
||||
|
||||
var path: String { "1/2/3" }
|
||||
var queryParams: [String: String] { ["hello": "world", "anyone?": "there?"] }
|
||||
}
|
||||
|
||||
private struct TestAmpersandRoute: ApiRoute {
|
||||
|
||||
var path: String { "1/2/3" }
|
||||
var queryParams: [String: String] { ["hello": "world", "anyone?": "there?"] }
|
||||
var postData: Data? { nil }
|
||||
var postParams: [String : String] { ["foo&": "bar&", "baz?": "BAM?"] }
|
||||
var queryParams: [String: String] { ["hello&": "world&", "anyone?": "there?"] }
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue