Skip to content

Commit

Permalink
Block Directory: Return inactive plugins in search results (#23688)
Browse files Browse the repository at this point in the history
* Block Directory: Return inactive plugins with block directory results

* Active a an inactive block plugin if we see a plugin URL

The plugin URL will only exist if the plugin is already installed, so we can use that to activate that plugin. We can also use this link for uninstallation, so we make sure to add it into the block object when installing a plugin.

* Add links to mocked plugin response

* Fix mocked test data

* Fix notice in install test
  • Loading branch information
ryelle authored Jul 6, 2020
1 parent cafa3fe commit 4726320
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 42 deletions.
4 changes: 0 additions & 4 deletions lib/class-wp-rest-block-directory-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,6 @@ public function get_items( $request ) {
$result = array();

foreach ( $response->plugins as $plugin ) {
if ( $this->find_plugin_for_slug( $plugin['slug'] ) ) {
continue;
}

$data = $this->prepare_item_for_response( $plugin, $request );
$result[] = $this->prepare_response_for_collection( $data );
}
Expand Down
42 changes: 31 additions & 11 deletions packages/block-directory/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { apiFetch, dispatch, select } from '@wordpress/data-controls';
* Internal dependencies
*/
import { loadAssets } from './controls';
import getPluginUrl from './utils/get-plugin-url';

/**
* Returns an action object used in signalling that the downloadable blocks
Expand Down Expand Up @@ -54,16 +55,35 @@ export function* installBlockType( block ) {
throw new Error( __( 'Block has no assets.' ) );
}
yield setIsInstalling( block.id, true );
const response = yield apiFetch( {
path: 'wp/v2/plugins',
data: {
slug: block.id,
status: 'active',
},
method: 'POST',

// If we have a wp:plugin link, the plugin is installed but inactive.
const url = getPluginUrl( block );
let links = {};
if ( url ) {
yield apiFetch( {
url,
data: {
status: 'active',
},
method: 'PUT',
} );
} else {
const response = yield apiFetch( {
path: 'wp/v2/plugins',
data: {
slug: block.id,
status: 'active',
},
method: 'POST',
} );
// Add the `self` link for newly-installed blocks.
links = response._links;
}

yield addInstalledBlockType( {
...block,
links: { ...block.links, ...links },
} );
const endpoint = response?._links?.self[ 0 ]?.href;
yield addInstalledBlockType( { ...block, endpoint } );

yield loadAssets( assets );
const registeredBlocks = yield select( 'core/blocks', 'getBlockTypes' );
Expand Down Expand Up @@ -112,14 +132,14 @@ export function* installBlockType( block ) {
export function* uninstallBlockType( block ) {
try {
yield apiFetch( {
url: block.endpoint,
url: getPluginUrl( block ),
data: {
status: 'inactive',
},
method: 'PUT',
} );
yield apiFetch( {
url: block.endpoint,
url: getPluginUrl( block ),
method: 'DELETE',
} );
yield removeInstalledBlockType( block );
Expand Down
145 changes: 118 additions & 27 deletions packages/block-directory/src/store/test/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,20 @@
import { installBlockType, uninstallBlockType } from '../actions';

describe( 'actions', () => {
const endpoint = '/wp-json/wp/v2/plugins/block/block';
const pluginEndpoint =
'https://example.com/wp-json/wp/v2/plugins/block/block';
const item = {
id: 'block/block',
name: 'Test Block',
assets: [ 'script.js' ],
links: {
'wp:install-plugin': [
{
href:
'https://example.com/wp-json/wp/v2/plugins?slug=waves',
},
],
},
};
const plugin = {
plugin: 'block/block.php',
Expand All @@ -18,24 +27,25 @@ describe( 'actions', () => {
_links: {
self: [
{
href: endpoint,
href: pluginEndpoint,
},
],
},
};

describe( 'installBlockType', () => {
const block = item;
it( 'should install a block successfully', () => {
const generator = installBlockType( item );
const generator = installBlockType( block );

expect( generator.next().value ).toEqual( {
type: 'CLEAR_ERROR_NOTICE',
blockId: item.id,
blockId: block.id,
} );

expect( generator.next().value ).toEqual( {
type: 'SET_INSTALLING_BLOCK',
blockId: item.id,
blockId: block.id,
isInstalling: true,
} );

Expand All @@ -47,15 +57,86 @@ describe( 'actions', () => {
},
} );

const itemWithEndpoint = { ...item, endpoint };
expect( generator.next( plugin ).value ).toEqual( {
type: 'ADD_INSTALLED_BLOCK_TYPE',
item: itemWithEndpoint,
item: {
...block,
links: {
...block.links,
self: [
{
href: pluginEndpoint,
},
],
},
},
} );

expect( generator.next().value ).toEqual( {
type: 'LOAD_ASSETS',
assets: block.assets,
} );

expect( generator.next().value ).toEqual( {
args: [],
selectorName: 'getBlockTypes',
storeKey: 'core/blocks',
type: 'SELECT',
} );

expect( generator.next( [ block ] ).value ).toEqual( {
type: 'SET_INSTALLING_BLOCK',
blockId: block.id,
isInstalling: false,
} );

expect( generator.next() ).toEqual( {
value: true,
done: true,
} );
} );

it( 'should activate an inactive block plugin successfully', () => {
const inactiveBlock = {
...block,
links: {
...block.links,
'wp:plugin': [
{
href: pluginEndpoint,
},
],
},
};
const generator = installBlockType( inactiveBlock );

expect( generator.next().value ).toEqual( {
type: 'CLEAR_ERROR_NOTICE',
blockId: inactiveBlock.id,
} );

expect( generator.next().value ).toEqual( {
type: 'SET_INSTALLING_BLOCK',
blockId: inactiveBlock.id,
isInstalling: true,
} );

expect( generator.next().value ).toMatchObject( {
type: 'API_FETCH',
request: {
url: pluginEndpoint,
method: 'PUT',
},
} );

expect( generator.next( plugin ).value ).toEqual( {
type: 'ADD_INSTALLED_BLOCK_TYPE',
item: inactiveBlock,
} );

expect( generator.next().value ).toEqual( {
type: 'LOAD_ASSETS',
assets: item.assets,
assets: inactiveBlock.assets,
} );

expect( generator.next().value ).toEqual( {
Expand All @@ -65,9 +146,9 @@ describe( 'actions', () => {
type: 'SELECT',
} );

expect( generator.next( [ item ] ).value ).toEqual( {
expect( generator.next( [ inactiveBlock ] ).value ).toEqual( {
type: 'SET_INSTALLING_BLOCK',
blockId: item.id,
blockId: inactiveBlock.id,
isInstalling: false,
} );

Expand All @@ -78,21 +159,21 @@ describe( 'actions', () => {
} );

it( 'should set an error if the plugin has no assets', () => {
const generator = installBlockType( { ...item, assets: [] } );
const generator = installBlockType( { ...block, assets: [] } );

expect( generator.next().value ).toEqual( {
type: 'CLEAR_ERROR_NOTICE',
blockId: item.id,
blockId: block.id,
} );

expect( generator.next().value ).toMatchObject( {
type: 'SET_ERROR_NOTICE',
blockId: item.id,
blockId: block.id,
} );

expect( generator.next().value ).toEqual( {
type: 'SET_INSTALLING_BLOCK',
blockId: item.id,
blockId: block.id,
isInstalling: false,
} );

Expand All @@ -103,16 +184,16 @@ describe( 'actions', () => {
} );

it( "should set an error if the plugin can't install", () => {
const generator = installBlockType( item );
const generator = installBlockType( block );

expect( generator.next().value ).toEqual( {
type: 'CLEAR_ERROR_NOTICE',
blockId: item.id,
blockId: block.id,
} );

expect( generator.next().value ).toEqual( {
type: 'SET_INSTALLING_BLOCK',
blockId: item.id,
blockId: block.id,
isInstalling: true,
} );

Expand All @@ -131,12 +212,12 @@ describe( 'actions', () => {
};
expect( generator.throw( apiError ).value ).toMatchObject( {
type: 'SET_ERROR_NOTICE',
blockId: item.id,
blockId: block.id,
} );

expect( generator.next().value ).toEqual( {
type: 'SET_INSTALLING_BLOCK',
blockId: item.id,
blockId: block.id,
isInstalling: false,
} );

Expand All @@ -148,16 +229,26 @@ describe( 'actions', () => {
} );

describe( 'uninstallBlockType', () => {
const itemWithEndpoint = { ...item, endpoint };
const block = {
...item,
links: {
...item.links,
self: [
{
href: pluginEndpoint,
},
],
},
};

it( 'should uninstall a block successfully', () => {
const generator = uninstallBlockType( itemWithEndpoint );
const generator = uninstallBlockType( block );

// First the deactivation step
expect( generator.next().value ).toMatchObject( {
type: 'API_FETCH',
request: {
url: endpoint,
url: pluginEndpoint,
method: 'PUT',
},
} );
Expand All @@ -166,14 +257,14 @@ describe( 'actions', () => {
expect( generator.next().value ).toMatchObject( {
type: 'API_FETCH',
request: {
url: endpoint,
url: pluginEndpoint,
method: 'DELETE',
},
} );

expect( generator.next().value ).toEqual( {
type: 'REMOVE_INSTALLED_BLOCK_TYPE',
item: itemWithEndpoint,
item: block,
} );

expect( generator.next() ).toEqual( {
Expand All @@ -183,20 +274,20 @@ describe( 'actions', () => {
} );

it( "should set a global notice if the plugin can't be deleted", () => {
const generator = uninstallBlockType( itemWithEndpoint );
const generator = uninstallBlockType( block );

expect( generator.next().value ).toMatchObject( {
type: 'API_FETCH',
request: {
url: endpoint,
url: pluginEndpoint,
method: 'PUT',
},
} );

expect( generator.next().value ).toMatchObject( {
type: 'API_FETCH',
request: {
url: endpoint,
url: pluginEndpoint,
method: 'DELETE',
},
} );
Expand Down
17 changes: 17 additions & 0 deletions packages/block-directory/src/store/utils/get-plugin-url.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Get the plugin's direct API link out of a block-directory response.
*
* @param {Object} block The block object
*
* @return {string} The plugin URL, if exists.
*/
export default function getPluginUrl( block ) {
if ( ! block ) {
return false;
}
const link = block.links[ 'wp:plugin' ] || block.links.self;
if ( link && link.length ) {
return link[ 0 ].href;
}
return false;
}
Loading

0 comments on commit 4726320

Please sign in to comment.