Repository navigation
Refactor and update the datapoint route registration to make use of WordPress' REST schema capabilities #6759
Copy link
Copy link
Closed
Labels
P2Low priorityLow priorityPHPTeam SIssues for Squad 1Issues for Squad 1Type: EnhancementImprovement of an existing featureImprovement of an existing feature
Description
Feature Description
As mentioned here, it would be nice to be able to leverage WordPress' argument schema validation for module datapoint REST requests. Additionally, we could make use of the resource schema capabilities too.
This would require some refactoring in order to register the datapoint routes individually, rather than using a dynamic path parameter.
Do not alter or remove anything below. The following sections will be managed by moderators only.
Acceptance criteria
- A module data endpoint can be registered as its own REST route, with its own argument schema describing the parameters it accepts and its own resource schema describing the data it returns.
- Module data endpoints that do not declare a schema keep being served by the existing catch-all route, with unchanged behaviour.
- An endpoint that declares a schema returns the same data, status codes and error messages as it does today, apart from the new validation errors.
- A request to an endpoint that declares a schema is rejected with a 400 response when a required parameter is missing or a parameter has the wrong type, before the request reaches Google's APIs.
- The argument and resource schemas of endpoints that declare them appear in the site's REST API index.
- No existing module data endpoint is converted in this issue: it only adds the ability to do so, and the conversions are handled separately.
- The Site Kit dashboard, module setup, module settings and shared dashboards continue to work unchanged.
Implementation Brief
- In
includes/Core/Modules/Schema_Aware_Datapoint.php(new file):- Declare an interface
Schema_Aware_Datapoint, followingExecutable_Datapoint, withget_args()returning the endpoint argument schema andget_schema()returning the resource schema.
- Declare an interface
- In
includes/Core/Modules/Module.php:- Move the lazy memoization of
$datapoint_definitionsout ofget_datapoint_definition()into a private helper. - Add
final public function get_schema_aware_datapoints()returning the memoized definitions whose values implementSchema_Aware_Datapoint, keyed by theirMETHOD:datapointid.
- Move the lazy memoization of
- In
includes/Core/Modules/REST_Modules_Controller.php:- Move the
WP_REST_Server::READABLEandWP_REST_Server::EDITABLEcallbacks inget_modules_data_route()into private methods taking the module slug and datapoint name, so the catch-all route and the per-datapoint routes run the same code, including theinvalid_module_slugandmodule_not_activeerrors. - Do the same for
resolve_datapoint_permission(), so a datapoint implementingPermission_Aware_Datapointkeeps its own permission check on either route. - Add
get_datapoint_routes(): for each module returned byModules::get_available_modules(), callget_schema_aware_datapoints()and build oneREST_Routeper datapoint name atmodules/<slug>/data/<datapoint>, with theGET:andPOST:definitions of the same name becoming the two endpoints of that route. - Pass each definition's
get_args()as the endpointargs, andget_schema()wrapped in a closure as the route-levelschemakey, matchingget_modules_list_route(). - In
get_rest_routes(), return theget_datapoint_routes()entries beforeget_modules_data_route().WP_REST_Servermatches routes in registration order, so the catch-all has to stay last. - A datapoint name needs no special handling when only one of its
GET:andPOST:definitions declares a schema:WP_REST_Servermoves on to the next matching route when the individual route has no handler for the request method, so the other method falls through to the catch-all.
- Move the
- Datapoints keep their existing
Missing_Required_Param_Exceptionguards.Module::get_data()is also called directly from PHP, for example fromAnalytics_4::get_google_tag_settings(), where REST validation does not run.
Test Coverage
- Add
tests/phpunit/includes/Core/Modules/Datapoints/FakeModule_Schema_Aware_Request.php, a fake datapoint implementingSchema_Aware_Datapointwith a required string argument and a resource schema, registered onFakeModulealongsideFakeModule_Test_Request. - In
tests/phpunit/integration/Core/Modules/ModuleTest.php:get_schema_aware_datapoints()returns only the definitions implementing the interface, keyed byMETHOD:datapoint.
- In
tests/phpunit/integration/Core/Modules/REST_Modules_ControllerTest.php:- the schema-aware fake datapoint is served by its own route and returns the same data as before.
- a request to that route without the required parameter returns a 400 and the datapoint is never executed.
- a fake datapoint that does not implement the interface is still served by the catch-all route.
- the individual route wins over the catch-all when both would match the path.
- requesting the schema-aware datapoint on a deactivated module returns
module_not_active. - a schema-aware datapoint that also implements
Permission_Aware_Datapointuses its own permission check on the individual route. - the route, its argument schema and its resource schema are listed in the
google-site-kit/v1REST index.
QA Brief
This PR lets a module datapoint declare the parameters it accepts, so Site Kit can check them before the datapoint runs. No datapoint declares them yet; that starts with #6760.
Setup
- Site Kit set up and connected with Analytics and Search Console. AdSense not set up, so it is not active.
- As an administrator, open "Dashboard sharing" from the Site Kit dashboard header and share Analytics and Search Console with Editors.
- The console steps below use this helper. Paste it into the DevTools console on the Site Kit dashboard first:
const siteKitRequest = ( method, path, body ) => fetch( _googlesitekitAPIFetchData.rootURL + 'google-site-kit/v1/' + path, { method, headers: { 'X-WP-Nonce': _googlesitekitAPIFetchData.nonce, 'Content-Type': 'application/json' }, body: body && JSON.stringify( body ) } ).then( async ( response ) => console.log( method, response.status, await response.text() ) );Module data still loads
- As an administrator, go to Site Kit > Dashboard. Open DevTools > Network and filter by
google-site-kit/v1/modules/. Scroll slowly to the bottom of the page, since widgets only request their data once they are on screen. - Expected: the widgets show data as before, and every
modules/<module>/data/<datapoint>request returns 200. - Log in as an editor and repeat step 1.
- Expected: the shared widgets show data as before, and every
modules/<module>/data/<datapoint>request returns 200.
PUT and PATCH use the same permission check as POST - As an editor, on the Site Kit dashboard, run:
siteKitRequest( 'POST', 'modules/analytics-4/data/save-site-goals-settings', { data: { settings: {} } } );
siteKitRequest( 'PUT', 'modules/analytics-4/data/save-site-goals-settings', { data: { settings: {} } } );
siteKitRequest( 'PATCH', 'modules/analytics-4/data/save-site-goals-settings', { data: { settings: {} } } );The empty settings object saves nothing, so this doesn't change the site.
- Expected: all three log status 200. (Before this PR, POST returned 200 but PUT and PATCH returned 403
rest_forbidden.)
Errors are unchanged
- As an administrator, on the Site Kit dashboard, run:
siteKitRequest( 'GET', 'modules/analytics-4/data/not-a-datapoint' );
siteKitRequest( 'POST', 'modules/analytics-4/data/not-a-datapoint' );
siteKitRequest( 'GET', 'modules/adsense/data/accounts' );- Expected: the first two log GET 400 and POST 400 with "code":"invalid_datapoint".
Notes
- The 400 errors for a missing or wrong-typed parameter only show up once Update all REST routes to make use of WordPress' REST schema capabilities #6760 makes a datapoint declare its parameters. PHPUnit tests cover them in this PR.Expected: the third logs GET 403 with "code":"module_not_active".
Changelog entry
- Add support for schemas in module datapoint routes.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
P2Low priorityLow priorityPHPTeam SIssues for Squad 1Issues for Squad 1Type: EnhancementImprovement of an existing featureImprovement of an existing feature
Activity
[-]Refactor and update the datapoint route registration to make use of WordPress' REST schema capabilities.[/-][+]Refactor and update the datapoint route registration to make use of WordPress' REST schema capabilities[/+]techanvil commentedon Aug 5, 2025
Hey @benbowler, thanks for drafting these AC. However, the focus of them is not aligned with the scope of the issue.
It's also worth noting that the
REST_Routesinfrastructure does already support argument and resource schemas, so I wouldn't say theREST_Routesinfra itself needs an update.The focus of this issue is in fact the module datapoint routes, rather than all routes. As the feature description points out, the datapoint routes are dynamic rather than registered individually, so we need to refactor them to allow individual registration with argument and resource schemas.
site-kit-wp/includes/Core/Modules/REST_Modules_Controller.php
Lines 538 to 539 in 10468f7
This itself is a significant enough piece of work that it will need to be broken down into multiple issues, I imagine the first issue (i.e., this one) will be to refactor the datapoint route registration in a way that allows for individual routes to be registered incrementally while retaining the catchall for the rest of the routes.
It would of course be good to ensure that all of our REST routes provide argument and resource schemas, but that's something we can address separately. I've created a new issue for this: #11234
techanvil commentedon Aug 6, 2025
... and then realised it's a duplicate of #6760, and closed it. 🤦
4 remaining items
kelvinballoo commentedon Oct 9, 2026
QA Update ✅
Verified and outcomes are as specified in the QAB. Moving to approval
Main admin
Details
All are with status 200.6759.-.01.-.Main.admin.-.200.status.ok.mov
Errors are unchanged, as expected.
View only
Details
6759.-.02.-.editor.-.200.status.ok.mov
6759.-.02.-.editor.-.POST.PATCH.PUT.200.ok.mov