mirror of
https://github.com/mas-cli/mas
synced 2024-11-21 19:23:01 +00:00
Merge pull request #572 from rgoldberg/561-itunes-search
Fix iTunes Search API issues
This commit is contained in:
commit
bf627ccd79
8 changed files with 52 additions and 33 deletions
|
@ -10,7 +10,7 @@ import ArgumentParser
|
|||
|
||||
extension Mas {
|
||||
/// Opens app page on MAS Preview. Uses the iTunes Lookup API:
|
||||
/// https://affiliate.itunes.apple.com/resources/documentation/itunes-store-web-service-search-api/#lookup
|
||||
/// https://performance-partners.apple.com/search-api
|
||||
struct Home: ParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
abstract: "Opens MAS Preview app page in a browser"
|
||||
|
|
|
@ -11,7 +11,7 @@ import Foundation
|
|||
|
||||
extension Mas {
|
||||
/// Displays app details. Uses the iTunes Lookup API:
|
||||
/// https://affiliate.itunes.apple.com/resources/documentation/itunes-store-web-service-search-api/#lookup
|
||||
/// https://performance-partners.apple.com/search-api
|
||||
struct Info: ParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
abstract: "Display app information from the Mac App Store"
|
||||
|
|
|
@ -13,7 +13,7 @@ private let masScheme = "macappstore"
|
|||
|
||||
extension Mas {
|
||||
/// Opens app page in MAS app. Uses the iTunes Lookup API:
|
||||
/// https://affiliate.itunes.apple.com/resources/documentation/itunes-store-web-service-search-api/#lookup
|
||||
/// https://performance-partners.apple.com/search-api
|
||||
struct Open: ParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
abstract: "Opens app page in AppStore.app"
|
||||
|
|
|
@ -10,7 +10,7 @@ import ArgumentParser
|
|||
|
||||
extension Mas {
|
||||
/// Search the Mac App Store using the iTunes Search API:
|
||||
/// https://affiliate.itunes.apple.com/resources/documentation/itunes-store-web-service-search-api/
|
||||
/// https://performance-partners.apple.com/search-api
|
||||
struct Search: ParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
abstract: "Search for apps from the Mac App Store"
|
||||
|
|
|
@ -10,7 +10,7 @@ import ArgumentParser
|
|||
|
||||
extension Mas {
|
||||
/// Opens vendor's app page in a browser. Uses the iTunes Lookup API:
|
||||
/// https://affiliate.itunes.apple.com/resources/documentation/itunes-store-web-service-search-api/#lookup
|
||||
/// https://performance-partners.apple.com/search-api
|
||||
struct Vendor: ParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
abstract: "Opens vendor's app page in a browser"
|
||||
|
|
|
@ -19,7 +19,7 @@ class MasStoreSearch: StoreSearch {
|
|||
// into the App Store. Instead, we'll make an educated guess that it matches the currently
|
||||
// selected locale in macOS. This obviously isn't always going to match, but it's probably
|
||||
// better than passing no "country" at all to the iTunes Search API.
|
||||
// https://affiliate.itunes.apple.com/resources/documentation/itunes-store-web-service-search-api/
|
||||
// https://performance-partners.apple.com/search-api
|
||||
private let country: String?
|
||||
private let networkManager: NetworkManager
|
||||
|
||||
|
@ -40,7 +40,7 @@ class MasStoreSearch: StoreSearch {
|
|||
func search(for appName: String) -> Promise<[SearchResult]> {
|
||||
// Search for apps for compatible platforms, in order of preference.
|
||||
// Macs with Apple Silicon can run iPad and iPhone apps.
|
||||
var entities = [Entity.macSoftware]
|
||||
var entities = [Entity.desktopSoftware]
|
||||
if SysCtlSystemCommand.isAppleSilicon {
|
||||
entities += [.iPadSoftware, .iPhoneSoftware]
|
||||
}
|
||||
|
|
|
@ -16,53 +16,73 @@ protocol StoreSearch {
|
|||
}
|
||||
|
||||
enum Entity: String {
|
||||
case desktopSoftware
|
||||
case macSoftware
|
||||
case iPadSoftware
|
||||
case iPhoneSoftware = "software"
|
||||
}
|
||||
|
||||
private enum URLAction {
|
||||
case lookup
|
||||
case search
|
||||
|
||||
var queryItemName: String {
|
||||
switch self {
|
||||
case .lookup:
|
||||
return "id"
|
||||
case .search:
|
||||
return "term"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Common methods
|
||||
extension StoreSearch {
|
||||
/// Builds the search URL for an app.
|
||||
///
|
||||
/// - Parameter appName: MAS app identifier.
|
||||
/// - Returns: URL for the search service or nil if appName can't be encoded.
|
||||
func searchURL(for appName: String, inCountry country: String?, ofEntity entity: Entity = .macSoftware) -> URL? {
|
||||
guard var components = URLComponents(string: "https://itunes.apple.com/search") else {
|
||||
return nil
|
||||
}
|
||||
|
||||
components.queryItems = [
|
||||
URLQueryItem(name: "media", value: "software"),
|
||||
URLQueryItem(name: "entity", value: entity.rawValue),
|
||||
URLQueryItem(name: "term", value: appName),
|
||||
]
|
||||
|
||||
if let country {
|
||||
components.queryItems!.append(URLQueryItem(name: "country", value: country))
|
||||
}
|
||||
|
||||
return components.url
|
||||
/// - Parameter searchTerm: term for which to search in MAS.
|
||||
/// - Returns: URL for the search service or nil if searchTerm can't be encoded.
|
||||
func searchURL(
|
||||
for searchTerm: String,
|
||||
inCountry country: String?,
|
||||
ofEntity entity: Entity = .desktopSoftware
|
||||
) -> URL? {
|
||||
url(.search, searchTerm, inCountry: country, ofEntity: entity)
|
||||
}
|
||||
|
||||
/// Builds the lookup URL for an app.
|
||||
///
|
||||
/// - Parameter appID: MAS app identifier.
|
||||
/// - Returns: URL for the lookup service or nil if appID can't be encoded.
|
||||
func lookupURL(forAppID appID: AppID, inCountry country: String?) -> URL? {
|
||||
guard var components = URLComponents(string: "https://itunes.apple.com/lookup") else {
|
||||
func lookupURL(
|
||||
forAppID appID: AppID,
|
||||
inCountry country: String?,
|
||||
ofEntity entity: Entity = .desktopSoftware
|
||||
) -> URL? {
|
||||
url(.lookup, String(appID), inCountry: country, ofEntity: entity)
|
||||
}
|
||||
|
||||
private func url(
|
||||
_ action: URLAction,
|
||||
_ queryItemValue: String,
|
||||
inCountry country: String?,
|
||||
ofEntity entity: Entity = .desktopSoftware
|
||||
) -> URL? {
|
||||
guard var components = URLComponents(string: "https://itunes.apple.com/\(action)") else {
|
||||
return nil
|
||||
}
|
||||
|
||||
components.queryItems = [
|
||||
URLQueryItem(name: "id", value: "\(appID)"),
|
||||
URLQueryItem(name: "entity", value: "desktopSoftware"),
|
||||
URLQueryItem(name: "media", value: "software"),
|
||||
URLQueryItem(name: "entity", value: entity.rawValue),
|
||||
]
|
||||
|
||||
if let country {
|
||||
components.queryItems!.append(URLQueryItem(name: "country", value: country))
|
||||
}
|
||||
|
||||
components.queryItems!.append(URLQueryItem(name: action.queryItemName, value: queryItemValue))
|
||||
|
||||
return components.url
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,17 +18,16 @@ public class MasStoreSearchSpec: QuickSpec {
|
|||
}
|
||||
describe("url string") {
|
||||
it("contains the app name") {
|
||||
let appName = "myapp"
|
||||
expect {
|
||||
MasStoreSearch().searchURL(for: appName, inCountry: "US")?.absoluteString
|
||||
MasStoreSearch().searchURL(for: "myapp", inCountry: "US")?.absoluteString
|
||||
}
|
||||
== "https://itunes.apple.com/search?media=software&entity=macSoftware&term=\(appName)&country=US"
|
||||
== "https://itunes.apple.com/search?media=software&entity=desktopSoftware&country=US&term=myapp"
|
||||
}
|
||||
it("contains the encoded app name") {
|
||||
expect {
|
||||
MasStoreSearch().searchURL(for: "My App", inCountry: "US")?.absoluteString
|
||||
}
|
||||
== "https://itunes.apple.com/search?media=software&entity=macSoftware&term=My%20App&country=US"
|
||||
== "https://itunes.apple.com/search?media=software&entity=desktopSoftware&country=US&term=My%20App"
|
||||
}
|
||||
}
|
||||
describe("store") {
|
||||
|
|
Loading…
Reference in a new issue