mirror of
https://github.com/mas-cli/mas
synced 2025-02-16 12:38:30 +00:00
🤞🏼 Rephrase StoreSearch as Promises
This commit is contained in:
parent
a9b018854d
commit
9494dea403
13 changed files with 48 additions and 119 deletions
|
@ -38,7 +38,7 @@ public struct HomeCommand: CommandProtocol {
|
|||
/// Runs the command.
|
||||
public func run(_ options: HomeOptions) -> Result<Void, MASError> {
|
||||
do {
|
||||
guard let result = try storeSearch.lookup(app: options.appId) else {
|
||||
guard let result = try storeSearch.lookup(app: options.appId).wait() else {
|
||||
print("No results found")
|
||||
return .failure(.noSearchResultsFound)
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ public struct InfoCommand: CommandProtocol {
|
|||
/// Runs the command.
|
||||
public func run(_ options: InfoOptions) -> Result<Void, MASError> {
|
||||
do {
|
||||
guard let result = try storeSearch.lookup(app: options.appId) else {
|
||||
guard let result = try storeSearch.lookup(app: options.appId).wait() else {
|
||||
print("No results found")
|
||||
return .failure(.noSearchResultsFound)
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ public struct LuckyCommand: CommandProtocol {
|
|||
var appId: Int?
|
||||
|
||||
do {
|
||||
let results = try storeSearch.search(for: options.appName)
|
||||
let results = try storeSearch.search(for: options.appName).wait()
|
||||
guard let result = results.first else {
|
||||
print("No results found")
|
||||
return .failure(.noSearchResultsFound)
|
||||
|
|
|
@ -54,7 +54,7 @@ public struct OpenCommand: CommandProtocol {
|
|||
return .failure(.noSearchResultsFound)
|
||||
}
|
||||
|
||||
guard let result = try storeSearch.lookup(app: appId)
|
||||
guard let result = try storeSearch.lookup(app: appId).wait()
|
||||
else {
|
||||
print("No results found")
|
||||
return .failure(.noSearchResultsFound)
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
import Commandant
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import enum Swift.Result
|
||||
|
||||
/// Command which displays a list of installed apps which have available updates
|
||||
/// ready to be installed from the Mac App Store.
|
||||
|
@ -34,19 +36,10 @@ public struct OutdatedCommand: CommandProtocol {
|
|||
|
||||
/// Runs the command.
|
||||
public func run(_: Options) -> Result<Void, MASError> {
|
||||
var failure: MASError?
|
||||
let group = DispatchGroup()
|
||||
for installedApp in appLibrary.installedApps {
|
||||
group.enter()
|
||||
storeSearch.lookup(app: installedApp.itemIdentifier.intValue) { storeApp, error in
|
||||
defer { group.leave() }
|
||||
|
||||
guard error == nil else {
|
||||
// Bubble up MASErrors
|
||||
failure = error as? MASError ?? .searchFailed
|
||||
return
|
||||
}
|
||||
|
||||
let promises = appLibrary.installedApps.map { installedApp in
|
||||
firstly {
|
||||
storeSearch.lookup(app: installedApp.itemIdentifier.intValue)
|
||||
}.done { storeApp in
|
||||
guard let storeApp = storeApp else {
|
||||
printWarning(
|
||||
"""
|
||||
|
@ -66,12 +59,13 @@ public struct OutdatedCommand: CommandProtocol {
|
|||
}
|
||||
}
|
||||
|
||||
group.wait()
|
||||
|
||||
if let failure = failure {
|
||||
return .failure(failure)
|
||||
}
|
||||
|
||||
return .success(())
|
||||
return firstly {
|
||||
when(fulfilled: promises)
|
||||
}.map {
|
||||
Result<Void, MASError>.success(())
|
||||
}.recover { error in
|
||||
// Bubble up MASErrors
|
||||
.value(Result<Void, MASError>.failure(error as? MASError ?? .searchFailed))
|
||||
}.wait()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ public struct SearchCommand: CommandProtocol {
|
|||
|
||||
public func run(_ options: Options) -> Result<Void, MASError> {
|
||||
do {
|
||||
let results = try storeSearch.search(for: options.appName)
|
||||
let results = try storeSearch.search(for: options.appName).wait()
|
||||
if results.isEmpty {
|
||||
print("No results found")
|
||||
return .failure(.noSearchResultsFound)
|
||||
|
|
|
@ -94,7 +94,7 @@ public struct UpgradeCommand: CommandProtocol {
|
|||
for installedApp in apps {
|
||||
// only upgrade apps whose local version differs from the store version
|
||||
group.enter()
|
||||
storeSearch.lookup(app: installedApp.itemIdentifier.intValue) { result, _ in
|
||||
try storeSearch.lookup(app: installedApp.itemIdentifier.intValue).done { result in
|
||||
defer { group.leave() }
|
||||
|
||||
if let storeApp = result, installedApp.isOutdatedWhenComparedTo(storeApp) {
|
||||
|
@ -103,7 +103,7 @@ public struct UpgradeCommand: CommandProtocol {
|
|||
|
||||
outdated.append(installedApp)
|
||||
}
|
||||
}
|
||||
}.wait()
|
||||
}
|
||||
|
||||
group.wait()
|
||||
|
|
|
@ -38,7 +38,7 @@ public struct VendorCommand: CommandProtocol {
|
|||
/// Runs the command.
|
||||
public func run(_ options: VendorOptions) -> Result<Void, MASError> {
|
||||
do {
|
||||
guard let result = try storeSearch.lookup(app: options.appId)
|
||||
guard let result = try storeSearch.lookup(app: options.appId).wait()
|
||||
else {
|
||||
print("No results found")
|
||||
return .failure(.noSearchResultsFound)
|
||||
|
|
|
@ -31,41 +31,27 @@ class MasStoreSearch: StoreSearch {
|
|||
/// - Parameter appName: MAS ID of app
|
||||
/// - Parameter completion: A closure that receives the search results or an Error if there is a
|
||||
/// problem with the network request. Results array will be empty if there were no matches.
|
||||
func search(for appName: String, _ completion: @escaping ([SearchResult]?, Error?) -> Void) {
|
||||
func search(for appName: String) -> Promise<[SearchResult]> {
|
||||
guard let url = searchURL(for: appName)
|
||||
else {
|
||||
completion(nil, MASError.urlEncoding)
|
||||
return
|
||||
return Promise(error: MASError.urlEncoding)
|
||||
}
|
||||
|
||||
firstly {
|
||||
loadSearchResults(url)
|
||||
}.done { results in
|
||||
completion(results, nil)
|
||||
}.catch { error in
|
||||
completion(nil, error)
|
||||
}
|
||||
return loadSearchResults(url)
|
||||
}
|
||||
|
||||
/// Looks up app details.
|
||||
///
|
||||
/// - Parameter appId: MAS ID of app
|
||||
/// - Parameter completion: A closure that receives the search result record of app, or nil if no apps match the ID,
|
||||
/// - Returns: A Promise for the search result record of app, or nil if no apps match the ID,
|
||||
/// or an Error if there is a problem with the network request.
|
||||
func lookup(app appId: Int, _ completion: @escaping (SearchResult?, Error?) -> Void) {
|
||||
func lookup(app appId: Int) -> Promise<SearchResult?> {
|
||||
guard let url = lookupURL(forApp: appId)
|
||||
else {
|
||||
completion(nil, MASError.urlEncoding)
|
||||
return
|
||||
return Promise(error: MASError.urlEncoding)
|
||||
}
|
||||
|
||||
firstly {
|
||||
loadSearchResults(url)
|
||||
}.done { results in
|
||||
completion(results.first, nil)
|
||||
}.catch { error in
|
||||
completion(nil, error)
|
||||
}
|
||||
return loadSearchResults(url).map { results in results.first }
|
||||
}
|
||||
|
||||
private func loadSearchResults(_ url: URL) -> Promise<[SearchResult]> {
|
||||
|
|
|
@ -7,67 +7,16 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
|
||||
/// Protocol for searching the MAS catalog.
|
||||
protocol StoreSearch {
|
||||
func lookup(app appId: Int, _ completion: @escaping (SearchResult?, Error?) -> Void)
|
||||
func search(for appName: String, _ completion: @escaping ([SearchResult]?, Error?) -> Void)
|
||||
func lookup(app appId: Int) -> Promise<SearchResult?>
|
||||
func search(for appName: String) -> Promise<[SearchResult]>
|
||||
}
|
||||
|
||||
// MARK: - Common methods
|
||||
extension StoreSearch {
|
||||
/// Looks up app details.
|
||||
///
|
||||
/// - Parameter appId: MAS ID of app
|
||||
/// - Returns: Search result record of app or nil if no apps match the ID.
|
||||
/// - Throws: Error if there is a problem with the network request.
|
||||
func lookup(app appId: Int) throws -> SearchResult? {
|
||||
var result: SearchResult?
|
||||
var error: Error?
|
||||
|
||||
let group = DispatchGroup()
|
||||
group.enter()
|
||||
lookup(app: appId) {
|
||||
result = $0
|
||||
error = $1
|
||||
group.leave()
|
||||
}
|
||||
|
||||
group.wait()
|
||||
|
||||
if let error = error {
|
||||
throw error
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/// Searches for an app.
|
||||
///
|
||||
/// - Parameter appName: MAS ID of app
|
||||
/// - Returns: Search results. Empty if there were no matches.
|
||||
/// - Throws: Error if there is a problem with the network request.
|
||||
func search(for appName: String) throws -> [SearchResult] {
|
||||
var results: [SearchResult]?
|
||||
var error: Error?
|
||||
|
||||
let group = DispatchGroup()
|
||||
group.enter()
|
||||
search(for: appName) {
|
||||
results = $0
|
||||
error = $1
|
||||
group.leave()
|
||||
}
|
||||
|
||||
group.wait()
|
||||
|
||||
if let error = error {
|
||||
throw error
|
||||
}
|
||||
|
||||
return results!
|
||||
}
|
||||
|
||||
/// Builds the search URL for an app.
|
||||
///
|
||||
/// - Parameter appName: MAS app identifier.
|
||||
|
|
|
@ -24,7 +24,7 @@ public class MasStoreSearchSpec: QuickSpec {
|
|||
|
||||
var results: [SearchResult]
|
||||
do {
|
||||
results = try storeSearch.search(for: "slack")
|
||||
results = try storeSearch.search(for: "slack").wait()
|
||||
expect(results.count) == 39
|
||||
} catch {
|
||||
let maserror = error as! MASError
|
||||
|
@ -43,7 +43,7 @@ public class MasStoreSearchSpec: QuickSpec {
|
|||
|
||||
var lookup: SearchResult?
|
||||
do {
|
||||
lookup = try storeSearch.lookup(app: appId)
|
||||
lookup = try storeSearch.lookup(app: appId).wait()
|
||||
} catch {
|
||||
let maserror = error as! MASError
|
||||
if case .jsonParsing(let nserror) = maserror {
|
||||
|
|
|
@ -6,31 +6,30 @@
|
|||
// Copyright © 2019 mas-cli. All rights reserved.
|
||||
//
|
||||
|
||||
import PromiseKit
|
||||
@testable import MasKit
|
||||
|
||||
class StoreSearchMock: StoreSearch {
|
||||
var apps: [Int: SearchResult] = [:]
|
||||
|
||||
func search(for appName: String, _ completion: @escaping ([SearchResult]?, Error?) -> Void) {
|
||||
func search(for appName: String) -> Promise<[SearchResult]> {
|
||||
let filtered = apps.filter { $1.trackName.contains(appName) }
|
||||
let results = filtered.map { $1 }
|
||||
completion(results, nil)
|
||||
return .value(results)
|
||||
}
|
||||
|
||||
func lookup(app appId: Int, _ completion: @escaping (SearchResult?, Error?) -> Void) {
|
||||
func lookup(app appId: Int) -> Promise<SearchResult?> {
|
||||
// Negative numbers are invalid
|
||||
guard appId > 0 else {
|
||||
completion(nil, MASError.searchFailed)
|
||||
return
|
||||
return Promise(error: MASError.searchFailed)
|
||||
}
|
||||
|
||||
guard let result = apps[appId]
|
||||
else {
|
||||
completion(nil, MASError.noSearchResultsFound)
|
||||
return
|
||||
return Promise(error: MASError.noSearchResultsFound)
|
||||
}
|
||||
|
||||
completion(result, nil)
|
||||
return .value(result)
|
||||
}
|
||||
|
||||
func reset() {
|
||||
|
|
|
@ -5,19 +5,20 @@
|
|||
// Created by Ben Chatelain on 1/11/19.
|
||||
// Copyright © 2019 mas-cli. All rights reserved.
|
||||
//
|
||||
import Nimble
|
||||
import Quick
|
||||
|
||||
import Nimble
|
||||
import PromiseKit
|
||||
import Quick
|
||||
@testable import MasKit
|
||||
|
||||
/// Protocol minimal implementation
|
||||
struct StoreSearchForTesting: StoreSearch {
|
||||
func lookup(app _: Int, _ completion: @escaping (SearchResult?, Error?) -> Void) {
|
||||
completion(nil, nil)
|
||||
func lookup(app _: Int) -> Promise<SearchResult?> {
|
||||
.value(nil)
|
||||
}
|
||||
|
||||
func search(for _: String, _ completion: @escaping ([SearchResult]?, Error?) -> Void) {
|
||||
completion([], nil)
|
||||
func search(for _: String) -> Promise<[SearchResult]> {
|
||||
.value([])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue