开发指南
为文档做贡献
最新的文档和教程可在 https://superset.apache.org/ 获取。
文档网站使用 Docusaurus 2 构建,这是一个现代静态网站生成器,其源代码位于 ./docs
目录中。
本地开发
要设置一个带有热重载功能的文档网站本地开发环境
cd docs
yarn install # Installs NPM dependencies
yarn start # Starts development server at https://127.0.0.1:3000
构建
要创建并提供文档网站的生产构建
yarn build
yarn serve
部署
对 master
的提交会触发文档网站的重建和重新部署。提交修改文档的拉取请求,并使用 docs:
前缀。
创建可视化插件
Superset 中的可视化效果在 JavaScript 或 TypeScript 中实现。Superset 预装了多种可视化类型(以下简称“可视化插件”),可以在 superset-frontend/plugins
目录下找到。可视化插件在 superset-frontend/src/visualizations/presets/MainPreset.js
中添加到应用程序中。Superset 项目始终乐于审查高质量可视化插件的新提案。但是,对于高度定制的可视化类型,建议维护 Superset 的分支,并手动添加自定义构建的可视化插件。
注意:有关创建和部署自定义可视化插件的更多社区资源,请访问 Superset Wiki
先决条件
要创建新的可视化插件,您需要以下条件
- 运行 MacOS 或 Linux(Windows 未正式支持,但可能有效)
- Node.js 16
- npm 7 或 8
还建议您对 React 和 npm/Node 系统有所了解。
创建简单的 Hello World 可视化插件
要开始,您需要 Superset Yeoman 生成器。建议使用与您正在使用的 Superset 版本捆绑在一起的模板版本。可以通过以下步骤进行安装
npm i -g yo
cd superset-frontend/packages/generator-superset
npm i
npm link
之后,您可以继续创建可视化插件。为可视化插件创建一个新目录,并使用 superset-plugin-chart
前缀,然后运行 Yeoman 生成器
mkdir /tmp/superset-plugin-chart-hello-world
cd /tmp/superset-plugin-chart-hello-world
初始化可视化插件
yo @superset-ui/superset
之后,生成器会问一些问题(默认值应该没问题)
$ 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
要构建可视化插件,请运行以下命令
npm i --force
npm run build
或者,要在开发模式下运行可视化插件(=在进行更改时重建),请使用以下命令启动开发服务器
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';
导入可视化插件,然后将以下内容添加到传递给 plugins
属性的数组中
new SupersetPluginChartHelloWorld().configure({ key: 'ext-hello-world' }),
之后,可视化插件应该在您运行 Superset 时显示出来,例如开发服务器
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 集成测试。可以在 此处找到自述文件
要运行所有集成测试,例如,从根目录运行此脚本
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://127.0.0.1:15433/memory/default
前端测试
我们使用 Jest 和 Enzyme 测试 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="https://127.0.0.1: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 (https://127.0.0.1:8088) set the environment variable before running the script
# e.g., CYPRESS_BASE_URL="https://127.0.0.1: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
调试服务器应用程序
请按照这些说明调试在 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。每次更改任何 Python 代码时,Flask 应用程序都会重新生成一个子进程。因此,使用第一个 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
您现在可以将调试器附加到该进程。使用 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,但它尚未识别任何子进程。为了“唤醒”调试器,您需要修改一个 Python 文件。这将触发 Flask 重新加载代码并创建一个新的子进程。VSCode 将检测到这个新的子进程,断点将被激活。
在 Kubernetes 环境中调试服务器应用程序
要调试在 Kubernetes 集群内的 POD 中运行的 Flask,您需要确保该 POD 作为 root 运行,并被授予 SYS_TRACE
功能。这些设置不应在生产环境中使用。
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 调试器。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
gettext
和lazy_gettext
(通常缩写为_
):用于翻译单个字符串。ngettext
:用于翻译可能变为复数的字符串。
from flask_babel import lazy_gettext as _
然后用它包装可翻译的字符串,例如 _('Translate me')
。在提取过程中,传递给 _
的字符串文字将被添加到为每种语言生成的 .po
文件中,以便以后进行翻译。
在运行时,_
函数将返回当前语言给定字符串的翻译,如果不存在翻译,则返回给定字符串本身。
在 TypeScript/JavaScript 中,技术类似:我们导入 t
(简单翻译)、tn
(包含数字的翻译)。
import { t, tn } from "@superset-ui/translation";
启用语言选择
将 LANGUAGES
变量添加到您的 superset_config.py
中。在其中包含多个选项将向导航栏右侧的 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
提取新的翻译字符串
在进行翻译时,我们需要定期从后端和前端提取字符串,以编译所有要翻译的字符串的列表。它不会自动发生,而是收集字符串并将它们放到 .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
在最佳实践方面,请避免全局(通过.pylintrc
)或文件头部的顶级级别禁用 Pylint 消息,尽管有一些例外。禁用应该在内联进行,因为它可以防止掩盖问题并提供有关禁用该消息的原因的上下文。
此外,Python 代码使用 Black 自动格式化,它被配置为 pre-commit 钩子。还有许多 编辑器集成
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"
]