@perses-dev/plugin-system 0.7.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/PluginLoadingBoundary/PluginLoader.js +2 -2
- package/dist/cjs/components/PluginLoadingBoundary/PluginLoadingBoundary.js +7 -7
- package/dist/cjs/components/PluginRegistry/PluginRegistry.js +48 -35
- package/dist/cjs/components/PluginRegistry/PluginRegistry.test.js +73 -0
- package/dist/cjs/components/PluginRegistry/index.js +2 -1
- package/dist/cjs/components/PluginRegistry/legacy/PluginRegistry.js +57 -0
- package/dist/cjs/components/PluginRegistry/legacy/index.js +29 -0
- package/dist/cjs/components/PluginRegistry/{registry-state.js → legacy/registry-state.js} +39 -39
- package/dist/cjs/components/PluginRegistry/plugin-indexes.js +67 -0
- package/dist/cjs/components/PluginRegistry/plugin-registry-model.js +25 -0
- package/dist/cjs/components/PluginRegistry/test-plugins/bert/index.js +26 -0
- package/dist/cjs/components/PluginRegistry/test-plugins/bert/plugin.json +32 -0
- package/dist/cjs/components/PluginRegistry/test-plugins/ernie/index.js +23 -0
- package/dist/cjs/components/PluginRegistry/test-plugins/ernie/plugin.json +24 -0
- package/dist/cjs/components/PluginRegistry/test-plugins/index.js +63 -0
- package/dist/cjs/components/index.js +1 -1
- package/dist/cjs/model/graph-queries.js +1 -1
- package/dist/cjs/model/index.js +2 -1
- package/dist/cjs/model/panels.js +26 -10
- package/dist/cjs/model/plugins.js +1 -1
- package/dist/cjs/model/variables.js +1 -1
- package/dist/cjs/model/visual-editing.js +14 -0
- package/dist/cjs/runtime/index.js +1 -0
- package/dist/cjs/runtime/plugins.js +38 -0
- package/dist/cjs/runtime/query-string.js +28 -0
- package/dist/cjs/runtime/template-variables.js +20 -9
- package/dist/cjs/runtime/time-range.js +1 -1
- package/dist/cjs/test/render.js +27 -0
- package/dist/cjs/test/setup-tests.js +24 -0
- package/dist/cjs/utils/cache-keys.js +22 -0
- package/dist/components/PluginLoadingBoundary/PluginLoader.js +1 -1
- package/dist/components/PluginLoadingBoundary/PluginLoadingBoundary.d.ts +2 -2
- package/dist/components/PluginLoadingBoundary/PluginLoadingBoundary.d.ts.map +1 -1
- package/dist/components/PluginLoadingBoundary/PluginLoadingBoundary.js +1 -1
- package/dist/components/PluginRegistry/PluginRegistry.d.ts +6 -21
- package/dist/components/PluginRegistry/PluginRegistry.d.ts.map +1 -1
- package/dist/components/PluginRegistry/PluginRegistry.js +1 -1
- package/dist/components/PluginRegistry/PluginRegistry.test.d.ts +2 -0
- package/dist/components/PluginRegistry/PluginRegistry.test.d.ts.map +1 -0
- package/dist/components/PluginRegistry/PluginRegistry.test.js +1 -0
- package/dist/components/PluginRegistry/index.d.ts +1 -0
- package/dist/components/PluginRegistry/index.d.ts.map +1 -1
- package/dist/components/PluginRegistry/index.js +1 -1
- package/dist/components/PluginRegistry/legacy/PluginRegistry.d.ts +29 -0
- package/dist/components/PluginRegistry/legacy/PluginRegistry.d.ts.map +1 -0
- package/dist/components/PluginRegistry/legacy/PluginRegistry.js +1 -0
- package/dist/components/PluginRegistry/legacy/index.d.ts +2 -0
- package/dist/components/PluginRegistry/legacy/index.d.ts.map +1 -0
- package/dist/components/PluginRegistry/legacy/index.js +1 -0
- package/dist/components/PluginRegistry/{registry-state.d.ts → legacy/registry-state.d.ts} +4 -4
- package/dist/components/PluginRegistry/legacy/registry-state.d.ts.map +1 -0
- package/dist/components/PluginRegistry/legacy/registry-state.js +1 -0
- package/dist/components/PluginRegistry/plugin-indexes.d.ts +11 -0
- package/dist/components/PluginRegistry/plugin-indexes.d.ts.map +1 -0
- package/dist/components/PluginRegistry/plugin-indexes.js +1 -0
- package/dist/components/PluginRegistry/plugin-registry-model.d.ts +10 -0
- package/dist/components/PluginRegistry/plugin-registry-model.d.ts.map +1 -0
- package/dist/components/PluginRegistry/plugin-registry-model.js +1 -0
- package/dist/components/PluginRegistry/test-plugins/bert/index.d.ts +4 -0
- package/dist/components/PluginRegistry/test-plugins/bert/index.d.ts.map +1 -0
- package/dist/components/PluginRegistry/test-plugins/bert/index.js +1 -0
- package/dist/components/PluginRegistry/test-plugins/bert/plugin.json +32 -0
- package/dist/components/PluginRegistry/test-plugins/ernie/index.d.ts +3 -0
- package/dist/components/PluginRegistry/test-plugins/ernie/index.d.ts.map +1 -0
- package/dist/components/PluginRegistry/test-plugins/ernie/index.js +1 -0
- package/dist/components/PluginRegistry/test-plugins/ernie/plugin.json +24 -0
- package/dist/components/PluginRegistry/test-plugins/index.d.ts +6 -0
- package/dist/components/PluginRegistry/test-plugins/index.d.ts.map +1 -0
- package/dist/components/PluginRegistry/test-plugins/index.js +1 -0
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -1
- package/dist/model/graph-queries.js +1 -1
- package/dist/model/index.d.ts +1 -0
- package/dist/model/index.d.ts.map +1 -1
- package/dist/model/index.js +1 -1
- package/dist/model/panels.d.ts +5 -2
- package/dist/model/panels.d.ts.map +1 -1
- package/dist/model/panels.js +1 -1
- package/dist/model/plugins.d.ts +20 -35
- package/dist/model/plugins.d.ts.map +1 -1
- package/dist/model/variables.d.ts +3 -3
- package/dist/model/variables.d.ts.map +1 -1
- package/dist/model/variables.js +1 -1
- package/dist/model/visual-editing.d.ts +18 -0
- package/dist/model/visual-editing.d.ts.map +1 -0
- package/dist/model/visual-editing.js +1 -0
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/plugins.d.ts +23 -0
- package/dist/runtime/plugins.d.ts.map +1 -0
- package/dist/runtime/plugins.js +1 -0
- package/dist/runtime/query-string.d.ts +11 -0
- package/dist/runtime/query-string.d.ts.map +1 -0
- package/dist/runtime/query-string.js +1 -0
- package/dist/runtime/template-variables.d.ts +6 -15
- package/dist/runtime/template-variables.d.ts.map +1 -1
- package/dist/runtime/template-variables.js +1 -1
- package/dist/runtime/time-range.d.ts +2 -2
- package/dist/runtime/time-range.d.ts.map +1 -1
- package/dist/test/render.d.ts +7 -0
- package/dist/test/render.d.ts.map +1 -0
- package/dist/test/render.js +1 -0
- package/dist/test/setup-tests.d.ts +2 -0
- package/dist/test/setup-tests.d.ts.map +1 -0
- package/dist/test/setup-tests.js +1 -0
- package/dist/utils/cache-keys.d.ts +6 -0
- package/dist/utils/cache-keys.d.ts.map +1 -0
- package/dist/utils/cache-keys.js +1 -0
- package/package.json +6 -6
- package/dist/cjs/components/PluginRegistry/create-plugin.js +0 -118
- package/dist/components/PluginRegistry/create-plugin.d.ts +0 -27
- package/dist/components/PluginRegistry/create-plugin.d.ts.map +0 -1
- package/dist/components/PluginRegistry/create-plugin.js +0 -1
- package/dist/components/PluginRegistry/registry-state.d.ts.map +0 -1
- package/dist/components/PluginRegistry/registry-state.js +0 -1
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.PluginLoader = void 0;
|
|
16
16
|
const react_query_1 = require("react-query");
|
|
17
|
-
const
|
|
17
|
+
const legacy_1 = require("../PluginRegistry/legacy");
|
|
18
18
|
/**
|
|
19
19
|
* Uses the PluginRegistry to load the specified plugin and throws any errors
|
|
20
20
|
* encountered while loading.
|
|
@@ -22,7 +22,7 @@ const PluginRegistry_1 = require("../PluginRegistry");
|
|
|
22
22
|
function PluginLoader(props) {
|
|
23
23
|
const { pluginType, kind } = props;
|
|
24
24
|
// Load the plugin and throw any loading/not found errors
|
|
25
|
-
const { loadPlugin } = (0,
|
|
25
|
+
const { loadPlugin } = (0, legacy_1.usePluginRegistry)();
|
|
26
26
|
const { error } = (0, react_query_1.useQuery)(`PluginLoader:${pluginType}_${kind}`, () => {
|
|
27
27
|
// The enabled option below should prevent this from being run until loadPlugin is available, but check here to
|
|
28
28
|
// make Typescript happy
|
|
@@ -5,7 +5,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
5
5
|
const react_1 = require("react");
|
|
6
6
|
const use_immer_1 = require("use-immer");
|
|
7
7
|
const model_1 = require("../../model");
|
|
8
|
-
const
|
|
8
|
+
const legacy_1 = require("../PluginRegistry/legacy");
|
|
9
9
|
const PluginLoader_1 = require("./PluginLoader");
|
|
10
10
|
/**
|
|
11
11
|
* Plugin dependencies are registered here by children. This takes care of loading
|
|
@@ -14,7 +14,7 @@ const PluginLoader_1 = require("./PluginLoader");
|
|
|
14
14
|
*/
|
|
15
15
|
function PluginLoadingBoundary(props) {
|
|
16
16
|
const { fallback, children } = props;
|
|
17
|
-
const { plugins } = (0,
|
|
17
|
+
const { plugins } = (0, legacy_1.usePluginRegistry)();
|
|
18
18
|
// Keep track of all plugin dependencies registered by child components
|
|
19
19
|
const [dependencies, setDependencies] = (0, use_immer_1.useImmer)(() => {
|
|
20
20
|
const deps = {};
|
|
@@ -72,14 +72,14 @@ exports.usePluginLoadingBoundary = usePluginLoadingBoundary;
|
|
|
72
72
|
* Generic usePlugin that will register the dependency with the nearest LoadingBoundary and get the plugin if it's
|
|
73
73
|
* already loaded
|
|
74
74
|
*/
|
|
75
|
-
function usePlugin(pluginType,
|
|
75
|
+
function usePlugin(pluginType, kind) {
|
|
76
76
|
// Tell the loading boundary about the dependency
|
|
77
77
|
const { registerPluginDependency } = usePluginLoadingBoundary();
|
|
78
78
|
(0, react_1.useEffect)(() => {
|
|
79
|
-
registerPluginDependency(pluginType,
|
|
80
|
-
}, [pluginType,
|
|
79
|
+
registerPluginDependency(pluginType, kind);
|
|
80
|
+
}, [pluginType, kind, registerPluginDependency]);
|
|
81
81
|
// Get the plugin, which could be undefined if it hasn't loaded yet
|
|
82
|
-
const { plugins } = (0,
|
|
83
|
-
return plugins[pluginType][
|
|
82
|
+
const { plugins } = (0, legacy_1.usePluginRegistry)();
|
|
83
|
+
return plugins[pluginType][kind];
|
|
84
84
|
}
|
|
85
85
|
exports.usePlugin = usePlugin;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.PluginRegistry = void 0;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
-
// Copyright
|
|
5
|
+
// Copyright 2022 The Perses Authors
|
|
6
6
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
7
|
// you may not use this file except in compliance with the License.
|
|
8
8
|
// You may obtain a copy of the License at
|
|
@@ -14,44 +14,57 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
14
14
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
15
|
// See the License for the specific language governing permissions and
|
|
16
16
|
// limitations under the License.
|
|
17
|
+
const core_1 = require("@perses-dev/core");
|
|
17
18
|
const react_1 = require("react");
|
|
18
|
-
const
|
|
19
|
-
const
|
|
19
|
+
const cache_keys_1 = require("../../utils/cache-keys");
|
|
20
|
+
const plugin_indexes_1 = require("./plugin-indexes");
|
|
21
|
+
const plugin_registry_model_1 = require("./plugin-registry-model");
|
|
20
22
|
/**
|
|
21
|
-
* PluginRegistryContext provider that keeps track of all available plugins and
|
|
22
|
-
*
|
|
23
|
+
* PluginRegistryContext provider that keeps track of all available plugins and provides an API for getting them or
|
|
24
|
+
* querying the metadata about them.
|
|
23
25
|
*/
|
|
24
26
|
function PluginRegistry(props) {
|
|
25
|
-
const {
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
const { getInstalledPlugins, importPluginModule, children } = props;
|
|
28
|
+
const getPluginIndexes = (0, plugin_indexes_1.usePluginIndexes)(getInstalledPlugins);
|
|
29
|
+
// De-dupe calls to import plugin modules
|
|
30
|
+
const importCache = (0, react_1.useRef)(new Map());
|
|
31
|
+
// Do useEvent here since this accesses the importPluginModule prop and we want a stable reference to it for the
|
|
32
|
+
// callback below
|
|
33
|
+
const loadPluginModule = (0, core_1.useEvent)((resource) => {
|
|
34
|
+
let request = importCache.current.get(resource);
|
|
35
|
+
if (request === undefined) {
|
|
36
|
+
request = importPluginModule(resource);
|
|
37
|
+
importCache.current.set(resource, request);
|
|
38
|
+
// Remove failed requests from the cache so they can potentially be retried
|
|
39
|
+
request.catch(() => importCache.current.delete(resource));
|
|
40
|
+
}
|
|
41
|
+
return request;
|
|
42
|
+
});
|
|
43
|
+
const getPlugin = (0, react_1.useCallback)(async (pluginType, kind) => {
|
|
44
|
+
// Get the indexes of the installed plugins
|
|
45
|
+
const pluginIndexes = await getPluginIndexes();
|
|
46
|
+
// Figure out what module the plugin is in by looking in the index
|
|
47
|
+
const typeAndKindKey = (0, cache_keys_1.getTypeAndKindKey)(pluginType, kind);
|
|
48
|
+
const resource = pluginIndexes.pluginResourcesByTypeAndKind.get(typeAndKindKey);
|
|
35
49
|
if (resource === undefined) {
|
|
36
|
-
throw new Error(`
|
|
50
|
+
throw new Error(`A ${pluginType} plugin for kind '${kind}' is not installed`);
|
|
51
|
+
}
|
|
52
|
+
// Treat the plugin module as a bunch of named exports that have plugins
|
|
53
|
+
const pluginModule = (await loadPluginModule(resource));
|
|
54
|
+
// We currently assume that plugin modules will have named exports that match the kinds they handle
|
|
55
|
+
const plugin = pluginModule[kind];
|
|
56
|
+
if (plugin === undefined) {
|
|
57
|
+
throw new Error(`The ${pluginType} plugin for kind '${kind}' is missing from the ${resource.metadata.name} plugin module`);
|
|
37
58
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
59
|
+
return plugin;
|
|
60
|
+
}, [getPluginIndexes, loadPluginModule]);
|
|
61
|
+
const listPluginMetadata = (0, react_1.useCallback)(async (pluginType) => {
|
|
62
|
+
var _a;
|
|
63
|
+
const pluginIndexes = await getPluginIndexes();
|
|
64
|
+
return (_a = pluginIndexes.pluginMetadataByType.get(pluginType)) !== null && _a !== void 0 ? _a : [];
|
|
65
|
+
}, [getPluginIndexes]);
|
|
66
|
+
// Create the registry's context value and render
|
|
67
|
+
const context = (0, react_1.useMemo)(() => ({ getPlugin, listPluginMetadata }), [getPlugin, listPluginMetadata]);
|
|
68
|
+
return (0, jsx_runtime_1.jsx)(plugin_registry_model_1.PluginRegistryContext.Provider, { value: context, children: children });
|
|
44
69
|
}
|
|
45
70
|
exports.PluginRegistry = PluginRegistry;
|
|
46
|
-
const PluginRegistryContext = (0, react_1.createContext)(undefined);
|
|
47
|
-
/**
|
|
48
|
-
* Gets the PluginRegistryContext, throwing if the provider is missing.
|
|
49
|
-
*/
|
|
50
|
-
function usePluginRegistry() {
|
|
51
|
-
const ctx = (0, react_1.useContext)(PluginRegistryContext);
|
|
52
|
-
if (ctx === undefined) {
|
|
53
|
-
throw new Error('No PluginRegistry context found. Did you forget a Provider?');
|
|
54
|
-
}
|
|
55
|
-
return ctx;
|
|
56
|
-
}
|
|
57
|
-
exports.usePluginRegistry = usePluginRegistry;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
4
|
+
const react_1 = require("@testing-library/react");
|
|
5
|
+
const plugins_1 = require("../../runtime/plugins");
|
|
6
|
+
const render_1 = require("../../test/render");
|
|
7
|
+
const PluginRegistry_1 = require("./PluginRegistry");
|
|
8
|
+
const test_plugins_1 = require("./test-plugins");
|
|
9
|
+
describe('PluginRegistry', () => {
|
|
10
|
+
const renderPluginRegistry = (children) => {
|
|
11
|
+
(0, render_1.renderWithContext)((0, jsx_runtime_1.jsx)(PluginRegistry_1.PluginRegistry, { ...test_plugins_1.testRegistryProps, children: children }));
|
|
12
|
+
};
|
|
13
|
+
it('can load a plugin that exists', async () => {
|
|
14
|
+
renderPluginRegistry((0, jsx_runtime_1.jsx)(PluginConsumer, { pluginType: "Variable", kind: "ErnieVariable" }));
|
|
15
|
+
const hasPlugin = await react_1.screen.findByText('Has plugin: true', undefined, { timeout: 3000 });
|
|
16
|
+
expect(hasPlugin).toBeInTheDocument();
|
|
17
|
+
});
|
|
18
|
+
it('errors when plugin is not installed', async () => {
|
|
19
|
+
// This plugin is not in the test metadata at all
|
|
20
|
+
renderPluginRegistry((0, jsx_runtime_1.jsx)(PluginConsumer, { pluginType: "GraphQuery", kind: "NotInstalled" }));
|
|
21
|
+
const error = await react_1.screen.findByText(/error:/i);
|
|
22
|
+
expect(error).toBeInTheDocument();
|
|
23
|
+
expect(error).toHaveTextContent(/not installed/i);
|
|
24
|
+
});
|
|
25
|
+
it('errors when plugin is missing from the module', async () => {
|
|
26
|
+
// This plugin is in the test metadata, but the code is missing from the module
|
|
27
|
+
renderPluginRegistry((0, jsx_runtime_1.jsx)(PluginConsumer, { pluginType: "Variable", kind: "MissingErnieVariable" }));
|
|
28
|
+
const error = await react_1.screen.findByText(/error:/i);
|
|
29
|
+
expect(error).toBeInTheDocument();
|
|
30
|
+
expect(error).toHaveTextContent(/missing/i);
|
|
31
|
+
});
|
|
32
|
+
it('lists metadata for plugin metadata that exists', async () => {
|
|
33
|
+
// There should be 3 variable plugins across both test modules
|
|
34
|
+
renderPluginRegistry((0, jsx_runtime_1.jsx)(MetadataConsumer, { pluginType: "Variable" }));
|
|
35
|
+
const table = await react_1.screen.findByRole('table');
|
|
36
|
+
expect(table).toBeInTheDocument();
|
|
37
|
+
const rows = react_1.screen.getAllByRole('row');
|
|
38
|
+
expect(rows).toHaveLength(3);
|
|
39
|
+
});
|
|
40
|
+
it('lists metadata for plugin types with no plugins available', async () => {
|
|
41
|
+
// There are no GraphQuery plugins in any of the test modules
|
|
42
|
+
renderPluginRegistry((0, jsx_runtime_1.jsx)(MetadataConsumer, { pluginType: "GraphQuery" }));
|
|
43
|
+
const table = await react_1.screen.findByRole('table');
|
|
44
|
+
expect(table).toBeInTheDocument();
|
|
45
|
+
const rows = react_1.screen.queryAllByRole('row');
|
|
46
|
+
expect(rows).toHaveLength(0);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
// A helper component for testing the PluginRegistry by calling usePlugin to load a plugin
|
|
50
|
+
const PluginConsumer = (props) => {
|
|
51
|
+
const { plugin, isLoading, error } = (0, plugins_1.usePlugin)(props.pluginType, props.kind);
|
|
52
|
+
if (error) {
|
|
53
|
+
return (0, jsx_runtime_1.jsxs)("div", { children: ["Error: ", error.message] });
|
|
54
|
+
}
|
|
55
|
+
if (isLoading) {
|
|
56
|
+
return (0, jsx_runtime_1.jsx)("div", { children: "Loading" });
|
|
57
|
+
}
|
|
58
|
+
return (0, jsx_runtime_1.jsxs)("div", { children: ["Has plugin: ", (plugin !== undefined).toString()] });
|
|
59
|
+
};
|
|
60
|
+
// A helper component for testing the PluginRegistry metadata APIs by calling useListPluginMetadata
|
|
61
|
+
const MetadataConsumer = (props) => {
|
|
62
|
+
const { pluginMetadata, isLoading, error } = (0, plugins_1.useListPluginMetadata)(props.pluginType);
|
|
63
|
+
if (error) {
|
|
64
|
+
return (0, jsx_runtime_1.jsxs)("div", { children: ["Error: ", error.message] });
|
|
65
|
+
}
|
|
66
|
+
if (isLoading) {
|
|
67
|
+
return (0, jsx_runtime_1.jsx)("div", { children: "Loading" });
|
|
68
|
+
}
|
|
69
|
+
if (pluginMetadata === undefined) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
return ((0, jsx_runtime_1.jsx)("table", { children: (0, jsx_runtime_1.jsx)("tbody", { children: pluginMetadata.map((item) => ((0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("td", { children: item.pluginType }), (0, jsx_runtime_1.jsx)("td", { children: item.kind }), (0, jsx_runtime_1.jsx)("td", { children: item.display.name }), (0, jsx_runtime_1.jsx)("td", { children: item.display.description })] }, item.kind))) }) }));
|
|
73
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// Copyright
|
|
2
|
+
// Copyright 2022 The Perses Authors
|
|
3
3
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
// you may not use this file except in compliance with the License.
|
|
5
5
|
// You may obtain a copy of the License at
|
|
@@ -26,4 +26,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
26
26
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
__exportStar(require("./plugin-registry-model"), exports);
|
|
29
30
|
__exportStar(require("./PluginRegistry"), exports);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.usePluginRegistry = exports.PluginRegistry = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
// Copyright 2021 The Perses Authors
|
|
6
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
// you may not use this file except in compliance with the License.
|
|
8
|
+
// You may obtain a copy of the License at
|
|
9
|
+
//
|
|
10
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
//
|
|
12
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
// See the License for the specific language governing permissions and
|
|
16
|
+
// limitations under the License.
|
|
17
|
+
const react_1 = require("react");
|
|
18
|
+
const react_query_1 = require("react-query");
|
|
19
|
+
const registry_state_1 = require("./registry-state");
|
|
20
|
+
/**
|
|
21
|
+
* PluginRegistryContext provider that keeps track of all available plugins and
|
|
22
|
+
* their implementations once they've been loaded.
|
|
23
|
+
*/
|
|
24
|
+
function PluginRegistry(props) {
|
|
25
|
+
const { children, getInstalledPlugins, importPluginModule } = props;
|
|
26
|
+
const installedPlugins = (0, react_query_1.useQuery)('installed-plugins', getInstalledPlugins);
|
|
27
|
+
const { loadablePlugins, plugins, register } = (0, registry_state_1.useRegistryState)(installedPlugins.data);
|
|
28
|
+
const loadPlugin = (0, react_1.useCallback)(async (pluginType, kind) => {
|
|
29
|
+
// Is it already loaded?
|
|
30
|
+
const plugin = plugins[pluginType][kind];
|
|
31
|
+
if (plugin !== undefined)
|
|
32
|
+
return;
|
|
33
|
+
// Is it a valid plugin we know about?
|
|
34
|
+
const resource = loadablePlugins[pluginType][kind];
|
|
35
|
+
if (resource === undefined) {
|
|
36
|
+
throw new Error(`No ${pluginType} plugin is available for kind ${kind}`);
|
|
37
|
+
}
|
|
38
|
+
// Load and register the resource
|
|
39
|
+
const pluginModule = await importPluginModule(resource);
|
|
40
|
+
register(resource, pluginModule);
|
|
41
|
+
}, [plugins, loadablePlugins, importPluginModule, register]);
|
|
42
|
+
const registry = (0, react_1.useMemo)(() => ({ plugins, loadPlugin: installedPlugins.isLoading ? undefined : loadPlugin }), [installedPlugins.isLoading, plugins, loadPlugin]);
|
|
43
|
+
return (0, jsx_runtime_1.jsx)(PluginRegistryContext.Provider, { value: registry, children: children });
|
|
44
|
+
}
|
|
45
|
+
exports.PluginRegistry = PluginRegistry;
|
|
46
|
+
const PluginRegistryContext = (0, react_1.createContext)(undefined);
|
|
47
|
+
/**
|
|
48
|
+
* Gets the PluginRegistryContext, throwing if the provider is missing.
|
|
49
|
+
*/
|
|
50
|
+
function usePluginRegistry() {
|
|
51
|
+
const ctx = (0, react_1.useContext)(PluginRegistryContext);
|
|
52
|
+
if (ctx === undefined) {
|
|
53
|
+
throw new Error('No PluginRegistry context found. Did you forget a Provider?');
|
|
54
|
+
}
|
|
55
|
+
return ctx;
|
|
56
|
+
}
|
|
57
|
+
exports.usePluginRegistry = usePluginRegistry;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright 2021 The Perses Authors
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
17
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
18
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
19
|
+
}
|
|
20
|
+
Object.defineProperty(o, k2, desc);
|
|
21
|
+
}) : (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
o[k2] = m[k];
|
|
24
|
+
}));
|
|
25
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
26
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
__exportStar(require("./PluginRegistry"), exports);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// Copyright
|
|
2
|
+
// Copyright 2022 The Perses Authors
|
|
3
3
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
// you may not use this file except in compliance with the License.
|
|
5
5
|
// You may obtain a copy of the License at
|
|
@@ -15,8 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
exports.useRegistryState = void 0;
|
|
16
16
|
const react_1 = require("react");
|
|
17
17
|
const use_immer_1 = require("use-immer");
|
|
18
|
-
const model_1 = require("
|
|
19
|
-
const create_plugin_1 = require("./create-plugin");
|
|
18
|
+
const model_1 = require("../../../model");
|
|
20
19
|
/**
|
|
21
20
|
* Hook for setting up plugin registry state. Returns the state, plus a function
|
|
22
21
|
* for registering plugins with that state.
|
|
@@ -32,12 +31,9 @@ function useRegistryState(installedPlugins) {
|
|
|
32
31
|
// If no plugins installed or waiting on that data, nothing else to do
|
|
33
32
|
if (installedPlugins === undefined)
|
|
34
33
|
return loadableProps;
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
const pluginType = supportedKinds[kind];
|
|
39
|
-
if (pluginType === undefined)
|
|
40
|
-
continue;
|
|
34
|
+
for (const resource of installedPlugins) {
|
|
35
|
+
for (const plugin of resource.spec.plugins) {
|
|
36
|
+
const { pluginType, kind } = plugin;
|
|
41
37
|
const map = loadableProps[pluginType];
|
|
42
38
|
if (map[kind] !== undefined) {
|
|
43
39
|
console.warn(`Got multiple ${pluginType} plugin definitions for kind ${kind}`);
|
|
@@ -45,9 +41,6 @@ function useRegistryState(installedPlugins) {
|
|
|
45
41
|
}
|
|
46
42
|
map[kind] = resource;
|
|
47
43
|
}
|
|
48
|
-
};
|
|
49
|
-
for (const resource of installedPlugins) {
|
|
50
|
-
addToLoadable(resource);
|
|
51
44
|
}
|
|
52
45
|
return loadableProps;
|
|
53
46
|
}, [installedPlugins]);
|
|
@@ -58,40 +51,47 @@ function useRegistryState(installedPlugins) {
|
|
|
58
51
|
}
|
|
59
52
|
return loadedPlugins;
|
|
60
53
|
});
|
|
61
|
-
// Create the register callback to pass to the module's setup function
|
|
62
|
-
const registerPlugin = (0, react_1.useCallback)((config) => {
|
|
63
|
-
switch (config.pluginType) {
|
|
64
|
-
case 'Variable':
|
|
65
|
-
setPlugins((draft) => {
|
|
66
|
-
draft.Variable[config.kind] = (0, create_plugin_1.createVariablePlugin)(config);
|
|
67
|
-
});
|
|
68
|
-
return;
|
|
69
|
-
case 'Panel':
|
|
70
|
-
setPlugins((draft) => {
|
|
71
|
-
draft.Panel[config.kind] = (0, create_plugin_1.createPanelPlugin)(config);
|
|
72
|
-
});
|
|
73
|
-
return;
|
|
74
|
-
case 'GraphQuery':
|
|
75
|
-
setPlugins((draft) => {
|
|
76
|
-
draft.GraphQuery[config.kind] = (0, create_plugin_1.createGraphQueryPlugin)(config);
|
|
77
|
-
});
|
|
78
|
-
return;
|
|
79
|
-
default:
|
|
80
|
-
const exhaustive = config;
|
|
81
|
-
throw new Error(`Unhandled plugin config: ${exhaustive}`);
|
|
82
|
-
}
|
|
83
|
-
}, [setPlugins]);
|
|
84
54
|
const registeredModules = (0, react_1.useRef)(new Set());
|
|
85
|
-
const register = (0, react_1.useCallback)((pluginModule) => {
|
|
55
|
+
const register = (0, react_1.useCallback)((resource, pluginModule) => {
|
|
86
56
|
// De-dupe register calls in case multiple plugin loading boundaries
|
|
87
57
|
// are waiting for the same module in parallel
|
|
88
58
|
if (registeredModules.current.has(pluginModule)) {
|
|
89
59
|
return;
|
|
90
60
|
}
|
|
91
|
-
//
|
|
92
|
-
pluginModule
|
|
61
|
+
// Treat plugin module as JS module with named exports that are each a Plugin
|
|
62
|
+
const plugins = pluginModule;
|
|
63
|
+
setPlugins((draft) => {
|
|
64
|
+
// Look for all the plugins specified in the metadata
|
|
65
|
+
for (const pluginMetadata of resource.spec.plugins) {
|
|
66
|
+
// Assume that plugins will be exported under the same named export as the kind they handle
|
|
67
|
+
// TODO: Do we need to allow for different named exports and an option in the metadata to tell us the name?
|
|
68
|
+
const { pluginType, kind } = pluginMetadata;
|
|
69
|
+
const plugin = plugins[kind];
|
|
70
|
+
if (plugin === undefined) {
|
|
71
|
+
// TODO: How to handle missing plugins?
|
|
72
|
+
console.warn(`Could not find ${pluginType} plugin for kind '${kind}' in ${resource.metadata.name}`);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
// Add to registry state
|
|
76
|
+
switch (pluginType) {
|
|
77
|
+
case 'Variable':
|
|
78
|
+
draft.Variable[kind] = plugin;
|
|
79
|
+
break;
|
|
80
|
+
case 'Panel':
|
|
81
|
+
draft.Panel[kind] = plugin;
|
|
82
|
+
break;
|
|
83
|
+
case 'GraphQuery':
|
|
84
|
+
draft.GraphQuery[kind] = plugin;
|
|
85
|
+
break;
|
|
86
|
+
default:
|
|
87
|
+
const exhaustive = pluginType;
|
|
88
|
+
throw new Error(`Unhandled plugin config: ${exhaustive}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
// Remember this module has been registered
|
|
93
93
|
registeredModules.current.add(pluginModule);
|
|
94
|
-
}, [
|
|
94
|
+
}, [setPlugins]);
|
|
95
95
|
return { loadablePlugins, plugins, register };
|
|
96
96
|
}
|
|
97
97
|
exports.useRegistryState = useRegistryState;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright 2022 The Perses Authors
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.usePluginIndexes = void 0;
|
|
16
|
+
const core_1 = require("@perses-dev/core");
|
|
17
|
+
const react_1 = require("react");
|
|
18
|
+
const cache_keys_1 = require("../../utils/cache-keys");
|
|
19
|
+
/**
|
|
20
|
+
* Returns an async callback for getting indexes of the installed plugin data.
|
|
21
|
+
*/
|
|
22
|
+
function usePluginIndexes(getInstalledPlugins) {
|
|
23
|
+
// Creates indexes from the installed plugins data (does useEvent because this accesses the getInstalledPlugins prop
|
|
24
|
+
// and we want a stable reference for the callback below)
|
|
25
|
+
const createPluginIndexes = (0, core_1.useEvent)(async () => {
|
|
26
|
+
const installedPlugins = await getInstalledPlugins();
|
|
27
|
+
// Create the two indexes from the installed plugins
|
|
28
|
+
const pluginResourcesByTypeAndKind = new Map();
|
|
29
|
+
const pluginMetadataByType = new Map();
|
|
30
|
+
for (const resource of installedPlugins) {
|
|
31
|
+
for (const pluginMetadata of resource.spec.plugins) {
|
|
32
|
+
const { pluginType, kind } = pluginMetadata;
|
|
33
|
+
// Index the plugin by type and kind to point at the module that contains it
|
|
34
|
+
const key = (0, cache_keys_1.getTypeAndKindKey)(pluginType, kind);
|
|
35
|
+
if (pluginResourcesByTypeAndKind.has(key)) {
|
|
36
|
+
console.warn(`Got more than one ${pluginType} plugin for kind ${kind}`);
|
|
37
|
+
}
|
|
38
|
+
pluginResourcesByTypeAndKind.set(key, resource);
|
|
39
|
+
// Index the metadata by plugin type
|
|
40
|
+
let list = pluginMetadataByType.get(pluginType);
|
|
41
|
+
if (list === undefined) {
|
|
42
|
+
list = [];
|
|
43
|
+
pluginMetadataByType.set(pluginType, list);
|
|
44
|
+
}
|
|
45
|
+
list.push(pluginMetadata);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
pluginResourcesByTypeAndKind,
|
|
50
|
+
pluginMetadataByType,
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
// De-dupe creating plugin indexes (i.e. requests to get installed plugins)
|
|
54
|
+
const pluginIndexesCache = (0, react_1.useRef)(undefined);
|
|
55
|
+
const getPluginIndexes = (0, react_1.useCallback)(() => {
|
|
56
|
+
let request = pluginIndexesCache.current;
|
|
57
|
+
if (request === undefined) {
|
|
58
|
+
request = createPluginIndexes();
|
|
59
|
+
pluginIndexesCache.current = request;
|
|
60
|
+
// Remove failed requests from the cache so they can potentially be retried
|
|
61
|
+
request.catch(() => pluginIndexesCache.current === undefined);
|
|
62
|
+
}
|
|
63
|
+
return request;
|
|
64
|
+
}, [createPluginIndexes]);
|
|
65
|
+
return getPluginIndexes;
|
|
66
|
+
}
|
|
67
|
+
exports.usePluginIndexes = usePluginIndexes;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright 2022 The Perses Authors
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.usePluginRegistry = exports.PluginRegistryContext = void 0;
|
|
16
|
+
const react_1 = require("react");
|
|
17
|
+
exports.PluginRegistryContext = (0, react_1.createContext)(undefined);
|
|
18
|
+
function usePluginRegistry() {
|
|
19
|
+
const ctx = (0, react_1.useContext)(exports.PluginRegistryContext);
|
|
20
|
+
if (ctx === undefined) {
|
|
21
|
+
throw new Error('PluginRegistryContext not found. Did you forget a provider?');
|
|
22
|
+
}
|
|
23
|
+
return ctx;
|
|
24
|
+
}
|
|
25
|
+
exports.usePluginRegistry = usePluginRegistry;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright 2022 The Perses Authors
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.BertPanel2 = exports.BertPanel1 = void 0;
|
|
16
|
+
// Dummy plugins to test loading
|
|
17
|
+
exports.BertPanel1 = {
|
|
18
|
+
PanelComponent: () => null,
|
|
19
|
+
OptionsEditorComponent: () => null,
|
|
20
|
+
createInitialOptions: () => ({}),
|
|
21
|
+
};
|
|
22
|
+
exports.BertPanel2 = {
|
|
23
|
+
PanelComponent: () => null,
|
|
24
|
+
OptionsEditorComponent: () => null,
|
|
25
|
+
createInitialOptions: () => ({}),
|
|
26
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "PluginModule",
|
|
3
|
+
"metadata": { "name": "Bert" },
|
|
4
|
+
"spec": {
|
|
5
|
+
"plugins": [
|
|
6
|
+
{
|
|
7
|
+
"pluginType": "Panel",
|
|
8
|
+
"kind": "BertPanel1",
|
|
9
|
+
"display": {
|
|
10
|
+
"name": "Bert Panel 1",
|
|
11
|
+
"description": ""
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"pluginType": "Panel",
|
|
16
|
+
"kind": "BertPanel2",
|
|
17
|
+
"display": {
|
|
18
|
+
"name": "Bert Panel 2",
|
|
19
|
+
"description": ""
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"pluginType": "Variable",
|
|
24
|
+
"kind": "BertVariable",
|
|
25
|
+
"display": {
|
|
26
|
+
"name": "Bert Variable",
|
|
27
|
+
"description": ""
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright 2022 The Perses Authors
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.ErnieVariable = void 0;
|
|
16
|
+
const data = [
|
|
17
|
+
{ label: 'Grover', value: 'Grover' },
|
|
18
|
+
{ label: 'Snuffleupagus', value: 'Snuffleupagus' },
|
|
19
|
+
];
|
|
20
|
+
// Dummy plugin to test loading
|
|
21
|
+
exports.ErnieVariable = {
|
|
22
|
+
useVariableOptions: () => ({ loading: false, error: undefined, data }),
|
|
23
|
+
};
|