Skip to content

Conversation

@jaclync
Copy link
Contributor

@jaclync jaclync commented Aug 25, 2025

For WOOMOB-1096

Description

Adds a new POSCatalogSyncRemote class to support incremental synchronization of POS catalog data. This enables merchants to efficiently sync only products and product variations that have been modified after a specific date, significantly reducing bandwidth usage and improving sync performance for large catalogs.

The implementation includes:

  • POSCatalogSyncRemote with protocol-based design for easy mocking in the future
  • loadProducts method with product type filtering and default values
  • loadProductVariations method for fetching variation data
    • We are tentatively using an existing wc-analytics endpoint, and we will create a new endpoint with a WC path later
  • Reusable pagination helper extracted to Remote base class

This PR also includes a feature flag for Local Catalog i1.

Steps to reproduce

Just CI is sufficient as the remote isn't used in the app yet.

Testing information

I tested with

let products = try await syncRemote.loadProducts(modifiedAfter: .init().addingTimeInterval(-90000), siteID: siteID, pageNumber: 1)
let variations = try await syncRemote.loadProductVariations(
    modifiedAfter: .init().addingTimeInterval(-90000),
    siteID: siteID,
    pageNumber: 1
)
print("Synced \(products.items.count) products and \(variations.items.count) variations for siteID \(siteID)")

in the app to verify that the remote methods are working as expected.


  • I have considered if this change warrants user-facing release notes and have added them to RELEASE-NOTES.txt if necessary.

@jaclync jaclync added this to the 23.2 milestone Aug 25, 2025
@jaclync jaclync changed the title Add POSCatalogSyncRemote for incremental sync API [Local Catalog] Add remote for products/variations incremental sync API Aug 25, 2025
@wpmobilebot
Copy link
Collaborator

wpmobilebot commented Aug 25, 2025

App Icon📲 You can test the changes from this Pull Request in WooCommerce iOS Prototype by scanning the QR code below to install the corresponding build.

App NameWooCommerce iOS Prototype
Build Numberpr16033-ae63cd3
Version23.1
Bundle IDcom.automattic.alpha.woocommerce
Commitae63cd3
Installation URL29dgs954jm168
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@jaclync jaclync requested a review from joshheald August 25, 2025 10:16
Copy link
Contributor

@joshheald joshheald left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Jaclyn, looks good 👍

public func loadProducts(modifiedAfter: Date, siteID: Int64, productTypes: [ProductType] = [.simple, .variable], pageNumber: Int)
async throws -> PagedItems<POSProduct> {
let path = "products"
let dateFormatter = ISO8601DateFormatter()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let dateFormatter = ISO8601DateFormatter()
let dateFormatter = ISO8601DateFormatter()

Perhaps best to have this in a property? I think DateFormatter is reasonably expensive to create, and this could be called quite a few times.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in b1f9850.

/// - pageNumber: Page number for pagination.
/// - Returns: Paginated list of POS products.
///
// periphery:ignore - TODO - remove when this endpoint is integrated with catalog sync
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the function marked for removal? Why...? I thought we'd need this all the way through?

Or is this just telling periphery to ignore the function and we'll remove that? Why do we need to ignore it if so?

Copy link
Contributor Author

@jaclync jaclync Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not the function, just the periphery ignore comment! The periphery:ignore comment is required now since it's not used in the app right now. I'll update the comment to avoid confusion.

Updatead in b0d5574.

let dateFormatter = ISO8601DateFormatter()
let parameters = [
ParameterKey.modifiedAfter: dateFormatter.string(from: modifiedAfter),
ParameterKey.productTypes: productTypes.map { $0.rawValue }.joined(separator: ","),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need to keep this under review, based on what we do with full sync. There are arguments to pull everything down, and filter on the client side... especially if that's easier to generate in the file.

Copy link
Contributor Author

@jaclync jaclync Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, right now the full sync does not support filtering on product types. I will remove this product types parameter for now.

Updated in ae63cd3.

Comment on lines +375 to +391
func createPagedItems<T>(items: [T],
responseHeaders: [String: String]?,
currentPageNumber: Int) -> PagedItems<T> {
// Extract total pages from response headers (case insensitive)
let totalPages = responseHeaders?.first(where: {
$0.key.lowercased() == PaginationHeaderKey.totalPagesCount.lowercased()
}).flatMap { Int($0.value) }

let hasMorePages = totalPages.map { currentPageNumber < $0 } ?? true

// Extract total count from response headers (case insensitive)
let totalItems = responseHeaders?.first(where: {
$0.key.lowercased() == PaginationHeaderKey.totalCount.lowercased()
}).flatMap { Int($0.value) }

return PagedItems(items: items, hasMorePages: hasMorePages, totalItems: totalItems)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 👍

@jaclync jaclync enabled auto-merge August 26, 2025 02:16
@jaclync jaclync merged commit 46a5125 into trunk Aug 26, 2025
14 checks passed
@jaclync jaclync deleted the feat/WOOMOB-1096-incremental-sync-remote branch August 26, 2025 02:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants