跳到主要内容

仪表板嵌入

准备

在开始嵌入仪表板之前,你需要进行一些配置。
首先,你需要在superset配置文件中启用嵌入功能,并使过滤条水平放置效果会更好些。
然后,确保嵌入配置项GUEST_ROLE_NAME = "Public"中的角色具有embedded的权限,请到superset中配置该角色的权限,为GUEST_TOKEN_JWT_SECRET设置一个复杂的密码。

FEATURE_FLAGS = {
"EMBEDDED_SUPERSET": True, # 启用嵌入功能
"HORIZONTAL_FILTER_BAR": True, # 使过滤条能够水平放置
}
GUEST_ROLE_NAME = "Public"
GUEST_TOKEN_JWT_SECRET = 'your-secret' # 你的密码,可以使用openssl rand -base64 42 生成

重启superset使配置生效,打开你想要嵌入的看板并点开仪表板的功能按钮,选择嵌入仪表板,你会看到下面的内容。


填入允许嵌入看板的域名,如果设置为空,则允许看板嵌入到任何目标中。完成后点击 启用嵌入(ENABLE EMBEDDING) ,然后superset会为你的看板生成一个唯一的UUID,后续你可以通过这个UUID嵌入看板到你的应用。


嵌入到应用

在前面superset嵌入看板的准备工作已经做好,这一步开始着手编写代码。仪表板嵌入过程组织为以下三个步骤:

  1. 定义一个提供访客令牌的接口
  2. 安装superset嵌入式 SDK
  3. 使用 SDK 嵌入仪表板

以下是下面教程中的示例代码

1.定义一个提供访客令牌的接口

后端通过调用superset的登录接口获取access_token,然后使用access_token调用/api/v1/security/guest_token/接口获取访客令牌。

注意

访客令牌创建只能在受信任的后端执行上下文中完成。请勿暴露你的superset API 密钥或访问令牌

下面是一个简单示例:

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import requests

app = FastAPI()

app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

base_url = 'http://127.0.0.1:8088'

@app.get("/fetchGuestToken")
def fetch_token():
url = f'{base_url}/api/v1/security/guest_token/'
access_token, refresh_token = get_access_token()
headers = {"Authorization": f'Bearer {access_token}'}
body = {
"resources": [
{
"id": "132d5a9f-cce6-4of7-a850-f881a3ee5741", # 仪表盘id或uuid
"type": "dashboard"
}
],
"rls": [
],
"user": {
"first_name": "Daniel",
"last_name": "Moraes",
"username": "dmoraes"
}
}
response = requests.post(url=url, json=body, headers=headers)
token = response.json()
return token

def get_access_token():
url = f'{base_url}/api/v1/security/login'
body = {
"username": "admin",
"password": "admin",
"provider": "db",
"refresh": True
}
response = requests.post(url=url, json=body)
tokens = response.json()
return tokens["access_token"], tokens["refresh_token"]

上面的接口在实际生产情况中应该使用发起当前请求的用户信息去调用superset接口,必要的话还需要加入权限判断。


/fetchGuestToken接口中使用的字段的解释:

  • user(必填):查看仪表板的用户的个人资料信息。此username字段对于每个用户应是唯一的。
  • resources(必填):用户被允许查看的仪表板列表(允许多个仪表板ID)。
  • rls(必需):指定适用于该令牌持有者的行级安全规则列表。行级安全规则会在查询该用户的数据集时(如查看图表时)应用额外的 WHERE 子句。 你需要了解:
    • dataset: 如果只想将规则应用于某个数据集,则可选择数据集:id 参数。
    • 示例子句:"publisher = 'Nintendo'""location_id = {warehouse.id}""NOT events.is_secret"team_id = {team_id}
    • 如果列出多条规则,则在 SQL 中使用 AND 将它们连接起来。如果要使用 OR 或类似子句,应将其括入引号中.
    • 可在 RLS 子句中使用 Jinja 模板来进一步定制。
警告

如果没有数据集参数,规则将应用于所有数据集。这样做非常方便,但要注意在引用支持仪表盘的数据集中不存在的列时可能会引入错误。 不要在 RLS 条款中插入不可信任的输入(例如,通过表单或外部服务提交给应用程序的信息)。这将使您的分析数据库遭受 SQL 注入攻击。

以下是一个完整示例:

{
"user": { # 用户信息。确保每个用户使用唯一的 “用户名”
"username": "admin",
"first_name": "user",
"last_name": "admin"
},
"resources": [ # 指定用户应能访问的仪表板
{ "type": "dashboard", "id": "7dc26b42-fd38-4965-8f60-e156ae233f6d" },
{ "type": "dashboard", "id": "d12678ae-b001-4f97-9822-a6bdf893e97c" }
],
"rls": [ # 应用的 RLS 规则
{ "clause": "username = 'grace_hopper'" }, # 这条规则应用于所有数据集
{ "dataset": 16, "clause": "environment = 'production'" },
{ "dataset": 42, "clause": "state = 'published'" }
]
}

2.安装superset嵌入式 SDK

使用 npm:

npm install --save @superset-ui/embedded-sdk

你还可以从 CDN 加载嵌入式 SDK。该 SDK 在全球范围内提供:

<script src="https://unpkg.com/@superset-ui/embedded-sdk"></script>

3.使用 SDK 嵌入仪表板

使用ES6导入或构建工具时:

import { embedDashboard } from "@superset-ui/embedded-sdk";

embedDashboard({
id: "132d5a9f-cce6-4of7-a850-f881a3ee5741", // 准备阶段生成的UUID
supersetDomain: "https://superset.example.com", // 你的superset域名
mountPoint: document.getElementById("my-superset-container"), // 仪表板挂载点
fetchGuestToken: () => fetchGuestTokenFromBackend(),
dashboardUiConfig: { // 仪表板UI配置: hideTitle, hideTab, hideChartControls, filters.visible, filters.expanded (可选), urlParams (可选)
hideTitle: true,
filters: {
expanded: true,
},
urlParams: {
foo: 'value1',
bar: 'value2',
// ...
}
},
// 额外的 iframe sandbox 属性
iframeSandboxExtras: ['allow-top-navigation', 'allow-popups-to-escape-sandbox']
});

使用CDN时:

<script>
supersetEmbeddedSdk.embedDashboard({
// ... 这些参数与上面提供的参数一样
});
</script>

在上面的代码中fetchGuestTokenFromBackend方法是需要你去实现的,它是一个异步函数,这个方法的作用是向后端请求访客令牌,然后将访客令牌返回。可以参考下面代码:

async function fetchGuestTokenFromBackend(){
const response = await fetch('/fetchGuestToken');
const {token} = await response.json();
return token;
}

embedDashboard方法返回一个对象,其中包含一些回调函数,你可以通过它获取一些仪表板的信息。

{
getScrollSize,
unmount,
getDashboardPermalink,
getActiveTabs,
}
  • getScrollSize:用来获取仪表板实际内容的宽和高。
  • unmount:卸载嵌入的仪表板
  • getDashboardPermalink:仪表板永久链接
  • getActiveTabs:获取当前页面激活的标签页

调整iframe窗口大小
通过getScrollSize()方法异步返回嵌入仪表板页面的完整宽度和高度,可以适当调整内容窗口的大小,让嵌入的仪表板看起来更加无缝。

const myDashboard = await embedDashboard({...});

setInterval(async () => {
const { width, height } = await myDashboard.getScrollSize();
setCss(` // 这是一个虚构的方法,用来设置iframe的大小
.embedded-superset iframe {
width: ${width};
height: ${height};
}
`);
}, 1000);