|
|
|
|
@ -100,17 +100,17 @@ public class Services { |
|
|
|
|
/// - serviceConf: A instance of ServiceConf |
|
|
|
|
/// - payload: a codable value stored in the body of the request |
|
|
|
|
/// - apiCallId: an optional id referencing an ApiCall |
|
|
|
|
fileprivate func _runRequest<T: Encodable, U: Decodable>(serviceCall: ServiceCall, payload: T, apiCallId: String? = nil) async throws -> U { |
|
|
|
|
fileprivate func _runRequest<T: Encodable, U: Decodable>(serviceCall: ServiceCall, payload: T) async throws -> U { |
|
|
|
|
var request = try self._baseRequest(call: serviceCall) |
|
|
|
|
request.httpBody = try jsonEncoder.encode(payload) |
|
|
|
|
return try await _runRequest(request, apiCallId: apiCallId) |
|
|
|
|
return try await _runRequest(request) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Runs a request using a traditional URLRequest |
|
|
|
|
/// - Parameters: |
|
|
|
|
/// - request: the URLRequest to run |
|
|
|
|
/// - apiCallId: the id of the ApiCall to delete in case of success, or to schedule for a rerun in case of failure |
|
|
|
|
fileprivate func _runRequest<T: Decodable>(_ request: URLRequest, apiCallId: String? = nil) async throws -> T { |
|
|
|
|
fileprivate func _runRequest<T: Storable, V: Decodable>(_ request: URLRequest, apiCall: ApiCall<T>) async throws -> V { |
|
|
|
|
let debugURL = request.url?.absoluteString ?? "" |
|
|
|
|
print("Run \(request.httpMethod ?? "") \(debugURL)") |
|
|
|
|
let task: (Data, URLResponse) = try await URLSession.shared.data(for: request) |
|
|
|
|
@ -121,13 +121,7 @@ public class Services { |
|
|
|
|
print("\(debugURL) ended, status code = \(statusCode)") |
|
|
|
|
switch statusCode { |
|
|
|
|
case 200..<300: // success |
|
|
|
|
if let apiCallId { |
|
|
|
|
if let collectionName = (T.self as? any Storable.Type)?.resourceName() { |
|
|
|
|
try await StoreCenter.main.deleteApiCallById(apiCallId, collectionName: collectionName) |
|
|
|
|
} else { |
|
|
|
|
StoreCenter.main.log(message: "collectionName not found for \(type(of: T.self)), could not delete ApiCall \(apiCallId)") |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
try await StoreCenter.main.deleteApiCallById(type: T.self, id: apiCall.id) |
|
|
|
|
default: // error |
|
|
|
|
Logger.log("Failed Run \(request.httpMethod ?? "") \(request.url?.absoluteString ?? "")") |
|
|
|
|
let errorString: String = String(data: task.0, encoding: .utf8) ?? "" |
|
|
|
|
@ -137,12 +131,8 @@ public class Services { |
|
|
|
|
errorMessage = message |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if let apiCallId, let type = (T.self as? any Storable.Type) { |
|
|
|
|
try await StoreCenter.main.rescheduleApiCalls(id: apiCallId, type: type) |
|
|
|
|
StoreCenter.main.logFailedAPICall(apiCallId, request: request, collectionName: type.resourceName(), error: errorMessage.message) |
|
|
|
|
} else { |
|
|
|
|
StoreCenter.main.logFailedAPICall(request: request, error: errorMessage.message) |
|
|
|
|
} |
|
|
|
|
try await StoreCenter.main.rescheduleApiCalls(id: apiCall.id, type: T.self) |
|
|
|
|
StoreCenter.main.logFailedAPICall(apiCall.id, request: request, collectionName: T.resourceName(), error: errorMessage.message) |
|
|
|
|
|
|
|
|
|
throw ServiceError.responseError(response: errorMessage.error) |
|
|
|
|
} |
|
|
|
|
@ -151,8 +141,45 @@ public class Services { |
|
|
|
|
StoreCenter.main.log(message: message) |
|
|
|
|
Logger.w(message) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return try jsonDecoder.decode(T.self, from: task.0) |
|
|
|
|
|
|
|
|
|
if !(V.self is Empty?.Type) { |
|
|
|
|
return try jsonDecoder.decode(V.self, from: task.0) |
|
|
|
|
} else { |
|
|
|
|
return try jsonDecoder.decode(V.self, from: "{}".data(using: .utf8)!) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Runs a request using a traditional URLRequest |
|
|
|
|
/// - Parameters: |
|
|
|
|
/// - request: the URLRequest to run |
|
|
|
|
/// - apiCallId: the id of the ApiCall to delete in case of success, or to schedule for a rerun in case of failure |
|
|
|
|
fileprivate func _runRequest<V: Decodable>(_ request: URLRequest) async throws -> V { |
|
|
|
|
let debugURL = request.url?.absoluteString ?? "" |
|
|
|
|
print("Run \(request.httpMethod ?? "") \(debugURL)") |
|
|
|
|
let task: (Data, URLResponse) = try await URLSession.shared.data(for: request) |
|
|
|
|
print("response = \(String(data: task.0, encoding: .utf8) ?? "")") |
|
|
|
|
|
|
|
|
|
if let response = task.1 as? HTTPURLResponse { |
|
|
|
|
let statusCode = response.statusCode |
|
|
|
|
print("\(debugURL) ended, status code = \(statusCode)") |
|
|
|
|
switch statusCode { |
|
|
|
|
case 200..<300: // success |
|
|
|
|
break |
|
|
|
|
default: // error |
|
|
|
|
Logger.log("Failed Run \(request.httpMethod ?? "") \(request.url?.absoluteString ?? "")") |
|
|
|
|
let errorString: String = String(data: task.0, encoding: .utf8) ?? "" |
|
|
|
|
var errorMessage = ErrorMessage(error: errorString, domain: "") |
|
|
|
|
if let message = self.errorMessageFromResponse(data: task.0) { |
|
|
|
|
errorMessage = message |
|
|
|
|
} |
|
|
|
|
throw ServiceError.responseError(response: errorMessage.error) |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
let message: String = "Unexpected and unmanaged URL Response \(task.1)" |
|
|
|
|
StoreCenter.main.log(message: message) |
|
|
|
|
Logger.w(message) |
|
|
|
|
} |
|
|
|
|
return try jsonDecoder.decode(V.self, from: task.0) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Returns if the token is required for a request |
|
|
|
|
@ -254,10 +281,10 @@ public class Services { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Executes an ApiCall |
|
|
|
|
func runApiCall<T: Storable>(_ apiCall: ApiCall<T>) async throws -> T { |
|
|
|
|
func runApiCall<T: Storable, V: Decodable>(_ apiCall: ApiCall<T>) async throws -> V { |
|
|
|
|
let request = try self._request(from: apiCall) |
|
|
|
|
print("HTTP \(request.httpMethod ?? "") : id = \(apiCall.dataId)") |
|
|
|
|
return try await self._runRequest(request, apiCallId: apiCall.id) |
|
|
|
|
return try await self._runRequest(request, apiCall: apiCall) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Returns the URLRequest for an ApiCall |
|
|
|
|
|