|
|
|
|
@ -43,13 +43,8 @@ public class Services { |
|
|
|
|
/// The base API URL to send requests |
|
|
|
|
fileprivate(set) var baseURL: String |
|
|
|
|
|
|
|
|
|
/// A KeychainStore object used to store the user's token |
|
|
|
|
let keychainStore: KeychainStore |
|
|
|
|
|
|
|
|
|
public init(url: String) { |
|
|
|
|
self.baseURL = url |
|
|
|
|
self.keychainStore = KeychainStore(serverId: url) |
|
|
|
|
Logger.log("create keystore with id: \(url)") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static let storeIdURLParameter = "store_id" |
|
|
|
|
@ -228,14 +223,14 @@ public class Services { |
|
|
|
|
// 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 { |
|
|
|
|
return try self._baseRequest(servicePath: call.path, method: call.method, requiresToken: call.requiresToken) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Returns the base URLRequest for a ServiceConf instance |
|
|
|
|
/// - Parameters: |
|
|
|
|
/// - conf: a ServiceConf instance |
|
|
|
|
fileprivate func _baseRequest(call: ServiceCall) throws -> URLRequest { |
|
|
|
|
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 |
|
|
|
|
@ -262,7 +257,7 @@ public class Services { |
|
|
|
|
request.setValue("application/json", forHTTPHeaderField: "Content-Type") |
|
|
|
|
request.addAppVersion() |
|
|
|
|
if !(requiresToken == false) { |
|
|
|
|
let token = try self.keychainStore.getValue() |
|
|
|
|
let token = try StoreCenter.main.token() |
|
|
|
|
request.addValue("Token \(token)", forHTTPHeaderField: "Authorization") |
|
|
|
|
} |
|
|
|
|
return request |
|
|
|
|
@ -270,28 +265,6 @@ public class Services { |
|
|
|
|
|
|
|
|
|
// MARK: - Synchronization |
|
|
|
|
|
|
|
|
|
/// Returns a base request for a path and method |
|
|
|
|
/// - Parameters: |
|
|
|
|
/// - method: the HTTP method to execute |
|
|
|
|
/// - payload: the content to put in the httpBody |
|
|
|
|
// 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 JSON.encoder.encode(payload) |
|
|
|
|
// |
|
|
|
|
// let token = try self.keychainStore.getValue() |
|
|
|
|
// request.addValue("Token \(token)", forHTTPHeaderField: "Authorization") |
|
|
|
|
// |
|
|
|
|
// return request |
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
/// Runs a request using a traditional URLRequest |
|
|
|
|
/// - Parameters: |
|
|
|
|
/// - request: the URLRequest to run |
|
|
|
|
@ -379,7 +352,7 @@ public class Services { |
|
|
|
|
request.setValue("application/json", forHTTPHeaderField: "Content-Type") |
|
|
|
|
|
|
|
|
|
if self._isTokenRequired(type: T.self, method: apiCall.method), StoreCenter.main.isAuthenticated { |
|
|
|
|
let token = try self.keychainStore.getValue() |
|
|
|
|
let token = try StoreCenter.main.token() |
|
|
|
|
request.addValue("Token \(token)", forHTTPHeaderField: "Authorization") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -421,7 +394,7 @@ public class Services { |
|
|
|
|
var request = URLRequest(url: url) |
|
|
|
|
request.httpMethod = HTTPMethod.post.rawValue |
|
|
|
|
request.setValue("application/json", forHTTPHeaderField: "Content-Type") |
|
|
|
|
let token = try self.keychainStore.getValue() |
|
|
|
|
let token = try StoreCenter.main.token() |
|
|
|
|
request.addValue("Token \(token)", forHTTPHeaderField: "Authorization") |
|
|
|
|
|
|
|
|
|
let modelName = String(describing: T.self) |
|
|
|
|
@ -471,7 +444,7 @@ public class Services { |
|
|
|
|
request.httpMethod = HTTPMethod.get.rawValue |
|
|
|
|
request.setValue("application/json", forHTTPHeaderField: "Content-Type") |
|
|
|
|
|
|
|
|
|
let token = try self.keychainStore.getValue() |
|
|
|
|
let token = try StoreCenter.main.token() |
|
|
|
|
request.addValue("Token \(token)", forHTTPHeaderField: "Authorization") |
|
|
|
|
|
|
|
|
|
return request |
|
|
|
|
@ -564,7 +537,7 @@ public class Services { |
|
|
|
|
request.addAppVersion() |
|
|
|
|
if self._isTokenRequired(type: T.self, method: apiCall.method) { |
|
|
|
|
do { |
|
|
|
|
let token = try self.keychainStore.getValue() |
|
|
|
|
let token = try StoreCenter.main.token() |
|
|
|
|
request.setValue("Token \(token)", forHTTPHeaderField: "Authorization") |
|
|
|
|
} catch { |
|
|
|
|
Logger.log("missing token") |
|
|
|
|
@ -613,28 +586,16 @@ public class Services { |
|
|
|
|
let credentials = Credentials(username: username, password: password, deviceId: deviceId) |
|
|
|
|
postRequest.httpBody = try JSON.encoder.encode(credentials) |
|
|
|
|
let response: AuthResponse = try await self._runRequest(postRequest) |
|
|
|
|
self._storeToken(username: username, token: response.token) |
|
|
|
|
try StoreCenter.main.storeToken(username: username, token: response.token) |
|
|
|
|
return response.token |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Stores a token for a corresponding username |
|
|
|
|
/// - Parameters: |
|
|
|
|
/// - username: the key used to store the token |
|
|
|
|
/// - token: the token to store |
|
|
|
|
fileprivate func _storeToken(username: String, token: String) { |
|
|
|
|
do { |
|
|
|
|
try self.keychainStore.deleteValue() |
|
|
|
|
try self.keychainStore.add(username: username, value: token) |
|
|
|
|
} catch { |
|
|
|
|
Logger.error(error) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// A login method that actually requests a token from the server, and stores the appropriate data for later usage |
|
|
|
|
/// - Parameters: |
|
|
|
|
/// - username: the account's username |
|
|
|
|
/// - password: the account's password |
|
|
|
|
public func login<U: UserBase>(username: String, password: String) async throws -> U { |
|
|
|
|
|
|
|
|
|
_ = try await requestToken(username: username, password: password) |
|
|
|
|
let postRequest = try self._baseRequest(call: getUserCall) |
|
|
|
|
let loggingDate = Date() // ideally we want the date of the latest retrieved object when loading collection objects |
|
|
|
|
@ -681,7 +642,7 @@ public class Services { |
|
|
|
|
async throws |
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
guard let username = StoreCenter.main.userName() else { |
|
|
|
|
guard let username = StoreCenter.main.userName else { |
|
|
|
|
throw ServiceError.missingUserName |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -696,7 +657,7 @@ public class Services { |
|
|
|
|
let response: Token = try await self._runRequest( |
|
|
|
|
serviceCall: changePasswordCall, payload: params) |
|
|
|
|
|
|
|
|
|
self._storeToken(username: username, token: response.token) |
|
|
|
|
try StoreCenter.main.storeToken(username: username, token: response.token) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// The method send a request to reset the user's password |
|
|
|
|
@ -724,21 +685,6 @@ public class Services { |
|
|
|
|
let _: Empty = try await self._runRequest(request) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Deletes the locally stored token |
|
|
|
|
func deleteToken() throws { |
|
|
|
|
try self.keychainStore.deleteValue() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Returns whether the Service has an associated token |
|
|
|
|
public func hasToken() -> Bool { |
|
|
|
|
do { |
|
|
|
|
_ = try self.keychainStore.getValue() |
|
|
|
|
return true |
|
|
|
|
} catch { |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Parse a json data and tries to extract its error message |
|
|
|
|
/// - Parameters: |
|
|
|
|
/// - data: some JSON data |
|
|
|
|
@ -763,10 +709,6 @@ public class Services { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func migrateToken(_ services: Services, userName: String) throws { |
|
|
|
|
try self._storeToken(username: userName, token: services.keychainStore.getValue()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// MARK: - Convenience method for tests |
|
|
|
|
|
|
|
|
|
/// Executes a POST request |
|
|
|
|
|