Bug fixes

This commit is contained in:
VincentMeilinger
2024-02-03 10:07:15 +01:00
parent 116ed0d077
commit 4c2b459683
15 changed files with 230 additions and 342 deletions

View File

@@ -1,105 +0,0 @@
//
// ApiRequest.swift
// Nextcloud Cookbook iOS Client
//
// Created by Vincent Meilinger on 16.11.23.
//
import Foundation
import OSLog
struct ApiRequest {
let path: String
let method: RequestMethod
let authString: String?
let headerFields: [HeaderField]
let body: Data?
/// The path to the Cookbook application on the nextcloud server.
let cookbookPath = "/index.php/apps/cookbook"
init(
path: String,
method: RequestMethod,
authString: String? = nil,
headerFields: [HeaderField] = [],
body: Data? = nil
) {
self.method = method
self.path = path
self.headerFields = headerFields
self.authString = authString
self.body = body
}
func send() async -> (Data?, NetworkError?) {
Logger.network.debug("\(method.rawValue) \(path) sending ...")
// Prepare URL
let urlString = UserSettings.shared.serverProtocol + UserSettings.shared.serverAddress + cookbookPath + path
print("Full path: \(urlString)")
//Logger.network.debug("Full path: \(urlString)")
guard let urlStringSanitized = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return (nil, .unknownError) }
guard let url = URL(string: urlStringSanitized) else { return (nil, .unknownError) }
// Create URL request
var request = URLRequest(url: url)
request.httpMethod = method.rawValue
// Set authentication string, if needed
if let authString = authString {
request.setValue(
"Basic \(authString)",
forHTTPHeaderField: "Authorization"
)
}
// Set other header fields
for headerField in headerFields {
request.setValue(
headerField.getValue(),
forHTTPHeaderField: headerField.getField()
)
}
// Set http body
if let body = body {
request.httpBody = body
}
// Wait for and return data and (decoded) response
var data: Data? = nil
var response: URLResponse? = nil
do {
(data, response) = try await URLSession.shared.data(for: request)
Logger.network.debug("\(method.rawValue) \(path) SUCCESS!")
if let error = decodeURLResponse(response: response as? HTTPURLResponse) {
print("\(method.rawValue) \(path) FAILURE: \(error.localizedDescription)")
return (nil, error)
}
if let data = data {
print(data, String(data: data, encoding: .utf8))
}
return (data!, nil)
} catch {
let error = decodeURLResponse(response: response as? HTTPURLResponse)
Logger.network.debug("\(method.rawValue) \(path) FAILURE: \(error.debugDescription)")
return (nil, error)
}
}
private func decodeURLResponse(response: HTTPURLResponse?) -> NetworkError? {
guard let response = response else {
return NetworkError.unknownError
}
print("Status code: ", response.statusCode)
switch response.statusCode {
case 200...299: return (nil)
case 300...399: return (NetworkError.redirectionError)
case 400...499: return (NetworkError.clientError)
case 500...599: return (NetworkError.serverError)
case 600: return (NetworkError.invalidRequest)
default: return (NetworkError.unknownError)
}
}
}

View File

@@ -10,7 +10,26 @@ import OSLog
import UIKit
/// The Cookbook API class used for requests to the Nextcloud Cookbook service.
let cookbookApi: CookbookApi.Type = getApi()
func getApi() -> CookbookApi.Type {
switch UserSettings.shared.cookbookApiVersion {
case .v1:
return CookbookApiV1.self
}
}
/// The Cookbook API version.
enum CookbookApiVersion: String {
case v1 = "v1"
}
/// A protocol defining common API endpoints that are likely to remain the same over future Cookbook API versions.
protocol CookbookApi {
static var basePath: String { get }
/// Not implemented yet.
static func importRecipe(
auth: String,

View File

@@ -10,9 +10,11 @@ import UIKit
class CookbookApiV1: CookbookApi {
static let basePath: String = "/index.php/apps/cookbook/api/v1"
static func importRecipe(auth: String, data: Data) async -> (RecipeDetail?, NetworkError?) {
let request = ApiRequest(
path: "/api/v1/import",
path: basePath + "/import",
method: .POST,
authString: auth,
headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .JSON), HeaderField.contentType(value: .JSON)]
@@ -26,7 +28,7 @@ class CookbookApiV1: CookbookApi {
static func getImage(auth: String, id: Int, size: RecipeImage.RecipeImageSize) async -> (UIImage?, NetworkError?) {
let imageSize = (size == .FULL ? "full" : "thumb")
let request = ApiRequest(
path: "/api/v1/recipes/\(id)/image?size=\(imageSize)",
path: basePath + "/recipes/\(id)/image?size=\(imageSize)",
method: .GET,
authString: auth,
headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .IMAGE)]
@@ -39,7 +41,7 @@ class CookbookApiV1: CookbookApi {
static func getRecipes(auth: String) async -> ([Recipe]?, NetworkError?) {
let request = ApiRequest(
path: "/api/v1/recipes",
path: basePath + "/recipes",
method: .GET,
authString: auth,
headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .JSON)]
@@ -56,7 +58,7 @@ class CookbookApiV1: CookbookApi {
}
let request = ApiRequest(
path: "/api/v1/recipes",
path: basePath + "/recipes",
method: .POST,
authString: auth,
headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .JSON), HeaderField.contentType(value: .JSON)],
@@ -80,7 +82,7 @@ class CookbookApiV1: CookbookApi {
static func getRecipe(auth: String, id: Int) async -> (RecipeDetail?, NetworkError?) {
let request = ApiRequest(
path: "/api/v1/recipes/\(id)",
path: basePath + "/recipes/\(id)",
method: .GET,
authString: auth,
headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .JSON)]
@@ -96,7 +98,7 @@ class CookbookApiV1: CookbookApi {
return .dataError
}
let request = ApiRequest(
path: "/api/v1/recipes/\(recipe.id)",
path: basePath + "/recipes/\(recipe.id)",
method: .PUT,
authString: auth,
headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .JSON), HeaderField.contentType(value: .JSON)],
@@ -120,7 +122,7 @@ class CookbookApiV1: CookbookApi {
static func deleteRecipe(auth: String, id: Int) async -> (NetworkError?) {
let request = ApiRequest(
path: "/api/v1/recipes/\(id)",
path: basePath + "/recipes/\(id)",
method: .DELETE,
authString: auth,
headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .JSON)]
@@ -133,7 +135,7 @@ class CookbookApiV1: CookbookApi {
static func getCategories(auth: String) async -> ([Category]?, NetworkError?) {
let request = ApiRequest(
path: "/api/v1/categories",
path: basePath + "/categories",
method: .GET,
authString: auth,
headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .JSON)]
@@ -146,7 +148,7 @@ class CookbookApiV1: CookbookApi {
static func getCategory(auth: String, named categoryName: String) async -> ([Recipe]?, NetworkError?) {
let request = ApiRequest(
path: "/api/v1/category/\(categoryName)",
path: basePath + "/category/\(categoryName)",
method: .GET,
authString: auth,
headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .JSON)]
@@ -159,7 +161,7 @@ class CookbookApiV1: CookbookApi {
static func renameCategory(auth: String, named categoryName: String, newName: String) async -> (NetworkError?) {
let request = ApiRequest(
path: "/api/v1/category/\(categoryName)",
path: basePath + "/category/\(categoryName)",
method: .PUT,
authString: auth,
headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .JSON)]
@@ -172,7 +174,7 @@ class CookbookApiV1: CookbookApi {
static func getTags(auth: String) async -> ([RecipeKeyword]?, NetworkError?) {
let request = ApiRequest(
path: "/api/v1/keywords",
path: basePath + "/keywords",
method: .GET,
authString: auth,
headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .JSON)]
@@ -185,7 +187,7 @@ class CookbookApiV1: CookbookApi {
static func getRecipesTagged(auth: String, keyword: String) async -> ([Recipe]?, NetworkError?) {
let request = ApiRequest(
path: "/api/v1/tags/\(keyword)",
path: basePath + "/tags/\(keyword)",
method: .GET,
authString: auth,
headerFields: [HeaderField.ocsRequest(value: true), HeaderField.accept(value: .JSON)]