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 {
static func resourceName() -> String { return "apicalls_" + T.resourceName() }
static func requestsRequiresToken() -> Bool { return false }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
var id: String = Store.randomId()

@ -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<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)
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<T : Storable>(type: T.Type, method: HTTPMethod) -> Bool {
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 {
return try self._baseRequest(servicePath: servicePath, method: .put, requiresToken: requiresToken)
fileprivate func _putRequest<T: Storable>(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<T: Storable>(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<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)
}
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)
return try await self._runRequest(postRequest)
}
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)
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
}

@ -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
}
}

@ -346,17 +346,17 @@ public class StoredCollection<T: Storable>: RandomAccessCollection, SomeCollecti
// 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 {
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<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 jsonString = try instance.jsonString()
var url = baseURL + T.resourceName() + "/"
@ -390,7 +390,7 @@ public class StoredCollection<T: Storable>: 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<T: Storable>: 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<T: Storable>: 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 {

Loading…
Cancel
Save