Skip to content

Commit 1b01f95

Browse files
author
Fernando Fernandes
committed
Finalize account overview
1 parent 7cb717a commit 1b01f95

16 files changed

+228
-43
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//
2+
// Decodable+String.swift
3+
//
4+
//
5+
// Created by Fernando Fernandes on 30.01.22.
6+
//
7+
8+
import Foundation
9+
10+
public extension Decodable {
11+
12+
func toString() -> String {
13+
if let data = try? JSONSerialization.data(withJSONObject: self, options: .prettyPrinted),
14+
let dataString = String(data: data, encoding: .utf8) {
15+
return dataString
16+
} else {
17+
return ""
18+
}
19+
}
20+
}

Sources/SwiftTrader/Extensions/URLRequest+HTTPMethod.swift renamed to Sources/SwiftTrader/HTTPMethod.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// URLRequest+HTTPMethod.swift
2+
// HTTPMethod.swift
33
//
44
//
55
// Created by Fernando Fernandes on 29.01.22.

Sources/SwiftTrader/Model/Kucoin/KucoinError.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import Foundation
99

10-
/// Represents errors that may occur while interacting with Kucoin REST APIs.
10+
/// Encapsulates Kucoin system errors that may occur while interacting with its REST APIs.
1111
///
1212
/// https://docs.kucoin.com/futures/#requests
1313
public struct KucoinSystemError: Codable {

Sources/SwiftTrader/Network/Kucoin/KucoinAccountOverviewRequest.swift renamed to Sources/SwiftTrader/Network/Kucoin/AccountOverview/KucoinAccountOverviewRequest.swift

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,7 @@ public struct KucoinAccountOverviewRequest: NetworkRequest {
2323
let accountOverviewResource = KucoinAccountOverviewResource(currencySymbol: currencySymbol)
2424
var urlRequest = URLRequest(url: try accountOverviewResource.url)
2525
urlRequest.httpMethod = HTTPMethod.GET.rawValue
26-
27-
#warning("TODO: header")
28-
// request.setValue(key, forHTTPHeaderField: "KC-API-KEY")
29-
// request.setValue(signature(secret: secret), forHTTPHeaderField: "KC-API-SIGN")
30-
// request.setValue("\(timestampMilliseconds)", forHTTPHeaderField: "KC-API-TIMESTAMP")
31-
// request.setValue(passphrase, forHTTPHeaderField: "KC-API-PASSPHRASE")
32-
26+
try KucoinAPI.setRequestHeaderFields(request: &urlRequest, kucoinAuth: kucoinAuth)
3327
return urlRequest
3428
}
3529
}
@@ -56,9 +50,6 @@ public struct KucoinAccountOverviewRequest: NetworkRequest {
5650

5751
public extension KucoinAccountOverviewRequest {
5852

59-
#warning("TODO: kucoin error")
60-
// case couldNotDecodeKucoinError(error: Error)
61-
// case statusCodeNotOK(statusCode: Int, error: String, kucoinError: String)
6253
func decode(_ data: Data) throws -> KucoinFuturesAccountOverviewResponse {
6354
try JSONDecoder().decode(KucoinFuturesAccountOverviewResponse.self, from: data)
6455
}

Sources/SwiftTrader/Network/Kucoin/KucoinAccountOverviewResource.swift renamed to Sources/SwiftTrader/Network/Kucoin/AccountOverview/KucoinAccountOverviewResource.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@ import Foundation
1010
/// The **resource** for requesting an overview of a Kucoin Futures account.
1111
///
1212
/// https://docs.kucoin.com/futures/#account
13-
public struct KucoinAccountOverviewResource: APIResource {
13+
public struct KucoinAccountOverviewResource: NetworkResource {
1414

1515
// MARK: - Properties
1616

1717
public var url: URL {
1818
get throws {
19-
let baseURLString = KucoinAPI.URL.base
19+
let baseURLString = KucoinAPI.Futures.URL.base
2020
guard var urlComponents = URLComponents(string: baseURLString) else {
2121
throw NetworkRequestError.invalidURLString(urlString: baseURLString)
2222
}
23-
urlComponents.path = KucoinAPI.Path.accountOverview
23+
urlComponents.path = KucoinAPI.Futures.Path.accountOverview
2424
let queryItems = [
2525
URLQueryItem(name: KucoinAPI.QueryParam.currency, value: currencySymbol.rawValue)
2626
]
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//
2+
// KucoinAPI+Header.swift
3+
//
4+
//
5+
// Created by Fernando Fernandes on 30.01.22.
6+
//
7+
8+
import Foundation
9+
10+
/// Holds logic to set values for default HTTP header fields common to all Kucoin APIs requests.
11+
extension KucoinAPI {
12+
13+
/// Sets values for default header fields in the given `URLRequest`
14+
///
15+
/// Default header fields include `KC-API-KEY`, `KC-API-SIGN`, `KC-API-TIMESTAMP`, `KC-API-PASSPHRASE`, etc
16+
///
17+
/// - Parameters:
18+
/// - request: `URLRequest` where the header field values are to be set.
19+
/// - kucoinAuth: `KucoinAuth` that holds Kucoin authentication data.
20+
static func setRequestHeaderFields(request: inout URLRequest, kucoinAuth: KucoinAuth) throws {
21+
setAPIKey(request: &request, kucoinAuth: kucoinAuth)
22+
setAPIPassphrase(request: &request, kucoinAuth: kucoinAuth)
23+
try setAPISignature(request: &request, kucoinAuth: kucoinAuth)
24+
setAPITimestamp(request: &request, kucoinAuth: kucoinAuth)
25+
}
26+
}
27+
28+
// MARK: - Private
29+
30+
private extension KucoinAPI {
31+
32+
/// "KC-API-KEY"
33+
static func setAPIKey(request: inout URLRequest, kucoinAuth: KucoinAuth) {
34+
request.setValue(kucoinAuth.apiKey, forHTTPHeaderField: KucoinAPI.HeaderField.apiKey)
35+
}
36+
37+
/// "KC-API-PASSPHRASE"
38+
static func setAPIPassphrase(request: inout URLRequest, kucoinAuth: KucoinAuth) {
39+
request.setValue(kucoinAuth.apiPassphrase, forHTTPHeaderField: KucoinAPI.HeaderField.apiPassphrase)
40+
}
41+
42+
/// "KC-API-SIGN"
43+
static func setAPISignature(request: inout URLRequest, kucoinAuth: KucoinAuth) throws {
44+
guard let httpMethodString = request.httpMethod else {
45+
throw KucoinAPIError.missingHTTPMethod
46+
}
47+
guard let httpMethod = HTTPMethod(rawValue: httpMethodString) else {
48+
throw KucoinAPIError.unsupportedHTTPMethod
49+
}
50+
guard let url = request.url else {
51+
throw KucoinAPIError.missingURL(url: request.url)
52+
}
53+
guard !url.path.isEmpty else {
54+
throw KucoinAPIError.missingPath(url: url)
55+
}
56+
let finalPath: String
57+
if let query = url.query, !query.isEmpty {
58+
finalPath = "\(url.path)?\(query)"
59+
} else {
60+
finalPath = url.path
61+
}
62+
let signature = try createSignature(for: httpMethod, path: finalPath, secret: kucoinAuth.apiSecret)
63+
request.setValue(signature, forHTTPHeaderField: KucoinAPI.HeaderField.apiSign)
64+
}
65+
66+
/// "KC-API-TIMESTAMP"
67+
static func setAPITimestamp(request: inout URLRequest, kucoinAuth: KucoinAuth) {
68+
request.setValue("\(Date().timestampMilliseconds)", forHTTPHeaderField: KucoinAPI.HeaderField.apiTimestamp)
69+
}
70+
}

Sources/SwiftTrader/Network/Kucoin/KucoinAPI+Signature.swift

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// File.swift
2+
// KucoinAPI+Signature.swift
33
//
44
//
55
// Created by Fernando Fernandes on 29.01.22.
@@ -15,32 +15,26 @@ import CryptoKit
1515
import Vapor
1616
#endif
1717

18-
/// Encapsulates errors that can happen while generating the signature that Kucoin APIs require.
19-
public enum KucoinSignatureError: Error {
20-
case unableToExtractDataFromString(string: String)
21-
}
22-
18+
/// Holds logic to sign requests against Kucoin APIs.
2319
public extension KucoinAPI {
2420

25-
#warning("TODO: I need this from urlrequest - /api/v1/account-overview?currency=USDT")
26-
2721
/// Creates and returns the signature for the `KC-API-SIGN` header field.
2822
///
2923
/// https://docs.kucoin.com/futures/#authentication
3024
/// - Parameters:
3125
/// - method: `HTTPMethod`. E.g.: `GET`, `POST.`
32-
/// - path: Endpoint path including query parameters. E.g.: *"/api/v1/account-overview?currency=USDT"*
26+
/// - path: Endpoint path including query parameters, if any. E.g.: *"/api/v1/account-overview?currency=USDT"*
3327
/// - secret: The API secret.
3428
/// - Returns: `Base64` encoded signature.
3529
static func createSignature(for method: HTTPMethod, path: String, secret: String) throws -> String {
3630
guard let secretData = secret.data(using: .utf8) else {
37-
throw KucoinSignatureError.unableToExtractDataFromString(string: secret)
31+
throw KucoinAPIError.stringToDataFailed(string: secret)
3832
}
3933
let key = SymmetricKey(data: secretData)
4034
let timestamp = Date().timestampMilliseconds
4135
let stringToSign = "\(timestamp)" + HTTPMethod.GET.rawValue + path
4236
guard let stringToSignData = stringToSign.data(using: .utf8) else {
43-
throw KucoinSignatureError.unableToExtractDataFromString(string: stringToSign)
37+
throw KucoinAPIError.stringToDataFailed(string: stringToSign)
4438
}
4539
let signature = HMAC<SHA256>.authenticationCode(for: stringToSignData, using: key)
4640
let base64signature = Data(signature).base64EncodedString()

Sources/SwiftTrader/Network/Kucoin/KucoinAPI.swift

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,25 @@ import Foundation
1010
/// Holds constants related to Kucoin REST APIs.
1111
public struct KucoinAPI {
1212

13+
public struct Futures {
14+
15+
public struct Path {
16+
static let accountOverview = "/api/v1/account-overview"
17+
}
18+
19+
public struct URL {
20+
static let base = "https://api-futures.kucoin.com"
21+
}
22+
}
23+
1324
public struct HeaderField {
1425
static let apiKey = "KC-API-KEY"
1526
static let apiSign = "KC-API-SIGN"
1627
static let apiTimestamp = "KC-API-TIMESTAMP"
1728
static let apiPassphrase = "KC-API-PASSPHRASE"
1829
}
1930

20-
public struct Path {
21-
static let accountOverview = "/api/v1/account-overview"
22-
}
23-
2431
public struct QueryParam {
2532
static let currency = "currency"
2633
}
27-
28-
public struct URL {
29-
static let base = "https://api-futures.kucoin.com"
30-
}
3134
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//
2+
// KucoinAPIError.swift
3+
//
4+
//
5+
// Created by Fernando Fernandes on 30.01.22.
6+
//
7+
8+
import Foundation
9+
10+
/// Encapsulates Kucoin API related errors.
11+
public enum KucoinAPIError: Error {
12+
13+
/// The HTTP method (e.g.: `GET` / `POST`) is absent.
14+
case missingHTTPMethod
15+
16+
/// The `URLRequest`'s `path` (e.g.: `/api/v1/account-overview`) is absent.
17+
case missingPath(url: URL)
18+
19+
/// The `URLRequest`'s `URL` (e.g.: `https://api-futures.kucoin.com`) is absent.
20+
case missingURL(url: URL?)
21+
22+
/// "someString".data(using: .utf8) failed.
23+
case stringToDataFailed(string: String)
24+
25+
/// The HTTP method (e.g.: `GET` / `POST`) is unsupported.
26+
case unsupportedHTTPMethod
27+
}

Sources/SwiftTrader/Network/NetworkRequest/NetworkRequestError.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Foundation
1010
/// Encapsulates errors that may happen during network requests.
1111
public enum NetworkRequestError: Error {
1212

13-
/// `Data` could not be parsed into a `Decodable` object.
13+
/// `Data` could not be parsed to an expected`Decodable` object.
1414
case couldNotDecodeModelFromData(error: Error)
1515

1616
/// Data from response is `nil`.
@@ -26,5 +26,7 @@ public enum NetworkRequestError: Error {
2626
case requestFailed(error: Error)
2727

2828
/// The response status code is something other than `200`.
29-
case statusCodeNotOK(statusCode: Int, localizedErrorMessage: String)
29+
///
30+
/// The underlying error may be verified by reading the `Data` argument.
31+
case statusCodeNotOK(statusCode: Int, localizedErrorMessage: String, data: Data)
3032
}

0 commit comments

Comments
 (0)