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 クライアント認証情報 → アクセストークン)
  2. 閲覧者ごとのフィルターを指定して埋め込みトークンをリクエストします
  3. トークンをクライアントに返し、クライアントが iframe に POST します

前提条件

  • CLIENT_ID と CLIENT_SECRET を持つ Domo API クライアント(developer.domo.com > My Account > New Client)
  • 埋め込み ID(埋め込みダイアログに表示される 5 文字の ID。ページ URL ではありません)
  • フィルタリングに使うデータセットの列名/ID

すべての認証処理はサーバー側で行う必要があります(CORS の制限があるため)。クライアント側に認証情報を公開しないでください。

埋め込みトークンのフロー

ステップ 1:アクセストークンを取得する
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:フィルター付きの埋め込みトークンをリクエストする

アクセストークンを使って埋め込みトークンをリクエストし、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:トークンの有効期間(分単位。1440 = 24 時間)
  • token:ダッシュボードまたはカードの埋め込み ID
  • permissions:付与する権限の配列 — READ、FILTER、EXPORT
  • filters:標準フィルターオブジェクトの配列(下記参照)
  • policies:適用する PDP ポリシー ID の配列(任意)

レスポンスには、埋め込みトークンを含む authentication プロパティが含まれます。

ステップ 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 送信により、URL やブラウザーの履歴にトークンが表示されるのを防げます。


フィルターの種類

フィルターには、埋め込みトークンのリクエストで使用する標準フィルターとSQL フィルターの 2 種類があります。

標準フィルター
{
  "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列の値が指定値以下数値/日付の範囲
標準フィルターの例

単一のフィルター — 西部地域のみを表示:

{
  "filters": [
    {
      "column": "Region",
      "operator": "IN",
      "values": ["West"]
    }
  ]
}

複数のフィルター — 西部地域かつ売上が 10000 を超えるデータを表示:

{
  "filters": [
    {
      "column": "Region",
      "operator": "IN",
      "values": ["West"]
    },
    {
      "column": "Revenue",
      "operator": "GREATER_THAN",
      "values": [10000]
    }
  ]
}

データセットを指定したフィルター — 複数のデータセットを含むダッシュボード内の 1 つのデータセットのみに適用:

{
  "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 フィルターの組み合わせ

1 つの認可で両方を併用できます。標準フィルターが先に適用され、その後 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'] }]
  }
}

データセットの切り替え

埋め込み時に埋め込みダッシュボードの基になるデータセットを切り替えます。これは、各顧客が同じスキーマの独自データセットを持つマルチテナントアーキテクチャで役立ちます。

仕組み

認可オブジェクトで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"
      }
    }
  ]
}

対象のデータセットは、元のデータセットと同じスキーマ(列名と型)である必要があります。

フィルターとの組み合わせ

リダイレクトが先に適用され、その後、切り替え後のデータセットに対してフィルターが実行されます。

{
  "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
  }
}
重要な注意事項
  • スキーマは一致している必要があります — 列名と型は完全に同一である必要があります(カード、フィルター、計算フィールドは名前で参照されます)
  • 一部のリダイレクトでも問題ありません — ダッシュボードのデータセットすべて、一部、またはどれもリダイレクトしないように設定できます
  • すべてのフィルタータイプと併用できます — リダイレクトが先に適用され、その後、標準/SQL/PDP フィルターが適用されます

注意点とベストプラクティス

  • JWT のサイズ制限(約 8 KB): 値のリストが長いと制限に達します。解決策:Domo で列を集約する、SQL フィルターを使用する、またはdatasourceIdで対象を分割する。
  • 列名の一致: 大文字と小文字を含め、正確に一致させる必要があります。datasourceIdがない場合、フィルターは一致する列を持つすべてのデータセットに適用されます。
  • トークンの更新: アクセストークンをキャッシュし、有効期限前に更新します。埋め込みトークンはリクエストごとに生成します。
  • 空のフィルター: []はフィルタリングなしを意味します(ユーザーにはすべてが表示されます)。必ずfiltersキーを含め、省略しないでください。
  • フィルターの検証: valuesは(EQUALSの場合も)配列である必要があります。数値は文字列ではなく数値として指定します。演算子名は正確に指定してください(GREATER_THAN_OR_EQUALではなくGREATER_THAN_EQUALS_TO)。
  • セキュリティ: クライアント側でトークンを生成しないでください。埋め込み 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[]
}

クイックリファレンス

プログラムによるフィルタリングで使用する Domo API エンドポイントの完全な一覧は、references/api-endpoints.mdを参照してください。

コメント