Make WordPress Core

Ticket #43316: 43316.6.diff

File 43316.6.diff, 56.8 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..4b9036415d
    - +  
     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::DELETABLE,
     122                                        'callback'            => array( $this, 'delete_item' ),
     123                                        'permission_callback' => array( $this->revision_controller, 'delete_item_permissions_check' ),
     124                                        'args'                => array(
     125                                                'force' => array(
     126                                                        'type'        => 'boolean',
     127                                                        'default'     => false,
     128                                                        'description' => __( 'Required to be true, as autosaves do not support trashing.', 'gutenberg' ),
     129                                                ),
     130                                        ),
     131                                ),
     132                                array(
     133                                        'methods'             => WP_REST_Server::CREATABLE,
     134                                        'callback'            => array( $this, 'create_item' ),
     135                                        'permission_callback' => array( $this->parent_controller, 'create_item_permissions_check' ),
     136                                        'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
     137                                ),
     138                                'schema' => array( $this, 'get_public_item_schema' ),
     139                        )
     140                );
     141
     142        }
     143
     144        /**
     145         * Get the parent post, if the ID is valid.
     146         *
     147         * @since 4.7.2
     148         *
     149         * @param int $id Supplied ID.
     150         * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
     151         */
     152        protected function get_parent( $parent ) {
     153                return $this->revision_controller->get_parent( $parent );
     154        }
     155        /**
     156         * Creates a single autosave.
     157         *
     158         * @since 5.0.0
     159         *
     160         * @param WP_REST_Request $request Full details about the request.
     161         * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     162         */
     163        public function create_item( $request ) {
     164
     165                // Map new fields onto the existing post data.
     166                $parent            = $this->get_parent( $request->get_param( 'parent' ) );
     167                $prepared_post     = $this->parent_controller->prepare_item_for_database( $request );
     168                $prepared_post->ID = $parent->ID;
     169
     170                // If the parent post a draft, autosaving updates it and does not create a revision.
     171                if ( 'draft' === $parent->post_status ) {
     172
     173                        define( 'DOING_AUTOSAVE', true );
     174                        $autosave_id = wp_update_post( (array) $prepared_post, true );
     175
     176                        if ( ! is_wp_error( $autosave_id ) ) {
     177                                $post = get_post( $autosave_id );
     178                        }
     179                } else {
     180
     181                        // Non-draft posts - update the post, creating an autosave.
     182                        $autosave_id         = $this->create_post_autosave( (array) $prepared_post );
     183                        $post                = get_post( $autosave_id );
     184                }
     185
     186                $request->set_param( 'context', 'edit' );
     187
     188                $response = $this->prepare_item_for_response( $post, $request );
     189                $response = rest_ensure_response( $response );
     190
     191                $response->set_status( 201 );
     192                $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->rest_namespace, $this->rest_base, $autosave_id ) ) );
     193
     194                return $response;
     195        }
     196
     197        /**
     198         * Update an autosave, if the ID is valid.
     199         *
     200         * @since 5.0.0
     201         *
     202         * @param WP_REST_Request $request Full data about the request.
     203         * @return WP_Post|WP_Error Revision post object if ID is valid, WP_Error otherwise.
     204         */
     205        public function update_item( $request ) {
     206                $parent = $request->get_param( 'parent' );
     207                $id     = $request->get_param( 'id' );
     208                $error  = new WP_Error( 'rest_post_invalid_id', __( 'Invalid autosave ID.', 'gutenberg' ), array( 'status' => 404 ) );
     209                if ( (int) $parent <= 0 ) {
     210                        return $error;
     211                }
     212
     213                $prepared_post     = $this->parent_controller->prepare_item_for_database( $request );
     214                $prepared_post->ID = $id;
     215                $post_id           = wp_update_post( (array) $prepared_post );
     216                $post              = get_post( $post_id );
     217                $fields_update     = $this->update_additional_fields_for_object( $post, $request );
     218
     219                if ( is_wp_error( $fields_update ) ) {
     220                        return $fields_update;
     221                }
     222
     223                $request->set_param( 'context', 'edit' );
     224
     225                $response = $this->prepare_item_for_response( $post, $request );
     226
     227                return rest_ensure_response( $response );
     228        }
     229        /**
     230         * Get the autosave, if the ID is valid.
     231         *
     232         * @since 5.0.0
     233         *
     234         * @param WP_REST_Request $request Full data about the request.
     235         * @return WP_Post|WP_Error Revision post object if ID is valid, WP_Error otherwise.
     236         */
     237        public function get_item( $request ) {
     238                $parent = $request->get_param( 'parent' );
     239                $error  = new WP_Error( 'rest_post_invalid_id', __( 'Invalid autosave ID.', 'gutenberg' ), array( 'status' => 404 ) );
     240                if ( (int) $parent <= 0 ) {
     241                        return $error;
     242                }
     243                $autosave = wp_get_post_autosave( (int) $parent );
     244
     245                if ( empty( $autosave ) || empty( $autosave->ID ) || 'revision' !== $autosave->post_type ) {
     246                        return $error;
     247                }
     248                $autosave->post_parent = $parent;
     249                $response = $this->prepare_item_for_response( $autosave, $request );
     250
     251                return $response;
     252        }
     253
     254        /**
     255         * Gets a collection of autosaves using wp_get_post_autosave.
     256         *
     257         * Contains the user's autosave, for empty if it doesn't exist.
     258         *
     259         * @since 5.0.0
     260         *
     261         * @param WP_REST_Request $request Full data about the request.
     262         * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     263         */
     264        public function get_items( $request ) {
     265                $parent = $this->get_parent( $request->get_param( 'parent' ) );
     266                if ( is_wp_error( $parent ) ) {
     267                        return $parent;
     268                }
     269
     270                $autosave = wp_get_post_autosave( $request->get_param( 'parent' ) );
     271
     272                if ( ! $autosave ) {
     273                        return array();
     274                }
     275
     276                $response   = array();
     277                $data       = $this->prepare_item_for_response( $autosave, $request );
     278                $response[] = $this->prepare_response_for_collection( $data );
     279
     280                return rest_ensure_response( $response );
     281        }
     282
     283
     284        /**
     285         * Retrieves the autosave's schema, conforming to JSON Schema.
     286         *
     287         * @since 5.0.0
     288         *
     289         * @return array Item schema data.
     290         */
     291        public function get_item_schema() {
     292                return $this->revision_controller->get_item_schema();
     293        }
     294
     295        /**
     296         * Creates autosave data for the specified post from $_POST data.
     297         *
     298         * From wp-admin/post.php.
     299         *
     300         * @since 2.6.0
     301         *
     302         * @param mixed $post_data Associative array containing the post data or int post ID.
     303         * @return mixed The autosave revision ID. WP_Error or 0 on error.
     304         */
     305        public function create_post_autosave( $post_data ) {
     306
     307                $post_id     = (int) $post_data['ID'];
     308                $post_author = get_current_user_id();
     309
     310                // Store one autosave per author. If there is already an autosave, overwrite it.
     311                $old_autosave = wp_get_post_autosave( $post_id, $post_author );
     312                if ( $old_autosave ) {
     313                        $new_autosave                = _wp_post_revision_data( $post_data, true );
     314                        $new_autosave['ID']          = $old_autosave->ID;
     315                        $new_autosave['post_author'] = $post_author;
     316
     317                        // If the new autosave has the same content as the post, delete the autosave.
     318                        $post                  = get_post( $post_id );
     319                        $autosave_is_different = false;
     320                        foreach ( array_intersect( array_keys( $new_autosave ), array_keys( _wp_post_revision_fields( $post ) ) ) as $field ) {
     321                                if ( normalize_whitespace( $new_autosave[ $field ] ) != normalize_whitespace( $post->$field ) ) {
     322                                        $autosave_is_different = true;
     323                                        break;
     324                                }
     325                        }
     326
     327                        if ( ! $autosave_is_different ) {
     328                                wp_delete_post_revision( $old_autosave->ID );
     329                                return 0;
     330                        }
     331
     332                        /**
     333                         * This filter is documented in wp-admin/post.php.
     334                         */
     335                        do_action( 'wp_creating_autosave', $new_autosave );
     336
     337                        return wp_update_post( $new_autosave );
     338                }
     339
     340                // _wp_put_post_revision() expects unescaped.
     341                $post_data = wp_unslash( $post_data );
     342
     343                // Otherwise create the new autosave as a special post revision.
     344                return _wp_put_post_revision( $post_data, true );
     345        }
     346
     347        /**
     348         * Prepares the revision for the REST response.
     349         *
     350         * @since 5.0.0
     351         *
     352         * @param WP_Post         $post    Post revision object.
     353         * @param WP_REST_Request $request Request object.
     354         *
     355         * @return WP_REST_Response Response object.
     356         */
     357        public function prepare_item_for_response( $post, $request ) {
     358                $data = array();
     359                $response = $this->revision_controller->prepare_item_for_response( $post, $request );
     360
     361                /**
     362                 * Filters a revision returned from the API.
     363                 *
     364                 * Allows modification of the revision right before it is returned.
     365                 *
     366                 * @since 5.0.0
     367                 *
     368                 * @param WP_REST_Response $response The response object.
     369                 * @param WP_Post          $post     The original revision object.
     370                 * @param WP_REST_Request  $request  Request used to generate the response.
     371                 */
     372                return apply_filters( 'rest_prepare_autosave', $response, $post, $request );
     373
     374        }
     375
     376}
  • 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..9824c29321
    - +  
     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        public static function wpSetUpBeforeClass( $factory ) {
     45                self::$post_id = $factory->post->create();
     46                self::$page_id = $factory->post->create( array( 'post_type' => 'page' ) );
     47
     48                self::$editor_id      = $factory->user->create(
     49                        array(
     50                                'role' => 'editor',
     51                        )
     52                );
     53                self::$contributor_id = $factory->user->create(
     54                        array(
     55                                'role' => 'contributor',
     56                        )
     57                );
     58
     59                wp_set_current_user( self::$editor_id );
     60
     61                // Create an autosave.
     62                self::$autosave_post_id = wp_create_post_autosave(
     63                        array(
     64                                'post_content' => 'This content is better.',
     65                                'post_ID'      => self::$post_id,
     66                                'post_type'    => 'post',
     67                        )
     68                );
     69
     70                self::$autosave_page_id = wp_create_post_autosave(
     71                        array(
     72                                'post_content' => 'This content is better.',
     73                                'post_ID'      => self::$page_id,
     74                                'post_type'    => 'post',
     75                        )
     76                );
     77
     78        }
     79
     80        public static function wpTearDownAfterClass() {
     81                // Also deletes revisions.
     82                wp_delete_post( self::$post_id, true );
     83                wp_delete_post( self::$page_id, true );
     84
     85                self::delete_user( self::$editor_id );
     86                self::delete_user( self::$contributor_id );
     87        }
     88
     89        public function setUp() {
     90                parent::setUp();
     91                wp_set_current_user( self::$editor_id );
     92
     93                $this->post_autosave = wp_get_post_autosave( self::$post_id );
     94        }
     95
     96        public function test_register_routes() {
     97                $routes = rest_get_server()->get_routes();
     98                $this->assertArrayHasKey( '/wp/v2/posts/(?P<parent>[\d]+)/autosaves', $routes );
     99                $this->assertArrayHasKey( '/wp/v2/posts/(?P<parent>[\d]+)/autosaves/(?P<id>[\d]+)', $routes );
     100                $this->assertArrayHasKey( '/wp/v2/pages/(?P<parent>[\d]+)/autosaves', $routes );
     101                $this->assertArrayHasKey( '/wp/v2/pages/(?P<parent>[\d]+)/autosaves/(?P<id>[\d]+)', $routes );
     102        }
     103
     104        public function test_context_param() {
     105                // Collection
     106                $request  = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     107                $response = rest_get_server()->dispatch( $request );
     108                $data     = $response->get_data();
     109                $this->assertEquals( 'view', $data['endpoints'][0]['args']['context']['default'] );
     110                $this->assertEqualSets( array( 'view', 'edit', 'embed' ), $data['endpoints'][0]['args']['context']['enum'] );
     111                // Single
     112                $request  = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     113                $response = rest_get_server()->dispatch( $request );
     114                $data     = $response->get_data();
     115                $this->assertEquals( 'view', $data['endpoints'][0]['args']['context']['default'] );
     116                $this->assertEqualSets( array( 'view', 'edit', 'embed' ), $data['endpoints'][0]['args']['context']['enum'] );   }
     117
     118        public function test_get_items() {
     119                wp_set_current_user( self::$editor_id );
     120                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     121                $response = rest_get_server()->dispatch( $request );
     122                $data     = $response->get_data();
     123                $this->assertEquals( 200, $response->get_status() );
     124                $this->assertCount( 1, $data );
     125
     126                $this->assertEquals( self::$autosave_post_id, $data[0]['id'] );
     127
     128                $this->check_get_autosave_response( $data[0], $this->post_autosave );
     129        }
     130
     131        public function test_get_items_no_permission() {
     132                wp_set_current_user( 0 );
     133                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     134                $response = rest_get_server()->dispatch( $request );
     135                $this->assertErrorResponse( 'rest_cannot_read', $response, 401 );
     136                wp_set_current_user( self::$contributor_id );
     137                $response = rest_get_server()->dispatch( $request );
     138                $this->assertErrorResponse( 'rest_cannot_read', $response, 403 );
     139        }
     140
     141        public function test_get_items_missing_parent() {
     142                wp_set_current_user( self::$editor_id );
     143                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/autosaves' );
     144                $response = rest_get_server()->dispatch( $request );
     145                $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
     146        }
     147
     148        public function test_get_items_invalid_parent_post_type() {
     149                wp_set_current_user( self::$editor_id );
     150                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$page_id . '/autosaves' );
     151                $response = rest_get_server()->dispatch( $request );
     152                $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
     153        }
     154
     155        public function test_get_item() {
     156                wp_set_current_user( self::$editor_id );
     157                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     158                $response = rest_get_server()->dispatch( $request );
     159                $this->assertEquals( 200, $response->get_status() );
     160                $data     = $response->get_data();
     161
     162                $this->check_get_autosave_response( $response, $this->post_autosave );
     163                $fields = array(
     164                        'author',
     165                        'date',
     166                        'date_gmt',
     167                        'modified',
     168                        'modified_gmt',
     169                        'guid',
     170                        'id',
     171                        'parent',
     172                        'slug',
     173                        'title',
     174                        'excerpt',
     175                        'content',
     176                );
     177                $this->assertEqualSets( $fields, array_keys( $data ) );
     178                $this->assertSame( self::$editor_id, $data['author'] );
     179        }
     180
     181        public function test_get_item_embed_context() {
     182                wp_set_current_user( self::$editor_id );
     183                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     184                $request->set_param( 'context', 'embed' );
     185                $response = rest_get_server()->dispatch( $request );
     186                $fields   = array(
     187                        'author',
     188                        'date',
     189                        'id',
     190                        'parent',
     191                        'slug',
     192                        'title',
     193                        'excerpt',
     194                );
     195                $data     = $response->get_data();
     196                $this->assertEqualSets( $fields, array_keys( $data ) );
     197        }
     198
     199        public function test_get_item_no_permission() {
     200                $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     201                wp_set_current_user( self::$contributor_id );
     202                $response = rest_get_server()->dispatch( $request );
     203                $this->assertErrorResponse( 'rest_cannot_read', $response, 403 );
     204        }
     205
     206        public function test_get_item_missing_parent() {
     207                wp_set_current_user( self::$editor_id );
     208                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/autosaves/' . self::$autosave_post_id );
     209                $response = rest_get_server()->dispatch( $request );
     210                $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
     211
     212        }
     213
     214        public function test_get_item_invalid_parent_post_type() {
     215                wp_set_current_user( self::$editor_id );
     216                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$page_id . '/autosaves' );
     217                $response = rest_get_server()->dispatch( $request );
     218                $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
     219        }
     220
     221        public function test_delete_item() {
     222                wp_set_current_user( self::$editor_id );
     223                $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     224                $request->set_param( 'force', true );
     225                $response = rest_get_server()->dispatch( $request );
     226                $this->assertEquals( 200, $response->get_status() );
     227                $this->assertNull( get_post( self::$autosave_post_id ) );
     228        }
     229
     230        public function test_delete_item_no_trash() {
     231                wp_set_current_user( self::$editor_id );
     232
     233                $request  = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     234                $response = rest_get_server()->dispatch( $request );
     235                $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501 );
     236
     237                $request->set_param( 'force', 'false' );
     238                $response = rest_get_server()->dispatch( $request );
     239                $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501 );
     240
     241                // Ensure the revision still exists
     242                $this->assertNotNull( get_post( self::$autosave_post_id ) );
     243        }
     244
     245        public function test_delete_item_no_permission() {
     246                wp_set_current_user( self::$contributor_id );
     247                $request  = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     248                $response = rest_get_server()->dispatch( $request );
     249                $this->assertErrorResponse( 'rest_cannot_read', $response, 403 );
     250        }
     251
     252        public function test_prepare_item() {
     253                wp_set_current_user( self::$editor_id );
     254                $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     255                $response = rest_get_server()->dispatch( $request );
     256                $this->assertEquals( 200, $response->get_status() );
     257                $this->check_get_autosave_response( $response, $this->post_autosave );
     258        }
     259
     260        public function test_get_item_schema() {
     261                $request    = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     262                $response   = rest_get_server()->dispatch( $request );
     263                $data       = $response->get_data();
     264                $properties = $data['schema']['properties'];
     265                $this->assertEquals( 12, count( $properties ) );
     266                $this->assertArrayHasKey( 'author', $properties );
     267                $this->assertArrayHasKey( 'content', $properties );
     268                $this->assertArrayHasKey( 'date', $properties );
     269                $this->assertArrayHasKey( 'date_gmt', $properties );
     270                $this->assertArrayHasKey( 'excerpt', $properties );
     271                $this->assertArrayHasKey( 'guid', $properties );
     272                $this->assertArrayHasKey( 'id', $properties );
     273                $this->assertArrayHasKey( 'modified', $properties );
     274                $this->assertArrayHasKey( 'modified_gmt', $properties );
     275                $this->assertArrayHasKey( 'parent', $properties );
     276                $this->assertArrayHasKey( 'slug', $properties );
     277                $this->assertArrayHasKey( 'title', $properties );
     278        }
     279
     280        public function test_create_item() {
     281
     282                wp_set_current_user( self::$editor_id );
     283
     284                $request  = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     285                $request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
     286                $params = $this->set_post_data();
     287                $request->set_body_params( $params );
     288                $response = rest_get_server()->dispatch( $request );
     289
     290                $this->check_create_autosave_response( $response );
     291        }
     292
     293        public function test_update_item() {
     294                wp_set_current_user( self::$editor_id );
     295                $request  = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     296                $request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
     297                $params = $this->set_post_data();
     298                $request->set_body_params( $params );
     299                $response = rest_get_server()->dispatch( $request );
     300                $this->assertErrorResponse( 'rest_post_exists', $response, 400 );
     301        }
     302
     303        public function test_get_additional_field_registration() {
     304                $schema = array(
     305                        'type'        => 'integer',
     306                        'description' => 'Some integer of mine',
     307                        'enum'        => array( 1, 2, 3, 4 ),
     308                        'context'     => array( 'view', 'edit' ),
     309                );
     310
     311                register_rest_field(
     312                        'post-revision', 'my_custom_int', array(
     313                                'schema'          => $schema,
     314                                'get_callback'    => array( $this, 'additional_field_get_callback' ),
     315                                'update_callback' => array( $this, 'additional_field_update_callback' ),
     316                        )
     317                );
     318
     319                $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     320
     321                $response = rest_get_server()->dispatch( $request );
     322                $data     = $response->get_data();
     323
     324                $this->assertArrayHasKey( 'my_custom_int', $data['schema']['properties'] );
     325                $this->assertEquals( $schema, $data['schema']['properties']['my_custom_int'] );
     326
     327                wp_set_current_user( 1 );
     328
     329                $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     330
     331                $response = rest_get_server()->dispatch( $request );
     332                $this->assertArrayHasKey( 'my_custom_int', $response->data );
     333
     334                global $wp_rest_additional_fields;
     335                $wp_rest_additional_fields = array();
     336        }
     337
     338        public function additional_field_get_callback( $object ) {
     339                return get_post_meta( $object['id'], 'my_custom_int', true );
     340        }
     341
     342        public function additional_field_update_callback( $value, $post ) {
     343                update_post_meta( $post->ID, 'my_custom_int', $value );
     344        }
     345
     346        protected function check_get_autosave_response( $response, $autosave ) {
     347                if ( $response instanceof WP_REST_Response ) {
     348                        $links    = $response->get_links();
     349                        $response = $response->get_data();
     350                } else {
     351                        $this->assertArrayHasKey( '_links', $response );
     352                        $links = $response['_links'];
     353                }
     354
     355                $this->assertEquals( $autosave->post_author, $response['author'] );
     356
     357                $rendered_content = apply_filters( 'the_content', $autosave->post_content );
     358                $this->assertEquals( $rendered_content, $response['content']['rendered'] );
     359
     360                $this->assertEquals( mysql_to_rfc3339( $autosave->post_date ), $response['date'] );
     361                $this->assertEquals( mysql_to_rfc3339( $autosave->post_date_gmt ), $response['date_gmt'] );
     362
     363                $rendered_guid = apply_filters( 'get_the_guid', $autosave->guid, $autosave->ID );
     364                $this->assertEquals( $rendered_guid, $response['guid']['rendered'] );
     365
     366                $this->assertEquals( $autosave->ID, $response['id'] );
     367                $this->assertEquals( mysql_to_rfc3339( $autosave->post_modified ), $response['modified'] );
     368                $this->assertEquals( mysql_to_rfc3339( $autosave->post_modified_gmt ), $response['modified_gmt'] );
     369                $this->assertEquals( $autosave->post_name, $response['slug'] );
     370
     371                $rendered_title = get_the_title( $autosave->ID );
     372                $this->assertEquals( $rendered_title, $response['title']['rendered'] );
     373
     374                $parent            = get_post( $autosave->post_parent );
     375                $parent_controller = new WP_REST_Posts_Controller( $parent->post_type );
     376                $parent_object     = get_post_type_object( $parent->post_type );
     377                $parent_base       = ! empty( $parent_object->rest_base ) ? $parent_object->rest_base : $parent_object->name;
     378                $this->assertEquals( rest_url( '/wp/v2/' . $parent_base . '/' . $autosave->post_parent ), $links['parent'][0]['href'] );
     379        }
     380
     381        public function test_get_item_sets_up_postdata() {
     382                wp_set_current_user( self::$editor_id );
     383                $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     384                rest_get_server()->dispatch( $request );
     385
     386                $post           = get_post();
     387                $parent_post_id = wp_is_post_revision( $post->ID );
     388
     389                $this->assertEquals( $post->ID, self::$autosave_post_id );
     390                $this->assertEquals( $parent_post_id, self::$post_id );
     391        }
     392
     393}
  • 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,