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
3 changes: 2 additions & 1 deletion Modules/Sources/Fakes/Yosemite.generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ extension Yosemite.POSSite {
public static func fake() -> Yosemite.POSSite {
.init(
siteID: .fake(),
lastIncrementalSyncDate: .fake()
lastIncrementalSyncDate: .fake(),
lastFullSyncDate: .fake()
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ struct V001InitialSchema {
try db.create(table: "site") { siteTable in
siteTable.primaryKey("id", .integer).notNull()
siteTable.column("lastCatalogIncrementalSyncDate", .datetime)
siteTable.column("lastCatalogFullSyncDate", .datetime)
}
}

Expand Down
14 changes: 10 additions & 4 deletions Modules/Sources/Storage/GRDB/Model/PersistedSite.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ public struct PersistedSite: Codable {
// periphery:ignore - TODO: remove ignore when populating database
public let id: Int64
// periphery:ignore - TODO: remove ignore when populating database
public let lastCatalogIncrementalSyncDate: Date?
public var lastCatalogIncrementalSyncDate: Date?
// periphery:ignore - TODO: remove ignore when populating database
public var lastCatalogFullSyncDate: Date?

// periphery:ignore - TODO: remove ignore when populating database
public init(id: Int64, lastCatalogIncrementalSyncDate: Date? = nil) {
public init(id: Int64, lastCatalogIncrementalSyncDate: Date? = nil, lastCatalogFullSyncDate: Date? = nil) {
self.id = id
self.lastCatalogIncrementalSyncDate = lastCatalogIncrementalSyncDate
self.lastCatalogFullSyncDate = lastCatalogFullSyncDate
}
}

Expand All @@ -21,9 +24,11 @@ extension PersistedSite: FetchableRecord, PersistableRecord {

public enum Columns {
// periphery:ignore - TODO: remove ignore when populating database
static let id = Column(CodingKeys.id)
public static let id = Column(CodingKeys.id)
// periphery:ignore - TODO: remove ignore when populating database
public static let lastCatalogIncrementalSyncDate = Column(CodingKeys.lastCatalogIncrementalSyncDate)
// periphery:ignore - TODO: remove ignore when populating database
static let lastCatalogIncrementalSyncDate = Column(CodingKeys.lastCatalogIncrementalSyncDate)
public static let lastCatalogFullSyncDate = Column(CodingKeys.lastCatalogFullSyncDate)
}
}

Expand All @@ -32,5 +37,6 @@ private extension PersistedSite {
enum CodingKeys: String, CodingKey {
case id
case lastCatalogIncrementalSyncDate
case lastCatalogFullSyncDate
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,7 @@ extension Storage.GeneralStoreSettings {
lastSelectedOrderStatus: NullableCopiableProp<String> = .copy,
favoriteProductIDs: CopiableProp<[Int64]> = .copy,
searchTermsByKey: CopiableProp<[String: [String]]> = .copy,
isPOSTabVisible: NullableCopiableProp<Bool> = .copy,
posLastFullSyncDate: NullableCopiableProp<Date> = .copy
isPOSTabVisible: NullableCopiableProp<Bool> = .copy
) -> Storage.GeneralStoreSettings {
let storeID = storeID ?? self.storeID
let isTelemetryAvailable = isTelemetryAvailable ?? self.isTelemetryAvailable
Expand All @@ -138,7 +137,6 @@ extension Storage.GeneralStoreSettings {
let favoriteProductIDs = favoriteProductIDs ?? self.favoriteProductIDs
let searchTermsByKey = searchTermsByKey ?? self.searchTermsByKey
let isPOSTabVisible = isPOSTabVisible ?? self.isPOSTabVisible
let posLastFullSyncDate = posLastFullSyncDate ?? self.posLastFullSyncDate

return Storage.GeneralStoreSettings(
storeID: storeID,
Expand All @@ -160,8 +158,7 @@ extension Storage.GeneralStoreSettings {
lastSelectedOrderStatus: lastSelectedOrderStatus,
favoriteProductIDs: favoriteProductIDs,
searchTermsByKey: searchTermsByKey,
isPOSTabVisible: isPOSTabVisible,
posLastFullSyncDate: posLastFullSyncDate
isPOSTabVisible: isPOSTabVisible
)
}
}
11 changes: 2 additions & 9 deletions Modules/Sources/Storage/Model/GeneralStoreSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,6 @@ public struct GeneralStoreSettings: Codable, Equatable, GeneratedCopiable {
///
public var isPOSTabVisible: Bool?

/// Last time a POS catalog full sync was completed for this store.
///
public var posLastFullSyncDate: Date?

public init(storeID: String? = nil,
isTelemetryAvailable: Bool = false,
Expand All @@ -109,8 +106,7 @@ public struct GeneralStoreSettings: Codable, Equatable, GeneratedCopiable {
lastSelectedOrderStatus: String? = nil,
favoriteProductIDs: [Int64] = [],
searchTermsByKey: [String: [String]] = [:],
isPOSTabVisible: Bool? = nil,
posLastFullSyncDate: Date? = nil) {
isPOSTabVisible: Bool? = nil) {
self.storeID = storeID
self.isTelemetryAvailable = isTelemetryAvailable
self.telemetryLastReportedTime = telemetryLastReportedTime
Expand All @@ -131,7 +127,6 @@ public struct GeneralStoreSettings: Codable, Equatable, GeneratedCopiable {
self.favoriteProductIDs = favoriteProductIDs
self.searchTermsByKey = searchTermsByKey
self.isPOSTabVisible = isPOSTabVisible
self.posLastFullSyncDate = posLastFullSyncDate
}

public func erasingSelectedTaxRateID() -> GeneralStoreSettings {
Expand All @@ -153,8 +148,7 @@ public struct GeneralStoreSettings: Codable, Equatable, GeneratedCopiable {
lastSelectedOrderStatus: lastSelectedOrderStatus,
favoriteProductIDs: favoriteProductIDs,
searchTermsByKey: searchTermsByKey,
isPOSTabVisible: isPOSTabVisible,
posLastFullSyncDate: posLastFullSyncDate)
isPOSTabVisible: isPOSTabVisible)
}
}

Expand Down Expand Up @@ -189,7 +183,6 @@ extension GeneralStoreSettings {
self.searchTermsByKey = try container.decodeIfPresent([String: [String]].self, forKey: .searchTermsByKey) ?? [:]

self.isPOSTabVisible = try container.decodeIfPresent(Bool.self, forKey: .isPOSTabVisible)
self.posLastFullSyncDate = try container.decodeIfPresent(Date.self, forKey: .posLastFullSyncDate)

// Decode new properties with `decodeIfPresent` and provide a default value if necessary.
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,17 @@ extension Yosemite.POSSimpleProduct {
extension Yosemite.POSSite {
public func copy(
siteID: CopiableProp<Int64> = .copy,
lastIncrementalSyncDate: NullableCopiableProp<Date> = .copy
lastIncrementalSyncDate: NullableCopiableProp<Date> = .copy,
lastFullSyncDate: NullableCopiableProp<Date> = .copy
) -> Yosemite.POSSite {
let siteID = siteID ?? self.siteID
let lastIncrementalSyncDate = lastIncrementalSyncDate ?? self.lastIncrementalSyncDate
let lastFullSyncDate = lastFullSyncDate ?? self.lastFullSyncDate

return Yosemite.POSSite(
siteID: siteID,
lastIncrementalSyncDate: lastIncrementalSyncDate
lastIncrementalSyncDate: lastIncrementalSyncDate,
lastFullSyncDate: lastFullSyncDate
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
// periphery:ignore:all
import Foundation
import Storage

extension PersistedSite {
init(from posSite: POSSite) {
self.init(
id: posSite.siteID,
lastCatalogIncrementalSyncDate: posSite.lastIncrementalSyncDate
lastCatalogIncrementalSyncDate: posSite.lastIncrementalSyncDate,
lastCatalogFullSyncDate: posSite.lastFullSyncDate
)
}

func toPOSSite() -> POSSite {
POSSite(
siteID: id,
lastIncrementalSyncDate: lastCatalogIncrementalSyncDate
lastIncrementalSyncDate: lastCatalogIncrementalSyncDate,
lastFullSyncDate: lastCatalogFullSyncDate
)
}
}
4 changes: 3 additions & 1 deletion Modules/Sources/Yosemite/PointOfSale/POSSite.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import Foundation
public struct POSSite: Equatable, GeneratedCopiable, GeneratedFakeable {
public let siteID: Int64
public let lastIncrementalSyncDate: Date?
public let lastFullSyncDate: Date?

public init(siteID: Int64, lastIncrementalSyncDate: Date? = nil) {
public init(siteID: Int64, lastIncrementalSyncDate: Date? = nil, lastFullSyncDate: Date? = nil) {
self.siteID = siteID
self.lastIncrementalSyncDate = lastIncrementalSyncDate
self.lastFullSyncDate = lastFullSyncDate
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@ public protocol SiteSpecificAppSettingsStoreMethodsProtocol {
// Search history methods
func getSearchTerms(for itemType: POSItemType, siteID: Int64) -> [String]
func setSearchTerms(_ terms: [String], for itemType: POSItemType, siteID: Int64)

// POS catalog sync timestamp methods
func getPOSLastFullSyncDate(for siteID: Int64) -> Date?
func setPOSLastFullSyncDate(_ date: Date?, for siteID: Int64)
}

/// Methods for managing site-specific app settings
Expand Down Expand Up @@ -102,20 +98,6 @@ extension SiteSpecificAppSettingsStoreMethods {
}
}

// MARK: - POS Catalog Sync Timestamps
extension SiteSpecificAppSettingsStoreMethods {
func getPOSLastFullSyncDate(for siteID: Int64) -> Date? {
let storeSettings = getStoreSettings(for: siteID)
return storeSettings.posLastFullSyncDate
}

func setPOSLastFullSyncDate(_ date: Date?, for siteID: Int64) {
let storeSettings = getStoreSettings(for: siteID)
let updatedSettings = storeSettings.copy(posLastFullSyncDate: date)
setStoreSettings(settings: updatedSettings, for: siteID)
}
}

// MARK: - Constants
private enum Constants {
static let generalStoreSettingsFileName = "general-store-settings.plist"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import Foundation
import protocol Networking.POSCatalogSyncRemoteProtocol
import class Networking.AlamofireNetwork
import class Networking.POSCatalogSyncRemote
import CocoaLumberjackSwift
import Storage

// TODO - remove the periphery ignore comment when the catalog is integrated with POS.
Expand All @@ -20,6 +19,7 @@ public protocol POSCatalogFullSyncServiceProtocol {
public struct POSCatalog {
public let products: [POSProduct]
public let variations: [POSProductVariation]
public let syncDate: Date
}

// TODO - remove the periphery ignore comment when the service is integrated with POS.
Expand Down Expand Up @@ -74,6 +74,7 @@ public final class POSCatalogFullSyncService: POSCatalogFullSyncServiceProtocol

private extension POSCatalogFullSyncService {
func loadCatalog(for siteID: Int64, syncRemote: POSCatalogSyncRemoteProtocol) async throws -> POSCatalog {
let syncStartDate = Date.now
// Loads products and variations in batches in parallel.
async let productsTask = batchedLoader.loadAll(
makeRequest: { pageNumber in
Expand All @@ -87,7 +88,7 @@ private extension POSCatalogFullSyncService {
)

let (products, variations) = try await (productsTask, variationsTask)
return POSCatalog(products: products, variations: variations)
return POSCatalog(products: products, variations: variations, syncDate: syncStartDate)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import Foundation
import protocol Networking.POSCatalogSyncRemoteProtocol
import class Networking.AlamofireNetwork
import class Networking.POSCatalogSyncRemote
import CocoaLumberjackSwift
import protocol Storage.GRDBManagerProtocol

// TODO - remove the periphery ignore comment when the service is integrated with POS.
Expand All @@ -12,7 +11,7 @@ public protocol POSCatalogIncrementalSyncServiceProtocol {
/// - Parameters:
/// - siteID: The site ID to sync catalog for.
/// - lastFullSyncDate: The date of the last full sync to use if no incremental sync date exists.
func startIncrementalSync(for siteID: Int64, lastFullSyncDate: Date) async throws
func startIncrementalSync(for siteID: Int64, lastFullSyncDate: Date, lastIncrementalSyncDate: Date?) async throws
}

// TODO - remove the periphery ignore comment when the service is integrated with POS.
Expand Down Expand Up @@ -43,21 +42,18 @@ public final class POSCatalogIncrementalSyncService: POSCatalogIncrementalSyncSe

// MARK: - Protocol Conformance

public func startIncrementalSync(for siteID: Int64, lastFullSyncDate: Date) async throws {
let modifiedAfter = try await latestSyncDate(siteID: siteID, lastFullSyncDate: lastFullSyncDate)
public func startIncrementalSync(for siteID: Int64, lastFullSyncDate: Date, lastIncrementalSyncDate: Date?) async throws {
let modifiedAfter = latestSyncDate(fullSyncDate: lastFullSyncDate, incrementalSyncDate: lastIncrementalSyncDate)

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

do {
let syncStartDate = Date()
let catalog = try await loadCatalog(for: siteID, modifiedAfter: modifiedAfter, syncRemote: syncRemote)
DDLogInfo("✅ Loaded \(catalog.products.count) products and \(catalog.variations.count) variations for siteID \(siteID)")

try await persistenceService.persistIncrementalCatalogData(catalog, siteID: siteID)
DDLogInfo("✅ Persisted \(catalog.products.count) products and \(catalog.variations.count) variations to database for siteID \(siteID)")

try await persistenceService.updateSite(.init(siteID: siteID, lastIncrementalSyncDate: syncStartDate))
DDLogInfo("✅ Updated last incremental sync date to \(syncStartDate) for siteID \(siteID)")
} catch {
DDLogError("❌ Failed to sync and persist catalog incrementally: \(error)")
throw error
Expand All @@ -69,6 +65,7 @@ public final class POSCatalogIncrementalSyncService: POSCatalogIncrementalSyncSe

private extension POSCatalogIncrementalSyncService {
func loadCatalog(for siteID: Int64, modifiedAfter: Date, syncRemote: POSCatalogSyncRemoteProtocol) async throws -> POSCatalog {
let syncStartDate = Date.now
async let productsTask = batchedLoader.loadAll(
makeRequest: { pageNumber in
try await syncRemote.loadProducts(modifiedAfter: modifiedAfter, siteID: siteID, pageNumber: pageNumber)
Expand All @@ -81,14 +78,14 @@ private extension POSCatalogIncrementalSyncService {
)

let (products, variations) = try await (productsTask, variationsTask)
return POSCatalog(products: products, variations: variations)
return POSCatalog(products: products, variations: variations, syncDate: syncStartDate)
}
}

// MARK: - Sync date

private extension POSCatalogIncrementalSyncService {
func latestSyncDate(siteID: Int64, lastFullSyncDate: Date) async throws -> Date {
try await persistenceService.loadSite(siteID: siteID)?.lastIncrementalSyncDate ?? lastFullSyncDate
func latestSyncDate(fullSyncDate: Date, incrementalSyncDate: Date?) -> Date {
max(fullSyncDate, incrementalSyncDate ?? .distantPast)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ import Foundation
import Storage
import GRDB

enum POSCatalogPersistenceError: Error, Equatable {
case siteNotFound(siteID: Int64)
}

protocol POSCatalogPersistenceServiceProtocol {
/// Clears existing data and persists new catalog data
/// - Parameters:
Expand All @@ -19,15 +15,6 @@ protocol POSCatalogPersistenceServiceProtocol {
/// - catalog: The catalog difference to persist
/// - siteID: The site ID to associate the catalog with
func persistIncrementalCatalogData(_ catalog: POSCatalog, siteID: Int64) async throws

/// Loads the POS site for the given site ID
/// - Parameter siteID: The site ID to load the POSSite for
/// - Returns: The loaded POSSite or nil if not found in storage
func loadSite(siteID: Int64) async throws -> POSSite?

/// Updates the PersistedSite based on POSSite data
/// - Parameter site: The POSSite containing the updated data
func updateSite(_ site: POSSite) async throws
}

final class POSCatalogPersistenceService: POSCatalogPersistenceServiceProtocol {
Expand All @@ -45,7 +32,7 @@ final class POSCatalogPersistenceService: POSCatalogPersistenceServiceProtocol {
// currently, we can't save for more than one site as entity IDs are not namespaced.
try PersistedSite.deleteAll(db)

let site = PersistedSite(id: siteID)
let site = PersistedSite(id: siteID, lastCatalogFullSyncDate: catalog.syncDate)
try site.insert(db)

for product in catalog.productsToPersist {
Expand Down Expand Up @@ -132,6 +119,9 @@ final class POSCatalogPersistenceService: POSCatalogPersistenceServiceProtocol {
for var attribute in catalog.variationAttributesToPersist {
try attribute.insert(db, onConflict: .replace)
}

var site = try PersistedSite.fetchOne(db, key: siteID)
try site?.updateChanges(db) { $0.lastCatalogIncrementalSyncDate = catalog.syncDate }
}

DDLogInfo("✅ Incremental catalog persistence complete")
Expand All @@ -149,23 +139,6 @@ final class POSCatalogPersistenceService: POSCatalogPersistenceServiceProtocol {
"\(variationImageCount) variation images, \(variationAttributeCount) variation attributes")
}
}

func loadSite(siteID: Int64) async throws -> POSSite? {
try await grdbManager.databaseConnection.read { db in
try PersistedSite.filter(key: siteID).fetchOne(db)?.toPOSSite()
}
}

func updateSite(_ site: POSSite) async throws {
try await grdbManager.databaseConnection.write { db in
guard try PersistedSite.filter(key: site.siteID).fetchOne(db) != nil else {
throw POSCatalogPersistenceError.siteNotFound(siteID: site.siteID)
}

let persistedSite = PersistedSite(from: site)
try persistedSite.update(db)
}
}
}

private extension POSCatalog {
Expand Down
Loading