mcpField
P
programmatic-filters

programmatic-filters

github.com/stahura安全24191 次安装26 周前更新
代码仓库

关于 programmatic-filters

programmatic-filters 是通过服务器端筛选和数据集切换保护嵌入式 Domo 内容的技能,指导开发者生成针对不同查看者的嵌入令牌,由 Domo 强制执行数据访问限制。

主要功能

  1. 01生成针对查看者的嵌入令牌
  2. 02应用标准筛选和 SQL 筛选
  3. 03切换嵌入内容所用数据集
  4. 04支持按用户和角色筛选
  5. 05在服务器端处理身份验证和令牌生成

适用场景

  1. 01按查看者或角色限制嵌入式仪表板数据
  2. 02对嵌入数据应用复杂 SQL 条件
  3. 03使用共享仪表板提供租户专属数据集
  4. 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。

工作原理

一个代理用户(服务账号)代表所有查看者执行操作。你的服务端:

  1. 通过 Domo 进行身份验证(OAuth 客户端凭据 → access token)
  2. 请求带有查看者专属筛选器的 embed token
  3. 将 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 ID
  • permissions:授予的权限数组 — READ、FILTER、EXPORT
  • filters:标准筛选器对象数组(见下文)
  • 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"]
}

必需属性:

属性类型描述
columnstring数据集中的确切列名
operatorstring比较运算符(见下表)
valuesarray用于筛选的值

可选属性:

属性类型描述
datasourceIdstring (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"]
        }
      ]
    }
  ]
}

必需属性:

属性类型描述
sqlFilterstringSQL WHERE 子句语法

可选属性:

属性类型描述
datasourceIdsstring[]要应用该筛选器的数据集 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 端点的完整列表。

评论

支持 Markdown