Files
omnivore/apple/OmnivoreKit/Sources/Services/DataService/Mutations/SavePDF.swift
Jackson Harper 7b81ad253d Store PDF paths as filenames instead of full URLs
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.
2022-06-09 13:52:49 -07:00

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