Skip to content

Commit

Permalink
Add plugins endpoints (#22454)
Browse files Browse the repository at this point in the history
This introduce a `plugins` REST API route for managing and installing plugins. The block directory routes have been updated to wrap this API using inner rest requests.

Co-authored-by: tellyworth <[email protected]>
  • Loading branch information
TimothyBJacobs and tellyworth authored Jun 10, 2020
1 parent c711a16 commit 26363cf
Show file tree
Hide file tree
Showing 10 changed files with 2,553 additions and 164 deletions.
489 changes: 327 additions & 162 deletions lib/class-wp-rest-block-directory-controller.php

Large diffs are not rendered by default.

948 changes: 948 additions & 0 deletions lib/class-wp-rest-plugins-controller.php

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ function gutenberg_is_experiment_enabled( $name ) {
if ( ! class_exists( 'WP_REST_Image_Editor_Controller' ) ) {
require dirname( __FILE__ ) . '/class-wp-rest-image-editor-controller.php';
}
if ( ! class_exists( 'WP_REST_Plugins_Controller' ) ) {
require_once dirname( __FILE__ ) . '/class-wp-rest-plugins-controller.php';
}
/**
* End: Include for phase 2
*/
Expand Down
14 changes: 14 additions & 0 deletions lib/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,20 @@ function gutenberg_register_rest_customizer_nonces() {
}
add_action( 'rest_api_init', 'gutenberg_register_rest_customizer_nonces' );


/**
* Registers the Plugins REST API routes.
*/
function gutenberg_register_plugins_endpoint() {
if ( ! gutenberg_is_experiment_enabled( 'gutenberg-block-directory' ) ) {
return;
}

$plugins = new WP_REST_Plugins_Controller();
$plugins->register_routes();
}
add_action( 'rest_api_init', 'gutenberg_register_plugins_endpoint' );

/**
* Hook in to the nav menu item post type and enable a post type rest endpoint.
*
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,8 @@
"test-unit:debug": "wp-scripts --inspect-brk test-unit-js --runInBand --no-cache --verbose --config test/unit/jest.config.js ",
"test-unit:update": "npm run test-unit -- --updateSnapshot",
"test-unit:watch": "npm run test-unit -- --watch",
"test-unit-php": "wp-env run phpunit 'phpunit -c /var/www/html/wp-content/plugins/gutenberg/phpunit.xml.dist'",
"test-unit-php-multisite": "wp-env run phpunit 'WP_MULTISITE=1 phpunit -c /var/www/html/wp-content/plugins/gutenberg/phpunit.xml.dist'",
"test-unit-php": "wp-env run phpunit 'phpunit -c /var/www/html/wp-content/plugins/gutenberg/phpunit.xml.dist --verbose'",
"test-unit-php-multisite": "wp-env run phpunit 'WP_MULTISITE=1 phpunit -c /var/www/html/wp-content/plugins/gutenberg/phpunit/multisite.xml --verbose'",
"test-unit:native": "cd test/native/ && cross-env NODE_ENV=test jest --config ./jest.config.js",
"test-unit:native:debug": "cd test/native/ && node --inspect-brk ../../node_modules/.bin/jest --runInBand --verbose --config ./jest.config.js",
"prestorybook:build": "npm run build:packages",
Expand Down
5 changes: 5 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@
<directory suffix="-test.php">./phpunit/</directory>
</testsuite>
</testsuites>
<groups>
<exclude>
<group>ms-required</group>
</exclude>
</groups>
</phpunit>
6 changes: 6 additions & 0 deletions phpunit/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ function fail_if_died( $message ) {
}
tests_add_filter( 'wp_die_handler', 'fail_if_died' );

$GLOBALS['wp_tests_options'] = array(
'gutenberg-experiments' => array(
'gutenberg-block-directory' => '1',
),
);

// Start up the WP testing environment.
require $_tests_dir . '/includes/bootstrap.php';

Expand Down
214 changes: 214 additions & 0 deletions phpunit/class-wp-rest-block-directory-controller-test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
<?php

/**
* Test WP_REST_Block_Directory_Controller_Test()
*
* @package Gutenberg
* phpcs:disable
*/
class WP_REST_Block_Directory_Controller_Test extends WP_Test_REST_Controller_Testcase {
protected static $admin_id;

public static function wpSetUpBeforeClass( $factory ) {
self::$admin_id = $factory->user->create(
array(
'role' => 'administrator',
)
);

if ( is_multisite() ) {
grant_super_admin( self::$admin_id );
}

if ( ! defined( 'FS_METHOD' ) ) {
define( 'FS_METHOD', 'direct' );
}
}

public static function wpTearDownAfterClass() {
self::delete_user( self::$admin_id );
}

public function test_register_routes() {
$routes = rest_get_server()->get_routes();

$this->assertArrayHasKey( '/__experimental/block-directory/search', $routes );
$this->assertArrayHasKey( '/__experimental/block-directory/install', $routes );
$this->assertArrayHasKey( '/__experimental/block-directory/uninstall', $routes );
}

public function test_context_param() {
// Collection.
$request = new WP_REST_Request( 'OPTIONS', '/__experimental/block-directory/search' );
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
$this->assertEquals( 'view', $data['endpoints'][0]['args']['context']['default'] );
$this->assertEquals( array( 'view' ), $data['endpoints'][0]['args']['context']['enum'] );
}

public function test_get_items() {
wp_set_current_user( self::$admin_id );

$request = new WP_REST_Request( 'GET', '/__experimental/block-directory/search' );
$request->set_query_params( array( 'term' => 'foo' ) );

$result = rest_do_request( $request );
$this->assertNotWPError( $result->as_error() );
$this->assertEquals( 200, $result->status );
}

public function test_get_items_wdotorg_unavailable() {
wp_set_current_user( self::$admin_id );

$request = new WP_REST_Request( 'GET', '/__experimental/block-directory/search' );
$request->set_query_params( array( 'term' => 'foo' ) );

$this->prevent_requests_to_host( 'api.wordpress.org' );

$this->expectException( 'PHPUnit_Framework_Error_Warning' );
$response = rest_do_request( $request );
$this->assertErrorResponse( 'plugins_api_failed', $response, 500 );
}

public function test_get_items_logged_out() {
$request = new WP_REST_Request( 'GET', '/__experimental/block-directory/search' );
$request->set_query_params( array( 'term' => 'foo' ) );
$response = rest_do_request( $request );
$this->assertErrorResponse( 'rest_block_directory_cannot_view', $response );
}

public function test_get_items_no_results() {
wp_set_current_user( self::$admin_id );

$request = new WP_REST_Request( 'GET', '/__experimental/block-directory/search' );
$request->set_query_params( array( 'term' => '0c4549ee68f24eaaed46a49dc983ecde' ) );
$response = rest_do_request( $request );
$data = $response->get_data();

// Should produce a 200 status with an empty array.
$this->assertEquals( 200, $response->status );
$this->assertEquals( array(), $data );
}

public function test_get_item() {
$this->markTestSkipped( 'Controller does not have get_item route.' );
}

public function test_create_item() {
if ( isset( get_plugins()['hello-dolly/hello.php'] ) ) {
delete_plugins( array( 'hello-dolly/hello.php' ) );
}

wp_set_current_user( self::$admin_id );

$request = new WP_REST_Request( 'POST', '/__experimental/block-directory/install' );
$request->set_body_params( array( 'slug' => 'hello-dolly' ) );

$response = rest_do_request( $request );
$this->skip_on_filesystem_error( $response );
$this->assertNotWPError( $response->as_error() );
$this->assertEquals( 201, $response->get_status() );
$this->assertEquals( 'Hello Dolly', $response->get_data()['name'] );
}

public function test_update_item() {
$this->markTestSkipped( 'Controller does not have update_item route.' );
}

public function test_delete_item() {
$this->markTestSkipped( 'Covered by Plugins controller tests.' );
}

public function test_prepare_item() {
wp_set_current_user( self::$admin_id );

// This will hit the live API. We're searching for `block` which should definitely return at least one result.
$request = new WP_REST_Request( 'GET', '/__experimental/block-directory/search' );
$request->set_query_params( array( 'term' => 'block' ) );
$response = rest_do_request( $request );
$data = $response->get_data();

$this->assertEquals( 200, $response->status );
// At least one result
$this->assertGreaterThanOrEqual( 1, count( $data ) );
// Each result should be an object with important attributes set
foreach ( $data as $plugin ) {
$this->assertArrayHasKey( 'name', $plugin );
$this->assertArrayHasKey( 'title', $plugin );
$this->assertArrayHasKey( 'id', $plugin );
$this->assertArrayHasKey( 'author_block_rating', $plugin );
$this->assertArrayHasKey( 'assets', $plugin );
$this->assertArrayHasKey( 'humanized_updated', $plugin );
}
}

public function test_get_item_schema() {
wp_set_current_user( self::$admin_id );

$request = new WP_REST_Request( 'OPTIONS', '/__experimental/block-directory/search' );
$request->set_query_params( array( 'term' => 'foo' ) );
$response = rest_do_request( $request );
$data = $response->get_data();

// Check endpoints
$this->assertEquals( [ 'GET' ], $data['endpoints'][0]['methods'] );
$this->assertTrue( $data['endpoints'][0]['args']['term'][ 'required'] );

$properties = $data['schema']['properties'];

$this->assertCount( 13, $properties );
$this->assertArrayHasKey( 'name', $properties );
$this->assertArrayHasKey( 'title', $properties );
$this->assertArrayHasKey( 'description', $properties );
$this->assertArrayHasKey( 'id', $properties );
$this->assertArrayHasKey( 'rating', $properties );
$this->assertArrayHasKey( 'rating_count', $properties );
$this->assertArrayHasKey( 'active_installs', $properties );
$this->assertArrayHasKey( 'author_block_rating', $properties );
$this->assertArrayHasKey( 'author_block_count', $properties );
$this->assertArrayHasKey( 'author', $properties );
$this->assertArrayHasKey( 'icon', $properties );
$this->assertArrayHasKey( 'humanized_updated', $properties );
$this->assertArrayHasKey( 'assets', $properties );
}

/**
* Skips the test if the response is an error due to the filesystem being unavailable.
*
* @since 5.5.0
*
* @param WP_REST_Response $response The response object to inspect.
*/
protected function skip_on_filesystem_error( WP_REST_Response $response ) {
if ( ! $response->is_error() ) {
return;
}

$code = $response->as_error()->get_error_code();

if ( 'fs_unavailable' === $code || false !== strpos( $code, 'mkdir_failed' ) ) {
$this->markTestSkipped( 'Filesystem is unavailable.' );
}
}

/**
* Simulate a network failure on outbound http requests to a given hostname.
*
* @param string $blocked_host The host to block connections to.
*/
private function prevent_requests_to_host( $blocked_host = 'api.wordpress.org' ) {
add_filter(
'pre_http_request',
static function ( $return, $args, $url ) use ( $blocked_host ) {
if ( @parse_url( $url, PHP_URL_HOST ) === $blocked_host ) {
return new WP_Error( 'plugins_api_failed', "An expected error occurred connecting to $blocked_host because of a unit test", "cURL error 7: Failed to connect to $blocked_host port 80: Connection refused" );

}

return $return;
},
10,
3
);
}
}
Loading

0 comments on commit 26363cf

Please sign in to comment.