REST API: Add batch support for posts and terms controllers.

This also exposes the value of `allow_batch` in `OPTIONS` requests to a route.

A future commit will add batch support to more resources.

Props spacedmonkey, chrisvanpatten.
See #53063.


git-svn-id: https://develop.svn.wordpress.org/trunk@52068 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Timothy Jacobs 2021-11-09 01:57:48 +00:00
parent 29615a6c70
commit ecf1d6a158
13 changed files with 188 additions and 9 deletions

View File

@ -1403,6 +1403,8 @@ class WP_REST_Server {
'endpoints' => array(),
);
$allow_batch = false;
if ( isset( $this->route_options[ $route ] ) ) {
$options = $this->route_options[ $route ];
@ -1410,6 +1412,8 @@ class WP_REST_Server {
$data['namespace'] = $options['namespace'];
}
$allow_batch = isset( $options['allow_batch'] ) ? $options['allow_batch'] : false;
if ( isset( $options['schema'] ) && 'help' === $context ) {
$data['schema'] = call_user_func( $options['schema'] );
}
@ -1430,6 +1434,12 @@ class WP_REST_Server {
'methods' => array_keys( $callback['methods'] ),
);
$callback_batch = isset( $callback['allow_batch'] ) ? $callback['allow_batch'] : $allow_batch;
if ( $callback_batch ) {
$endpoint_data['allow_batch'] = $callback_batch;
}
if ( isset( $callback['args'] ) ) {
$endpoint_data['args'] = array();

View File

@ -16,6 +16,14 @@
*/
class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
/**
* Whether the controller supports batching.
*
* @since 5.9.0
* @var false
*/
protected $allow_batch = false;
/**
* Registers the routes for attachments.
*

View File

@ -39,6 +39,14 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
*/
protected $password_check_passed = array();
/**
* Whether the controller supports batching.
*
* @since 5.9.0
* @var array
*/
protected $allow_batch = array( 'v1' => true );
/**
* Constructor.
*
@ -80,7 +88,8 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
'permission_callback' => array( $this, 'create_item_permissions_check' ),
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
),
'schema' => array( $this, 'get_public_item_schema' ),
'allow_batch' => $this->allow_batch,
'schema' => array( $this, 'get_public_item_schema' ),
)
);
@ -98,7 +107,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
$this->namespace,
'/' . $this->rest_base . '/(?P<id>[\d]+)',
array(
'args' => array(
'args' => array(
'id' => array(
'description' => __( 'Unique identifier for the post.' ),
'type' => 'integer',
@ -128,7 +137,8 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
),
),
),
'schema' => array( $this, 'get_public_item_schema' ),
'allow_batch' => $this->allow_batch,
'schema' => array( $this, 'get_public_item_schema' ),
)
);
}

View File

@ -48,6 +48,14 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
*/
protected $total_terms;
/**
* Whether the controller supports batching.
*
* @since 5.9.0
* @var array
*/
protected $allow_batch = array( 'v1' => true );
/**
* Constructor.
*
@ -89,7 +97,8 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
'permission_callback' => array( $this, 'create_item_permissions_check' ),
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
),
'schema' => array( $this, 'get_public_item_schema' ),
'allow_batch' => $this->allow_batch,
'schema' => array( $this, 'get_public_item_schema' ),
)
);
@ -97,7 +106,7 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
$this->namespace,
'/' . $this->rest_base . '/(?P<id>[\d]+)',
array(
'args' => array(
'args' => array(
'id' => array(
'description' => __( 'Unique identifier for the term.' ),
'type' => 'integer',
@ -129,7 +138,8 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
),
),
),
'schema' => array( $this, 'get_public_item_schema' ),
'allow_batch' => $this->allow_batch,
'schema' => array( $this, 'get_public_item_schema' ),
)
);
}

View File

@ -24,6 +24,14 @@ class WP_REST_Widgets_Controller extends WP_REST_Controller {
*/
protected $widgets_retrieved = false;
/**
* Whether the controller supports batching.
*
* @since 5.9.0
* @var array
*/
protected $allow_batch = array( 'v1' => true );
/**
* Widgets controller constructor.
*
@ -56,7 +64,7 @@ class WP_REST_Widgets_Controller extends WP_REST_Controller {
'permission_callback' => array( $this, 'create_item_permissions_check' ),
'args' => $this->get_endpoint_args_for_item_schema(),
),
'allow_batch' => array( 'v1' => true ),
'allow_batch' => $this->allow_batch,
'schema' => array( $this, 'get_public_item_schema' ),
)
);
@ -90,7 +98,7 @@ class WP_REST_Widgets_Controller extends WP_REST_Controller {
),
),
),
'allow_batch' => array( 'v1' => true ),
'allow_batch' => $this->allow_batch,
'schema' => array( $this, 'get_public_item_schema' ),
)
);

View File

@ -160,6 +160,7 @@ class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control
$request = new WP_REST_Request( 'OPTIONS', '/wp/v2/media' );
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
$this->assertArrayNotHasKey( 'allow_batch', $data['endpoints'][0] );
$this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] );
$this->assertSame( array( 'view', 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] );
// Single.
@ -174,6 +175,7 @@ class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control
$request = new WP_REST_Request( 'OPTIONS', '/wp/v2/media/' . $attachment_id );
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
$this->assertArrayNotHasKey( 'allow_batch', $data['endpoints'][0] );
$this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] );
$this->assertSame( array( 'view', 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] );
}

View File

@ -117,6 +117,7 @@ class WP_Test_REST_Categories_Controller extends WP_Test_REST_Controller_Testcas
$request = new WP_REST_Request( 'OPTIONS', '/wp/v2/categories' );
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
$this->assertSame( array( 'v1' => true ), $data['endpoints'][0]['allow_batch'] );
$this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] );
$this->assertSameSets( array( 'view', 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] );
// Single.
@ -124,6 +125,7 @@ class WP_Test_REST_Categories_Controller extends WP_Test_REST_Controller_Testcas
$request = new WP_REST_Request( 'OPTIONS', '/wp/v2/categories/' . $category1 );
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
$this->assertSame( array( 'v1' => true ), $data['endpoints'][0]['allow_batch'] );
$this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] );
$this->assertSameSets( array( 'view', 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] );
}

View File

@ -64,7 +64,8 @@ class WP_Test_REST_Pages_Controller extends WP_Test_REST_Post_Type_Controller_Te
$request = new WP_REST_Request( 'OPTIONS', '/wp/v2/pages' );
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
$keys = array_keys( $data['endpoints'][0]['args'] );
$this->assertSame( array( 'v1' => true ), $data['endpoints'][0]['allow_batch'] );
$keys = array_keys( $data['endpoints'][0]['args'] );
sort( $keys );
$this->assertSame(
array(

View File

@ -154,12 +154,14 @@ class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te
$request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts' );
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
$this->assertSame( array( 'v1' => true ), $data['endpoints'][0]['allow_batch'] );
$this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] );
$this->assertSame( array( 'view', 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] );
// Single.
$request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id );
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
$this->assertSame( array( 'v1' => true ), $data['endpoints'][0]['allow_batch'] );
$this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] );
$this->assertSame( array( 'view', 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] );
}

View File

@ -370,6 +370,38 @@ class Tests_REST_Server extends WP_Test_REST_TestCase {
$this->assertSame( $sent_headers['Allow'], 'POST' );
}
/**
* @ticket 53063
*/
public function test_batched_options() {
register_rest_route(
'test-ns',
'/test',
array(
array(
'methods' => array( 'GET' ),
'callback' => '__return_null',
'permission_callback' => '__return_true',
),
array(
'methods' => array( 'POST' ),
'callback' => '__return_null',
'permission_callback' => '__return_null',
'allow_batch' => false,
),
'allow_batch' => array( 'v1' => true ),
)
);
$request = new WP_REST_Request( 'OPTIONS', '/test-ns/test' );
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
$this->assertSame( array( 'v1' => true ), $data['endpoints'][0]['allow_batch'] );
$this->assertArrayNotHasKey( 'allow_batch', $data['endpoints'][1] );
}
public function test_allow_header_sent_on_options_request() {
register_rest_route(
'test-ns',

View File

@ -135,6 +135,7 @@ class WP_Test_REST_Tags_Controller extends WP_Test_REST_Controller_Testcase {
$request = new WP_REST_Request( 'OPTIONS', '/wp/v2/tags' );
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
$this->assertSame( array( 'v1' => true ), $data['endpoints'][0]['allow_batch'] );
$this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] );
$this->assertSameSets( array( 'view', 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] );
// Single.
@ -142,6 +143,7 @@ class WP_Test_REST_Tags_Controller extends WP_Test_REST_Controller_Testcase {
$request = new WP_REST_Request( 'OPTIONS', '/wp/v2/tags/' . $tag1 );
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
$this->assertSame( array( 'v1' => true ), $data['endpoints'][0]['allow_batch'] );
$this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] );
$this->assertSameSets( array( 'view', 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] );
}

View File

@ -1523,6 +1523,8 @@ class WP_Test_REST_Widgets_Controller extends WP_Test_REST_Controller_Testcase {
$data = $response->get_data();
$properties = $data['schema']['properties'];
$this->assertSame( array( 'v1' => true ), $data['endpoints'][0]['allow_batch'] );
$this->assertCount( 7, $properties );
$this->assertArrayHasKey( 'id', $properties );
$this->assertArrayHasKey( 'id_base', $properties );

View File

@ -261,6 +261,9 @@ mockedApiResponse.Schema = {
"methods": [
"GET"
],
"allow_batch": {
"v1": true
},
"args": {
"context": {
"description": "Scope under which the request is made; determines fields present in response.",
@ -602,6 +605,9 @@ mockedApiResponse.Schema = {
"methods": [
"POST"
],
"allow_batch": {
"v1": true
},
"args": {
"date": {
"description": "The date the post was published, in the site's timezone.",
@ -840,6 +846,9 @@ mockedApiResponse.Schema = {
"methods": [
"GET"
],
"allow_batch": {
"v1": true
},
"args": {
"id": {
"description": "Unique identifier for the post.",
@ -870,6 +879,9 @@ mockedApiResponse.Schema = {
"PUT",
"PATCH"
],
"allow_batch": {
"v1": true
},
"args": {
"id": {
"description": "Unique identifier for the post.",
@ -1098,6 +1110,9 @@ mockedApiResponse.Schema = {
"methods": [
"DELETE"
],
"allow_batch": {
"v1": true
},
"args": {
"id": {
"description": "Unique identifier for the post.",
@ -1579,6 +1594,9 @@ mockedApiResponse.Schema = {
"methods": [
"GET"
],
"allow_batch": {
"v1": true
},
"args": {
"context": {
"description": "Scope under which the request is made; determines fields present in response.",
@ -1766,6 +1784,9 @@ mockedApiResponse.Schema = {
"methods": [
"POST"
],
"allow_batch": {
"v1": true
},
"args": {
"date": {
"description": "The date the post was published, in the site's timezone.",
@ -1976,6 +1997,9 @@ mockedApiResponse.Schema = {
"methods": [
"GET"
],
"allow_batch": {
"v1": true
},
"args": {
"id": {
"description": "Unique identifier for the post.",
@ -2006,6 +2030,9 @@ mockedApiResponse.Schema = {
"PUT",
"PATCH"
],
"allow_batch": {
"v1": true
},
"args": {
"id": {
"description": "Unique identifier for the post.",
@ -2206,6 +2233,9 @@ mockedApiResponse.Schema = {
"methods": [
"DELETE"
],
"allow_batch": {
"v1": true
},
"args": {
"id": {
"description": "Unique identifier for the post.",
@ -3396,6 +3426,9 @@ mockedApiResponse.Schema = {
"methods": [
"GET"
],
"allow_batch": {
"v1": true
},
"args": {
"context": {
"description": "Scope under which the request is made; determines fields present in response.",
@ -3541,6 +3574,9 @@ mockedApiResponse.Schema = {
"methods": [
"POST"
],
"allow_batch": {
"v1": true
},
"args": {
"date": {
"description": "The date the post was published, in the site's timezone.",
@ -3656,6 +3692,9 @@ mockedApiResponse.Schema = {
"methods": [
"GET"
],
"allow_batch": {
"v1": true
},
"args": {
"id": {
"description": "Unique identifier for the post.",
@ -3686,6 +3725,9 @@ mockedApiResponse.Schema = {
"PUT",
"PATCH"
],
"allow_batch": {
"v1": true
},
"args": {
"id": {
"description": "Unique identifier for the post.",
@ -3791,6 +3833,9 @@ mockedApiResponse.Schema = {
"methods": [
"DELETE"
],
"allow_batch": {
"v1": true
},
"args": {
"id": {
"description": "Unique identifier for the post.",
@ -4606,6 +4651,9 @@ mockedApiResponse.Schema = {
"methods": [
"GET"
],
"allow_batch": {
"v1": true
},
"args": {
"context": {
"description": "Scope under which the request is made; determines fields present in response.",
@ -5370,6 +5418,9 @@ mockedApiResponse.Schema = {
"methods": [
"POST"
],
"allow_batch": {
"v1": true
},
"args": {
"description": {
"description": "HTML description of the term.",
@ -5418,6 +5469,9 @@ mockedApiResponse.Schema = {
"methods": [
"GET"
],
"allow_batch": {
"v1": true
},
"args": {
"id": {
"description": "Unique identifier for the term.",
@ -5443,6 +5497,9 @@ mockedApiResponse.Schema = {
"PUT",
"PATCH"
],
"allow_batch": {
"v1": true
},
"args": {
"id": {
"description": "Unique identifier for the term.",
@ -5481,6 +5538,9 @@ mockedApiResponse.Schema = {
"methods": [
"DELETE"
],
"allow_batch": {
"v1": true
},
"args": {
"id": {
"description": "Unique identifier for the term.",
@ -5508,6 +5568,9 @@ mockedApiResponse.Schema = {
"methods": [
"GET"
],
"allow_batch": {
"v1": true
},
"args": {
"context": {
"description": "Scope under which the request is made; determines fields present in response.",
@ -5615,6 +5678,9 @@ mockedApiResponse.Schema = {
"methods": [
"POST"
],
"allow_batch": {
"v1": true
},
"args": {
"description": {
"description": "HTML description of the term.",
@ -5658,6 +5724,9 @@ mockedApiResponse.Schema = {
"methods": [
"GET"
],
"allow_batch": {
"v1": true
},
"args": {
"id": {
"description": "Unique identifier for the term.",
@ -5683,6 +5752,9 @@ mockedApiResponse.Schema = {
"PUT",
"PATCH"
],
"allow_batch": {
"v1": true
},
"args": {
"id": {
"description": "Unique identifier for the term.",
@ -5716,6 +5788,9 @@ mockedApiResponse.Schema = {
"methods": [
"DELETE"
],
"allow_batch": {
"v1": true
},
"args": {
"id": {
"description": "Unique identifier for the term.",
@ -7664,6 +7739,9 @@ mockedApiResponse.Schema = {
"methods": [
"GET"
],
"allow_batch": {
"v1": true
},
"args": {
"context": {
"description": "Scope under which the request is made; determines fields present in response.",
@ -7687,6 +7765,9 @@ mockedApiResponse.Schema = {
"methods": [
"POST"
],
"allow_batch": {
"v1": true
},
"args": {
"id": {
"description": "Unique identifier for the widget.",
@ -7762,6 +7843,9 @@ mockedApiResponse.Schema = {
"methods": [
"GET"
],
"allow_batch": {
"v1": true
},
"args": {
"context": {
"description": "Scope under which the request is made; determines fields present in response.",
@ -7782,6 +7866,9 @@ mockedApiResponse.Schema = {
"PUT",
"PATCH"
],
"allow_batch": {
"v1": true
},
"args": {
"id": {
"description": "Unique identifier for the widget.",
@ -7837,6 +7924,9 @@ mockedApiResponse.Schema = {
"methods": [
"DELETE"
],
"allow_batch": {
"v1": true
},
"args": {
"force": {
"description": "Whether to force removal of the widget, or move it to the inactive sidebar.",