Adds token exempted method for Storables

multistore
Laurent 2 years ago
parent 6a208ae2e4
commit bdcd3c7e08
  1. 2
      LeStorage/Codables/ApiCall.swift
  2. 61
      LeStorage/Services.swift
  3. 11
      LeStorage/Storable.swift
  4. 14
      LeStorage/StoredCollection.swift

@ -15,7 +15,7 @@ protocol SomeCall: Storable {
class ApiCall<T: Storable>: ModelObject, Storable, SomeCall { class ApiCall<T: Storable>: ModelObject, Storable, SomeCall {
static func resourceName() -> String { return "apicalls_" + T.resourceName() } static func resourceName() -> String { return "apicalls_" + T.resourceName() }
static func requestsRequiresToken() -> Bool { return false } static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
var id: String = Store.randomId() var id: String = Store.randomId()

@ -7,7 +7,7 @@
import Foundation import Foundation
enum Method: String { public enum HTTPMethod: String, CaseIterable {
case get = "GET" case get = "GET"
case post = "POST" case post = "POST"
case put = "PUT" case put = "PUT"
@ -27,7 +27,7 @@ fileprivate enum ServiceConf: String {
case getUser = "plus/user-by-token/" case getUser = "plus/user-by-token/"
case changePassword = "plus/change-password/" case changePassword = "plus/change-password/"
var method: Method { var method: HTTPMethod {
switch self { switch self {
case .createAccount, .requestToken: case .createAccount, .requestToken:
return .post return .post
@ -86,7 +86,7 @@ public class Services {
return try await _runRequest(request, apiCallId: apiCallId) return try await _runRequest(request, apiCallId: apiCallId)
} }
fileprivate func _runRequest<T: Encodable, U: Decodable>(servicePath: String, method: Method, payload: T, apiCallId: String? = nil) async throws -> U { fileprivate func _runRequest<T: Encodable, U: Decodable>(servicePath: String, method: HTTPMethod, payload: T, apiCallId: String? = nil) async throws -> U {
var request = try self._baseRequest(servicePath: servicePath, method: method) var request = try self._baseRequest(servicePath: servicePath, method: method)
request.httpBody = try jsonEncoder.encode(payload) request.httpBody = try jsonEncoder.encode(payload)
return try await _runRequest(request, apiCallId: apiCallId) return try await _runRequest(request, apiCallId: apiCallId)
@ -124,24 +124,41 @@ public class Services {
} }
return try jsonDecoder.decode(T.self, from: task.0) return try jsonDecoder.decode(T.self, from: task.0)
} }
fileprivate func _postRequest(servicePath: String, requiresToken: Bool? = nil) throws -> URLRequest { fileprivate func _isTokenRequired<T : Storable>(type: T.Type, method: HTTPMethod) -> Bool {
return try self._baseRequest(servicePath: servicePath, method: .post, requiresToken: requiresToken) let methods = T.tokenExemptedMethods()
if methods.contains(method) {
return false
} else {
return true
}
}
fileprivate func _getRequest<T: Storable>(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<T: Storable>(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 { fileprivate func _putRequest<T: Storable>(type: T.Type, id: String) throws -> URLRequest {
return try self._baseRequest(servicePath: servicePath, method: .put, requiresToken: requiresToken) 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 { fileprivate func _deleteRequest<T: Storable>(type: T.Type, id: String) throws -> URLRequest {
return try self._baseRequest(servicePath: servicePath, method: .get, requiresToken: requiresToken) 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 { fileprivate func _baseRequest(conf: ServiceConf) throws -> URLRequest {
return try self._baseRequest(servicePath: conf.rawValue, method: conf.method, requiresToken: conf.requiresToken) 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 let urlString = baseURL + servicePath
guard let url = URL(string: urlString) else { guard let url = URL(string: urlString) else {
throw ServiceError.urlCreationError(url: urlString) throw ServiceError.urlCreationError(url: urlString)
@ -160,18 +177,18 @@ public class Services {
// MARK: - Services // MARK: - Services
public func get<T: Storable>() async throws -> [T] { public func get<T: Storable>() 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) return try await self._runRequest(getRequest)
} }
public func post<T: Storable>(_ instance: T) async throws -> T { public func post<T: Storable>(_ 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) postRequest.httpBody = try jsonEncoder.encode(instance)
return try await self._runRequest(postRequest) return try await self._runRequest(postRequest)
} }
public func put<T: Storable>(_ instance: T) async throws -> T { public func put<T: Storable>(_ 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) postRequest.httpBody = try jsonEncoder.encode(instance)
return try await self._runRequest(postRequest) return try await self._runRequest(postRequest)
} }
@ -190,14 +207,16 @@ public class Services {
request.httpBody = apiCall.body.data(using: .utf8) request.httpBody = apiCall.body.data(using: .utf8)
request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue("application/json", forHTTPHeaderField: "Content-Type")
do {
let token = try self.keychainStore.getToken() if let method = HTTPMethod(rawValue: apiCall.method), self._isTokenRequired(type: T.self, method: method) {
// Logger.log("current token = \(token)") do {
request.setValue("Token \(token)", forHTTPHeaderField: "Authorization") let token = try self.keychainStore.getToken()
} catch { request.setValue("Token \(token)", forHTTPHeaderField: "Authorization")
Logger.log("missing token") } catch {
Logger.log("missing token")
}
} }
return request return request
} }

@ -9,7 +9,7 @@ import Foundation
public protocol Storable: Codable, Identifiable where ID : StringProtocol { public protocol Storable: Codable, Identifiable where ID : StringProtocol {
static func resourceName() -> String static func resourceName() -> String
static func requestsRequiresToken() -> Bool static func tokenExemptedMethods() -> [HTTPMethod]
func deleteDependencies() throws func deleteDependencies() throws
} }
@ -26,4 +26,13 @@ extension Storable {
var stringId: String { return String(self.id) } 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
}
} }

@ -346,17 +346,17 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
// MARK: - Synchronization // MARK: - Synchronization
fileprivate func _callForInstance(_ instance: T, method: Method) throws -> ApiCall<T>? { fileprivate func _callForInstance(_ instance: T, method: HTTPMethod) throws -> ApiCall<T>? {
guard let apiCallCollection = self.apiCallsCollection else { guard let apiCallCollection = self.apiCallsCollection else {
throw StoredCollectionError.missingApiCallCollection throw StoredCollectionError.missingApiCallCollection
} }
if let existingCall = apiCallCollection.first(where: { $0.dataId == instance.id }) { if let existingCall = apiCallCollection.first(where: { $0.dataId == instance.id }) {
switch existingCall.method { switch existingCall.method {
case Method.post.rawValue, Method.put.rawValue: case HTTPMethod.post.rawValue, HTTPMethod.put.rawValue:
existingCall.body = try instance.jsonString() existingCall.body = try instance.jsonString()
return existingCall return existingCall
case Method.delete.rawValue: case HTTPMethod.delete.rawValue:
try self.deleteApiCallById(existingCall.id) try self.deleteApiCallById(existingCall.id)
return nil return nil
default: default:
@ -368,7 +368,7 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
} }
} }
fileprivate func _createCall(_ instance: T, method: Method) throws -> ApiCall<T> { fileprivate func _createCall(_ instance: T, method: HTTPMethod) throws -> ApiCall<T> {
let baseURL = try _store.service().baseURL let baseURL = try _store.service().baseURL
let jsonString = try instance.jsonString() let jsonString = try instance.jsonString()
var url = baseURL + T.resourceName() + "/" var url = baseURL + T.resourceName() + "/"
@ -390,7 +390,7 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
return 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) try self._prepareCall(apiCall: apiCall)
Task { Task {
@ -412,7 +412,7 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
return 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) try self._prepareCall(apiCall: apiCall)
Task { Task {
do { do {
@ -432,7 +432,7 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
return 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) try self._prepareCall(apiCall: apiCall)
Task { Task {
do { do {

Loading…
Cancel
Save