Skip to main content

开发指南

为文档做贡献

最新的文档和教程可在 https://superset.apache.org/ 找到。

文档站点使用 Docusaurus 2 构建,这是一个现代静态网站生成器,其源代码位于 ./docs

本地开发

要为文档站点设置带有热重载的本地开发环境:

cd docs
yarn install # Installs NPM dependencies
yarn start # Starts development server at http://localhost:3000

构建

要创建并提供文档站点的生产构建:

yarn build
yarn serve

部署

master 分支的提交会触发文档站点的重新构建和重新部署。 提交修改文档的拉取请求时,请使用 docs: 前缀。

创建可视化插件

Superset 中的可视化是在 JavaScript 或 TypeScript 中实现的。 Superset 预装了多种可视化类型(以下简称 “viz 插件”), 可以在 superset-frontend/plugins 目录下找到。 viz 插件通过 superset-frontend/src/visualizations/presets/MainPreset.js 添加到应用程序中。Superset 项目总是乐于审查高质量 viz 插件的新提案。 然而,对于高度定制的 viz 类型,建议维护 Superset 的分支,并手动添加自定义构建的 viz 插件。

注意: 关于创建和部署自定义可视化插件的额外社区生成资源可以 在 Superset Wiki 上找到。

先决条件

为了创建一个新的 viz 插件,你需要以下内容:

  • 运行 MacOS 或 Linux(Windows 不受官方支持,但可能可行)
  • Node.js 16
  • npm 7 或 8

推荐具备 React 和 npm/Node 系统的基本熟悉程度。

创建一个简单的 Hello World viz 插件

开始之前,你需要 Superset Yeoman Generator。 推荐使用与你使用的 Superset 版本配套的模板版本。 可以通过以下步骤安装:

npm i -g yo
cd superset-frontend/packages/generator-superset
npm i
npm link

之后你可以继续创建 viz 插件。为 viz 插件创建一个新目录, 前缀为 superset-plugin-chart,然后运行 Yeoman generator:

mkdir /tmp/superset-plugin-chart-hello-world
cd /tmp/superset-plugin-chart-hello-world

初始化 viz 插件:

yo @superset-ui/superset

之后 generator 会询问一些问题(默认选项应该没问题):

$ yo @superset-ui/superset
_-----_ ╭──────────────────────────╮
| | │ Welcome to the │
|--(o)--| │ generator-superset │
`---------´ │ generator! │
( _´U`_ ) ╰──────────────────────────╯
/___A___\ /
| ~ |
__'.___.'__
´ ` |° ´ Y `
? Package name: superset-plugin-chart-hello-world
? Description: Hello World
? What type of chart would you like? Time-series chart
create package.json
create .gitignore
create babel.config.js
create jest.config.js
create README.md
create tsconfig.json
create src/index.ts
create src/plugin/buildQuery.ts
create src/plugin/controlPanel.ts
create src/plugin/index.ts
create src/plugin/transformProps.ts
create src/types.ts
create src/SupersetPluginChartHelloWorld.tsx
create test/index.test.ts
create test/__mocks__/mockExportString.js
create test/plugin/buildQuery.test.ts
create test/plugin/transformProps.test.ts
create types/external.d.ts
create src/images/thumbnail.png

为了构建 viz 插件,运行以下命令:

npm i --force
npm run build

或者,为了在开发模式下运行 viz 插件(=每当有更改时自动重建),启动 dev 服务器:

npm run dev

为了将包添加到 Superset 中,前往你 Superset 源码文件夹中的 superset-frontend 子目录,运行

npm i -S /tmp/superset-plugin-chart-hello-world

如果你将包发布到 npm,你当然也可以直接从那里安装。 之后编辑 superset-frontend/src/visualizations/presets/MainPreset.js 文件, 并进行以下更改:

import { SupersetPluginChartHelloWorld } from 'superset-plugin-chart-hello-world';

导入 viz 插件,稍后将以下内容添加到传递给 plugins 属性的数组中:

new SupersetPluginChartHelloWorld().configure({ key: 'ext-hello-world' }),

之后,当你运行 Superset 时,viz 插件应该会出现,例如开发服务器:

npm run dev-server

测试

Python 测试

所有的 Python 测试都在 tox 中执行, 这是一个标准化的测试框架。 所有 Python 测试都可以通过 tox 的任何 环境 运行,例如,

tox -e <environment>

例如,

tox -e py38

或者,你可以通过以下命令运行单个文件中的所有测试,

tox -e <environment> -- tests/test_file.py

或者针对特定测试运行,

tox -e <environment> -- tests/test_file.py::TestClassName::test_method_name

请注意,测试环境使用临时目录来定义 SQLite 数据库,每次调用测试命令组之前都会清除这些目录。

Superset 代码库中还包括了一个实用脚本来运行 Python 集成测试。 readme 可以在这里找到

例如,要运行所有集成测试,从根目录运行此脚本:

scripts/tests/run.sh

你还可以使用 pytest 运行位于 './tests/unit_tests' 中的单元测试。 这是一种简单的运行不需要任何数据库设置的独立测试的方法。

pytest ./link_to_test.py

使用本地 Presto 连接进行测试

如果你更改了 Presto/Trino 的数据库引擎规范,你可以使用 Docker 运行本地的 Presto 集群:

docker run -p 15433:15433 starburstdata/presto:350-e.6

然后更新 SUPERSET__SQLALCHEMY_EXAMPLES_URI 以指向本地 Presto 集群:

export SUPERSET__SQLALCHEMY_EXAMPLES_URI=presto://localhost:15433/memory/default

前端测试

我们使用 JestEnzyme 来测试 TypeScript/JavaScript。测试可以通过以下命令运行:

cd superset-frontend
npm run test

要运行单个测试文件:

npm run test -- path/to/file.js

e2e 集成测试

我们使用 Cypress 进行端到端的集成测试。 快速入门的一个简单选项是利用 tox 在隔离环境中运行整个测试套件。

tox -e cypress

或者,你可以按照以下步骤在你的开发环境中进行更底层的设置:

首先设置 python/flask 后端:

export SUPERSET_CONFIG=tests.integration_tests.superset_test_config
export SUPERSET_TESTENV=true
export CYPRESS_BASE_URL="http://localhost:8081"
superset db upgrade
superset load_test_users
superset init
superset load-examples --load-test-data
superset run --port 8081

在另一个终端中,准备前端并运行 Cypress 测试:

cd superset-frontend
npm run build-instrumented

cd cypress-base
npm install

# run tests via headless Chrome browser (requires Chrome 64+)
npm run cypress-run-chrome

# run tests from a specific file
npm run cypress-run-chrome -- --spec cypress/e2e/explore/link.test.ts

# run specific file with video capture
npm run cypress-run-chrome -- --spec cypress/e2e/dashboard/index.test.js --config video=true

# to open the cypress ui
npm run cypress-debug

# to point cypress to a url other than the default (http://localhost:8088) set the environment variable before running the script
# e.g., CYPRESS_BASE_URL="http://localhost:9000"
CYPRESS_BASE_URL=<your url> npm run cypress open

参见 superset-frontend/cypress_build.sh

或者,你可以使用 docker compose 环境进行测试:

确保你在 /etc/hosts 文件中添加了以下行: 127.0.0.1 db

如果你已经启动了 Docker 环境,请使用以下命令确保有一个全新的数据库实例: docker compose down -v

启动环境:

CYPRESS_CONFIG=true docker compose up

它将在端口 8088 上提供后端和前端。

运行 Cypress 测试:

cd cypress-base
npm install
npm run cypress open

调试 Server App

遵循以下说明来调试运行在 Docker 容器内的 Flask 应用。

首先,在 ./docker-compose.yaml 文件中添加以下内容:

superset:
env_file: docker/.env
image: *superset-image
container_name: superset_app
command: ["/app/docker/docker-bootstrap.sh", "app"]
restart: unless-stopped
+ cap_add:
+ - SYS_PTRACE
ports:
- 8088:8088
+ - 5678:5678
user: "root"
depends_on: *superset-depends-on
volumes: *superset-volumes
environment:
CYPRESS_CONFIG: "${CYPRESS_CONFIG}"

启动 Superset,像往常一样:

docker compose up

安装所需的库和包到 Docker 容器中:

进入 superset_app 容器:

docker exec -it superset_app /bin/bash
root@39ce8cf9d6ab:/app#

在容器内运行以下命令:

apt update
apt install -y gdb
apt install -y net-tools
pip install debugpy

找到 Flask 进程的 PID。确保使用第一个 PID。 Flask 应用每次你更改任何 Python 代码时都会重新生成一个子进程。 因此,使用第一个 PID 很重要。

ps -ef

UID PID PPID C STIME TTY TIME CMD
root 1 0 0 14:09 ? 00:00:00 bash /app/docker/docker-bootstrap.sh app
root 6 1 4 14:09 ? 00:00:04 /usr/local/bin/python /usr/bin/flask run -p 8088 --with-threads --reload --debugger --host=0.0.0.0
root 10 6 7 14:09 ? 00:00:07 /usr/local/bin/python /usr/bin/flask run -p 8088 --with-threads --reload --debugger --host=0.0.0.0

debugpy 注入到运行中的 Flask 进程中。在这个例子中,PID 为 6。

python3 -m debugpy --listen 0.0.0.0:5678 --pid 6

验证 debugpy 是否正在监听端口 5678:

netstat -tunap

Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:5678 0.0.0.0:* LISTEN 462/python
tcp 0 0 0.0.0.0:8088 0.0.0.0:* LISTEN 6/python

你现在可以将 debugger 附加到进程中。 使用 VSCode,你可以配置一个启动配置文件 .vscode/launch.json 如下所示:

{
"version": "0.2.0",
"configurations": [
{
"name": "Attach to Superset App in Docker Container",
"type": "python",
"request": "attach",
"connect": {
"host": "127.0.0.1",
"port": 5678
},
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "/app"
}
]
},
]
}

VSCode 不会立即在断点处停止。我们已经附加到了 PID 6,但是它还不知道任何子进程。 为了“唤醒” debugger,你需要修改一个 Python 文件。 这将触发 Flask 重新加载代码并创建一个新的子进程。 这个新的子进程将被 VSCode 检测到,断点将被激活。

在 Kubernetes 环境中调试 Server App

要在 Kubernetes 集群内的 POD 中调试运行的 Flask,你需要确保 POD 以 root 用户身份运行并且被授予 SYS_PTRACE 能力。这些设置不应该在生产环境中使用。

  securityContext:
capabilities:
add: ["SYS_PTRACE"]

有关详细信息,请参阅 为容器设置能力

一旦 POD 以 root 用户身份运行并具有 SYS_PTRACE 能力,它就能够调试 Flask 应用。

你可以遵循与 Docker Compose 相同的说明。进入 POD 并安装所需的库和包;gdb、netstat 和 debugpy。

在 Kubernetes 环境中,节点通常无法从集群外部访问。 因此,VSCode 将无法远程连接到 Kubernetes 节点上的端口 5678。 为此,你需要创建一个隧道,将 5678 端口转发到你的本地机器。

kubectl port-forward  pod/superset-<some random id> 5678:5678

现在你可以使用与上面相同的配置启动 VSCode debugger。 VSCode 将连接到 127.0.0.1:5678,该端口由 kubectl 转发到远程 Kubernetes POD。

Storybook

Superset 包含一个 Storybook 用于预览各种 Superset 组件及其变体的布局和样式。要打开并查看 Storybook:

cd superset-frontend
npm run storybook

在为 Superset 贡献新的 React 组件时,请尝试在组件的 jsx/tsx 文件旁边添加一个 Story。

贡献翻译

我们使用 Flask-Babel 来翻译 Superset。 在 Python 文件中,我们使用来自 Flask-Babel 的以下 翻译函数

  • gettextlazy_gettext(通常别名为 _):用于翻译单数字符串。
  • ngettext:用于翻译可能变为复数形式的字符串。
from flask_babel import lazy_gettext as _

然后用它包裹可翻译的字符串,例如 _('Translate me')。 在提取过程中,传递给 _ 的字符串字面量将被添加到为每种语言生成的 .po 文件中以供后续翻译。

运行时,_ 函数将返回给定字符串在当前语言下的翻译,如果没有可用的翻译,则返回给定的字符串本身。

在 TypeScript/JavaScript 中,方法类似: 我们导入 t(简单翻译),tn(包含数字的翻译)。

import { t, tn } from "@superset-ui/translation";

启用语言选择

在您的 superset_config.py 中添加 LANGUAGES 变量。 如果有多个选项,将在导航栏右侧的 UI 中添加语言选择下拉菜单。

LANGUAGES = {
'en': {'flag': 'us', 'name': 'English'},
'fr': {'flag': 'fr', 'name': 'French'},
'zh': {'flag': 'cn', 'name': 'Chinese'},
}

创建新的语言词典

首先检查目标语言的语言代码是否已经存在。 检查目标语言的 两位字母 ISO 639-1 代码 是否已存在于 superset/translations 目录中:

ls superset/translations | grep -E "^[a-z]{2}\/"

如果您的语言已经有现成的翻译,则跳过本节。

以下语言已经被 Flask AppBuilder 支持,这将使您更容易将应用程序翻译为目标语言: Flask AppBuilder i18n 文档

为了创建一种新语言的词典,首先确保安装了必要的依赖项:

pip install -r superset/translations/requirements.txt

然后运行以下命令,其中 LANGUAGE_CODE 替换为目标语言的语言代码:

pybabel init -i superset/translations/messages.pot -d superset/translations -l LANGUAGE_CODE

例如,要添加芬兰语的翻译(语言代码 fi),运行以下命令:

pybabel init -i superset/translations/messages.pot -d superset/translations -l fi

Extracting new strings for translation

Periodically, when working on translations, we need to extract the strings from both the backend and the frontend to compile a list of all strings to be translated. It doesn't happen automatically and is a required step to gather the strings and get them into the .po files where they can be translated, so that they can then be compiled.

This script does just that:

提取新字符串以进行翻译

在处理翻译的过程中,我们需要定期从后端和前端提取字符串来编译一个待翻译的所有字符串列表。 这不是自动发生的,并且是收集字符串并将它们放入 .po 文件中的必要步骤, 以便进行翻译,之后才能进行编译。

此脚本正是为此目的而设计:

./scripts/translations/babel_update.sh

更新语言文件

运行以下命令以更新语言文件中的新提取的字符串。

 pybabel update -i superset/translations/messages.pot -d superset/translations --ignore-obsolete

然后您可以翻译位于 superset/translation 下的文件中的字符串,每个语言有一个文件夹。 您可以使用 Poedit 更方便地翻译 po 文件。 这里有一个 教程

要在 MacOS 上执行翻译,可以通过 Homebrew 安装 poedit

brew install poedit

之后,只需启动 poedit 应用程序并打开 messages.po 文件。 对于芬兰语翻译的情况,这将是 superset/translations/fi/LC_MESSAGES/messages.po

应用翻译

为了让翻译在前端可用,我们需要将 PO 文件转换为一系列 JSON 文件。 要将所有 PO 文件转换为格式化的 JSON 文件,可以使用 build-translation 脚本

# Install dependencies if you haven't already
cd superset-frontend/ && npm ci
# Compile translations for the frontend
npm run build-translation

最后,为了让翻译生效,我们需要使用 pybabel 将翻译目录编译为后端的二进制 MO 文件。

# inside the project root
pybabel compile -d superset/translations

代码检查

Python

我们使用 Pylint 进行代码检查,可以通过以下方式调用:

# for python
tox -e pylint

在最佳实践方面,请避免全局禁用 Pylint 消息(通过 .pylintrc 文件)或在文件头部顶层禁用,尽管有一些例外情况。禁用应在线进行,这样可以防止掩盖问题,并提供禁用所述消息的原因上下文。

此外,Python 代码使用 Black 自动格式化,它被配置为预提交钩子。还有许多 编辑器集成

TypeScript

cd superset-frontend
npm ci
# run eslint checks
npm run eslint -- .
# run tsc (typescript) checks
npm run type

如果在使用 vscode 的 eslint 扩展,请在您的工作区 settings.json 文件中加入以下内容:

"eslint.workingDirectories": [
"superset-frontend"
]