From b66a4e6ec6f845451d6eb42d6207b7da00bb66c6 Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Tue, 25 May 2021 10:22:02 +1000 Subject: [PATCH] REST API: Use batch endpoint included in Core (#32174) --- lib/class-wp-rest-batch-controller.php | 308 ------------------ lib/load.php | 3 - lib/rest-api.php | 9 - .../core-data/src/batch/default-processor.js | 2 +- .../src/batch/test/default-processor.js | 2 +- phpunit/class-rest-batch-controller-test.php | 232 ------------- 6 files changed, 2 insertions(+), 554 deletions(-) delete mode 100644 lib/class-wp-rest-batch-controller.php delete mode 100644 phpunit/class-rest-batch-controller-test.php diff --git a/lib/class-wp-rest-batch-controller.php b/lib/class-wp-rest-batch-controller.php deleted file mode 100644 index d5e909f8f57533..00000000000000 --- a/lib/class-wp-rest-batch-controller.php +++ /dev/null @@ -1,308 +0,0 @@ - array( $this, 'serve_batch_request' ), - 'permission_callback' => '__return_true', - 'methods' => 'POST', - 'args' => array( - 'validation' => array( - 'type' => 'string', - 'enum' => array( 'require-all-validate', 'normal' ), - 'default' => 'normal', - ), - 'requests' => array( - 'required' => true, - 'type' => 'array', - 'maxItems' => 25, - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'method' => array( - 'type' => 'string', - 'enum' => array( 'POST', 'PUT', 'PATCH', 'DELETE' ), - 'default' => 'POST', - ), - 'path' => array( - 'type' => 'string', - 'required' => true, - ), - 'body' => array( - 'type' => 'object', - 'properties' => array(), - 'additionalProperties' => true, - ), - 'headers' => array( - 'type' => 'object', - 'properties' => array(), - 'additionalProperties' => array( - 'type' => array( 'string', 'array' ), - 'items' => array( - 'type' => 'string', - ), - ), - ), - ), - ), - ), - ), - ) - ); - } - - /** - * Serves the batch request. - * - * @since 9.2.0 - * - * @param WP_REST_Request $batch_request The batch request object. - * @return WP_REST_Response - */ - public function serve_batch_request( WP_REST_Request $batch_request ) { - $requests = array(); - - foreach ( $batch_request['requests'] as $args ) { - $parsed_url = wp_parse_url( $args['path'] ); - - if ( false === $parsed_url ) { - $requests[] = new WP_Error( 'parse_path_failed', __( 'Could not parse the path.', 'gutenberg' ), array( 'status' => 400 ) ); - - continue; - } - - $single_request = new WP_REST_Request( isset( $args['method'] ) ? $args['method'] : 'POST', $parsed_url['path'] ); - - if ( ! empty( $parsed_url['query'] ) ) { - $query_args = null; // Satisfy linter. - wp_parse_str( $parsed_url['query'], $query_args ); - $single_request->set_query_params( $query_args ); - } - - if ( ! empty( $args['body'] ) ) { - $single_request->set_body_params( $args['body'] ); - } - - if ( ! empty( $args['headers'] ) ) { - $single_request->set_headers( $args['headers'] ); - } - - $requests[] = $single_request; - } - - if ( ! is_callable( array( rest_get_server(), 'match_request_to_handler' ) ) ) { - return $this->polyfill_batching( $requests ); - } - - $matches = array(); - $validation = array(); - $has_error = false; - - foreach ( $requests as $single_request ) { - $match = rest_get_server()->match_request_to_handler( $single_request ); - $matches[] = $match; - $error = null; - - if ( is_wp_error( $match ) ) { - $error = $match; - } - - if ( ! $error ) { - list( $route, $handler ) = $match; - - if ( isset( $handler['allow_batch'] ) ) { - $allow_batch = $handler['allow_batch']; - } else { - $route_options = rest_get_server()->get_route_options( $route ); - $allow_batch = isset( $route_options['allow_batch'] ) ? $route_options['allow_batch'] : false; - } - - if ( ! is_array( $allow_batch ) || empty( $allow_batch['v1'] ) ) { - $error = new WP_Error( - 'rest_batch_not_allowed', - __( 'The requested route does not support batch requests.', 'gutenberg' ), - array( 'status' => 400 ) - ); - } - } - - if ( ! $error ) { - $check_required = $single_request->has_valid_params(); - if ( is_wp_error( $check_required ) ) { - $error = $check_required; - } - } - - if ( ! $error ) { - $check_sanitized = $single_request->sanitize_params(); - if ( is_wp_error( $check_sanitized ) ) { - $error = $check_sanitized; - } - } - - if ( $error ) { - $has_error = true; - $validation[] = $error; - } else { - $validation[] = true; - } - } - - $responses = array(); - - if ( $has_error && 'require-all-validate' === $batch_request['validation'] ) { - foreach ( $validation as $valid ) { - if ( is_wp_error( $valid ) ) { - $responses[] = rest_get_server()->envelope_response( $this->error_to_response( $valid ), false )->get_data(); - } else { - $responses[] = null; - } - } - - return new WP_REST_Response( - array( - 'failed' => 'validation', - 'responses' => $responses, - ), - WP_Http::MULTI_STATUS - ); - } - - foreach ( $requests as $i => $single_request ) { - $clean_request = clone $single_request; - $clean_request->set_url_params( array() ); - $clean_request->set_attributes( array() ); - $clean_request->set_default_params( array() ); - - /** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */ - $result = apply_filters( 'rest_pre_dispatch', null, rest_get_server(), $clean_request ); - - if ( empty( $result ) ) { - $match = $matches[ $i ]; - $error = null; - - if ( is_wp_error( $validation[ $i ] ) ) { - $error = $validation[ $i ]; - } - - if ( is_wp_error( $match ) ) { - $result = $this->error_to_response( $match ); - } else { - list( $route, $handler ) = $match; - - if ( ! $error && ! is_callable( $handler['callback'] ) ) { - $error = new WP_Error( - 'rest_invalid_handler', - __( 'The handler for the route is invalid', 'gutenberg' ), - array( 'status' => 500 ) - ); - } - - $result = rest_get_server()->respond_to_request( $single_request, $route, $handler, $error ); - } - } - - /** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */ - $result = apply_filters( 'rest_post_dispatch', rest_ensure_response( $result ), rest_get_server(), $single_request ); - - $responses[] = rest_get_server()->envelope_response( $result, false )->get_data(); - } - - return new WP_REST_Response( array( 'responses' => $responses ), WP_Http::MULTI_STATUS ); - } - - /** - * Polyfills a simple form of batching for compatibility for non-trunk installs. - * - * @since 9.2.0 - * - * @param WP_REST_Request[] $requests The list of requests to perform. - * @return WP_REST_Response The response object. - */ - protected function polyfill_batching( $requests ) { - $responses = array(); - - foreach ( $requests as $request ) { - if ( 0 !== strpos( $request->get_route(), '/__experimental' ) && 0 !== strpos( $request->get_route(), '/wp/v2/widgets' ) ) { - $error = new WP_Error( - 'rest_batch_not_allowed', - __( 'The requested route does not support batch requests.', 'gutenberg' ), - array( 'status' => 400 ) - ); - $responses[] = rest_get_server()->envelope_response( $this->error_to_response( $error ), false )->get_data(); - continue; - } - - $result = rest_get_server()->dispatch( $request ); - /** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */ - $result = apply_filters( 'rest_post_dispatch', rest_ensure_response( $result ), rest_get_server(), $request ); - - $responses[] = rest_get_server()->envelope_response( $result, false )->get_data(); - } - - return new WP_REST_Response( array( 'responses' => $responses ), WP_Http::MULTI_STATUS ); - } - - /** - * Converts an error to a response object. - * - * @see WP_REST_Server::error_to_response() This is a temporary copy of that method due to visibility. - * - * @since 9.2.0 - * - * @param WP_Error $error WP_Error instance. - * @return WP_REST_Response List of associative arrays with code and message keys. - */ - protected function error_to_response( $error ) { - $error_data = $error->get_error_data(); - - if ( is_array( $error_data ) && isset( $error_data['status'] ) ) { - $status = $error_data['status']; - } else { - $status = 500; - } - - $errors = array(); - - foreach ( (array) $error->errors as $code => $messages ) { - foreach ( (array) $messages as $message ) { - $errors[] = array( - 'code' => $code, - 'message' => $message, - 'data' => $error->get_error_data( $code ), - ); - } - } - - $data = $errors[0]; - if ( count( $errors ) > 1 ) { - // Remove the primary error. - array_shift( $errors ); - $data['additional_errors'] = $errors; - } - - $response = new WP_REST_Response( $data, $status ); - - return $response; - } -} diff --git a/lib/load.php b/lib/load.php index 440a1389f5fd99..36c35a4762db88 100644 --- a/lib/load.php +++ b/lib/load.php @@ -56,9 +56,6 @@ function gutenberg_is_experiment_enabled( $name ) { if ( ! class_exists( 'WP_Rest_Customizer_Nonces' ) ) { require_once __DIR__ . '/class-wp-rest-customizer-nonces.php'; } - if ( ! class_exists( 'WP_REST_Batch_Controller' ) ) { - require_once __DIR__ . '/class-wp-rest-batch-controller.php'; - } if ( ! class_exists( 'WP_REST_Templates_Controller' ) ) { require_once __DIR__ . '/full-site-editing/class-wp-rest-templates-controller.php'; } diff --git a/lib/rest-api.php b/lib/rest-api.php index f1336d86b33a47..5cdc258c08d5ab 100644 --- a/lib/rest-api.php +++ b/lib/rest-api.php @@ -64,15 +64,6 @@ function gutenberg_register_sidebars_and_widgets_endpoint() { } add_action( 'rest_api_init', 'gutenberg_register_sidebars_and_widgets_endpoint' ); -/** - * Registers the Batch REST API routes. - */ -function gutenberg_register_batch_endpoint() { - $batch = new WP_REST_Batch_Controller(); - $batch->register_routes(); -} -add_action( 'rest_api_init', 'gutenberg_register_batch_endpoint' ); - /** * Registers the Block editor settings REST API routes. */ diff --git a/packages/core-data/src/batch/default-processor.js b/packages/core-data/src/batch/default-processor.js index 1d98d5af1fb8ff..d459923218e129 100644 --- a/packages/core-data/src/batch/default-processor.js +++ b/packages/core-data/src/batch/default-processor.js @@ -14,7 +14,7 @@ import apiFetch from '@wordpress/api-fetch'; */ export default async function defaultProcessor( requests ) { const batchResponse = await apiFetch( { - path: '/v1/batch', + path: '/batch/v1', method: 'POST', data: { validation: 'require-all-validate', diff --git a/packages/core-data/src/batch/test/default-processor.js b/packages/core-data/src/batch/test/default-processor.js index 888ead29e883d2..c6d2515410b826 100644 --- a/packages/core-data/src/batch/test/default-processor.js +++ b/packages/core-data/src/batch/test/default-processor.js @@ -27,7 +27,7 @@ describe( 'defaultProcessor', () => { ]; const expectedFetchOptions = { - path: '/v1/batch', + path: '/batch/v1', method: 'POST', data: { validation: 'require-all-validate', diff --git a/phpunit/class-rest-batch-controller-test.php b/phpunit/class-rest-batch-controller-test.php deleted file mode 100644 index 3226adb7d12e2d..00000000000000 --- a/phpunit/class-rest-batch-controller-test.php +++ /dev/null @@ -1,232 +0,0 @@ -user->create( - array( - 'role' => 'administrator', - ) - ); - } - - /** - * Delete test data after our tests run. - * - * @since 9.2.0 - */ - public static function wpTearDownAfterClass() { - self::delete_user( self::$administrator_id ); - } - - /** - * @since 9.2.0 - */ - public function setUp() { - parent::setUp(); - - $this->tag_id = self::factory()->tag->create(); - $this->menu_id = wp_create_nav_menu( rand_str() ); - $this->menu_item_id = wp_update_nav_menu_item( - $this->menu_id, - 0, - array( - 'menu-item-type' => 'taxonomy', - 'menu-item-object' => 'post_tag', - 'menu-item-object-id' => $this->tag_id, - 'menu-item-status' => 'publish', - ) - ); - - /** @var WP_REST_Server $wp_rest_server */ - global $wp_rest_server; - $wp_rest_server = new Spy_REST_Server; - do_action( 'rest_api_init', $wp_rest_server ); - } - - /** - * @since 9.2.0 - */ - public function tearDown() { - parent::tearDown(); - /** @var WP_REST_Server $wp_rest_server */ - global $wp_rest_server; - $wp_rest_server = null; - } - - /** - * @ticket 50244 - */ - public function test_batch_requires_allow_batch_opt_in() { - register_rest_route( - 'test-ns/v1', - '/test', - array( - 'methods' => 'POST', - 'callback' => static function () { - return new WP_REST_Response( 'data' ); - }, - 'permission_callback' => '__return_true', - ) - ); - - $request = new WP_REST_Request( 'POST', '/v1/batch' ); - $request->set_body_params( - array( - 'requests' => array( - array( - 'path' => '/test-ns/v1/test', - ), - ), - ) - ); - - $response = rest_do_request( $request ); - - $this->assertEquals( 207, $response->get_status() ); - $this->assertEquals( 'rest_batch_not_allowed', $response->get_data()['responses'][0]['body']['code'] ); - } - - /** - * @ticket 50244 - */ - public function test_batch_pre_validation() { - wp_set_current_user( self::$administrator_id ); - - $request = new WP_REST_Request( 'POST', '/v1/batch' ); - $request->set_body_params( - array( - 'validation' => 'require-all-validate', - 'requests' => array( - array( - 'path' => '/__experimental/menu-items', - 'body' => array( - 'title' => 'Hello World', - 'content' => 'From the moon.', - 'type' => 'custom', - 'url' => '#', - 'menus' => $this->menu_id, - ), - ), - array( - 'path' => '/__experimental/menu-items', - 'body' => array( - 'title' => 'Hello Moon', - 'content' => 'From the world.', - 'status' => 'garbage', - 'type' => 'custom', - 'url' => '#', - 'menus' => $this->menu_id, - ), - ), - ), - ) - ); - - $response = rest_get_server()->dispatch( $request ); - $data = $response->get_data(); - - $this->assertEquals( 207, $response->get_status() ); - $this->assertArrayHasKey( 'failed', $data ); - $this->assertEquals( 'validation', $data['failed'] ); - $this->assertCount( 2, $data['responses'] ); - $this->assertNull( $data['responses'][0] ); - $this->assertEquals( 400, $data['responses'][1]['status'] ); - } - - /** - * @ticket 50244 - */ - public function test_batch_create() { - wp_set_current_user( self::$administrator_id ); - - $request = new WP_REST_Request( 'POST', '/v1/batch' ); - $request->set_body_params( - array( - 'requests' => array( - array( - 'path' => '/__experimental/menu-items', - 'body' => array( - 'title' => 'Hello World', - 'content' => 'From the moon.', - 'type' => 'custom', - 'url' => '#', - 'menus' => $this->menu_id, - ), - ), - array( - 'path' => '/__experimental/menu-items', - 'body' => array( - 'title' => 'Hello Moon', - 'status' => 'draft', - 'content' => 'From the world.', - 'type' => 'custom', - 'url' => '#', - 'menus' => $this->menu_id, - ), - ), - ), - ) - ); - - $response = rest_get_server()->dispatch( $request ); - $data = $response->get_data(); - - $this->assertEquals( 207, $response->get_status() ); - $this->assertArrayHasKey( 'responses', $data ); - $this->assertCount( 2, $data['responses'] ); - $this->assertEquals( 'Hello World', $data['responses'][0]['body']['title']['rendered'] ); - $this->assertEquals( 'Hello Moon', $data['responses'][1]['body']['title']['rendered'] ); - } -}