@concepttocloud/saiku-embed 3.18.2 → 3.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +166 -6
- package/package.json +1 -1
- package/saiku-embed.js +45 -35
- package/saiku-embed.js.map +1 -1
package/README.md
CHANGED
|
@@ -50,6 +50,91 @@ globally — no further setup.
|
|
|
50
50
|
></saiku-embed>
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
+
### A saved query, rendered as a hierarchical matrix (v3.19)
|
|
54
|
+
|
|
55
|
+
Matrix mode preserves the row / column axis structure — measures on
|
|
56
|
+
columns, dimension members on rows — instead of flattening to a single
|
|
57
|
+
row-key map like `render="table"` does. Useful for pivot-style reports.
|
|
58
|
+
|
|
59
|
+
```html
|
|
60
|
+
<saiku-embed
|
|
61
|
+
server="..."
|
|
62
|
+
token="..."
|
|
63
|
+
path="homes/admin/Examples/Sales.saiku"
|
|
64
|
+
render="matrix"
|
|
65
|
+
height="500px"
|
|
66
|
+
></saiku-embed>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### An AI ask widget over a cube (v3.19, `kind="ai"`)
|
|
70
|
+
|
|
71
|
+
Point the token at a cube (rather than a saved query) and drop in a
|
|
72
|
+
plain-English ask box. Behind the scenes it POSTs to
|
|
73
|
+
`/rest/saiku/api/embed/ai/{cubeId}/ask`, which runs the question through
|
|
74
|
+
the server's configured LLM provider under the pinned owner's data
|
|
75
|
+
scope. Requires an AI-kind token (see "Minting an AI token" below).
|
|
76
|
+
|
|
77
|
+
```html
|
|
78
|
+
<saiku-embed
|
|
79
|
+
server="..."
|
|
80
|
+
token="..."
|
|
81
|
+
kind="ai"
|
|
82
|
+
path="foodmart/FoodMart/FoodMart/Sales"
|
|
83
|
+
height="200px"
|
|
84
|
+
></saiku-embed>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### A single KPI tile (v3.20, `render="kpi"`)
|
|
88
|
+
|
|
89
|
+
The most common embed shape — one governed figure rendered large. Derives its
|
|
90
|
+
value from the same records response as `render="table"`, so it needs no server
|
|
91
|
+
change. Point it at a saved query whose last measure is the headline number;
|
|
92
|
+
when the query carries a prior measure column, a delta chip is shown.
|
|
93
|
+
|
|
94
|
+
```html
|
|
95
|
+
<saiku-embed
|
|
96
|
+
server="..."
|
|
97
|
+
token="..."
|
|
98
|
+
path="homes/admin/Examples/NetRevenue.saiku"
|
|
99
|
+
render="kpi"
|
|
100
|
+
height="160px"
|
|
101
|
+
></saiku-embed>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### A saved query, sliced at embed time (v3.20, `filter`)
|
|
105
|
+
|
|
106
|
+
Pass slicer overrides as a JSON array. They ride the same validated slicer path
|
|
107
|
+
the dashboard filter tiles use — the saved query's cube binding and axes are
|
|
108
|
+
untouched, so a host can parameterise an embed without re-authoring the query.
|
|
109
|
+
|
|
110
|
+
```html
|
|
111
|
+
<saiku-embed
|
|
112
|
+
server="..."
|
|
113
|
+
token="..."
|
|
114
|
+
path="homes/admin/Examples/Sales.saiku"
|
|
115
|
+
filter='[{"dimension":"Time","level":"Year","members":["[Time].[2024]"]}]'
|
|
116
|
+
></saiku-embed>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### A persona-scoped AI ask (v3.20, `space`)
|
|
120
|
+
|
|
121
|
+
Add a `space` to a `kind="ai"` embed to scope the assistant to an admin-authored
|
|
122
|
+
[Agent Space](../../../docs/AGENT-SPACES-SPEC.md) persona. The persona's system
|
|
123
|
+
prompt, skill filter, and cube allowlist apply server-side. The cube stays
|
|
124
|
+
pinned by the token, so a space can only **narrow** what the guest reaches — if
|
|
125
|
+
the space's allowlist excludes the pinned cube, the ask fails closed.
|
|
126
|
+
|
|
127
|
+
```html
|
|
128
|
+
<saiku-embed
|
|
129
|
+
server="..."
|
|
130
|
+
token="..."
|
|
131
|
+
kind="ai"
|
|
132
|
+
path="foodmart/FoodMart/FoodMart/Sales"
|
|
133
|
+
space="foodmart-sales-analyst"
|
|
134
|
+
height="240px"
|
|
135
|
+
></saiku-embed>
|
|
136
|
+
```
|
|
137
|
+
|
|
53
138
|
### A saved dashboard
|
|
54
139
|
|
|
55
140
|
```html
|
|
@@ -79,18 +164,43 @@ If the resource is marked publicly embeddable on the server
|
|
|
79
164
|
|
|
80
165
|
| Attribute | Default | Notes |
|
|
81
166
|
|-----------|-------------|------------------------------------------------------------------------|
|
|
82
|
-
| `server` | _(
|
|
83
|
-
| `path` | _(required)_|
|
|
84
|
-
| `kind` | `query` | `query` or `
|
|
167
|
+
| `server` | _(optional)_| Origin of the Saiku launcher, e.g. `https://demo.saiku.bi`. Leave empty for same-origin (v3.19+) |
|
|
168
|
+
| `path` | _(required)_| `kind=query`: saved query path (`.saiku`) — `kind=dashboard`: dashboard path (`.saikudash`) — `kind=ai`: cube ref `connection/catalog/schema/cubeName` |
|
|
169
|
+
| `kind` | `query` | `query`, `dashboard`, or `ai` |
|
|
85
170
|
| `token` | _(none)_ | Embed token from `POST /saiku/api/embed/tokens`. Omit for public reads |
|
|
86
|
-
| `render` | `table` | For `kind=query`: `table` or `
|
|
171
|
+
| `render` | `table` | For `kind=query`: `table`, `matrix`, `chart`, or `kpi` (v3.20) |
|
|
87
172
|
| `mode` | `bar` | For `render=chart`: `bar`, `line`, or `pie` |
|
|
88
173
|
| `height` | `400px` | CSS height of the rendered surface |
|
|
174
|
+
| `space` | _(none)_ | For `kind=ai`: Agent Space persona id — scopes the ask server-side (v3.20) |
|
|
175
|
+
| `filter` | _(none)_ | For `kind=query`: JSON array of slicer overrides applied at embed time (v3.20) |
|
|
176
|
+
| `theme` | _(light)_ | `light`, `dark`, or `auto` (follow `prefers-color-scheme`) (v3.20) |
|
|
89
177
|
|
|
90
178
|
The component re-renders whenever an attribute changes, so frameworks
|
|
91
179
|
binding state to attrs (React's JSX, Vue's `:server="..."`, etc.) just
|
|
92
180
|
work.
|
|
93
181
|
|
|
182
|
+
## Events (v3.20)
|
|
183
|
+
|
|
184
|
+
The element emits namespaced `CustomEvent`s so the host page can react to what
|
|
185
|
+
happens inside the embed. All bubble and are `composed`, so a listener on the
|
|
186
|
+
`<saiku-embed>` element receives them:
|
|
187
|
+
|
|
188
|
+
| Event | `detail` | Fires when |
|
|
189
|
+
|-------------------|-----------------------------------|-----------------------------------------|
|
|
190
|
+
| `saiku:load` | `{ kind, rows }` | a query / matrix / kpi surface loads |
|
|
191
|
+
| `saiku:error` | `{ message }` | a query load fails (friendly message) |
|
|
192
|
+
| `saiku:select` | `{ row }` | a table row is clicked (`render=table`) |
|
|
193
|
+
| `saiku:ai-query` | `{ question, degraded }` | an AI ask resolves (`kind=ai`) |
|
|
194
|
+
|
|
195
|
+
```js
|
|
196
|
+
const el = document.querySelector("saiku-embed");
|
|
197
|
+
el.addEventListener("saiku:load", (e) => console.log("loaded", e.detail.rows, "rows"));
|
|
198
|
+
el.addEventListener("saiku:select", (e) => showDetail(e.detail.row));
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
In React (via `@concepttocloud/saiku-embed-react`) the same events are exposed
|
|
202
|
+
as `onLoad` / `onError` / `onSelect` / `onAiQuery` callback props.
|
|
203
|
+
|
|
94
204
|
## Server-side: minting a token
|
|
95
205
|
|
|
96
206
|
Authenticated as a user who has GRANT on the saved query / dashboard:
|
|
@@ -108,6 +218,28 @@ curl -X POST 'https://YOUR-SAIKU/rest/saiku/api/embed/tokens' \
|
|
|
108
218
|
# → { "status": "OK", "token": "tx-...", "expiresAt": ... }
|
|
109
219
|
```
|
|
110
220
|
|
|
221
|
+
### Minting an AI token (v3.19)
|
|
222
|
+
|
|
223
|
+
For `kind="ai"` embeds. `resourcePath` is a cube ref rather than a
|
|
224
|
+
file path. Mint is admin-only for v1 (cube-level ACLs are a follow-up).
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
curl -X POST 'https://YOUR-SAIKU/rest/saiku/api/embed/tokens' \
|
|
228
|
+
-u admin:admin \
|
|
229
|
+
-H 'Content-Type: application/json' \
|
|
230
|
+
-d '{
|
|
231
|
+
"resourceKind": "ai",
|
|
232
|
+
"resourcePath": "foodmart/FoodMart/FoodMart/Sales",
|
|
233
|
+
"ttlHours": 72,
|
|
234
|
+
"label": "DimSum widget on marketing site"
|
|
235
|
+
}'
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Server-side, an AI ask requires the launcher to have an LLM provider
|
|
239
|
+
configured (`saiku.ai.ask.provider = anthropic | openai` plus the
|
|
240
|
+
matching API key). Without it, the widget renders a degraded message
|
|
241
|
+
rather than an error.
|
|
242
|
+
|
|
111
243
|
Paste the `token` into the host page's `<saiku-embed token="...">`.
|
|
112
244
|
Tokens are server-authoritative — revoke any time via:
|
|
113
245
|
|
|
@@ -144,8 +276,14 @@ curl -X DELETE 'https://YOUR-SAIKU/rest/saiku/api/embed/public?kind=query&path=s
|
|
|
144
276
|
|
|
145
277
|
The embed lives inside an
|
|
146
278
|
[open shadow root](https://developer.mozilla.org/docs/Web/API/ShadowRoot),
|
|
147
|
-
so host page CSS can't leak in and vice versa.
|
|
148
|
-
|
|
279
|
+
so host page CSS can't leak in and vice versa.
|
|
280
|
+
|
|
281
|
+
For a quick dark surface, set `theme="dark"` (or `theme="auto"` to follow the
|
|
282
|
+
viewer's `prefers-color-scheme`) — it swaps the whole palette without the host
|
|
283
|
+
having to set each variable (v3.20). Leaving `theme` unset keeps the original
|
|
284
|
+
light palette, so existing embeds are unchanged.
|
|
285
|
+
|
|
286
|
+
To fine-tune individual colours, set CSS variables on the host page:
|
|
149
287
|
|
|
150
288
|
```css
|
|
151
289
|
saiku-embed {
|
|
@@ -161,6 +299,28 @@ saiku-embed {
|
|
|
161
299
|
}
|
|
162
300
|
```
|
|
163
301
|
|
|
302
|
+
### Theming the chart itself
|
|
303
|
+
|
|
304
|
+
The variables above style the embed **chrome** (frame, header, table). To brand
|
|
305
|
+
the **chart series + axes**, set these (custom properties inherit through the
|
|
306
|
+
shadow boundary, so the canvas chart picks them up):
|
|
307
|
+
|
|
308
|
+
```css
|
|
309
|
+
saiku-embed {
|
|
310
|
+
/* Series colour cycle — set as many as you need, 1..8, contiguously.
|
|
311
|
+
Any unset → the chart falls back to the built-in palette. */
|
|
312
|
+
--saiku-embed-chart-1: #2563eb;
|
|
313
|
+
--saiku-embed-chart-2: #16a34a;
|
|
314
|
+
--saiku-embed-chart-3: #dc2626;
|
|
315
|
+
/* …up to --saiku-embed-chart-8 */
|
|
316
|
+
|
|
317
|
+
/* Axis labels / legend / titles use --saiku-embed-fg;
|
|
318
|
+
axis + split lines use --saiku-embed-muted (both shared with the chrome). */
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
An embed with none of these set renders exactly as before (ECharts defaults).
|
|
323
|
+
|
|
164
324
|
## Security model
|
|
165
325
|
|
|
166
326
|
- **Header-only token transport.** The token travels as
|