This fixes issues where the full URL of our directory path changes, also it moves files into documents instead of caches, and ensures PDFs are downloaded before opening.
169 lines
5.5 KiB
Swift
169 lines
5.5 KiB
Swift
import Combine
|
|
import Foundation
|
|
import Models
|
|
import SwiftGraphQL
|
|
|
|
public struct UploadFileRequestPayload {
|
|
public let uploadID: String?
|
|
public let uploadFileID: String?
|
|
public let urlString: String?
|
|
}
|
|
|
|
public extension DataService {
|
|
func uploadFileRequest(id: String, url: String) async throws -> UploadFileRequestPayload {
|
|
enum MutationResult {
|
|
case success(payload: UploadFileRequestPayload)
|
|
case error(errorCode: Enums.UploadFileRequestErrorCode?)
|
|
}
|
|
|
|
let input = InputObjects.UploadFileRequestInput(
|
|
clientRequestId: OptionalArgument(id),
|
|
contentType: "application/pdf",
|
|
createPageEntry: OptionalArgument(false),
|
|
url: url
|
|
)
|
|
|
|
let selection = Selection<MutationResult, Unions.UploadFileRequestResult> {
|
|
try $0.on(
|
|
uploadFileRequestError: .init { .error(errorCode: try? $0.errorCodes().first) },
|
|
uploadFileRequestSuccess: .init {
|
|
.success(
|
|
payload: UploadFileRequestPayload(
|
|
uploadID: try $0.id(),
|
|
uploadFileID: try $0.uploadFileId(),
|
|
urlString: try $0.uploadSignedUrl()
|
|
)
|
|
)
|
|
}
|
|
)
|
|
}
|
|
|
|
let mutation = Selection.Mutation {
|
|
try $0.uploadFileRequest(input: input, selection: selection)
|
|
}
|
|
|
|
let path = appEnvironment.graphqlPath
|
|
let headers = networker.defaultHeaders
|
|
|
|
return try await withCheckedThrowingContinuation { continuation in
|
|
send(mutation, to: path, headers: headers) { result in
|
|
switch result {
|
|
case let .success(payload):
|
|
if let graphqlError = payload.errors {
|
|
continuation.resume(
|
|
throwing: SaveArticleError.unknown(description: graphqlError.first.debugDescription)
|
|
)
|
|
return
|
|
}
|
|
|
|
switch payload.data {
|
|
case let .success(payload):
|
|
if let urlString = payload.urlString, let url = URL(string: urlString) {
|
|
continuation.resume(returning: payload)
|
|
} else {
|
|
continuation.resume(throwing: SaveArticleError.unknown(description: "No upload URL"))
|
|
}
|
|
case let .error(errorCode: errorCode):
|
|
switch errorCode {
|
|
case .unauthorized:
|
|
continuation.resume(throwing: SaveArticleError.unauthorized)
|
|
default:
|
|
continuation.resume(throwing: SaveArticleError.unknown(description: errorCode?.rawValue ?? "unknown"))
|
|
}
|
|
}
|
|
case let .failure(error):
|
|
continuation.resume(throwing: SaveArticleError.make(from: error))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func uploadFile(id _: String, localPdfURL: URL, url: URL) async throws {
|
|
var request = URLRequest(url: url)
|
|
request.httpMethod = "PUT"
|
|
request.addValue("application/pdf", forHTTPHeaderField: "content-type")
|
|
|
|
return try await withCheckedThrowingContinuation { continuation in
|
|
let task = networker.urlSession.uploadTask(with: request, fromFile: localPdfURL) { _, response, _ in
|
|
if let httpResponse = response as? HTTPURLResponse, 200 ... 299 ~= httpResponse.statusCode {
|
|
continuation.resume()
|
|
} else {
|
|
continuation.resume(throwing: SaveArticleError.unknown(description: "Invalid response"))
|
|
}
|
|
}
|
|
task.resume()
|
|
}
|
|
}
|
|
|
|
func saveFilePublisher(requestId: String, uploadFileId: String, url: String) async throws -> String? {
|
|
enum MutationResult {
|
|
case saved(requestId: String, url: String)
|
|
case error(errorCode: Enums.SaveErrorCode)
|
|
}
|
|
|
|
let input = InputObjects.SaveFileInput(
|
|
clientRequestId: requestId,
|
|
source: "ios-file",
|
|
uploadFileId: uploadFileId,
|
|
url: url
|
|
)
|
|
|
|
let selection = Selection<MutationResult, Unions.SaveResult> {
|
|
try $0.on(
|
|
saveError: .init { .error(errorCode: (try? $0.errorCodes().first) ?? .unknown) },
|
|
saveSuccess: .init {
|
|
if let requestId = try? $0.clientRequestId(), let url = try? $0.url() {
|
|
return .saved(requestId: requestId, url: url)
|
|
} else {
|
|
return .error(errorCode: .unknown)
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
let mutation = Selection.Mutation {
|
|
try $0.saveFile(input: input, selection: selection)
|
|
}
|
|
|
|
let path = appEnvironment.graphqlPath
|
|
let headers = networker.defaultHeaders
|
|
|
|
return try await withCheckedThrowingContinuation { continuation in
|
|
send(mutation, to: path, headers: headers) { result in
|
|
switch result {
|
|
case let .success(payload):
|
|
if let graphqlError = payload.errors {
|
|
continuation.resume(throwing: SaveArticleError.unknown(description: graphqlError.first.debugDescription))
|
|
return
|
|
}
|
|
|
|
switch payload.data {
|
|
case let .saved(requestId: requestId, url: _):
|
|
continuation.resume(returning: requestId)
|
|
case let .error(errorCode: errorCode):
|
|
switch errorCode {
|
|
case .unauthorized:
|
|
continuation.resume(throwing: SaveArticleError.unauthorized)
|
|
default:
|
|
continuation.resume(throwing: SaveArticleError.unknown(description: errorCode.rawValue))
|
|
}
|
|
}
|
|
case let .failure(error):
|
|
continuation.resume(throwing: SaveArticleError.make(from: error))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private extension SaveError {
|
|
static func make(from httpError: HttpError) -> SaveArticleError {
|
|
switch httpError {
|
|
case .network, .timeout:
|
|
return .network
|
|
case .badpayload, .badURL, .badstatus, .cancelled:
|
|
return .unknown(description: httpError.localizedDescription)
|
|
}
|
|
}
|
|
}
|