FIrst pass at OPML import. Broken.

This commit is contained in:
Jeremy Beker 2019-06-16 15:14:20 -04:00
parent a6e0cae377
commit 42203c0170
No known key found for this signature in database
GPG Key ID: CD5EE767A4A34FD0
2 changed files with 113 additions and 106 deletions

View File

@ -49,10 +49,11 @@ final class GoogleReaderCompatibleAPICaller: NSObject {
case editTag = "/reader/api/0/edit-tag" case editTag = "/reader/api/0/edit-tag"
} }
// private let GoogleReaderCompatibleBaseURL = URL(string: "https://api.GoogleReaderCompatible.com/v2/")!
private var transport: Transport! private var transport: Transport!
var credentials: Credentials? var credentials: Credentials?
private var accessToken: String?
weak var accountMetadata: AccountMetadata? weak var accountMetadata: AccountMetadata?
var server: String? { var server: String? {
@ -127,6 +128,13 @@ final class GoogleReaderCompatibleAPICaller: NSObject {
} }
func requestAuthorizationToken(endpoint: URL, completion: @escaping (Result<String, Error>) -> Void) { func requestAuthorizationToken(endpoint: URL, completion: @escaping (Result<String, Error>) -> Void) {
// If we have a token already, use it
if let accessToken = accessToken {
completion(.success(accessToken))
return
}
// Otherwise request one.
guard let credentials = credentials else { guard let credentials = credentials else {
completion(.failure(CredentialsError.incompleteCredentials)) completion(.failure(CredentialsError.incompleteCredentials))
return return
@ -143,67 +151,19 @@ final class GoogleReaderCompatibleAPICaller: NSObject {
} }
// Convert the return data to UTF8 and then parse out the Auth token // Convert the return data to UTF8 and then parse out the Auth token
guard let rawData = String(data: resultData, encoding: .utf8) else { guard let accessToken = String(data: resultData, encoding: .utf8) else {
completion(.failure(TransportError.noData)) completion(.failure(TransportError.noData))
break break
} }
self.accessToken = accessToken
completion(.success(rawData)) completion(.success(accessToken))
case .failure(let error): case .failure(let error):
completion(.failure(error)) completion(.failure(error))
} }
} }
} }
func importOPML(opmlData: Data, completion: @escaping (Result<GoogleReaderCompatibleImportResult, Error>) -> Void) {
// let callURL = GoogleReaderCompatibleBaseURL.appendingPathComponent("imports.json")
// var request = URLRequest(url: callURL, credentials: credentials)
// request.addValue("text/xml; charset=utf-8", forHTTPHeaderField: HTTPRequestHeader.contentType)
//
// transport.send(request: request, method: HTTPMethod.post, payload: opmlData) { result in
//
// switch result {
// case .success(let (_, data)):
//
// guard let resultData = data else {
// completion(.failure(TransportError.noData))
// break
// }
//
// do {
// let result = try JSONDecoder().decode(GoogleReaderCompatibleImportResult.self, from: resultData)
// completion(.success(result))
// } catch {
// completion(.failure(error))
// }
//
// case .failure(let error):
// completion(.failure(error))
// }
//
// }
}
func retrieveOPMLImportResult(importID: Int, completion: @escaping (Result<GoogleReaderCompatibleImportResult?, Error>) -> Void) {
// let callURL = GoogleReaderCompatibleBaseURL.appendingPathComponent("imports/\(importID).json")
// let request = URLRequest(url: callURL, credentials: credentials)
//
// transport.send(request: request, resultType: GoogleReaderCompatibleImportResult.self) { result in
//
// switch result {
// case .success(let (_, importResult)):
// completion(.success(importResult))
// case .failure(let error):
// completion(.failure(error))
// }
//
// }
}
func retrieveTags(completion: @escaping (Result<[GoogleReaderCompatibleTag]?, Error>) -> Void) { func retrieveTags(completion: @escaping (Result<[GoogleReaderCompatibleTag]?, Error>) -> Void) {
guard let baseURL = APIBaseURL else { guard let baseURL = APIBaseURL else {

View File

@ -210,31 +210,113 @@ final class GoogleReaderCompatibleAccountDelegate: AccountDelegate {
return return
} }
os_log(.debug, log: log, "Begin importing OPML...") let parserData = ParserData(url: opmlFile.absoluteString, data: opmlData)
opmlImportInProgress = true var opmlDocument: RSOPMLDocument?
caller.importOPML(opmlData: opmlData) { result in do {
switch result { opmlDocument = try RSOPMLParser.parseOPML(with: parserData)
case .success(let importResult): } catch {
if importResult.complete { completion(.failure(error))
os_log(.debug, log: self.log, "Import OPML done.") return
self.opmlImportInProgress = false }
DispatchQueue.main.async {
guard let loadDocument = opmlDocument else {
completion(.success(())) completion(.success(()))
return
}
// We use the same mechanism to load local accounts as we do to load the subscription
// OPML all accounts.
BatchUpdate.shared.perform {
loadOPML(account: account, opmlDocument: loadDocument)
}
completion(.success(()))
}
func loadOPML(account: Account, opmlDocument: RSOPMLDocument) {
guard let children = opmlDocument.children else {
return
}
loadOPMLItems(account: account, items: children, parentFolder: nil)
}
func loadOPMLItems(account: Account, items: [RSOPMLItem], parentFolder: Folder?) {
var feedsToAdd = Set<String>()
items.forEach { (item) in
if let feedSpecifier = item.feedSpecifier {
feedsToAdd.insert(feedSpecifier.feedURL)
return
}
guard let folderName = item.titleFromAttributes else {
// Folder doesnt have a name, so it wont be created, and its items will go one level up.
if let itemChildren = item.children {
loadOPMLItems(account: account, items: itemChildren, parentFolder: parentFolder)
}
return
}
if let itemChildren = item.children, let folder = account.ensureFolder(with: folderName) {
loadOPMLItems(account: account, items: itemChildren, parentFolder: folder)
}
}
let group = DispatchGroup()
if let parentFolder = parentFolder {
for url in feedsToAdd {
group.enter()
caller.createSubscription(url: url) { result in
group.leave()
switch result {
case .success(let subResult):
switch subResult {
case .created(let subscription):
let feed = account.createFeed(with: subscription.name, url: subscription.url, feedID: String(subscription.feedID), homePageURL: subscription.homePageURL)
feed.subscriptionID = String(subscription.feedID)
account.addFeed(feed, to: parentFolder) { _ in }
default:
break
}
case .failure(_):
break
}
}
} }
} else { } else {
self.checkImportResult(opmlImportResultID: importResult.importResultID, completion: completion) for url in feedsToAdd {
group.enter()
caller.createSubscription(url: url) { result in
group.leave()
switch result {
case .success(let subResult):
switch subResult {
case .created(let subscription):
let feed = account.createFeed(with: subscription.name, url: subscription.url, feedID: String(subscription.feedID), homePageURL: subscription.homePageURL)
feed.subscriptionID = String(subscription.feedID)
account.addFeed(feed)
default:
break
}
case .failure(_):
break
} }
case .failure(let error):
os_log(.debug, log: self.log, "Import OPML failed.")
self.opmlImportInProgress = false
DispatchQueue.main.async {
let wrappedError = AccountError.wrappedError(error: error, account: account)
completion(.failure(wrappedError))
} }
} }
} }
group.notify(queue: DispatchQueue.main) {
DispatchQueue.main.async {
self.refreshAll(for: account) { (_) in }
}
}
} }
func addFolder(for account: Account, name: String, completion: @escaping (Result<Folder, Error>) -> Void) { func addFolder(for account: Account, name: String, completion: @escaping (Result<Folder, Error>) -> Void) {
@ -498,41 +580,6 @@ private extension GoogleReaderCompatibleAccountDelegate {
} }
func checkImportResult(opmlImportResultID: Int, completion: @escaping (Result<Void, Error>) -> Void) {
DispatchQueue.main.async {
Timer.scheduledTimer(withTimeInterval: 15, repeats: true) { timer in
os_log(.debug, log: self.log, "Checking status of OPML import...")
self.caller.retrieveOPMLImportResult(importID: opmlImportResultID) { result in
switch result {
case .success(let importResult):
if let result = importResult, result.complete {
os_log(.debug, log: self.log, "Checking status of OPML import successfully completed.")
timer.invalidate()
self.opmlImportInProgress = false
DispatchQueue.main.async {
completion(.success(()))
}
}
case .failure(let error):
os_log(.debug, log: self.log, "Import OPML check failed.")
timer.invalidate()
self.opmlImportInProgress = false
DispatchQueue.main.async {
completion(.failure(error))
}
}
}
}
}
}
func syncFolders(_ account: Account, _ tags: [GoogleReaderCompatibleTag]?) { func syncFolders(_ account: Account, _ tags: [GoogleReaderCompatibleTag]?) {
guard let tags = tags else { return } guard let tags = tags else { return }