Comment REST API route: add support for filtering by multiple statuses. - #9870
Comment REST API route: add support for filtering by multiple statuses.#9870adamsilverstein wants to merge 27 commits into
Conversation
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
We might not need to extend the REST API itself. See WordPress/gutenberg#71271 (comment) |
Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com>
Good point; that said its probably worth adding as its a simple change and reflects what the underlying core comments class already supports (so it feels like a bug or oversight that it isn't supported already). While we don't need it yet, I could easily see us or a plugin wanting this. |
| @@ -1681,7 +1681,7 @@ public function get_collection_params() { | |||
| 'default' => 'approve', | |||
| 'description' => __( 'Limit result set to comments assigned a specific status. Requires authorization.' ), | |||
| 'sanitize_callback' => 'sanitize_key', | |||
There was a problem hiding this comment.
This is an issue since it only does anything for strings.
There was a problem hiding this comment.
Good point. I'll expand this to handle arrays based on what we do in posts. Should be straightforward to recuse existing code for each item.
There was a problem hiding this comment.
Added handling in 5fba5f6 calling sanitize_key for each status.
There was a problem hiding this comment.
Actually @TimothyBJacobs I'm not sure we need this -
- sanitization only called when creating or updating a comment - in this case we should still only accept a single status value, right? sanitize_key would throw an error, maybe thats what we want?
Also, while writing tests, I noticed as far as I can see we already strictly enforce status values from a list in WP_Test_REST_Comments_Controller::handle_status_param. Shouldn't the endpoint accept custom status values like WP_Comment_Query?
There was a problem hiding this comment.
handle_status_param is for the writes though, right? This is the arg list for the query parameters so they'd be a bit different.
Are custom comment status really supported by Core? wp_get_comment_status makes it seem like the list is fixed.
We should probably have an enum keyword with all of the supported statuses I think?
There was a problem hiding this comment.
Thanks for the feedback @TimothyBJacobs
We don't need this immediately so I am deprioritizing it for the moment.
There was a problem hiding this comment.
Picking this back up. I updated the param to a proper array of strings with each value run through sanitize_key, following the pattern of the posts controller, and added tests for the sanitize callback directly.
On the enum question: I looked at it and decided against for now. WP_Comment_Query explicitly accepts custom comment statuses, and the endpoint currently accepts any string here (eg. spam and trash work today for users with edit_posts), so an enum limited to the registered statuses would be a back compat break. What do you think?
Worth noting that while testing I found the status comparison in get_items_permissions_check needed updating too - with the param now an array it never string-matched approve, which broke unauthenticated requests. Fixed by comparing against the parsed list, with tests covering the authorization boundary.
…roller.php Co-authored-by: Mukesh Panchal <mukeshpanchal27@users.noreply.github.com>
Co-authored-by: Mukesh Panchal <mukeshpanchal27@users.noreply.github.com>
With the status collection param now an array, the sanitized value never string-matches 'approve', so unauthenticated requests using the default status were rejected. Compare against the parsed list instead, define the schema as an array of strings per review feedback, and regenerate the QUnit fixture to match.
Co-authored-by: Weston Ruter <westonruter@gmail.com>
| public function sanitize_comment_statuses( $statuses ): array { | ||
| $statuses = wp_parse_list( $statuses ); | ||
| return array_values( array_filter( array_unique( array_map( 'sanitize_key', $statuses ) ) ) ); | ||
| } |
There was a problem hiding this comment.
True, but '0' is not a valid status. So this doesn't seem necessary.
There was a problem hiding this comment.
I thought so too, but it turns out '0' is queryable: WP_Comment_Query passes unrecognized statuses straight through to a comment_approved match (its default case), and held comments are stored with comment_approved of '0'. So ?status=0 works today as an alias for hold for authorized users, and sanitize_key preserves it - only the bare array_filter() dropped it. I made the filter explicit in 6dd63d2 and added the '0' cases to the sanitization tests.
westonruter
left a comment
There was a problem hiding this comment.
Good to add this test: #9870 (comment)
Otherwise, pre-approving.
| foreach ( $protected_params as $param ) { | ||
| if ( 'status' === $param ) { | ||
| if ( 'approve' !== $request[ $param ] ) { | ||
| if ( array( 'approve' ) !== $request[ $param ] ) { |
There was a problem hiding this comment.
Oh, is there a back-compat concern with the status arg now becoming an array of strings? Could there be any plugins that are looking at this parsed request param which would fail now?
There was a problem hiding this comment.
Good question. The raw query string is unchanged, but once sanitization runs (which happens before the permission callback), $request['status'] is always an array - so a plugin doing a string comparison against the parsed param, eg. 'approve' === $request['status'] inside a rest_comment_query filter callback, would now see array( 'approve' ) and fail. $prepared_args['status'] handed to WP_Comment_Query also becomes an array, though that part is transparent since the query class accepts both forms.
So yes, there is a narrow back compat surface for code inspecting the parsed request param directly. It matches the shape the posts controller status param has had since 4.7 ([39104], https://core.trac.wordpress.org/ticket/38420), so the pattern is well established. I'd rather call this out in the dev note than try to preserve the string shape for single values, which would make the parsed type unpredictable. What do you think?
There was a problem hiding this comment.
I can try searching https://veloria.dev/ but may be hard to be sure without a ton of digging. I can also merge this in early 7.2 so there is plenty of time to get feedback.
The unauthenticated 401 boundary was only covered for the array form of the status parameter. Since the permission check now relies on the sanitize callback having parsed the raw value, the comma-separated form needs its own case so it cannot regress independently. Convert the test to a data provider covering both forms.
array_filter() without a callback drops all falsy strings, including '0'. WP_Comment_Query treats a literal '0' status as a direct comment_approved match (how held comments are stored), so ?status=0 is a working query today and must survive sanitization. Filter only empty strings instead.
| @@ -1777,9 +1777,12 @@ public function get_collection_params() { | |||
|
|
|||
| $query_params['status'] = array( | |||
| 'default' => 'approve', | |||
There was a problem hiding this comment.
@adamsilverstein Why this one is still string instead of array?
There was a problem hiding this comment.
I guess the endpoint will accept a string or array? Still for consistency you are right this should probably be an array.
Add support for filtering the comments collection endpoint by multiple statuses, eg.
/wp/v2/comments?status=approve,holdorstatus[]=approve&status[]=hold.WP_Comment_Queryalready accepts an array of statuses, so this brings the REST endpoint in line with what the underlying query supports. Single string values continue to work as before.Originally extracted from the Notes work, see WordPress/gutenberg#71271 (WordPress/gutenberg#71271 (comment))
Changes:
statuscollection param as an array of strings, each sanitized withsanitize_key, following the pattern of the posts controller.get_items_permissions_checkto handle array values. Behavior is unchanged: unauthenticated requests can only use the defaultapprovestatus, anything else requiresedit_posts.allstatus queries, sanitization of the param, and the authorization boundary.Note: I did not add an
enumof allowed values here -WP_Comment_Queryexplicitly supports custom comment statuses and the endpoint currently accepts any string (eg.spamandtrashwork today for authorized users), so an enum limited to the registered statuses would be a back compat break.Testing instructions
curl --user admin:APP_PASSWORD "https://example.test/wp-json/wp/v2/comments?status=approve,hold"?status=hold, and verify only pending comments are returned.?status=approvestill returns approved comments and?status=approve,holdreturns arest_forbidden_paramerror.npm run test:php -- --filter WP_Test_REST_Comments_ControllerTrac ticket: https://core.trac.wordpress.org/ticket/63982
This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.