From a8bd5388570882d983a8b3d13c3bdac2ed08e0c2 Mon Sep 17 00:00:00 2001 From: Laurent Date: Sat, 27 Jul 2024 12:16:49 +0200 Subject: [PATCH] Adds a service to delete accounts --- LeStorage/Services.swift | 110 ++++++++++++++++++++++------------- LeStorage/Utils/Errors.swift | 1 + 2 files changed, 70 insertions(+), 41 deletions(-) diff --git a/LeStorage/Services.swift b/LeStorage/Services.swift index 660a280..c4573cf 100644 --- a/LeStorage/Services.swift +++ b/LeStorage/Services.swift @@ -14,38 +14,51 @@ public enum HTTPMethod: String, CaseIterable, Codable { case delete = "DELETE" } -fileprivate enum ServiceConf: String { - case createAccount = "users/" - case requestToken = "token-auth/" - case logout = "api-token-logout/" - case getUser = "user-by-token/" - case changePassword = "change-password/" - case postDeviceToken = "device-token/" - - var method: HTTPMethod { - switch self { - case .createAccount, .requestToken, .logout, .postDeviceToken: - return .post - case .changePassword: - return .put - default: - return .get - } - } - - var requiresToken: Bool? { - switch self { - case .createAccount, .requestToken: - return false - case .getUser, .changePassword, .logout, .postDeviceToken: - return true -// default: -// return nil - } - } - +struct ServiceCall { + var path: String + var method: HTTPMethod + var requiresToken: Bool } +let createAccountCall: ServiceCall = ServiceCall(path: "users", method: .post, requiresToken: false) +let requestTokenCall: ServiceCall = ServiceCall(path: "token-auth/", method: .post, requiresToken: false) +let logoutCall: ServiceCall = ServiceCall(path: "api-token-logout/", method: .post, requiresToken: true) +let getUserCall: ServiceCall = ServiceCall(path: "user-by-token/", method: .get, requiresToken: true) +let changePasswordCall: ServiceCall = ServiceCall(path: "change-password/", method: .put, requiresToken: true) +let postDeviceTokenCall: ServiceCall = ServiceCall(path: "device-token/", method: .post, requiresToken: true) + +//fileprivate enum ServiceConf: String { +// case createAccount = "users/" +// case requestToken = "token-auth/" +// case logout = "api-token-logout/" +// case getUser = "user-by-token/" +// case changePassword = "change-password/" +// case postDeviceToken = "device-token/" +// +// var method: HTTPMethod { +// switch self { +// case .createAccount, .requestToken, .logout, .postDeviceToken: +// return .post +// case .changePassword: +// return .put +// default: +// return .get +// } +// } +// +// var requiresToken: Bool? { +// switch self { +// case .createAccount, .requestToken: +// return false +// case .getUser, .changePassword, .logout, .postDeviceToken: +// return true +//// default: +//// return nil +// } +// } +// +//} + /// A class used to send HTTP request to the django server public class Services { @@ -87,8 +100,8 @@ 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(serviceConf: ServiceConf, payload: T, apiCallId: String? = nil) async throws -> U { - var request = try self._baseRequest(conf: serviceConf) + fileprivate func _runRequest(serviceCall: ServiceCall, payload: T, apiCallId: String? = nil) async throws -> U { + var request = try self._baseRequest(call: serviceCall) request.httpBody = try jsonEncoder.encode(payload) return try await _runRequest(request, apiCallId: apiCallId) } @@ -189,8 +202,8 @@ public class Services { /// Returns the base URLRequest for a ServiceConf instance /// - Parameters: /// - conf: a ServiceConf instance - fileprivate func _baseRequest(conf: ServiceConf) throws -> URLRequest { - return try self._baseRequest(servicePath: conf.rawValue, method: conf.method, requiresToken: conf.requiresToken) + 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 @@ -292,7 +305,7 @@ public class Services { /// - Parameters: /// - user: A user instance to send to the server public func createAccount(user: U) async throws -> V { - return try await _runRequest(serviceConf: .createAccount, payload: user) + return try await _runRequest(serviceCall: createAccountCall, payload: user) } /// Requests a token for a username and password @@ -300,7 +313,7 @@ public class Services { /// - username: the account's username /// - password: the account's password public func requestToken(username: String, password: String) async throws -> String { - var postRequest = try self._baseRequest(conf: .requestToken) + 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) @@ -328,7 +341,7 @@ public class Services { /// - password: the account's password public func login(username: String, password: String) async throws -> U { _ = try await requestToken(username: username, password: password) - let postRequest = try self._baseRequest(conf: .getUser) + let postRequest = try self._baseRequest(call: getUserCall) let user: U = try await self._runRequest(postRequest) // StoreCenter.main.setUserUUID(uuidString: user.id) // StoreCenter.main.setUserName(user.username) @@ -342,7 +355,7 @@ public class Services { /// - password: the account's password public func logout() async throws { let deviceId: String = StoreCenter.main.deviceId() - let _: Empty = try await self._runRequest(serviceConf: .logout, payload: Logout(deviceId: deviceId)) + let _: Empty = try await self._runRequest(serviceCall: logoutCall, payload: Logout(deviceId: deviceId)) } /// A login method that actually requests a token from the server, and stores the appropriate data for later usage @@ -352,8 +365,8 @@ public class Services { public func postDeviceToken(deviceToken: Data) async throws { let tokenString = deviceToken.map { String(format: "%02x", $0) }.joined() let token = DeviceToken(value: tokenString) - Logger.log("Send device token = \(tokenString)") - let _: DeviceToken = try await self._runRequest(serviceConf: .postDeviceToken, payload: token) +// Logger.log("Send device token = \(tokenString)") + let _: DeviceToken = try await self._runRequest(serviceCall: postDeviceTokenCall, payload: token) } /// A method that sends a request to change a user's password @@ -374,7 +387,7 @@ public class Services { } let params = ChangePasswordParams(old_password: oldPassword, new_password1: password1, new_password2: password2) - let response: Token = try await self._runRequest(serviceConf: .changePassword, payload: params) + let response: Token = try await self._runRequest(serviceCall: changePasswordCall, payload: params) self._storeToken(username: username, token: response.token) } @@ -389,6 +402,21 @@ public class Services { Logger.log("response = \(response)") } + /// 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 deleteAccount() async throws { + guard let userId = StoreCenter.main.userId else { + throw ServiceError.missingUserId + } + let path = "users/\(userId)/" + let deleteAccount = ServiceCall(path: path, method: .delete, requiresToken: true) + + let request = try self._baseRequest(call: deleteAccount) + let _: Empty = try await self._runRequest(request) + } + /// Deletes the locally stored token func deleteToken() throws { try self.keychainStore.deleteValue() diff --git a/LeStorage/Utils/Errors.swift b/LeStorage/Utils/Errors.swift index ce91a31..2eb0d9f 100644 --- a/LeStorage/Utils/Errors.swift +++ b/LeStorage/Utils/Errors.swift @@ -24,6 +24,7 @@ public enum ServiceError: Error { case urlCreationError(url: String) case cantConvertToUUID(id: String) case missingUserName + case missingUserId case responseError(response: String) }