Skip to content

Commit cdb08d6

Browse files
committed
Persist last incremental sync date in Site and replace usage in POSCatalogIncrementalSyncService.
1 parent a86d762 commit cdb08d6

File tree

5 files changed

+193
-15
lines changed

5 files changed

+193
-15
lines changed

Modules/Sources/Yosemite/Tools/POS/POSCatalogIncrementalSyncService.swift

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ public final class POSCatalogIncrementalSyncService: POSCatalogIncrementalSyncSe
2121
private let syncRemote: POSCatalogSyncRemoteProtocol
2222
private let batchSize: Int
2323
private let persistenceService: POSCatalogPersistenceServiceProtocol
24-
private var lastIncrementalSyncDates: [Int64: Date] = [:]
2524
private let batchedLoader: BatchedRequestLoader
2625

2726
public convenience init?(credentials: Credentials?, batchSize: Int = 1, grdbManager: GRDBManagerProtocol) {
@@ -45,7 +44,7 @@ public final class POSCatalogIncrementalSyncService: POSCatalogIncrementalSyncSe
4544
// MARK: - Protocol Conformance
4645

4746
public func startIncrementalSync(for siteID: Int64, lastFullSyncDate: Date) async throws {
48-
let modifiedAfter = lastIncrementalSyncDates[siteID] ?? lastFullSyncDate
47+
let modifiedAfter = try await latestSyncDate(siteID: siteID, lastFullSyncDate: lastFullSyncDate)
4948

5049
DDLogInfo("🔄 Starting incremental catalog sync for site ID: \(siteID), modifiedAfter: \(modifiedAfter)")
5150

@@ -57,8 +56,7 @@ public final class POSCatalogIncrementalSyncService: POSCatalogIncrementalSyncSe
5756
try await persistenceService.persistIncrementalCatalogData(catalog, siteID: siteID)
5857
DDLogInfo("✅ Persisted \(catalog.products.count) products and \(catalog.variations.count) variations to database for siteID \(siteID)")
5958

60-
// TODO: WOOMOB-1289 - replace with store settings persistence
61-
lastIncrementalSyncDates[siteID] = syncStartDate
59+
try await persistenceService.updateSite(.init(siteID: siteID, lastIncrementalSyncDate: syncStartDate))
6260
DDLogInfo("✅ Updated last incremental sync date to \(syncStartDate) for siteID \(siteID)")
6361
} catch {
6462
DDLogError("❌ Failed to sync and persist catalog incrementally: \(error)")
@@ -86,3 +84,11 @@ private extension POSCatalogIncrementalSyncService {
8684
return POSCatalog(products: products, variations: variations)
8785
}
8886
}
87+
88+
// MARK: - Sync date
89+
90+
private extension POSCatalogIncrementalSyncService {
91+
func latestSyncDate(siteID: Int64, lastFullSyncDate: Date) async throws -> Date {
92+
try await persistenceService.loadSite(siteID: siteID)?.lastIncrementalSyncDate ?? lastFullSyncDate
93+
}
94+
}

Modules/Sources/Yosemite/Tools/POS/POSCatalogPersistenceService.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
import Foundation
33
import Storage
44

5+
enum POSCatalogPersistenceError: Error, Equatable {
6+
case siteNotFound(siteID: Int64)
7+
}
8+
59
protocol POSCatalogPersistenceServiceProtocol {
610
/// Clears existing data and persists new catalog data
711
/// - Parameters:
@@ -14,6 +18,15 @@ protocol POSCatalogPersistenceServiceProtocol {
1418
/// - catalog: The catalog difference to persist
1519
/// - siteID: The site ID to associate the catalog with
1620
func persistIncrementalCatalogData(_ catalog: POSCatalog, siteID: Int64) async throws
21+
22+
/// Loads the POS site for the given site ID
23+
/// - Parameter siteID: The site ID to load the POSSite for
24+
/// - Returns: The loaded POSSite or nil if not found in storage
25+
func loadSite(siteID: Int64) async throws -> POSSite?
26+
27+
/// Updates the PersistedSite based on POSSite data
28+
/// - Parameter site: The POSSite containing the updated data
29+
func updateSite(_ site: POSSite) async throws
1730
}
1831

1932
final class POSCatalogPersistenceService: POSCatalogPersistenceServiceProtocol {
@@ -138,6 +151,23 @@ final class POSCatalogPersistenceService: POSCatalogPersistenceServiceProtocol {
138151
"\(variationImageCount) variation images, \(variationAttributeCount) variation attributes")
139152
}
140153
}
154+
155+
func loadSite(siteID: Int64) async throws -> POSSite? {
156+
try await grdbManager.databaseConnection.read { db in
157+
try PersistedSite.filter(key: siteID).fetchOne(db)?.toPOSSite()
158+
}
159+
}
160+
161+
func updateSite(_ site: POSSite) async throws {
162+
try await grdbManager.databaseConnection.write { db in
163+
guard try PersistedSite.filter(key: site.siteID).fetchOne(db) != nil else {
164+
throw POSCatalogPersistenceError.siteNotFound(siteID: site.siteID)
165+
}
166+
167+
let persistedSite = PersistedSite(from: site)
168+
try persistedSite.update(db)
169+
}
170+
}
141171
}
142172

143173
private extension POSCatalog {

Modules/Tests/YosemiteTests/Tools/POS/POSCatalogFullSyncServiceTests.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,4 +184,8 @@ private final class MockPOSCatalogPersistenceService: POSCatalogPersistenceServi
184184
}
185185

186186
func persistIncrementalCatalogData(_ catalog: POSCatalog, siteID: Int64) async throws {}
187+
188+
func loadSite(siteID: Int64) async throws -> POSSite? { nil }
189+
190+
func updateSite(_ site: POSSite) async throws {}
187191
}

Modules/Tests/YosemiteTests/Tools/POS/POSCatalogIncrementalSyncServiceTests.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ private final class MockPOSCatalogPersistenceService: POSCatalogPersistenceServi
183183
private(set) var persistIncrementalCatalogDataLastPersistedSiteID: Int64?
184184
var persistIncrementalCatalogDataError: Error?
185185

186+
private var storedSites: [Int64: POSSite] = [:]
187+
186188
func replaceAllCatalogData(_ catalog: POSCatalog, siteID: Int64) async throws {}
187189

188190
func persistIncrementalCatalogData(_ catalog: POSCatalog, siteID: Int64) async throws {
@@ -193,4 +195,12 @@ private final class MockPOSCatalogPersistenceService: POSCatalogPersistenceServi
193195
throw error
194196
}
195197
}
198+
199+
func loadSite(siteID: Int64) async throws -> POSSite? {
200+
storedSites[siteID]
201+
}
202+
203+
func updateSite(_ site: POSSite) async throws {
204+
storedSites[site.siteID] = site
205+
}
196206
}

Modules/Tests/YosemiteTests/Tools/POS/POSCatalogPersistenceServiceTests.swift

Lines changed: 139 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ struct POSCatalogPersistenceServiceTests {
88
private let sut: POSCatalogPersistenceService
99
private let sampleSiteID: Int64 = 134
1010

11+
private var db: GRDBDatabaseConnection {
12+
grdbManager.databaseConnection
13+
}
14+
1115
init() throws {
1216
self.grdbManager = try GRDBManager()
1317
self.sut = POSCatalogPersistenceService(grdbManager: grdbManager)
@@ -32,7 +36,6 @@ struct POSCatalogPersistenceServiceTests {
3236
try await sut.replaceAllCatalogData(catalog, siteID: sampleSiteID)
3337

3438
// Then
35-
let db = grdbManager.databaseConnection
3639
try await db.read { db in
3740
let siteCount = try PersistedSite.fetchCount(db)
3841
let productCount = try PersistedProduct.fetchCount(db)
@@ -61,7 +64,6 @@ struct POSCatalogPersistenceServiceTests {
6164
try await sut.replaceAllCatalogData(catalog, siteID: sampleSiteID)
6265

6366
// Then
64-
let db = grdbManager.databaseConnection
6567
try await db.read { db in
6668
let imageCount = try PersistedProductImage.fetchCount(db)
6769
let attributeCount = try PersistedProductAttribute.fetchCount(db)
@@ -86,7 +88,6 @@ struct POSCatalogPersistenceServiceTests {
8688
try await sut.replaceAllCatalogData(catalog, siteID: sampleSiteID)
8789

8890
// Then
89-
let db = grdbManager.databaseConnection
9091
try await db.read { db in
9192
let imageCount = try PersistedProductVariationImage.fetchCount(db)
9293
let attributeCount = try PersistedProductVariationAttribute.fetchCount(db)
@@ -115,7 +116,6 @@ struct POSCatalogPersistenceServiceTests {
115116
try await sut.replaceAllCatalogData(catalog, siteID: sampleSiteID)
116117

117118
// Then - should not fail and should handle duplicates
118-
let db = grdbManager.databaseConnection
119119
try await db.read { db in
120120
let productCount = try PersistedProduct.fetchCount(db)
121121
let imageCount = try PersistedProductImage.fetchCount(db)
@@ -143,7 +143,6 @@ struct POSCatalogPersistenceServiceTests {
143143
try await sut.replaceAllCatalogData(newCatalog, siteID: sampleSiteID)
144144

145145
// Then - should have only new data
146-
let db = grdbManager.databaseConnection
147146
try await db.read { db in
148147
let productCount = try PersistedProduct.fetchCount(db)
149148
let variationCount = try PersistedProductVariation.fetchCount(db)
@@ -175,7 +174,6 @@ struct POSCatalogPersistenceServiceTests {
175174
try await sut.replaceAllCatalogData(emptyCatalog, siteID: sampleSiteID)
176175

177176
// Then - all related data should be gone
178-
let db = grdbManager.databaseConnection
179177
try await db.read { db in
180178
let productCount = try PersistedProduct.fetchCount(db)
181179
let imageCount = try PersistedProductImage.fetchCount(db)
@@ -208,7 +206,6 @@ struct POSCatalogPersistenceServiceTests {
208206
try await sut.replaceAllCatalogData(catalogWithoutVariations, siteID: sampleSiteID)
209207

210208
// Then - variation and its related data should be gone
211-
let db = grdbManager.databaseConnection
212209
try await db.read { db in
213210
let productCount = try PersistedProduct.fetchCount(db)
214211
let variationCount = try PersistedProductVariation.fetchCount(db)
@@ -236,7 +233,6 @@ struct POSCatalogPersistenceServiceTests {
236233
try await sut.persistIncrementalCatalogData(catalog, siteID: sampleSiteID)
237234

238235
// Then
239-
let db = grdbManager.databaseConnection
240236
try await db.read { db in
241237
let siteCount = try PersistedSite.fetchCount(db)
242238
let productCount = try PersistedProduct.fetchCount(db)
@@ -363,7 +359,6 @@ struct POSCatalogPersistenceServiceTests {
363359
try await sut.persistIncrementalCatalogData(catalog, siteID: sampleSiteID)
364360

365361
// Then
366-
let db = grdbManager.databaseConnection
367362
try await db.read { db in
368363
let siteCount = try PersistedSite.fetchCount(db)
369364
let variationCount = try PersistedProductVariation.fetchCount(db)
@@ -480,19 +475,152 @@ struct POSCatalogPersistenceServiceTests {
480475
#expect(variations[1].id == 2)
481476
}
482477
}
478+
479+
// MARK: - Site Management Tests
480+
481+
@Test func loadSite_returns_nil_when_site_does_not_exist() async throws {
482+
// When
483+
let result = try await sut.loadSite(siteID: 999)
484+
485+
// Then
486+
#expect(result == nil)
487+
}
488+
489+
@Test func loadSite_returns_site_when_site_exists() async throws {
490+
// Given
491+
let siteID: Int64 = 123
492+
let lastSyncDate = Date(timeIntervalSince1970: 1000)
493+
let site = POSSite(siteID: siteID, lastIncrementalSyncDate: lastSyncDate)
494+
try await insertSite(site)
495+
496+
// When
497+
let result = try await sut.loadSite(siteID: siteID)
498+
499+
// Then
500+
let loadedSite = try #require(result)
501+
#expect(loadedSite.siteID == siteID)
502+
#expect(loadedSite.lastIncrementalSyncDate == lastSyncDate)
503+
}
504+
505+
@Test func loadSite_returns_site_with_nil_sync_date_when_no_sync_date_stored() async throws {
506+
// Given
507+
let siteID: Int64 = 456
508+
let site = POSSite(siteID: siteID, lastIncrementalSyncDate: nil)
509+
try await insertSite(site)
510+
511+
// When
512+
let result = try await sut.loadSite(siteID: siteID)
513+
514+
// Then
515+
let loadedSite = try #require(result)
516+
#expect(loadedSite.siteID == siteID)
517+
#expect(loadedSite.lastIncrementalSyncDate == nil)
518+
}
519+
520+
@Test func updateSite_throws_error_when_site_does_not_exist() async throws {
521+
// Given
522+
let siteID: Int64 = 789
523+
let lastSyncDate = Date(timeIntervalSince1970: 2000)
524+
let site = POSSite(siteID: siteID, lastIncrementalSyncDate: lastSyncDate)
525+
526+
// When/Then
527+
await #expect(throws: POSCatalogPersistenceError.siteNotFound(siteID: siteID)) {
528+
try await sut.updateSite(site)
529+
}
530+
531+
// And verify no site was created
532+
try await db.read { db in
533+
let siteCount = try PersistedSite.fetchCount(db)
534+
#expect(siteCount == 0)
535+
}
536+
}
537+
538+
@Test func updateSite_updates_existing_site() async throws {
539+
// Given - create initial site
540+
let siteID: Int64 = 101112
541+
let initialSyncDate = Date(timeIntervalSince1970: 1500)
542+
let initialSite = POSSite(siteID: siteID, lastIncrementalSyncDate: initialSyncDate)
543+
try await insertSite(initialSite)
544+
545+
// When - update with new sync date
546+
let updatedSyncDate = Date(timeIntervalSince1970: 3000)
547+
let updatedSite = POSSite(siteID: siteID, lastIncrementalSyncDate: updatedSyncDate)
548+
try await sut.updateSite(updatedSite)
549+
550+
// Then
551+
try await db.read { db in
552+
let siteCount = try PersistedSite.fetchCount(db)
553+
#expect(siteCount == 1)
554+
555+
let persistedSite = try PersistedSite.fetchOne(db)
556+
#expect(persistedSite?.id == siteID)
557+
#expect(persistedSite?.posLastIncrementalSyncDate == updatedSyncDate)
558+
}
559+
}
560+
561+
@Test func updateSite_can_set_sync_date_to_nil() async throws {
562+
// Given
563+
let siteID: Int64 = 131415
564+
let initialSyncDate = Date(timeIntervalSince1970: 4000)
565+
let initialSite = POSSite(siteID: siteID, lastIncrementalSyncDate: initialSyncDate)
566+
try await insertSite(initialSite)
567+
568+
// When
569+
let updatedSite = POSSite(siteID: siteID, lastIncrementalSyncDate: nil)
570+
try await sut.updateSite(updatedSite)
571+
572+
// Then
573+
try await db.read { db in
574+
let persistedSite = try PersistedSite.fetchOne(db)
575+
#expect(persistedSite?.id == siteID)
576+
#expect(persistedSite?.posLastIncrementalSyncDate == nil)
577+
}
578+
}
579+
580+
@Test func loadSite_and_updateSite_work_together_for_multiple_sites() async throws {
581+
// Given
582+
let site1ID: Int64 = 100
583+
let site2ID: Int64 = 200
584+
let site1Date = Date(timeIntervalSince1970: 1000)
585+
let site2Date = Date(timeIntervalSince1970: 2000)
586+
587+
let site1 = POSSite(siteID: site1ID, lastIncrementalSyncDate: site1Date)
588+
let site2 = POSSite(siteID: site2ID, lastIncrementalSyncDate: site2Date)
589+
590+
// When
591+
try await insertSite(site1)
592+
try await insertSite(site2)
593+
594+
// Then
595+
let loadedSite1 = try await sut.loadSite(siteID: site1ID)
596+
let loadedSite2 = try await sut.loadSite(siteID: site2ID)
597+
598+
#expect(loadedSite1?.siteID == site1ID)
599+
#expect(loadedSite1?.lastIncrementalSyncDate == site1Date)
600+
#expect(loadedSite2?.siteID == site2ID)
601+
#expect(loadedSite2?.lastIncrementalSyncDate == site2Date)
602+
603+
// When loading non-existent site returns nil
604+
let nonExistentSite = try await sut.loadSite(siteID: 999)
605+
#expect(nonExistentSite == nil)
606+
}
483607
}
484608

485609
private extension POSCatalogPersistenceServiceTests {
610+
func insertSite(_ site: POSSite) async throws {
611+
try await db.write { db in
612+
try PersistedSite(from: site).insert(db, onConflict: .replace)
613+
}
614+
}
615+
486616
func insertProduct(_ product: POSProduct) async throws {
487-
let db = grdbManager.databaseConnection
488617
try await db.write { db in
489618
try PersistedSite(id: sampleSiteID).insert(db, onConflict: .ignore)
490619
}
491620
try product.save(to: db)
492621
}
493622

494623
func insertVariation(_ variation: POSProductVariation) async throws {
495-
let db = grdbManager.databaseConnection
496624
try variation.save(to: db)
497625
}
498626
}

0 commit comments

Comments
 (0)