Skip to content

Add /products endpoint#1289

Merged
oguzkocer merged 14 commits intotrunkfrom
products-endpoint
May 5, 2026
Merged

Add /products endpoint#1289
oguzkocer merged 14 commits intotrunkfrom
products-endpoint

Conversation

@oguzkocer
Copy link
Copy Markdown
Contributor

Implement GET /products with optional type filter parameter. The Product type covers all product variants (domain registrations, transfers, Jetpack plans, etc.) with optional fields for domain-specific data (tld, sale_coupon, HSTS) and non-domain data (introductory_offer, price tiers).

@oguzkocer oguzkocer added this to the 0.2 milestone Apr 17, 2026
@wpmobilebot
Copy link
Copy Markdown
Collaborator

wpmobilebot commented Apr 17, 2026

XCFramework Build

This PR's XCFramework is available for testing. Add to your Package.swift:

.package(url: "https://github.com/automattic/wordpress-rs", branch: "pr-build/1289")

Built from e022ffa

@oguzkocer oguzkocer marked this pull request as ready for review April 17, 2026 19:48
@oguzkocer oguzkocer requested a review from jkmassel April 17, 2026 19:49
Comment thread wp_api/src/wp_com/products.rs Outdated
/// Numeric sale price when a coupon applies.
#[serde(default)]
#[uniffi(default = None)]
pub sale_cost: Option<f64>,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

#1283 has landed, so we could probably use Decimal2 here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It hadn't landed yet when I worked on this. Let me make that change.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 6b6b53d.

Copy link
Copy Markdown
Contributor

@jkmassel jkmassel left a comment

Choose a reason for hiding this comment

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

Some suggestions for you to work around the strangeness of this API shape.

Comment thread wp_api/src/wp_com/products.rs Outdated
#[uniffi(default = None)]
pub usage_limit: Option<u32>,
/// Cost per interval during the offer period.
pub cost_per_interval: f64,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same point about Decimal2

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 900783a.

Note that upon reviewing the API implementation, I've also updated tld_rank to Option<f64> per Claude's suggestion.

Comment thread wp_api/src/wp_com/products.rs Outdated
pub struct ProductsParams {
/// Filter by product type (e.g. `"domains"`).
#[uniffi(default = None)]
pub product_type: Option<String>,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This endpoint has a bunch of localized fields, so we need to provide the locale we want strings in.

The argument is locale, and it can be WPComLanguage – that should automatically handle serialization into query strings for you.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in bb3d213.

Comment thread wp_api/src/wp_com/products.rs Outdated
pub struct ProductsParams {
/// Filter by product type (e.g. `"domains"`).
#[uniffi(default = None)]
pub product_type: Option<String>,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

WDYT about introducing a ProductType enum for this?

It would need the Other case to handle any new values, but it'd avoid stringly-typing for an operation like "get all the domain registration products" – the API returns all results instead of no results if something is misspelled.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in f3b6c71.

Comment thread wp_api/src/wp_com/products.rs Outdated
/// Absent for non-registration products like domain mapping.
#[serde(default)]
#[uniffi(default = None)]
pub tld: Option<String>,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

tld, is_privacy_protection_product_purchase_allowed, is_hsts_required, is_dot_gay_notice_required are all only defined on domains.

WDYT about defining DomainProductInfo to hold these?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 8d88fb4.

Copy link
Copy Markdown
Contributor

@jkmassel jkmassel left a comment

Choose a reason for hiding this comment

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

Left some suggestions

@oguzkocer oguzkocer requested a review from jkmassel April 21, 2026 04:16
Copy link
Copy Markdown
Contributor

@jkmassel jkmassel left a comment

Choose a reason for hiding this comment

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

Left some suggestions

/// Locale for localized product names and descriptions.
#[uniffi(default = None)]
pub locale: Option<WPComLanguage>,
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There's also a currency param that controls which currency is returned.

We should implement it, probably as an enum with USD, EUR, AUD, etc. We can have a catch-all Other for it. It can be a separate PR, but we should update the places where currency is emitted to use that too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll address this in a separate PR to avoid some merge conflicts.

Comment thread wp_api/src/wp_com/products.rs Outdated
pub cost_smallest_unit: u64,
pub currency_code: CurrencyCode,
/// Billing period (e.g. `"year"`).
pub product_term: String,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This seems like a constrained set of values – should we make it an enum as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in a5ea0e1.

Comment thread wp_api/src/wp_com/products.rs Outdated
/// Details of an active sale coupon applied to a product.
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
pub struct SaleCoupon {
pub start_date: String,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Based on the field name, I think I'd expect this to be a Date type?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 229811b.

Comment thread wp_api/src/wp_com/products.rs Outdated
/// Domain-specific fields. Fields are populated only for domain
/// registration products; all will be `None` for other product types.
#[serde(flatten)]
pub domain_info: DomainProductInfo,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This should probably be optional, because not all products are domains. When it is, all of the fields can be non-optional because they're only populated for domain-shaped objects.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 7b88560.

Comment thread wp_api/src/wp_com/products.rs Outdated
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
pub struct IntroductoryOffer {
/// Unit of the offer interval (e.g. `"month"`).
pub interval_unit: String,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is actually an enum on the server too:

Value Meaning
"day" Daily interval
"week" Weekly interval
"month" Monthly interval
"year" Annual interval
"indefinite" No end / indefinite

Could have Other just in case but it seems nicely shaped.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 18f6b59.

@oguzkocer oguzkocer force-pushed the products-endpoint branch from 229811b to 2b257e8 Compare May 4, 2026 17:17
@jkmassel jkmassel requested review from jkmassel and removed request for jkmassel May 4, 2026 17:45
Copy link
Copy Markdown
Contributor

@jkmassel jkmassel left a comment

Choose a reason for hiding this comment

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

echoing @crazytonyli's approval

oguzkocer added 7 commits May 4, 2026 20:53
Implement `GET /products` with optional `type` filter parameter. The
`Product` type covers all product variants (domain registrations,
transfers, Jetpack plans, etc.) with optional fields for domain-specific
data (`tld`, `sale_coupon`, HSTS) and non-domain data
(`introductory_offer`, price tiers).
- Product.cost: f64 → Decimal2
- IntroductoryOffer.cost_per_interval: f64 → Decimal2
- SaleCoupon.tld_rank: Option<u32> → Option<f64> to match backend
Accept an optional WPComLanguage locale in ProductsParams to get
localized product names and descriptions from the API.
Replace stringly-typed product_type filter with a ProductTypeFilter
enum (Domains, Jetpack, Other) to prevent typos that silently return
all results instead of a filtered set.
Group domain-specific optional fields (tld, is_privacy_protection_-
product_purchase_allowed, is_hsts_required, is_dot_gay_notice_required)
into a DomainProductInfo struct, flattened for JSON compatibility.
Match the DerivedRequest trait signature which requires &self.
oguzkocer added 7 commits May 4, 2026 20:53
CurrencyCode (in wp_com module) wraps an ISO 4217 string.
ProductId (in products module) wraps a u64 with built-in
deserialization from both numeric and string representations,
since the API is inconsistent about the encoding.
Since tld and is_privacy_protection_product_purchase_allowed are
always present for domain products, make them non-optional. With
tld as a required String, serde(flatten) correctly produces None
for non-domain products and Some for domain products.
Known values from the backend: month, year, two years, three years,
hundred years, one time. Includes Other(String) fallback.
Matches the backend's `Time_Span_Unit` enum: day, week, month,
year, indefinite. Includes Other(String) fallback.
The existing date deserializer already handles the MySQL format
("2025-01-01 00:00:00") used by the coupon start_date and expires
fields.
@oguzkocer oguzkocer force-pushed the products-endpoint branch from ddbbdf7 to e022ffa Compare May 5, 2026 00:53
@oguzkocer oguzkocer enabled auto-merge (squash) May 5, 2026 00:54
@oguzkocer oguzkocer merged commit 7c40aa4 into trunk May 5, 2026
34 checks passed
@oguzkocer oguzkocer deleted the products-endpoint branch May 5, 2026 01:21
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