rusackas commented on code in PR #36298: URL: https://github.com/apache/superset/pull/36298#discussion_r2566427870
########## docs/developer_portal/extensions/development.md: ########## @@ -0,0 +1,245 @@ +--- +title: Development +sidebar_position: 5 +--- + +<!-- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +--> + +# Development + +This guide covers everything you need to know about developing extensions for Superset, from project structure to development workflow. + +## Project Structure + +The `apache-superset-extensions-cli` package provides a command-line interface (CLI) that streamlines the extension development workflow. It offers the following commands: + +``` +superset-extensions init: Generates the initial folder structure and scaffolds a new extension project. + +superset-extensions build: Builds extension assets. + +superset-extensions bundle: Packages the extension into a .supx file. + +superset-extensions dev: Automatically rebuilds the extension as files change. +``` + +When creating a new extension with `superset-extensions init <extension-name>`, the CLI generates a standardized folder structure: + +``` +dataset_references/ +├── extension.json +├── frontend/ +│ ├── src/ +│ ├── webpack.config.js +│ ├── tsconfig.json +│ └── package.json +├── backend/ +│ ├── src/ +│ └── dataset_references/ +│ ├── tests/ +│ ├── pyproject.toml +│ └── requirements.txt +├── dist/ +│ ├── manifest.json +│ ├── frontend +│ └── dist/ +│ ├── remoteEntry.d7a9225d042e4ccb6354.js +│ └── 900.038b20cdff6d49cfa8d9.js +│ └── backend +│ └── dataset_references/ +│ ├── __init__.py +│ ├── api.py +│ └── entrypoint.py +├── dataset_references-1.0.0.supx +└── README.md +``` + +The `extension.json` file serves as the declared metadata for the extension, containing the extension's name, version, author, description, and a list of capabilities. This file is essential for the host application to understand how to load and manage the extension. + +The `frontend` directory contains the source code for the frontend components of the extension, including React components, styles, and assets. The `webpack.config.js` file is used to configure Webpack for building the frontend code, while the `tsconfig.json` file defines the TypeScript configuration for the project. The `package.json` file specifies the dependencies and scripts for building and testing the frontend code. + +The `backend` directory contains the source code for the backend components of the extension, including Python modules, tests, and configuration files. The `pyproject.toml` file is used to define the Python package and its dependencies, while the r`equirements.txt` file lists the required Python packages for the extension. The `src` folder contains the functional backend source files, `tests` directory contains unit tests for the backend code, ensuring that the extension behaves as expected and meets the defined requirements. + +The `dist` directory is built when running the `build` or `dev` command, and contains the files that will be included in the bundle. The `manifest.json` file contains critical metadata about the extension, including the majority of the contents of the `extension.json` file, but also other build-time information, like the name of the built Webpack Module Federation remote entry file. The files in the `dist` directory will be zipped into the final `.supx` file. Although this file is technically a zip archive, the `.supx` extension makes it clear that it is a Superset extension package and follows a specific file layout. This packaged file can be distributed and installed in Superset instances. + +The `README.md` file provides documentation and instructions for using the extension, including how to install, configure, and use its functionality. + +## Extension Metadata + +The `extension.json` file contains all metadata necessary for the host application to understand and manage the extension: + +``` json +{ + "name": "dataset_references", + "version": "1.0.0", + "frontend": { + "contributions": { + "views": { + "sqllab.panels": [ + { + "id": "dataset_references.main", + "name": "Dataset references" + } + ] + } + }, + "moduleFederation": { + "exposes": ["./index"] + } + }, + "backend": { + "entryPoints": ["dataset_references.entrypoint"], + "files": ["backend/src/dataset_references/**/*.py"] + }, +} +``` + +The `contributions` section declares how the extension extends Superset's functionality through views, commands, menus, and other contribution types. The `backend` section specifies entry points and files to include in the bundle. + +## Interacting with the Host + +Extensions interact with Superset through well-defined, versioned APIs provided by the `@apache-superset/core` (frontend) and `apache-superset-core` (backend) packages. These APIs are designed to be stable, discoverable, and consistent for both built-in and external extensions. + +**Note**: The `superset_core.api` module provides abstract classes that are replaced with concrete implementations via dependency injection when Superset initializes. This allows extensions to use the same interfaces as the host application. + +### Frontend APIs + +The frontend extension APIs (via `@apache-superset/core`) are organized into logical namespaces such as `authentication`, `commands`, `extensions`, `sqlLab`, and others. Each namespace groups related functionality, making it easy for extension authors to discover and use the APIs relevant to their needs. For example, the `sqlLab` namespace provides events and methods specific to SQL Lab, allowing extensions to react to user actions and interact with the SQL Lab environment: + +``` typescript +export const getCurrentTab: () => Tab | undefined; + +export const getDatabases: () => Database[]; + +export const getTabs: () => Tab[]; + +export const onDidChangeEditorContent: Event<string>; + +export const onDidClosePanel: Event<Panel>; + +export const onDidChangeActivePanel: Event<Panel>; + +export const onDidChangeTabTitle: Event<string>; + +export const onDidQueryRun: Event<Editor>; + +export const onDidQueryStop: Event<Editor>; +``` + +The following code demonstrates more examples of the existing frontend APIs: + +``` typescript +import { core, commands, sqlLab, authentication, Button } from '@apache-superset/core'; +import MyPanel from './MyPanel'; + +export function activate(context) { + // Register a new panel (view) in SQL Lab and use shared UI components in your extension's React code + const panelDisposable = core.registerView('my_extension.panel', <MyPanel><Button/></MyPanel>); + + // Register a custom command + const commandDisposable = commands.registerCommand('my_extension.copy_query', { + title: 'Copy Query', + execute: () => { + // Command logic here + }, + }); + + // Listen for query run events in SQL Lab + const eventDisposable = sqlLab.onDidQueryRun(editor => { + // Handle query execution event + }); + + // Access a CSRF token for secure API requests + authentication.getCSRFToken().then(token => { + // Use token as needed + }); + + // Add all disposables for automatic cleanup on deactivation + context.subscriptions.push(panelDisposable, commandDisposable, eventDisposable); +} +``` + +### Backend APIs + +Backend APIs (via `apache-superset-core`) follow a similar pattern, providing access to Superset's models, sessions, and query capabilities. Extensions can register REST API endpoints, access the metadata database, and interact with Superset's core functionality. + +Extension endpoints are registered under a dedicated `/extensions` namespace to avoid conflicting with built-in endpoints and also because they don't share the same version constraints. By grouping all extension endpoints under `/extensions`, Superset establishes a clear boundary between core and extension functionality, making it easier to manage, document, and secure both types of APIs. + +``` python +from superset_core.api.models import Database, get_session +from superset_core.api.daos import DatabaseDAO +from superset_core.api.rest_api import add_extension_api +from .api import DatasetReferencesAPI + +# Register a new extension REST API +add_extension_api(DatasetReferencesAPI) + +# Fetch Superset entities via the DAO to apply base filters that filter out entities +# that the user doesn't have access to +databases = DatabaseDAO.find_all() + +# ..or apply simple filters on top of base filters +databases = DatabaseDAO.filter_by(uuid=database.uuid) +if not databases: + raise Exception("Database not found") + +return databases[0] + +# Perform complex queries using SQLAlchemy Query, also filtering out +# inaccessible entities +session = get_session() +databases_query = session.query(Database).filter( + Database.database_name.ilike("%abc%") +) +return DatabaseDAO.query(databases_query) + +# Bypass security model for highly custom use cases Review Comment: > # Bypass security model That sounds scary... would we want to document that? Not sure what security issues the `.all()` might really expose. Is that something extension reviewers need to watch out for, or should we CI/lint some means to warn about any security bypasses? -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
