Abilities API: Normalize required schema shape for REST responses#12013
Abilities API: Normalize required schema shape for REST responses#12013gziolo wants to merge 5 commits into
required schema shape for REST responses#12013Conversation
|
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. |
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. |
03946ca to
508a98f
Compare
There was a problem hiding this comment.
Pull request overview
Normalizes Abilities API REST-exposed JSON Schemas so the required keyword is always represented in JSON Schema draft-04 form (parent required: [] array), even when abilities were registered using WordPress’s draft-03-style per-property required: true/false booleans. This keeps the REST schema contract compatible with standard JSON Schema validators consumed by clients.
Changes:
- Add server-side schema-response normalization to convert per-property
requiredbooleans into a parentrequiredarray (and drop boolean-only cases with no draft-04 equivalent). - Preserve/merge existing draft-04
requiredarrays while de-duplicating entries. - Add PHPUnit coverage for top-level, nested object, array
itemsobject, mixed required forms,required: false, and scalar-root cases.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/wp-includes/rest-api/endpoints/class-wp-rest-abilities-v1-list-controller.php |
Adds required normalization logic to prepare_schema_for_response() so REST responses conform to JSON Schema draft-04. |
tests/phpunit/tests/rest-api/wpRestAbilitiesV1ListController.php |
Adds tests asserting correct conversion/stripping/merging behavior for required across multiple schema shapes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| // Collect draft-03 per-property `required: true` flags into a draft-04 | ||
| // `required` array of property names on the parent object schema. | ||
| if ( isset( $schema['properties'] ) && is_array( $schema['properties'] ) ) { |
There was a problem hiding this comment.
I wonder if REST is the right place for this transformation or if it is something that should be considered at the ability registration level. The advantage of being on the ability level, is that for things like wp_ai_client use abilities and MCP adapter the abilities would also have the draft-04 shape.
There was a problem hiding this comment.
It's an open question to me as well. I shared more thoughts in https://core.trac.wordpress.org/ticket/64955 on the topic. I decided to start with an actual use case, which solves a real problem that developers might encounter when using the ability registered on the server through the newly introduced @wordpress/abilities package.
If you look at steps 5 and 6 outlined in https://core.trac.wordpress.org/ticket/64955#comment:1, I covered two possible pathways:
- Step 5: Emit authoring guidance via
_doing_it_wrong() - Step 6: Introduce
schema_versionfor opt-in strict authoring
The original idea I had was that we eventually tap into the ability registration phase and first warn about schema incompatibilities for existing abilities while auto-correcting them (like in REST API now), and offer a way to enable strict validation that would prevent registration when the schema doesn't follow the spec.
If we are on board with that plan, then we can follow up with these strategies in separate PRs.
jorgefilipecosta
left a comment
There was a problem hiding this comment.
Left a comment for consideration but other than that it looks good.
When exposing an ability's input and output schemas through the REST API, convert any draft-03 per-property `required` boolean into a draft-04 `required` array of property names on the parent object schema, and drop boolean `required` flags that have no draft-04 equivalent (such as on a scalar root schema). WordPress accepts both forms internally, but only the array form is valid JSON Schema draft-04, so clients consuming `wp-abilities/v1` now receive a conformant schema. The transformation only affects the REST response copy; server-side validation continues to use the stored schema via validate_input(). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Weston Ruter <westonruter@gmail.com>
When a draft-04 `required` array is already present on an object schema, `rest_validate_object_value_from_schema()` validates only that array and ignores any draft-03 per-property `required` booleans. Align `prepare_schema_for_response()` with that precedence so the published schema describes exactly what gets enforced instead of merging the two forms (which over-stated requirements for mixed schemas). Also clear the dangling `foreach` reference after collecting flags. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pin the contract that `rest_validate_object_value_from_schema()` validates only the draft-04 `required` array when one is present on an object node, ignoring per-property `required` booleans at the same level. Existing coverage exercised each form independently and mixed them across nesting levels, but never on a single node — the case the Abilities REST schema normalization relies on. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
9387993 to
bd946f7
Compare
Drop the now-unnecessary deduplication and reindexing when building the draft-04 `required` array. Property keys are unique, so the collected list needs no `array_unique()`/`array_values()`, and when a draft-04 `required` array is already present it is left untouched rather than rebuilt. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Normalizes the
requiredkeyword in an ability's input and output schemas to its JSON Schema draft-04 form when those schemas are exposed through the REST API (wp-abilities/v1).Background
Ability input and output schemas are a public contract. Clients, including the
@wordpress/abilitiesJavaScript client, consume these schemas as standard JSON Schema and validate ability input and output against them. They therefore need a draft-04-conformant shape that standard validators understand.On the server, WordPress accepts both forms supported by
rest_validate_value_from_schema():requiredarray of property names on the object schema.requiredboolean.Both forms validate server-side, but only the array form is valid JSON Schema draft-04. Ability schemas registered with per-property booleans, including examples shown for
wp_register_ability(), were therefore exposed to clients in a shape that standard draft-04 validators do not recognize.Changes
In
WP_REST_Abilities_V1_List_Controller::prepare_schema_for_response():required: truebooleans and emits them as a parentrequired: [ ... ]array of property names.requiredarray.itemsas well.required: falsefrom property schemas without emitting an empty parent array.requiredvalues that have no draft-04 equivalent, such as on scalar root schemas.rest_validate_object_value_from_schema()precedence: when an object already has a draft-04requiredarray, that array wins; per-property booleans on the same object are ignored for collection but still removed from the REST response.The transformation only rewrites the REST response copy. Stored ability schemas and server-side validation behavior are unchanged.
Tests
Adds coverage for:
items.required: falsestripping.requiredstripping.rest_validate_value_from_schema()precedence contract.Trac ticket: https://core.trac.wordpress.org/ticket/64955