CMM-1288: Add endpoints for Subscribers tab#1200
Conversation
Implements the /rest/v1.1/sites/{site_id}/stats/emails/summary endpoint
with params (period, quantity, sort_field, sort_order), response types,
unit tests, JSON fixtures, and e2e integration tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements the /rest/v1.1/sites/{site_id}/stats/subscribers endpoint
with params (unit, quantity, date, stat_fields), response types with
helper methods for extracting subscribers and paid subscribers data,
unit tests, JSON fixtures for day/week/month/year/empty responses,
and e2e integration tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds the /wpcom/v2/sites/{site_id}/subscribers_by_user_type endpoint
to the existing subscribers module. Includes params (per_page, page,
user_type, sort), reuses the existing ListSubscribersResponse type,
unit tests, and JSON test fixtures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The API can return invalid dates like "-001-11-30T00:00:00+00:00" for some subscribers. Change date_subscribed to Option<WpGmtDateTime> with a lenient deserializer that returns None for unparseable dates instead of failing the entire response. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
jkmassel
left a comment
There was a problem hiding this comment.
Caught a few things to take a look over (mostly suggestions by the bot)
- Rename Alltime to AllTime with serde/strum rename overrides - Move date deserializer to wp_utc_date_format_option and wp_gmt_date_time_option modules - Add #[serde(default)] on date_subscribed for missing fields - Fix hardcoded period index in stats subscribers data extraction - Replace StatsEmailsSummarySortOrder with shared WpApiParamOrder - Add #[serde(rename_all = "snake_case")] to SubscribersByUserTypeSortField - Remove unnecessary Hash derive from StatsSubscribersResponse - Add e2e test for subscribers by user type endpoint Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…onse Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…endpoints Address two issues flagged in PR #1200 review: 1. Fix unsafe row indexing in stats_visits.rs: The get_stats_data() function hard-coded row[0] for period and used unwrap() on position(). Now it safely looks up both "period" and data field indices, using .get() for bounds-safe access instead of panicking on out-of-bounds indices. 2. Remove Hash derive from all stats types: Hash was blindly copied across 12 stats Period/Unit enums and 8 DataPoint/Response types in stats_visits.rs, but none are used as HashMap/HashSet keys. Removed Hash from: - StatsVisitsUnit, StatsClicksPeriod, StatsReferrersPeriod, StatsSearchTermsPeriod, StatsTopAuthorsPeriod, StatsTopPostsPeriod, StatsCountryViewsPeriod, StatsRegionViewsPeriod, StatsCityViewsPeriod, StatsDevicesPeriod, StatsFileDownloadsPeriod, StatsVideoPlaysPeriod - StatsVisitsResponse, StatsVisitsDataPoint, StatsVisitorsDataPoint, StatsLikesDataPoint, StatsReblogsDataPoint, StatsCommentsDataPoint, StatsPostsDataPoint, StatsVisitsDataValue All 1256 unit tests pass. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
|
@jkmassel can you take another look? |
| "user_id": 3251794, | ||
| "subscription_id": 10660, | ||
| "email_address": "olduser@example.com", | ||
| "date_subscribed": "-001-11-30T00:00:00+00:00", |
There was a problem hiding this comment.
Note: This format comes from this real error I got while using the app:
ResponseParsingError(reason=Invalid date format: -001-11-30T00:00:00+00:00 at line 1 column 41532, response={"total":88,"pages":1,"page":1,"per_page":100,"subscribers":[{"user_id":33840434,"subscription_id":792200219,"email_address":"email@nikhilchavan.com","date_subscribed":"2024-12-26T10:08:55+00:00","is_email_subscriber":false,"subscription_status":null,"avatar":"https:\/\/0.gravatar.com\/avatar\/fb265c001fcd9384d6dc59d2c47f43196c3a5835e298bcd0ba18b5ae0d82bb96?s=128&d=https%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D128&r=G","display_name":"Nik","url":"https:\/\/nikhilc.dev"},{"user_id":257457202,"subscription_id":790270118,"email_address":"michal.dziewonski@automattic.com","date_subscribed":"2024-12-02T14:34:36+00:00","is_email_subscriber":false,"subscription_status":null,"avatar":"https:\/\/2.gravatar.com\/avatar\/5e3c9de90dc73677b682d85fe23af5f477082ab9c1b65b06067ea6e3fa3017d1?s=128&d=https%3A%2F%2F2.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D128&r=G","display_name":"Micha\u0142","url":"https:\/\/michaldziewonski.wpcomstaging.com"},{"user_id":179649005,"subscription_id":786241236,"email_address":"sorin.nunca@automattic.com","date_subscribed":"2024-10-19T10:32:44+00:00","is_email_subscriber":false,"subscription_status":null,"avatar":"h
…endpoints (#1218) Address two issues flagged in PR #1200 review: 1. Fix unsafe row indexing in stats_visits.rs: The get_stats_data() function hard-coded row[0] for period and used unwrap() on position(). Now it safely looks up both "period" and data field indices, using .get() for bounds-safe access instead of panicking on out-of-bounds indices. 2. Remove Hash derive from all stats types: Hash was blindly copied across 12 stats Period/Unit enums and 8 DataPoint/Response types in stats_visits.rs, but none are used as HashMap/HashSet keys. Removed Hash from: - StatsVisitsUnit, StatsClicksPeriod, StatsReferrersPeriod, StatsSearchTermsPeriod, StatsTopAuthorsPeriod, StatsTopPostsPeriod, StatsCountryViewsPeriod, StatsRegionViewsPeriod, StatsCityViewsPeriod, StatsDevicesPeriod, StatsFileDownloadsPeriod, StatsVideoPlaysPeriod - StatsVisitsResponse, StatsVisitsDataPoint, StatsVisitorsDataPoint, StatsLikesDataPoint, StatsReblogsDataPoint, StatsCommentsDataPoint, StatsPostsDataPoint, StatsVisitsDataValue All 1256 unit tests pass. Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Remove optional date handling for date_subscribed. Malformed or missing dates now produce a parsing error instead of being silently treated as None. Remove wp_gmt_date_time_option and wp_utc_date_format_option modules that are no longer needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…endpoints (#1218) Address two issues flagged in PR #1200 review: 1. Fix unsafe row indexing in stats_visits.rs: The get_stats_data() function hard-coded row[0] for period and used unwrap() on position(). Now it safely looks up both "period" and data field indices, using .get() for bounds-safe access instead of panicking on out-of-bounds indices. 2. Remove Hash derive from all stats types: Hash was blindly copied across 12 stats Period/Unit enums and 8 DataPoint/Response types in stats_visits.rs, but none are used as HashMap/HashSet keys. Removed Hash from: - StatsVisitsUnit, StatsClicksPeriod, StatsReferrersPeriod, StatsSearchTermsPeriod, StatsTopAuthorsPeriod, StatsTopPostsPeriod, StatsCountryViewsPeriod, StatsRegionViewsPeriod, StatsCityViewsPeriod, StatsDevicesPeriod, StatsFileDownloadsPeriod, StatsVideoPlaysPeriod - StatsVisitsResponse, StatsVisitsDataPoint, StatsVisitorsDataPoint, StatsLikesDataPoint, StatsReblogsDataPoint, StatsCommentsDataPoint, StatsPostsDataPoint, StatsVisitsDataValue All 1256 unit tests pass. Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
…endpoints (#1218) Address two issues flagged in PR #1200 review: 1. Fix unsafe row indexing in stats_visits.rs: The get_stats_data() function hard-coded row[0] for period and used unwrap() on position(). Now it safely looks up both "period" and data field indices, using .get() for bounds-safe access instead of panicking on out-of-bounds indices. 2. Remove Hash derive from all stats types: Hash was blindly copied across 12 stats Period/Unit enums and 8 DataPoint/Response types in stats_visits.rs, but none are used as HashMap/HashSet keys. Removed Hash from: - StatsVisitsUnit, StatsClicksPeriod, StatsReferrersPeriod, StatsSearchTermsPeriod, StatsTopAuthorsPeriod, StatsTopPostsPeriod, StatsCountryViewsPeriod, StatsRegionViewsPeriod, StatsCityViewsPeriod, StatsDevicesPeriod, StatsFileDownloadsPeriod, StatsVideoPlaysPeriod - StatsVisitsResponse, StatsVisitsDataPoint, StatsVisitorsDataPoint, StatsLikesDataPoint, StatsReblogsDataPoint, StatsCommentsDataPoint, StatsPostsDataPoint, StatsVisitsDataValue All 1256 unit tests pass. Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
jkmassel
left a comment
There was a problem hiding this comment.
A couple of minor improvements, but this is ready to go otherwise.
…ypeUserType Remove redundant #[serde(rename)] and #[strum(serialize)] attributes from StatsEmailsSummarySortField variants since rename_all/serialize_all already handle the conversion. Rename SubscribersByUserTypeUserType to WPComSubscriberType for clarity. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add stats insights endpoint
Add support for the WP.com REST API v1.1 stats/insights endpoint,
which returns posting activity patterns including best hour/day to post,
hourly view breakdowns, and yearly posting summaries.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Handle empty array responses in stats insights deserialization
The WP.com API returns `[]` instead of `{}` for empty map fields
(days, hours, hourly_views). Use `deserialize_empty_array_or_hashmap`
to handle both cases gracefully.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Handle integer values in map deserialization for stats insights
The WP.com API returns `0` instead of `{}` for empty map fields
(days, hours, hourly_views) on some sites. Extend
`deserialize_empty_array_or_hashmap` to also accept integers,
treating them as empty HashMaps.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Make map deserializer handle all non-map placeholder values
The WP.com API can return various non-map values (0, null, false, [])
for map fields on sites with no data. Make deserialize_empty_array_or_hashmap
handle all of them by adding visit_bool, visit_none, visit_unit, and
visit_f64 handlers, returning empty HashMap for any non-map value.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Rewrite map deserializers to use serde_json::Value intermediate
Replace the visitor-based approach with a simpler Value-based strategy
that first deserializes any JSON value, then only converts JSON objects
into HashMaps. All non-object values (integers, null, false, arrays,
strings, etc.) are treated as empty map / None. This is more robust
against unexpected API response formats.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add stats summary endpoint
Add GET /rest/v1.1/sites/<site_id>/stats endpoint that returns
aggregate site statistics and recent visit time-series data.
Reuses StatsVisitsResponse for the visits field. Supports
locale query parameter.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add locale parameter to stats insights endpoint
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Bump fastlane-plugin-wpmreleasetoolkit from 14.1.0 to 14.2.0 (#1223)
Bumps [fastlane-plugin-wpmreleasetoolkit](https://github.com/wordpress-mobile/release-toolkit) from 14.1.0 to 14.2.0.
- [Release notes](https://github.com/wordpress-mobile/release-toolkit/releases)
- [Changelog](https://github.com/wordpress-mobile/release-toolkit/blob/trunk/CHANGELOG.md)
- [Commits](wordpress-mobile/release-toolkit@14.1.0...14.2.0)
---
updated-dependencies:
- dependency-name: fastlane-plugin-wpmreleasetoolkit
dependency-version: 14.2.0
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Bump fluent-templates from 0.13.2 to 0.13.3 (#1222)
Bumps [fluent-templates](https://github.com/XAMPPRocky/fluent-templates) from 0.13.2 to 0.13.3.
- [Release notes](https://github.com/XAMPPRocky/fluent-templates/releases)
- [Changelog](https://github.com/XAMPPRocky/fluent-templates/blob/master/CHANGELOG.md)
- [Commits](XAMPPRocky/fluent-templates@fluent-templates-v0.13.2...fluent-templates-v0.13.3)
---
updated-dependencies:
- dependency-name: fluent-templates
dependency-version: 0.13.3
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Fix unsafe row indexing and remove unnecessary Hash derives in stats endpoints (#1218)
Address two issues flagged in PR #1200 review:
1. Fix unsafe row indexing in stats_visits.rs: The get_stats_data() function
hard-coded row[0] for period and used unwrap() on position(). Now it safely
looks up both "period" and data field indices, using .get() for bounds-safe
access instead of panicking on out-of-bounds indices.
2. Remove Hash derive from all stats types: Hash was blindly copied across
12 stats Period/Unit enums and 8 DataPoint/Response types in stats_visits.rs,
but none are used as HashMap/HashSet keys. Removed Hash from:
- StatsVisitsUnit, StatsClicksPeriod, StatsReferrersPeriod, StatsSearchTermsPeriod,
StatsTopAuthorsPeriod, StatsTopPostsPeriod, StatsCountryViewsPeriod,
StatsRegionViewsPeriod, StatsCityViewsPeriod, StatsDevicesPeriod,
StatsFileDownloadsPeriod, StatsVideoPlaysPeriod
- StatsVisitsResponse, StatsVisitsDataPoint, StatsVisitorsDataPoint,
StatsLikesDataPoint, StatsReblogsDataPoint, StatsCommentsDataPoint,
StatsPostsDataPoint, StatsVisitsDataValue
All 1256 unit tests pass.
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
* Bump chrono from 0.4.43 to 0.4.44 (#1221)
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.43 to 0.4.44.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](chronotope/chrono@v0.4.43...v0.4.44)
---
updated-dependencies:
- dependency-name: chrono
dependency-version: 0.4.44
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Bump proc-macro-crate from 3.4.0 to 3.5.0 (#1225)
Bumps [proc-macro-crate](https://github.com/bkchr/proc-macro-crate) from 3.4.0 to 3.5.0.
- [Release notes](https://github.com/bkchr/proc-macro-crate/releases)
- [Commits](bkchr/proc-macro-crate@v3.4.0...v3.5.0)
---
updated-dependencies:
- dependency-name: proc-macro-crate
dependency-version: 3.5.0
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Bump quote from 1.0.44 to 1.0.45 (#1224)
Bumps [quote](https://github.com/dtolnay/quote) from 1.0.44 to 1.0.45.
- [Release notes](https://github.com/dtolnay/quote/releases)
- [Commits](dtolnay/quote@1.0.44...1.0.45)
---
updated-dependencies:
- dependency-name: quote
dependency-version: 1.0.45
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Add a migration script to ensure custom terms are re-fetched (#1217)
* Fix loading trashed post by ids (#1226)
* Add integration tests for fetching trashed posts by id
* Fix loading trashed post by ids
* Add PostStatus::Any
* Support filtering by "any" status (#1210)
* Support filtering by "any" status
* Use PostStatus::Any
* Add stats tags endpoint
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Revert changes to deserialize_empty_array_or_hashmap helpers
Restores the original Visitor-based implementation. These helpers
don't need to be modified for the stats tags endpoint.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Tony Li <tony.li@automattic.com>
Description
Add three new WP.com API endpoints to support the Subscribers tab feature:
/rest/v1.1/sites/{site_id}/stats/emails/summary/rest/v1.1/sites/{site_id}/stats/subscribers/wpcom/v2/sites/{site_id}/subscribers_by_user_typeChanges
Stats emails summary
StatsEmailsSummaryParamswithperiod,quantity,sort_field, andsort_orderparametersStatsEmailsSummaryResponseandStatsEmailsSummaryPostresponse typesWpDerivedRequestmacro withWpComNamespace::RestV1_1WpComApiRequestBuilderandWpComApiClientStats subscribers
StatsSubscribersParamswithunit,quantity, andstat_fields(comma-joined) parametersStatsSubscribersResponsereusingStatsVisitsDataValuefor the fields+data array patternsubscribers_data()andsubscribers_paid_data()with#[uniffi::export]WpComNamespace::RestV1_1WpComApiRequestBuilderandWpComApiClientSubscribers by user type
SubscribersByUserTypeParamswithuser_type,per_page,page, andsortparametersSubscribersByUserTypeUserTypeandSubscribersByUserTypeSortFieldenumsListSubscribersResponsetypeListSubscribersByUserTypevariant toSubscribersRequestwithWpComNamespace::V2Test plan
cargo buildsucceedscargo test --lib -p wp_api— all new unit tests passcargo fmtclean🤖 Generated with Claude Code