|
|
|
|
@ -32,6 +32,10 @@ let changePasswordCall: ServiceCall = ServiceCall( |
|
|
|
|
path: "change-password/", method: .put, requiresToken: true) |
|
|
|
|
let postDeviceTokenCall: ServiceCall = ServiceCall( |
|
|
|
|
path: "device-token/", method: .post, requiresToken: true) |
|
|
|
|
let getUserDataAccessCall: ServiceCall = ServiceCall( |
|
|
|
|
path: "user-data-access/", method: .get, requiresToken: true) |
|
|
|
|
let userSearchCall: ServiceCall = ServiceCall( |
|
|
|
|
path: "users-search/", method: .get, requiresToken: true) |
|
|
|
|
|
|
|
|
|
/// A class used to send HTTP request to the django server |
|
|
|
|
public class Services { |
|
|
|
|
@ -49,31 +53,27 @@ 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 |
|
|
|
|
// }() |
|
|
|
|
|
|
|
|
|
// MARK: - Base |
|
|
|
|
|
|
|
|
|
/// Runs a request using a configuration object |
|
|
|
|
/// - Parameters: |
|
|
|
|
/// - 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) |
|
|
|
|
fileprivate func _runRequest<U: Decodable>(serviceCall: ServiceCall) |
|
|
|
|
async throws -> U |
|
|
|
|
{ |
|
|
|
|
let request = try self._baseRequest(call: serviceCall) |
|
|
|
|
return try await _runRequest(request) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Runs a request using a configuration object |
|
|
|
|
/// - Parameters: |
|
|
|
|
/// - 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) |
|
|
|
|
async throws -> U { |
|
|
|
|
var request = try self._baseRequest(call: serviceCall) |
|
|
|
|
request.httpBody = try JSON.encoder.encode(payload) |
|
|
|
|
return try await _runRequest(request) |
|
|
|
|
@ -197,41 +197,12 @@ public class Services { |
|
|
|
|
identifier: identifier) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Returns a POST request for the resource |
|
|
|
|
/// - Parameters: |
|
|
|
|
/// - type: the type of the request resource |
|
|
|
|
// fileprivate func _postRequest<T: SyncedStorable>(type: T.Type) throws -> URLRequest { |
|
|
|
|
// let requiresToken = self._isTokenRequired(type: T.self, method: .post) |
|
|
|
|
// return try self._baseRequest( |
|
|
|
|
// servicePath: T.path(), method: .post, requiresToken: requiresToken) |
|
|
|
|
// } |
|
|
|
|
// |
|
|
|
|
// /// Returns a PUT request for the resource |
|
|
|
|
// /// - Parameters: |
|
|
|
|
// /// - type: the type of the request resource |
|
|
|
|
// fileprivate func _putRequest<T: SyncedStorable>(type: T.Type, id: String) throws -> URLRequest { |
|
|
|
|
// let requiresToken = self._isTokenRequired(type: T.self, method: .put) |
|
|
|
|
// return try self._baseRequest( |
|
|
|
|
// servicePath: T.path(id: id), method: .put, requiresToken: requiresToken) |
|
|
|
|
// } |
|
|
|
|
// |
|
|
|
|
// /// Returns a DELETE request for the resource |
|
|
|
|
// /// - Parameters: |
|
|
|
|
// /// - type: the type of the request resource |
|
|
|
|
// fileprivate func _deleteRequest<T: SyncedStorable>(type: T.Type, id: String) throws |
|
|
|
|
// -> URLRequest |
|
|
|
|
// { |
|
|
|
|
// let requiresToken = self._isTokenRequired(type: T.self, method: .delete) |
|
|
|
|
// return try self._baseRequest( |
|
|
|
|
// servicePath: T.path(id: id), method: .delete, requiresToken: requiresToken) |
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
/// Returns the base URLRequest for a ServiceConf instance |
|
|
|
|
/// - Parameters: |
|
|
|
|
/// - conf: a ServiceConf instance |
|
|
|
|
fileprivate func _baseRequest(call: ServiceCall) throws -> URLRequest { |
|
|
|
|
fileprivate func _baseRequest(call: ServiceCall, getArguments: [String: String]? = nil) throws -> URLRequest { |
|
|
|
|
return try self._baseRequest( |
|
|
|
|
servicePath: call.path, method: call.method, requiresToken: call.requiresToken) |
|
|
|
|
servicePath: call.path, method: call.method, requiresToken: call.requiresToken, getArguments: getArguments) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Returns a base request for a path and method |
|
|
|
|
@ -242,13 +213,18 @@ public class Services { |
|
|
|
|
/// - identifier: an optional StoreIdentifier that allows to filter GET requests with the StoreIdentifier values |
|
|
|
|
fileprivate func _baseRequest( |
|
|
|
|
servicePath: String, method: HTTPMethod, requiresToken: Bool? = nil, |
|
|
|
|
identifier: String? = nil |
|
|
|
|
identifier: String? = nil, getArguments: [String: String]? = nil |
|
|
|
|
) throws -> URLRequest { |
|
|
|
|
var urlString = baseURL + servicePath |
|
|
|
|
var arguments: [String:String] = getArguments ?? [:] |
|
|
|
|
if let identifier { |
|
|
|
|
let component = "?store_id=\(identifier)" |
|
|
|
|
urlString.append(component) |
|
|
|
|
arguments["store_id"] = identifier |
|
|
|
|
// let component = "?store_id=\(identifier)" |
|
|
|
|
// urlString.append(component) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
urlString.append(arguments.toQueryString()) |
|
|
|
|
|
|
|
|
|
guard let url = URL(string: urlString) else { |
|
|
|
|
throw ServiceError.urlCreationError(url: urlString) |
|
|
|
|
} |
|
|
|
|
@ -338,7 +314,9 @@ public class Services { |
|
|
|
|
/// - since: The date from which updates are retrieved |
|
|
|
|
func synchronizeLastUpdates(since: Date?) async throws { |
|
|
|
|
let request = try self._getSyncLogRequest(since: since) |
|
|
|
|
try await self._runGetSyncLogRequest(request) |
|
|
|
|
try await self._runRequest(request) { data in |
|
|
|
|
StoreCenter.main.synchronizeContent(data) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Returns the URLRequest for an ApiCall |
|
|
|
|
@ -370,7 +348,7 @@ public class Services { |
|
|
|
|
/// Runs the a sync request and forwards the response to the StoreCenter for processing |
|
|
|
|
/// - Parameters: |
|
|
|
|
/// - request: The synchronization request |
|
|
|
|
fileprivate func _runGetSyncLogRequest(_ request: URLRequest) async throws { |
|
|
|
|
fileprivate func _runRequest(_ request: URLRequest, _ success: (Data) -> ()) async throws { |
|
|
|
|
let debugURL = request.url?.absoluteString ?? "" |
|
|
|
|
|
|
|
|
|
// print("Run \(request.httpMethod ?? "") \(debugURL)") |
|
|
|
|
@ -382,7 +360,7 @@ public class Services { |
|
|
|
|
print("\(debugURL) ended, status code = \(statusCode)") |
|
|
|
|
switch statusCode { |
|
|
|
|
case 200..<300: // success |
|
|
|
|
StoreCenter.main.synchronizeContent(task.0) |
|
|
|
|
success(task.0) |
|
|
|
|
default: // error |
|
|
|
|
Logger.log( |
|
|
|
|
"Failed Run \(request.httpMethod ?? "") \(request.url?.absoluteString ?? "")") |
|
|
|
|
@ -482,6 +460,13 @@ public class Services { |
|
|
|
|
throw ServiceError.urlCreationError(url: stringURL) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// MARK: - Others |
|
|
|
|
|
|
|
|
|
public func searchUsers(string: String) async throws -> [ShortUser] { |
|
|
|
|
let baseRequest = try self._baseRequest(call: userSearchCall, getArguments: ["search": string]) |
|
|
|
|
return try await self._runRequest(baseRequest) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// MARK: - Authentication |
|
|
|
|
|
|
|
|
|
@ -553,6 +538,13 @@ public class Services { |
|
|
|
|
// Logger.log("Send device token = \(tokenString)") |
|
|
|
|
let _: Empty = try await self._runRequest(serviceCall: postDeviceTokenCall, payload: token) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public func getUserDataAccess() async throws { |
|
|
|
|
let request = try self._baseRequest(call: getUserDataAccessCall) |
|
|
|
|
try await self._runRequest(request) { data in |
|
|
|
|
StoreCenter.main.userDataAccessRetrieved(data) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// A method that sends a request to change a user's password |
|
|
|
|
/// - Parameters: |
|
|
|
|
@ -652,11 +644,6 @@ public class Services { |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//struct GetSyncLog: Codable { |
|
|
|
|
// var updates: [String: Codable] |
|
|
|
|
// var deletions: [String: Codable] |
|
|
|
|
//} |
|
|
|
|
|
|
|
|
|
struct SyncPayload<T: Encodable>: Encodable { |
|
|
|
|
var operation: String |
|
|
|
|
var modelName: String |
|
|
|
|
@ -708,3 +695,8 @@ public protocol UserBase: Codable { |
|
|
|
|
public protocol UserPasswordBase: UserBase { |
|
|
|
|
var password: String { get } |
|
|
|
|
} |
|
|
|
|
public struct ShortUser: Codable, Identifiable, Equatable { |
|
|
|
|
public var id: String |
|
|
|
|
public var firstName: String |
|
|
|
|
public var lastName: String |
|
|
|
|
} |
|
|
|
|
|