Abilities API: Refine filtering and expose meta over REST#12233
Conversation
Narrow the `category` argument of `wp_get_abilities()` to a single string so it matches `namespace`. Both arguments now take one slug. Callers that need to match several values can use `item_include_callback`. Starting simple keeps the public surface predictable and leaves room to accept arrays later without a breaking change. Add a `meta` query parameter to the REST abilities list endpoint, alongside `category` and `namespace`. The schema declares the well-defined behavioral annotations (`readonly`, `destructive`, `idempotent`), so a query-string value such as "true" is coerced to a boolean before matching. The forced `show_in_rest` condition always wins, so a caller cannot use meta to reveal abilities that are hidden from REST. Expand unit test coverage for the single-string category, the REST meta parameter, AND logic across several conditions, and the `show_in_rest` guard. Regenerate the REST API client fixture for the new query parameter. Fixes #64990. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
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. |
|
Hi there! 👋 Thank you for your contribution to WordPress! 💖 It looks like this is your first pull request to No one monitors this repository for new pull requests. Pull requests must be attached to a Trac ticket to be considered for inclusion in WordPress Core. To attach a pull request to a Trac ticket, please include the ticket's full URL in your pull request description. Pull requests are never merged on GitHub. The WordPress codebase continues to be managed through the SVN repository that this GitHub repository mirrors. Please feel free to open pull requests to work on any contribution you are making. More information about how GitHub pull requests can be used to contribute to WordPress can be found in the Core Handbook. Please include automated tests. Including tests in your pull request is one way to help your patch be considered faster. To learn about WordPress' test suites, visit the Automated Testing page in the handbook. If you have not had a chance, please review the Contribute with Code page in the WordPress Core Handbook. The Developer Hub also documents the various coding standards that are followed:
Thank you, |
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. |
jorgefilipecosta
left a comment
There was a problem hiding this comment.
I think we may have an issue. On ticket https://core.trac.wordpress.org/ticket/64990 we refer the case of filtering mcp.public === true.
But currently these filters don't work because mcp is an additional property, REST leaves the query-string value as 'true', while _wp_get_abilities_match_meta() strictly compares it with boolean true, so the ability is excluded.
I had this test case a reproduction of the bug:
<?php
class Tests_PR_12233_Custom_Meta extends WP_UnitTestCase {
protected $server;
public function set_up(): void {
parent::set_up();
global $wp_current_filter, $wp_rest_server;
$wp_current_filter[] = 'wp_abilities_api_categories_init';
wp_register_ability_category(
'probe',
array(
'label' => 'Probe',
'description' => 'Probe category.',
)
);
array_pop( $wp_current_filter );
$wp_current_filter[] = 'wp_abilities_api_init';
wp_register_ability(
'probe/custom-meta',
array(
'label' => 'Custom meta',
'description' => 'Custom boolean meta.',
'category' => 'probe',
'execute_callback' => '__return_true',
'permission_callback' => '__return_true',
'meta' => array(
'show_in_rest' => true,
'mcp' => array( 'public' => true ),
),
)
);
array_pop( $wp_current_filter );
$wp_rest_server = new WP_REST_Server();
$this->server = $wp_rest_server;
do_action( 'rest_api_init' );
wp_set_current_user( self::factory()->user->create( array( 'role' => 'subscriber' ) ) );
}
public function tear_down(): void {
global $wp_rest_server;
wp_unregister_ability( 'probe/custom-meta' );
wp_unregister_ability_category( 'probe' );
$wp_rest_server = null;
parent::tear_down();
}
public function test_query_string_boolean_for_custom_meta_matches(): void {
$request = new WP_REST_Request( 'GET', '/wp-abilities/v1/abilities' );
$request->set_query_params(
array(
'meta' => array(
'mcp' => array( 'public' => 'true' ),
),
)
);
$response = $this->server->dispatch( $request );
$names = wp_list_pluck( $response->get_data(), 'name' );
$this->assertContains( 'probe/custom-meta', $names );
}
}The solution for this is not clear I guess the possibility is to scope the REST parameter to the explicitly declared annotation fields. But that removes filtering options. Or we extend the API so when someone uses a an annotation they can pass additional schema information.
| 'properties' => array( | ||
| 'annotations' => array( | ||
| 'description' => __( 'Annotations for the ability.' ), | ||
| 'type' => array( 'boolean', 'null' ), |
There was a problem hiding this comment.
Should we update the type here to match the type we are using on the new added code on get_collection_params?
|
Hi @gziolo the PR seems to be in a good shape, just left some comments for consideration. |
| } | ||
|
|
||
| if ( ! empty( $request['meta'] ) ) { | ||
| // Merge caller meta first so the forced show_in_rest filter always wins. |
There was a problem hiding this comment.
looks like a typical AI tutorial style comment, i'd remove it as it just states the obvious
There was a problem hiding this comment.
It was deliberately added to ensure it doesn't regress to allow fetching abilities that were explicitly excluded from the REST API. If any, that can be rephrased if you find it unclear.
| // Step 1a: Filter by category (OR logic within the arg). | ||
| if ( ! empty( $category ) && ! in_array( $ability->get_category(), $category, true ) ) { | ||
| // Step 1a: Filter by category. | ||
| if ( '' !== $category && $ability->get_category() !== $category ) { |
There was a problem hiding this comment.
did you verify that the logic remains the same?
There was a problem hiding this comment.
As mentioned in the ticket and the description of this PR, this is an anticipated change to align with the current handling of namespace and category through the REST API. I committed the initial implementation during this release cycle, so there’s still room for refinement in the implementation.
Trac ticket: https://core.trac.wordpress.org/ticket/64990
Summary
This addresses the remaining feedback on filtering support for
wp_get_abilities(). It makes the declarative filter arguments consistent and exposesmetafiltering through the REST API.Details
Single-string
category. Thecategoryargument now takes one slug, the same shape asnamespace. Both do an exact match on a single value. A caller that needs to match several values can useitem_include_callback. Starting with a single string keeps the public surface simple and predictable, and it leaves room to accept arrays later without a breaking change. The reverse (arrays now, single value later) could not be undone.REST
metaparameter. The abilities list endpoint (/wp-abilities/v1/abilities) now accepts ametaquery parameter, next tocategoryandnamespace. Conditions combine with AND logic and may be nested. The schema declares the well-defined behavioral annotations (readonly,destructive,idempotent), so a query-string value such astrueis coerced to a real boolean before matching. Open-ended meta keys still pass through as sent.Visibility stays protected. The endpoint always forces
meta[show_in_rest] => true. Caller meta is merged first, so this forced condition always wins. A caller cannot usemetato reveal abilities that are hidden from REST.Testing
New and updated unit tests cover:
categoryas a single string, and that a non-string value is ignored.metaparameter, including string-to-boolean coercion for annotations.show_in_restguard, so meta cannot widen visibility.Note
The
tests/qunit/fixtures/wp-api-generated.jschange is the auto-generated REST API client fixture, regenerated to add the newmetaquery parameter.🤖 Generated with Claude Code