Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add plugins endpoints #22454

Merged
merged 25 commits into from
Jun 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a8283a1
First pass at plugins endpoint.
TimothyBJacobs May 19, 2020
1ca7189
phpcs fixes. Don't check status permissions if not updating it
TimothyBJacobs May 19, 2020
9ecb573
Try forcing the filesystem method to direct
TimothyBJacobs May 19, 2020
471332a
Fix spacing issue
TimothyBJacobs May 19, 2020
4480a8a
Clean up block directory controller
tellyworth May 20, 2020
2221a1d
Refactors block directory controller to make inner requests to the pl…
TimothyBJacobs May 21, 2020
1af90c5
Fix permission check for activating a plugin on install. Add more links.
TimothyBJacobs May 21, 2020
1cca9d2
Check if the block plugin is already installed.
TimothyBJacobs May 23, 2020
269253c
Skip tests on fs errors.
TimothyBJacobs May 23, 2020
3ee82a0
Fix PHPCS
TimothyBJacobs May 23, 2020
5999ff1
Refactors directory tests to use the controller base class.
TimothyBJacobs May 23, 2020
95cbea4
Add some more tests. Fix status handling.
TimothyBJacobs May 23, 2020
8fc84be
Use wp_tests_options instead of manually using a filter.
TimothyBJacobs Jun 4, 2020
633b5d4
Fix multisite tests.
TimothyBJacobs Jun 4, 2020
9e21b2d
Add a bunch more multisite test.
TimothyBJacobs Jun 4, 2020
8bb6b98
Fix PHPCS
TimothyBJacobs Jun 4, 2020
0d44167
Wrap specific network permission checks in an is_multisite conditional.
TimothyBJacobs Jun 4, 2020
a4bb8eb
Add text_domain to response.
TimothyBJacobs Jun 4, 2020
1182129
Add more network only plugin tests.
TimothyBJacobs Jun 4, 2020
b39f30b
Follow core pattern for DELETE responses.
TimothyBJacobs Jun 10, 2020
3737daf
Introduce prepare_links method for generating links. Use sprintf for …
TimothyBJacobs Jun 10, 2020
f525a35
Improve argument sanitization & validation.
TimothyBJacobs Jun 10, 2020
98deefe
Explicitly add a 500 status code when returning a passed-through error.
TimothyBJacobs Jun 10, 2020
09a21d0
Add "rest_prepare_plugin" filter.
TimothyBJacobs Jun 10, 2020
3d1a7d6
Add "status" and "search" parameters.
TimothyBJacobs Jun 10, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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