Add Activepieces integration for workflow automation

- Add Activepieces fork with SmoothSchedule custom piece
- Create integrations app with Activepieces service layer
- Add embed token endpoint for iframe integration
- Create Automations page with embedded workflow builder
- Add sidebar visibility fix for embed mode
- Add list inactive customers endpoint to Public API
- Include SmoothSchedule triggers: event created/updated/cancelled
- Include SmoothSchedule actions: create/update/cancel events, list resources/services/customers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
poduck
2025-12-18 22:59:37 -05:00
parent 9848268d34
commit 3aa7199503
16292 changed files with 1284892 additions and 4708 deletions

View File

@@ -0,0 +1,37 @@
{
"extends": [
"../../../../.eslintrc.json"
],
"ignorePatterns": [
"!**/*"
],
"overrides": [
{
"files": [
"*.ts",
"*.tsx",
"*.js",
"*.jsx"
],
"rules": {},
"extends": [
"plugin:prettier/recommended"
],
"plugins": ["prettier"]
},
{
"files": [
"*.ts",
"*.tsx"
],
"rules": {}
},
{
"files": [
"*.js",
"*.jsx"
],
"rules": {}
}
]
}

View File

@@ -0,0 +1,7 @@
# pieces-metabase
This library was generated with [Nx](https://nx.dev).
## Building
Run `nx build pieces-metabase` to build the library.

View File

@@ -0,0 +1,7 @@
{
"name": "@activepieces/piece-metabase",
"version": "0.1.22",
"dependencies": {
"playwright": "1.55.1"
}
}

View File

@@ -0,0 +1,57 @@
{
"name": "pieces-metabase",
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/pieces/community/metabase/src",
"projectType": "library",
"targets": {
"build": {
"executor": "@nx/js:tsc",
"outputs": [
"{options.outputPath}"
],
"options": {
"outputPath": "dist/packages/pieces/community/metabase",
"tsConfig": "packages/pieces/community/metabase/tsconfig.lib.json",
"packageJson": "packages/pieces/community/metabase/package.json",
"main": "packages/pieces/community/metabase/src/index.ts",
"assets": [
"packages/pieces/community/metabase/*.md",
{
"input": "packages/pieces/community/metabase/src/i18n",
"output": "./src/i18n",
"glob": "**/!(i18n.json)"
}
],
"buildableProjectDepsInPackageJsonType": "dependencies",
"updateBuildableProjectDepsInPackageJson": true
},
"dependsOn": [
"^build",
"prebuild"
]
},
"publish": {
"command": "node tools/scripts/publish.mjs pieces-metabase {args.ver} {args.tag}",
"dependsOn": [
"build"
]
},
"lint": {
"executor": "@nx/eslint:lint",
"outputs": [
"{options.outputFile}"
]
},
"prebuild": {
"executor": "nx:run-commands",
"options": {
"cwd": "packages/pieces/community/metabase",
"command": "bun install --no-save --silent"
},
"dependsOn": [
"^build"
]
}
},
"tags": []
}

View File

@@ -0,0 +1,30 @@
{
"The simplest way to ask questions and learn from data": "Der einfachste Weg, Fragen zu stellen und von Daten zu lernen",
"Metabase API base URL": "Metabase API Basis-URL",
"API key": "API-Schlüssel",
"Embedding key": "Einbetten Schlüssel",
"Generate one on your Metabase instance (settings -> authentication -> API keys)": "Generieren Sie eine in Ihrer Metabase-Instanz (Einstellungen -> Authentifizierung -> API-Schlüssel)",
"Needed if you want to generate a graph of a question (settings -> embedding -> static embedding).": "Benötigt wenn Sie ein Diagramm einer Frage generieren möchten (Einstellungen -> Einbetten -> statische Einbetten).",
"Metabase authentication requires a baseUrl and a password.": "Metabase Authentifizierung erfordert eine baseUrl und ein Passwort.",
"Get question": "Fragen erhalten",
"Get Question PNG Preview": "PNG-Vorschau für Fragen abrufen",
"Get Dashboard Questions": "Dashboard-Fragen erhalten",
"Embed question": "Frage einbetten",
"Get the graph of the question": "Graph der Frage abrufen",
"Fetch the results of a Metabase question": "Die Ergebnisse einer Metabase-Frage abrufen",
"Get PNG preview rendering (low resolution) of a Metabase card/question.": "Erhalte PNG Vorschau Rendering (geringe Auflösung) einer Metabase-Karte/Frage.",
"Execute all questions across all tabs in a Metabase dashboard and return their consolidated results in a single response": "Alle Fragen in allen Registerkarten in einem Metabase-Dashboard ausführen und ihre konsolidierten Ergebnisse in einer einzigen Antwort zurückgeben",
"Enable embedding for a Metabase question and configure parameters": "Einbetten für eine Metabase-Frage aktivieren und Parameter konfigurieren",
"Get the graph of a Metabase question and save it as a png": "Ruft die Grafik einer Metabase-Frage ab und speichert sie als png",
"Metabase question ID": "Metabase Frage-ID",
"Parameters (slug name -> value)": "Parameter (Slug Name -> Wert)",
"Metabase Dashboard ID": "Metabase Dashboard-ID",
"Enable embedding": "Einbetten aktivieren",
"Parameter settings": "Parametereinstellungen",
"The name of the graph (without the extension)": "Der Name des Graphen (ohne Erweiterung)",
"Wait Time (seconds)": "Wartezeit (Sekunden)",
"Dashboard parameters to apply to all questions": "Dashboard-Parameter für alle Fragen",
"Whether to enable embedding for this question": "Gibt an, ob die Einbettung für diese Frage aktiviert wird",
"Configure how each parameter should be handled in the embed": "Konfigurieren, wie jeder Parameter in der Einbettung behandelt werden soll",
"How long to wait for the graph to render completely in seconds": "Wie lange warten soll, bis der Graph in Sekunden komplett rendert wird"
}

View File

@@ -0,0 +1,30 @@
{
"The simplest way to ask questions and learn from data": "La forma más sencilla de hacer preguntas y aprender de los datos",
"Metabase API base URL": "URL base de la API Metabase",
"API key": "Clave API",
"Embedding key": "Clave de inserción",
"Generate one on your Metabase instance (settings -> authentication -> API keys)": "Generar una en su instancia de Metabase (configuración-> autenticación-> claves API)",
"Needed if you want to generate a graph of a question (settings -> embedding -> static embedding).": "Se necesita si desea generar un gráfico de una pregunta (configuración-> incrustación-> incrustación).",
"Metabase authentication requires a baseUrl and a password.": "La autenticación de Metabase requiere una URL base y una contraseña.",
"Get question": "Obtener pregunta",
"Get Question PNG Preview": "Obtener la vista previa PNG de Pregunta",
"Get Dashboard Questions": "Obtener preguntas del panel",
"Embed question": "Incrustar pregunta",
"Get the graph of the question": "Obtener el gráfico de la pregunta",
"Fetch the results of a Metabase question": "Obtener los resultados de una pregunta de Metabase",
"Get PNG preview rendering (low resolution) of a Metabase card/question.": "Obtener la previsualización PNG (baja resolución) de una tarjeta Metabase/pregunta.",
"Execute all questions across all tabs in a Metabase dashboard and return their consolidated results in a single response": "Ejecutar todas las preguntas en todas las pestañas en un panel de Metabase y devolver sus resultados consolidados en una sola respuesta",
"Enable embedding for a Metabase question and configure parameters": "Habilitar la incrustación para una pregunta de Metabase y configurar parámetros",
"Get the graph of a Metabase question and save it as a png": "Obtener el gráfico de una pregunta de Metabase y guardarla como un png",
"Metabase question ID": "ID de pregunta de Metabase",
"Parameters (slug name -> value)": "Parámetros (nombre del slug -> valor)",
"Metabase Dashboard ID": "ID del tablero de metabasa",
"Enable embedding": "Habilitar incrustación",
"Parameter settings": "Ajustes de parámetro",
"The name of the graph (without the extension)": "El nombre del gráfico (sin la extensión)",
"Wait Time (seconds)": "Tiempo de espera (segundos)",
"Dashboard parameters to apply to all questions": "Parámetros del panel a aplicar a todas las preguntas",
"Whether to enable embedding for this question": "Activar el embebido para esta pregunta",
"Configure how each parameter should be handled in the embed": "Configurar como cada parámetro debe ser manejado en el embebido",
"How long to wait for the graph to render completely in seconds": "Cuánto tiempo esperar a que el gráfico se renderice completamente en segundos"
}

View File

@@ -0,0 +1,30 @@
{
"The simplest way to ask questions and learn from data": "La façon la plus simple de poser des questions et d'apprendre des données",
"Metabase API base URL": "URL de base de l'API Metabase",
"API key": "Clé API",
"Embedding key": "Clé d'intégration",
"Generate one on your Metabase instance (settings -> authentication -> API keys)": "Générez-en une sur votre instance Metabase (paramètres -> authentification -> clés API)",
"Needed if you want to generate a graph of a question (settings -> embedding -> static embedding).": "Nécessaire si vous voulez générer un graphe d'une question (paramètres -> intégration -> intégration statique).",
"Metabase authentication requires a baseUrl and a password.": "L'authentification Metabase nécessite une baseUrl et un mot de passe.",
"Get question": "Obtenir une question",
"Get Question PNG Preview": "Obtenir l'aperçu de la question PNG",
"Get Dashboard Questions": "Obtenir des questions sur le tableau de bord",
"Embed question": "Intégrer la question",
"Get the graph of the question": "Obtenir le graphe de la question",
"Fetch the results of a Metabase question": "Récupérer les résultats d'une question de Metabase",
"Get PNG preview rendering (low resolution) of a Metabase card/question.": "Obtenir le rendu de l'aperçu PNG (basse résolution) d'une carte/question Metabase.",
"Execute all questions across all tabs in a Metabase dashboard and return their consolidated results in a single response": "Exécuter toutes les questions sur tous les onglets d'un tableau de bord Metabase et retourner leurs résultats consolidés en une seule réponse",
"Enable embedding for a Metabase question and configure parameters": "Activer l'intégration pour une question Metabase et configurer les paramètres",
"Get the graph of a Metabase question and save it as a png": "Récupère le graphique d'une question Metabase et l'enregistre en tant que png",
"Metabase question ID": "ID de la question de la métabase",
"Parameters (slug name -> value)": "Paramètres (slug name -> value)",
"Metabase Dashboard ID": "ID du tableau de bord de la métabase",
"Enable embedding": "Activer l'intégration",
"Parameter settings": "Paramètres du paramètre",
"The name of the graph (without the extension)": "Le nom du graphique (sans extension)",
"Wait Time (seconds)": "Temps d'attente (secondes)",
"Dashboard parameters to apply to all questions": "Paramètres du tableau de bord à appliquer à toutes les questions",
"Whether to enable embedding for this question": "Activer ou non l'intégration pour cette question",
"Configure how each parameter should be handled in the embed": "Configurer la façon dont chaque paramètre doit être traité dans l'embed",
"How long to wait for the graph to render completely in seconds": "Combien de temps attendre que le graphique soit rendu complètement en secondes"
}

View File

@@ -0,0 +1,30 @@
{
"The simplest way to ask questions and learn from data": "質問をしたり、データから学ぶ最も簡単な方法",
"Metabase API base URL": "Metabase API base URL",
"API key": "API キー",
"Embedding key": "埋め込みキー",
"Generate one on your Metabase instance (settings -> authentication -> API keys)": "Metabaseインスタンスで生成します (設定 → 認証 → APIキー)",
"Needed if you want to generate a graph of a question (settings -> embedding -> static embedding).": "質問のグラフを生成する場合に必要です (設定 > 埋め込み -> 静的埋め込み)。",
"Metabase authentication requires a baseUrl and a password.": "Metabase認証にはbaseUrlとパスワードが必要です。",
"Get question": "質問を取得",
"Get Question PNG Preview": "問題PNGのプレビューを取得",
"Get Dashboard Questions": "ダッシュボードの質問を取得する",
"Embed question": "埋め込みの質問",
"Get the graph of the question": "質問のグラフを取得する",
"Fetch the results of a Metabase question": "メタベースの質問の結果を取得",
"Get PNG preview rendering (low resolution) of a Metabase card/question.": "Metabaseカード/質問のPNGプレビューレンダリング低解像度を取得します。",
"Execute all questions across all tabs in a Metabase dashboard and return their consolidated results in a single response": "Metabase ダッシュボード内のすべてのタブですべての問題を実行し、統合された結果を一度に返信します",
"Enable embedding for a Metabase question and configure parameters": "メタベースの質問への埋め込みを有効にし、パラメータを設定します",
"Get the graph of a Metabase question and save it as a png": "Metabase質問のグラフを取得し、png として保存します。",
"Metabase question ID": "メタベースの質問ID",
"Parameters (slug name -> value)": "パラメータ (スラグ名 -> 値)",
"Metabase Dashboard ID": "メタベースダッシュボードID",
"Enable embedding": "埋め込みを有効にする",
"Parameter settings": "パラメーターの設定",
"The name of the graph (without the extension)": "グラフの名前 (拡張子なし)",
"Wait Time (seconds)": "待機時間 (秒)",
"Dashboard parameters to apply to all questions": "すべての質問に適用するダッシュボードパラメータ",
"Whether to enable embedding for this question": "この質問に埋め込みを有効にするかどうか",
"Configure how each parameter should be handled in the embed": "各パラメータの埋め込み処理方法を設定します",
"How long to wait for the graph to render completely in seconds": "グラフが完全にレンダリングされるまでの時間(秒)"
}

View File

@@ -0,0 +1,30 @@
{
"The simplest way to ask questions and learn from data": "De eenvoudigste manier om vragen te stellen en te leren van gegevens",
"Metabase API base URL": "Metabase API basis-URL",
"API key": "API sleutel",
"Embedding key": "Sleutel insluiten",
"Generate one on your Metabase instance (settings -> authentication -> API keys)": "Genereer een op uw Metabase instantie (instellingen -> authenticatie--> API sleutels)",
"Needed if you want to generate a graph of a question (settings -> embedding -> static embedding).": "Nodig als u een grafiek van een vraag wilt genereren (instellingen -> embedding -> statische embedding).",
"Metabase authentication requires a baseUrl and a password.": "Metabase authenticatie vereist een baseUrl en een wachtwoord.",
"Get question": "Vraag ophalen",
"Get Question PNG Preview": "Krijg Vraag PNG Voorbeeld",
"Get Dashboard Questions": "Krijg Dashboard Vragen",
"Embed question": "Vraag insluiten",
"Get the graph of the question": "Bekijk de grafiek van de vraag",
"Fetch the results of a Metabase question": "Haal de resultaten van een Metabase vraag op",
"Get PNG preview rendering (low resolution) of a Metabase card/question.": "Krijg PNG weergave (lage resolutie) van een Metabase kaart/vraag.",
"Execute all questions across all tabs in a Metabase dashboard and return their consolidated results in a single response": "Voer alle vragen uit in alle tabbladen in een Metabase dashboard en geef hun geconsolideerde resultaten in één enkele reactie",
"Enable embedding for a Metabase question and configure parameters": "Activeer insluiten voor een Metabase vraag en configureer parameters",
"Get the graph of a Metabase question and save it as a png": "Bekijk de grafiek van een Metabase vraag en sla deze op als een png",
"Metabase question ID": "Metabase vraag ID",
"Parameters (slug name -> value)": "Parameters (slug name -> waarde)",
"Metabase Dashboard ID": "Metabase Dashboard ID",
"Enable embedding": "Insluiten inschakelen",
"Parameter settings": "Parameter instellingen",
"The name of the graph (without the extension)": "De naam van de grafiek (zonder de extensie)",
"Wait Time (seconds)": "Wachttijd (seconden)",
"Dashboard parameters to apply to all questions": "Dashboard parameters om toe te passen op alle vragen",
"Whether to enable embedding for this question": "Insluiten inschakelen voor deze vraag",
"Configure how each parameter should be handled in the embed": "Stel in hoe elke parameter moet worden afgehandeld in de embed",
"How long to wait for the graph to render completely in seconds": "Hoe lang te wachten tot de grafiek volledig in seconden wordt weergegeven"
}

View File

@@ -0,0 +1,30 @@
{
"The simplest way to ask questions and learn from data": "A maneira mais simples de fazer perguntas e aprender com os dados",
"Metabase API base URL": "Metabase URL base da API",
"API key": "Chave da API",
"Embedding key": "Incorporando a chave",
"Generate one on your Metabase instance (settings -> authentication -> API keys)": "Gerar uma em sua instância do Metabase (configurações -> autenticação -> chaves da API)",
"Needed if you want to generate a graph of a question (settings -> embedding -> static embedding).": "Necessário se você deseja gerar um gráfico de uma questão (configurações-> incorporação -> incorporação estática).",
"Metabase authentication requires a baseUrl and a password.": "A autenticação do Metabase requer uma baseUrl e uma senha.",
"Get question": "Obter pergunta",
"Get Question PNG Preview": "Obter Pré-visualização de Questões PNG",
"Get Dashboard Questions": "Obter perguntas do painel",
"Embed question": "Incorporar pergunta",
"Get the graph of the question": "Obtenha o gráfico da pergunta",
"Fetch the results of a Metabase question": "Obter os resultados de uma pergunta Metabase",
"Get PNG preview rendering (low resolution) of a Metabase card/question.": "Obter a pré-visualização PNG (baixa resolução) de um cartão/pergunta de Metabase .",
"Execute all questions across all tabs in a Metabase dashboard and return their consolidated results in a single response": "Execute todas as questões em todas as abas em um painel de Metabase e retorne seus resultados consolidados em uma única resposta",
"Enable embedding for a Metabase question and configure parameters": "Habilitar a incorporação de uma pergunta Metabase e configurar parâmetros",
"Get the graph of a Metabase question and save it as a png": "Obter o gráfico de uma pergunta Metabase e salvá-lo como png",
"Metabase question ID": "ID da pergunta Metabase",
"Parameters (slug name -> value)": "Parâmetros (nome -> nome do valor)",
"Metabase Dashboard ID": "Metabase Dashboard ID",
"Enable embedding": "Habilitar incorporação",
"Parameter settings": "Definições de parâmetros",
"The name of the graph (without the extension)": "O nome do gráfico (sem a extensão)",
"Wait Time (seconds)": "Tempo de espera (segundos)",
"Dashboard parameters to apply to all questions": "Parâmetros do painel para aplicar a todas as questões",
"Whether to enable embedding for this question": "Se deseja habilitar a incorporação desta questão",
"Configure how each parameter should be handled in the embed": "Configure como cada parâmetro deve ser tratado na incorporação",
"How long to wait for the graph to render completely in seconds": "Quanto tempo espera o gráfico ser completamente renderizado em segundos"
}

View File

@@ -0,0 +1,31 @@
{
"Metabase": "Метабаза",
"The simplest way to ask questions and learn from data": "Самый простой способ задавать вопросы и учиться на данных",
"Metabase API base URL": "Базовый URL-адрес Metabase API",
"API key": "API ключ",
"Embedding key": "Встраиваемый ключ",
"Generate one on your Metabase instance (settings -> authentication -> API keys)": "Сгенерировать один из экземпляров Metabase (настройки -> аутентификация -> API ключей)",
"Needed if you want to generate a graph of a question (settings -> embedding -> static embedding).": "Необходимо, если вы хотите сгенерировать график вопроса (настройки -> встраивание -> статическое встраивание).",
"Metabase authentication requires a baseUrl and a password.": "Для аутентификации базы данных требуется baseUrl и пароль.",
"Get question": "Получить вопрос",
"Get Question PNG Preview": "Получить предварительный просмотр вопроса PNG",
"Get Dashboard Questions": "Получить вопросы в меню настроек",
"Embed question": "Вставить вопрос",
"Get the graph of the question": "Получить график вопроса",
"Fetch the results of a Metabase question": "Получить результаты вопроса метабазы",
"Get PNG preview rendering (low resolution) of a Metabase card/question.": "Получить PNG предварительный рендеринг (низкое разрешение) Metabase карты/вопроса.",
"Execute all questions across all tabs in a Metabase dashboard and return their consolidated results in a single response": "Выполнить все вопросы во всех вкладках панели Metabase и вернуть сводные результаты в один ответ",
"Enable embedding for a Metabase question and configure parameters": "Включить встраивание вопроса Metabase и настроить параметры",
"Get the graph of a Metabase question and save it as a png": "Получить график метабазисного вопроса и сохранить его как png",
"Metabase question ID": "ID метабазового вопроса",
"Parameters (slug name -> value)": "Параметры (slug name -> значение)",
"Metabase Dashboard ID": "ID панели Metabase",
"Enable embedding": "Включить вставку",
"Parameter settings": "Настройки параметров",
"The name of the graph (without the extension)": "Название графика (без расширения)",
"Wait Time (seconds)": "Время ожидания (секунд)",
"Dashboard parameters to apply to all questions": "Параметры приборной панели для всех вопросов",
"Whether to enable embedding for this question": "Включить ли встраивание этого вопроса",
"Configure how each parameter should be handled in the embed": "Настройте как каждый параметр должен быть обработан в виде вставки",
"How long to wait for the graph to render completely in seconds": "Как долго ждать полной отрисовки графика в секундах"
}

View File

@@ -0,0 +1,30 @@
{
"The simplest way to ask questions and learn from data": "The simplest way to ask questions and learn from data",
"Metabase API base URL": "Metabase API base URL",
"API key": "API key",
"Embedding key": "Embedding key",
"Generate one on your Metabase instance (settings -> authentication -> API keys)": "Generate one on your Metabase instance (settings -> authentication -> API keys)",
"Needed if you want to generate a graph of a question (settings -> embedding -> static embedding).": "Needed if you want to generate a graph of a question (settings -> embedding -> static embedding).",
"Metabase authentication requires a baseUrl and a password.": "Metabase authentication requires a baseUrl and a password.",
"Get question": "Get question",
"Get Question PNG Preview": "Get Question PNG Preview",
"Get Dashboard Questions": "Get Dashboard Questions",
"Embed question": "Embed question",
"Get the graph of the question": "Get the graph of the question",
"Fetch the results of a Metabase question": "Fetch the results of a Metabase question",
"Get PNG preview rendering (low resolution) of a Metabase card/question.": "Get PNG preview rendering (low resolution) of a Metabase card/question.",
"Execute all questions across all tabs in a Metabase dashboard and return their consolidated results in a single response": "Execute all questions across all tabs in a Metabase dashboard and return their consolidated results in a single response",
"Enable embedding for a Metabase question and configure parameters": "Enable embedding for a Metabase question and configure parameters",
"Get the graph of a Metabase question and save it as a png": "Get the graph of a Metabase question and save it as a png",
"Metabase question ID": "Metabase question ID",
"Parameters (slug name -> value)": "Parameters (slug name -> value)",
"Metabase Dashboard ID": "Metabase Dashboard ID",
"Enable embedding": "Enable embedding",
"Parameter settings": "Parameter settings",
"The name of the graph (without the extension)": "The name of the graph (without the extension)",
"Wait Time (seconds)": "Wait Time (seconds)",
"Dashboard parameters to apply to all questions": "Dashboard parameters to apply to all questions",
"Whether to enable embedding for this question": "Whether to enable embedding for this question",
"Configure how each parameter should be handled in the embed": "Configure how each parameter should be handled in the embed",
"How long to wait for the graph to render completely in seconds": "How long to wait for the graph to render completely in seconds"
}

View File

@@ -0,0 +1,31 @@
{
"Metabase": "Metabase",
"The simplest way to ask questions and learn from data": "The simplest way to ask questions and learn from data",
"Metabase API base URL": "Metabase API base URL",
"API key": "API key",
"Embedding key": "Embedding key",
"Generate one on your Metabase instance (settings -> authentication -> API keys)": "Generate one on your Metabase instance (settings -> authentication -> API keys)",
"Needed if you want to generate a graph of a question (settings -> embedding -> static embedding).": "Needed if you want to generate a graph of a question (settings -> embedding -> static embedding).",
"Metabase authentication requires a baseUrl and a password.": "Metabase authentication requires a baseUrl and a password.",
"Get question": "Get question",
"Get Question PNG Preview": "Get Question PNG Preview",
"Get Dashboard Questions": "Get Dashboard Questions",
"Embed question": "Embed question",
"Get the graph of the question": "Get the graph of the question",
"Fetch the results of a Metabase question": "Fetch the results of a Metabase question",
"Get PNG preview rendering (low resolution) of a Metabase card/question.": "Get PNG preview rendering (low resolution) of a Metabase card/question.",
"Execute all questions across all tabs in a Metabase dashboard and return their consolidated results in a single response": "Execute all questions across all tabs in a Metabase dashboard and return their consolidated results in a single response",
"Enable embedding for a Metabase question and configure parameters": "Enable embedding for a Metabase question and configure parameters",
"Get the graph of a Metabase question and save it as a png": "Get the graph of a Metabase question and save it as a png",
"Metabase question ID": "Metabase question ID",
"Parameters (slug name -> value)": "Parameters (slug name -> value)",
"Metabase Dashboard ID": "Metabase Dashboard ID",
"Enable embedding": "Enable embedding",
"Parameter settings": "Parameter settings",
"The name of the graph (without the extension)": "The name of the graph (without the extension)",
"Wait Time (seconds)": "Wait Time (seconds)",
"Dashboard parameters to apply to all questions": "Dashboard parameters to apply to all questions",
"Whether to enable embedding for this question": "Whether to enable embedding for this question",
"Configure how each parameter should be handled in the embed": "Configure how each parameter should be handled in the embed",
"How long to wait for the graph to render completely in seconds": "How long to wait for the graph to render completely in seconds"
}

View File

@@ -0,0 +1,30 @@
{
"The simplest way to ask questions and learn from data": "The simplest way to ask questions and learn from data",
"Metabase API base URL": "Metabase API base URL",
"API key": "API key",
"Embedding key": "Embedding key",
"Generate one on your Metabase instance (settings -> authentication -> API keys)": "Generate one on your Metabase instance (settings -> authentication -> API keys)",
"Needed if you want to generate a graph of a question (settings -> embedding -> static embedding).": "Needed if you want to generate a graph of a question (settings -> embedding -> static embedding).",
"Metabase authentication requires a baseUrl and a password.": "Metabase authentication requires a baseUrl and a password.",
"Get question": "Get question",
"Get Question PNG Preview": "Get Question PNG Preview",
"Get Dashboard Questions": "Get Dashboard Questions",
"Embed question": "Embed question",
"Get the graph of the question": "Get the graph of the question",
"Fetch the results of a Metabase question": "Fetch the results of a Metabase question",
"Get PNG preview rendering (low resolution) of a Metabase card/question.": "Get PNG preview rendering (low resolution) of a Metabase card/question.",
"Execute all questions across all tabs in a Metabase dashboard and return their consolidated results in a single response": "Execute all questions across all tabs in a Metabase dashboard and return their consolidated results in a single response",
"Enable embedding for a Metabase question and configure parameters": "Enable embedding for a Metabase question and configure parameters",
"Get the graph of a Metabase question and save it as a png": "Get the graph of a Metabase question and save it as a png",
"Metabase question ID": "Metabase question ID",
"Parameters (slug name -> value)": "Parameters (slug name -> value)",
"Metabase Dashboard ID": "Metabase Dashboard ID",
"Enable embedding": "Enable embedding",
"Parameter settings": "Parameter settings",
"The name of the graph (without the extension)": "The name of the graph (without the extension)",
"Wait Time (seconds)": "Wait Time (seconds)",
"Dashboard parameters to apply to all questions": "Dashboard parameters to apply to all questions",
"Whether to enable embedding for this question": "Whether to enable embedding for this question",
"Configure how each parameter should be handled in the embed": "Configure how each parameter should be handled in the embed",
"How long to wait for the graph to render completely in seconds": "How long to wait for the graph to render completely in seconds"
}

View File

@@ -0,0 +1,85 @@
import {
createPiece,
PieceAuth,
Property,
} from '@activepieces/pieces-framework';
import { getQuestion } from './lib/actions/get-question';
import { getQuestionPngPreview } from './lib/actions/get-png-rendering';
import { getDashboardQuestions } from './lib/actions/get-dashboard';
import { queryMetabaseApi } from './lib/common';
import { HttpMethod, is_chromium_installed } from '@activepieces/pieces-common';
import { getGraphQuestion } from './lib/actions/get-graph-question';
import { embedQuestion } from './lib/actions/embed-question';
import { AppConnectionType } from '@activepieces/shared';
const baseProps = {
baseUrl: Property.ShortText({
displayName: 'Metabase API base URL',
required: true,
}),
apiKey: PieceAuth.SecretText({
displayName: 'API key',
description:
'Generate one on your Metabase instance (settings -> authentication -> API keys)',
required: true,
}),
};
const authProps = is_chromium_installed()
? {
...baseProps,
embeddingKey: Property.ShortText({
displayName: 'Embedding key',
description:
'Needed if you want to generate a graph of a question (settings -> embedding -> static embedding).',
required: false,
}),
}
: baseProps;
export const metabaseAuth = PieceAuth.CustomAuth({
description: 'Metabase authentication requires a baseUrl and a password.',
required: true,
props: authProps,
validate: async ({ auth }) => {
try {
await queryMetabaseApi(
{
endpoint: 'login-history/current',
method: HttpMethod.GET,
},
{
type: AppConnectionType.CUSTOM_AUTH,
props: auth,
}
);
return {
valid: true,
};
} catch (e) {
return {
valid: false,
error: 'Invalid API Key or base URL',
};
}
},
});
export const metabase = createPiece({
displayName: 'Metabase',
description: 'The simplest way to ask questions and learn from data',
auth: metabaseAuth,
minimumSupportedRelease: '0.30.0',
logoUrl: 'https://cdn.activepieces.com/pieces/metabase.png',
authors: ['AdamSelene', 'abuaboud', 'valentin-mourtialon', 'Kevinyu-alan'],
actions: [
getQuestion,
getQuestionPngPreview,
getDashboardQuestions,
embedQuestion,
...(is_chromium_installed() ? [getGraphQuestion] : []),
],
triggers: [],
});

View File

@@ -0,0 +1,143 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { queryMetabaseApi } from '../common';
import { metabaseAuth } from '../..';
import { HttpMethod } from '@activepieces/pieces-common';
interface MetabaseParam {
id: string;
slug: string;
name?: string;
}
export const embedQuestion = createAction({
name: 'embedQuestion',
auth: metabaseAuth,
requireAuth: true,
displayName: 'Embed question',
description:
'Enable embedding for a Metabase question and configure parameters',
props: {
questionId: Property.ShortText({
displayName: 'Metabase question ID',
required: true,
}),
enableEmbedding: Property.Checkbox({
displayName: 'Enable embedding',
description: 'Whether to enable embedding for this question',
required: true,
defaultValue: true,
}),
parameterSettings: Property.DynamicProperties<false, typeof metabaseAuth>({
displayName: 'Parameter settings',
auth: metabaseAuth,
description:
'Configure how each parameter should be handled in the embed',
required: false,
refreshers: ['questionId', 'enableEmbedding'],
props: async ({ auth, questionId, enableEmbedding }) => {
if (!questionId || !enableEmbedding || !auth) {
return {};
}
try {
const card = await queryMetabaseApi(
{
endpoint: `card/${(questionId as string).split('-')[0]}`,
method: HttpMethod.GET,
},
auth
);
const parameters = (card['parameters'] as MetabaseParam[]) || [];
const props: Record<string, any> = {};
// Get current embedding settings if they exist
const currentEmbeddingParams = card.embedding_params || {};
parameters.forEach((param) => {
const paramName = param.name || param.slug;
const currentSetting =
currentEmbeddingParams[param.slug] || 'disabled';
props[param.slug] = Property.StaticDropdown({
displayName: paramName,
description: `How to handle parameter: ${paramName}`,
required: false,
defaultValue: currentSetting,
options: {
options: [
{ label: 'Disabled', value: 'disabled' },
{ label: 'Editable', value: 'enabled' },
{ label: 'Locked', value: 'locked' },
],
},
});
});
return props;
} catch (error) {
return {};
}
},
}),
},
async run({ auth, propsValue }) {
const questionId = propsValue.questionId.split('-')[0];
// First, get the current question details
const card = await queryMetabaseApi(
{ endpoint: `card/${questionId}`, method: HttpMethod.GET },
auth
);
const parameters = (card['parameters'] as MetabaseParam[]) || [];
const updatePayload = {
...card,
enable_embedding: propsValue.enableEmbedding,
};
// If parameter settings are provided, update the parameters
// Create embedding_params object if parameter settings are provided
if (propsValue.parameterSettings && parameters.length > 0) {
const embeddingParams: Record<string, string> = {};
parameters.forEach((param) => {
const paramSetting = propsValue.parameterSettings?.[
param.slug
] as string;
if (paramSetting) {
embeddingParams[param.slug] = paramSetting;
} else {
embeddingParams[param.slug] = 'disabled';
}
});
updatePayload.embedding_params = embeddingParams;
}
// Update the card with embedding settings
const response = await queryMetabaseApi(
{
endpoint: `card/${questionId}`,
method: HttpMethod.PUT,
body: updatePayload,
},
auth
);
if (response.error) {
throw new Error(response.error);
}
return {
success: true,
message: propsValue.enableEmbedding
? 'Question embedding has been enabled successfully'
: 'Question embedding has been disabled successfully',
embeddingParams: propsValue.enableEmbedding
? updatePayload.embedding_params
: {},
};
},
});

View File

@@ -0,0 +1,156 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { queryMetabaseApi } from '../common';
import { metabaseAuth } from '../..';
import { HttpMethod } from '@activepieces/pieces-common';
interface MetabaseParam {
id: string;
name?: string;
slug?: string;
type?: string;
values_source_config?: {
values?: unknown[];
};
}
interface CardResult {
name: string;
data?: unknown;
error?: string;
}
interface DashboardParameter {
id: string;
name: string;
slug: string;
type: string;
possible_values?: unknown[];
}
interface DashboardResult {
dashboard_name: string;
available_parameters: DashboardParameter[];
cards_results: Record<string, CardResult>;
}
export const getDashboardQuestions = createAction({
name: 'getDashboardQuestions',
auth: metabaseAuth,
requireAuth: true,
displayName: 'Get Dashboard Questions',
description:
'Execute all questions across all tabs in a Metabase dashboard and return their consolidated results in a single response',
props: {
dashboardId: Property.ShortText({
displayName: 'Metabase Dashboard ID',
required: true,
}),
parameters: Property.Object({
displayName: 'Parameters (slug name -> value)',
description: 'Dashboard parameters to apply to all questions',
required: false,
}),
},
async run({ auth, propsValue }) {
const dashboardId = propsValue.dashboardId.split('-')[0];
const dashboardData = await queryMetabaseApi(
{ endpoint: `dashboard/${dashboardId}`, method: HttpMethod.GET },
auth
);
if (dashboardData.error) {
throw new Error(`Error fetching dashboard: ${dashboardData.error}`);
}
const dashboardParameters = dashboardData.parameters || [];
const result: DashboardResult = {
dashboard_name: dashboardData.name || 'Unknown Dashboard',
available_parameters: dashboardParameters.map((param: MetabaseParam) => ({
id: param.id,
name: param.name || 'Unknown',
slug: param.slug || '',
type: param.type || '',
possible_values: param.values_source_config?.values || [],
})),
cards_results: {},
};
// Execute each card and collect results
for (const dashcard of dashboardData.dashcards || []) {
const cardId = dashcard.card_id;
const dashcardId = dashcard.id;
if (!cardId) continue;
let cardName = 'Unknown';
if (dashcard.card && typeof dashcard.card === 'object') {
cardName = dashcard.card.name || `Card ${cardId}`;
}
const cardParameters = [];
// Map dashboard parameters to card parameters using parameter_mappings
if (propsValue.parameters && dashcard.parameter_mappings) {
for (const mapping of dashcard.parameter_mappings) {
const paramId = mapping.parameter_id;
// Find corresponding dashboard parameter
const dashParam = dashboardParameters.find(
(p: { id: string }) => p.id === paramId
);
if (
dashParam &&
dashParam.slug &&
propsValue.parameters[dashParam.slug] !== undefined
) {
cardParameters.push({
id: paramId,
target: mapping.target,
type: dashParam.type,
value: propsValue.parameters[dashParam.slug],
});
}
}
}
try {
const cardResult = await queryMetabaseApi(
{
endpoint: `dashboard/${dashboardId}/dashcard/${dashcardId}/card/${cardId}/query/json`,
method: HttpMethod.POST,
body: {
parameters: cardParameters,
},
},
auth
);
const cardIdStr = String(cardId);
result.cards_results[cardIdStr] = {
name: cardName,
data: cardResult,
};
} catch (error: unknown) {
let errorMessage = 'Unknown error';
if (
error &&
typeof error === 'object' &&
'message' in error &&
typeof error.message === 'string'
) {
errorMessage = error.message;
}
const cardIdStr = String(cardId);
result.cards_results[cardIdStr] = {
name: cardName,
error: `Failed to execute: ${errorMessage}`,
};
}
}
return result;
},
});

View File

@@ -0,0 +1,135 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { metabaseAuth } from '../..';
import jwt from 'jsonwebtoken';
import { chromium } from 'playwright';
export const getGraphQuestion = createAction({
name: 'getGraphQuestion',
auth: metabaseAuth,
requireAuth: true,
displayName: 'Get the graph of the question',
description: 'Get the graph of a Metabase question and save it as a png',
props: {
questionId: Property.ShortText({
displayName: 'Metabase question ID',
required: true,
}),
parameters: Property.Object({
displayName: 'Parameters (slug name -> value)',
required: false,
}),
graphName: Property.ShortText({
displayName: 'The name of the graph (without the extension)',
required: false,
}),
waitTime: Property.Number({
displayName: 'Wait Time (seconds)',
description:
'How long to wait for the graph to render completely in seconds',
required: true,
defaultValue: 5,
}),
},
async run({ auth, propsValue, files }) {
if ('embeddingKey' in auth && !auth.embeddingKey)
return 'An embedding key is needed.';
if (propsValue.waitTime <= 0)
return 'The wait time needs to be superior to 0';
const questionId = propsValue.questionId.split('-')[0];
const numericQuestionId = parseInt(questionId);
const payload = {
resource: { question: numericQuestionId },
params: propsValue.parameters,
exp: Math.round(Date.now() / 1000) + 10 * 60,
};
// @ts-expect-error we expect an embedding key if the user can use this action.
const token = jwt.sign(payload, auth.embeddingKey);
const graphName = propsValue.graphName
? propsValue.graphName + '.png'
: `metabase_question_${questionId}.png`;
const iframeUrl =
auth.props.baseUrl +
'/embed/question/' +
token +
'#bordered=true&titled=true';
const browser = await chromium.launch({
headless: true,
chromiumSandbox: false,
executablePath: '/usr/bin/chromium',
});
try {
const context = await browser.newContext({
viewport: {
width: 1600,
height: 1200,
},
deviceScaleFactor: 2,
});
const page = await context.newPage();
const response = await page.goto(iframeUrl, {
waitUntil: 'networkidle',
timeout: 30000,
});
if (!response || !response.ok()) {
throw new Error(
`Page load failed with status: ${response ? response.status() : 400}`
);
}
// we wait so the graph can load
await page.waitForTimeout(propsValue.waitTime * 1000);
const mainElement = await page.$('main');
let screenshotBuffer;
if (mainElement) {
screenshotBuffer = await mainElement.screenshot({
path: graphName,
type: 'png',
});
} else {
screenshotBuffer = await page.screenshot({
path: graphName,
type: 'png',
clip: {
x: 0,
y: 0,
width: 1600,
height: 1120, // so it doesn't screenshot the metabase banner
},
});
}
const fileUrl = await files.write({
fileName: graphName,
data: screenshotBuffer,
});
return {
file: {
filename: graphName,
base64Content: screenshotBuffer.toString('base64'),
download: fileUrl,
},
iframeUrl,
};
} catch (error) {
console.error(
'Please verify that either your embedding key and question id are valid or that the question is embedded and published.'
);
console.error('Error capturing Metabase chart:', error);
throw error;
} finally {
await browser.close();
}
},
});

View File

@@ -0,0 +1,48 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { metabaseAuth } from '../..';
import { queryMetabaseApi } from '../common';
import { HttpMethod } from '@activepieces/pieces-common';
export const getQuestionPngPreview = createAction({
name: 'getQuestionPngPreview',
auth: metabaseAuth,
requireAuth: true,
displayName: 'Get Question PNG Preview',
description:
'Get PNG preview rendering (low resolution) of a Metabase card/question.',
props: {
questionId: Property.ShortText({
displayName: 'Metabase question ID',
required: true,
}),
},
async run({ auth, propsValue, files }) {
const questionId = propsValue.questionId.split('-')[0];
const response = await queryMetabaseApi(
{
endpoint: `pulse/preview_card_png/${questionId}`,
method: HttpMethod.GET,
headers: {
Accept: 'image/png',
},
responseType: 'arraybuffer',
},
auth
);
if (response.error) {
throw new Error(response.error);
}
const fileUrl = await files.write({
fileName: `metabase_question_${questionId}.png`,
data: Buffer.from(response, 'base64'),
});
return {
fileName: `metabase_question_${questionId}.png`,
file: fileUrl,
};
},
});

View File

@@ -0,0 +1,69 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { queryMetabaseApi } from '../common';
import { metabaseAuth } from '../..';
import { HttpMethod } from '@activepieces/pieces-common';
interface MetabaseParam {
id: string;
target: unknown[];
type: string[];
slug: string;
}
export const getQuestion = createAction({
name: 'getQuestion',
auth: metabaseAuth,
requireAuth: true,
displayName: 'Get question',
description: 'Fetch the results of a Metabase question',
props: {
questionId: Property.ShortText({
displayName: 'Metabase question ID',
required: true,
}),
parameters: Property.Object({
displayName: 'Parameters (slug name -> value)',
required: false,
}),
},
async run({ auth, propsValue }) {
const questionId = propsValue.questionId.split('-')[0];
const card = await queryMetabaseApi(
{ endpoint: `card/${questionId}`, method: HttpMethod.GET },
auth
);
const parameters = card['parameters'] as MetabaseParam[];
const response = await queryMetabaseApi(
{
endpoint: `card/${questionId}/query`,
method: HttpMethod.POST,
body: {
collection_preview: false,
ignore_cache: false,
parameters: parameters
.filter(
(param) =>
propsValue.parameters &&
propsValue.parameters[param.slug] !== undefined
)
.map((param) => {
return {
id: param.id,
target: param.target,
type: param.type,
value:
propsValue.parameters && propsValue.parameters[param.slug],
};
}),
},
},
auth
);
if (response.error) {
throw new Error(response.error);
} else {
return response;
}
},
});

View File

@@ -0,0 +1,40 @@
import {
httpClient,
HttpHeaders,
HttpMethod,
HttpRequest,
QueryParams,
} from '@activepieces/pieces-common';
import {
AppConnectionValueForAuthProperty,
CustomAuthProps,
StaticPropsValue,
} from '@activepieces/pieces-framework';
import { metabaseAuth } from '..';
export async function queryMetabaseApi(
params: {
endpoint: string;
method: HttpMethod;
queryParams?: QueryParams;
headers?: HttpHeaders;
body?: object;
responseType?: 'arraybuffer' | 'json' | 'blob' | 'text';
},
auth: AppConnectionValueForAuthProperty<typeof metabaseAuth>
) {
const request: HttpRequest = {
method: params.method,
url: `${auth.props.baseUrl}/api/${params.endpoint}`,
queryParams: params.queryParams,
headers: {
...params.headers,
'Content-Type': 'application/json',
'X-API-KEY': auth.props.apiKey,
},
body: JSON.stringify(params.body),
responseType: params.responseType,
};
const response = await httpClient.sendRequest(request);
return response.body;
}

View File

@@ -0,0 +1,22 @@
{
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"module": "commonjs",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true
},
"files": [],
"include": [],
"exclude": ["node_modules"],
"references": [
{
"path": "./tsconfig.lib.json"
}
],
}

View File

@@ -0,0 +1,11 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"outDir": "../../../../dist/out-tsc",
"declaration": true,
"types": ["node"]
},
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts", "node_modules"],
"include": ["src/**/*.ts"]
}