Skip to content

Commit 192aead

Browse files
Merge pull request #297 from woocommerce/develop
Merging 0.7 into Master
2 parents 9f360a8 + 3e7629d commit 192aead

File tree

96 files changed

+3078
-478
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+3078
-478
lines changed

Networking/Networking.xcodeproj/project.pbxproj

Lines changed: 47 additions & 3 deletions
Large diffs are not rendered by default.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import Foundation
2+
3+
4+
/// Mapper: TopEarnerStats
5+
///
6+
class TopEarnerStatsMapper: Mapper {
7+
8+
/// (Attempts) to convert a dictionary into an TopEarnerStats entity.
9+
///
10+
func map(response: Data) throws -> TopEarnerStats {
11+
let decoder = JSONDecoder()
12+
return try decoder.decode(TopEarnerStats.self, from: response)
13+
}
14+
}

Networking/Networking/Model/Account.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,22 @@ private extension Account {
5050
case gravatarUrl = "avatar_URL"
5151
}
5252
}
53+
54+
55+
// MARK: - Comparable Conformance
56+
//
57+
extension Account: Comparable {
58+
public static func == (lhs: Account, rhs: Account) -> Bool {
59+
return lhs.userID == rhs.userID &&
60+
lhs.displayName == rhs.displayName &&
61+
lhs.email == rhs.email &&
62+
lhs.username == rhs.username &&
63+
lhs.gravatarUrl == rhs.gravatarUrl
64+
}
65+
66+
public static func < (lhs: Account, rhs: Account) -> Bool {
67+
return lhs.userID < rhs.userID ||
68+
(lhs.userID == rhs.userID && lhs.username < rhs.username) ||
69+
(lhs.userID == rhs.userID && lhs.username == rhs.username && lhs.displayName < rhs.displayName)
70+
}
71+
}

Networking/Networking/Model/Order.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,8 @@ extension Order: Comparable {
183183
lhs.paymentMethodTitle == rhs.paymentMethodTitle &&
184184
lhs.billingAddress == rhs.billingAddress &&
185185
lhs.shippingAddress == rhs.shippingAddress &&
186-
lhs.coupons == rhs.coupons &&
186+
lhs.coupons.count == rhs.coupons.count &&
187+
lhs.coupons.sorted() == rhs.coupons.sorted() &&
187188
lhs.items.count == rhs.items.count &&
188189
lhs.items.sorted() == rhs.items.sorted()
189190
}

Networking/Networking/Model/Site.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,26 @@ public struct Site: Decodable {
5858
}
5959

6060

61+
// MARK: - Comparable Conformance
62+
//
63+
extension Site: Comparable {
64+
public static func == (lhs: Site, rhs: Site) -> Bool {
65+
return lhs.siteID == rhs.siteID &&
66+
lhs.name == rhs.name &&
67+
lhs.description == rhs.description &&
68+
lhs.url == rhs.url &&
69+
lhs.isWooCommerceActive == rhs.isWooCommerceActive &&
70+
lhs.isWordPressStore == rhs.isWordPressStore
71+
}
72+
73+
public static func < (lhs: Site, rhs: Site) -> Bool {
74+
return lhs.siteID < rhs.siteID ||
75+
(lhs.siteID == rhs.siteID && lhs.name < rhs.name) ||
76+
(lhs.siteID == rhs.siteID && lhs.name == rhs.name && lhs.description < rhs.description)
77+
}
78+
}
79+
80+
6181
/// Defines all of the Site CodingKeys.
6282
///
6383
private extension Site {
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import Foundation
2+
3+
4+
/// Represents Top Earner (aka top performer) stats over a specific period.
5+
///
6+
public struct TopEarnerStats: Decodable {
7+
public let date: String
8+
public let granularity: StatGranularity
9+
public let limit: String
10+
public let items: [TopEarnerStatsItem]?
11+
12+
13+
/// The public initializer for top earner stats.
14+
///
15+
public init(from decoder: Decoder) throws {
16+
let container = try decoder.container(keyedBy: CodingKeys.self)
17+
18+
let date = try container.decode(String.self, forKey: .date)
19+
let granularity = try container.decode(StatGranularity.self, forKey: .unit)
20+
let limit = try container.decode(String.self, forKey: .limit)
21+
let items = try container.decode([TopEarnerStatsItem].self, forKey: .items)
22+
23+
self.init(date: date, granularity: granularity, limit: limit, items: items)
24+
}
25+
26+
27+
/// TopEarnerStats struct initializer.
28+
///
29+
public init(date: String, granularity: StatGranularity, limit: String, items: [TopEarnerStatsItem]?) {
30+
self.date = date
31+
self.granularity = granularity
32+
self.limit = limit
33+
self.items = items
34+
}
35+
}
36+
37+
38+
/// Defines all of the TopEarnerStats CodingKeys.
39+
///
40+
private extension TopEarnerStats {
41+
enum CodingKeys: String, CodingKey {
42+
case date = "date"
43+
case unit = "unit"
44+
case limit = "limit"
45+
case items = "data"
46+
}
47+
}
48+
49+
50+
// MARK: - Comparable Conformance
51+
//
52+
extension TopEarnerStats: Comparable {
53+
public static func == (lhs: TopEarnerStats, rhs: TopEarnerStats) -> Bool {
54+
return lhs.date == rhs.date &&
55+
lhs.granularity == rhs.granularity &&
56+
lhs.limit == rhs.limit &&
57+
lhs.items?.count == rhs.items?.count &&
58+
lhs.items?.sorted() == rhs.items?.sorted()
59+
}
60+
61+
public static func < (lhs: TopEarnerStats, rhs: TopEarnerStats) -> Bool {
62+
return lhs.date < rhs.date ||
63+
(lhs.date == rhs.date && lhs.limit < rhs.limit)
64+
}
65+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import Foundation
2+
3+
4+
/// Represents a single top earner stat for a specific period.
5+
///
6+
public struct TopEarnerStatsItem: Decodable {
7+
8+
/// Product ID
9+
///
10+
public let productID: Int
11+
12+
/// Product name
13+
///
14+
public let productName: String
15+
16+
/// Quantity sold
17+
///
18+
public let quantity: Int
19+
20+
/// Average price of item
21+
///
22+
public let price: Double
23+
24+
/// Total revenue from product
25+
///
26+
public let total: Double
27+
28+
/// Currency
29+
///
30+
public let currency: String
31+
32+
/// Image URL for product
33+
///
34+
public let imageUrl: String?
35+
36+
37+
/// Designated Initializer.
38+
///
39+
public init(productID: Int, productName: String, quantity: Int, price: Double, total: Double, currency: String, imageUrl: String?) {
40+
self.productID = productID
41+
self.productName = productName
42+
self.quantity = quantity
43+
self.price = price
44+
self.total = total
45+
self.currency = currency
46+
self.imageUrl = imageUrl
47+
}
48+
}
49+
50+
51+
/// Defines all of the TopEarnerStatsItem CodingKeys.
52+
///
53+
private extension TopEarnerStatsItem {
54+
enum CodingKeys: String, CodingKey {
55+
case productID = "ID"
56+
case productName = "name"
57+
case total = "total"
58+
case quantity = "quantity"
59+
case price = "price"
60+
case imageUrl = "image"
61+
case currency = "currency"
62+
}
63+
}
64+
65+
66+
// MARK: - Comparable Conformance
67+
//
68+
extension TopEarnerStatsItem: Comparable {
69+
public static func == (lhs: TopEarnerStatsItem, rhs: TopEarnerStatsItem) -> Bool {
70+
return lhs.productID == rhs.productID &&
71+
lhs.productName == rhs.productName &&
72+
lhs.quantity == rhs.quantity &&
73+
lhs.price == rhs.price &&
74+
lhs.total == rhs.total &&
75+
lhs.currency == rhs.currency &&
76+
lhs.imageUrl == rhs.imageUrl
77+
}
78+
79+
public static func < (lhs: TopEarnerStatsItem, rhs: TopEarnerStatsItem) -> Bool {
80+
return lhs.quantity < rhs.quantity ||
81+
(lhs.quantity == rhs.quantity && lhs.productName < rhs.productName)
82+
}
83+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import Foundation
2+
import Alamofire
3+
4+
5+
/// TopEarnersStats: Remote Endpoints
6+
///
7+
public class TopEarnersStatsRemote: Remote {
8+
9+
/// Fetch the top earner (aka Top Performer) stats ( stats for a given site for the current day, week, month, or year (depends on the given granularity of the `unit` parameter).
10+
///
11+
/// - Parameters:
12+
/// - siteID: The site ID
13+
/// - unit: Defines the granularity of the stats we are fetching (one of 'day', 'week', 'month', or 'year')
14+
/// - latestDateToInclude: The latest date to include in the results (see Note below)
15+
/// - limit: Maximum number of `unit`s to fetch
16+
/// - completion: Closure to be executed upon completion.
17+
///
18+
/// Note: `latestDateToInclude` date string must be formatted appropriately given the `unit` param. See: `DateFormatter.Stats` extension for some helper funcs.
19+
///
20+
public func loadTopEarnersStats(for siteID: Int, unit: StatGranularity, latestDateToInclude: String, limit: Int, completion: @escaping (TopEarnerStats?, Error?) -> Void) {
21+
let path = "\(Constants.sitesPath)/\(siteID)/\(Constants.topEarnersStatsPath)/"
22+
let parameters = [ParameterKeys.unit: unit.rawValue,
23+
ParameterKeys.date: latestDateToInclude,
24+
ParameterKeys.limit: String(limit)]
25+
let request = DotcomRequest(wordpressApiVersion: .wpcomMark2, method: .get, path: path, parameters: parameters)
26+
let mapper = TopEarnerStatsMapper()
27+
enqueue(request, mapper: mapper, completion: completion)
28+
}
29+
}
30+
31+
32+
// MARK: - Constants!
33+
//
34+
private extension TopEarnersStatsRemote {
35+
enum Constants {
36+
static let sitesPath: String = "sites"
37+
static let topEarnersStatsPath: String = "stats/top-earners"
38+
}
39+
40+
enum ParameterKeys {
41+
static let unit: String = "unit"
42+
static let date: String = "date"
43+
static let limit: String = "limit"
44+
}
45+
}

0 commit comments

Comments
 (0)