WordPress.org

Make WordPress Core

Ticket #43316: 43316.8.diff

File 43316.8.diff, 57.9 KB (added by adamsilverstein, 4 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..cb8d1c3167
    - +  
     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                $response = array();
     271                $post_id   = $request->get_param( 'parent' );
     272                $revisions = wp_get_post_revisions( $post_id, array( 'check_enabled' => false ) );
     273
     274                foreach ( $revisions as $revision ) {
     275                        if ( false !== strpos( $revision->post_name, "{$post_id}-autosave" ) ) {
     276                                $data       = $this->prepare_item_for_response( $revision, $request );
     277                                $response[] = $this->prepare_response_for_collection( $data );
     278                        }
     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                        // If the autosave content is significantly different, create a revision.
     338                        $autosave_sizediff = strlen( $new_autosave['content'] ) - strlen( $old_autosave['content'] );
     339                        $create_revision   = $autosave_sizediff > 250;
     340
     341                        /**
     342                         * Filter whether a revision is created when an autosave is made via the REST API.
     343                         *
     344                         * @since 5.0.0
     345                         *
     346                         * @param bool  $create_revision   Create a revision?
     347                         * @param array $post_data         The autosave post data.
     348                         * @param int   $autosave_sizediff The calculated autosave difference.
     349                         */
     350                        if ( apply_filters( 'wp_create_revision_for_api_autosave', $create_revision, $post_data, $autosave_sizediff
     351                        ) ) {
     352                                _wp_put_post_revision( $new_autosave );
     353                        }
     354
     355                        return wp_update_post( $new_autosave );
     356                }
     357
     358                // _wp_put_post_revision() expects unescaped.
     359                $post_data = wp_unslash( $post_data );
     360
     361                // Otherwise create the new autosave as a special post revision.
     362                return _wp_put_post_revision( $post_data, true );
     363        }
     364
     365        /**
     366         * Prepares the revision for the REST response.
     367         *
     368         * @since 5.0.0
     369         *
     370         * @param WP_Post         $post    Post revision object.
     371         * @param WP_REST_Request $request Request object.
     372         *
     373         * @return WP_REST_Response Response object.
     374         */
     375        public function prepare_item_for_response( $post, $request ) {
     376                $data = array();
     377                $response = $this->revision_controller->prepare_item_for_response( $post, $request );
     378
     379                /**
     380                 * Filters a revision returned from the API.
     381                 *
     382                 * Allows modification of the revision right before it is returned.
     383                 *
     384                 * @since 5.0.0
     385                 *
     386                 * @param WP_REST_Response $response The response object.
     387                 * @param WP_Post          $post     The original revision object.
     388                 * @param WP_REST_Request  $request  Request used to generate the response.
     389                 */
     390                return apply_filters( 'rest_prepare_autosave', $response, $post, $request );
     391
     392        }
     393
     394}
  • src/wp-settings.php

    diff --git a/src/wp-settings.php b/src/wp-settings.php
    index 6b1f32bbff..767b9ddf73 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..2714b480ce 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 = { 
    12301447                }
    12311448            ]
    12321449        },
    1233         "/wp/v2/pages/(?P<parent>[\\d]+)/revisions": {
     1450        "/wp/v2/pages/(?P<parent>[\\d]+)/revisions": {
     1451            "namespace": "wp/v2",
     1452            "methods": [
     1453                "GET"
     1454            ],
     1455            "endpoints": [
     1456                {
     1457                    "methods": [
     1458                        "GET"
     1459                    ],
     1460                    "args": {
     1461                        "parent": {
     1462                            "required": false,
     1463                            "description": "The ID for the parent of the object.",
     1464                            "type": "integer"
     1465                        },
     1466                        "context": {
     1467                            "required": false,
     1468                            "default": "view",
     1469                            "enum": [
     1470                                "view",
     1471                                "embed",
     1472                                "edit"
     1473                            ],
     1474                            "description": "Scope under which the request is made; determines fields present in response.",
     1475                            "type": "string"
     1476                        }
     1477                    }
     1478                }
     1479            ]
     1480        },
     1481        "/wp/v2/pages/(?P<parent>[\\d]+)/revisions/(?P<id>[\\d]+)": {
     1482            "namespace": "wp/v2",
     1483            "methods": [
     1484                "GET",
     1485                "DELETE"
     1486            ],
     1487            "endpoints": [
     1488                {
     1489                    "methods": [
     1490                        "GET"
     1491                    ],
     1492                    "args": {
     1493                        "parent": {
     1494                            "required": false,
     1495                            "description": "The ID for the parent of the object.",
     1496                            "type": "integer"
     1497                        },
     1498                        "id": {
     1499                            "required": false,
     1500                            "description": "Unique identifier for the object.",
     1501                            "type": "integer"
     1502                        },
     1503                        "context": {
     1504                            "required": false,
     1505                            "default": "view",
     1506                            "enum": [
     1507                                "view",
     1508                                "embed",
     1509                                "edit"
     1510                            ],
     1511                            "description": "Scope under which the request is made; determines fields present in response.",
     1512                            "type": "string"
     1513                        }
     1514                    }
     1515                },
     1516                {
     1517                    "methods": [
     1518                        "DELETE"
     1519                    ],
     1520                    "args": {
     1521                        "parent": {
     1522                            "required": false,
     1523                            "description": "The ID for the parent of the object.",
     1524                            "type": "integer"
     1525                        },
     1526                        "id": {
     1527                            "required": false,
     1528                            "description": "Unique identifier for the object.",
     1529                            "type": "integer"
     1530                        },
     1531                        "force": {
     1532                            "required": false,
     1533                            "default": false,
     1534                            "description": "Required to be true, as revisions do not support trashing.",
     1535                            "type": "boolean"
     1536                        }
     1537                    }
     1538                }
     1539            ]
     1540        },
     1541        "/wp/v2/pages/(?P<parent>[\\d]+)/autosaves": {
    12341542            "namespace": "wp/v2",
    12351543            "methods": [
    1236                 "GET"
     1544                "GET",
     1545                "POST"
    12371546            ],
    12381547            "endpoints": [
    12391548                {
    mockedApiResponse.Schema = { 
    12581567                            "type": "string"
    12591568                        }
    12601569                    }
     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                    }
    12611632                }
    12621633            ]
    12631634        },
    1264         "/wp/v2/pages/(?P<parent>[\\d]+)/revisions/(?P<id>[\\d]+)": {
     1635        "/wp/v2/pages/(?P<parent>[\\d]+)/autosaves/(?P<id>[\\d]+)": {
    12651636            "namespace": "wp/v2",
    12661637            "methods": [
    12671638                "GET",
    1268                 "DELETE"
     1639                "DELETE",
     1640                "POST"
    12691641            ],
    12701642            "endpoints": [
    12711643                {
    mockedApiResponse.Schema = { 
    13141686                        "force": {
    13151687                            "required": false,
    13161688                            "default": false,
    1317                             "description": "Required to be true, as revisions do not support trashing.",
     1689                            "description": "Required to be true, as autosaves do not support trashing.",
    13181690                            "type": "boolean"
    13191691                        }
    13201692                    }
     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                    }
    13211755                }
    13221756            ]
    13231757        },
    mockedApiResponse.PostModel = { 
    36574091};
    36584092
    36594093mockedApiResponse.postRevisions = [
     4094    {
     4095        "author": 2,
     4096        "date": "2017-02-14T00:00:00",
     4097        "date_gmt": "2017-02-14T00:00:00",
     4098        "id": 4,
     4099        "modified": "2017-02-14T00:00:00",
     4100        "modified_gmt": "2017-02-14T00:00:00",
     4101        "parent": 3,
     4102        "slug": "3-revision-v1",
     4103        "guid": {
     4104            "rendered": "http://example.org/?p=4"
     4105        },
     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    },
    36604123    {
    36614124        "author": 2,
    36624125        "date": "2017-02-14T00:00:00",
    mockedApiResponse.revision = { 
    37114174    }
    37124175};
    37134176
     4177mockedApiResponse.postAutosaves = [
     4178    {
     4179        "author": 2,
     4180        "date": "2017-02-14T00:00:00",
     4181        "date_gmt": "2017-02-14T00:00:00",
     4182        "id": 5,
     4183        "modified": "2017-02-14T00:00:00",
     4184        "modified_gmt": "2017-02-14T00:00:00",
     4185        "parent": 3,
     4186        "slug": "3-autosave-v1",
     4187        "guid": {
     4188            "rendered": "http://example.org/?p=5"
     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=/wp/v2/posts/3"
     4203                }
     4204            ]
     4205        }
     4206    }
     4207];
     4208
     4209mockedApiResponse.autosave = {
     4210    "author": 2,
     4211    "date": "2017-02-14T00:00:00",
     4212    "date_gmt": "2017-02-14T00:00:00",
     4213    "id": 5,
     4214    "modified": "2017-02-14T00:00:00",
     4215    "modified_gmt": "2017-02-14T00:00:00",
     4216    "parent": 3,
     4217    "slug": "3-autosave-v1",
     4218    "guid": {
     4219        "rendered": "http://example.org/?p=5"
     4220    },
     4221    "title": {
     4222        "rendered": ""
     4223    },
     4224    "content": {
     4225        "rendered": "<p>Autosave post content.</p>\n"
     4226    },
     4227    "excerpt": {
     4228        "rendered": ""
     4229    }
     4230};
     4231
    37144232mockedApiResponse.PagesCollection = [
    37154233    {
    37164234        "id": 5,
    mockedApiResponse.pageRevisions = [ 
    38384356        "guid": {
    38394357            "rendered": "http://example.org/?p=6"
    38404358        },
     4359        "title": {
     4360            "rendered": ""
     4361        },
     4362        "content": {
     4363            "rendered": "<p>Autosave page content.</p>\n"
     4364        },
     4365        "excerpt": {
     4366            "rendered": ""
     4367        },
     4368        "_links": {
     4369            "parent": [
     4370                {
     4371                    "href": "http://example.org/index.php?rest_route=/wp/v2/pages/5"
     4372                }
     4373            ]
     4374        }
     4375    },
     4376    {
     4377        "author": 2,
     4378        "date": "2017-02-14T00:00:00",
     4379        "date_gmt": "2017-02-14T00:00:00",
     4380        "id": 7,
     4381        "modified": "2017-02-14T00:00:00",
     4382        "modified_gmt": "2017-02-14T00:00:00",
     4383        "parent": 6,
     4384        "slug": "6-revision-v1",
     4385        "guid": {
     4386            "rendered": "http://example.org/?p=7"
     4387        },
    38414388        "title": {
    38424389            "rendered": "REST API Client Fixture: Page"
    38434390        },
    mockedApiResponse.pageRevisions = [ 
    38504397        "_links": {
    38514398            "parent": [
    38524399                {
    3853                     "href": "http://example.org/index.php?rest_route=/wp/v2/pages/5"
     4400                    "href": "http://example.org/index.php?rest_route=/wp/v2/pages/6"
    38544401                }
    38554402            ]
    38564403        }
    mockedApiResponse.pageRevision = { 
    38804427    }
    38814428};
    38824429
     4430mockedApiResponse.pageAutosaves = [
     4431    {
     4432        "author": 2,
     4433        "date": "2017-02-14T00:00:00",
     4434        "date_gmt": "2017-02-14T00:00:00",
     4435        "id": 8,
     4436        "modified": "2017-02-14T00:00:00",
     4437        "modified_gmt": "2017-02-14T00:00:00",
     4438        "parent": 6,
     4439        "slug": "6-autosave-v1",
     4440        "guid": {
     4441            "rendered": "http://example.org/?p=8"
     4442        },
     4443        "title": {
     4444            "rendered": ""
     4445        },
     4446        "content": {
     4447            "rendered": "<p>Autosave page content.</p>\n"
     4448        },
     4449        "excerpt": {
     4450            "rendered": ""
     4451        },
     4452        "_links": {
     4453            "parent": [
     4454                {
     4455                    "href": "http://example.org/index.php?rest_route=/wp/v2/pages/6"
     4456                }
     4457            ]
     4458        }
     4459    }
     4460];
     4461
     4462mockedApiResponse.pageAutosave = {
     4463    "author": 2,
     4464    "date": "2017-02-14T00:00:00",
     4465    "date_gmt": "2017-02-14T00:00:00",
     4466    "id": 8,
     4467    "modified": "2017-02-14T00:00:00",
     4468    "modified_gmt": "2017-02-14T00:00:00",
     4469    "parent": 6,
     4470    "slug": "6-autosave-v1",
     4471    "guid": {
     4472        "rendered": "http://example.org/?p=8"
     4473    },
     4474    "title": {
     4475        "rendered": ""
     4476    },
     4477    "content": {
     4478        "rendered": "<p>Autosave page content.</p>\n"
     4479    },
     4480    "excerpt": {
     4481        "rendered": ""
     4482    }
     4483};
     4484
    38834485mockedApiResponse.MediaCollection = [
    38844486    {
    38854487        "id": 7,