From 62ee6497e90b7c72098dfce06f6cb99995bae52a Mon Sep 17 00:00:00 2001 From: Gilad Lekner Date: Sun, 17 Jan 2021 16:38:57 +0200 Subject: [PATCH] migrating reducer extension to new dynamic sdk --- .../console-app/console-extensions.json | 7 ++ frontend/packages/console-app/package.json | 8 +- .../extension-checks/reducers.spec.ts | 2 +- frontend/packages/console-app/src/plugin.tsx | 10 --- .../src/extensions/reducers.ts | 21 ++++++ .../src/schema/console-extensions.ts | 3 +- .../console-plugin-sdk/src/typings/index.ts | 1 - .../src/typings/reducers.ts | 19 ----- .../kubevirt-plugin/console-extensions.json | 13 +++- .../packages/kubevirt-plugin/package.json | 6 ++ .../packages/kubevirt-plugin/src/plugin.tsx | 13 +--- .../packages/topology/console-extensions.json | 10 ++- frontend/packages/topology/package.json | 6 ++ frontend/packages/topology/src/plugin.tsx | 11 +-- .../packages/topology/src/redux/reducer.ts | 2 +- frontend/public/redux.ts | 75 +++++++++---------- 16 files changed, 110 insertions(+), 97 deletions(-) create mode 100644 frontend/packages/console-dynamic-plugin-sdk/src/extensions/reducers.ts delete mode 100644 frontend/packages/console-plugin-sdk/src/typings/reducers.ts diff --git a/frontend/packages/console-app/console-extensions.json b/frontend/packages/console-app/console-extensions.json index dd94d9a074f..1c723eeab55 100644 --- a/frontend/packages/console-app/console-extensions.json +++ b/frontend/packages/console-app/console-extensions.json @@ -11,6 +11,13 @@ "kind": "DevWorkspace" } } + }, + { + "type": "console.reducer", + "properties": { + "namespace": "console", + "handler": { "$codeRef": "reducer" } + } } ] } diff --git a/frontend/packages/console-app/package.json b/frontend/packages/console-app/package.json index 728d293157e..7058a330396 100644 --- a/frontend/packages/console-app/package.json +++ b/frontend/packages/console-app/package.json @@ -29,6 +29,12 @@ "@console/topology": "0.0.0-fixed" }, "consolePlugin": { - "entry": "src/plugin.tsx" + "entry": "src/plugin.tsx", + "exposedModules": { + "reducer": "src/redux/reducer.ts" + }, + "dependencies": { + "@console/pluginAPI": "~0.0.1" + } } } diff --git a/frontend/packages/console-app/src/__tests__/extension-checks/reducers.spec.ts b/frontend/packages/console-app/src/__tests__/extension-checks/reducers.spec.ts index 30f3cdc7da1..4934d5e4af2 100644 --- a/frontend/packages/console-app/src/__tests__/extension-checks/reducers.spec.ts +++ b/frontend/packages/console-app/src/__tests__/extension-checks/reducers.spec.ts @@ -1,6 +1,6 @@ import * as _ from 'lodash'; import { testedExtensions } from '../plugin-test-utils'; -import { isReduxReducer } from '@console/plugin-sdk'; +import { isReduxReducer } from '@console/dynamic-plugin-sdk/src/extensions/reducers'; describe('ReduxReducer', () => { it('duplicate namespaces are not allowed', () => { diff --git a/frontend/packages/console-app/src/plugin.tsx b/frontend/packages/console-app/src/plugin.tsx index 89d8711a528..ba951b25333 100644 --- a/frontend/packages/console-app/src/plugin.tsx +++ b/frontend/packages/console-app/src/plugin.tsx @@ -12,7 +12,6 @@ import { DashboardsOverviewHealthPrometheusSubsystem, DashboardsOverviewInventoryItem, DashboardsOverviewHealthOperator, - ReduxReducer, ResourceDetailsPage, ResourceListPage, ResourceClusterNavItem, @@ -45,7 +44,6 @@ import { getClusterUpdateTimestamp, isClusterUpdateActivity, } from './components/dashboards-page/activity'; -import reducer from './redux/reducer'; import * as models from './models'; import { TourContext, useTourValuesForContext } from './components/tour/tour-context'; import { @@ -63,7 +61,6 @@ type ConsumedExtensions = | DashboardsOverviewHealthPrometheusSubsystem | DashboardsOverviewInventoryItem | DashboardsOverviewHealthOperator - | ReduxReducer | ResourceListPage | ResourceDetailsPage | ResourceClusterNavItem @@ -215,13 +212,6 @@ const plugin: Plugin = [ ).default, }, }, - { - type: 'ReduxReducer', - properties: { - namespace: 'console', - reducer, - }, - }, { type: 'NavItem/ResourceCluster', properties: { diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/extensions/reducers.ts b/frontend/packages/console-dynamic-plugin-sdk/src/extensions/reducers.ts new file mode 100644 index 00000000000..44653562d66 --- /dev/null +++ b/frontend/packages/console-dynamic-plugin-sdk/src/extensions/reducers.ts @@ -0,0 +1,21 @@ +import { Reducer } from 'redux'; +import { CodeRef, Extension } from '@console/plugin-sdk/src/typings/base'; + +export namespace ExtensionProperties { + export type ReduxReducer = { + /** The key to represent reducer-managed substate within the Redux state object. */ + namespace: string; + /** The reducer handler, resolved to a function that returns a reducer. */ + handler: CodeRef; + }; +} + +// Extension types + +export type ReduxReducer = Extension & { + type: 'console.reducer'; +}; + +// Type guards + +export const isReduxReducer = (e: Extension): e is ReduxReducer => e.type === 'console.reducer'; diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/schema/console-extensions.ts b/frontend/packages/console-dynamic-plugin-sdk/src/schema/console-extensions.ts index 451d2fb1076..0b01f832afa 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/schema/console-extensions.ts +++ b/frontend/packages/console-dynamic-plugin-sdk/src/schema/console-extensions.ts @@ -1,6 +1,7 @@ import { FeatureFlag, ModelFeatureFlag } from '../extensions/feature-flags'; +import { ReduxReducer } from '../extensions/reducers'; -export type SupportedExtension = FeatureFlag | ModelFeatureFlag; +export type SupportedExtension = FeatureFlag | ModelFeatureFlag | ReduxReducer; /** * Schema of Console plugin's `console-extensions.json` file. diff --git a/frontend/packages/console-plugin-sdk/src/typings/index.ts b/frontend/packages/console-plugin-sdk/src/typings/index.ts index 3447bfac5da..a75ee53b610 100644 --- a/frontend/packages/console-plugin-sdk/src/typings/index.ts +++ b/frontend/packages/console-plugin-sdk/src/typings/index.ts @@ -14,7 +14,6 @@ export * from './yaml-templates'; export * from './global-configs'; export * from './clusterserviceversions'; export * from './dev-catalog'; -export * from './reducers'; export * from './horizontal-nav'; export * from './providers'; export * from './pvc'; diff --git a/frontend/packages/console-plugin-sdk/src/typings/reducers.ts b/frontend/packages/console-plugin-sdk/src/typings/reducers.ts deleted file mode 100644 index 2114eab003c..00000000000 --- a/frontend/packages/console-plugin-sdk/src/typings/reducers.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Reducer } from 'redux'; -import { Extension } from './base'; - -namespace ExtensionProperties { - export interface ReduxReducer { - /** The key to represent reducer-managed substate within the Redux state object. */ - namespace: string; - /** The reducer function, operating on reducer-managed substate. */ - reducer: Reducer; - } -} - -export interface ReduxReducer extends Extension { - type: 'ReduxReducer'; -} - -export const isReduxReducer = (e: Extension): e is ReduxReducer => { - return e.type === 'ReduxReducer'; -}; diff --git a/frontend/packages/kubevirt-plugin/console-extensions.json b/frontend/packages/kubevirt-plugin/console-extensions.json index 74c9926ab6b..6fb4461c429 100644 --- a/frontend/packages/kubevirt-plugin/console-extensions.json +++ b/frontend/packages/kubevirt-plugin/console-extensions.json @@ -1,4 +1,15 @@ { "$schema": "../console-dynamic-plugin-sdk/dist/schema/console-extensions.json", - "data": [] + "data": [ + { + "type": "console.reducer", + "properties": { + "namespace": "kubevirt", + "handler": { "$codeRef": "redux" } + }, + "flags": { + "required": ["KUBEVIRT"] + } + } + ] } diff --git a/frontend/packages/kubevirt-plugin/package.json b/frontend/packages/kubevirt-plugin/package.json index a6e2248bce8..ccf43f87780 100644 --- a/frontend/packages/kubevirt-plugin/package.json +++ b/frontend/packages/kubevirt-plugin/package.json @@ -40,6 +40,12 @@ "integration-tests/tests/vm.tab.snapshots.scenario.ts", "integration-tests/tests/vmi.actions.scenario.ts" ] + }, + "exposedModules": { + "redux": "src/redux/index.ts" + }, + "dependencies": { + "@console/pluginAPI": "~0.0.1" } } } diff --git a/frontend/packages/kubevirt-plugin/src/plugin.tsx b/frontend/packages/kubevirt-plugin/src/plugin.tsx index 64f9ef42383..ff41a5a15b5 100644 --- a/frontend/packages/kubevirt-plugin/src/plugin.tsx +++ b/frontend/packages/kubevirt-plugin/src/plugin.tsx @@ -14,7 +14,6 @@ import { DashboardsOverviewHealthURLSubsystem, DashboardsOverviewInventoryItem, DashboardsInventoryItemGroup, - ReduxReducer, ProjectDashboardInventoryItem, DashboardsOverviewResourceActivity, ContextProvider, @@ -26,6 +25,7 @@ import { CatalogItemType, CatalogItemFilter, } from '@console/plugin-sdk'; +import { ReduxReducer } from '@console/dynamic-plugin-sdk/src/extensions/reducers'; import { DashboardsStorageCapacityDropdownItem } from '@console/ceph-storage-plugin'; import { TemplateModel, PodModel, PersistentVolumeClaimModel } from '@console/internal/models'; import { getName } from '@console/shared/src/selectors/common'; @@ -37,7 +37,6 @@ import { getVMStatusGroups, VMOffGroupIcon, } from './components/dashboards-page/overview-dashboard/inventory'; -import kubevirtReducer from './redux'; import { diskImportKindMapping } from './components/dashboards-page/overview-dashboard/utils'; import { TopologyConsumedExtensions, getTopologyPlugin } from './topology/topology-plugin'; import { @@ -374,16 +373,6 @@ const plugin: Plugin = [ required: FLAG_KUBEVIRT, }, }, - { - type: 'ReduxReducer', - properties: { - namespace: 'kubevirt', - reducer: kubevirtReducer, - }, - flags: { - required: [FLAG_KUBEVIRT], - }, - }, { type: 'Project/Dashboard/Inventory/Item', properties: { diff --git a/frontend/packages/topology/console-extensions.json b/frontend/packages/topology/console-extensions.json index 74c9926ab6b..7c479857c2f 100644 --- a/frontend/packages/topology/console-extensions.json +++ b/frontend/packages/topology/console-extensions.json @@ -1,4 +1,12 @@ { "$schema": "../console-dynamic-plugin-sdk/dist/schema/console-extensions.json", - "data": [] + "data": [ + { + "type": "console.reducer", + "properties": { + "namespace": "devconsole", + "handler": { "$codeRef": "reducer.default" } + } + } + ] } diff --git a/frontend/packages/topology/package.json b/frontend/packages/topology/package.json index 6e5414a3f5e..7cf55376df3 100644 --- a/frontend/packages/topology/package.json +++ b/frontend/packages/topology/package.json @@ -21,6 +21,12 @@ "topology": [ "integration-tests/**/*.scenario.ts" ] + }, + "exposedModules": { + "reducer": "src/utils/reducer.ts" + }, + "dependencies": { + "@console/pluginAPI": "~0.0.1" } } } diff --git a/frontend/packages/topology/src/plugin.tsx b/frontend/packages/topology/src/plugin.tsx index ae168effe08..0cc10ddf2a1 100755 --- a/frontend/packages/topology/src/plugin.tsx +++ b/frontend/packages/topology/src/plugin.tsx @@ -1,10 +1,9 @@ import * as _ from 'lodash'; -import { Plugin, ReduxReducer, ModelDefinition, ModelFeatureFlag } from '@console/plugin-sdk'; +import { Plugin, ModelDefinition, ModelFeatureFlag } from '@console/plugin-sdk'; import { OperatorsTopologyConsumedExtensions, operatorsTopologyPlugin, } from './operators/operatorsTopologyPlugin'; -import reducer from './utils/reducer'; import * as models from './models'; import { ServiceBindingModel } from './models/service-binding'; import { ALLOW_SERVICE_BINDING_FLAG } from './const'; @@ -14,7 +13,6 @@ import { TopologyDecoratorProvider } from './extensions'; type ConsumedExtensions = | ModelDefinition | ModelFeatureFlag - | ReduxReducer | TopologyDecoratorProvider | OperatorsTopologyConsumedExtensions; @@ -32,13 +30,6 @@ const plugin: Plugin = [ flag: ALLOW_SERVICE_BINDING_FLAG, }, }, - { - type: 'ReduxReducer', - properties: { - namespace: 'devconsole', - reducer, - }, - }, ...operatorsTopologyPlugin, ...defaultDecoratorsPlugin, ]; diff --git a/frontend/packages/topology/src/redux/reducer.ts b/frontend/packages/topology/src/redux/reducer.ts index 0d493026398..3d388b656e8 100644 --- a/frontend/packages/topology/src/redux/reducer.ts +++ b/frontend/packages/topology/src/redux/reducer.ts @@ -2,7 +2,7 @@ import { Map } from 'immutable'; import { TopologyAction, Actions } from './action'; import { DEFAULT_TOPOLOGY_FILTERS } from '../filters/const'; -export type State = Map; +type State = Map; export default (state: State, action: TopologyAction) => { if (!state) { diff --git a/frontend/public/redux.ts b/frontend/public/redux.ts index c13dff671db..8f6cd5d587e 100644 --- a/frontend/public/redux.ts +++ b/frontend/public/redux.ts @@ -1,13 +1,14 @@ import { applyMiddleware, combineReducers, createStore, compose, ReducersMapObject } from 'redux'; import * as _ from 'lodash-es'; - -import { isReduxReducer } from '@console/plugin-sdk/src/typings/reducers'; -import { isExtensionInUse, getGatingFlagNames } from '@console/plugin-sdk/src/store'; import { featureReducer, featureReducerName, FeatureState } from './reducers/features'; import k8sReducers, { K8sState } from './reducers/k8s'; import UIReducers, { UIState } from './reducers/ui'; import { dashboardsReducer, DashboardsState } from './reducers/dashboards'; -import { pluginStore } from './plugins'; +import { isReduxReducer, ReduxReducer } from '@console/dynamic-plugin-sdk/src/extensions/reducers'; +import { + extensionDiffListener, + subscribeToExtensions, +} from '@console/plugin-sdk/src/api/subscribeToExtensions'; const composeEnhancers = (process.env.NODE_ENV !== 'production' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose; @@ -53,41 +54,37 @@ const store = createStore( composeEnhancers(applyMiddleware(thunk)), ); -const addPluginListener = () => { - const reducerExtensions = pluginStore.getAllExtensions().filter(isReduxReducer); - const getReduxFlagsObject = () => { - const gatingFlags = getGatingFlagNames(reducerExtensions); - const featureState = store.getState()[featureReducerName]; - return featureState ? _.pick(featureState.toObject(), gatingFlags) : null; - }; - - let flagsObject = getReduxFlagsObject(); - - store.subscribe(() => { - const currentFlagsObject = getReduxFlagsObject(); - - if (JSON.stringify(flagsObject) !== JSON.stringify(currentFlagsObject)) { - flagsObject = currentFlagsObject; - - const pluginReducerExtensions = reducerExtensions.filter((e) => - isExtensionInUse(e, flagsObject), - ); - - const pluginReducers: ReducersMapObject = pluginReducerExtensions.reduce((map, e) => { - map[e.properties.namespace] = e.properties.reducer; - return map; - }, {}); - - const nextReducers: ReducersMapObject = _.isEmpty(pluginReducers) - ? baseReducers - : { plugins: combineReducers(pluginReducers), ...baseReducers }; - - store.replaceReducer(combineReducers(nextReducers)); - } - }); -}; - -addPluginListener(); +const processedExtensions: ReduxReducer[] = []; +const pluginReducers: ReducersMapObject = {}; + +subscribeToExtensions( + extensionDiffListener((added, removed) => { + removed.forEach((e) => { + delete pluginReducers[e.properties.namespace]; + }); + + const newExtensions = added.filter((e) => !processedExtensions.includes(e)); + const handlerPromises = newExtensions.map((e) => e.properties.handler()); + + Promise.all(handlerPromises) + .then((reducers) => { + newExtensions.forEach((e, i) => { + pluginReducers[e.properties.namespace] = reducers[i]; + }); + + const nextReducers: ReducersMapObject = _.isEmpty(pluginReducers) + ? baseReducers + : { plugins: combineReducers(pluginReducers), ...baseReducers }; + + store.replaceReducer(combineReducers(nextReducers)); + }) + .catch((err) => { + // eslint-disable-next-line no-console + console.error('Failed to add reducer extension', err); + }); + }), + isReduxReducer, +); if (process.env.NODE_ENV !== 'production') { // Expose Redux store for debugging