From c20ce1792dab3f54cbee146c8e38e347220fd391 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Thu, 17 Jul 2025 20:12:49 -0400 Subject: [PATCH] Add country field support to subscribers API - Add `include` parameter to request country field for subscribers - Make `SubscriberCountry` fields optional to handle API returning false - Add integration test for country field inclusion BREAKING CHANGE: `SubscriberCountry` fields are now optional --- wp_api/src/wp_com/subscribers.rs | 47 ++++++++++++++----- .../tests/test_wp_com_subscribers_immut.rs | 34 +++++++++++++- 2 files changed, 68 insertions(+), 13 deletions(-) diff --git a/wp_api/src/wp_com/subscribers.rs b/wp_api/src/wp_com/subscribers.rs index 41abe815..1b4f17f7 100644 --- a/wp_api/src/wp_com/subscribers.rs +++ b/wp_api/src/wp_com/subscribers.rs @@ -7,7 +7,7 @@ use crate::{ }; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, ops::Not}; -use wp_serde_helper::deserialize_u64_or_string; +use wp_serde_helper::{deserialize_false_or_string, deserialize_u64_or_string}; #[derive(Debug, Serialize, Deserialize, uniffi::Record)] pub struct Subscriber { @@ -74,8 +74,10 @@ pub enum SubscriptionStatus { #[derive(Debug, Serialize, Deserialize, uniffi::Record)] pub struct SubscriberCountry { - code: String, - name: String, + #[serde(default, deserialize_with = "deserialize_false_or_string")] + code: Option, + #[serde(default, deserialize_with = "deserialize_false_or_string")] + name: Option, } #[derive(Debug, Serialize, Deserialize, uniffi::Record)] @@ -95,27 +97,30 @@ pub struct SubscriptionPlan { #[derive(Debug, Default, PartialEq, Eq, uniffi::Record)] pub struct SubscribersListParams { - // The current page. + /// The current page. #[uniffi(default = None)] pub page: Option, - // The amount of items to show per page. + /// The amount of items to show per page. #[uniffi(default = None)] pub per_page: Option, - // Search for subscribers + /// Search for subscribers #[uniffi(default = None)] pub search: Option, - // Sort subscribers by a specific field + /// Sort subscribers by a specific field #[uniffi(default = None)] pub sort: Option, - // Sort order + /// Sort order #[uniffi(default = None)] pub sort_order: Option, - // Filter subscribers by a specific subscriber type + /// Filter subscribers by a specific subscriber type #[uniffi(default = None)] pub filter: Option, - // Array of filters to apply (combined with AND logic). If provided, overrides the single filter parameter. + /// Array of filters to apply (combined with AND logic). If provided, overrides the single filter parameter. #[uniffi(default = None)] pub filters: Option>, + /// An array of additional fields to include + #[uniffi(default = [])] + pub include: Vec, } impl AppendUrlQueryPairs for SubscribersListParams { @@ -125,7 +130,8 @@ impl AppendUrlQueryPairs for SubscribersListParams { .append_option_query_value_pair("per_page", self.per_page.as_ref()) .append_option_query_value_pair("search", self.search.as_ref()) .append_option_query_value_pair("sort", self.sort.as_ref()) - .append_option_query_value_pair("sort_order", self.sort_order.as_ref()); + .append_option_query_value_pair("sort_order", self.sort_order.as_ref()) + .append_vec_query_value_pair("include", self.include.as_ref()); if let Some(filters) = &self.filters { query_pairs_mut.append_pair( @@ -165,6 +171,23 @@ pub enum ListSubscribersSortField { impl_as_query_value_from_to_string!(ListSubscribersSortField); +#[derive( + Debug, + Serialize, + Deserialize, + Eq, + PartialEq, + uniffi::Enum, + strum_macros::EnumString, + strum_macros::Display, +)] +#[strum(serialize_all = "snake_case")] +pub enum ListSubscribersIncludeField { + Country, +} + +impl_as_query_value_from_to_string!(ListSubscribersIncludeField); + #[derive(Debug, Serialize, Deserialize, uniffi::Record)] pub struct ListSubscribersResponse { pub total: u64, @@ -396,6 +419,7 @@ mod tests { sort_order: Some(WpApiParamOrder::Asc), filter: Some(SubscriberType::All), filters: Some(vec![SubscriberType::All]), + include: vec![], }; let mut query_pairs = url.query_pairs_mut(); @@ -421,6 +445,7 @@ mod tests { sort_order: Some(WpApiParamOrder::Asc), filter: Some(SubscriberType::EmailSubscriber), filters: None, + include: vec![], }; let mut query_pairs = url.query_pairs_mut(); diff --git a/wp_api_integration_tests/tests/test_wp_com_subscribers_immut.rs b/wp_api_integration_tests/tests/test_wp_com_subscribers_immut.rs index 2c655f3f..4e623ee3 100644 --- a/wp_api_integration_tests/tests/test_wp_com_subscribers_immut.rs +++ b/wp_api_integration_tests/tests/test_wp_com_subscribers_immut.rs @@ -1,8 +1,8 @@ use wp_api::wp_com::{ WpComSiteId, subscribers::{ - IndividualSubscriberParams, IndividualSubscriberStatsParams, ListSubscribersSortField, - SubscribersListParams, SubscriptionId, + IndividualSubscriberParams, IndividualSubscriberStatsParams, ListSubscribersIncludeField, + ListSubscribersSortField, SubscribersListParams, SubscriptionId, }, }; use wp_api_integration_tests::{WpComTestCredentials, prelude::*, wp_com_client}; @@ -26,6 +26,36 @@ async fn list_subscribers(#[case] params: SubscribersListParams) { ); } +#[tokio::test] +#[parallel] +#[ignore] +async fn list_subscribers_include_country() { + let subscribers = wp_com_client() + .subscribers() + .list_subscribers( + &WpComSiteId(WpComTestCredentials::instance().site_id), + &SubscribersListParams { + include: vec![ListSubscribersIncludeField::Country], + ..Default::default() + }, + ) + .await + .assert_response(); + assert!( + subscribers.data.total > 0, + "Retrieved no subscribers: {subscribers:#?}" + ); + let first_subscriber = subscribers + .data + .subscribers + .first() + .expect("Confirmed that there is at least one subscriber"); + assert!( + first_subscriber.country.is_some(), + "'country' field is requested and should be available" + ); +} + #[tokio::test] #[apply(retrieve_cases)] #[parallel]