From 63965ca44397e5902e3bfacdad5f57728e9c5e53 Mon Sep 17 00:00:00 2001 From: Kelly Dwan Date: Fri, 3 Jul 2020 12:36:52 -0400 Subject: [PATCH 1/5] Block Directory: Return inactive plugins with block directory results --- lib/class-wp-rest-block-directory-controller.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/class-wp-rest-block-directory-controller.php b/lib/class-wp-rest-block-directory-controller.php index 9e911d28c9bd0..df1f2e408506e 100644 --- a/lib/class-wp-rest-block-directory-controller.php +++ b/lib/class-wp-rest-block-directory-controller.php @@ -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 ); } From 55928649972ecc2ea2ca94495d02f0d0eeff10bb Mon Sep 17 00:00:00 2001 From: Kelly Dwan Date: Fri, 3 Jul 2020 13:25:24 -0400 Subject: [PATCH 2/5] 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. --- packages/block-directory/src/store/actions.js | 42 +++-- .../block-directory/src/store/test/actions.js | 146 ++++++++++++++---- .../src/store/utils/get-plugin-url.js | 17 ++ 3 files changed, 167 insertions(+), 38 deletions(-) create mode 100644 packages/block-directory/src/store/utils/get-plugin-url.js diff --git a/packages/block-directory/src/store/actions.js b/packages/block-directory/src/store/actions.js index 09e7b6b5d52b9..ec73abe2ffbdc 100644 --- a/packages/block-directory/src/store/actions.js +++ b/packages/block-directory/src/store/actions.js @@ -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 @@ -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' ); @@ -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 ); diff --git a/packages/block-directory/src/store/test/actions.js b/packages/block-directory/src/store/test/actions.js index 71244383d624d..306b41cfaa538 100644 --- a/packages/block-directory/src/store/test/actions.js +++ b/packages/block-directory/src/store/test/actions.js @@ -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', @@ -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, } ); @@ -47,15 +57,87 @@ 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: 'SET_ERROR_NOTICE', + blockId: inactiveBlock.id, + notice: false, + } ); + + 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( { @@ -65,9 +147,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, } ); @@ -78,21 +160,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, } ); @@ -103,16 +185,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, } ); @@ -131,12 +213,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, } ); @@ -148,16 +230,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', }, } ); @@ -166,14 +258,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( { @@ -183,12 +275,12 @@ 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', }, } ); @@ -196,7 +288,7 @@ describe( 'actions', () => { expect( generator.next().value ).toMatchObject( { type: 'API_FETCH', request: { - url: endpoint, + url: pluginEndpoint, method: 'DELETE', }, } ); diff --git a/packages/block-directory/src/store/utils/get-plugin-url.js b/packages/block-directory/src/store/utils/get-plugin-url.js new file mode 100644 index 0000000000000..71bd8257d66b4 --- /dev/null +++ b/packages/block-directory/src/store/utils/get-plugin-url.js @@ -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; +} From b42c29db2da388a7132952bb0ebc58149927668e Mon Sep 17 00:00:00 2001 From: Kelly Dwan Date: Fri, 3 Jul 2020 14:41:59 -0400 Subject: [PATCH 3/5] Add links to mocked plugin response --- .../specs/editor/plugins/block-directory-add.test.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/e2e-tests/specs/editor/plugins/block-directory-add.test.js b/packages/e2e-tests/specs/editor/plugins/block-directory-add.test.js index 249e5bba64598..09b7be821e475 100644 --- a/packages/e2e-tests/specs/editor/plugins/block-directory-add.test.js +++ b/packages/e2e-tests/specs/editor/plugins/block-directory-add.test.js @@ -55,6 +55,15 @@ const MOCK_INSTALLED_BLOCK_PLUGIN_DETAILS = { requires_wp: '', requires_php: '', text_domain: 'block-directory-test-block', + _links: [ + { + self: [ + { + href: '', + }, + ], + }, + ], }; const MOCK_BLOCK2 = { From 44902349bd56c0b9ee003e660ac10da11010bdf2 Mon Sep 17 00:00:00 2001 From: Kelly Dwan Date: Fri, 3 Jul 2020 15:49:35 -0400 Subject: [PATCH 4/5] Fix mocked test data --- .../editor/plugins/block-directory-add.test.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/e2e-tests/specs/editor/plugins/block-directory-add.test.js b/packages/e2e-tests/specs/editor/plugins/block-directory-add.test.js index 09b7be821e475..b77f97f952601 100644 --- a/packages/e2e-tests/specs/editor/plugins/block-directory-add.test.js +++ b/packages/e2e-tests/specs/editor/plugins/block-directory-add.test.js @@ -37,6 +37,7 @@ const MOCK_BLOCK1 = { 'https://fake_url.com/block.js', // we will mock this ], humanized_updated: '5 months ago', + links: {}, }; const MOCK_INSTALLED_BLOCK_PLUGIN_DETAILS = { @@ -55,15 +56,13 @@ const MOCK_INSTALLED_BLOCK_PLUGIN_DETAILS = { requires_wp: '', requires_php: '', text_domain: 'block-directory-test-block', - _links: [ - { - self: [ - { - href: '', - }, - ], - }, - ], + _links: { + self: [ + { + href: '', + }, + ], + }, }; const MOCK_BLOCK2 = { From 147ea272f5ac2428a2a1074e361b0bbba3879ac3 Mon Sep 17 00:00:00 2001 From: Kelly Dwan Date: Fri, 3 Jul 2020 16:01:05 -0400 Subject: [PATCH 5/5] Fix notice in install test --- packages/block-directory/src/store/test/actions.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/block-directory/src/store/test/actions.js b/packages/block-directory/src/store/test/actions.js index 306b41cfaa538..147ad2b1e415b 100644 --- a/packages/block-directory/src/store/test/actions.js +++ b/packages/block-directory/src/store/test/actions.js @@ -111,9 +111,8 @@ describe( 'actions', () => { const generator = installBlockType( inactiveBlock ); expect( generator.next().value ).toEqual( { - type: 'SET_ERROR_NOTICE', + type: 'CLEAR_ERROR_NOTICE', blockId: inactiveBlock.id, - notice: false, } ); expect( generator.next().value ).toEqual( {