ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Lightdash 数据仓库适配器接入指南:从零添加一个新的 Warehouse 连接的七层实施清单

Lightdash 数据仓库适配器接入指南:从零添加一个新的 Warehouse 连接的七层实施清单 Lightdash 数据仓库适配器接入指南从零添加一个新的 Warehouse 连接的七层实施清单【免费下载链接】lightdashAgentic BI. Analytics at the speed of code ⚡️项目地址: https://gitcode.com/GitHub_Trending/li/lightdash本文基于 Lightdash 官方仓库中的add-warehouse-adapter技能文档系统讲解如何在 Lightdash 中新增一个数据仓库Warehouse连接的完整流程。该文档以 Athena 适配器PRs #19751/#19752作为首个规范范例、以 MotherDuck/DuckDB 适配器作为最近一次实现参照把整个过程拆解为公共类型、后端、仓库客户端、前端、CLI、Docker、Demo 项目七个层次。读完本文你将掌握新增一个仓库适配器所需的全部改动点、命令、测试与验证方式能够按图索骥完成从类型定义到 UI 表单再到 dbt 适配器打包的端到端落地。为什么需要一套标准化的仓库适配器接入流程Lightdash 是运行在 dbt 之上的语义分析层其核心能力之一是连接多种数据仓库BigQuery、Snowflake、PostgreSQL、Redshift、Databricks、Trino、ClickHouse、Athena、DuckDB 等。每一种仓库连接都需要在代码库中贯穿多个包公共类型定义packages/common、后端 API 与服务packages/backend、实际执行查询的仓库客户端packages/warehouses、前端连接表单packages/frontend、CLI 的 dbt profile 生成packages/cli以及 Docker 镜像中的 dbt 适配器依赖。由于仓库连接涉及数据库迁移、密钥脱敏、SQL 方言、UI 表单等多处强约束代码大量switch语句是穷举式的漏改任何一处都会导致编译失败、查询报错或密钥泄露。因此社区将完整的接入过程固化为一份分层 checklist供贡献者逐项核对、逐步提交。整体架构七个层次各自负责什么层次所在目录核心职责Layer 1 公共类型packages/common/src/定义仓库类型枚举、凭据类型、敏感字段、联合类型、字段引用符Layer 2 后端packages/backend/src/数据库迁移、实体定义、dbt profile 生成、服务层密钥处理Layer 3 仓库客户端packages/warehouses/src/实际执行查询、获取目录与表结构的 SQL 客户端Layer 4 前端packages/frontend/src/连接表单、校验器、默认值、Logo 与注册映射Layer 5 CLIpackages/cli/src/从 dbt target 转换出仓库凭据的 CLI 逻辑Layer 6 DockerDockerfile为各 dbt venv 安装对应 dbt 适配器 pip 包Layer 7 Demo 项目examples/full-jaffle-shop-demo/种子数据列类型、SQL 宏、profiles 的适配兼容下面按照 Layers 顺序逐一展开每个改动点并附上仓库源码中的佐证。Layer 1公共类型层packages/common这是整个接入的第一站所有下游代码都依赖这里的类型定义。改动集中在packages/common/src/types/projects.ts一个文件。1.1 添加 WarehouseTypes 枚举项在 projects.ts 的WarehouseTypes枚举中追加新仓库类型的字符串字面量。当前仓库已有export enum WarehouseTypes { BIGQUERY bigquery, POSTGRES postgres, REDSHIFT redshift, SNOWFLAKE snowflake, DATABRICKS databricks, TRINO trino, CLICKHOUSE clickhouse, ATHENA athena, DUCKDB duckdb, }新类型应沿用同名小写字符串约定例如XXX xxx。1.2 定义 Create 凭据类型与只读凭据类型以type: WarehouseTypes.XXX作为可辨识联合discriminant定义CreateXxxCredentials。这里有一个关键安全约定敏感字段token、password、key 等一律声明为可选token?: string而不是用空字符串哨兵值作为必填。例如 Databricks 的凭据中personalAccessToken?: string、refreshToken?: string、oauthClientSecret?: string均为可选见 projects.ts。随后定义不带敏感字段的只读版本export type XxxCredentials Omit CreateXxxCredentials, SensitiveCredentialsFieldNames ;1.3 敏感字段登记projects.ts 中的sensitiveCredentialsFieldNames数组集中登记了所有敏感字段名包括user、password、keyfileContents、personalAccessToken、privateKey、sshTunnelPrivateKey、token、refreshToken、oauthClientId、oauthClientSecret、accessKeyId、secretAccessKey、sessionToken等。若新仓库的敏感字段不在其中必须补入否则前端表单回填与密钥清理逻辑无法识别它。1.4 更新两个联合类型将CreateXxxCredentials加入CreateWarehouseCredentials联合类型将XxxCredentials加入WarehouseCredentials联合类型。当前CreateWarehouseCredentials已包含 9 个成员见 projects.ts。同时要更新 userWarehouseCredentials.ts 中的UserWarehouseCredentials与UserWarehouseCredentialsWithSecrets两个联合类型——个人仓库凭据用户自带的连接同样走这套类型体系。1.5 字段引用符与聚合函数getFieldQuoteChar()位于 warehouse.ts根据仓库类型返回标识符引用符BigQuery 与 Databricks 使用反引号其余含 Athena、DuckDB使用双引号。新适配器需要在此补充 case。注意该函数已被标记deprecated新代码优先使用WarehouseSqlBuilder.getFieldQuoteChar()但公共层的这一入口仍需同步。getAggregatedField()warehouse.ts按适配器类型生成聚合 SQL若新适配器的聚合方言与现有分支一致可落入已有 case。1.6 时区与转换映射packages/common/src/utils/timeFrames.ts中的适配器配置 map 以及packages/common/src/compiler/translator.ts的convertTimezone()都需要确认包含新适配器——时区换算依赖按仓库方言生成不同的 SQL 片段。1.7 类型检查与 lint完成本层改动后运行pnpm -F common typecheck pnpm -F common lintLayer 2后端层packages/backend2.1 数据库迁移使用仓库提供的迁移脚手架生成新迁移文件pnpm -F backend create-migration add_xxx_warehouse_typeup中向warehouse_types表插入新类型down中删除该类型的凭据与类型记录。仓库中的实际范例add_athena_warehouse_type.ts展示了两者的标准写法export async function up(knex: Knex): Promisevoid { await knex(warehouse_types).insert([{ warehouse_type: athena }]); } export async function down(knex: Knex): Promisevoid { await knex(warehouse_credentials) .delete() .where(warehouse_type, athena); await knex(warehouse_types).delete().where(warehouse_type, athena); }warehouse_types表是warehouse_credentials表的外键目标见迁移 add_credentials_table.ts 中的references(warehouse_type).inTable(warehouse_types)因此必须先插入类型再写入凭据。2.2 实体与 dbt profile 生成在 warehouseCredentials.ts 的warehouseTypes数组中追加xxx。该数组以as const声明是数据库层允许的仓库类型白名单。在 profiles.ts 的credentialsTarget()中在default分支之前为新适配器添加 case。这里有一条明确的安全红线密钥必须通过envVarReference()/envVar()模式传递绝不能写入原始环境变量名或内联明文值。其实现为const envVar (v: string) LIGHTDASH_DBT_PROFILE_VAR_${v.toUpperCase()}; const envVarReference (v: string) {{ env_var(${envVar(v)}) }};ClickHouse 是推荐的参照模式target 中写password: envVarReference(password)environment 中写[envVar(password)]: credentials.password。这样 dbt 生成 profile 时密钥以LIGHTDASH_DBT_PROFILE_VAR_*环境变量注入而不是出现在 profile 明文里。此外需确认 DbtMetadataApiClient.ts 中的quoteChars已包含新适配器否则元数据 API 生成的 SQL 引用符会不匹配。2.3 服务层穷举 switch 更新ProjectService中有多处穷举式 switch 必须同步遗漏任何一处都会导致新仓库在对应路径上报错clearSecretsFromCredentials()ProjectService.ts负责在保存前清空项目/组织级凭据中的密钥让用户后续用自己的凭据覆盖。规范做法是把敏感字段置空如{ ...credentials, password: }而非解构剔除后做不安全的as断言。参考现有实现Athena 清空accessKeyId/secretAccessKeyDuckDB 按connectionType分支MotherDuck 清空tokenDUCKLAKE 清空 catalog 中的user/password以及 S3/GCS/Azure 数据路径中的各类密钥。仓库客户端创建 switch根据credentials.type实例化对应WarehouseClient。用户凭据创建 switch构造用户级仓库凭据时的分支。getDatabaseFromWarehouseCredentials()从凭据中提取默认数据库名。同样需要更新的还有 UserWarehouseCredentialsModel.ts 中的相关分支。完成本层后运行pnpm -F backend typecheck pnpm -F backend lintLayer 3仓库客户端层packages/warehouses这是执行 SQL 的核心层新增文件为src/warehouseClients/XxxWarehouseClient.ts。3.1 客户端类与 SQL Builder客户端类需满足三个结构要求继承WarehouseBaseClientCreateXxxCredentials实现XxxSqlBuilder extends WarehouseBaseSqlBuilder负责方言相关的 SQL 生成实现四个核心方法streamQuery()流式查询、getCatalog()目录、getAllTables()全部表、getFields()字段。基类已经提供了test()、runQuery()、executeAsyncQuery()的通用实现基于上述四个方法组合而来因此不要添加空操作的覆盖方法直接继承即可。3.2 凭据传递的两条铁律直接向客户端库传凭据对象参照 ClickHouse/Databricks 的写法绝不要经由process.env中转——这是为了避免密钥意外落入进程环境变量、被日志或子进程继承。若需要备选构造方式例如预聚合 pre-aggregate 场景使用构造函数overrides参数不要对 readonly 字段做AnyType强制转换。3.3 工厂、SSH 隧道与导出在 warehouseClientFromCredentials.ts 的工厂 switch 中追加 case当前工厂已按类型分发到 Snowflake/Postgres/Redshift/BigQuery/Databricks/Trino/ClickHouse/Athena/DuckDB 客户端。在src/ssh/sshTunnel.ts中添加 case——对于云仓库通常是空操作break无需 SSH 隧道。从src/index.ts导出新客户端。3.4 测试编写src/warehouseClients/XxxWarehouseClient.test.ts覆盖 SQL builder 生成、查询执行与目录获取等关键路径。运行pnpm -F lightdash/warehouses typecheck pnpm -F lightdash/warehouses testLayer 4前端层packages/frontend4.1 连接表单组件新建src/components/ProjectConnection/WarehouseForms/XxxForm.tsx参照AthenaForm.tsx的模式使用 Mantine v8 组件构建表单并导出XxxSchemaInput供DbtSettingsForm复用。同时在ProjectConnectFlow/Assets/下添加仓库的 SVG Logo。4.2 表单注册链路前端有一整条注册链每处都要登记否则表单无法渲染或保存文件改动WarehouseForms/defaultValues.ts定义XxxDefaultValues并加入warehouseDefaultValuesWarehouseForms/validators.ts添加 zod/ajv 校验器WarehouseSettingsForm.tsx注册 labels 与 forms 映射DbtSettingsForm.tsx在 schema input switch 中登记XxxSchemaInputProjectConnectFlow/utils.tsx在WarehouseTypeLabels数组中追加标签UserSettings/MyWarehouseConnectionsPanel/CreateCredentialsModal.tsx在defaultCredentials中登记UserSettings/MyWarehouseConnectionsPanel/EditCredentialsModal.tsx在getCredentialsWithPlaceholders中登记编辑时用占位符代替敏感字段回填UserSettings/MyWarehouseConnectionsPanel/WarehouseFormInputs.tsx登记表单输入前端层完成后运行pnpm -F frontend typecheck pnpm -F frontend lintLayer 5CLI 层packages/cliCLI 层负责把用户 dbt 项目中的 profile如profiles.yml里的 target转换成 Lightdash 仓库凭据。新增文件src/dbt/targets/xxx.ts包含三部分XxxTarget类型与 dbt profile 结构一一对应的 TypeScript 类型xxxSchema: JSONSchemaTypeXxxTarget用于 ajv 校验的 JSON SchemaconvertXxxSchema()转换函数。然后在 profile.ts 的warehouseCredentialsFromDbtTarget()中添加case xxx:并在src/handlers/dbt/getWarehouseClient.ts的getMockCredentials()中补充 mock 凭据用于本地无真实凭据的开发调试。Layer 6Docker 层DockerfileLightdash 镜像为不同 dbt 版本维护了多个 Python venv新适配器的 pip 包必须安装到对应版本只添加到支持该适配器的 dbt 版本1.8 及以上不装到 1.4–1.7。仓库先例dbt-athena从 1.9 添加dbt-duckdb从 1.8 添加。适配器包名通常为dbt-xxx如dbt-duckdb、dbt-athena。版本约束写法二选一若适配器有自己的独立版本体系用不带版本的dbt-xxx若适配器跟随 dbt-core 版本演进用dbt-xxx~X.Y.0。选型前需到包索引确认包名与版本兼容性。Layer 7Demo 项目兼容性examples/full-jaffle-shop-demo新增仓库后需要确保官方 DemoJaffle Shop也能在该仓库上跑通这通常能暴露方言差异更新dbt/data/seeds.yml的列类型——例如 DuckDB 不支持jsonb、TIME等类型更新dbt/macros/中带仓库分支的 SQL 宏如quarter_end_date.sql、casts.sql在profiles/profiles.yml中添加新仓库的 target验证dbt seed --target xxx dbt run --target xxx收尾验证代码生成与全量类型检查所有代码改动完成后还有三个生成/校验步骤不能遗漏pnpm generate-api # 重新生成 routes.ts 与 swagger.json pnpm generate:chart-as-code-schema pnpm check:chart-as-code-schema pnpm -F common typecheck pnpm -F backend typecheck pnpm -F frontend typecheckgenerate-api会基于 tsoa 注释重新生成 API 路由与 OpenAPI 文档若凭据类型变化未同步Swagger 契约就会过期。关键参考文件速查表层次关键文件用途Commonpackages/common/src/types/projects.ts类型、敏感字段、联合类型Commonpackages/common/src/utils/warehouse.ts字段引用符、聚合函数Backendpackages/backend/src/dbt/profiles.tsdbt profile 生成与密钥注入Backendpackages/backend/src/database/entities/warehouseCredentials.ts数据库实体与类型白名单Backendpackages/backend/src/database/migrations/20260122193054_add_athena_warehouse_type.ts迁移范式AthenaBackendpackages/backend/src/services/ProjectService/ProjectService.ts密钥清理与客户端创建Warehousepackages/warehouses/src/warehouseClientFromCredentials.ts客户端工厂Frontendpackages/frontend/src/components/ProjectConnection/WarehouseSettingsForm.tsx表单注册入口CLIpackages/cli/src/dbt/profile.tsCLI target 转换DockerDockerfile各 dbt 版本的适配器 pip 包典型案例与最佳实践回顾仓库中有两个可直接对照的完整实现AthenaPRs #19751/#19752第一个完整走通本文七层模式的适配器从公共类型、后端迁移到前端表单、Docker 打包全部齐备最适合作为新适配器的模板MotherDuck/DuckDB最近一次实现展示了更复杂的凭据结构——按connectionType区分 MotherDuck、DuckLake、Embedded、Analytics 四种连接模式并在clearSecretsFromCredentials()中为每种模式分别处理密钥体现了清单中按既有模式扩展的原则。最后总结几条贯穿始终的工程规范敏感字段一律可选化并在sensitiveCredentialsFieldNames登记密钥在 profile 与存储层均不可明文客户端与服务的switch穷举必须逐一更新配合 TypeScript 的assertUnreachable在漏写时直接编译报错每层独立跑通 typecheck 与 lint 后再进入下一层。遵循这份清单新增一个仓库适配器将从容易遗漏的隐性知识变成可复制、可评审、可测试的标准化流程。【免费下载链接】lightdashAgentic BI. Analytics at the speed of code ⚡️项目地址: https://gitcode.com/GitHub_Trending/li/lightdash创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表