Skip to content

Commit

Permalink
feat: Allow returning complete api response
Browse files Browse the repository at this point in the history
  • Loading branch information
PhilippeWeidmann committed Feb 15, 2024
1 parent a1689a3 commit ab305a8
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 11 deletions.
47 changes: 36 additions & 11 deletions Sources/InfomaniakCore/Networking/ApiFetcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ public protocol RefreshTokenDelegate: AnyObject {

@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
open class ApiFetcher {
enum ErrorDomain: Error {
case noServerResponse
}

public typealias RequestModifier = (inout URLRequest) throws -> Void

/// All status except 401 are handled by our code, 401 status is handled by Alamofire's Authenticator code
Expand Down Expand Up @@ -138,22 +142,43 @@ open class ApiFetcher {

open func perform<T: Decodable>(request: DataRequest,
decoder: JSONDecoder = ApiFetcher.decoder) async throws -> (data: T, responseAt: Int?) {
let validServerResponse: ValidServerResponse<T> = try await perform(request: request, decoder: decoder)
return (validServerResponse.validApiResponse.data, validServerResponse.validApiResponse.responseAt)
}

open func perform<T: Decodable>(request: DataRequest,
decoder: JSONDecoder = ApiFetcher.decoder) async throws -> ValidServerResponse<T> {
let validatedRequest = request.validate(statusCode: ApiFetcher.handledHttpStatus)
let response = await validatedRequest.serializingDecodable(ApiResponse<T>.self,
automaticallyCancelling: true,
decoder: decoder).response
let apiResponse = try response.result.get()
return try handleApiResponse(apiResponse, responseStatusCode: response.response?.statusCode ?? -1)
let dataResponse = await validatedRequest.serializingDecodable(ApiResponse<T>.self,
automaticallyCancelling: true,
decoder: decoder).response
return try handleApiResponse(dataResponse)
}

open func handleApiResponse<T: Decodable>(_ response: ApiResponse<T>,
responseStatusCode: Int) throws -> (data: T, responseAt: Int?) {
if let responseData = response.data {
return (responseData, response.responseAt)
} else if let apiError = response.error {
open func handleApiResponse<T: Decodable>(_ dataResponse: DataResponse<ApiResponse<T>, AFError>) throws
-> ValidServerResponse<T> {
let apiResponse = try dataResponse.result.get()

// This value should not be null because dataResponse.result.get should throw before
guard let serverResponse = dataResponse.response else {
throw ErrorDomain.noServerResponse
}

if let responseData = apiResponse.data {
let validApiResponse = ValidApiResponse(
result: apiResponse.result,
data: responseData,
total: apiResponse.total,
pages: apiResponse.pages,
page: apiResponse.page,
itemsPerPage: apiResponse.itemsPerPage,
responseAt: apiResponse.responseAt
)
return ValidServerResponse(responseHeaders: serverResponse.headers, validApiResponse: validApiResponse)
} else if let apiError = apiResponse.error {
throw InfomaniakError.apiError(apiError)
} else {
throw InfomaniakError.serverError(statusCode: responseStatusCode)
throw InfomaniakError.serverError(statusCode: serverResponse.statusCode)
}
}

Expand Down
34 changes: 34 additions & 0 deletions Sources/InfomaniakCore/Networking/ValidApiResponse.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
Infomaniak Core - iOS
Copyright (C) 2024 Infomaniak Network SA
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import Alamofire

public struct ValidServerResponse<ValidApiResponseContent> {
public let responseHeaders: HTTPHeaders
public let validApiResponse: ValidApiResponse<ValidApiResponseContent>
}

public struct ValidApiResponse<ResponseContent> {
public let result: ApiResult
public let data: ResponseContent
public let total: Int?
public let pages: Int?
public let page: Int?
public let itemsPerPage: Int?
public let responseAt: Int?
}

0 comments on commit ab305a8

Please sign in to comment.