Make authorize endpoint async
This commit is contained in:
parent
578d22f3c2
commit
ccd600b880
|
@ -62,6 +62,7 @@
|
|||
516896352448EBEA00185AC5 /* FeedProviderManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516896342448EBEA00185AC5 /* FeedProviderManager.swift */; };
|
||||
5170743C232AEDB500A461A3 /* OPMLFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5170743B232AEDB500A461A3 /* OPMLFile.swift */; };
|
||||
5193CD54245E3F7A0092735E /* RedditFeedProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5193CD53245E3F7A0092735E /* RedditFeedProvider.swift */; };
|
||||
5193CD81245F295E0092735E /* RedditUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5193CD80245F295E0092735E /* RedditUser.swift */; };
|
||||
519E84A62433D49000D238B0 /* OPMLNormalizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E84A52433D49000D238B0 /* OPMLNormalizer.swift */; };
|
||||
519E84A82434C5EF00D238B0 /* CloudKitArticlesZone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E84A72434C5EF00D238B0 /* CloudKitArticlesZone.swift */; };
|
||||
519E84AC2435019100D238B0 /* CloudKitArticlesZoneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E84AB2435019100D238B0 /* CloudKitArticlesZoneDelegate.swift */; };
|
||||
|
@ -317,6 +318,7 @@
|
|||
516896342448EBEA00185AC5 /* FeedProviderManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedProviderManager.swift; sourceTree = "<group>"; };
|
||||
5170743B232AEDB500A461A3 /* OPMLFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPMLFile.swift; sourceTree = "<group>"; };
|
||||
5193CD53245E3F7A0092735E /* RedditFeedProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedditFeedProvider.swift; sourceTree = "<group>"; };
|
||||
5193CD80245F295E0092735E /* RedditUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedditUser.swift; sourceTree = "<group>"; };
|
||||
519E84A52433D49000D238B0 /* OPMLNormalizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPMLNormalizer.swift; sourceTree = "<group>"; };
|
||||
519E84A72434C5EF00D238B0 /* CloudKitArticlesZone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudKitArticlesZone.swift; sourceTree = "<group>"; };
|
||||
519E84AB2435019100D238B0 /* CloudKitArticlesZoneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudKitArticlesZoneDelegate.swift; sourceTree = "<group>"; };
|
||||
|
@ -631,6 +633,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
5193CD53245E3F7A0092735E /* RedditFeedProvider.swift */,
|
||||
5193CD80245F295E0092735E /* RedditUser.swift */,
|
||||
);
|
||||
path = Reddit;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1216,6 +1219,7 @@
|
|||
9E1D15512334282100F4944C /* FeedlyMirrorCollectionsAsFoldersOperation.swift in Sources */,
|
||||
9E1773D7234575AB0056A5A8 /* FeedlyTag.swift in Sources */,
|
||||
3B826DAB2385C81C00FC1ADB /* FeedWranglerConfig.swift in Sources */,
|
||||
5193CD81245F295E0092735E /* RedditUser.swift in Sources */,
|
||||
515E4EB62324FF8C0057B0E7 /* URLRequest+RSWeb.swift in Sources */,
|
||||
51B36315244BCCA4000DEF2A /* TwitterSearchResult.swift in Sources */,
|
||||
9EB1D576238E6A3900A753D7 /* FeedlyAddNewFeedOperation.swift in Sources */,
|
||||
|
|
|
@ -31,51 +31,32 @@ public struct RedditFeedProvider: FeedProvider {
|
|||
private static let userPaths = ["/home", "/notifications"]
|
||||
private static let reservedPaths = ["/search", "/explore", "/messages", "/i", "/compose"]
|
||||
|
||||
public var username: String
|
||||
public var username: String?
|
||||
|
||||
private var oauthToken: String
|
||||
private var oauthTokenSecret: String
|
||||
private var oauthRefreshToken: String
|
||||
|
||||
private var client: OAuthSwiftClient
|
||||
|
||||
public init?(tokenSuccess: OAuthSwift.TokenSuccess) {
|
||||
guard let username = tokenSuccess.parameters["screen_name"] as? String else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.username = username
|
||||
self.oauthToken = tokenSuccess.credential.oauthToken
|
||||
self.oauthTokenSecret = tokenSuccess.credential.oauthTokenSecret
|
||||
|
||||
let tokenCredentials = Credentials(type: .oauthAccessToken, username: username, secret: oauthToken)
|
||||
try? CredentialsManager.storeCredentials(tokenCredentials, server: Self.server)
|
||||
|
||||
let tokenSecretCredentials = Credentials(type: .oauthAccessTokenSecret, username: username, secret: oauthTokenSecret)
|
||||
try? CredentialsManager.storeCredentials(tokenSecretCredentials, server: Self.server)
|
||||
|
||||
client = OAuthSwiftClient(consumerKey: Secrets.twitterConsumerKey,
|
||||
consumerSecret: Secrets.twitterConsumerSecret,
|
||||
oauthToken: oauthToken,
|
||||
oauthTokenSecret: oauthTokenSecret,
|
||||
version: .oauth1)
|
||||
private var oauthSwift: OAuth2Swift?
|
||||
private var client: OAuthSwiftClient? {
|
||||
return oauthSwift?.client
|
||||
}
|
||||
|
||||
public init?(username: String) {
|
||||
self.username = username
|
||||
|
||||
guard let tokenCredentials = try? CredentialsManager.retrieveCredentials(type: .oauthAccessToken, server: Self.server, username: username),
|
||||
let tokenSecretCredentials = try? CredentialsManager.retrieveCredentials(type: .oauthAccessTokenSecret, server: Self.server, username: username) else {
|
||||
let refreshTokenCredentials = try? CredentialsManager.retrieveCredentials(type: .oauthRefreshToken, server: Self.server, username: username) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.oauthToken = tokenCredentials.secret
|
||||
self.oauthTokenSecret = tokenSecretCredentials.secret
|
||||
|
||||
client = OAuthSwiftClient(consumerKey: Secrets.twitterConsumerKey,
|
||||
consumerSecret: Secrets.twitterConsumerSecret,
|
||||
oauthToken: oauthToken,
|
||||
oauthTokenSecret: oauthTokenSecret,
|
||||
version: .oauth1)
|
||||
self.init(oauthToken: tokenCredentials.secret, oauthRefreshToken: refreshTokenCredentials.secret)
|
||||
self.username = username
|
||||
}
|
||||
|
||||
init(oauthToken: String, oauthRefreshToken: String) {
|
||||
self.oauthToken = oauthToken
|
||||
self.oauthRefreshToken = oauthRefreshToken
|
||||
oauthSwift = Self.oauth2Swift
|
||||
oauthSwift!.client.credential.oauthToken = oauthToken
|
||||
oauthSwift!.client.credential.oauthRefreshToken = oauthRefreshToken
|
||||
}
|
||||
|
||||
public func ability(_ urlComponents: URLComponents) -> FeedProviderAbility {
|
||||
|
@ -125,6 +106,34 @@ public struct RedditFeedProvider: FeedProvider {
|
|||
completion(.success(Set<ParsedItem>()))
|
||||
}
|
||||
|
||||
public static func create(tokenSuccess: OAuthSwift.TokenSuccess, completion: @escaping (Result<RedditFeedProvider, Error>) -> Void) {
|
||||
let oauthToken = tokenSuccess.credential.oauthToken
|
||||
let oauthRefreshToken = tokenSuccess.credential.oauthRefreshToken
|
||||
var redditFeedProvider = RedditFeedProvider(oauthToken: oauthToken, oauthRefreshToken: oauthRefreshToken)
|
||||
|
||||
redditFeedProvider.retrieveUserName() { result in
|
||||
switch result {
|
||||
case .success(let username):
|
||||
|
||||
do {
|
||||
let tokenCredentials = Credentials(type: .oauthAccessToken, username: username, secret: oauthToken)
|
||||
try CredentialsManager.storeCredentials(tokenCredentials, server: Self.server)
|
||||
let tokenSecretCredentials = Credentials(type: .oauthRefreshToken, username: username, secret: oauthRefreshToken)
|
||||
try CredentialsManager.storeCredentials(tokenSecretCredentials, server: Self.server)
|
||||
|
||||
redditFeedProvider.username = username
|
||||
completion(.success(redditFeedProvider))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// MARK: OAuth1SwiftProvider
|
||||
|
@ -132,7 +141,37 @@ public struct RedditFeedProvider: FeedProvider {
|
|||
extension RedditFeedProvider: OAuth2SwiftProvider {
|
||||
|
||||
public static var oauth2Swift: OAuth2Swift {
|
||||
return OAuth2Swift(consumerKey: "", consumerSecret: "", authorizeUrl: "", accessTokenUrl: "", responseType: "")
|
||||
let oauth2 = OAuth2Swift(consumerKey: Secrets.redditConsumerKey,
|
||||
consumerSecret: "",
|
||||
authorizeUrl: "https://www.reddit.com/api/v1/authorize.compact?",
|
||||
accessTokenUrl: "https://www.reddit.com/api/v1/access_token",
|
||||
responseType: "token")
|
||||
oauth2.accessTokenBasicAuthentification = true
|
||||
return oauth2
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension RedditFeedProvider {
|
||||
|
||||
func retrieveUserName(completion: @escaping (Result<String, Error>) -> Void) {
|
||||
guard let client = client else {
|
||||
completion(.failure(RedditFeedProviderError.unknown))
|
||||
return
|
||||
}
|
||||
|
||||
client.request(Self.apiBase + "/api/v1/me", method: .GET) { result in
|
||||
switch result {
|
||||
case .success(let response):
|
||||
if let redditUser = try? JSONDecoder().decode(RedditUser.self, from: response.data), let username = redditUser.name {
|
||||
completion(.success(username))
|
||||
} else {
|
||||
completion(.failure(RedditFeedProviderError.unknown))
|
||||
}
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// RedditUser.swift
|
||||
// Account
|
||||
//
|
||||
// Created by Maurice Parker on 5/3/20.
|
||||
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct RedditUser: Codable {
|
||||
|
||||
let name: String?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case name = "name"
|
||||
}
|
||||
|
||||
// var url: String {
|
||||
// return "https://twitter.com/\(screenName ?? "")"
|
||||
// }
|
||||
//
|
||||
// func renderAsHTML() -> String? {
|
||||
// var html = String()
|
||||
// html += "<div><a href=\"\(url)\">"
|
||||
// if let avatarURL = avatarURL {
|
||||
// html += "<img class=\"twitterAvatar\" src=\"\(avatarURL)\">"
|
||||
// }
|
||||
// html += "<span class=\"twitterUsername\">"
|
||||
// if let name = name {
|
||||
// html += " \(name)"
|
||||
// }
|
||||
// if let screenName = screenName {
|
||||
// html += " @\(screenName)"
|
||||
// }
|
||||
// html += "</span></a></div>"
|
||||
// return html
|
||||
// }
|
||||
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
%{
|
||||
import os
|
||||
|
||||
secrets = ['FEED_WRANGLER_KEY', 'MERCURY_CLIENT_ID', 'MERCURY_CLIENT_SECRET', 'FEEDLY_CLIENT_ID', 'FEEDLY_CLIENT_SECRET', 'TWITTER_CONSUMER_KEY', 'TWITTER_CONSUMER_SECRET']
|
||||
secrets = ['FEED_WRANGLER_KEY', 'MERCURY_CLIENT_ID', 'MERCURY_CLIENT_SECRET', 'FEEDLY_CLIENT_ID', 'FEEDLY_CLIENT_SECRET', 'TWITTER_CONSUMER_KEY', 'TWITTER_CONSUMER_SECRET', 'REDDIT_CONSUMER_KEY']
|
||||
|
||||
def chunks(seq, size):
|
||||
return (seq[i:(i + size)] for i in range(0, len(seq), size))
|
||||
|
|
|
@ -59,8 +59,12 @@ class ExtensionPointEnableWindowController: NSWindowController {
|
|||
if let oauth1 = extensionPointType as? OAuth1SwiftProvider.Type {
|
||||
enableOauth1(oauth1)
|
||||
} else {
|
||||
ExtensionPointManager.shared.activateExtensionPoint(extensionPointType)
|
||||
hostWindow!.endSheet(window!, returnCode: NSApplication.ModalResponse.OK)
|
||||
ExtensionPointManager.shared.activateExtensionPoint(extensionPointType) { result in
|
||||
if case .failure(let error) = result {
|
||||
self.presentError(error)
|
||||
}
|
||||
self.hostWindow!.endSheet(self.window!, returnCode: NSApplication.ModalResponse.OK)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -120,8 +124,12 @@ private extension ExtensionPointEnableWindowController {
|
|||
|
||||
switch result {
|
||||
case .success(let tokenSuccess):
|
||||
ExtensionPointManager.shared.activateExtensionPoint(extensionPointType, tokenSuccess: tokenSuccess)
|
||||
self.hostWindow!.endSheet(self.window!, returnCode: NSApplication.ModalResponse.OK)
|
||||
ExtensionPointManager.shared.activateExtensionPoint(extensionPointType, tokenSuccess: tokenSuccess) { result in
|
||||
if case .failure(let error) = result {
|
||||
self.presentError(error)
|
||||
}
|
||||
self.hostWindow!.endSheet(self.window!, returnCode: NSApplication.ModalResponse.OK)
|
||||
}
|
||||
case .failure(let oauthSwiftError):
|
||||
NSApplication.shared.presentError(oauthSwiftError)
|
||||
}
|
||||
|
|
|
@ -15,6 +15,18 @@ public extension Notification.Name {
|
|||
static let ActiveExtensionPointsDidChange = Notification.Name(rawValue: "ActiveExtensionPointsDidChange")
|
||||
}
|
||||
|
||||
public enum ExtensionPointManagerError: LocalizedError {
|
||||
case unableToCreate
|
||||
|
||||
public var localizedDescription: String {
|
||||
switch self {
|
||||
case .unableToCreate:
|
||||
return NSLocalizedString("Unable to create extension.", comment: "Unable to create extension")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final class ExtensionPointManager: FeedProviderManagerDelegate {
|
||||
|
||||
static let shared = ExtensionPointManager()
|
||||
|
@ -74,10 +86,16 @@ final class ExtensionPointManager: FeedProviderManagerDelegate {
|
|||
loadExtensionPoints()
|
||||
}
|
||||
|
||||
func activateExtensionPoint(_ extensionPointType: ExtensionPoint.Type, tokenSuccess: OAuthSwift.TokenSuccess? = nil) {
|
||||
if let extensionPoint = self.extensionPoint(for: extensionPointType, tokenSuccess: tokenSuccess) {
|
||||
activeExtensionPoints[extensionPoint.extensionPointID] = extensionPoint
|
||||
saveExtensionPointIDs()
|
||||
func activateExtensionPoint(_ extensionPointType: ExtensionPoint.Type, tokenSuccess: OAuthSwift.TokenSuccess? = nil, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
self.extensionPoint(for: extensionPointType, tokenSuccess: tokenSuccess) { result in
|
||||
switch result {
|
||||
case .success(let extensionPoint):
|
||||
self.activeExtensionPoints[extensionPoint.extensionPointID] = extensionPoint
|
||||
self.saveExtensionPointIDs()
|
||||
completion(.success(()))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,30 +123,36 @@ private extension ExtensionPointManager {
|
|||
NotificationCenter.default.post(name: .ActiveExtensionPointsDidChange, object: nil, userInfo: nil)
|
||||
}
|
||||
|
||||
func extensionPoint(for extensionPointType: ExtensionPoint.Type, tokenSuccess: OAuthSwift.TokenSuccess?) -> ExtensionPoint? {
|
||||
func extensionPoint(for extensionPointType: ExtensionPoint.Type, tokenSuccess: OAuthSwift.TokenSuccess?, completion: @escaping (Result<ExtensionPoint, Error>) -> Void) {
|
||||
switch extensionPointType {
|
||||
#if os(macOS)
|
||||
case is SendToMarsEditCommand.Type:
|
||||
return SendToMarsEditCommand()
|
||||
completion(.success(SendToMarsEditCommand()))
|
||||
case is SendToMicroBlogCommand.Type:
|
||||
return SendToMicroBlogCommand()
|
||||
completion(.success(SendToMicroBlogCommand()))
|
||||
#endif
|
||||
case is TwitterFeedProvider.Type:
|
||||
if let tokenSuccess = tokenSuccess {
|
||||
return TwitterFeedProvider(tokenSuccess: tokenSuccess)
|
||||
if let tokenSuccess = tokenSuccess, let twitter = TwitterFeedProvider(tokenSuccess: tokenSuccess) {
|
||||
completion(.success(twitter))
|
||||
} else {
|
||||
return nil
|
||||
completion(.failure(ExtensionPointManagerError.unableToCreate))
|
||||
}
|
||||
case is RedditFeedProvider.Type:
|
||||
if let tokenSuccess = tokenSuccess {
|
||||
return RedditFeedProvider(tokenSuccess: tokenSuccess)
|
||||
RedditFeedProvider.create(tokenSuccess: tokenSuccess) { result in
|
||||
switch result {
|
||||
case .success(let reddit):
|
||||
completion(.success(reddit))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
completion(.failure(ExtensionPointManagerError.unableToCreate))
|
||||
}
|
||||
default:
|
||||
assertionFailure("Unrecognized Extension Point Type.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func extensionPoint(for extensionPointID: ExtensionPointIdentifer) -> ExtensionPoint? {
|
||||
|
|
|
@ -20,10 +20,16 @@ extension RedditFeedProvider: ExtensionPoint {
|
|||
}()
|
||||
|
||||
var extensionPointID: ExtensionPointIdentifer {
|
||||
guard let username = username else {
|
||||
fatalError()
|
||||
}
|
||||
return ExtensionPointIdentifer.reddit(username)
|
||||
}
|
||||
|
||||
var title: String {
|
||||
guard let username = username else {
|
||||
fatalError()
|
||||
}
|
||||
return "/u/\(username)"
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue