Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/1.intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,10 @@ function my_plugin_register_ability(){
'permission_callback' => function( $input ) {
return current_user_can( 'manage_options' );
},
'show_in_rest' => true,
));
'meta' => array(
'show_in_rest' => true,
),
) );
}
```

Expand Down
5 changes: 2 additions & 3 deletions docs/2.getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,8 @@ function my_plugin_register_abilities() {
'execute_callback' => 'my_plugin_get_site_title',
'permission_callback' => '__return_true', // Everyone can access this
'meta' => array(
'category' => 'site-info',
),
'show_in_rest' => true, // Optional: expose via REST API
'category' => 'site-info',
'show_in_rest' => true, // Optional: expose via REST API
) );
}

Expand Down
6 changes: 3 additions & 3 deletions docs/3.registering-abilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ The `$args` array accepts the following keys:
- The callback receives one optional argument: it can have any type as defined in the input schema (e.g., `array`, `object`, `string`, etc.).
- The callback should return a boolean (`true` if the user has permission, `false` otherwise), or a `WP_Error` object on failure.
- If the input does not validate against the input schema, the permission callback will not be called, and a `WP_Error` will be returned instead.
- `show_in_rest` (`boolean`, **Optional**): Whether to expose this ability via the REST API. Default: `false`.
- When `true`, the ability will be listed in REST API responses and can be executed via REST endpoints.
- When `false`, the ability will be hidden from REST API listings and cannot be executed via REST endpoints, but remains available for internal PHP usage.
- `meta` (`array`, **Optional**): An associative array for storing arbitrary additional metadata about the ability.
- `show_in_rest` (`boolean`, **Optional**): Whether to expose this ability via the REST API. Default: `false`.
- When `true`, the ability will be listed in REST API responses and can be executed via REST endpoints.
- When `false`, the ability will be hidden from REST API listings and cannot be executed via REST endpoints, but remains available for internal PHP usage.

## Ability ID Convention

Expand Down
4 changes: 2 additions & 2 deletions docs/5.rest-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ Access to all Abilities REST API endpoints requires an authenticated user (see t

## Controlling REST API Exposure

By default, registered abilities are **not** exposed via the REST API. You can control whether an individual ability appears in the REST API by using the `show_in_rest` argument when registering the ability:
By default, registered abilities are **not** exposed via the REST API. You can control whether an individual ability appears in the REST API by using the `show_in_rest` meta when registering the ability:

- `show_in_rest => true`: The ability is listed in REST API responses and can be executed via REST endpoints.
- `show_in_rest => false` (default): The ability is hidden from REST API listings and cannot be executed via REST endpoints. The ability remains available for internal PHP usage via `wp_execute_ability()`.

Abilities with `show_in_rest => false` will return a `rest_ability_not_found` error if accessed via REST endpoints.
Abilities with meta `show_in_rest => false` will return a `rest_ability_not_found` error if accessed via REST endpoints.

## Schema

Expand Down
10 changes: 6 additions & 4 deletions includes/abilities-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
* alphanumeric characters, dashes and the forward slash.
* @param array<string,mixed> $args An associative array of arguments for the ability. This should include
* `label`, `description`, `input_schema`, `output_schema`, `execute_callback`,
* `permission_callback`, `annotations`, `meta`, `show_in_rest`, and `ability_class`.
* `permission_callback`, `annotations`, `meta`, and `ability_class`.
* @return ?\WP_Ability An instance of registered ability on success, null on failure.
*
* @phpstan-param array{
Expand All @@ -35,9 +35,11 @@
* permission_callback?: callable( mixed $input= ): (bool|\WP_Error),
* input_schema?: array<string,mixed>,
* output_schema?: array<string,mixed>,
* annotations?: array<string,mixed>,
* meta?: array<string,mixed>,
* show_in_rest?: bool,
* annotations?: array<string,(bool|string)>,
* meta?: array{
* show_in_rest?: bool,
* ...<string,mixed>,
* },
* ability_class?: class-string<\WP_Ability>,
* ...<string, mixed>
* } $args
Expand Down
8 changes: 5 additions & 3 deletions includes/abilities-api/class-wp-abilities-registry.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,11 @@ final class WP_Abilities_Registry {
* permission_callback?: callable( mixed $input= ): (bool|\WP_Error),
* input_schema?: array<string,mixed>,
* output_schema?: array<string,mixed>,
* annotations?: array<string,mixed>,
* meta?: array<string,mixed>,
* show_in_rest?: bool,
* annotations?: array<string,(bool|string)>,
* meta?: array{
* show_in_rest?: bool,
* ...<string, mixed>
* },
* ability_class?: class-string<\WP_Ability>,
* ...<string, mixed>
* } $args
Expand Down
51 changes: 31 additions & 20 deletions includes/abilities-api/class-wp-ability.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@
* @see WP_Abilities_Registry
*/
class WP_Ability {

/**
* The default value for the `show_in_rest` meta.
*
* @since n.e.x.t
* @var bool
*/
protected const DEFAULT_SHOW_IN_REST = false;

/**
* The default ability annotations.
* They are not guaranteed to provide a faithful description of ability behavior.
Expand Down Expand Up @@ -114,15 +123,7 @@ class WP_Ability {
* @since 0.1.0
* @var array<string,mixed>
*/
protected $meta = array();

/**
* Whether to show the ability in the REST API.
*
* @since n.e.x.t
* @var bool
*/
protected $show_in_rest = false;
protected $meta;

/**
* Constructor.
Expand All @@ -138,7 +139,7 @@ class WP_Ability {
* @param string $name The name of the ability, with its namespace.
* @param array<string,mixed> $args An associative array of arguments for the ability. This should include
* `label`, `description`, `input_schema`, `output_schema`, `execute_callback`,
* `permission_callback`, `annotations`, `meta`, and `show_in_rest`.
* `permission_callback`, `annotations`, and `meta`.
*/
public function __construct( string $name, array $args ) {
$this->name = $name;
Expand Down Expand Up @@ -186,9 +187,11 @@ public function __construct( string $name, array $args ) {
* permission_callback: callable( mixed $input= ): (bool|\WP_Error),
* input_schema?: array<string,mixed>,
* output_schema?: array<string,mixed>,
* annotations?: array<string,mixed>,
* meta?: array<string,mixed>,
* show_in_rest?: bool,
* annotations?: array<string,(bool|string)>,
* meta?: array{
* show_in_rest?: bool,
* ...<string, mixed>
* },
* ...<string, mixed>,
* } $args
*/
Expand Down Expand Up @@ -243,17 +246,23 @@ protected function prepare_properties( array $args ): array {
);
}

if ( isset( $args['show_in_rest'] ) && ! is_bool( $args['show_in_rest'] ) ) {
if ( isset( $args['meta']['show_in_rest'] ) && ! is_bool( $args['meta']['show_in_rest'] ) ) {
throw new \InvalidArgumentException(
esc_html__( 'The ability properties should provide a valid `show_in_rest` boolean.' )
esc_html__( 'The ability meta should provide a valid `show_in_rest` boolean.' )
);
}

// Set defaults for optional args.
// Set defaults for optional meta.
$args['annotations'] = wp_parse_args(
$args['annotations'] ?? array(),
static::$default_annotations
);
$args['meta'] = wp_parse_args(
$args['meta'] ?? array(),
array(
'show_in_rest' => self::DEFAULT_SHOW_IN_REST,
)
);

return $args;
}
Expand Down Expand Up @@ -337,14 +346,16 @@ public function get_meta(): array {
}

/**
* Checks whether the ability should be shown in the REST API.
* Retrieves a specific metadata item for the ability.
*
* @since n.e.x.t
*
* @return bool True if the ability should be shown in the REST API, false otherwise.
* @param string $key The metadata key to retrieve.
* @param mixed $default_value Optional. The default value to return if the metadata item is not found. Default `null`.
* @return mixed The value of the metadata item, or the default value if not found.
*/
public function show_in_rest(): bool {
return $this->show_in_rest;
public function get_meta_item( string $key, $default_value = null ) {
return array_key_exists( $key, $this->meta ) ? $this->meta[ $key ] : $default_value;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public function get_items( $request ) {
$abilities = array_filter(
wp_get_abilities(),
static function ( $ability ) {
return $ability->show_in_rest();
return $ability->get_meta_item( 'show_in_rest' );
}
);

Expand Down Expand Up @@ -155,7 +155,7 @@ static function ( $ability ) {
*/
public function get_item( $request ) {
$ability = wp_get_ability( $request->get_param( 'name' ) );
if ( ! $ability || ! $ability->show_in_rest() ) {
if ( ! $ability || ! $ability->get_meta_item( 'show_in_rest' ) ) {
return new \WP_Error(
'rest_ability_not_found',
__( 'Ability not found.' ),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ public function run_ability( $request ) {
*/
public function run_ability_permissions_check( $request ) {
$ability = wp_get_ability( $request->get_param( 'name' ) );
if ( ! $ability || ! $ability->show_in_rest() ) {
if ( ! $ability || ! $ability->get_meta_item( 'show_in_rest' ) ) {
return new \WP_Error(
'rest_ability_not_found',
__( 'Ability not found.' ),
Expand Down
6 changes: 3 additions & 3 deletions tests/unit/abilities-api/wpAbilitiesRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ public function set_up(): void {
return true;
},
'meta' => array(
'category' => 'math',
'category' => 'math',
'show_in_rest' => true,
),
'show_in_rest' => true,
);
}

Expand Down Expand Up @@ -307,7 +307,7 @@ public function test_register_invalid_meta_type() {
* @expectedIncorrectUsage WP_Abilities_Registry::register
*/
public function test_register_invalid_show_in_rest_type() {
self::$test_ability_args['show_in_rest'] = 5;
self::$test_ability_args['meta']['show_in_rest'] = 5;

$result = $this->registry->register( self::$test_ability_name, self::$test_ability_args );
$this->assertNull( $result );
Expand Down
73 changes: 64 additions & 9 deletions tests/unit/abilities-api/wpAbility.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,27 +117,80 @@ public function test_annotations_throws_exception() {
}

/**
* Tests that `show_in_rest` defaults to false when not provided.
* Tests that getting non-existing metadata item returns default value.
*/
public function test_show_in_rest_defaults_to_false() {
public function test_meta_get_non_existing_item_returns_default() {
$ability = new WP_Ability( self::$test_ability_name, self::$test_ability_properties );

$this->assertFalse( $ability->show_in_rest(), '`show_in_rest` should default to false.' );
$this->assertNull(
$ability->get_meta_item( 'non_existing' ),
'Non-existing metadata item should return null.'
);
}

/**
* Tests that getting non-existing metadata item with custom default returns that default.
*/
public function test_meta_get_non_existing_item_with_custom_default() {
$ability = new WP_Ability( self::$test_ability_name, self::$test_ability_properties );

$this->assertSame(
'default_value',
$ability->get_meta_item( 'non_existing', 'default_value' ),
'Non-existing metadata item should return custom default value.'
);
}

/**
* Tests that `show_in_rest` metadata defaults to false when not provided.
*/
public function test_meta_show_in_rest_defaults_to_false() {
$ability = new WP_Ability( self::$test_ability_name, self::$test_ability_properties );

$this->assertFalse(
$ability->get_meta_item( 'show_in_rest' ),
'`show_in_rest` metadata should default to false.'
);
}

/**
* Tests that `show_in_rest` can be set to true.
* Tests that `show_in_rest` metadata can be set to true.
*/
public function test_show_in_rest_can_be_set_to_true() {
public function test_meta_show_in_rest_can_be_set_to_true() {
$args = array_merge(
self::$test_ability_properties,
array(
'show_in_rest' => true,
'meta' => array(
'show_in_rest' => true,
),
)
);
$ability = new WP_Ability( self::$test_ability_name, $args );

$this->assertTrue(
$ability->get_meta_item( 'show_in_rest' ),
'`show_in_rest` metadata should be true.'
);
}

/**
* Tests that `show_in_rest` can be set to false.
*/
public function test_show_in_rest_can_be_set_to_false() {
$args = array_merge(
self::$test_ability_properties,
array(
'meta' => array(
'show_in_rest' => false,
),
)
);
$ability = new WP_Ability( self::$test_ability_name, $args );

$this->assertTrue( $ability->show_in_rest(), '`show_in_rest` should be true.' );
$this->assertFalse(
$ability->get_meta_item( 'show_in_rest' ),
'`show_in_rest` metadata should be false.'
);
}

/**
Expand All @@ -147,12 +200,14 @@ public function test_show_in_rest_throws_exception() {
$args = array_merge(
self::$test_ability_properties,
array(
'show_in_rest' => 5,
'meta' => array(
'show_in_rest' => 5,
),
)
);

$this->expectException( InvalidArgumentException::class );
$this->expectExceptionMessage( 'The ability properties should provide a valid `show_in_rest` boolean.' );
$this->expectExceptionMessage( 'The ability meta should provide a valid `show_in_rest` boolean.' );

new WP_Ability( self::$test_ability_name, $args );
}
Expand Down
7 changes: 3 additions & 4 deletions tests/unit/abilities-api/wpRegisterAbility.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ public function set_up(): void {
'destructive' => false,
),
'meta' => array(
'category' => 'math',
'category' => 'math',
'show_in_rest' => true,
),
'show_in_rest' => true,
);
}

Expand Down Expand Up @@ -147,8 +147,7 @@ public function test_register_valid_ability(): void {
),
$result->get_annotations()
);
$this->assertSame( self::$test_ability_args['meta'], $result->get_meta() );
$this->assertSame( self::$test_ability_args['show_in_rest'], $result->show_in_rest() );
$this->assertEquals( self::$test_ability_args['meta'], $result->get_meta() );
$this->assertTrue(
$result->check_permissions(
array(
Expand Down
Loading