{{ name }}
) - ); - console.log("{{ name }} extension activated"); -}; - -export const deactivate = () => { - console.log("{{ name }} extension deactivated"); -}; +views.registerView( + { id: "{{ id }}.example", name: "{{ display_name }}" }, + "sqllab.panels", + () =>{{ display_name }}
, +); diff --git a/superset-extensions-cli/src/superset_extensions_cli/templates/frontend/webpack.config.js.j2 b/superset-extensions-cli/src/superset_extensions_cli/templates/frontend/webpack.config.js.j2 index b3bf5139899..93fc61459fb 100644 --- a/superset-extensions-cli/src/superset_extensions_cli/templates/frontend/webpack.config.js.j2 +++ b/superset-extensions-cli/src/superset_extensions_cli/templates/frontend/webpack.config.js.j2 @@ -1,6 +1,7 @@ const path = require("path"); const { ModuleFederationPlugin } = require("webpack").container; const packageConfig = require("./package"); +const extensionConfig = require("../extension.json"); module.exports = (env, argv) => { const isProd = argv.mode === "production"; @@ -19,7 +20,7 @@ module.exports = (env, argv) => { filename: isProd ? undefined : "[name].[contenthash].js", chunkFilename: "[name].[contenthash].js", path: path.resolve(__dirname, "dist"), - publicPath: `/api/v1/extensions/{{ publisher }}/{{ name }}/`, + publicPath: `/api/v1/extensions/${extensionConfig.publisher}/${extensionConfig.name}/`, }, resolve: { extensions: [".ts", ".tsx", ".js", ".jsx"], diff --git a/superset-extensions-cli/tests/test_cli_build.py b/superset-extensions-cli/tests/test_cli_build.py index 94116e76b5a..2e95ff4f3c1 100644 --- a/superset-extensions-cli/tests/test_cli_build.py +++ b/superset-extensions-cli/tests/test_cli_build.py @@ -59,20 +59,6 @@ def extension_with_build_structure(): "permissions": [], } - if include_frontend: - extension_json["frontend"] = { - "contributions": { - "commands": [], - "views": {}, - "menus": {}, - "editors": [], - }, - "moduleFederation": { - "exposes": ["./index"], - "name": "testOrg_testExtension", - }, - } - if include_backend: extension_json["backend"] = { "entryPoints": [ @@ -249,18 +235,6 @@ def test_build_manifest_creates_correct_manifest_structure(isolated_filesystem): "version": "1.0.0", "permissions": ["read_data"], "dependencies": ["some_dep"], - "frontend": { - "contributions": { - "commands": [{"id": "test_command", "title": "Test"}], - "views": {}, - "menus": {}, - "editors": [], - }, - "moduleFederation": { - "exposes": ["./index"], - "name": "testOrg_testExtension", - }, - }, "backend": { "entryPoints": ["superset_extensions.test_org.test_extension.entrypoint"] }, @@ -281,11 +255,8 @@ def test_build_manifest_creates_correct_manifest_structure(isolated_filesystem): # Verify frontend section assert manifest.frontend is not None - assert manifest.frontend.contributions.commands == [ - {"id": "test_command", "title": "Test"} - ] - assert manifest.frontend.moduleFederation.exposes == ["./index"] assert manifest.frontend.remoteEntry == "remoteEntry.abc123.js" + assert manifest.frontend.moduleFederationName == "testOrg_testExtension" # Verify backend section assert manifest.backend is not None diff --git a/superset-extensions-cli/tests/test_cli_init.py b/superset-extensions-cli/tests/test_cli_init.py index 7d9d5f284bd..ce9fa963b6a 100644 --- a/superset-extensions-cli/tests/test_cli_init.py +++ b/superset-extensions-cli/tests/test_cli_init.py @@ -223,21 +223,8 @@ def test_extension_json_content_is_correct( # Load and verify more complex nested structures content = load_json_file(extension_json_path) - # Verify frontend section exists and has correct structure - assert "frontend" in content - frontend = content["frontend"] - assert "contributions" in frontend - assert "moduleFederation" in frontend - assert frontend["contributions"] == { - "commands": [], - "views": {}, - "menus": {}, - "editors": [], - } - assert frontend["moduleFederation"] == { - "exposes": ["./index"], - "name": "testOrg_testExtension", - } + # Verify frontend section is not present (contributions are code-first) + assert "frontend" not in content # Verify backend section exists and has correct structure assert "backend" in content diff --git a/superset-extensions-cli/tests/test_templates.py b/superset-extensions-cli/tests/test_templates.py index 29858e494a3..80dee4fae09 100644 --- a/superset-extensions-cli/tests/test_templates.py +++ b/superset-extensions-cli/tests/test_templates.py @@ -78,21 +78,8 @@ def test_extension_json_template_renders_with_both_frontend_and_backend( assert parsed["license"] == "Apache-2.0" assert parsed["permissions"] == [] - # Verify frontend section exists - assert "frontend" in parsed - frontend = parsed["frontend"] - assert "contributions" in frontend - assert "moduleFederation" in frontend - assert frontend["contributions"] == { - "commands": [], - "views": {}, - "menus": {}, - "editors": [], - } - assert frontend["moduleFederation"] == { - "exposes": ["./index"], - "name": "testOrg_testExtension", - } + # Verify frontend section is not present (contributions are code-first) + assert "frontend" not in parsed # Verify backend section exists assert "backend" in parsed @@ -109,7 +96,7 @@ def test_extension_json_template_renders_with_both_frontend_and_backend( @pytest.mark.parametrize( "include_frontend,include_backend,expected_sections", [ - (True, False, ["frontend"]), + (True, False, []), (False, True, ["backend"]), (False, False, []), ], diff --git a/superset-frontend/packages/superset-core/src/api/commands.ts b/superset-frontend/packages/superset-core/src/api/commands.ts index 523f55238aa..f00aa3a4812 100644 --- a/superset-frontend/packages/superset-core/src/api/commands.ts +++ b/superset-frontend/packages/superset-core/src/api/commands.ts @@ -28,19 +28,40 @@ import { Disposable } from './core'; /** - * Registers a command that can be invoked via a keyboard shortcut, - * a menu item, an action, or directly. + * Describes a command that can be contributed to the application. + */ +export interface Command { + /** The unique identifier for the command. */ + id: string; + /** The display title of the command. */ + title: string; + /** The icon associated with the command. */ + icon?: string; + /** A description of what the command does. */ + description: string; +} + +/** + * Registers a command with its handler as a module-level side effect. * * Registering a command with an existing command identifier twice - * will cause an error. + * will cause a warning and overwrite the existing command. * - * @param command A unique identifier for the command. + * @param command The command descriptor. * @param callback A command handler function. * @param thisArg The `this` context used when invoking the handler function. * @returns Disposable which unregisters this command on disposal. + * + * @example + * ```typescript + * commands.registerCommand( + * { id: 'sqllab_parquet.export', title: 'Export to Parquet', icon: 'FileOutlined', description: 'Export results to Parquet format' }, + * async () => { exportToParquet(); }, + * ); + * ``` */ export declare function registerCommand( - command: string, + command: Command, callback: (...args: any[]) => any, thisArg?: any, ): Disposable; @@ -59,12 +80,16 @@ export declare function executeCommandThis is a custom component from my extension.
- *