Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Storage/Storage/Tools/StorageType+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,13 @@ public extension StorageType {
return firstObject(ofType: OrderStatsV4Interval.self, matching: predicate)
}

/// Retrieves the Stored SiteSummaryStats.
///
func loadSiteSummaryStats(date: String, period: String) -> SiteSummaryStats? {
let predicate = \SiteSummaryStats.date =~ date && \SiteSummaryStats.period =~ period
return firstObject(ofType: SiteSummaryStats.self, matching: predicate)
}

// MARK: - Order Statuses

/// Retrieves all of the Stores OrderStatuses for the provided siteID.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,8 @@ private extension AnalyticsHubViewModel {
let action = StatsActionV4.retrieveSiteSummaryStats(siteID: siteID,
period: period,
quantity: timeRangeSelectionType.quantity,
latestDateToInclude: latestDateToInclude) { result in
latestDateToInclude: latestDateToInclude,
saveInStorage: false) { result in
continuation.resume(with: result)
}
stores.dispatch(action)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ final class AnalyticsHubViewModelTests: XCTestCase {
case let .retrieveTopEarnerStats(_, _, _, _, _, _, _, completion):
let topEarners = TopEarnerStats.fake().copy(items: [.fake()])
completion(.success(topEarners))
case let .retrieveSiteSummaryStats(_, _, _, _, completion):
case let .retrieveSiteSummaryStats(_, _, _, _, _, completion):
let siteStats = SiteSummaryStats.fake().copy(visitors: 30, views: 53)
completion(.success(siteStats))
default:
Expand Down Expand Up @@ -61,7 +61,7 @@ final class AnalyticsHubViewModelTests: XCTestCase {
completion(.failure(NSError(domain: "Test", code: 1)))
case let .retrieveTopEarnerStats(_, _, _, _, _, _, _, completion):
completion(.failure(NSError(domain: "Test", code: 1)))
case let .retrieveSiteSummaryStats(_, _, _, _, completion):
case let .retrieveSiteSummaryStats(_, _, _, _, _, completion):
completion(.failure(NSError(domain: "Test", code: 1)))
default:
break
Expand Down Expand Up @@ -99,7 +99,7 @@ final class AnalyticsHubViewModelTests: XCTestCase {
case let .retrieveTopEarnerStats(_, _, _, _, _, _, _, completion):
let topEarners = TopEarnerStats.fake().copy(items: [.fake()])
completion(.success(topEarners))
case let .retrieveSiteSummaryStats(_, _, _, _, completion):
case let .retrieveSiteSummaryStats(_, _, _, _, _, completion):
let siteStats = SiteSummaryStats.fake()
loadingSessionsCard = vm.sessionsCard
completion(.success(siteStats))
Expand Down
4 changes: 4 additions & 0 deletions Yosemite/Yosemite.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@
CC2C036C262F316600928C9C /* ShippingLabelAccountSettings+ReadOnlyConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC2C036B262F316600928C9C /* ShippingLabelAccountSettings+ReadOnlyConvertible.swift */; };
CC2C0372262F32D800928C9C /* ShippingLabelPaymentMethod+ReadonlyConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC2C0371262F32D800928C9C /* ShippingLabelPaymentMethod+ReadonlyConvertible.swift */; };
CC6A054628773F75002C144E /* OrderMetaData+ReadOnlyConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC6A054528773F75002C144E /* OrderMetaData+ReadOnlyConvertible.swift */; };
CC80E40C294B454A00D5FF45 /* SiteSummaryStats+ReadOnlyConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC80E40B294B454A00D5FF45 /* SiteSummaryStats+ReadOnlyConvertible.swift */; };
CE01014F2368C41600783459 /* Refund+ReadOnlyType.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE01014E2368C41600783459 /* Refund+ReadOnlyType.swift */; };
CE0DB6C0233EB3F300A27E7A /* OrderRefundCondensed+ReadOnlyConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE0DB6BF233EB3F300A27E7A /* OrderRefundCondensed+ReadOnlyConvertible.swift */; };
CE12FBDB221F406100C59248 /* OrderStatus+ReadOnlyConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE12FBDA221F406100C59248 /* OrderStatus+ReadOnlyConvertible.swift */; };
Expand Down Expand Up @@ -768,6 +769,7 @@
CC2C036B262F316600928C9C /* ShippingLabelAccountSettings+ReadOnlyConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ShippingLabelAccountSettings+ReadOnlyConvertible.swift"; sourceTree = "<group>"; };
CC2C0371262F32D800928C9C /* ShippingLabelPaymentMethod+ReadonlyConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ShippingLabelPaymentMethod+ReadonlyConvertible.swift"; sourceTree = "<group>"; };
CC6A054528773F75002C144E /* OrderMetaData+ReadOnlyConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrderMetaData+ReadOnlyConvertible.swift"; sourceTree = "<group>"; };
CC80E40B294B454A00D5FF45 /* SiteSummaryStats+ReadOnlyConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SiteSummaryStats+ReadOnlyConvertible.swift"; sourceTree = "<group>"; };
CE01014E2368C41600783459 /* Refund+ReadOnlyType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Refund+ReadOnlyType.swift"; sourceTree = "<group>"; };
CE0DB6BF233EB3F300A27E7A /* OrderRefundCondensed+ReadOnlyConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrderRefundCondensed+ReadOnlyConvertible.swift"; sourceTree = "<group>"; };
CE12FBDA221F406100C59248 /* OrderStatus+ReadOnlyConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrderStatus+ReadOnlyConvertible.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1293,6 +1295,7 @@
2618707B2540B6A4006522A1 /* ShippingLineTax+ReadOnlyConvertible.swift */,
B505254B20EE6491008090F5 /* Site+ReadOnlyConvertible.swift */,
7492FAD8217FAD1000ED2C69 /* SiteSetting+ReadOnlyConvertible.swift */,
CC80E40B294B454A00D5FF45 /* SiteSummaryStats+ReadOnlyConvertible.swift */,
744A3216216D55F80051439B /* SiteVisitStats+ReadOnlyConvertible.swift */,
744A3217216D55F80051439B /* SiteVisitStatsItem+ReadOnlyConvertible.swift */,
45E4620F2684C63700011BF2 /* StateOfACountry+ReadOnlyConvertible.swift */,
Expand Down Expand Up @@ -1978,6 +1981,7 @@
03FBDA222631521100ACE257 /* CouponAction.swift in Sources */,
CE4FD4562350FD4800A16B31 /* Refund+ReadOnlyConvertible.swift in Sources */,
CE3B7AD92229C3570050FE4B /* OrderStatus+ReadOnlyType.swift in Sources */,
CC80E40C294B454A00D5FF45 /* SiteSummaryStats+ReadOnlyConvertible.swift in Sources */,
026CF62C237D92DC009563D4 /* ProductVariationAttribute+ReadOnlyConvertible.swift in Sources */,
247CE7AB2582DB9300F9D9D1 /* String+Extensions.swift in Sources */,
FE28F6F026844231004465C7 /* UserStore.swift in Sources */,
Expand Down
4 changes: 3 additions & 1 deletion Yosemite/Yosemite/Actions/StatsActionV4.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,13 @@ public enum StatsActionV4: Action {
saveInStorage: Bool,
onCompletion: (Result<TopEarnerStats, Error>) -> Void)

/// Retrieves the site summary stats for the provided site ID, period(s), and date, without saving them to the Storage layer.
/// Retrieves the site summary stats for the provided site ID, period(s), and date.
/// Conditionally saves them to storage, if a single period is retrieved.
///
case retrieveSiteSummaryStats(siteID: Int64,
period: StatGranularity,
quantity: Int,
latestDateToInclude: Date,
saveInStorage: Bool,
onCompletion: (Result<SiteSummaryStats, Error>) -> Void)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Foundation
import Storage


// MARK: - Storage.SiteSummaryStats: ReadOnlyConvertible
//
extension Storage.SiteSummaryStats: ReadOnlyConvertible {

/// Updates the Storage.SiteSummaryStats with the ReadOnly.
///
public func update(with stats: Yosemite.SiteSummaryStats) {
siteID = stats.siteID
date = stats.date
period = stats.period.rawValue
visitors = Int64(stats.visitors)
views = Int64(stats.views)
}

/// Returns a ReadOnly version of the receiver.
///
public func toReadOnly() -> Yosemite.SiteSummaryStats {
SiteSummaryStats(siteID: siteID,
date: date,
period: StatGranularity(rawValue: period) ?? .day,
visitors: Int(visitors),
views: Int(views))
}
}
25 changes: 23 additions & 2 deletions Yosemite/Yosemite/Stores/StatsStoreV4.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,13 @@ public final class StatsStoreV4: Store {
let period,
let quantity,
let latestDateToInclude,
let saveInStorage,
let onCompletion):
retrieveSiteSummaryStats(siteID: siteID,
period: period,
quantity: quantity,
latestDateToInclude: latestDateToInclude,
saveInStorage: saveInStorage,
onCompletion: onCompletion)
}
}
Expand Down Expand Up @@ -189,19 +191,24 @@ private extension StatsStoreV4 {
}
}

/// Retrieves the site summary stats for the provided site ID, period(s), and date, without saving them to the Storage layer.
/// Retrieves the site summary stats for the provided site ID, period(s), and date.
/// Conditionally saves them to storage, if a single period is retrieved.
///
func retrieveSiteSummaryStats(siteID: Int64,
period: StatGranularity,
quantity: Int,
latestDateToInclude: Date,
saveInStorage: Bool,
onCompletion: @escaping (Result<SiteSummaryStats, Error>) -> Void) {
if quantity == 1 {
siteStatsRemote.loadSiteSummaryStats(for: siteID,
period: period,
includingDate: latestDateToInclude) { result in
includingDate: latestDateToInclude) { [weak self] result in
switch result {
case .success(let siteSummaryStats):
if saveInStorage {
self?.upsertStoredSiteSummaryStats(readOnlyStats: siteSummaryStats)
}
onCompletion(.success(siteSummaryStats))
case .failure(let error):
onCompletion(.failure(SiteStatsStoreError(error: error)))
Expand Down Expand Up @@ -486,6 +493,20 @@ extension StatsStoreV4 {
}
}

// MARK: Site summary stats
extension StatsStoreV4 {
/// Updates (OR Inserts) the specified ReadOnly SiteSummaryStats Entity into the Storage Layer.
///
func upsertStoredSiteSummaryStats(readOnlyStats: Networking.SiteSummaryStats) {
assert(Thread.isMainThread)

let storage = storageManager.viewStorage
let storageSiteSummaryStats = storage.loadSiteSummaryStats(date: readOnlyStats.date, period: readOnlyStats.period.rawValue)
?? storage.insertNewObject(ofType: Storage.SiteSummaryStats.self)
storageSiteSummaryStats.update(with: readOnlyStats)
storage.saveIfNeeded()
}
}

// MARK: Convert Leaderboard into TopEarnerStats
//
Expand Down
67 changes: 62 additions & 5 deletions Yosemite/YosemiteTests/Stores/StatsStoreV4Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,8 @@ final class StatsStoreV4Tests: XCTestCase {
let action = StatsActionV4.retrieveSiteSummaryStats(siteID: self.sampleSiteID,
period: .day,
quantity: 1,
latestDateToInclude: DateFormatter.dateFromString(with: "2022-12-09T17:06:55")) { result in
latestDateToInclude: DateFormatter.dateFromString(with: "2022-12-09T17:06:55"),
saveInStorage: false) { result in
promise(result)
}
store.onAction(action)
Expand All @@ -541,7 +542,8 @@ final class StatsStoreV4Tests: XCTestCase {
let action = StatsActionV4.retrieveSiteSummaryStats(siteID: self.sampleSiteID,
period: .month,
quantity: 3,
latestDateToInclude: DateFormatter.dateFromString(with: "2022-12-31T17:06:55")) { _ in
latestDateToInclude: DateFormatter.dateFromString(with: "2022-12-31T17:06:55"),
saveInStorage: false) { _ in
promise(())
}
store.onAction(action)
Expand All @@ -567,7 +569,8 @@ final class StatsStoreV4Tests: XCTestCase {
let action = StatsActionV4.retrieveSiteSummaryStats(siteID: self.sampleSiteID,
period: .month,
quantity: 3,
latestDateToInclude: DateFormatter.dateFromString(with: "2022-12-31T17:06:55")) { result in
latestDateToInclude: DateFormatter.dateFromString(with: "2022-12-31T17:06:55"),
saveInStorage: false) { result in
promise(result)
}
store.onAction(action)
Expand All @@ -591,7 +594,8 @@ final class StatsStoreV4Tests: XCTestCase {
let action = StatsActionV4.retrieveSiteSummaryStats(siteID: self.sampleSiteID,
period: .day,
quantity: 1,
latestDateToInclude: DateFormatter.dateFromString(with: "2022-12-09T17:06:55")) { result in
latestDateToInclude: DateFormatter.dateFromString(with: "2022-12-09T17:06:55"),
saveInStorage: false) { result in
promise(result)
}
store.onAction(action)
Expand All @@ -612,7 +616,8 @@ final class StatsStoreV4Tests: XCTestCase {
let action = StatsActionV4.retrieveSiteSummaryStats(siteID: self.sampleSiteID,
period: .day,
quantity: 1,
latestDateToInclude: DateFormatter.dateFromString(with: "2022-12-09T17:06:55")) { result in
latestDateToInclude: DateFormatter.dateFromString(with: "2022-12-09T17:06:55"),
saveInStorage: false) { result in
promise(result)
}
store.onAction(action)
Expand All @@ -621,6 +626,50 @@ final class StatsStoreV4Tests: XCTestCase {
// Then
XCTAssertTrue(result.isFailure)
}

/// Verifies that `StatsActionV4.retrieveSiteSummaryStats` effectively persists any retrieved SiteSummaryStats.
///
func test_retrieveSiteSummaryStats_effectively_persists_retrieved_stats() {
// Given
let store = StatsStoreV4(dispatcher: dispatcher, storageManager: storageManager, network: network)
network.simulateResponse(requestUrlSuffix: "sites/\(sampleSiteID)/stats/summary/", filename: "site-summary-stats")
XCTAssertEqual(viewStorage.countObjects(ofType: Storage.SiteSummaryStats.self), 0)

// When
let result: Result<Networking.SiteSummaryStats, Error> = waitFor { promise in
let action = StatsActionV4.retrieveSiteSummaryStats(siteID: self.sampleSiteID,
period: .day,
quantity: 1,
latestDateToInclude: DateFormatter.dateFromString(with: "2022-12-09T17:06:55"),
saveInStorage: true) { result in
promise(result)
}
store.onAction(action)
}

// Then
XCTAssertTrue(result.isSuccess)
XCTAssertEqual(viewStorage.countObjects(ofType: Storage.SiteSummaryStats.self), 1)

let readOnlySiteSummaryStats = viewStorage.firstObject(ofType: Storage.SiteSummaryStats.self)?.toReadOnly()
XCTAssertEqual(readOnlySiteSummaryStats, sampleSiteSummaryStats())
}

/// Verifies that `upsertStoredSiteSummaryStats` does not produce duplicate entries.
///
func test_upsertStoredSiteSummaryStats_effectively_updates_preexistant_SiteSummaryStats() {
let statsStore = StatsStoreV4(dispatcher: dispatcher, storageManager: storageManager, network: network)

XCTAssertNil(viewStorage.loadSiteSummaryStats(date: "2022-12-09", period: StatGranularity.day.rawValue))
statsStore.upsertStoredSiteSummaryStats(readOnlyStats: sampleSiteSummaryStats())
XCTAssertEqual(viewStorage.countObjects(ofType: Storage.SiteSummaryStats.self), 1)
statsStore.upsertStoredSiteSummaryStats(readOnlyStats: sampleSiteSummaryStatsMutated())
XCTAssertEqual(viewStorage.countObjects(ofType: Storage.SiteSummaryStats.self), 1)

let expectedSiteSummaryStats = sampleSiteSummaryStatsMutated()
let storageSiteSummaryStats = viewStorage.loadSiteSummaryStats(date: "2022-12-09", period: StatGranularity.day.rawValue)
XCTAssertEqual(storageSiteSummaryStats?.toReadOnly(), expectedSiteSummaryStats)
}
}


Expand Down Expand Up @@ -812,6 +861,14 @@ private extension StatsStoreV4Tests {
views: 123)
}

func sampleSiteSummaryStatsMutated() -> Networking.SiteSummaryStats {
return SiteSummaryStats(siteID: sampleSiteID,
date: "2022-12-09",
period: .day,
visitors: 15,
views: 127)
}

func sampleSiteSummaryStatsQuarter() -> Networking.SiteSummaryStats {
return SiteSummaryStats(siteID: sampleSiteID,
date: "2022-12-09",
Expand Down