关于 programmatic-filters
programmatic-filters 是通过服务器端筛选和数据集切换保护嵌入式 Domo 内容的技能,指导开发者生成针对不同查看者的嵌入令牌,由 Domo 强制执行数据访问限制。
主要功能
- 01生成针对查看者的嵌入令牌
- 02应用标准筛选和 SQL 筛选
- 03切换嵌入内容所用数据集
- 04支持按用户和角色筛选
- 05在服务器端处理身份验证和令牌生成
适用场景
- 01按查看者或角色限制嵌入式仪表板数据
- 02对嵌入数据应用复杂 SQL 条件
- 03使用共享仪表板提供租户专属数据集
- 04生成经过身份验证的卡片或仪表板嵌入内容
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ stahura/ domo-ai-vibe-rules --skill programmatic-filters安装后在 Claude Code 中输入/programmatic-filters确认已加载。
附带文件references/api-endpoints.md
详细介绍
Domo 程序化筛选
通过服务端筛选器和数据集切换,控制每个查看者在嵌入式 Domo 内容中看到的数据。由 Domo 强制执行 — 最终用户无法绕过。如需客户端筛选,请参阅 jsapi-filters。
工作原理
一个代理用户(服务账号)代表所有查看者执行操作。你的服务端:
- 通过 Domo 进行身份验证(OAuth 客户端凭据 → access token)
- 请求带有查看者专属筛选器的 embed token
- 将 token 返回给客户端,由客户端将其 POST 到 iframe
前提条件
- 拥有
CLIENT_ID和CLIENT_SECRET的 Domo API 客户端(developer.domo.com > My Account > New Client) - Embed ID(来自嵌入对话框的 5 位字符 ID,而非页面 URL)
- 用于筛选的数据集列名/ID
所有身份验证都必须在服务端进行(CORS 限制)。切勿在客户端暴露凭据。
Embed Token 流程
第 1 步:获取 Access Token
POST https://api.domo.com/oauth/token?grant_type=client_credentials&scope=data%20audit%20user%20dashboard
Authorization: Basic base64(CLIENT_ID:CLIENT_SECRET)
Node.js:
const credentials = Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64')
const response = await fetch(
'https://api.domo.com/oauth/token?grant_type=client_credentials&scope=data%20audit%20user%20dashboard',
{
method: 'GET',
headers: { Authorization: `Basic ${credentials}` }
}
)
const { access_token } = await response.json()
Python:
import requests
from base64 import b64encode
credentials = b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode()).decode()
response = requests.get(
"https://api.domo.com/oauth/token",
params={"grant_type": "client_credentials", "scope": "data audit user dashboard"},
headers={"Authorization": f"Basic {credentials}"}
)
access_token = response.json()["access_token"]
第 2 步:请求带有筛选器的 Embed Token
使用 access token 请求 embed token,并在 authorizations 负载中包含你的筛选器:
对于仪表板:
POST https://api.domo.com/v1/stories/embed/auth
Authorization: Bearer {access_token}
Content-Type: application/json
对于卡片:
POST https://api.domo.com/v1/cards/embed/auth
Authorization: Bearer {access_token}
Content-Type: application/json
负载结构:
{
"sessionLength": 1440,
"authorizations": [
{
"token": "<embed_id>",
"permissions": ["READ", "FILTER", "EXPORT"],
"filters": [],
"policies": []
}
]
}
sessionLength:Token 有效期,以分钟为单位(1440 = 24 小时)token:仪表板或卡片的 embed IDpermissions:授予的权限数组 —READ、FILTER、EXPORTfilters:标准筛选器对象数组(见下文)policies:要应用的 PDP 策略 ID 数组(可选)
响应中包含一个 authentication 属性,其中包含 embed token。
第 3 步:渲染嵌入内容
通过以 iframe 为目标的隐藏 POST 表单提交。仪表板:https://public.domo.com/embed/pages/{embed_id},卡片:https://public.domo.com/cards/{embed_id}
<iframe id="domo-embed" name="domo-embed" width="100%" height="600"></iframe>
<form id="embed-form" action="https://public.domo.com/embed/pages/{embed_id}" method="POST" target="domo-embed">
<input type="hidden" name="embedToken" value="{embed_token}" />
</form>
<script>document.getElementById('embed-form').submit();</script>
POST 提交可防止 token 出现在 URL 或浏览器历史记录中。
筛选器类型
两种类型:标准筛选器和 SQL 筛选器,二者都位于 embed token 请求中。
标准筛选器
{
"column": "Region",
"operator": "IN",
"values": ["West", "East"]
}
必需属性:
| 属性 | 类型 | 描述 |
|---|---|---|
column | string | 数据集中的确切列名 |
operator | string | 比较运算符(见下表) |
values | array | 用于筛选的值 |
可选属性:
| 属性 | 类型 | 描述 |
|---|---|---|
datasourceId | string (UUID) | 将筛选器限制到特定数据集。若不设置此项,筛选器将应用于嵌入内容中所有具有匹配列名的数据集。 |
运算符
| 运算符 | 描述 | 典型用途 |
|---|---|---|
IN | 列值在列表中 | 多值匹配 |
NOT_IN | 列值不在列表中 | 排除 |
EQUALS | 列值等于 | 单值精确匹配 |
NOT_EQUALS | 列值不等于 | 单值排除 |
GREATER_THAN | 列值大于 | 数值/日期范围 |
GREATER_THAN_EQUALS_TO | 列值大于或等于 | 数值/日期范围 |
LESS_THAN | 列值小于 | 数值/日期范围 |
LESS_THAN_EQUALS_TO | 列值小于或等于 | 数值/日期范围 |
标准筛选器示例
单个筛选器 — 仅显示 West 区域:
{
"filters": [
{
"column": "Region",
"operator": "IN",
"values": ["West"]
}
]
}
多个筛选器 — West 区域,收入超过 10000:
{
"filters": [
{
"column": "Region",
"operator": "IN",
"values": ["West"]
},
{
"column": "Revenue",
"operator": "GREATER_THAN",
"values": [10000]
}
]
}
数据集专属筛选器 — 仅应用于多数据集仪表板中的某一个数据集:
{
"filters": [
{
"column": "Region",
"operator": "IN",
"values": ["West"],
"datasourceId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
]
}
SQL 筛选器
对于复杂逻辑(OR、BETWEEN、LIKE、嵌套表达式),请通过 sqlFilters 使用 SQL WHERE 语法(与 filters 同级,而非嵌套其中)。
{
"authorizations": [
{
"token": "<embed_id>",
"permissions": ["READ", "FILTER", "EXPORT"],
"filters": [],
"sqlFilters": [
{
"sqlFilter": "`Region` IN ('West', 'East') AND `Revenue` > 10000",
"datasourceIds": ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"]
}
]
}
]
}
必需属性:
| 属性 | 类型 | 描述 |
|---|---|---|
sqlFilter | string | SQL WHERE 子句语法 |
可选属性:
| 属性 | 类型 | 描述 |
|---|---|---|
datasourceIds | string[] | 要应用该筛选器的数据集 UUID 数组 |
语法规则
列名使用反引号,字符串使用单引号,数值不加引号。支持:IN、NOT IN、BETWEEN、LIKE、AND、OR、IS NULL、IS NOT NULL。使用括号进行分组。
SQL 筛选示例
OR 条件(仅靠标准筛选无法实现):
{
"sqlFilter": "`Region` = 'West' OR `Department` = 'Sales'"
}
使用 BETWEEN 筛选日期范围:
{
"sqlFilter": "`Order Date` BETWEEN '2024-01-01' AND '2024-12-31'"
}
使用 LIKE 进行部分匹配:
{
"sqlFilter": "`Product Name` LIKE '%Pro%'"
}
复杂的嵌套逻辑:
{
"sqlFilter": "(`Region` IN ('West', 'East') AND `Revenue` > 10000) OR `Priority` = 'Critical'"
}
针对特定数据集:
{
"sqlFilter": "`Status` = 'Active'",
"datasourceIds": [
"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"
]
}
组合标准筛选与 SQL 筛选
两者可以在同一授权中共存。标准筛选先应用,然后 SQL 筛选进一步收窄范围:
{
"token": "<embed_id>",
"permissions": ["READ", "FILTER", "EXPORT"],
"filters": [
{
"column": "Region",
"operator": "IN",
"values": ["West"]
}
],
"sqlFilters": [
{
"sqlFilter": "`Revenue` > 10000 OR `Priority` = 'Critical'"
}
]
}
常见模式
按用户筛选
将用户与筛选条件的映射存储在数据库中,在嵌入时进行查找:
// Pseudocode — adapt to your framework and data layer
async function getEmbedTokenForUser(userId: string, embedId: string) {
const user = await getUser(userId)
const dashboard = user.dashboards.find(d => d.embedId === embedId)
const filters = dashboard.filters.map(f => ({
column: f.column,
operator: f.operator,
values: f.values
}))
const accessToken = await getDomoAccessToken()
const embedToken = await getDomoEmbedToken(accessToken, embedId, filters)
return embedToken
}
基于角色的筛选
根据用户角色而非个人分配来应用不同的筛选条件:
function getFiltersForRole(role: string): Filter[] {
switch (role) {
case 'regional-manager':
return [{ column: 'Region', operator: 'IN', values: [user.region] }]
case 'executive':
return [] // No filters — sees everything
case 'analyst':
return [{ column: 'Department', operator: 'EQUALS', values: [user.department] }]
default:
return [{ column: 'Public', operator: 'EQUALS', values: ['true'] }]
}
}
数据集切换
在嵌入时将嵌入式仪表板的底层数据集重定向——适用于多租户架构,其中每个客户拥有自己具有相同 schema 的数据集。
工作原理
在授权对象中传入 datasetRedirects(原始数据集 ID → 目标数据集 ID):
{
"sessionLength": 1440,
"authorizations": [
{
"token": "<embed_id>",
"permissions": ["READ", "FILTER", "EXPORT"],
"filters": [],
"datasetRedirects": {
"original-dataset-uuid-1": "target-dataset-uuid-1",
"original-dataset-uuid-2": "target-dataset-uuid-2"
}
}
]
}
目标数据集必须与原始 schema 匹配(列名和类型)。
与筛选组合使用
重定向先应用,然后筛选针对替换后的数据集运行:
{
"sessionLength": 1440,
"authorizations": [
{
"token": "<embed_id>",
"permissions": ["READ", "FILTER", "EXPORT"],
"datasetRedirects": {
"a19d0ef1-ca31-4bfd-b168-018b93109671": "a19d0ef1-ca31-4bfd-b168-018b93109672"
},
"filters": [
{ "column": "Color", "operator": "IN", "values": ["Red"] },
{ "column": "Model", "operator": "IN", "values": ["Mountain", "Road", "Commuter"] }
]
}
]
}
按用户切换数据集
将每个租户映射到其数据集 ID:
function getDatasetRedirects(tenant: Tenant): Record<string, string> {
// The template dashboard was built on these "original" datasets
const templateDatasets = {
sales: 'aaaa-bbbb-cccc-dddd',
inventory: 'eeee-ffff-gggg-hhhh'
}
// Each tenant has their own copies
return {
[templateDatasets.sales]: tenant.salesDatasetId,
[templateDatasets.inventory]: tenant.inventoryDatasetId
}
}
重要说明
- Schema 必须匹配——列名和类型必须完全一致(卡片、筛选器和计算字段按名称引用)
- 可以部分重定向——可以重定向仪表板的全部、部分或不重定向任何数据集
- 兼容所有筛选类型——重定向先应用,然后是标准/SQL/PDP 筛选
注意事项与最佳实践
- JWT 大小限制(约 8KB): 较长的值列表会触及此限制。解决方案:在 Domo 中聚合列、使用 SQL 筛选,或通过
datasourceId定向进行拆分。 - 列名匹配: 要求拼写和大小写完全一致。如果不指定
datasourceId,筛选将应用于所有具有匹配列的数据集。 - 令牌刷新: 缓存访问令牌,在过期前刷新。按请求生成嵌入令牌。
- 空筛选:
[]表示不筛选(用户可以看到所有内容)。务必包含filters键——不要省略它。 - 筛选验证:
values必须是数组(即使是EQUALS也是如此)。数值必须是数字,而不是字符串。运算符名称必须精确(GREATER_THAN_EQUALS_TO,而不是GREATER_THAN_OR_EQUAL)。 - 安全性: 绝不要在客户端生成令牌。在服务端验证嵌入 ID 授权和筛选值。使用最小的
permissions(如果不需要,省略EXPORT)。
TypeScript 类型定义
type FilterOperator =
| 'IN'
| 'NOT_IN'
| 'EQUALS'
| 'NOT_EQUALS'
| 'GREATER_THAN'
| 'GREATER_THAN_EQUALS_TO'
| 'LESS_THAN'
| 'LESS_THAN_EQUALS_TO'
interface StandardFilter {
column: string
operator: FilterOperator
values: (string | number)[]
datasourceId?: string
}
interface SqlFilter {
sqlFilter: string
datasourceIds?: string[]
}
type EmbedPermission = 'READ' | 'FILTER' | 'EXPORT'
interface EmbedAuthorization {
token: string
permissions: EmbedPermission[]
filters: StandardFilter[]
sqlFilters?: SqlFilter[]
policies?: string[]
datasetRedirects?: Record<string, string> // originalDatasetId → targetDatasetId
}
interface EmbedTokenRequest {
sessionLength: number
authorizations: EmbedAuthorization[]
}
快速参考
阅读 references/api-endpoints.md 以获取编程式筛选中使用的 Domo API 端点的完整列表。