Make WordPress Core

Ticket #43316: 43316.5.diff

File 43316.5.diff, 57.5 KB (added by adamsilverstein, 7 years ago)
  • src/wp-includes/rest-api.php

    diff --git a/src/wp-includes/rest-api.php b/src/wp-includes/rest-api.php
    index 0cce9fa62f..51e5e6f1a3 100644
    a b function create_initial_rest_routes() { 
    192192                if ( post_type_supports( $post_type->name, 'revisions' ) ) {
    193193                        $revisions_controller = new WP_REST_Revisions_Controller( $post_type->name );
    194194                        $revisions_controller->register_routes();
     195                        $controller = new WP_REST_Autosaves_Controller( $post_type->name );
     196                        $controller->register_routes();
    195197                }
    196198        }
    197199
  • new file src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php

    diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php
    new file mode 100644
    index 0000000000..e9bafbe40f
    - +  
     1<?php
     2/**
     3 * REST API: WP_REST_Autosaves_Controller class.
     4 *
     5 * @package WordPress
     6 * @subpackage REST_API
     7 * @since 5.0.0
     8 */
     9
     10/**
     11 * Core class used to access autosaves via the REST API.
     12 *
     13 * @since 5.0.0
     14 *
     15 * @see WP_REST_Controller
     16 */
     17class WP_REST_Autosaves_Controller extends WP_REST_Revisions_Controller {
     18
     19        /**
     20         * Parent post type.
     21         *
     22         * @since 5.0.0
     23         * @var string
     24         */
     25        private $parent_post_type;
     26
     27        /**
     28         * Parent controller.
     29         *
     30         * @since 5.0.0
     31         * @var WP_REST_Controller
     32         */
     33        private $parent_controller;
     34
     35        /**
     36         * Parent controller.
     37         *
     38         * @since 5.0.0
     39         * @var WP_REST_Controller
     40         */
     41        private $revision_controller;
     42
     43        /**
     44         * The base of the parent controller's route.
     45         *
     46         * @since 5.0.0
     47         * @var string
     48         */
     49        private $parent_base;
     50
     51        /**
     52         * Constructor.
     53         *
     54         * @since 5.0.0
     55         *
     56         * @param string $parent_post_type Post type of the parent.
     57         */
     58        public function __construct( $parent_post_type ) {
     59                $this->parent_post_type    = $parent_post_type;
     60                $this->parent_controller   = new WP_REST_Posts_Controller( $parent_post_type );
     61                $this->revision_controller = new WP_REST_Revisions_Controller( $parent_post_type );
     62                $this->rest_namespace      = 'wp/v2';
     63                $this->rest_base           = 'autosaves';
     64                $post_type_object          = get_post_type_object( $parent_post_type );
     65                $this->parent_base         = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
     66        }
     67
     68        /**o
     69         * Registers routes for autosaves.
     70         *
     71         * @since 5.0.0
     72         *
     73         * @see register_rest_route()
     74         */
     75        public function register_routes() {
     76                register_rest_route(
     77                        $this->rest_namespace, '/' . $this->parent_base . '/(?P<parent>[\d]+)/' . $this->rest_base, array(
     78                                'args'   => array(
     79                                        'parent' => array(
     80                                                'description' => __( 'The ID for the parent of the object.', 'gutenberg' ),
     81                                                'type'        => 'integer',
     82                                        ),
     83                                ),
     84                                array(
     85                                        'methods'             => WP_REST_Server::READABLE,
     86                                        'callback'            => array( $this, 'get_items' ),
     87                                        'permission_callback' => array( $this->revision_controller, 'get_items_permissions_check' ),
     88                                        'args'                => $this->get_collection_params(),
     89                                ),
     90                                array(
     91                                        'methods'             => WP_REST_Server::CREATABLE,
     92                                        'callback'            => array( $this, 'create_item' ),
     93                                        'permission_callback' => array( $this->parent_controller, 'create_item_permissions_check' ),
     94                                        'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
     95                                ),
     96                                'schema' => array( $this, 'get_public_item_schema' ),
     97                        )
     98                );
     99
     100                register_rest_route(
     101                        $this->rest_namespace, '/' . $this->parent_base . '/(?P<parent>[\d]+)/' . $this->rest_base . '/(?P<id>[\d]+)', array(
     102                                'args'   => array(
     103                                        'parent' => array(
     104                                                'description' => __( 'The ID for the parent of the object.', 'gutenberg' ),
     105                                                'type'        => 'integer',
     106                                        ),
     107                                        'id'     => array(
     108                                                'description' => __( 'Unique identifier for the object.', 'gutenberg' ),
     109                                                'type'        => 'integer',
     110                                        ),
     111                                ),
     112                                array(
     113                                        'methods'             => WP_REST_Server::READABLE,
     114                                        'callback'            => array( $this, 'get_item' ),
     115                                        'permission_callback' => array( $this->revision_controller, 'get_item_permissions_check' ),
     116                                        'args'                => array(
     117                                                'context' => $this->get_context_param( array( 'default' => 'view' ) ),
     118                                        ),
     119                                ),
     120                                array(
     121                                        'methods'             => WP_REST_Server::EDITABLE,
     122                                        'callback'            => array( $this, 'update_item' ),
     123                                        'permission_callback' => '__return_true', //array( $this->parent_controller, 'update_item_permissions_check' ),
     124                                        'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
     125                                ),
     126                                array(
     127                                        'methods'             => WP_REST_Server::DELETABLE,
     128                                        'callback'            => array( $this, 'delete_item' ),
     129                                        'permission_callback' => array( $this->revision_controller, 'delete_item_permissions_check' ),
     130                                        'args'                => array(
     131                                                'force' => array(
     132                                                        'type'        => 'boolean',
     133                                                        'default'     => false,
     134                                                        'description' => __( 'Required to be true, as autosaves do not support trashing.', 'gutenberg' ),
     135                                                ),
     136                                        ),
     137                                ),
     138                                array(
     139                                        'methods'             => WP_REST_Server::CREATABLE,
     140                                        'callback'            => array( $this, 'create_item' ),
     141                                        'permission_callback' => array( $this->parent_controller, 'create_item_permissions_check' ),
     142                                        'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
     143                                ),
     144                                'schema' => array( $this, 'get_public_item_schema' ),
     145                        )
     146                );
     147
     148        }
     149
     150        /**
     151         * Get the parent post, if the ID is valid.
     152         *
     153         * @since 4.7.2
     154         *
     155         * @param int $id Supplied ID.
     156         * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
     157         */
     158        protected function get_parent( $parent ) {
     159                return $this->revision_controller->get_parent( $parent );
     160        }
     161        /**
     162         * Creates a single autosave.
     163         *
     164         * @since 5.0.0
     165         *
     166         * @param WP_REST_Request $request Full details about the request.
     167         * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     168         */
     169        public function create_item( $request ) {
     170
     171                // Map new fields onto the existing post data.
     172                $parent            = $this->get_parent( $request->get_param( 'parent' ) );
     173                $prepared_post     = $this->parent_controller->prepare_item_for_database( $request );
     174                $prepared_post->ID = $parent->ID;
     175
     176                // If the parent post a draft, autosaving updates it and does not create a revision.
     177                if ( 'draft' === $parent->post_status ) {
     178
     179                        define( 'DOING_AUTOSAVE', true );
     180                        $autosave_id = wp_update_post( (array) $prepared_post, true );
     181
     182                        if ( ! is_wp_error( $autosave_id ) ) {
     183                                $post = get_post( $autosave_id );
     184                        }
     185                } else {
     186
     187                        // Non-draft posts - update the post, creating an autosave.
     188                        $autosave_id         = $this->create_post_autosave( (array) $prepared_post );
     189                        $post                = get_post( $autosave_id );
     190                }
     191
     192                $request->set_param( 'context', 'edit' );
     193
     194                $response = $this->prepare_item_for_response( $post, $request );
     195                $response = rest_ensure_response( $response );
     196
     197                $response->set_status( 201 );
     198                $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->rest_namespace, $this->rest_base, $autosave_id ) ) );
     199
     200                return $response;
     201        }
     202
     203        /**
     204         * Update an autosave, if the ID is valid.
     205         *
     206         * @since 5.0.0
     207         *
     208         * @param WP_REST_Request $request Full data about the request.
     209         * @return WP_Post|WP_Error Revision post object if ID is valid, WP_Error otherwise.
     210         */
     211        public function update_item( $request ) {
     212                $parent = $request->get_param( 'parent' );
     213                $id     = $request->get_param( 'id' );
     214                $error  = new WP_Error( 'rest_post_invalid_id', __( 'Invalid autosave ID.', 'gutenberg' ), array( 'status' => 404 ) );
     215                if ( (int) $parent <= 0 ) {
     216                        return $error;
     217                }
     218
     219                $prepared_post     = $this->parent_controller->prepare_item_for_database( $request );
     220                $prepared_post->ID = $id;
     221                $post_id           = wp_update_post( (array) $prepared_post );
     222                $post              = get_post( $post_id );
     223                $fields_update     = $this->update_additional_fields_for_object( $post, $request );
     224
     225                if ( is_wp_error( $fields_update ) ) {
     226                        return $fields_update;
     227                }
     228
     229                $request->set_param( 'context', 'edit' );
     230
     231                $response = $this->prepare_item_for_response( $post, $request );
     232
     233                return rest_ensure_response( $response );
     234        }
     235        /**
     236         * Get the autosave, if the ID is valid.
     237         *
     238         * @since 5.0.0
     239         *
     240         * @param WP_REST_Request $request Full data about the request.
     241         * @return WP_Post|WP_Error Revision post object if ID is valid, WP_Error otherwise.
     242         */
     243        public function get_item( $request ) {
     244                $parent = $request->get_param( 'parent' );
     245                $error  = new WP_Error( 'rest_post_invalid_id', __( 'Invalid autosave ID.', 'gutenberg' ), array( 'status' => 404 ) );
     246                if ( (int) $parent <= 0 ) {
     247                        return $error;
     248                }
     249                $autosave = wp_get_post_autosave( (int) $parent );
     250
     251                if ( empty( $autosave ) || empty( $autosave->ID ) || 'revision' !== $autosave->post_type ) {
     252                        return $error;
     253                }
     254                $autosave->post_parent = $parent;
     255                $response = $this->prepare_item_for_response( $autosave, $request );
     256
     257                return $response;
     258        }
     259
     260        /**
     261         * Gets a collection of autosaves using wp_get_post_autosave.
     262         *
     263         * Contains the user's autosave, for empty if it doesn't exist.
     264         *
     265         * @since 5.0.0
     266         *
     267         * @param WP_REST_Request $request Full data about the request.
     268         * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     269         */
     270        public function get_items( $request ) {
     271                $parent = $this->get_parent( $request->get_param( 'parent' ) );
     272                if ( is_wp_error( $parent ) ) {
     273                        return $parent;
     274                }
     275
     276                $autosave = wp_get_post_autosave( $request->get_param( 'parent' ) );
     277
     278                if ( ! $autosave ) {
     279                        return array();
     280                }
     281
     282                $response   = array();
     283                $data       = $this->prepare_item_for_response( $autosave, $request );
     284                $response[] = $this->prepare_response_for_collection( $data );
     285
     286                return rest_ensure_response( $response );
     287        }
     288
     289
     290        /**
     291         * Retrieves the autosave's schema, conforming to JSON Schema.
     292         *
     293         * @since 5.0.0
     294         *
     295         * @return array Item schema data.
     296         */
     297        public function get_item_schema() {
     298                return $this->revision_controller->get_item_schema();
     299        }
     300
     301        /**
     302         * Creates autosave data for the specified post from $_POST data.
     303         *
     304         * From wp-admin/post.php.
     305         *
     306         * @since 2.6.0
     307         *
     308         * @param mixed $post_data Associative array containing the post data or int post ID.
     309         * @return mixed The autosave revision ID. WP_Error or 0 on error.
     310         */
     311        public function create_post_autosave( $post_data ) {
     312
     313                $post_id     = (int) $post_data['ID'];
     314                $post_author = get_current_user_id();
     315
     316                // Store one autosave per author. If there is already an autosave, overwrite it.
     317                $old_autosave = wp_get_post_autosave( $post_id, $post_author );
     318                if ( $old_autosave ) {
     319                        $new_autosave                = _wp_post_revision_data( $post_data, true );
     320                        $new_autosave['ID']          = $old_autosave->ID;
     321                        $new_autosave['post_author'] = $post_author;
     322
     323                        // If the new autosave has the same content as the post, delete the autosave.
     324                        $post                  = get_post( $post_id );
     325                        $autosave_is_different = false;
     326                        foreach ( array_intersect( array_keys( $new_autosave ), array_keys( _wp_post_revision_fields( $post ) ) ) as $field ) {
     327                                if ( normalize_whitespace( $new_autosave[ $field ] ) != normalize_whitespace( $post->$field ) ) {
     328                                        $autosave_is_different = true;
     329                                        break;
     330                                }
     331                        }
     332
     333                        if ( ! $autosave_is_different ) {
     334                                wp_delete_post_revision( $old_autosave->ID );
     335                                return 0;
     336                        }
     337
     338                        /**
     339                         * This filter is documented in wp-admin/post.php.
     340                         */
     341                        do_action( 'wp_creating_autosave', $new_autosave );
     342
     343                        return wp_update_post( $new_autosave );
     344                }
     345
     346                // _wp_put_post_revision() expects unescaped.
     347                $post_data = wp_unslash( $post_data );
     348
     349                // Otherwise create the new autosave as a special post revision.
     350                return _wp_put_post_revision( $post_data, true );
     351        }
     352
     353        /**
     354         * Prepares the revision for the REST response.
     355         *
     356         * @since 5.0.0
     357         *
     358         * @param WP_Post         $post    Post revision object.
     359         * @param WP_REST_Request $request Request object.
     360         *
     361         * @return WP_REST_Response Response object.
     362         */
     363        public function prepare_item_for_response( $post, $request ) {
     364                $data = array();
     365                $response = $this->revision_controller->prepare_item_for_response( $post, $request );
     366
     367                /**
     368                 * Filters a revision returned from the API.
     369                 *
     370                 * Allows modification of the revision right before it is returned.
     371                 *
     372                 * @since 5.0.0
     373                 *
     374                 * @param WP_REST_Response $response The response object.
     375                 * @param WP_Post          $post     The original revision object.
     376                 * @param WP_REST_Request  $request  Request used to generate the response.
     377                 */
     378                return apply_filters( 'rest_prepare_autosave', $response, $post, $request );
     379
     380        }
     381
     382}
  • src/wp-settings.php

    diff --git a/src/wp-settings.php b/src/wp-settings.php
    index 6edd3c98ca..b2c4189c64 100644
    a b require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-attachments-contro 
    230230require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-post-types-controller.php' );
    231231require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-post-statuses-controller.php' );
    232232require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-revisions-controller.php' );
     233require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-autosaves-controller.php' );
    233234require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-taxonomies-controller.php' );
    234235require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-terms-controller.php' );
    235236require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-users-controller.php' );
  • new file tests/phpunit/tests/rest-api/rest-autosaves-controller.php

    diff --git a/tests/phpunit/tests/rest-api/rest-autosaves-controller.php b/tests/phpunit/tests/rest-api/rest-autosaves-controller.php
    new file mode 100644
    index 0000000000..2a99a0ab24
    - +  
     1<?php
     2/**
     3 * Unit tests covering WP_REST_Autosaves_Controller functionality.
     4 *
     5 * @package WordPress
     6 * @subpackage REST API
     7 */
     8
     9/**
     10 * @group restapi-autosaves
     11 * @group restapi
     12 */
     13class WP_Test_REST_Autosaves_Controller extends WP_Test_REST_Controller_Testcase {
     14        protected static $post_id;
     15        protected static $page_id;
     16
     17        protected static $autosave_post_id;
     18        protected static $autosave_page_id;
     19
     20        protected static $editor_id;
     21        protected static $contributor_id;
     22
     23        protected function set_post_data( $args = array() ) {
     24                $defaults = array(
     25                        'title'   => 'Post Title',
     26                        'content' => 'Post content',
     27                        'excerpt' => 'Post excerpt',
     28                        'name'    => 'test',
     29                        'author'  => get_current_user_id(),
     30                );
     31
     32                return wp_parse_args( $args, $defaults );
     33        }
     34
     35        protected function check_create_autosave_response( $response ) {
     36                $this->assertNotInstanceOf( 'WP_Error', $response );
     37                $response = rest_ensure_response( $response );
     38
     39                $this->assertEquals( 201, $response->get_status() );
     40                $headers = $response->get_headers();
     41                $this->assertArrayHasKey( 'Location', $headers );
     42        }
     43
     44        protected function check_update_autosave_response( $response ) {
     45                $this->assertNotInstanceOf( 'WP_Error', $response );
     46                $response = rest_ensure_response( $response );
     47
     48                $this->assertEquals( 200, $response->get_status() );
     49                $headers = $response->get_headers();
     50                $this->assertArrayNotHasKey( 'Location', $headers );
     51        }
     52
     53
     54        public static function wpSetUpBeforeClass( $factory ) {
     55                self::$post_id = $factory->post->create();
     56                self::$page_id = $factory->post->create( array( 'post_type' => 'page' ) );
     57
     58                self::$editor_id      = $factory->user->create(
     59                        array(
     60                                'role' => 'editor',
     61                        )
     62                );
     63                self::$contributor_id = $factory->user->create(
     64                        array(
     65                                'role' => 'contributor',
     66                        )
     67                );
     68
     69                wp_set_current_user( self::$editor_id );
     70
     71                // Create an autosave.
     72                self::$autosave_post_id = wp_create_post_autosave(
     73                        array(
     74                                'post_content' => 'This content is better.',
     75                                'post_ID'      => self::$post_id,
     76                                'post_type'    => 'post',
     77                        )
     78                );
     79
     80                self::$autosave_page_id = wp_create_post_autosave(
     81                        array(
     82                                'post_content' => 'This content is better.',
     83                                'post_ID'      => self::$page_id,
     84                                'post_type'    => 'post',
     85                        )
     86                );
     87
     88        }
     89
     90        public static function wpTearDownAfterClass() {
     91                // Also deletes revisions.
     92                wp_delete_post( self::$post_id, true );
     93                wp_delete_post( self::$page_id, true );
     94
     95                self::delete_user( self::$editor_id );
     96                self::delete_user( self::$contributor_id );
     97        }
     98
     99        public function setUp() {
     100                parent::setUp();
     101                wp_set_current_user( self::$editor_id );
     102
     103                $this->post_autosave = wp_get_post_autosave( self::$post_id );
     104        }
     105
     106        public function test_register_routes() {
     107                $routes = rest_get_server()->get_routes();
     108                $this->assertArrayHasKey( '/wp/v2/posts/(?P<parent>[\d]+)/autosaves', $routes );
     109                $this->assertArrayHasKey( '/wp/v2/posts/(?P<parent>[\d]+)/autosaves/(?P<id>[\d]+)', $routes );
     110                $this->assertArrayHasKey( '/wp/v2/pages/(?P<parent>[\d]+)/autosaves', $routes );
     111                $this->assertArrayHasKey( '/wp/v2/pages/(?P<parent>[\d]+)/autosaves/(?P<id>[\d]+)', $routes );
     112        }
     113
     114        public function test_context_param() {
     115                // Collection
     116                $request  = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     117                $response = rest_get_server()->dispatch( $request );
     118                $data     = $response->get_data();
     119                $this->assertEquals( 'view', $data['endpoints'][0]['args']['context']['default'] );
     120                $this->assertEqualSets( array( 'view', 'edit', 'embed' ), $data['endpoints'][0]['args']['context']['enum'] );
     121                // Single
     122                $request  = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     123                $response = rest_get_server()->dispatch( $request );
     124                $data     = $response->get_data();
     125                $this->assertEquals( 'view', $data['endpoints'][0]['args']['context']['default'] );
     126                $this->assertEqualSets( array( 'view', 'edit', 'embed' ), $data['endpoints'][0]['args']['context']['enum'] );   }
     127
     128        public function test_get_items() {
     129                wp_set_current_user( self::$editor_id );
     130                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     131                $response = rest_get_server()->dispatch( $request );
     132                $data     = $response->get_data();
     133                $this->assertEquals( 200, $response->get_status() );
     134                $this->assertCount( 1, $data );
     135
     136                $this->assertEquals( self::$autosave_post_id, $data[0]['id'] );
     137
     138                $this->check_get_autosave_response( $data[0], $this->post_autosave );
     139        }
     140
     141        public function test_get_items_no_permission() {
     142                wp_set_current_user( 0 );
     143                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     144                $response = rest_get_server()->dispatch( $request );
     145                $this->assertErrorResponse( 'rest_cannot_read', $response, 401 );
     146                wp_set_current_user( self::$contributor_id );
     147                $response = rest_get_server()->dispatch( $request );
     148                $this->assertErrorResponse( 'rest_cannot_read', $response, 403 );
     149        }
     150
     151        public function test_get_items_missing_parent() {
     152                wp_set_current_user( self::$editor_id );
     153                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/autosaves' );
     154                $response = rest_get_server()->dispatch( $request );
     155                $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
     156        }
     157
     158        public function test_get_items_invalid_parent_post_type() {
     159                wp_set_current_user( self::$editor_id );
     160                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$page_id . '/autosaves' );
     161                $response = rest_get_server()->dispatch( $request );
     162                $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
     163        }
     164
     165        public function test_get_item() {
     166                wp_set_current_user( self::$editor_id );
     167                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     168                $response = rest_get_server()->dispatch( $request );
     169                $this->assertEquals( 200, $response->get_status() );
     170                $data     = $response->get_data();
     171
     172                $this->check_get_autosave_response( $response, $this->post_autosave );
     173                $fields = array(
     174                        'author',
     175                        'date',
     176                        'date_gmt',
     177                        'modified',
     178                        'modified_gmt',
     179                        'guid',
     180                        'id',
     181                        'parent',
     182                        'slug',
     183                        'title',
     184                        'excerpt',
     185                        'content',
     186                );
     187                $this->assertEqualSets( $fields, array_keys( $data ) );
     188                $this->assertSame( self::$editor_id, $data['author'] );
     189        }
     190
     191        public function test_get_item_embed_context() {
     192                wp_set_current_user( self::$editor_id );
     193                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     194                $request->set_param( 'context', 'embed' );
     195                $response = rest_get_server()->dispatch( $request );
     196                $fields   = array(
     197                        'author',
     198                        'date',
     199                        'id',
     200                        'parent',
     201                        'slug',
     202                        'title',
     203                        'excerpt',
     204                );
     205                $data     = $response->get_data();
     206                $this->assertEqualSets( $fields, array_keys( $data ) );
     207        }
     208
     209        public function test_get_item_no_permission() {
     210                $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     211                wp_set_current_user( self::$contributor_id );
     212                $response = rest_get_server()->dispatch( $request );
     213                $this->assertErrorResponse( 'rest_cannot_read', $response, 403 );
     214        }
     215
     216        public function test_get_item_missing_parent() {
     217                wp_set_current_user( self::$editor_id );
     218                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/autosaves/' . self::$autosave_post_id );
     219                $response = rest_get_server()->dispatch( $request );
     220                $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
     221
     222        }
     223
     224        public function test_get_item_invalid_parent_post_type() {
     225                wp_set_current_user( self::$editor_id );
     226                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$page_id . '/autosaves' );
     227                $response = rest_get_server()->dispatch( $request );
     228                $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
     229        }
     230
     231        public function test_delete_item() {
     232                wp_set_current_user( self::$editor_id );
     233                $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     234                $request->set_param( 'force', true );
     235                $response = rest_get_server()->dispatch( $request );
     236                $this->assertEquals( 200, $response->get_status() );
     237                $this->assertNull( get_post( self::$autosave_post_id ) );
     238        }
     239
     240        public function test_delete_item_no_trash() {
     241                wp_set_current_user( self::$editor_id );
     242
     243                $request  = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     244                $response = rest_get_server()->dispatch( $request );
     245                $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501 );
     246
     247                $request->set_param( 'force', 'false' );
     248                $response = rest_get_server()->dispatch( $request );
     249                $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501 );
     250
     251                // Ensure the revision still exists
     252                $this->assertNotNull( get_post( self::$autosave_post_id ) );
     253        }
     254
     255        public function test_delete_item_no_permission() {
     256                wp_set_current_user( self::$contributor_id );
     257                $request  = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     258                $response = rest_get_server()->dispatch( $request );
     259                $this->assertErrorResponse( 'rest_cannot_read', $response, 403 );
     260        }
     261
     262        public function test_prepare_item() {
     263                wp_set_current_user( self::$editor_id );
     264                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     265                $response = rest_get_server()->dispatch( $request );
     266                $this->assertEquals( 200, $response->get_status() );
     267                $this->check_get_autosave_response( $response, $this->post_autosave );
     268        }
     269
     270        public function test_get_item_schema() {
     271                $request    = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     272                $response   = rest_get_server()->dispatch( $request );
     273                $data       = $response->get_data();
     274                $properties = $data['schema']['properties'];
     275                $this->assertEquals( 12, count( $properties ) );
     276                $this->assertArrayHasKey( 'author', $properties );
     277                $this->assertArrayHasKey( 'content', $properties );
     278                $this->assertArrayHasKey( 'date', $properties );
     279                $this->assertArrayHasKey( 'date_gmt', $properties );
     280                $this->assertArrayHasKey( 'excerpt', $properties );
     281                $this->assertArrayHasKey( 'guid', $properties );
     282                $this->assertArrayHasKey( 'id', $properties );
     283                $this->assertArrayHasKey( 'modified', $properties );
     284                $this->assertArrayHasKey( 'modified_gmt', $properties );
     285                $this->assertArrayHasKey( 'parent', $properties );
     286                $this->assertArrayHasKey( 'slug', $properties );
     287                $this->assertArrayHasKey( 'title', $properties );
     288        }
     289
     290        public function test_create_item() {
     291
     292                wp_set_current_user( self::$editor_id );
     293
     294                $request  = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     295                $request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
     296                $params = $this->set_post_data();
     297                $request->set_body_params( $params );
     298                $response = rest_get_server()->dispatch( $request );
     299
     300                $this->check_create_autosave_response( $response );
     301        }
     302
     303        public function test_update_item() {
     304                wp_set_current_user( self::$editor_id );
     305                $request  = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     306                $request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
     307                $params = $this->set_post_data();
     308                $request->set_body_params( $params );
     309                $response = rest_get_server()->dispatch( $request );
     310
     311                $this->check_update_autosave_response( $response );
     312        }
     313
     314        public function test_get_additional_field_registration() {
     315                $schema = array(
     316                        'type'        => 'integer',
     317                        'description' => 'Some integer of mine',
     318                        'enum'        => array( 1, 2, 3, 4 ),
     319                        'context'     => array( 'view', 'edit' ),
     320                );
     321
     322                register_rest_field(
     323                        'post-revision', 'my_custom_int', array(
     324                                'schema'          => $schema,
     325                                'get_callback'    => array( $this, 'additional_field_get_callback' ),
     326                                'update_callback' => array( $this, 'additional_field_update_callback' ),
     327                        )
     328                );
     329
     330                $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     331
     332                $response = rest_get_server()->dispatch( $request );
     333                $data     = $response->get_data();
     334
     335                $this->assertArrayHasKey( 'my_custom_int', $data['schema']['properties'] );
     336                $this->assertEquals( $schema, $data['schema']['properties']['my_custom_int'] );
     337
     338                wp_set_current_user( 1 );
     339
     340                $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     341
     342                $response = rest_get_server()->dispatch( $request );
     343                $this->assertArrayHasKey( 'my_custom_int', $response->data );
     344
     345                global $wp_rest_additional_fields;
     346                $wp_rest_additional_fields = array();
     347        }
     348
     349        public function additional_field_get_callback( $object ) {
     350                return get_post_meta( $object['id'], 'my_custom_int', true );
     351        }
     352
     353        public function additional_field_update_callback( $value, $post ) {
     354                update_post_meta( $post->ID, 'my_custom_int', $value );
     355        }
     356
     357        protected function check_get_autosave_response( $response, $autosave ) {
     358                if ( $response instanceof WP_REST_Response ) {
     359                        $links    = $response->get_links();
     360                        $response = $response->get_data();
     361                } else {
     362                        $this->assertArrayHasKey( '_links', $response );
     363                        $links = $response['_links'];
     364                }
     365
     366                $this->assertEquals( $autosave->post_author, $response['author'] );
     367
     368                $rendered_content = apply_filters( 'the_content', $autosave->post_content );
     369                $this->assertEquals( $rendered_content, $response['content']['rendered'] );
     370
     371                $this->assertEquals( mysql_to_rfc3339( $autosave->post_date ), $response['date'] );
     372                $this->assertEquals( mysql_to_rfc3339( $autosave->post_date_gmt ), $response['date_gmt'] );
     373
     374                $rendered_guid = apply_filters( 'get_the_guid', $autosave->guid, $autosave->ID );
     375                $this->assertEquals( $rendered_guid, $response['guid']['rendered'] );
     376
     377                $this->assertEquals( $autosave->ID, $response['id'] );
     378                $this->assertEquals( mysql_to_rfc3339( $autosave->post_modified ), $response['modified'] );
     379                $this->assertEquals( mysql_to_rfc3339( $autosave->post_modified_gmt ), $response['modified_gmt'] );
     380                $this->assertEquals( $autosave->post_name, $response['slug'] );
     381
     382                $rendered_title = get_the_title( $autosave->ID );
     383                $this->assertEquals( $rendered_title, $response['title']['rendered'] );
     384
     385                $parent            = get_post( $autosave->post_parent );
     386                $parent_controller = new WP_REST_Posts_Controller( $parent->post_type );
     387                $parent_object     = get_post_type_object( $parent->post_type );
     388                $parent_base       = ! empty( $parent_object->rest_base ) ? $parent_object->rest_base : $parent_object->name;
     389                $this->assertEquals( rest_url( '/wp/v2/' . $parent_base . '/' . $autosave->post_parent ), $links['parent'][0]['href'] );
     390        }
     391
     392        public function test_get_item_sets_up_postdata() {
     393                wp_set_current_user( self::$editor_id );
     394                $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     395                rest_get_server()->dispatch( $request );
     396
     397                $post           = get_post();
     398                $parent_post_id = wp_is_post_revision( $post->ID );
     399
     400                $this->assertEquals( $post->ID, self::$autosave_post_id );
     401                $this->assertEquals( $parent_post_id, self::$post_id );
     402        }
     403
     404}
  • tests/phpunit/tests/rest-api/rest-schema-setup.php

    diff --git a/tests/phpunit/tests/rest-api/rest-schema-setup.php b/tests/phpunit/tests/rest-api/rest-schema-setup.php
    index e5b3460122..e3271318b6 100644
    a b class WP_Test_REST_Schema_Initialization extends WP_Test_REST_TestCase { 
    8989                        '/wp/v2/posts/(?P<id>[\\d]+)',
    9090                        '/wp/v2/posts/(?P<parent>[\\d]+)/revisions',
    9191                        '/wp/v2/posts/(?P<parent>[\\d]+)/revisions/(?P<id>[\\d]+)',
     92                        '/wp/v2/posts/(?P<parent>[\\d]+)/autosaves',
     93                        '/wp/v2/posts/(?P<parent>[\\d]+)/autosaves/(?P<id>[\\d]+)',
    9294                        '/wp/v2/pages',
    9395                        '/wp/v2/pages/(?P<id>[\\d]+)',
    9496                        '/wp/v2/pages/(?P<parent>[\\d]+)/revisions',
    9597                        '/wp/v2/pages/(?P<parent>[\\d]+)/revisions/(?P<id>[\\d]+)',
     98                        '/wp/v2/pages/(?P<parent>[\\d]+)/autosaves',
     99                        '/wp/v2/pages/(?P<parent>[\\d]+)/autosaves/(?P<id>[\\d]+)',
    96100                        '/wp/v2/media',
    97101                        '/wp/v2/media/(?P<id>[\\d]+)',
    98102                        '/wp/v2/types',
    class WP_Test_REST_Schema_Initialization extends WP_Test_REST_TestCase { 
    159163                $post_revisions   = array_values( wp_get_post_revisions( $post_id ) );
    160164                $post_revision_id = $post_revisions[ count( $post_revisions ) - 1 ]->ID;
    161165
     166                // Create an autosave.
     167                wp_create_post_autosave(
     168                        array(
     169                                'post_ID'      => $post_id,
     170                                'post_content' => 'Autosave post content.',
     171                                'post_type'    => 'post',
     172                        )
     173                );
     174
    162175                $page_id = $this->factory->post->create(
    163176                        array(
    164177                                'post_type'     => 'page',
    class WP_Test_REST_Schema_Initialization extends WP_Test_REST_TestCase { 
    180193                $page_revisions   = array_values( wp_get_post_revisions( $page_id ) );
    181194                $page_revision_id = $page_revisions[ count( $page_revisions ) - 1 ]->ID;
    182195
     196                // Create an autosave.
     197                wp_create_post_autosave(
     198                        array(
     199                                'post_ID'      => $page_id,
     200                                'post_content' => 'Autosave page content.',
     201                                'post_type'    => 'page',
     202                        )
     203                );
     204
    183205                $tag_id = $this->factory->tag->create(
    184206                        array(
    185207                                'name'        => 'REST API Client Fixture: Tag',
    class WP_Test_REST_Schema_Initialization extends WP_Test_REST_TestCase { 
    271293                                'route' => '/wp/v2/posts/' . $post_id . '/revisions/' . $post_revision_id,
    272294                                'name'  => 'revision',
    273295                        ),
     296                        array(
     297                                'route' => '/wp/v2/posts/' . $post_id . '/autosaves',
     298                                'name'  => 'postAutosaves',
     299                        ),
     300                        array(
     301                                'route' => '/wp/v2/posts/' . $post_id . '/autosaves/' . $post_revision_id,
     302                                'name'  => 'autosave',
     303                        ),
    274304                        array(
    275305                                'route' => '/wp/v2/pages',
    276306                                'name'  => 'PagesCollection',
    class WP_Test_REST_Schema_Initialization extends WP_Test_REST_TestCase { 
    287317                                'route' => '/wp/v2/pages/' . $page_id . '/revisions/' . $page_revision_id,
    288318                                'name'  => 'pageRevision',
    289319                        ),
     320                        array(
     321                                'route' => '/wp/v2/pages/' . $page_id . '/autosaves',
     322                                'name'  => 'pageAutosaves',
     323                        ),
     324                        array(
     325                                'route' => '/wp/v2/pages/' . $page_id . '/autosaves/' . $page_revision_id,
     326                                'name'  => 'pageAutosave',
     327                        ),
    290328                        array(
    291329                                'route' => '/wp/v2/media',
    292330                                'name'  => 'MediaCollection',
  • tests/qunit/fixtures/wp-api-generated.js

    diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js
    index 583843fbc1..c70451fee5 100644
    a b mockedApiResponse.Schema = { 
    783783                }
    784784            ]
    785785        },
     786        "/wp/v2/posts/(?P<parent>[\\d]+)/autosaves": {
     787            "namespace": "wp/v2",
     788            "methods": [
     789                "GET",
     790                "POST"
     791            ],
     792            "endpoints": [
     793                {
     794                    "methods": [
     795                        "GET"
     796                    ],
     797                    "args": {
     798                        "parent": {
     799                            "required": false,
     800                            "description": "The ID for the parent of the object.",
     801                            "type": "integer"
     802                        },
     803                        "context": {
     804                            "required": false,
     805                            "default": "view",
     806                            "enum": [
     807                                "view",
     808                                "embed",
     809                                "edit"
     810                            ],
     811                            "description": "Scope under which the request is made; determines fields present in response.",
     812                            "type": "string"
     813                        }
     814                    }
     815                },
     816                {
     817                    "methods": [
     818                        "POST"
     819                    ],
     820                    "args": {
     821                        "parent": {
     822                            "required": false,
     823                            "description": "The ID for the parent of the object.",
     824                            "type": "integer"
     825                        },
     826                        "author": {
     827                            "required": false,
     828                            "description": "The ID for the author of the object.",
     829                            "type": "integer"
     830                        },
     831                        "date": {
     832                            "required": false,
     833                            "description": "The date the object was published, in the site's timezone.",
     834                            "type": "string"
     835                        },
     836                        "date_gmt": {
     837                            "required": false,
     838                            "description": "The date the object was published, as GMT.",
     839                            "type": "string"
     840                        },
     841                        "id": {
     842                            "required": false,
     843                            "description": "Unique identifier for the object.",
     844                            "type": "integer"
     845                        },
     846                        "modified": {
     847                            "required": false,
     848                            "description": "The date the object was last modified, in the site's timezone.",
     849                            "type": "string"
     850                        },
     851                        "modified_gmt": {
     852                            "required": false,
     853                            "description": "The date the object was last modified, as GMT.",
     854                            "type": "string"
     855                        },
     856                        "slug": {
     857                            "required": false,
     858                            "description": "An alphanumeric identifier for the object unique to its type.",
     859                            "type": "string"
     860                        },
     861                        "title": {
     862                            "required": false,
     863                            "description": "The title for the object.",
     864                            "type": "object"
     865                        },
     866                        "content": {
     867                            "required": false,
     868                            "description": "The content for the object.",
     869                            "type": "object"
     870                        },
     871                        "excerpt": {
     872                            "required": false,
     873                            "description": "The excerpt for the object.",
     874                            "type": "object"
     875                        }
     876                    }
     877                }
     878            ]
     879        },
     880        "/wp/v2/posts/(?P<parent>[\\d]+)/autosaves/(?P<id>[\\d]+)": {
     881            "namespace": "wp/v2",
     882            "methods": [
     883                "GET",
     884                "DELETE",
     885                "POST"
     886            ],
     887            "endpoints": [
     888                {
     889                    "methods": [
     890                        "GET"
     891                    ],
     892                    "args": {
     893                        "parent": {
     894                            "required": false,
     895                            "description": "The ID for the parent of the object.",
     896                            "type": "integer"
     897                        },
     898                        "id": {
     899                            "required": false,
     900                            "description": "Unique identifier for the object.",
     901                            "type": "integer"
     902                        },
     903                        "context": {
     904                            "required": false,
     905                            "default": "view",
     906                            "enum": [
     907                                "view",
     908                                "embed",
     909                                "edit"
     910                            ],
     911                            "description": "Scope under which the request is made; determines fields present in response.",
     912                            "type": "string"
     913                        }
     914                    }
     915                },
     916                {
     917                    "methods": [
     918                        "DELETE"
     919                    ],
     920                    "args": {
     921                        "parent": {
     922                            "required": false,
     923                            "description": "The ID for the parent of the object.",
     924                            "type": "integer"
     925                        },
     926                        "id": {
     927                            "required": false,
     928                            "description": "Unique identifier for the object.",
     929                            "type": "integer"
     930                        },
     931                        "force": {
     932                            "required": false,
     933                            "default": false,
     934                            "description": "Required to be true, as autosaves do not support trashing.",
     935                            "type": "boolean"
     936                        }
     937                    }
     938                },
     939                {
     940                    "methods": [
     941                        "POST"
     942                    ],
     943                    "args": {
     944                        "parent": {
     945                            "required": false,
     946                            "description": "The ID for the parent of the object.",
     947                            "type": "integer"
     948                        },
     949                        "id": {
     950                            "required": false,
     951                            "description": "Unique identifier for the object.",
     952                            "type": "integer"
     953                        },
     954                        "author": {
     955                            "required": false,
     956                            "description": "The ID for the author of the object.",
     957                            "type": "integer"
     958                        },
     959                        "date": {
     960                            "required": false,
     961                            "description": "The date the object was published, in the site's timezone.",
     962                            "type": "string"
     963                        },
     964                        "date_gmt": {
     965                            "required": false,
     966                            "description": "The date the object was published, as GMT.",
     967                            "type": "string"
     968                        },
     969                        "modified": {
     970                            "required": false,
     971                            "description": "The date the object was last modified, in the site's timezone.",
     972                            "type": "string"
     973                        },
     974                        "modified_gmt": {
     975                            "required": false,
     976                            "description": "The date the object was last modified, as GMT.",
     977                            "type": "string"
     978                        },
     979                        "slug": {
     980                            "required": false,
     981                            "description": "An alphanumeric identifier for the object unique to its type.",
     982                            "type": "string"
     983                        },
     984                        "title": {
     985                            "required": false,
     986                            "description": "The title for the object.",
     987                            "type": "object"
     988                        },
     989                        "content": {
     990                            "required": false,
     991                            "description": "The content for the object.",
     992                            "type": "object"
     993                        },
     994                        "excerpt": {
     995                            "required": false,
     996                            "description": "The excerpt for the object.",
     997                            "type": "object"
     998                        }
     999                    }
     1000                }
     1001            ]
     1002        },
    7861003        "/wp/v2/pages": {
    7871004            "namespace": "wp/v2",
    7881005            "methods": [
    mockedApiResponse.Schema = { 
    13211538                }
    13221539            ]
    13231540        },
     1541        "/wp/v2/pages/(?P<parent>[\\d]+)/autosaves": {
     1542            "namespace": "wp/v2",
     1543            "methods": [
     1544                "GET",
     1545                "POST"
     1546            ],
     1547            "endpoints": [
     1548                {
     1549                    "methods": [
     1550                        "GET"
     1551                    ],
     1552                    "args": {
     1553                        "parent": {
     1554                            "required": false,
     1555                            "description": "The ID for the parent of the object.",
     1556                            "type": "integer"
     1557                        },
     1558                        "context": {
     1559                            "required": false,
     1560                            "default": "view",
     1561                            "enum": [
     1562                                "view",
     1563                                "embed",
     1564                                "edit"
     1565                            ],
     1566                            "description": "Scope under which the request is made; determines fields present in response.",
     1567                            "type": "string"
     1568                        }
     1569                    }
     1570                },
     1571                {
     1572                    "methods": [
     1573                        "POST"
     1574                    ],
     1575                    "args": {
     1576                        "parent": {
     1577                            "required": false,
     1578                            "description": "The ID for the parent of the object.",
     1579                            "type": "integer"
     1580                        },
     1581                        "author": {
     1582                            "required": false,
     1583                            "description": "The ID for the author of the object.",
     1584                            "type": "integer"
     1585                        },
     1586                        "date": {
     1587                            "required": false,
     1588                            "description": "The date the object was published, in the site's timezone.",
     1589                            "type": "string"
     1590                        },
     1591                        "date_gmt": {
     1592                            "required": false,
     1593                            "description": "The date the object was published, as GMT.",
     1594                            "type": "string"
     1595                        },
     1596                        "id": {
     1597                            "required": false,
     1598                            "description": "Unique identifier for the object.",
     1599                            "type": "integer"
     1600                        },
     1601                        "modified": {
     1602                            "required": false,
     1603                            "description": "The date the object was last modified, in the site's timezone.",
     1604                            "type": "string"
     1605                        },
     1606                        "modified_gmt": {
     1607                            "required": false,
     1608                            "description": "The date the object was last modified, as GMT.",
     1609                            "type": "string"
     1610                        },
     1611                        "slug": {
     1612                            "required": false,
     1613                            "description": "An alphanumeric identifier for the object unique to its type.",
     1614                            "type": "string"
     1615                        },
     1616                        "title": {
     1617                            "required": false,
     1618                            "description": "The title for the object.",
     1619                            "type": "object"
     1620                        },
     1621                        "content": {
     1622                            "required": false,
     1623                            "description": "The content for the object.",
     1624                            "type": "object"
     1625                        },
     1626                        "excerpt": {
     1627                            "required": false,
     1628                            "description": "The excerpt for the object.",
     1629                            "type": "object"
     1630                        }
     1631                    }
     1632                }
     1633            ]
     1634        },
     1635        "/wp/v2/pages/(?P<parent>[\\d]+)/autosaves/(?P<id>[\\d]+)": {
     1636            "namespace": "wp/v2",
     1637            "methods": [
     1638                "GET",
     1639                "DELETE",
     1640                "POST"
     1641            ],
     1642            "endpoints": [
     1643                {
     1644                    "methods": [
     1645                        "GET"
     1646                    ],
     1647                    "args": {
     1648                        "parent": {
     1649                            "required": false,
     1650                            "description": "The ID for the parent of the object.",
     1651                            "type": "integer"
     1652                        },
     1653                        "id": {
     1654                            "required": false,
     1655                            "description": "Unique identifier for the object.",
     1656                            "type": "integer"
     1657                        },
     1658                        "context": {
     1659                            "required": false,
     1660                            "default": "view",
     1661                            "enum": [
     1662                                "view",
     1663                                "embed",
     1664                                "edit"
     1665                            ],
     1666                            "description": "Scope under which the request is made; determines fields present in response.",
     1667                            "type": "string"
     1668                        }
     1669                    }
     1670                },
     1671                {
     1672                    "methods": [
     1673                        "DELETE"
     1674                    ],
     1675                    "args": {
     1676                        "parent": {
     1677                            "required": false,
     1678                            "description": "The ID for the parent of the object.",
     1679                            "type": "integer"
     1680                        },
     1681                        "id": {
     1682                            "required": false,
     1683                            "description": "Unique identifier for the object.",
     1684                            "type": "integer"
     1685                        },
     1686                        "force": {
     1687                            "required": false,
     1688                            "default": false,
     1689                            "description": "Required to be true, as autosaves do not support trashing.",
     1690                            "type": "boolean"
     1691                        }
     1692                    }
     1693                },
     1694                {
     1695                    "methods": [
     1696                        "POST"
     1697                    ],
     1698                    "args": {
     1699                        "parent": {
     1700                            "required": false,
     1701                            "description": "The ID for the parent of the object.",
     1702                            "type": "integer"
     1703                        },
     1704                        "id": {
     1705                            "required": false,
     1706                            "description": "Unique identifier for the object.",
     1707                            "type": "integer"
     1708                        },
     1709                        "author": {
     1710                            "required": false,
     1711                            "description": "The ID for the author of the object.",
     1712                            "type": "integer"
     1713                        },
     1714                        "date": {
     1715                            "required": false,
     1716                            "description": "The date the object was published, in the site's timezone.",
     1717                            "type": "string"
     1718                        },
     1719                        "date_gmt": {
     1720                            "required": false,
     1721                            "description": "The date the object was published, as GMT.",
     1722                            "type": "string"
     1723                        },
     1724                        "modified": {
     1725                            "required": false,
     1726                            "description": "The date the object was last modified, in the site's timezone.",
     1727                            "type": "string"
     1728                        },
     1729                        "modified_gmt": {
     1730                            "required": false,
     1731                            "description": "The date the object was last modified, as GMT.",
     1732                            "type": "string"
     1733                        },
     1734                        "slug": {
     1735                            "required": false,
     1736                            "description": "An alphanumeric identifier for the object unique to its type.",
     1737                            "type": "string"
     1738                        },
     1739                        "title": {
     1740                            "required": false,
     1741                            "description": "The title for the object.",
     1742                            "type": "object"
     1743                        },
     1744                        "content": {
     1745                            "required": false,
     1746                            "description": "The content for the object.",
     1747                            "type": "object"
     1748                        },
     1749                        "excerpt": {
     1750                            "required": false,
     1751                            "description": "The excerpt for the object.",
     1752                            "type": "object"
     1753                        }
     1754                    }
     1755                }
     1756            ]
     1757        },
    13241758        "/wp/v2/media": {
    13251759            "namespace": "wp/v2",
    13261760            "methods": [
    mockedApiResponse.postRevisions = [ 
    36694103        "guid": {
    36704104            "rendered": "http://example.org/?p=4"
    36714105        },
     4106        "title": {
     4107            "rendered": ""
     4108        },
     4109        "content": {
     4110            "rendered": "<p>Autosave post content.</p>\n"
     4111        },
     4112        "excerpt": {
     4113            "rendered": ""
     4114        },
     4115        "_links": {
     4116            "parent": [
     4117                {
     4118                    "href": "http://example.org/index.php?rest_route=/wp/v2/posts/3"
     4119                }
     4120            ]
     4121        }
     4122    },
     4123    {
     4124        "author": 59,
     4125        "date": "2017-02-14T00:00:00",
     4126        "date_gmt": "2017-02-14T00:00:00",
     4127        "id": 427,
     4128        "modified": "2017-02-14T00:00:00",
     4129        "modified_gmt": "2017-02-14T00:00:00",
     4130        "parent": 426,
     4131        "slug": "426-revision-v1",
     4132        "guid": {
     4133            "rendered": "http://example.org/?p=427"
     4134        },
    36724135        "title": {
    36734136            "rendered": "REST API Client Fixture: Post"
    36744137        },
    mockedApiResponse.postRevisions = [ 
    36814144        "_links": {
    36824145            "parent": [
    36834146                {
    3684                     "href": "http://example.org/index.php?rest_route=/wp/v2/posts/3"
     4147                    "href": "http://example.org/index.php?rest_route=/wp/v2/posts/426"
    36854148                }
    36864149            ]
    36874150        }
    mockedApiResponse.revision = { 
    37114174    }
    37124175};
    37134176
     4177mockedApiResponse.postAutosaves = [
     4178    {
     4179        "author": 59,
     4180        "date": "2017-02-14T00:00:00",
     4181        "date_gmt": "2017-02-14T00:00:00",
     4182        "id": 428,
     4183        "modified": "2017-02-14T00:00:00",
     4184        "modified_gmt": "2017-02-14T00:00:00",
     4185        "parent": 426,
     4186        "slug": "426-autosave-v1",
     4187        "guid": {
     4188            "rendered": "http://example.org/?p=428"
     4189        },
     4190        "title": {
     4191            "rendered": ""
     4192        },
     4193        "content": {
     4194            "rendered": "<p>Autosave post content.</p>\n"
     4195        },
     4196        "excerpt": {
     4197            "rendered": ""
     4198        },
     4199        "_links": {
     4200            "parent": [
     4201                {
     4202                    "href": "http://example.org/index.php?rest_route=/426"
     4203                }
     4204            ]
     4205        }
     4206    }
     4207];
     4208
     4209mockedApiResponse.autosave = {
     4210    "ID": 427,
     4211    "post_author": "59",
     4212    "post_date": "2018-03-10 16:46:38",
     4213    "post_date_gmt": "2018-03-10 16:46:38",
     4214    "post_content": "Updated post content.",
     4215    "post_title": "REST API Client Fixture: Post",
     4216    "post_excerpt": "REST API Client Fixture: Post",
     4217    "post_status": "inherit",
     4218    "comment_status": "closed",
     4219    "ping_status": "closed",
     4220    "post_password": "",
     4221    "post_name": "426-revision-v1",
     4222    "to_ping": "",
     4223    "pinged": "",
     4224    "post_modified": "2018-03-10 16:46:38",
     4225    "post_modified_gmt": "2018-03-10 16:46:38",
     4226    "post_content_filtered": "",
     4227    "post_parent": 426,
     4228    "guid": "http://example.org/?p=427",
     4229    "menu_order": 0,
     4230    "post_type": "revision",
     4231    "post_mime_type": "",
     4232    "comment_count": "0",
     4233    "filter": "raw"
     4234};
     4235
    37144236mockedApiResponse.PagesCollection = [
    37154237    {
    37164238        "id": 5,
    mockedApiResponse.pageRevisions = [ 
    38384360        "guid": {
    38394361            "rendered": "http://example.org/?p=6"
    38404362        },
     4363        "title": {
     4364            "rendered": ""
     4365        },
     4366        "content": {
     4367            "rendered": "<p>Autosave page content.</p>\n"
     4368        },
     4369        "excerpt": {
     4370            "rendered": ""
     4371        },
     4372        "_links": {
     4373            "parent": [
     4374                {
     4375                    "href": "http://example.org/index.php?rest_route=/wp/v2/pages/5"
     4376                }
     4377            ]
     4378        }
     4379    },
     4380    {
     4381        "author": 59,
     4382        "date": "2017-02-14T00:00:00",
     4383        "date_gmt": "2017-02-14T00:00:00",
     4384        "id": 430,
     4385        "modified": "2017-02-14T00:00:00",
     4386        "modified_gmt": "2017-02-14T00:00:00",
     4387        "parent": 429,
     4388        "slug": "429-revision-v1",
     4389        "guid": {
     4390            "rendered": "http://example.org/?p=430"
     4391        },
    38414392        "title": {
    38424393            "rendered": "REST API Client Fixture: Page"
    38434394        },
    mockedApiResponse.pageRevisions = [ 
    38504401        "_links": {
    38514402            "parent": [
    38524403                {
    3853                     "href": "http://example.org/index.php?rest_route=/wp/v2/pages/5"
     4404                    "href": "http://example.org/index.php?rest_route=/wp/v2/pages/429"
    38544405                }
    38554406            ]
    38564407        }
    mockedApiResponse.pageRevision = { 
    38804431    }
    38814432};
    38824433
     4434mockedApiResponse.pageAutosaves = [
     4435    {
     4436        "author": 59,
     4437        "date": "2017-02-14T00:00:00",
     4438        "date_gmt": "2017-02-14T00:00:00",
     4439        "id": 431,
     4440        "modified": "2017-02-14T00:00:00",
     4441        "modified_gmt": "2017-02-14T00:00:00",
     4442        "parent": 429,
     4443        "slug": "429-autosave-v1",
     4444        "guid": {
     4445            "rendered": "http://example.org/?p=431"
     4446        },
     4447        "title": {
     4448            "rendered": ""
     4449        },
     4450        "content": {
     4451            "rendered": "<p>Autosave page content.</p>\n"
     4452        },
     4453        "excerpt": {
     4454            "rendered": ""
     4455        },
     4456        "_links": {
     4457            "parent": [
     4458                {
     4459                    "href": "http://example.org/index.php?rest_route=/429"
     4460                }
     4461            ]
     4462        }
     4463    }
     4464];
     4465
     4466mockedApiResponse.pageAutosave = {
     4467    "ID": 430,
     4468    "post_author": "59",
     4469    "post_date": "2018-03-10 16:46:38",
     4470    "post_date_gmt": "2018-03-10 16:46:38",
     4471    "post_content": "Updated page content.",
     4472    "post_title": "REST API Client Fixture: Page",
     4473    "post_excerpt": "REST API Client Fixture: Page",
     4474    "post_status": "inherit",
     4475    "comment_status": "closed",
     4476    "ping_status": "closed",
     4477    "post_password": "",
     4478    "post_name": "429-revision-v1",
     4479    "to_ping": "",
     4480    "pinged": "",
     4481    "post_modified": "2018-03-10 16:46:38",
     4482    "post_modified_gmt": "2018-03-10 16:46:38",
     4483    "post_content_filtered": "",
     4484    "post_parent": 429,
     4485    "guid": "http://example.org/?p=430",
     4486    "menu_order": 0,
     4487    "post_type": "revision",
     4488    "post_mime_type": "",
     4489    "comment_count": "0",
     4490    "filter": "raw"
     4491};
     4492
    38834493mockedApiResponse.MediaCollection = [
    38844494    {
    38854495        "id": 7,