Metabase Embedding SDK 编程式创建仪表盘:CreateDashboardValues 类型全解析
【免费下载链接】metabaseThe easy-to-use open source Business Intelligence and Embedded Analytics tool that lets everyone work with data :bar_chart:项目地址: https://gitcode.com/GitHub_Trending/me/metabase
CreateDashboardValues是 Metabase Embedding SDK 中描述"创建新仪表盘所需参数"的核心类型,它同时服务于useCreateDashboardApiHook 与底层POST /api/dashboard接口。本文将以该类型为骨架,逐一拆解name、description、collectionId三个字段的含义、取值规则与底层解析逻辑,并给出可直接落地的 TypeScript 实战示例,帮助你在嵌入应用中用几行代码动态生成仪表盘。
CreateDashboardValues:编程式建盘的参数契约
在 Embedding SDK 中,当你需要在宿主应用中"以代码方式"新建一个仪表盘(例如根据用户操作动态生成报表容器)时,传入的参数对象就是CreateDashboardValues。它的完整定义如下:
export type CreateDashboardValues = Omit< CreateDashboardProperties, "collection_id" > & { /** * Collection in which to create a new dashboard. * You can use predefined system values like `root` or `personal`. */ collectionId: SdkCollectionId; };该定义位于 frontend/src/embedding-sdk-bundle/types/dashboard.ts,其核心手法是:从核心应用的表单属性CreateDashboardProperties中剔除内部字段collection_id,再替换为面向 SDK 使用者的collectionId(类型为SdkCollectionId)。这意味着你在 SDK 侧永远不需要关心后端真实的数值型集合 ID,只需给出语义化的引用即可。
CreateDashboardProperties本身定义于 frontend/src/metabase/common/CreateDashboard/CreateDashboardForm.tsx,是 Metabase 核心产品"新建仪表盘"表单同款的数据结构,保证了嵌入式场景与原生界面的行为一致。
属性一览
CreateDashboardValues共包含三个属性,其中name与collectionId必填,description可空:
| 属性 | 类型 | 说明 |
|---|---|---|
collectionId | SdkCollectionId | 新仪表盘将被创建到哪个集合(Collection)中。可使用预定义的系统值,如root或personal。 |
description | string \| null | 仪表盘描述。 |
name | string | 仪表盘标题。 |
name:仪表盘标题
name是必填字段,对应创建后仪表盘显示的名称。在表单校验层(CreateDashboardForm.tsx 中的 Yup schema),它被定义为:
name: Yup.string() .required(Errors.required) .max(DASHBOARD_NAME_MAX_LENGTH, Errors.maxLength) .default(""),即:必填、不允许为空字符串,且最大长度受DASHBOARD_NAME_MAX_LENGTH约束。该常量定义于 frontend/src/metabase/common/utils/dashboard.ts:
export const DASHBOARD_NAME_MAX_LENGTH = 254; export const DASHBOARD_DESCRIPTION_MAX_LENGTH = 1500;因此标题最长 254 个字符,描述最长 1500 个字符。超出长度时,SDK 侧的表单组件与后端校验都会拒绝创建。
description:仪表盘描述
description类型为string | null,即允许显式传入null表示无描述。在 Yup schema 中:
description: Yup.string() .nullable() .max(DASHBOARD_DESCRIPTION_MAX_LENGTH, Errors.maxLength) .default(null),如果不传,SDK 会以null作为默认值提交。后端接口同样将description定义为可选({:optional true} [:maybe :string]),并在存储时原样保留。
collectionId:决定仪表盘的归属集合
collectionId是三个属性中最具 SDK 特色、也最值得展开讲解的一个。它不要求你提供后端数据库里的原始数值 ID,而是接受一组"集合引用",包括:
- 预定义系统值:
"personal"(当前用户个人集合)、"root"(根集合)、"tenant"(租户集合); - 普通数值 ID:某个具体集合的数据库 ID;
- 字符串实体 ID:集合的
entity_id(SdkEntityId类型)。
其类型定义同时出现在 SdkCollectionId.md 与 frontend/src/embedding-sdk-bundle/types/collection.ts:
export type SdkCollectionId = | number | "personal" | "root" | "tenant" | SdkEntityId;其中SdkEntityId是一个品牌化字符串类型(type SdkEntityId = string & {}),用于在类型层面与普通字符串区分,详见 SdkEntityId.md。需要说明的是,该类型刻意不包含核心应用CollectionId中的"users"、"trash"等值——SDK 的公开 API 不支持这些内部集合,源码注释对此有明确说明。
集合引用的底层解析规则
当你把collectionId传给 SDK 后,真正发生转换的地方是 frontend/src/embedding-sdk-bundle/store/collections.ts 中的getCollectionIdValueFromReference(一个基于 Redux 状态的createSelector)。它依据传入的引用类型进行分支匹配:
| 传入值 | 解析结果 | 说明 |
|---|---|---|
"personal" | 当前用户的个人集合 ID | 从getUserPersonalCollectionId派生 |
"tenant" | 当前用户的租户集合 ID(tenant_collection_id) | 若用户不属于任何租户,会抛出"You must be a tenant member to access the tenant collection."错误 |
"root" | null | 关键细节:根集合在后端 API 中就是用null表示的 |
| 数值 / 字符串 | 原样透传 | 数值作为真实集合 ID;字符串按实体 ID 处理 |
| 其他 | 抛错 | 抛出"Invalid collection id, expected number \| string \| 'root' \| 'personal' \| 'tenant'" |
这段逻辑(collections.ts)同时揭示了两个易踩的坑:
"root"会被转换为null提交。这是因为后端POST /api/dashboard端点用collection_id = null表示根集合,而非字面量字符串"root";"tenant"依赖当前用户的租户成员身份,非租户成员直接抛出异常,调用前应做好容错(如try/catch或先判断当前用户)。
实战:通过 useCreateDashboardApi 创建仪表盘
CreateDashboardValues最典型的使用场景是配合useCreateDashboardApiHook。其签名定义于 useCreateDashboardApi.md:
function useCreateDashboardApi(): { createDashboard: ( params: CreateDashboardValues, ) => Promise<MetabaseDashboard>; } | null;注意返回值为null或对象:在 SDK 完全加载并初始化完成之前,该 Hook 返回null,因此调用前必须先判空。一个完整的实战示例:
import { useCreateDashboardApi } from "@metabase/embedding-sdk-react"; function CreateDashboardButton() { const { createDashboard } = useCreateDashboardApi() ?? {}; const handleCreate = async () => { if (!createDashboard) { // SDK 尚未就绪,此时 createDashboard 不可用 return; } try { const dashboard = await createDashboard({ name: "Q3 销售周报", // 必填,仪表盘标题(≤ 254 字符) description: "由嵌入应用动态生成", // 可选,可传 null collectionId: "personal", // 可选但强烈建议显式指定; // 不传时 SDK 默认使用 "personal" }); console.log("创建成功,仪表盘 ID:", dashboard.id); console.log("实体 ID:", dashboard.entity_id); } catch (error) { // 处理权限不足、名称超长、租户集合不可达等异常 } }; return <button onClick={handleCreate}>新建仪表盘</button>; }一个值得注意的默认值细节:SDK 的createDashboard实现(frontend/src/embedding-sdk-bundle/lib/create-dashboard.ts)在解构参数时为collectionId提供了默认值"personal":
export const createDashboard = (reduxStore: SdkStore) => async ({ collectionId = "personal", ...rest }: CreateDashboardValues) => { const realCollectionId = getCollectionIdValueFromReference( reduxStore.getState(), collectionId, ); const action = createDashboardMutation.initiate({ ...rest, collection_id: realCollectionId, }); return reduxStore.dispatch(action).unwrap(); };即:即使你在类型层面按必填声明了collectionId,运行时也可以省略它,仪表盘会被创建到当前用户的个人集合中。解析后的真实集合 ID 被映射为collection_id字段,连同其余参数一起交给 RTK Query 的createDashboardmutation(定义于 frontend/src/metabase/api/dashboard.ts),最终发出POST /api/dashboard请求。
创建成功后的返回值:MetabaseDashboard
createDashboard返回Promise<MetabaseDashboard>,即创建成功的完整仪表盘实体,其结构见 MetabaseDashboard.md,对应源码定义在 dashboard.ts:
export type MetabaseDashboard = { id: SdkDashboardId; entity_id: SdkEntityId; created_at: string; updated_at: string; collection?: MetabaseCollection | null; name: string; description: string | null; "last-edit-info": { id: number; email: string; first_name: string; last_name: string; timestamp: string; }; };其中id是SdkDashboardId(数值 ID、字符串 ID 或实体 ID 的联合类型)。拿到返回值后,你可以直接将其id传入InteractiveDashboard/StaticDashboard等组件立即渲染刚创建的仪表盘,形成"创建即展示"的闭环。
另一种方式:CreateDashboardModal 组件
如果你的产品更希望复用 Metabase 自带的新建界面(而不是自绘表单),可以使用CreateDashboardModal组件,其声明见 CreateDashboardModal.md,入参类型为 CreateDashboardModalProps.md:
| 属性 | 类型 | 说明 |
|---|---|---|
initialCollectionId? | SdkCollectionId | 初始选中的集合,可使用root、personal等系统值 |
isOpen? | boolean | 弹窗是否打开 |
onClose? | () => void | 关闭弹窗的处理函数 |
onCreate | (dashboard: MetabaseDashboard) => void | 创建成功后的回调,参数即新建的仪表盘 |
targetCollection? | SdkCollectionId | 固定保存到指定集合;设置后保存弹窗中的集合选择器会被隐藏 |
该组件的属性同样建立在SdkCollectionId之上,因此initialCollectionId与targetCollection支持与CreateDashboardValues.collectionId完全相同的取值集合。两种方式对比:useCreateDashboardApi适合完全自定义 UI 与流程(例如静默创建、批量创建);CreateDashboardModal适合快速交付,直接复用官方弹窗交互。
底层调用链:从前端 SDK 到后端接口
为便于排查问题,这里梳理创建仪表盘的完整链路(均可从仓库源码验证):
- SDK 入口:create-dashboard.ts 接收
CreateDashboardValues,用getCollectionIdValueFromReference解析collectionId; - RTK Query 层:dashboard.ts 的
createDashboardmutation 组装POST /api/dashboard请求体,请求体类型CreateDashboardRequest含name、description?、parameters?、cache_ttl?、collection_id?等字段; - 后端端点:src/metabase/dashboards_rest/api.clj 中
POST /端点接收并校验请求,schema 要求name为NonBlankString(非空白字符串),description、cache_ttl、collection_id、collection_position均可选; - 权限与落库:后端先执行
api/create-check :model/Dashboard(确认当前用户对目标集合具备建盘权限),随后在事务中调用insert-dashboard!写入,并发布:event/dashboard-create事件、上报 Snowplow 分析事件(见 api.clj); - 响应:后端返回补齐了
last-edit-info、根集合信息等详情的仪表盘对象,即MetabaseDashboard。
这条链路意味着:CreateDashboardValues中的name/description直接映射为后端同名字段,而collectionId经过"引用 → 真实 ID →collection_id"的两步转换,最终决定新仪表盘挂在哪个集合下——这也是创建请求在权限校验时依据的目标集合。
常见错误与排查要点
useCreateDashboardApi()返回null:SDK 尚未初始化完成,需等待MetabaseProvider加载后再调用,代码中务必判空;"You must be a tenant member to access the tenant collection.":使用了"tenant"引用但当前用户无租户集合,改用"personal"、"root"或具体集合 ID;"Invalid collection id...":传入了类型允许之外的值(例如"all"——该值仅存在于SdkBrowserCollectionId,属于集合浏览器的虚拟顶层,不能用于创建仪表盘);- 名称/描述超长:
name超过 254、description超过 1500 字符会被表单校验或后端拒绝,建议在 UI 侧先做长度提示; - 权限不足:目标集合无创建权限时后端
create-check会拒绝请求,可先确认当前嵌入用户的集合权限配置。
综上,CreateDashboardValues虽只有三个字段,却是打通"嵌入应用 → SDK → Metabase 后端"创建链路的关键契约。理解SdkCollectionId的引用语义与默认值行为,即可在嵌入场景中稳定、可预期地动态创建仪表盘。
【免费下载链接】metabaseThe easy-to-use open source Business Intelligence and Embedded Analytics tool that lets everyone work with data :bar_chart:项目地址: https://gitcode.com/GitHub_Trending/me/metabase
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考