Skip to content

Commit 3df0ded

Browse files
authored
Add new WpApiParamCommentsStatus to be used in query (#1560)
* Add failing test for the comments list status filter * Use a request-side status type for the comments list query WP_Comment_Query recognizes the query values approve, hold, all, and any, and treats anything else as a literal comment_approved value. The response vocabulary value approved therefore silently returned an empty result set. CommentListParams.status is now Option<WpApiParamCommentsStatus>, which also adds typed all and any values that were previously only reachable via Custom. * Re-export WpApiParamCommentsStatus from the public Swift module * Add changelog entry for the typed comment list status * Cover all and any in the comment status forbidden-param test * Fix the parent_exclude comment list case sending parent
1 parent d813414 commit 3df0ded

6 files changed

Lines changed: 134 additions & 22 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020

2121
### Changed
2222

23+
- **BREAKING:** `CommentListParams.status` now takes `WpApiParamCommentsStatus` instead of `CommentStatus`, fixing `status=approved` silently returning no comments and adding typed `all`/`any`. Use `.approve` in place of `Custom("approve")`.
2324
- **BREAKING:** `product_type` fields on `Product` and `WPComProduct` changed from `String` to `ProductType`. Callers that match on or construct these values will need to wrap/unwrap with `ProductType(...)`.
2425
- **BREAKING:** The cache now enables SQLite foreign key enforcement on every connection it prepares, and fails with `SqliteDbError::ForeignKeysUnavailable` if the setting doesn't take effect. Removing a site relies on `ON DELETE CASCADE` to clear its cached rows, so on builds where enforcement defaulted to off those rows were silently left behind.
2526
- **BREAKING:** `ShoppingCart.coupon` changed from `String` to `CouponCode`, and `ShoppingCartCostOverride.override_code` from `String` to `CostOverrideCode`, so the shopping cart and site plans describe these values with the same types. Callers will need to wrap/unwrap with `CouponCode(...)` / `CostOverrideCode(...)`.

native/swift/Sources/wordpress-api/Exports.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ public typealias CommentCreateParams = WordPressAPIInternal.CommentCreateParams
186186
public typealias CommentUpdateParams = WordPressAPIInternal.CommentUpdateParams
187187
public typealias CommentDeleteParams = WordPressAPIInternal.CommentDeleteParams
188188
public typealias CommentStatus = WordPressAPIInternal.CommentStatus
189+
public typealias WpApiParamCommentsStatus = WordPressAPIInternal.WpApiParamCommentsStatus
189190
public typealias CommentType = WordPressAPIInternal.CommentType
190191
public typealias CommentsRequestExecutor = WordPressAPIInternal.CommentsRequestExecutor
191192

wp_api/src/comments.rs

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,12 @@ pub struct CommentListParams {
130130
pub post: Vec<PostId>,
131131
/// Limit result set to comments assigned a specific status. Requires authorization.
132132
/// Default: approve
133+
///
134+
/// This is a [`WpApiParamCommentsStatus`], not a [`CommentStatus`],
135+
/// because the query vocabulary differs from the stored status
136+
/// vocabulary. See the type's documentation.
133137
#[uniffi(default = None)]
134-
pub status: Option<CommentStatus>,
138+
pub status: Option<WpApiParamCommentsStatus>,
135139
/// Limit result set to comments assigned a specific type. Requires authorization.
136140
/// Default: comment
137141
#[uniffi(default = None)]
@@ -465,6 +469,54 @@ fn comment_status_from_string(value: String) -> CommentStatus {
465469
CommentStatus::from_str(value.as_str()).unwrap_or(CommentStatus::Custom(value))
466470
}
467471

472+
/// Comment status values accepted by the `status` query param when listing
473+
/// comments.
474+
///
475+
/// This is deliberately a different type from [`CommentStatus`]. WordPress
476+
/// core's `WP_Comment_Query` recognizes the query values `approve`, `hold`,
477+
/// `all`, and `any`, and passes any other value through as a literal
478+
/// `comment_approved` column value. Approved comments are stored as `'1'`,
479+
/// so querying with the stored/response vocabulary (`status=approved`)
480+
/// matches nothing and silently returns an empty result set.
481+
#[derive(
482+
Debug,
483+
Clone,
484+
PartialEq,
485+
Eq,
486+
PartialOrd,
487+
Ord,
488+
Hash,
489+
Serialize,
490+
Deserialize,
491+
uniffi::Enum,
492+
strum_macros::EnumString,
493+
strum_macros::Display,
494+
)]
495+
#[uniffi::export(Display)]
496+
#[serde(rename_all = "snake_case")]
497+
#[strum(serialize_all = "snake_case")]
498+
pub enum WpApiParamCommentsStatus {
499+
/// Approved and pending (hold) comments. Spam and trash are excluded.
500+
All,
501+
/// Every comment regardless of status, including spam, trash, and
502+
/// custom statuses. `WP_Comment_Query` removes the status predicate
503+
/// entirely for this value.
504+
Any,
505+
/// Approved comments. This is the server default when no status is sent,
506+
/// and the only value permitted for unauthenticated requests.
507+
Approve,
508+
/// Pending comments.
509+
Hold,
510+
Spam,
511+
Trash,
512+
/// A custom `comment_approved` value, passed through as a literal.
513+
#[serde(untagged)]
514+
#[strum(default)]
515+
Custom(String),
516+
}
517+
518+
impl_as_query_value_from_to_string!(WpApiParamCommentsStatus);
519+
468520
#[uniffi::export]
469521
fn comment_type_from_string(value: String) -> CommentType {
470522
CommentType::from_str(value.as_str()).unwrap_or(CommentType::Custom(value))
@@ -509,11 +561,13 @@ mod tests {
509561
#[case(generate!(CommentListParams, (parent, vec![CommentId(44444), CommentId(44445)])), "parent=44444%2C44445")]
510562
#[case(generate!(CommentListParams, (parent_exclude, vec![CommentId(55555), CommentId(55556)])), "parent_exclude=55555%2C55556")]
511563
#[case(generate!(CommentListParams, (post, vec![PostId(66666), PostId(66667)])), "post=66666%2C66667")]
512-
#[case(generate!(CommentListParams, (status, Some(CommentStatus::Hold))), "status=hold")]
513-
#[case(generate!(CommentListParams, (status, Some(CommentStatus::Approved))), "status=approved")]
514-
#[case(generate!(CommentListParams, (status, Some(CommentStatus::Spam))), "status=spam")]
515-
#[case(generate!(CommentListParams, (status, Some(CommentStatus::Trash))), "status=trash")]
516-
#[case(generate!(CommentListParams, (status, Some(CommentStatus::Custom("foo".to_string())))), "status=foo")]
564+
#[case(generate!(CommentListParams, (status, Some(WpApiParamCommentsStatus::All))), "status=all")]
565+
#[case(generate!(CommentListParams, (status, Some(WpApiParamCommentsStatus::Any))), "status=any")]
566+
#[case(generate!(CommentListParams, (status, Some(WpApiParamCommentsStatus::Approve))), "status=approve")]
567+
#[case(generate!(CommentListParams, (status, Some(WpApiParamCommentsStatus::Hold))), "status=hold")]
568+
#[case(generate!(CommentListParams, (status, Some(WpApiParamCommentsStatus::Spam))), "status=spam")]
569+
#[case(generate!(CommentListParams, (status, Some(WpApiParamCommentsStatus::Trash))), "status=trash")]
570+
#[case(generate!(CommentListParams, (status, Some(WpApiParamCommentsStatus::Custom("foo".to_string())))), "status=foo")]
517571
#[case(generate!(CommentListParams, (comment_type, Some(CommentType::Comment))), "type=comment")]
518572
#[case(generate!(CommentListParams, (comment_type, Some(CommentType::Pingback))), "type=pingback")]
519573
#[case(generate!(CommentListParams, (comment_type, Some(CommentType::Trackback))), "type=trackback")]
@@ -536,7 +590,7 @@ mod tests {
536590
parent: vec![CommentId(44444), CommentId(44445)],
537591
parent_exclude: vec![CommentId(55555), CommentId(55556)],
538592
post: vec![PostId(66666), PostId(66667)],
539-
status: Some(CommentStatus::Spam),
593+
status: Some(WpApiParamCommentsStatus::Spam),
540594
comment_type: Some(CommentType::Pingback),
541595
password: Some("p_q".to_string()),
542596
},

wp_api/src/request/endpoint/comments_endpoint.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ mod tests {
3838
use crate::{
3939
UserId, WpApiParamOrder,
4040
comments::{
41-
CommentDeleteParams, CommentId, CommentRetrieveParams, CommentStatus, CommentType,
41+
CommentDeleteParams, CommentId, CommentRetrieveParams, CommentType,
4242
SparseCommentFieldWithEditContext, SparseCommentFieldWithEmbedContext,
43-
SparseCommentFieldWithViewContext, WpApiParamCommentsOrderBy,
43+
SparseCommentFieldWithViewContext, WpApiParamCommentsOrderBy, WpApiParamCommentsStatus,
4444
},
4545
generate,
4646
posts::PostId,
@@ -80,11 +80,13 @@ mod tests {
8080
#[case(generate!(CommentListParams, (parent, vec![CommentId(44444), CommentId(44445)])), "parent=44444%2C44445")]
8181
#[case(generate!(CommentListParams, (parent_exclude, vec![CommentId(55555), CommentId(55556)])), "parent_exclude=55555%2C55556")]
8282
#[case(generate!(CommentListParams, (post, vec![PostId(66666), PostId(66667)])), "post=66666%2C66667")]
83-
#[case(generate!(CommentListParams, (status, Some(CommentStatus::Hold))), "status=hold")]
84-
#[case(generate!(CommentListParams, (status, Some(CommentStatus::Approved))), "status=approved")]
85-
#[case(generate!(CommentListParams, (status, Some(CommentStatus::Spam))), "status=spam")]
86-
#[case(generate!(CommentListParams, (status, Some(CommentStatus::Trash))), "status=trash")]
87-
#[case(generate!(CommentListParams, (status, Some(CommentStatus::Custom("foo".to_string())))), "status=foo")]
83+
#[case(generate!(CommentListParams, (status, Some(WpApiParamCommentsStatus::All))), "status=all")]
84+
#[case(generate!(CommentListParams, (status, Some(WpApiParamCommentsStatus::Any))), "status=any")]
85+
#[case(generate!(CommentListParams, (status, Some(WpApiParamCommentsStatus::Approve))), "status=approve")]
86+
#[case(generate!(CommentListParams, (status, Some(WpApiParamCommentsStatus::Hold))), "status=hold")]
87+
#[case(generate!(CommentListParams, (status, Some(WpApiParamCommentsStatus::Spam))), "status=spam")]
88+
#[case(generate!(CommentListParams, (status, Some(WpApiParamCommentsStatus::Trash))), "status=trash")]
89+
#[case(generate!(CommentListParams, (status, Some(WpApiParamCommentsStatus::Custom("foo".to_string())))), "status=foo")]
8890
#[case(generate!(CommentListParams, (comment_type, Some(CommentType::Comment))), "type=comment")]
8991
#[case(generate!(CommentListParams, (comment_type, Some(CommentType::Pingback))), "type=pingback")]
9092
#[case(generate!(CommentListParams, (comment_type, Some(CommentType::Trackback))), "type=trackback")]
@@ -194,7 +196,7 @@ mod tests {
194196
parent: vec![CommentId(44444), CommentId(44445)],
195197
parent_exclude: vec![CommentId(55555), CommentId(55556)],
196198
post: vec![PostId(66666), PostId(66667)],
197-
status: Some(CommentStatus::Spam),
199+
status: Some(WpApiParamCommentsStatus::Spam),
198200
comment_type: Some(CommentType::Pingback),
199201
password: Some("p_q".to_string()),
200202
}

wp_api_integration_tests/tests/test_comments_err.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use wp_api::{
22
comments::{
33
CommentCreateParams, CommentCreateParamsBuilder, CommentDeleteParams, CommentListParams,
44
CommentRetrieveParams, CommentStatus, CommentType, CommentUpdateParams,
5+
WpApiParamCommentsStatus,
56
},
67
posts::PostId,
78
};
@@ -321,7 +322,14 @@ async fn list_err_forbidden_param_comment_type(
321322
#[rstest]
322323
#[parallel]
323324
async fn list_err_forbidden_param_status(
324-
#[values(CommentStatus::Hold, CommentStatus::Spam, CommentStatus::Trash)] status: CommentStatus,
325+
#[values(
326+
WpApiParamCommentsStatus::Hold,
327+
WpApiParamCommentsStatus::All,
328+
WpApiParamCommentsStatus::Any,
329+
WpApiParamCommentsStatus::Spam,
330+
WpApiParamCommentsStatus::Trash
331+
)]
332+
status: WpApiParamCommentsStatus,
325333
) {
326334
api_client_as_subscriber()
327335
.comments()

wp_api_integration_tests/tests/test_comments_immut.rs

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use wp_api::{
33
comments::{
44
CommentId, CommentListParams, CommentRetrieveParams, CommentStatus, CommentType,
55
SparseCommentFieldWithEditContext, SparseCommentFieldWithEmbedContext,
6-
SparseCommentFieldWithViewContext, WpApiParamCommentsOrderBy,
6+
SparseCommentFieldWithViewContext, WpApiParamCommentsOrderBy, WpApiParamCommentsStatus,
77
},
88
posts::PostId,
99
users::UserAvatarSize,
@@ -43,6 +43,50 @@ async fn list_with_view_context(#[case] params: CommentListParams) {
4343
.assert_response();
4444
}
4545

46+
#[tokio::test]
47+
#[parallel]
48+
async fn list_with_status_approve_returns_comments() {
49+
let response = api_client()
50+
.comments()
51+
.list_with_edit_context(&CommentListParams {
52+
status: Some(WpApiParamCommentsStatus::Approve),
53+
per_page: Some(100),
54+
..Default::default()
55+
})
56+
.await
57+
.assert_response();
58+
assert!(
59+
!response.data.is_empty(),
60+
"listing comments with the approved status filter must not return an empty set"
61+
);
62+
}
63+
64+
#[tokio::test]
65+
#[parallel]
66+
async fn list_with_status_all_and_any_are_supersets() {
67+
let count = |status| async move {
68+
api_client()
69+
.comments()
70+
.list_with_edit_context(&CommentListParams {
71+
status: Some(status),
72+
per_page: Some(100),
73+
..Default::default()
74+
})
75+
.await
76+
.assert_response()
77+
.data
78+
.len()
79+
};
80+
let approve = count(WpApiParamCommentsStatus::Approve).await;
81+
let all = count(WpApiParamCommentsStatus::All).await;
82+
let any = count(WpApiParamCommentsStatus::Any).await;
83+
// The test site seeds hold comments (excluded from approve) and
84+
// spam/trash comments (excluded from all).
85+
assert!(approve > 0, "approve must not be empty");
86+
assert!(all > approve, "all (approve + hold) must exceed approve");
87+
assert!(any > all, "any (no status filter) must exceed all");
88+
}
89+
4690
#[tokio::test]
4791
#[parallel]
4892
async fn retrieve_with_edit_context() {
@@ -242,12 +286,14 @@ async fn parse_extras() {
242286
#[case::order(generate!(CommentListParams, (order, Some(WpApiParamOrder::Asc))))]
243287
#[case::orderby(generate!(CommentListParams, (orderby, Some(WpApiParamCommentsOrderBy::Id))))]
244288
#[case::parent(generate!(CommentListParams, (parent, vec![CommentId(1), CommentId(2)])))]
245-
#[case::parent_exclude(generate!(CommentListParams, (parent, vec![CommentId(1), CommentId(2)])))]
289+
#[case::parent_exclude(generate!(CommentListParams, (parent_exclude, vec![CommentId(1), CommentId(2)])))]
246290
#[case::post(generate!(CommentListParams, (post, vec![PostId(1), PostId(2)])))]
247-
#[case::status_hold(generate!(CommentListParams, (status, Some(CommentStatus::Hold))))]
248-
#[case::status_approve(generate!(CommentListParams, (status, Some(CommentStatus::Approved))))]
249-
#[case::status_spam(generate!(CommentListParams, (status, Some(CommentStatus::Spam))))]
250-
#[case::status_trash(generate!(CommentListParams, (status, Some(CommentStatus::Trash))))]
291+
#[case::status_hold(generate!(CommentListParams, (status, Some(WpApiParamCommentsStatus::Hold))))]
292+
#[case::status_approve(generate!(CommentListParams, (status, Some(WpApiParamCommentsStatus::Approve))))]
293+
#[case::status_all(generate!(CommentListParams, (status, Some(WpApiParamCommentsStatus::All))))]
294+
#[case::status_any(generate!(CommentListParams, (status, Some(WpApiParamCommentsStatus::Any))))]
295+
#[case::status_spam(generate!(CommentListParams, (status, Some(WpApiParamCommentsStatus::Spam))))]
296+
#[case::status_trash(generate!(CommentListParams, (status, Some(WpApiParamCommentsStatus::Trash))))]
251297
#[case::comment_type_comment(generate!(CommentListParams, (comment_type, Some(CommentType::Comment))))]
252298
#[case::comment_type_pingback(generate!(CommentListParams, (comment_type, Some(CommentType::Pingback))))]
253299
#[case::comment_type_trackback(generate!(CommentListParams, (comment_type, Some(CommentType::Trackback))))]

0 commit comments

Comments
 (0)