Aller au contenu

Intégration de Saiku

Le Web Component <saiku-embed/> permet d’intégrer une requête Saiku enregistrée ou un dashboard dans n’importe quelle page HTML — votre site marketing, un article de blog, un portail client, un wiki, ou un dashboard React / Vue. Le même élément personnalisé fonctionne dans tous les hôtes : JSX React, templates Vue, Svelte, HTML pur.

C’est un véritable Web Component, pas un wrapper de framework, donc la page hôte n’a besoin de rien savoir sur les rouages internes de Saiku.

Installation

Deux façons équivalentes de charger le bundle. Choisissez celle qui convient à votre page hôte.

Aucune étape de build — pointez une balise <script> vers le bundle que votre Saiku sert déjà :

<script src="https://YOUR-WORKSPACE.saiku.bi/ui/saiku-embed.js"></script>

Le téléchargement pèse environ 213 Ko gzippés. Les navigateurs le mettent en cache, donc la deuxième page sur la même origine l’obtient instantanément.

Un exemple concret

L’intégration minimale viable — une requête enregistrée, rendue sous forme de tableau :

<saiku-embed
server="https://YOUR-WORKSPACE.saiku.bi"
token="tx-..."
path="homes/admin/Examples/Trend.saiku"
height="400px"
></saiku-embed>

Les trois attributs porteurs sont server (origine de votre Saiku), path (chemin de dépôt de la requête enregistrée) et token (le jeton d’intégration — voir Création d’un jeton ci-dessous). Tout le reste a des valeurs par défaut sensées.

Création d’un jeton

Les jetons sont créés par un utilisateur authentifié qui peut lire la ressource. Le jeton est opaque et limité : il accorde un accès en lecture seule à une requête enregistrée ou un dashboard, expire après le TTL que vous spécifiez, et peut être révoqué à tout moment.

Mint a token
curl -X POST 'https://YOUR-WORKSPACE.saiku.bi/rest/saiku/api/embed/tokens' \
-u admin:admin \
-H 'Content-Type: application/json' \
-d '{
"resourceKind": "query",
"resourcePath": "homes/admin/Examples/Trend.saiku",
"ttlHours": 72,
"label": "Marketing landing page"
}'
# Response:
# {
# "status": "OK",
# "token": "tx-...",
# "resourceKind": "query",
# "resourcePath": "homes/admin/Examples/Trend.saiku",
# "expiresAt": 1739102400000
# }

Collez le token dans <saiku-embed token="..."> de votre page hôte.

Bornes du TTL. Par défaut 72 heures. Maximum 30 jours (720 heures). Un TTL court est la valeur par défaut la plus sûre pour un jeton qui traverse des pages hôtes arbitraires.

Limite par utilisateur. Chaque utilisateur peut détenir jusqu’à 200 jetons actifs à la fois. Révoquez les jetons inutilisés pour libérer du budget.

Lister vos jetons

Fenêtre de terminal
curl 'https://YOUR-WORKSPACE.saiku.bi/rest/saiku/api/embed/tokens' \
-u admin:admin

Les admins peuvent lister tous les jetons du système avec ?all=true.

Révoquer un jeton

Fenêtre de terminal
curl -X DELETE 'https://YOUR-WORKSPACE.saiku.bi/rest/saiku/api/embed/tokens/<token>' \
-u admin:admin

La révocation prend effet à la toute prochaine requête — il n’y a pas de session invitée à détourner.

Intégrations publiques (anonymes)

Si une ressource doit être lisible sans jeton — un graphique public sur votre page d’accueil, un dashboard ouvert sur un blog — basculez-la en intégration publique :

Fenêtre de terminal
curl -X POST 'https://YOUR-WORKSPACE.saiku.bi/rest/saiku/api/embed/public' \
-u admin:admin \
-H 'Content-Type: application/json' \
-d '{
"resourceKind": "query",
"resourcePath": "shared/public-chart.saiku",
"label": "Homepage chart"
}'

La page hôte omet alors complètement le jeton :

<saiku-embed
server="https://YOUR-WORKSPACE.saiku.bi"
path="shared/public-chart.saiku"
render="chart"
></saiku-embed>

Lister + révoquer les autorisations publiques

Fenêtre de terminal
# List your public grants
curl 'https://YOUR-WORKSPACE.saiku.bi/rest/saiku/api/embed/public' \
-u admin:admin
# Revoke
curl -X DELETE 'https://YOUR-WORKSPACE.saiku.bi/rest/saiku/api/embed/public?kind=query&path=shared/public-chart.saiku' \
-u admin:admin

Référence des attributs

AttributDéfautNotes
serverrequisOrigine de votre launcher Saiku, par ex. https://YOUR-WORKSPACE.saiku.bi
pathrequisChemin de dépôt. Se termine par .saiku pour les requêtes, .saikudash pour les dashboards
kindqueryquery ou dashboard
token(aucun)Jeton d’intégration de POST /saiku/api/embed/tokens. Omettre pour les lectures publiques anonymes
rendertablePour kind="query" : table ou chart
modebarPour render="chart" : bar, line ou pie
height400pxHauteur CSS (une min-height sur la surface rendue)

Les attributs sont réactifs — le composant se re-rend dès que l’un d’eux change. Dans React, Vue ou Svelte, lier un état à ces props fonctionne directement.

Exemples

Requête enregistrée sous forme de tableau

<saiku-embed
server="https://YOUR-WORKSPACE.saiku.bi"
token="tx-..."
path="homes/admin/Examples/Trend.saiku"
></saiku-embed>

Requête enregistrée sous forme de graphique

<saiku-embed
server="https://YOUR-WORKSPACE.saiku.bi"
token="tx-..."
path="homes/admin/Examples/Sales.saiku"
render="chart"
mode="bar"
height="500px"
></saiku-embed>

Modes de graphique :

  • bar (par défaut) — barres catégorielles ; la première colonne non numérique est la catégorie, chaque colonne numérique devient sa propre série
  • line — même disposition, séries en ligne
  • pie — première colonne numérique seulement ; retombe sur « No numeric series » si le résultat est uniquement textuel

Les info-bulles affichent les chaînes de cellule formatées par le serveur ($1,234, 12.3%, etc.) afin que la page hôte voie les mêmes légendes que le workbench Saiku.

Dashboard enregistré

<saiku-embed
server="https://YOUR-WORKSPACE.saiku.bi"
token="tx-..."
kind="dashboard"
path="homes/admin/exec.saikudash"
height="700px"
></saiku-embed>

Le composant récupère la mise en page du dashboard une fois, puis exécute une requête par tuile pour chaque graphique / KPI en parallèle sous la portée de données du propriétaire du dashboard. Types de tuiles en v1 :

Type de tuileCe qui est rendu
textLe texte de la tuile, comme un paragraphe
chartUn graphique ECharts (utilise tile.chartType comme mode)
kpiUn grand chiffre unique issu de la mesure de la tuile
filterIgnorée — les widgets de filtre sont réservés au workbench en v1
autrePlaceholder « Unsupported tile »

Intégration publique anonyme

Lorsque la ressource a une autorisation publique, omettez le jeton :

<saiku-embed
server="https://YOUR-WORKSPACE.saiku.bi"
path="shared/public.saiku"
render="chart"
mode="pie"
></saiku-embed>

Dans React

import "@concepttocloud/saiku-embed";
export function SalesEmbed({ token }: { token: string }) {
return (
<saiku-embed
server="https://YOUR-WORKSPACE.saiku.bi"
token={token}
path="homes/admin/Examples/Sales.saiku"
render="chart"
mode="line"
height="500px"
/>
);
}

Dans Vue

<template>
<saiku-embed
server="https://YOUR-WORKSPACE.saiku.bi"
:token="token"
path="homes/admin/Examples/Sales.saiku"
render="chart"
/>
</template>
<script setup lang="ts">
import "@concepttocloud/saiku-embed";
defineProps<{ token: string }>();
</script>

Thématisation

L’intégration s’affiche à l’intérieur d’un shadow root ouvert, donc le CSS de la page hôte ne peut pas pénétrer et le CSS de l’intégration ne peut pas s’échapper. Pour recolorer, définissez des variables CSS sur le sélecteur saiku-embed de la page hôte :

saiku-embed {
--saiku-embed-fg: #0f172a;
--saiku-embed-bg: transparent;
--saiku-embed-border: #cbd5e1;
--saiku-embed-header-bg: #f1f5f9;
--saiku-embed-tile-bg: #ffffff;
--saiku-embed-row-hover: #e2e8f0;
--saiku-embed-negative: #b91c1c;
--saiku-embed-error: #b91c1c;
--saiku-embed-muted: #64748b;
}

Le mode sombre n’est qu’une media query qui bascule les mêmes variables :

@media (prefers-color-scheme: dark) {
saiku-embed {
--saiku-embed-fg: #f1f5f9;
--saiku-embed-bg: #0f172a;
--saiku-embed-border: #334155;
--saiku-embed-header-bg: #1e293b;
--saiku-embed-tile-bg: #1e293b;
--saiku-embed-row-hover: #334155;
}
}

Modèle de sécurité

La surface d’intégration est conçue pour des pages hôtes hostiles — votre requête enregistrée pourrait se retrouver sur un site tiers que vous ne contrôlez pas. Les valeurs par défaut reflètent cela.

Les jetons sont opaques, autoritatifs côté serveur, révocables

  • Les jetons sont des identifiants aléatoires de 256 bits, encodés en Base64-URL — non devinables.
  • Ils ne portent aucune revendication embarquée : le serveur conserve l’enregistrement faisant autorité (type + chemin de ressource, snapshot du propriétaire, expiration, drapeau de révocation) et le consulte à chaque requête. La révocation prend effet à la toute prochaine requête — pas d’attente de type « expiration de signature JWT ».
  • Chaque jeton épingle exactement une ressource. Rejouer un jeton contre n’importe quelle autre ressource (mauvais chemin, mauvais type, expiré, révoqué) renvoie la même 401 opaque EMBED_INVALID — les sondages ne peuvent rien énumérer.

Transport uniquement par en-tête

Le jeton voyage dans l’en-tête HTTP X-Saiku-Embed-Token. Nous n’acceptons délibérément pas les paramètres de requête ?token=… car les paramètres d’URL fuient dans :

  • les logs d’accès des servlets
  • les logs des proxys frontaux
  • l’historique du navigateur
  • l’en-tête Referer envoyé sur chaque ressource sortante de la page hôte

Le JS fourni lit le jeton depuis votre attribut <saiku-embed token="…"> et l’envoie comme en-tête à chaque fetch.

Isolation des cookies inter-origines

L’intégration fait des fetch avec credentials: "omit". Si l’utilisateur de la page hôte est connecté à Saiku dans un autre onglet, ce cookie de session ne circule pas avec les lectures d’intégration. Le jeton EST le seul porteur d’authentification sur cette surface.

Portée de données du propriétaire

Les jetons et les autorisations publiques capturent un snapshot de la liste de rôles du propriétaire au moment de la création / autorisation et exécutent la requête d’intégration sous cette identité via sessionService.runAs. Une requête intégrée publiquement qui utilise des filtres injectés par session s’affiche selon la perspective du donneur, et non sous le défaut anonyme (vide) — ce que vous avez autorisé au moment de l’octroi est exactement ce que le public voit.

En-têtes de réponse en défense en profondeur

Chaque réponse d’intégration porte :

  • X-Content-Type-Options: nosniff — les navigateurs ne doivent pas faire de MIME-sniff sur un corps JSON (qui pourrait porter du HTML de tuile texte ou des légendes de membres) et l’exécuter comme HTML
  • Referrer-Policy: no-referrer — ne jamais laisser fuir le jeton via Referer sur les ressources sortantes
  • Cache-Control: no-store, max-age=0 — les données métier intégrées ne sont jamais mises en cache par les proxys ou l’historique du navigateur

Erreurs conviviales

Lorsqu’un fetch échoue (jeton expiré, jeton révoqué, mauvais chemin, autorisation publique manquante), la page hôte voit un « This embed is unavailable. » générique — pas le corps brut EMBED_INVALID. La page hôte est un tiers et n’a pas besoin de savoir si l’échec était dû à un jeton expiré ou à une révocation.

Taille du bundle

Environ 213 Ko gzippés au moment de la rédaction — runtime d’élément personnalisé Svelte 5 + ECharts (core + bar / line / pie + quatre composants courants, tree-shaken modulaire) + les moteurs de rendu d’intégration. Le bundle est mis en cache de manière agressive par le navigateur ; la deuxième intégration sur la même origine l’obtient instantanément.

Limitations

  • Format records uniquement. Le format cellset matriciel n’est pas rendu en v1.
  • Les tuiles de filtre sont ignorées sur les dashboards. L’intégration affiche les données telles qu’elles ont été conçues sans barre de filtre interactive.
  • Le Markdown dans les tuiles texte s’affiche en texte brut. Nous ne bundlons pas marked pour limiter la taille.
  • Les résultats AI Query (/ai/query) ne sont pas encore câblés comme source d’intégration. Suivi dans saiku — une PR de suivi ajoutera kind="ai".

Voir aussi

  • Authentification — création de clés API pour l’accès programmatique à Saiku Cloud (différent des jetons d’intégration)
  • MCP — connecter des LLMs à vos cubes