Skip to content

Commit deedd46

Browse files
authored
Merge pull request #8283 from woocommerce/issue/8199-refactore-store-methods
Analytics Hub: Prepare retrieveTopEarnerStats method
2 parents 14bff8e + 586f774 commit deedd46

File tree

6 files changed

+111
-99
lines changed

6 files changed

+111
-99
lines changed

WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewModel.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ final class DashboardViewModel {
9797
latestDateToInclude: latestDateToInclude,
9898
quantity: Constants.topEarnerStatsLimit,
9999
forceRefresh: forceRefresh,
100+
saveInStorage: true,
100101
onCompletion: { result in
101102
switch result {
102103
case .success:
@@ -106,7 +107,9 @@ final class DashboardViewModel {
106107
case .failure(let error):
107108
DDLogError("⛔️ Dashboard (Top Performers) — Error synchronizing top earner stats: \(error)")
108109
}
109-
onCompletion?(result)
110+
111+
let voidResult = result.map { _ in () } // Caller expects no entity in the result.
112+
onCompletion?(voidResult)
110113
})
111114
stores.dispatch(action)
112115
}

WooCommerce/WooCommerceTests/ViewRelated/Dashboard/DashboardViewModelTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ final class DashboardViewModelTests: XCTestCase {
5252
completion(.failure(DotcomError.empty))
5353
} else if case let .retrieveSiteVisitStats(_, _, _, _, completion) = action {
5454
completion(.failure(DotcomError.noRestRoute))
55-
} else if case let .retrieveTopEarnerStats(_, _, _, _, _, _, completion) = action {
55+
} else if case let .retrieveTopEarnerStats(_, _, _, _, _, _, _, completion) = action {
5656
completion(.failure(DotcomError.noRestRoute))
5757
}
5858
}

Yosemite/Yosemite/Actions/StatsActionV4.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,15 @@ public enum StatsActionV4: Action {
3737
latestDateToInclude: Date,
3838
onCompletion: (Result<Void, Error>) -> Void)
3939

40-
/// Synchronizes `TopEarnerStats` for the provided siteID, time range, and date.
40+
/// Retrieves `TopEarnerStats` for the provided siteID, time range, and date.
41+
/// Conditionally saves it to storage.
4142
///
4243
case retrieveTopEarnerStats(siteID: Int64,
4344
timeRange: StatsTimeRangeV4,
4445
earliestDateToInclude: Date,
4546
latestDateToInclude: Date,
4647
quantity: Int,
4748
forceRefresh: Bool,
48-
onCompletion: (Result<Void, Error>) -> Void)
49+
saveInStorage: Bool,
50+
onCompletion: (Result<TopEarnerStats, Error>) -> Void)
4951
}

Yosemite/Yosemite/Model/Mocks/ActionHandlers/MockStatsActionV4Handler.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ struct MockStatsActionV4Handler: MockActionHandler {
1414
retrieveStats(siteID: siteID, timeRange: timeRange, onCompletion: onCompletion)
1515
case .retrieveSiteVisitStats(let siteID, _, let timeRange, _, let onCompletion):
1616
retrieveSiteVisitStats(siteID: siteID, timeRange: timeRange, onCompletion: onCompletion)
17-
case .retrieveTopEarnerStats(let siteID, let timeRange, _, _, _, _, let onCompletion):
17+
case .retrieveTopEarnerStats(let siteID, let timeRange, _, _, _, _, _, let onCompletion):
1818
retrieveTopEarnerStats(siteID: siteID, timeRange: timeRange, onCompletion: onCompletion)
1919
default: unimplementedAction(action: action)
2020
}
@@ -52,19 +52,19 @@ struct MockStatsActionV4Handler: MockActionHandler {
5252
}
5353
}
5454

55-
func retrieveTopEarnerStats(siteID: Int64, timeRange: StatsTimeRangeV4, onCompletion: @escaping (Result<Void, Error>) -> ()) {
55+
func retrieveTopEarnerStats(siteID: Int64, timeRange: StatsTimeRangeV4, onCompletion: @escaping (Result<TopEarnerStats, Error>) -> ()) {
5656
let store = StatsStoreV4(dispatcher: Dispatcher(), storageManager: storageManager, network: NullNetwork())
5757

5858
switch timeRange {
59-
case .today:
60-
success(onCompletion)
61-
case .thisWeek:
62-
success(onCompletion)
63-
case .thisMonth:
64-
store.upsertStoredTopEarnerStats(readOnlyStats: objectGraph.thisMonthTopProducts)
65-
onCompletion(.success(()))
66-
case .thisYear:
67-
success(onCompletion)
59+
case .today:
60+
onCompletion(.success(objectGraph.thisMonthTopProducts))
61+
case .thisWeek:
62+
onCompletion(.success(objectGraph.thisMonthTopProducts))
63+
case .thisMonth:
64+
store.upsertStoredTopEarnerStats(readOnlyStats: objectGraph.thisMonthTopProducts)
65+
onCompletion(.success(objectGraph.thisMonthTopProducts))
66+
case .thisYear:
67+
onCompletion(.success(objectGraph.thisMonthTopProducts))
6868
}
6969
}
7070
}

Yosemite/Yosemite/Stores/StatsStoreV4.swift

Lines changed: 72 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,15 @@ public final class StatsStoreV4: Store {
8080
let latestDateToInclude,
8181
let quantity,
8282
let forceRefresh,
83+
let saveInStorage,
8384
let onCompletion):
8485
retrieveTopEarnerStats(siteID: siteID,
8586
timeRange: timeRange,
8687
earliestDateToInclude: earliestDateToInclude,
8788
latestDateToInclude: latestDateToInclude,
8889
quantity: quantity,
8990
forceRefresh: forceRefresh,
91+
saveInStorage: saveInStorage,
9092
onCompletion: onCompletion)
9193
}
9294
}
@@ -178,36 +180,45 @@ private extension StatsStoreV4 {
178180
}
179181

180182
/// Retrieves the top earner stats associated with the provided Site ID (if any!).
183+
/// Saves to storage if required,
181184
///
182185
func retrieveTopEarnerStats(siteID: Int64,
183186
timeRange: StatsTimeRangeV4,
184187
earliestDateToInclude: Date,
185188
latestDateToInclude: Date,
186189
quantity: Int,
187190
forceRefresh: Bool,
188-
onCompletion: @escaping (Result<Void, Error>) -> Void) {
191+
saveInStorage: Bool,
192+
onCompletion: @escaping (Result<TopEarnerStats, Error>) -> Void) {
189193
Task { @MainActor in
190194
do {
191-
try await loadTopEarnerStats(siteID: siteID,
192-
timeRange: timeRange,
193-
earliestDateToInclude: earliestDateToInclude,
194-
latestDateToInclude: latestDateToInclude,
195-
quantity: quantity,
196-
forceRefresh: forceRefresh)
197-
onCompletion(.success(()))
195+
let topEarnersStats = try await loadTopEarnerStats(siteID: siteID,
196+
timeRange: timeRange,
197+
earliestDateToInclude: earliestDateToInclude,
198+
latestDateToInclude: latestDateToInclude,
199+
quantity: quantity,
200+
forceRefresh: forceRefresh)
201+
if saveInStorage {
202+
upsertStoredTopEarnerStats(readOnlyStats: topEarnersStats)
203+
}
204+
onCompletion(.success(topEarnersStats))
198205
} catch {
199-
if let error = error as? DotcomError, error == .noRestRoute {
200-
let result = await Result {
201-
try await loadTopEarnerStatsWithDeprecatedAPI(siteID: siteID,
202-
timeRange: timeRange,
203-
earliestDateToInclude: earliestDateToInclude,
204-
latestDateToInclude: latestDateToInclude,
205-
quantity: quantity,
206-
forceRefresh: forceRefresh)
207-
}
206+
guard let error = error as? DotcomError, error == .noRestRoute else {
207+
return onCompletion(.failure(error))
208+
}
208209

209-
onCompletion(result)
210-
} else {
210+
do {
211+
let topEarnersStats = try await loadTopEarnerStatsWithDeprecatedAPI(siteID: siteID,
212+
timeRange: timeRange,
213+
earliestDateToInclude: earliestDateToInclude,
214+
latestDateToInclude: latestDateToInclude,
215+
quantity: quantity,
216+
forceRefresh: forceRefresh)
217+
if saveInStorage {
218+
upsertStoredTopEarnerStats(readOnlyStats: topEarnersStats)
219+
}
220+
onCompletion(.success(topEarnersStats))
221+
} catch {
211222
onCompletion(.failure(error))
212223
}
213224
}
@@ -220,8 +231,8 @@ private extension StatsStoreV4 {
220231
earliestDateToInclude: Date,
221232
latestDateToInclude: Date,
222233
quantity: Int,
223-
forceRefresh: Bool) async throws {
224-
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) -> Void in
234+
forceRefresh: Bool) async throws -> TopEarnerStats {
235+
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<TopEarnerStats, Error>) -> Void in
225236
let dateFormatter = DateFormatter.Defaults.iso8601WithoutTimeZone
226237
let earliestDate = dateFormatter.string(from: earliestDateToInclude)
227238
let latestDate = dateFormatter.string(from: latestDateToInclude)
@@ -237,16 +248,12 @@ private extension StatsStoreV4 {
237248

238249
switch result {
239250
case .success(let leaderboards):
240-
self.convertAndStoreLeaderboardsIntoTopEarners(siteID: siteID,
241-
granularity: timeRange.topEarnerStatsGranularity,
242-
date: latestDateToInclude,
243-
leaderboards: leaderboards,
244-
quantity: quantity) { result in
245-
if case let .failure(error) = result {
246-
continuation.resume(throwing: error)
247-
} else {
248-
continuation.resume(returning: (()))
249-
}
251+
self.convertLeaderboardsIntoTopEarners(siteID: siteID,
252+
granularity: timeRange.topEarnerStatsGranularity,
253+
date: latestDateToInclude,
254+
leaderboards: leaderboards,
255+
quantity: quantity) { result in
256+
continuation.resume(with: result)
250257
}
251258
case .failure(let error):
252259
continuation.resume(throwing: error)
@@ -261,8 +268,8 @@ private extension StatsStoreV4 {
261268
earliestDateToInclude: Date,
262269
latestDateToInclude: Date,
263270
quantity: Int,
264-
forceRefresh: Bool) async throws {
265-
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) -> Void in
271+
forceRefresh: Bool) async throws -> TopEarnerStats {
272+
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<TopEarnerStats, Error>) -> Void in
266273
let dateFormatter = DateFormatter.Defaults.iso8601WithoutTimeZone
267274
let earliestDate = dateFormatter.string(from: earliestDateToInclude)
268275
let latestDate = dateFormatter.string(from: latestDateToInclude)
@@ -278,16 +285,12 @@ private extension StatsStoreV4 {
278285

279286
switch result {
280287
case .success(let leaderboards):
281-
self.convertAndStoreLeaderboardsIntoTopEarners(siteID: siteID,
282-
granularity: timeRange.topEarnerStatsGranularity,
283-
date: latestDateToInclude,
284-
leaderboards: leaderboards,
285-
quantity: quantity) { result in
286-
if case let .failure(error) = result {
287-
continuation.resume(throwing: error)
288-
} else {
289-
continuation.resume(returning: ())
290-
}
288+
self.convertLeaderboardsIntoTopEarners(siteID: siteID,
289+
granularity: timeRange.topEarnerStatsGranularity,
290+
date: latestDateToInclude,
291+
leaderboards: leaderboards,
292+
quantity: quantity) { result in
293+
continuation.resume(with: result)
291294
}
292295
case .failure(let error):
293296
continuation.resume(throwing: error)
@@ -435,15 +438,15 @@ extension StatsStoreV4 {
435438
//
436439
private extension StatsStoreV4 {
437440

438-
/// Converts and stores a top-product `leaderboard` into a `StatsTopEarner`
441+
/// Converts a top-product `leaderboard` into a `StatsTopEarner`
439442
/// Since a `leaderboard` does not contain the necessary product information, this method fetches the related product before starting the conversion.
440443
///
441-
func convertAndStoreLeaderboardsIntoTopEarners(siteID: Int64,
442-
granularity: StatGranularity,
443-
date: Date,
444-
leaderboards: [Leaderboard],
445-
quantity: Int,
446-
onCompletion: @escaping (Result<Void, Error>) -> Void) {
444+
func convertLeaderboardsIntoTopEarners(siteID: Int64,
445+
granularity: StatGranularity,
446+
date: Date,
447+
leaderboards: [Leaderboard],
448+
quantity: Int,
449+
onCompletion: @escaping (Result<TopEarnerStats, Error>) -> Void) {
447450

448451
// Find the top products leaderboard by its ID
449452
guard let topProducts = leaderboards.first(where: { $0.id == Constants.topProductsID }) else {
@@ -457,13 +460,13 @@ private extension StatsStoreV4 {
457460

458461
switch topProductsResult {
459462
case .success(let products):
460-
self.mergeAndStoreTopProductsAndStoredProductsIntoTopEarners(siteID: siteID,
461-
granularity: granularity,
462-
date: date,
463-
topProducts: topProducts,
464-
storedProducts: products,
465-
quantityLimit: quantity)
466-
onCompletion(.success(()))
463+
let topEarners = self.mergeTopProductsAndStoredProductsIntoTopEarners(siteID: siteID,
464+
granularity: granularity,
465+
date: date,
466+
topProducts: topProducts,
467+
storedProducts: products,
468+
quantityLimit: quantity)
469+
onCompletion(.success((topEarners)))
467470
case .failure(let error):
468471
onCompletion(.failure(error))
469472
}
@@ -507,24 +510,21 @@ private extension StatsStoreV4 {
507510
return products.map { $0.toReadOnly() }
508511
}
509512

510-
/// Merges and stores a top-product leaderboard with an array of stored products into a `TopEarnerStats` object
513+
/// Merges a top-product leaderboard with an array of stored products into a `TopEarnerStats` object
511514
///
512-
func mergeAndStoreTopProductsAndStoredProductsIntoTopEarners(siteID: Int64,
513-
granularity: StatGranularity,
514-
date: Date,
515-
topProducts: Leaderboard,
516-
storedProducts: [Product],
517-
quantityLimit: Int) {
515+
func mergeTopProductsAndStoredProductsIntoTopEarners(siteID: Int64,
516+
granularity: StatGranularity,
517+
date: Date,
518+
topProducts: Leaderboard,
519+
storedProducts: [Product],
520+
quantityLimit: Int) -> TopEarnerStats {
518521
let statsDate = Self.buildDateString(from: date, with: granularity)
519522
let statsItems = LeaderboardStatsConverter.topEarnerStatsItems(from: topProducts, using: storedProducts)
520-
let stats = TopEarnerStats(siteID: siteID,
521-
date: statsDate,
522-
granularity: granularity,
523-
limit: String(quantityLimit),
524-
items: statsItems
525-
)
526-
527-
upsertStoredTopEarnerStats(readOnlyStats: stats)
523+
return TopEarnerStats(siteID: siteID,
524+
date: statsDate,
525+
granularity: granularity,
526+
limit: String(quantityLimit),
527+
items: statsItems)
528528
}
529529
}
530530

0 commit comments

Comments
 (0)