|
|
|
|
@ -7,6 +7,15 @@ |
|
|
|
|
|
|
|
|
|
import Foundation |
|
|
|
|
|
|
|
|
|
public enum ServiceError: Error { |
|
|
|
|
case urlCreationError(url: String) |
|
|
|
|
case cantConvertToUUID(id: String) |
|
|
|
|
case missingUserName |
|
|
|
|
case missingUserId |
|
|
|
|
case responseError(response: String) |
|
|
|
|
case cantDecodeData(content: String) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public enum HTTPMethod: String, CaseIterable, Codable { |
|
|
|
|
case get = "GET" |
|
|
|
|
case post = "POST" |
|
|
|
|
@ -206,6 +215,30 @@ public class Services { |
|
|
|
|
return try self._baseRequest(servicePath: call.path, method: call.method, requiresToken: call.requiresToken) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Returns a base request for a path and method |
|
|
|
|
/// - Parameters: |
|
|
|
|
/// - servicePath: the path to add to the API base URL |
|
|
|
|
/// - method: the HTTP method to execute |
|
|
|
|
/// - requiresToken: An optional boolean to indicate if the token is required |
|
|
|
|
/// - identifier: an optional StoreIdentifier that allows to filter GET requests with the StoreIdentifier values |
|
|
|
|
fileprivate func _baseSyncRequest(method: HTTPMethod, payload: Encodable) throws -> URLRequest { |
|
|
|
|
let urlString = baseURL + "data/" |
|
|
|
|
|
|
|
|
|
guard let url = URL(string: urlString) else { |
|
|
|
|
throw ServiceError.urlCreationError(url: urlString) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var request = URLRequest(url: url) |
|
|
|
|
request.httpMethod = method.rawValue |
|
|
|
|
request.setValue("application/json", forHTTPHeaderField: "Content-Type") |
|
|
|
|
request.httpBody = try jsonEncoder.encode(payload) |
|
|
|
|
|
|
|
|
|
let token = try self.keychainStore.getValue() |
|
|
|
|
request.addValue("Token \(token)", forHTTPHeaderField: "Authorization") |
|
|
|
|
|
|
|
|
|
return request |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Returns a base request for a path and method |
|
|
|
|
/// - Parameters: |
|
|
|
|
/// - servicePath: the path to add to the API base URL |
|
|
|
|
@ -240,9 +273,15 @@ public class Services { |
|
|
|
|
|
|
|
|
|
/// Executes a POST request |
|
|
|
|
public func post<T: Storable>(_ instance: T) async throws -> T { |
|
|
|
|
var postRequest = try self._postRequest(type: T.self) |
|
|
|
|
postRequest.httpBody = try jsonEncoder.encode(instance) |
|
|
|
|
return try await self._runRequest(postRequest) |
|
|
|
|
|
|
|
|
|
let method: HTTPMethod = .post |
|
|
|
|
let payload = SyncPayload(operation: method.rawValue, modelName: T.resourceName(), data: instance) |
|
|
|
|
let syncRequest = try self._baseSyncRequest(method: .post, payload: payload) |
|
|
|
|
return try await self._runRequest(syncRequest) |
|
|
|
|
|
|
|
|
|
// var postRequest = try self._postRequest(type: T.self) |
|
|
|
|
// postRequest.httpBody = try jsonEncoder.encode(instance) |
|
|
|
|
// return try await self._runRequest(postRequest) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Executes a PUT request |
|
|
|
|
@ -254,11 +293,43 @@ public class Services { |
|
|
|
|
|
|
|
|
|
/// Executes an ApiCall |
|
|
|
|
func runApiCall<T: Storable>(_ apiCall: ApiCall<T>) async throws -> T { |
|
|
|
|
let request = try self._request(from: apiCall) |
|
|
|
|
let request = try self._syncRequest(from: apiCall) |
|
|
|
|
print("HTTP \(request.httpMethod ?? "") : id = \(apiCall.dataId)") |
|
|
|
|
return try await self._runRequest(request, apiCallId: apiCall.id) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Returns the URLRequest for an ApiCall |
|
|
|
|
/// - Parameters: |
|
|
|
|
/// - apiCall: An ApiCall instance to configure the returned request |
|
|
|
|
fileprivate func _syncRequest<T: Storable>(from apiCall: ApiCall<T>) throws -> URLRequest { |
|
|
|
|
|
|
|
|
|
let urlString = baseURL + "data/" |
|
|
|
|
|
|
|
|
|
guard let url = URL(string: urlString) else { |
|
|
|
|
throw ServiceError.urlCreationError(url: urlString) |
|
|
|
|
} |
|
|
|
|
guard let bodyData = apiCall.body.data(using: .utf8) else { |
|
|
|
|
throw ServiceError.cantDecodeData(content: apiCall.body) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var request = URLRequest(url: url) |
|
|
|
|
request.httpMethod = HTTPMethod.post.rawValue |
|
|
|
|
request.setValue("application/json", forHTTPHeaderField: "Content-Type") |
|
|
|
|
|
|
|
|
|
// moyennement fan de decoder pour recoder derriere |
|
|
|
|
let data = try jsonDecoder.decode(T.self, from: bodyData) |
|
|
|
|
let payload = SyncPayload(operation: apiCall.method.rawValue, modelName: T.resourceName(), data: data) |
|
|
|
|
|
|
|
|
|
request.httpBody = try jsonEncoder.encode(payload) |
|
|
|
|
|
|
|
|
|
if self._isTokenRequired(type: T.self, method: apiCall.method) { |
|
|
|
|
let token = try self.keychainStore.getValue() |
|
|
|
|
request.addValue("Token \(token)", forHTTPHeaderField: "Authorization") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return request |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Returns the URLRequest for an ApiCall |
|
|
|
|
/// - Parameters: |
|
|
|
|
/// - apiCall: An ApiCall instance to configure the returned request |
|
|
|
|
@ -280,6 +351,7 @@ public class Services { |
|
|
|
|
|
|
|
|
|
return request |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Returns the URL corresponding to the ApiCall |
|
|
|
|
/// - Parameters: |
|
|
|
|
@ -460,6 +532,12 @@ public class Services { |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
struct SyncPayload<T: Encodable>: Encodable { |
|
|
|
|
var operation: String |
|
|
|
|
var modelName: String |
|
|
|
|
var data: T |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
struct ErrorMessage { |
|
|
|
|
let error: String |
|
|
|
|
let domain: String |
|
|
|
|
|