|
|
|
|
@ -49,20 +49,20 @@ public class Services { |
|
|
|
|
/// The base API URL to send requests |
|
|
|
|
fileprivate(set) var baseURL: String |
|
|
|
|
|
|
|
|
|
fileprivate var jsonEncoder: JSONEncoder = { |
|
|
|
|
let encoder = JSONEncoder() |
|
|
|
|
encoder.keyEncodingStrategy = .convertToSnakeCase |
|
|
|
|
encoder.outputFormatting = .prettyPrinted |
|
|
|
|
encoder.dateEncodingStrategy = .iso8601 |
|
|
|
|
return encoder |
|
|
|
|
}() |
|
|
|
|
|
|
|
|
|
fileprivate var jsonDecoder: JSONDecoder = { |
|
|
|
|
let decoder = JSONDecoder() |
|
|
|
|
decoder.keyDecodingStrategy = .convertFromSnakeCase |
|
|
|
|
decoder.dateDecodingStrategy = .iso8601 |
|
|
|
|
return decoder |
|
|
|
|
}() |
|
|
|
|
// fileprivate var jsonEncoder: JSONEncoder = { |
|
|
|
|
// let encoder = JSONEncoder() |
|
|
|
|
// encoder.keyEncodingStrategy = .convertToSnakeCase |
|
|
|
|
// encoder.outputFormatting = .prettyPrinted |
|
|
|
|
// encoder.dateEncodingStrategy = .iso8601 |
|
|
|
|
// return encoder |
|
|
|
|
// }() |
|
|
|
|
// |
|
|
|
|
// fileprivate var jsonDecoder: JSONDecoder = { |
|
|
|
|
// let decoder = JSONDecoder() |
|
|
|
|
// decoder.keyDecodingStrategy = .convertFromSnakeCase |
|
|
|
|
// decoder.dateDecodingStrategy = .iso8601 |
|
|
|
|
// return decoder |
|
|
|
|
// }() |
|
|
|
|
|
|
|
|
|
// MARK: - Base |
|
|
|
|
|
|
|
|
|
@ -75,7 +75,7 @@ public class Services { |
|
|
|
|
async throws -> U |
|
|
|
|
{ |
|
|
|
|
var request = try self._baseRequest(call: serviceCall) |
|
|
|
|
request.httpBody = try jsonEncoder.encode(payload) |
|
|
|
|
request.httpBody = try JSON.encoder.encode(payload) |
|
|
|
|
return try await _runRequest(request) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -99,7 +99,7 @@ public class Services { |
|
|
|
|
try await StoreCenter.main.deleteApiCallById(type: T.self, id: apiCall.id) |
|
|
|
|
|
|
|
|
|
if T.self == GetSyncData.self { |
|
|
|
|
StoreCenter.main.synchronizeContent(task.0, decoder: self.jsonDecoder) |
|
|
|
|
StoreCenter.main.synchronizeContent(task.0) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
default: // error |
|
|
|
|
@ -131,9 +131,9 @@ public class Services { |
|
|
|
|
|
|
|
|
|
fileprivate func _decode<V: Decodable>(data: Data) throws -> V { |
|
|
|
|
if !(V.self is Empty?.Type || V.self is Empty.Type) { |
|
|
|
|
return try jsonDecoder.decode(V.self, from: data) |
|
|
|
|
return try JSON.decoder.decode(V.self, from: data) |
|
|
|
|
} else { |
|
|
|
|
return try jsonDecoder.decode(V.self, from: "{}".data(using: .utf8)!) |
|
|
|
|
return try JSON.decoder.decode(V.self, from: "{}".data(using: .utf8)!) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -277,7 +277,7 @@ public class Services { |
|
|
|
|
var request = URLRequest(url: url) |
|
|
|
|
request.httpMethod = method.rawValue |
|
|
|
|
request.setValue("application/json", forHTTPHeaderField: "Content-Type") |
|
|
|
|
request.httpBody = try jsonEncoder.encode(payload) |
|
|
|
|
request.httpBody = try JSON.encoder.encode(payload) |
|
|
|
|
|
|
|
|
|
let token = try self.keychainStore.getValue() |
|
|
|
|
request.addValue("Token \(token)", forHTTPHeaderField: "Authorization") |
|
|
|
|
@ -288,33 +288,41 @@ public class Services { |
|
|
|
|
/// Returns the URLRequest for an ApiCall |
|
|
|
|
/// - Parameters: |
|
|
|
|
/// - apiCall: An ApiCall instance to configure the returned request |
|
|
|
|
fileprivate func _syncRequest<T: SyncedStorable>(from apiCall: ApiCall<T>) throws -> URLRequest |
|
|
|
|
{ |
|
|
|
|
fileprivate func _syncRequest<T: SyncedStorable>(from apiCall: ApiCall<T>) throws -> URLRequest { |
|
|
|
|
|
|
|
|
|
let urlString = baseURL + "data/" |
|
|
|
|
var urlString = baseURL + "data/" |
|
|
|
|
if let urlParameters = apiCall.formattedURLParameters() { |
|
|
|
|
urlString.append(urlParameters) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
guard let url = URL(string: urlString) else { |
|
|
|
|
throw ServiceError.urlCreationError(url: urlString) |
|
|
|
|
} |
|
|
|
|
guard let body = apiCall.body, let bodyData = body.data(using: .utf8) else { |
|
|
|
|
throw ServiceError.cantDecodeData(content: apiCall.body) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var request = URLRequest(url: url) |
|
|
|
|
request.httpMethod = HTTPMethod.post.rawValue |
|
|
|
|
if apiCall.method == .get { |
|
|
|
|
request.httpMethod = HTTPMethod.get.rawValue |
|
|
|
|
} else { |
|
|
|
|
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 modelName = String(describing: T.self) |
|
|
|
|
|
|
|
|
|
let payload = SyncPayload( |
|
|
|
|
operation: apiCall.method.rawValue, |
|
|
|
|
modelName: modelName, |
|
|
|
|
data: data, |
|
|
|
|
storeId: data.getStoreId()) |
|
|
|
|
|
|
|
|
|
request.httpBody = try jsonEncoder.encode(payload) |
|
|
|
|
if let body = apiCall.body { |
|
|
|
|
if let data = body.data(using: .utf8) { |
|
|
|
|
let object = try JSON.decoder.decode(T.self, from: data) |
|
|
|
|
let modelName = String(describing: T.self) |
|
|
|
|
let payload = SyncPayload( |
|
|
|
|
operation: apiCall.method.rawValue, |
|
|
|
|
modelName: modelName, |
|
|
|
|
data: object, |
|
|
|
|
storeId: object.getStoreId()) |
|
|
|
|
request.httpBody = try JSON.encoder.encode(payload) |
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
throw ServiceError.cantDecodeData(resource: T.resourceName(), method: apiCall.method.rawValue, content: apiCall.body) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if self._isTokenRequired(type: T.self, method: apiCall.method) { |
|
|
|
|
let token = try self.keychainStore.getValue() |
|
|
|
|
@ -371,7 +379,7 @@ public class Services { |
|
|
|
|
print("\(debugURL) ended, status code = \(statusCode)") |
|
|
|
|
switch statusCode { |
|
|
|
|
case 200..<300: // success |
|
|
|
|
StoreCenter.main.synchronizeContent(task.0, decoder: self.jsonDecoder) |
|
|
|
|
StoreCenter.main.synchronizeContent(task.0) |
|
|
|
|
default: // error |
|
|
|
|
Logger.log( |
|
|
|
|
"Failed Run \(request.httpMethod ?? "") \(request.url?.absoluteString ?? "")") |
|
|
|
|
@ -463,12 +471,8 @@ public class Services { |
|
|
|
|
/// - apiCall: an instance of ApiCall to build to URL |
|
|
|
|
fileprivate func _url<T: Storable>(from apiCall: ApiCall<T>) throws -> URL { |
|
|
|
|
var stringURL: String = self.baseURL |
|
|
|
|
switch apiCall.method { |
|
|
|
|
case HTTPMethod.put, HTTPMethod.delete: |
|
|
|
|
stringURL += T.path(id: apiCall.dataId) |
|
|
|
|
default: |
|
|
|
|
stringURL += T.path() |
|
|
|
|
} |
|
|
|
|
stringURL += apiCall.urlExtension() |
|
|
|
|
|
|
|
|
|
if let url = URL(string: stringURL) { |
|
|
|
|
return url |
|
|
|
|
} else { |
|
|
|
|
@ -493,7 +497,7 @@ public class Services { |
|
|
|
|
var postRequest = try self._baseRequest(call: requestTokenCall) |
|
|
|
|
let deviceId = StoreCenter.main.deviceId() |
|
|
|
|
let credentials = Credentials(username: username, password: password, deviceId: deviceId) |
|
|
|
|
postRequest.httpBody = try jsonEncoder.encode(credentials) |
|
|
|
|
postRequest.httpBody = try JSON.encoder.encode(credentials) |
|
|
|
|
let response: AuthResponse = try await self._runRequest(postRequest) |
|
|
|
|
self._storeToken(username: username, token: response.token) |
|
|
|
|
return response.token |
|
|
|
|
@ -580,7 +584,7 @@ public class Services { |
|
|
|
|
public func forgotPassword(email: String) async throws { |
|
|
|
|
var postRequest = try self._baseRequest( |
|
|
|
|
servicePath: "dj-rest-auth/password/reset/", method: .post, requiresToken: false) |
|
|
|
|
postRequest.httpBody = try jsonEncoder.encode(Email(email: email)) |
|
|
|
|
postRequest.httpBody = try JSON.encoder.encode(Email(email: email)) |
|
|
|
|
let response: Email = try await self._runRequest(postRequest) |
|
|
|
|
Logger.log("response = \(response)") |
|
|
|
|
} |
|
|
|
|
|