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);

Reply via email to