This is an automated email from the ASF dual-hosted git repository. suddjian pushed a commit to branch viz-metadata-search in repository https://gitbox.apache.org/repos/asf/superset.git
commit 10fb7cfdcadb5ba997d3143eb0db7d8af634d6d9 Author: David Aaron Suddjian <[email protected]> AuthorDate: Fri Jun 25 17:00:59 2021 -0700 add metadata to plugin context --- .../src/components/DynamicPlugins/index.tsx | 96 ++++++++++++++++------ .../explore/components/ExploreViewContainer.jsx | 8 +- 2 files changed, 76 insertions(+), 28 deletions(-) diff --git a/superset-frontend/src/components/DynamicPlugins/index.tsx b/superset-frontend/src/components/DynamicPlugins/index.tsx index 90f4ba7..b11798f 100644 --- a/superset-frontend/src/components/DynamicPlugins/index.tsx +++ b/superset-frontend/src/components/DynamicPlugins/index.tsx @@ -17,30 +17,45 @@ * under the License. */ import React, { useContext, useEffect, useReducer } from 'react'; -import { defineSharedModules, logging, makeApi } from '@superset-ui/core'; +import { + ChartMetadata, + defineSharedModules, + getChartMetadataRegistry, + logging, + makeApi, +} from '@superset-ui/core'; import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags'; +import { omitBy } from 'lodash'; + +const metadataRegistry = getChartMetadataRegistry(); export type PluginContextType = { loading: boolean; - plugins: { + /** These are actually only the dynamic plugins */ + dynamicPlugins: { [key: string]: { key: string; - loading: boolean; + mounting: boolean; error: null | Error; }; }; + keys: string[]; + /** Mounted means the plugin's js bundle has been imported */ + mountedPluginMetadata: Record<string, ChartMetadata>; fetchAll: () => void; }; const dummyPluginContext: PluginContextType = { loading: true, - plugins: {}, + dynamicPlugins: {}, + keys: [], + mountedPluginMetadata: {}, fetchAll: () => {}, }; /** - * It is highly recommended to use the useDynamicPluginContext hook instead. - * @see useDynamicPluginContext + * It is highly recommended to use the usePluginContext hook instead. + * @see usePluginContext */ export const PluginContext = React.createContext(dummyPluginContext); @@ -52,7 +67,7 @@ export const PluginContext = React.createContext(dummyPluginContext); * Those are compiled into the Superset bundle at build time. * Dynamic plugins are added by the end user and can be any webhosted javascript. */ -export const useDynamicPluginContext = () => useContext(PluginContext); +export const usePluginContext = () => useContext(PluginContext); // the plugin returned from the API type Plugin = { @@ -75,38 +90,60 @@ type BeginAction = { keys: string[]; }; +type ChangedKeysAction = { + type: 'changed keys'; +}; + +type PluginAction = BeginAction | CompleteAction | ChangedKeysAction; + +function getRegistryData() { + return { + keys: metadataRegistry.keys(), + mountedPluginMetadata: omitBy( + metadataRegistry.getMap(), + value => value === undefined, + ) as Record<string, ChartMetadata>, // cast required to get rid of undefined values + }; +} + function pluginContextReducer( state: PluginContextType, - action: BeginAction | CompleteAction, + action: PluginAction, ): PluginContextType { switch (action.type) { case 'begin': { - const plugins = { ...state.plugins }; + const plugins = { ...state.dynamicPlugins }; action.keys.forEach(key => { - plugins[key] = { key, error: null, loading: true }; + plugins[key] = { key, error: null, mounting: true }; }); return { ...state, loading: action.keys.length > 0, - plugins, + dynamicPlugins: plugins, }; } case 'complete': { return { ...state, - loading: Object.values(state.plugins).some( - plugin => plugin.loading && plugin.key !== action.key, + loading: Object.values(state.dynamicPlugins).some( + plugin => plugin.mounting && plugin.key !== action.key, ), - plugins: { - ...state.plugins, + dynamicPlugins: { + ...state.dynamicPlugins, [action.key]: { key: action.key, - loading: false, + mounting: false, error: action.error, }, }, }; } + case 'changed keys': { + return { + ...state, + ...getRegistryData(), + }; + } default: return state; } @@ -126,14 +163,18 @@ const sharedModules = { }; export const DynamicPluginProvider: React.FC = ({ children }) => { - const [pluginState, dispatch] = useReducer(pluginContextReducer, { - // use the dummy plugin context, and override the methods - ...dummyPluginContext, - // eslint-disable-next-line @typescript-eslint/no-use-before-define - fetchAll, - loading: isFeatureEnabled(FeatureFlag.DYNAMIC_PLUGINS), - // TODO: Write fetchByKeys - }); + const [pluginState, dispatch] = useReducer( + pluginContextReducer, + dummyPluginContext, + state => ({ + ...state, + ...getRegistryData(), + // eslint-disable-next-line @typescript-eslint/no-use-before-define + fetchAll, + loading: isFeatureEnabled(FeatureFlag.DYNAMIC_PLUGINS), + // TODO: Write fetchByKeys + }), + ); // For now, we fetch all the plugins at the same time. // In the future it would be nice to fetch on an as-needed basis. @@ -171,6 +212,13 @@ export const DynamicPluginProvider: React.FC = ({ children }) => { if (isFeatureEnabled(FeatureFlag.DYNAMIC_PLUGINS)) { fetchAll(); } + const registryListener = () => { + dispatch({ type: 'changed keys' }); + }; + metadataRegistry.addListener(registryListener); + return () => { + metadataRegistry.removeListener(registryListener); + }; }, []); return ( diff --git a/superset-frontend/src/explore/components/ExploreViewContainer.jsx b/superset-frontend/src/explore/components/ExploreViewContainer.jsx index 63efc2b..fa920b1 100644 --- a/superset-frontend/src/explore/components/ExploreViewContainer.jsx +++ b/superset-frontend/src/explore/components/ExploreViewContainer.jsx @@ -25,7 +25,7 @@ import { styled, t, css, useTheme } from '@superset-ui/core'; import { debounce } from 'lodash'; import { Resizable } from 're-resizable'; -import { useDynamicPluginContext } from 'src/components/DynamicPlugins'; +import { usePluginContext } from 'src/components/DynamicPlugins'; import { Global } from '@emotion/react'; import { Tooltip } from 'src/components/Tooltip'; import { usePrevious } from 'src/common/hooks/usePrevious'; @@ -159,9 +159,9 @@ function useWindowSize({ delayMs = 250 } = {}) { } function ExploreViewContainer(props) { - const dynamicPluginContext = useDynamicPluginContext(); - const dynamicPlugin = dynamicPluginContext.plugins[props.vizType]; - const isDynamicPluginLoading = dynamicPlugin && dynamicPlugin.loading; + const dynamicPluginContext = usePluginContext(); + const dynamicPlugin = dynamicPluginContext.dynamicPlugins[props.vizType]; + const isDynamicPluginLoading = dynamicPlugin && dynamicPlugin.mounting; const wasDynamicPluginLoading = usePrevious(isDynamicPluginLoading); const previousControls = usePrevious(props.controls);
