@jitto / analyze-embed
导游: GUIDE.md,将您从一无所有变成嵌入式仪表板。
在您自己的web应用程序中为您自己的登录用户显示Jitto仪表板,使用React或纯JavaScript。 每个用户只能看到Jitto的行规则允许的行;您的页面从不包含Jitto密码或秘密。
状态:0.4。 预计在1.0之前会有一些小的变化。每个版本都有哪些变化: CHANGELOG.md.
暂不可用:
filterChanged为你的应用设置的过滤器触发(source: 'host');更改用户 过滤栏中的make还没有报告。本自述文件中的其他所有内容都按照描述工作。
过滤器需要Jitto服务器列出它们。 通过
filters(或称setFilters)只有一次您的Jitto服务器 返回filter_bar在它象征性的回答中。在那之前,每个过滤器都被拒绝了filters结尾failed仪表板无法加载。在0.2.0中filters选项被忽略了。
安装
npm install @jitto/analyze-embed
一个包裹,两个入口 @jitto/analyze-embed (纯JavaScript)和 @jitto/analyze-embed/react (反应)。
两者都运行在相同的核心上,所以相同的选项给出相同的事件。包括TypeScript声明。
快速入门
- 从Jitto租户管理员那里获得三样东西: 租户的Jitto主机(例如)
https://example-tenant.jittocloud.com;-uat和-dev主机为其他环境),仪表板的id,并确认您的网站的来源(例如https://portal.client.example)是基于承租人的合法来源。起源是准确的:https://,主机,可选 端口。 - 安装:
npm install @jitto/analyze-embed. - 写
fetchToken(见 令牌):它要求Jitto使用已登录用户自己的仪表板令牌 从您的登录库访问令牌。 - 安装仪表板:
import { JittoDashboard } from '@jitto/analyze-embed/react';
export function DeskOverview() {
return (
<JittoDashboard
dashboardId="3f0c6a52-8d1e-4b7a-9c2f-5e8d71a4b6c0"
embedOrigin="https://example-tenant.jittocloud.com"
fetchToken={fetchToken} // see Tokens: it sends the user's own access token
style={{ height: 600 }}
/>
);
}
以测试用户身份打开该页。您将看到该用户的行。
反应: <JittoDashboard>
import { useRef } from 'react';
import { JittoDashboard } from '@jitto/analyze-embed/react';
import type { JittoDashboardHandle } from '@jitto/analyze-embed';
export function Report() {
const dashboard = useRef<JittoDashboardHandle>(null);
return (
<JittoDashboard
ref={dashboard}
dashboardId="3f0c6a52-8d1e-4b7a-9c2f-5e8d71a4b6c0"
embedOrigin="https://example-tenant.jittocloud.com"
fetchToken={fetchToken}
theme={{ mode: 'light', colorPrimary: '#0b7a75' }}
onLoaded={({ durationMs }) => console.log(`loaded in ${durationMs} ms`)}
onFailed={({ reason, message }) => console.warn(reason, message)}
onSizeChanged={({ height }) => console.log('frame height', height)}
className="report"
/>
);
}
| 道具 | 类型 | 要求 | 它是做什么的 |
|---|---|---|---|
dashboardId | string | 是 | 租户管理员提供给您的Jitto仪表板id。 |
embedOrigin | string | 是 | 您的租户的Jitto主机,作为来源(https://example-tenant.jittocloud.com). SDK帧只有这个原点后面跟着 /embed (API的 embed_url),只对这个起源说话。它不能从其他任何东西中获得宿主。 |
fetchToken | (dashboardId, signal) => Promise<EmbedTokenResponse> | 是 | 获取具有自己访问令牌的已登录用户的指示板令牌。看到 令牌. |
filters | Filters | 否 | 在第一次渲染之前应用的过滤器,按过滤器名称。被拒绝的人以 failed. 看到 过滤器. |
theme | ThemeTokens | 否 | 颜色、字体、浅色或深色。看到 主题. |
tokenDeadlineMs | number | 否 | 一个令牌请求的截止日期。默认的 20000. |
loadDeadlineMs | number | 否 | 之前框架可以保持沉默多久 failed 理性地燃烧 network。默认的 30000。未显示的挂载(如隐藏选项卡中的挂载)将等待,直到它显示出来。 |
retryDelaysMs | readonly number[] | 否 | 在每次重试可重试令牌失败(429,500,502,deadline, network)之前等待。默认的 [2000, 5000, 15000]。在山上, Retry-After 超过60秒没有等待; failed 火灾。 |
fetchDownload | (request, signal) => Promise<Response> | 否 | 已弃用并被忽略: download() 询问仪表板,仪表板在自己的会话上进行下载。已在下一个小版本中删除。 |
downloadDeadlineMs | number | 否 | 截止日期 download(),包括仪表板等待PDF。默认的 180000. |
onLoaded | (e) => void | 否 | 仪表板呈现。看到 事件. |
onFailed | (e) => void | 否 | 仪表板无法加载或停止工作。 |
onFilterChanged | (e) => void | 否 | 应用了应用程序设置的过滤器(source: 'host')。 |
onFilterRejected | (e) => void | 否 | 仪表板拒绝过滤来自 setFilters 或者改变了 filters 道具。没有任何应用。换道具这是唯一的报告。 |
onSizeChanged | (e) => void | 否 | 页面上仪表板框架的大小发生了变化。看到 大小. |
className | string | 否 | 容器元素的类。 |
style | Record<string, string | number> | 否 | 容器元素的样式。给它一个高度。 |
ref | Ref<JittoDashboardHandle> | 否 | 相同的手柄 mountJittoDashboard 的回报。 |
如何更改道具:
- 改变为
dashboardId或者embedOrigin重新安装仪表板。 theme是按值比较的。相等的值没有任何作用,因此内联对象是安全的。另一个值是 就地应用,如setTheme会。删除theme改变不了什么:一个主题可以替换,不能删除,所以 仪表板保持其当前外观。filters太按值比较了,键序被忽略了,所以呢filters={{ Ccy: userCcy }}是安全的内联。一个不同的 值被应用为setFilters会;拒绝电话onFilterRejected过滤器实际上会保留下来。删除filters改变不了什么;通过{}清除你的应用设置的每个过滤器。使用道具或手柄setFilters对于过滤器,不能两者都用。- 回调函数、令牌函数和截止日期在使用时被读取。新函数在下一次使用时生效, 没有重新安装。
该组件仅在浏览器中挂载。在服务器渲染的应用程序中,在客户端渲染它。
纯JavaScript: mountJittoDashboard
import { mountJittoDashboard } from '@jitto/analyze-embed';
const element = document.getElementById('dashboard');
if (!element) throw new Error('no #dashboard element on the page');
const dashboard = mountJittoDashboard(element, {
dashboardId: '3f0c6a52-8d1e-4b7a-9c2f-5e8d71a4b6c0',
embedOrigin: 'https://example-tenant.jittocloud.com',
fetchToken,
onFailed: ({ reason, message }) => console.warn(reason, message),
});
const stop = dashboard.on('loaded', ({ durationMs }) => console.log(`loaded in ${durationMs} ms`));
// Later, when your app's own controls change:
export function useDarkTheme() {
dashboard.setTheme({ mode: 'dark' });
}
// When your page or view goes away:
export function teardown() {
stop();
dashboard.unmount();
}
mountJittoDashboard(element, options) 使用与React props相同的选项和回调(除了 className,
style 和 ref)并返回句柄:
| 成员 | 它是做什么的 |
|---|---|
dashboardId | 仪表盘id显示了这个挂载。 |
setFilters(filters) | 替换你的应用设置的过滤器。返回一个承诺,一旦仪表板应用它们就会解析;然后 filterChanged 火灾与 source: 'host'. 看到 过滤器. |
setTheme(theme) | 替换适当的主题标记。帧不会重新加载。 |
download(format, options) | 将指示板下载为 pdf,或一个图表作为 csv (与 chartId),就像仪表板自己的下载菜单一样,过滤器在仪表板中生效。SDK询问仪表板,仪表板在自己的会话中下载;你的应用程序不发送请求。行是用户自己的。拒绝。 DownloadError。参见指南 添加下载按钮. |
on(event, handler) | 订阅一个事件。返回一个取消订阅的函数。 |
unmount() | 删除指示板并取消挂起的令牌请求和计时器。打两次电话是安全的。 |
这个 setFilters 承诺总能解决问题。它拒绝了 SetFiltersError 谁的 outcome 是:
deadline:内部没有应用过滤器loadDeadlineMs.failed理性地燃烧network和 安装结束。没有显示的仪表板(在隐藏的选项卡中)得到另一个loadDeadlineMs代替。ended山先结束了,或者已经结束了。replaced稍后setFilters电话是在这个应用之前打来的。
如果仪表板拒绝输入名称或值, setFilters 拒绝。 FilterError 相反,并没有应用任何东西。
它的 code 是 unknown_filter 或者 invalid_filter_value, filterNames 列出被拒绝的名字和
knownFilterNames 列出仪表板拥有的每个过滤器。 filterRejected 触发同样的错误。之前打过的电话
第一个令牌到达等待它:然后按调用顺序根据令牌的筛选器列表检查名称。
令牌
您的页面向Jitto请求一个带有登录用户自己的访问令牌的短期指示板令牌,即您的 登录库已经成立。该令牌是用户的身份:在后端没有令牌端点,没有 共享帐户,没有秘密,您的代码从不说明用户是谁或他们可能看到什么。
-
SDK调用您的
fetchToken(dashboardId, signal)挂载一次,并在令牌生命周期的80%(最多)时再次挂载一次 大约24.8天后,这是浏览器计时器所能保持的最长延迟)。 -
你的
fetchToken从浏览器向Jitto发送用户自己的访问令牌:POST /api/v1/embed-tokens HTTP/1.1 Host: example-tenant.jittocloud.com Origin: https://portal.client.example Authorization: Bearer <the user's own access token> Content-Type: application/json {"dashboard_id": "3f0c6a52-8d1e-4b7a-9c2f-5e8d71a4b6c0"} -
Jitto根据您的租户的身份提供程序(发行者、签名、受众、到期)检查令牌,读取 用户组,并应用租户管理员存储在Jitto中的行规则。它检查
Origin针对您的租户允许的来源,并仅针对这些来源回答浏览器的CORS预飞行。 -
Jitto用一个能活5分钟的仪表盘代币来回答。答案是
Cache-Control: no-store:{ "token": "<opaque>", "dashboard_id": "3f0c6a52-8d1e-4b7a-9c2f-5e8d71a4b6c0", "embed_url": "https://example-tenant.jittocloud.com/embed", "expires_in": 300 } -
你的
fetchToken将它与SDK的字段名一起交给SDK (dashboardId,embedUrl,expiresIn,filterBar当答案filter_bar(仪表板的过滤器),值不变。SDK将检查答案 它自己,用理智拒绝错误的人token.
当出现错误时,Jitto用HTTP状态和问题体(code, correlation_id),以及 Retry-After
在429路上。对于其他受众或来自其他发行者的过期令牌,将使用401拒绝
unauthenticated (原因 token);没有行规则读取的组中的用户得到403 no_group (原因 permission)。
您的租户管理员向Jitto注册您的身份提供程序一次。
你的 fetchToken 拒绝带有状态和API的HTTP错误 code。这段代码是用来测试的
401、403、422、429、502和错误的答案。 getAccessToken() 您的登录库是:
async function fetchToken(dashboardId, signal) {
const r = await fetch('https://example-tenant.jittocloud.com/api/v1/embed-tokens', {
method: 'POST',
signal,
cache: 'no-store',
headers: {
Authorization: `Bearer ${await getAccessToken()}`, // the signed-in user's own token, from your sign-in library
'Content-Type': 'application/json',
},
body: JSON.stringify({ dashboard_id: dashboardId }), // nothing else: Jitto reads who the user is from the token
});
const body = await r.json().catch(() => null);
if (!r.ok) {
const retryAfter = Number(r.headers.get('Retry-After'));
throw Object.assign(new Error(`token request failed with HTTP ${r.status}`), {
status: r.status,
code: body?.code,
correlationId: body?.correlation_id,
retryAfterSeconds: retryAfter > 0 ? retryAfter : undefined,
});
}
if (!body || typeof body !== 'object') return body; // the SDK refuses it with reason `token`
const answer = { token: body.token, dashboardId: body.dashboard_id, embedUrl: body.embed_url };
if ('expires_in' in body) answer.expiresIn = body.expires_in;
if ('filter_bar' in body) answer.filterBar = body.filter_bar; // the dashboard's filters; without it, filters are refused
return answer; // the SDK checks it and refuses a wrong answer with reason `token`
}
事件
订阅 on… Props或回调,或with handle.on(name, handler).
| 事件 | 回调 | 有效载荷 | 当 |
|---|---|---|---|
loaded | onLoaded | { dashboardId, durationMs } | 第一次挂载后渲染的仪表板。 |
failed | onFailed | { dashboardId, reason, message, status?, code?, correlationId? } | 指示板无法加载,令牌刷新失败,或者 setFilters 重新加载没有及时渲染。每座坐骑最多发一次火;然后挂载完成,SDK在其位置显示一条简短的Jitto消息。重新挂载以重试。 |
filterChanged | onFilterChanged | { dashboardId, filters, source } | 过滤器的效果改变了:每次应用一次 setFilters 电话或 filters 道具变更 source: 'host'. 'user' (过滤栏的更改)尚未报告。 |
filterRejected | onFilterRejected | { dashboardId, error } | 仪表板拒绝过滤来自 setFilters 或者改变了 filters 道具。 error 是 FilterError 电话拒绝了。没有应用任何内容,仪表板也没有重新加载。 |
sizeChanged | onSizeChanged | { dashboardId, width, height } | 页面上仪表板框架的大小发生了变化。看到 大小. |
reason 是其中之一 token, permission, network 和 not_found.
大小
给容器一个高度,比如窗口的高度减去页面的页眉。仪表板框架采用 仪表盘自身的高度:
- 永远不要比容器高:一个更高的仪表板在框架内滚动,所以它的过滤条,信息和 对话框留在屏幕上;
- 永远不要短于容器或600px,以较短者为准,这样较短的仪表板可以为它的过滤条保留空间 和对话框。在那下面,框架结束在仪表板做。
sizeChanged 报告页面上框架的大小。您可以调整容器的大小,这样在a下面就没有空间了
短仪表板;这永远不会使框架比容器高。
过滤器
过滤器设置仪表板首先显示的内容,根据其过滤栏使用的名称(例如 Ccy,
Trade Date & Time),没错,包括案例在内。
- 把它们放在mount上
filters,然后是setFilters(或者是新的filters道具)。 - 名称与令牌一起出现:Jitto的回答列出了仪表板的过滤器
filter_bar,每一个name和一个kind,你呢?fetchToken将其作为filterBar。没有它,每个过滤器都被拒绝。 - 只向具有过滤器的仪表板发送过滤器。指示板缺少的名称将使用
FilterError等等 安装仪表板不加载;它永远不会被忽略,因此仪表板永远不会显示未经过滤的数据。 - 一个值是一个字符串,数字或布尔值,一个列表(几个值),一个日期范围
{ from, to }(两天写好)YYYY-MM-DD,例如2026-06-30,from之前或之前to(两者都包括在内),或者null清除过滤器。 过滤器接受的值取决于它的值kind:多项选择过滤器上的值列表,正好是两个 数字[min, max]在数字范围上,日期范围仅在日期范围过滤器上。 - 列表最多包含1000个值,字符串最多包含256个字符,这是仪表板和Jitto的限制
导出API,因此
download()从来没有拒绝过他们。超过它们的价值是被拒绝的invalid_filter_value, 设置超过50个过滤器的调用也是如此。不能设置名称大于255个字符的过滤器。 setFilters替换你的应用设置的每个过滤器:你遗漏的名称会回到仪表板自己的默认值。- 过滤器按名称在仪表板的地址中传播,一旦编码,该地址最多容纳4096字节。一个
需要更多的电话(多项选择过滤器上的长列表)被立即拒绝
invalid_filter_value, 单独为过滤器命名太长(名称和值),否则为其列表值过滤器命名,否则为每个过滤器命名。 - 与任何地址一样,仪表板可以出现在Jitto主机的访问日志中,包括过滤器名称和值。 传递用户选择的值(货币、日期),而不是秘密或个人数据。
download()在仪表板中给出过滤器生效的文件:那些是你的应用程序设置的,以及用户自己的 过滤器条的变化,因为。- 过滤器缩小了视图。它们不是安全保障。 用户可以在过滤栏中更改它们。用户可能会看到什么 只能由日本柔道的排位规则来决定。
- 今天
setFilters重新加载框架内的仪表板。您的页面保持挂载状态,并且没有请求新的令牌, 但是用户自己的过滤条会改变,滚动条的位置会重置。如果可以的话,在mount上设置过滤器。
主题
主题令牌可以改变颜色、字体和浅色或深色。Jitto保持布局;没有CSS钩子。
| 令牌 | 它改变了什么 |
|---|---|
mode | 'light' 或者 'dark'. |
colorPrimary | 按钮,选择的状态,链接。 |
colorBackground | 仪表板的页面背景。 |
colorSurface | 图表和过滤卡背景。 |
colorText | 正文。 |
colorBorder | 边界和分隔物。 |
fontFamily | 一个CSS font-family 列表。该字体必须已经可以被仪表板主机加载。 |
颜色是CSS颜色字符串(#1a73e8, rgb(…)). 其他任何内容都会被控制台警告忽略。 setTheme
永远不要重新加载框架。
错误和故障排除
每一次失败都是一次失败 failed 事件有以下四个原因之一。 status, code 和 correlationId 来自
当API回答时;报价的 correlationId 当你向吉托求助时。
| 原因 | 常见的原因 | 该怎么做 |
|---|---|---|
token | 用户的令牌被拒绝(401 unauthenticated(过期、受众错误、发行方错误);令牌请求太多(429) rate_limited,重试);里面没有令牌 tokenDeadlineMs;另一个仪表盘的答案或者 embedUrl 那不是你的 embedOrigin 紧随其后的是 /embed. | 检查用户是否已登录,您的登录库是否返回当前令牌。检查 embedOrigin。每次页面视图请求一个令牌,并让SDK刷新。 |
permission | 仪表板不在租户的可嵌入列表中(403) not_embeddable);你的原产地是不允许的 origin_not_allowed);用户不属于任何组,行规则读取(403) no_group);仪表板的数据集没有行规则(422) row_rules_incomplete);嵌入未打开(503) embedding_not_configured)。 | 请您的租户管理员修复设置 code 的名字。 |
not_found | 没有这个id的仪表盘(404) unknown_dashboard)。 | 检查 dashboardId. |
network | 没有应答,服务器错误(500,502,重试第一次),拒绝CORS预飞行,或帧保持沉默 loadDeadlineMs。令牌请求上的CORS错误,或者 frame-ancestors 错误,在浏览器控制台中表示不允许页面的起源。 | 检查网络。对于CORS或 frame-ancestors 如果出现错误,则要求按照浏览器显示的方式添加您的原点(localhost 和 127.0.0.1 不同,端口也不同)。 |
其他症状:
| 你看到 | 导致 | 修复 |
|---|---|---|
| 仪表板加载时没有行 | 用户组不匹配行规则 | 询问您的租户管理员要读取哪些组的行规则。 |
| 没有错误的空白区域 | 容器没有高度 | 把容器(style 或者 className)高度。 |
| 您自己的CSP会阻止帧或令牌请求 | 你的 frame-src 或者 connect-src 不允许房客的Jitto主机 | 添加你 embedOrigin (例如 https://example-tenant.jittocloud.com)。 frame-src 和 connect-src. |
支持的浏览器和框架
建议1.0.0版本;端到端测试在发布前确认列表。
- 浏览器: Chrome、Edge、Firefox和Safari当前和以前的主要版本。
页面必须被送达
https://,包括本地发展(例如https://localhost:5173)。 - 反应: 18和19,通过
@jitto/analyze-embed/react. - 一切 (Angular, Vue,服务器端渲染页面,没有框架):使用
mountJittoDashboard从@jitto/analyze-embed。专用的包装器可能遵循相同的核心。 - 打字稿: 包括声明。每个选项、事件和有效负载都被键入。
版本控制
- 软件包如下 语义版本控制。对选项、事件、有效负载或 故障原因是新主要版本。
- 包的主版本与嵌入API的路径版本匹配:
@jitto/analyze-embed1.X与/api/v1/只有。附加的API更改保留在v1中。 - 已弃用的API版本回答为
Deprecation,Sunset和Link头,至少90天之前,它是 移除。
完整的指南和API参考
- 嵌入指南, GUIDE.md 在这个包中:整个流程,租户管理员的设置, 还有一份安全检查清单。
- API引用,由嵌入API的OpenAPI 3.1契约生成。您的Jitto联系人提供了您的 租户。
两者都在1.0.0之前移到Jitto文档站点,然后本节链接到那里。