Skip to content

Commit 06b560c

Browse files
authored
feat(request): Handle array query parameters (#5)
1 parent 6ec589c commit 06b560c

File tree

5 files changed

+98
-12
lines changed

5 files changed

+98
-12
lines changed

Sources/SimpleHTTP/Foundation/URL+Request.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ extension URL {
1010
throw URLComponents.Error.invalid(path: request.path)
1111
}
1212

13-
let queryItems = (components.queryItems ?? []) + request.parameters.queryItems
13+
let queryItems = (components.queryItems ?? []) + request.query.queryItems
1414

1515
components.queryItems = queryItems.isEmpty ? nil : queryItems
1616

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import Foundation
2+
3+
extension Dictionary where Key == String, Value == QueryParam {
4+
/// transform query params into URLQueryItem`
5+
var queryItems: [URLQueryItem] {
6+
self.flatMap { key, value -> [URLQueryItem] in
7+
switch value.queryValue {
8+
case .single(let value):
9+
return [URLQueryItem(name: key, value: value)]
10+
case .collection(let values):
11+
return values.map { URLQueryItem(name: "\(key)[]", value: $0) }
12+
case .none:
13+
return []
14+
}
15+
}
16+
}
17+
18+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import Foundation
2+
3+
/// Protocol allowing to use a type as a query parameter
4+
public protocol QueryParam {
5+
/// the parameter value
6+
var queryValue: QueryValue? { get }
7+
}
8+
9+
/// Query parameter value
10+
public enum QueryValue: Equatable {
11+
case single(String)
12+
case collection([String])
13+
}
14+
15+
extension QueryParam where Self: RawRepresentable, RawValue: QueryParam {
16+
public var queryValue: QueryValue? { rawValue.queryValue }
17+
}
18+
19+
extension Int: QueryParam {
20+
public var queryValue: QueryValue? { .single(String(self)) }
21+
}
22+
23+
extension String: QueryParam {
24+
public var queryValue: QueryValue? { .single(self) }
25+
}
26+
27+
extension Bool: QueryParam {
28+
public var queryValue: QueryValue? { .single(self ? "true" : "false") }
29+
}
30+
31+
extension Data: QueryParam {
32+
public var queryValue: QueryValue? { String(data: self, encoding: .utf8).map(QueryValue.single) }
33+
}
34+
35+
extension Optional: QueryParam where Wrapped: QueryParam {
36+
public var queryValue: QueryValue? {
37+
self.flatMap { $0.queryValue }
38+
}
39+
}
40+
41+
extension Array: QueryParam where Element: QueryParam {
42+
public var queryValue: QueryValue? {
43+
let values = self
44+
.compactMap { $0.queryValue }
45+
.flatMap { queryValue -> [String] in
46+
switch queryValue {
47+
case .single(let value):
48+
return [value]
49+
case .collection(let values):
50+
return values
51+
}
52+
}
53+
54+
return .collection(values)
55+
}
56+
}

Sources/SimpleHTTP/Request/Request.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,32 @@ public struct Request<Output> {
1616
public let path: String
1717
public let method: Method
1818
public let body: Encodable?
19-
public let parameters: [String: String]
19+
public let query: [String: QueryParam]
2020
public private(set) var headers: HTTPHeaderFields = [:]
2121

22-
public static func get(_ path: Path, parameters: [String: String] = [:]) -> Self {
23-
self.init(path: path, method: .get, parameters: parameters, body: nil)
22+
public static func get(_ path: Path, query: [String: QueryParam] = [:]) -> Self {
23+
self.init(path: path, method: .get, query: query, body: nil)
2424
}
2525

26-
public static func post(_ path: Path, body: Encodable?, parameters: [String: String] = [:])
26+
public static func post(_ path: Path, body: Encodable?, query: [String: QueryParam] = [:])
2727
-> Self {
28-
self.init(path: path, method: .post, parameters: parameters, body: body)
28+
self.init(path: path, method: .post, query: query, body: body)
2929
}
3030

31-
public static func put(_ path: Path, body: Encodable, parameters: [String: String] = [:])
31+
public static func put(_ path: Path, body: Encodable, query: [String: QueryParam] = [:])
3232
-> Self {
33-
self.init(path: path, method: .put, parameters: parameters, body: body)
33+
self.init(path: path, method: .put, query: query, body: body)
3434
}
3535

36-
public static func delete(_ path: Path, parameters: [String: String] = [:]) -> Self {
37-
self.init(path: path, method: .delete, parameters: parameters, body: nil)
36+
public static func delete(_ path: Path, query: [String: QueryParam] = [:]) -> Self {
37+
self.init(path: path, method: .delete, query: query, body: nil)
3838
}
3939

40-
private init(path: Path, method: Method, parameters: [String: String] = [:], body: Encodable?) {
40+
private init(path: Path, method: Method, query: [String: QueryParam], body: Encodable?) {
4141
self.path = path.path
4242
self.method = method
4343
self.body = body
44-
self.parameters = parameters
44+
self.query = query
4545
}
4646

4747
/// add headers to the request
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import XCTest
2+
import SimpleHTTP
3+
4+
class QueryParamTests: XCTestCase {
5+
6+
func test_queryValue_multidimenstionalArray_returnFlattenCollection() {
7+
let array: [[Int]] = [[1, 2, 3],[4,5,6]]
8+
9+
XCTAssertEqual(array.queryValue, .collection(["1", "2", "3", "4", "5", "6"]))
10+
}
11+
12+
}

0 commit comments

Comments
 (0)