diff --git a/LeStorage/Codables/ApiCall.swift b/LeStorage/Codables/ApiCall.swift index 10b3eab..216b863 100644 --- a/LeStorage/Codables/ApiCall.swift +++ b/LeStorage/Codables/ApiCall.swift @@ -15,7 +15,7 @@ protocol SomeCall: Storable { class ApiCall: ModelObject, Storable, SomeCall { static func resourceName() -> String { return "apicalls_" + T.resourceName() } - static func requestsRequiresToken() -> Bool { return false } + static func tokenExemptedMethods() -> [HTTPMethod] { return [] } var id: String = Store.randomId() diff --git a/LeStorage/Services.swift b/LeStorage/Services.swift index c6904e7..ed48709 100644 --- a/LeStorage/Services.swift +++ b/LeStorage/Services.swift @@ -7,7 +7,7 @@ import Foundation -enum Method: String { +public enum HTTPMethod: String, CaseIterable { case get = "GET" case post = "POST" case put = "PUT" @@ -27,7 +27,7 @@ fileprivate enum ServiceConf: String { case getUser = "plus/user-by-token/" case changePassword = "plus/change-password/" - var method: Method { + var method: HTTPMethod { switch self { case .createAccount, .requestToken: return .post @@ -86,7 +86,7 @@ public class Services { return try await _runRequest(request, apiCallId: apiCallId) } - fileprivate func _runRequest(servicePath: String, method: Method, payload: T, apiCallId: String? = nil) async throws -> U { + fileprivate func _runRequest(servicePath: String, method: HTTPMethod, payload: T, apiCallId: String? = nil) async throws -> U { var request = try self._baseRequest(servicePath: servicePath, method: method) request.httpBody = try jsonEncoder.encode(payload) return try await _runRequest(request, apiCallId: apiCallId) @@ -124,24 +124,41 @@ public class Services { } return try jsonDecoder.decode(T.self, from: task.0) } - - fileprivate func _postRequest(servicePath: String, requiresToken: Bool? = nil) throws -> URLRequest { - return try self._baseRequest(servicePath: servicePath, method: .post, requiresToken: requiresToken) + + fileprivate func _isTokenRequired(type: T.Type, method: HTTPMethod) -> Bool { + let methods = T.tokenExemptedMethods() + if methods.contains(method) { + return false + } else { + return true + } + } + + fileprivate func _getRequest(type: T.Type) throws -> URLRequest { + let requiresToken = self._isTokenRequired(type: T.self, method: .get) + return try self._baseRequest(servicePath: T.path(), method: .get, requiresToken: requiresToken) + } + + fileprivate func _postRequest(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) } - fileprivate func _putRequest(servicePath: String, requiresToken: Bool? = nil) throws -> URLRequest { - return try self._baseRequest(servicePath: servicePath, method: .put, requiresToken: requiresToken) + fileprivate func _putRequest(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) } - fileprivate func _getRequest(servicePath: String, requiresToken: Bool? = nil) throws -> URLRequest { - return try self._baseRequest(servicePath: servicePath, method: .get, requiresToken: requiresToken) + fileprivate func _deleteRequest(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) } fileprivate func _baseRequest(conf: ServiceConf) throws -> URLRequest { return try self._baseRequest(servicePath: conf.rawValue, method: conf.method, requiresToken: conf.requiresToken) } - fileprivate func _baseRequest(servicePath: String, method: Method, requiresToken: Bool? = nil) throws -> URLRequest { + fileprivate func _baseRequest(servicePath: String, method: HTTPMethod, requiresToken: Bool? = nil) throws -> URLRequest { let urlString = baseURL + servicePath guard let url = URL(string: urlString) else { throw ServiceError.urlCreationError(url: urlString) @@ -160,18 +177,18 @@ public class Services { // MARK: - Services public func get() async throws -> [T] { - let getRequest = try _getRequest(servicePath: T.resourceName() + "/", requiresToken: T.requestsRequiresToken()) + let getRequest = try _getRequest(type: T.self) return try await self._runRequest(getRequest) } public func post(_ instance: T) async throws -> T { - var postRequest = try self._postRequest(servicePath: T.resourceName() + "/", requiresToken: T.requestsRequiresToken()) + var postRequest = try self._postRequest(type: T.self) postRequest.httpBody = try jsonEncoder.encode(instance) return try await self._runRequest(postRequest) } public func put(_ instance: T) async throws -> T { - var postRequest = try self._putRequest(servicePath: T.resourceName() + "/" + instance.id + "/", requiresToken: T.requestsRequiresToken()) + var postRequest = try self._putRequest(type: T.self, id: instance.stringId) postRequest.httpBody = try jsonEncoder.encode(instance) return try await self._runRequest(postRequest) } @@ -190,14 +207,16 @@ public class Services { request.httpBody = apiCall.body.data(using: .utf8) request.setValue("application/json", forHTTPHeaderField: "Content-Type") - do { - let token = try self.keychainStore.getToken() -// Logger.log("current token = \(token)") - request.setValue("Token \(token)", forHTTPHeaderField: "Authorization") - } catch { - Logger.log("missing token") + + if let method = HTTPMethod(rawValue: apiCall.method), self._isTokenRequired(type: T.self, method: method) { + do { + let token = try self.keychainStore.getToken() + request.setValue("Token \(token)", forHTTPHeaderField: "Authorization") + } catch { + Logger.log("missing token") + } } - + return request } diff --git a/LeStorage/Storable.swift b/LeStorage/Storable.swift index cb0ffe0..9e063ec 100644 --- a/LeStorage/Storable.swift +++ b/LeStorage/Storable.swift @@ -9,7 +9,7 @@ import Foundation public protocol Storable: Codable, Identifiable where ID : StringProtocol { static func resourceName() -> String - static func requestsRequiresToken() -> Bool + static func tokenExemptedMethods() -> [HTTPMethod] func deleteDependencies() throws } @@ -26,4 +26,13 @@ extension Storable { var stringId: String { return String(self.id) } + static func path(id: String? = nil) -> String { + var path = self.resourceName() + "/" + if let id { + path.append(id) + path.append("/") + } + return path + } + } diff --git a/LeStorage/StoredCollection.swift b/LeStorage/StoredCollection.swift index 185efc9..0eb65db 100644 --- a/LeStorage/StoredCollection.swift +++ b/LeStorage/StoredCollection.swift @@ -346,17 +346,17 @@ public class StoredCollection: RandomAccessCollection, SomeCollecti // MARK: - Synchronization - fileprivate func _callForInstance(_ instance: T, method: Method) throws -> ApiCall? { + fileprivate func _callForInstance(_ instance: T, method: HTTPMethod) throws -> ApiCall? { guard let apiCallCollection = self.apiCallsCollection else { throw StoredCollectionError.missingApiCallCollection } if let existingCall = apiCallCollection.first(where: { $0.dataId == instance.id }) { switch existingCall.method { - case Method.post.rawValue, Method.put.rawValue: + case HTTPMethod.post.rawValue, HTTPMethod.put.rawValue: existingCall.body = try instance.jsonString() return existingCall - case Method.delete.rawValue: + case HTTPMethod.delete.rawValue: try self.deleteApiCallById(existingCall.id) return nil default: @@ -368,7 +368,7 @@ public class StoredCollection: RandomAccessCollection, SomeCollecti } } - fileprivate func _createCall(_ instance: T, method: Method) throws -> ApiCall { + fileprivate func _createCall(_ instance: T, method: HTTPMethod) throws -> ApiCall { let baseURL = try _store.service().baseURL let jsonString = try instance.jsonString() var url = baseURL + T.resourceName() + "/" @@ -390,7 +390,7 @@ public class StoredCollection: RandomAccessCollection, SomeCollecti return } - if let apiCall = try self._callForInstance(instance, method: Method.post) { + if let apiCall = try self._callForInstance(instance, method: HTTPMethod.post) { try self._prepareCall(apiCall: apiCall) Task { @@ -412,7 +412,7 @@ public class StoredCollection: RandomAccessCollection, SomeCollecti return } - if let apiCall = try self._callForInstance(instance, method: Method.put) { + if let apiCall = try self._callForInstance(instance, method: HTTPMethod.put) { try self._prepareCall(apiCall: apiCall) Task { do { @@ -432,7 +432,7 @@ public class StoredCollection: RandomAccessCollection, SomeCollecti return } - if let apiCall = try self._callForInstance(instance, method: Method.delete) { + if let apiCall = try self._callForInstance(instance, method: HTTPMethod.delete) { try self._prepareCall(apiCall: apiCall) Task { do {