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,54 @@
{
"Enterprise-grade relational database": "Enterprise-grade relationale Datenbank",
"Connection Type": "Verbindungstyp",
"Host": "Host",
"Port": "Port",
"Service Name": "Dienstname",
"Connection String": "Verbindungszeichenkette",
"Username": "Benutzername",
"Password": "Kennwort",
"How you want to connect": "Wie Sie sich verbinden möchten",
"Database server hostname or IP": "Hostname oder IP des Datenbankservers",
"Database port": "Datenbank-Port",
"Oracle service name": "Oracle-Dienstname",
"Full connection string (e.g., host:port/serviceName)": "Kompletter Verbindungsstring (z.B. host:port/serviceName)",
"Connect to Oracle Database using either Service Name (host/port/service) or a full connection string.": "Verbinden Sie sich mit Oracle-Datenbank entweder mit einem Service-Namen (Host/Port/Service) oder einem vollständigen Verbindungs-String.",
"Insert Row": "Insert Row",
"Insert Rows": "Insert Rows",
"Run Custom SQL": "Benutzerdefiniertes SQL ausführen",
"Update Row": "Zeile aktualisieren",
"Delete Row": "Zeile löschen",
"Find Row": "Finde Zeile",
"Insert a row into an Oracle table": "Eine Zeile in eine Oracle-Tabelle einfügen",
"Insert multiple rows into an Oracle table": "Mehrere Zeilen in eine Oracle-Tabelle einfügen",
"Execute custom SQL or PL/SQL in Oracle": "Benutzerdefiniertes SQL oder PL/SQL in Oracle ausführen",
"Update rows in an Oracle table": "Aktualisiere Zeilen in einer Oracle-Tabelle",
"Delete rows from an Oracle table": "Datensätze aus einer Oracle-Tabelle löschen",
"Find rows in an Oracle table": "Datensätze in einer Oracle-Tabelle finden",
"Table Name": "Tabellenname",
"Row": "Zeile",
"Rows": "Zeilen",
"Markdown": "Markdown",
"SQL Query": "SQL-Abfrage",
"Bind Parameters": "Verbindungsparameter",
"Values": "Werte",
"Filter (WHERE)": "Filter (WHERE)",
"Column names and values to insert": "Spaltennamen und Werte zum Einfügen",
"Array of objects with column names and values": "Array von Objekten mit Spaltennamen und Werten",
"**DO NOT** insert dynamic input directly into the query. Use bind parameters (:param) to prevent **SQL injection**.": "**NICHT** Fügen Sie dynamische Eingaben direkt in die Abfrage ein. Verwenden Sie Bind Parameter (:param), um **SQL Injection** zu verhindern.",
"SQL or PL/SQL to execute. Use :param for bind parameters.": "SQL oder PL/SQL zum Ausführen. Verwenden Sie :param für Bind-Parameter.",
"Key-value pairs for bind variables": "Schlüssel-Wert-Paare für Bind-Variablen",
"Column names and new values to set": "Spaltennamen und neue zu setzende Werte",
"Conditions to match rows. Empty object updates ALL rows.": "Bedingungen für Zeilen. Leeres Objekt aktualisiert ALLE Zeilen.",
"Conditions to match rows for deletion": "Bedingungen für die Löschung der Zeilen",
"Conditions to match rows": "Bedingungen für Zeilen übereinstimmen",
"New Row": "Neue Zeile",
"Triggers when a new row is created": "Wird ausgelöst, wenn eine neue Zeile erstellt wird",
"Order By Column": "Nach Spalte bestellen",
"Order Direction": "Bestellrichtung",
"**NOTE:** Fetches latest rows using the order column (newest first), then keeps polling for new rows.": "**HINWEIS:** Holt die neuesten Zeilen mit der Bestellspalte (neueste zuerst) und fragt dann nach neuen Zeilen.",
"Column that increases over time (ID or timestamp)": "Spalte, die im Laufe der Zeit steigt (ID oder Zeitstempel)",
"Sort direction to fetch newest rows first": "Sortierrichtung, um die neuesten Zeilen zuerst zu holen",
"Ascending": "Aufsteigend",
"Descending": "Absteigend"
}

View File

@@ -0,0 +1,54 @@
{
"Enterprise-grade relational database": "Base de datos relacional de grado empresarial",
"Connection Type": "Tipo de conexión",
"Host": "Anfitrión",
"Port": "Puerto",
"Service Name": "Nombre del servicio",
"Connection String": "Cadena de conexión",
"Username": "Usuario",
"Password": "Contraseña",
"How you want to connect": "Cómo quieres conectar",
"Database server hostname or IP": "Servidor de base de datos o IP",
"Database port": "Puerto de base de datos",
"Oracle service name": "Nombre del servicio Oracle",
"Full connection string (e.g., host:port/serviceName)": "Cadena de conexión completa (por ejemplo, host:port/serviceName)",
"Connect to Oracle Database using either Service Name (host/port/service) or a full connection string.": "Conéctate a Oracle Database usando un nombre de servicio (host/puerto/servicio) o una cadena de conexión completa.",
"Insert Row": "Insert Row",
"Insert Rows": "Insert Rows",
"Run Custom SQL": "Ejecutar SQL personalizado",
"Update Row": "Actualizar fila",
"Delete Row": "Eliminar fila",
"Find Row": "Buscar fila",
"Insert a row into an Oracle table": "Inserta una fila en una tabla Oracle",
"Insert multiple rows into an Oracle table": "Insertar múltiples filas en una tabla Oracle",
"Execute custom SQL or PL/SQL in Oracle": "Ejecutar SQL o PL/SQL personalizado en Oracle",
"Update rows in an Oracle table": "Actualizar registros en una tabla Oracle",
"Delete rows from an Oracle table": "Eliminar filas de una tabla Oracle",
"Find rows in an Oracle table": "Buscar registros en una tabla Oracle",
"Table Name": "Nombre de tabla",
"Row": "Fila",
"Rows": "Filas",
"Markdown": "Markdown",
"SQL Query": "Consulta SQL",
"Bind Parameters": "Parámetros de enlace",
"Values": "Valores",
"Filter (WHERE)": "Filtrar (WHERE)",
"Column names and values to insert": "Nombres y valores de columna a insertar",
"Array of objects with column names and values": "Matriz de objetos con nombres y valores de columna",
"**DO NOT** insert dynamic input directly into the query. Use bind parameters (:param) to prevent **SQL injection**.": "**NO** inserta entrada dinámica directamente en la consulta. Use parámetros de enlace (:param) para prevenir la **inyección SQL**.",
"SQL or PL/SQL to execute. Use :param for bind parameters.": "SQL o PL/SQL para ejecutar. Utilice :param para parámetros de enlace.",
"Key-value pairs for bind variables": "Pares de clave-valor para variables de enlace",
"Column names and new values to set": "Nombres de columnas y nuevos valores a establecer",
"Conditions to match rows. Empty object updates ALL rows.": "Condiciones para coincidir con las filas. El objeto vacío actualiza TODAS las filas.",
"Conditions to match rows for deletion": "Condiciones para coincidir con las filas para su eliminación",
"Conditions to match rows": "Condiciones para coincidir filas",
"New Row": "Nueva fila",
"Triggers when a new row is created": "Desencadena cuando se crea una nueva fila",
"Order By Column": "Ordenar por columna",
"Order Direction": "Dirección del pedido",
"**NOTE:** Fetches latest rows using the order column (newest first), then keeps polling for new rows.": "**NOTA:** Obtiene las últimas filas usando la columna de orden (más reciente primero), luego sigue leyendo las filas nuevas.",
"Column that increases over time (ID or timestamp)": "Columna que aumenta con el tiempo (ID o marca de tiempo)",
"Sort direction to fetch newest rows first": "Ordenar la dirección para obtener las filas más recientes primero",
"Ascending": "Ascendiente",
"Descending": "Descendiente"
}

View File

@@ -0,0 +1,54 @@
{
"Enterprise-grade relational database": "Base de données relationnelle de niveau entreprise",
"Connection Type": "Type de connexion",
"Host": "Hôte",
"Port": "Port",
"Service Name": "Nom du service",
"Connection String": "Chaîne de connexion",
"Username": "Nom d'utilisateur",
"Password": "Password",
"How you want to connect": "Comment vous voulez vous connecter",
"Database server hostname or IP": "Nom d'hôte ou IP du serveur de base de données",
"Database port": "Port de la base de données",
"Oracle service name": "Nom du service Oracle",
"Full connection string (e.g., host:port/serviceName)": "Chaîne de connexion complète (par exemple, hôte:port/serviceName)",
"Connect to Oracle Database using either Service Name (host/port/service) or a full connection string.": "Connectez-vous à la base de données Oracle en utilisant soit le nom du service (hôte/port/service) soit une chaîne de connexion complète.",
"Insert Row": "Insert Row",
"Insert Rows": "Insert Rows",
"Run Custom SQL": "Exécuter SQL personnalisé",
"Update Row": "Mettre à jour la ligne",
"Delete Row": "Supprimer la ligne",
"Find Row": "Trouver une ligne",
"Insert a row into an Oracle table": "Insérer une ligne dans une table Oracle",
"Insert multiple rows into an Oracle table": "Insérer plusieurs lignes dans une table Oracle",
"Execute custom SQL or PL/SQL in Oracle": "Exécuter SQL personnalisé ou PL/SQL dans Oracle",
"Update rows in an Oracle table": "Mettre à jour les lignes dans une table Oracle",
"Delete rows from an Oracle table": "Supprimer les lignes d'une table Oracle",
"Find rows in an Oracle table": "Trouver des lignes dans une table Oracle",
"Table Name": "Nom de la table",
"Row": "Ligne",
"Rows": "Lignes",
"Markdown": "Markdown",
"SQL Query": "Requête SQL",
"Bind Parameters": "Lier les paramètres",
"Values": "Valeurs",
"Filter (WHERE)": "Filtrer (WHERE)",
"Column names and values to insert": "Noms de colonnes et valeurs à insérer",
"Array of objects with column names and values": "Tableau d'objets avec des noms de colonnes et des valeurs",
"**DO NOT** insert dynamic input directly into the query. Use bind parameters (:param) to prevent **SQL injection**.": "**NE PAS INS** insérer une entrée dynamique directement dans la requête. Utilisez les paramètres de liaison (:param) pour éviter **l'injection SQL**.",
"SQL or PL/SQL to execute. Use :param for bind parameters.": "SQL ou PL/SQL à exécuter. Utilisez :param pour lier les paramètres.",
"Key-value pairs for bind variables": "Paires clé-valeur pour les variables liées",
"Column names and new values to set": "Noms de colonnes et nouvelles valeurs à définir",
"Conditions to match rows. Empty object updates ALL rows.": "Conditions de recherche des lignes. L'objet vide met à jour TOUTES les lignes.",
"Conditions to match rows for deletion": "Conditions de recherche des lignes pour la suppression",
"Conditions to match rows": "Conditions de correspondance des lignes",
"New Row": "Nouvelle ligne",
"Triggers when a new row is created": "Déclenche quand une nouvelle ligne est créée",
"Order By Column": "Trier par colonne",
"Order Direction": "Direction de la commande",
"**NOTE:** Fetches latest rows using the order column (newest first), then keeps polling for new rows.": "**REMARQUE :** Récupère les dernières lignes en utilisant la colonne d'ordre (plus récent en premier), puis continue de voter pour les nouvelles lignes.",
"Column that increases over time (ID or timestamp)": "Colonne qui augmente au fil du temps (ID ou horodatage)",
"Sort direction to fetch newest rows first": "Trier la direction pour récupérer les dernières lignes en premier",
"Ascending": "Ascendant",
"Descending": "Descendant"
}

View File

@@ -0,0 +1,54 @@
{
"Enterprise-grade relational database": "エンタープライズグレードのリレーショナルデータベース",
"Connection Type": "接続タイプ",
"Host": "ホスト",
"Port": "ポート",
"Service Name": "サービス名",
"Connection String": "接続文字列",
"Username": "ユーザー名",
"Password": "Password",
"How you want to connect": "接続方法",
"Database server hostname or IP": "データベースサーバーのホスト名または IP",
"Database port": "データベースポート",
"Oracle service name": "Oracle サービス名",
"Full connection string (e.g., host:port/serviceName)": "完全な接続文字列 (例: host:port/serviceName)",
"Connect to Oracle Database using either Service Name (host/port/service) or a full connection string.": "サービス名 (ホスト/ポート/サービス) または完全な接続文字列を使用して、Oracle Databaseに接続します。",
"Insert Row": "Insert Row",
"Insert Rows": "Insert Rows",
"Run Custom SQL": "カスタム SQL を実行",
"Update Row": "行を更新",
"Delete Row": "行を削除",
"Find Row": "行を検索",
"Insert a row into an Oracle table": "Oracleテーブルに行を挿入",
"Insert multiple rows into an Oracle table": "Oracleテーブルに複数の行を挿入",
"Execute custom SQL or PL/SQL in Oracle": "Oracle でカスタム SQL または PL/SQL を実行します",
"Update rows in an Oracle table": "Oracleテーブルの行を更新",
"Delete rows from an Oracle table": "Oracleテーブルから行を削除",
"Find rows in an Oracle table": "Oracleテーブルの行を検索",
"Table Name": "テーブル名",
"Row": "行",
"Rows": "行",
"Markdown": "Markdown",
"SQL Query": "SQL クエリ",
"Bind Parameters": "パラメータのバインド",
"Values": "値",
"Filter (WHERE)": "フィルター (WHERE)",
"Column names and values to insert": "挿入する列名と値",
"Array of objects with column names and values": "列名と値を持つオブジェクトの配列",
"**DO NOT** insert dynamic input directly into the query. Use bind parameters (:param) to prevent **SQL injection**.": "**クエリに直接動的入力を挿入しないでください** **SQLインジェクション**を防ぐためにバインドパラメータ(:param)を使用してください。",
"SQL or PL/SQL to execute. Use :param for bind parameters.": "SQLまたはPL/SQLを実行します。バインドパラメータに:paramを使用します。",
"Key-value pairs for bind variables": "バインド変数のキーと値のペア",
"Column names and new values to set": "設定する列名と新しい値",
"Conditions to match rows. Empty object updates ALL rows.": "行と一致する条件。空のオブジェクトはすべての行を更新します。",
"Conditions to match rows for deletion": "削除のための行を一致させる条件",
"Conditions to match rows": "行に一致する条件",
"New Row": "新しい行",
"Triggers when a new row is created": "新しい行が作成されたときにトリガーします",
"Order By Column": "並び順",
"Order Direction": "注文の方向",
"**NOTE:** Fetches latest rows using the order column (newest first), then keeps polling for new rows.": "**注意:** 順序列(最初に最新)を使って最新の行をフェッチし、新しい行のポーリングを続けます。",
"Column that increases over time (ID or timestamp)": "時間の経過とともに増加する列IDまたはタイムスタンプ",
"Sort direction to fetch newest rows first": "最新の行を最初に取得するためのソート方向",
"Ascending": "昇順",
"Descending": "降順"
}

View File

@@ -0,0 +1,54 @@
{
"Enterprise-grade relational database": "Enterprise-grade relationele database",
"Connection Type": "Type verbinding",
"Host": "Hostnaam",
"Port": "Poort",
"Service Name": "Service Naam",
"Connection String": "Connectie String",
"Username": "Gebruikersnaam",
"Password": "Wachtwoord",
"How you want to connect": "Hoe u verbinding wilt maken",
"Database server hostname or IP": "Hostnaam van databaseserver of IP",
"Database port": "Database poort",
"Oracle service name": "Oracle servicenaam",
"Full connection string (e.g., host:port/serviceName)": "Volledige connectie string (bijv. host:poort/servicenaam)",
"Connect to Oracle Database using either Service Name (host/port/service) or a full connection string.": "Maak verbinding met Oracle Database met behulp van een servicenaam (host/poort/service) of een volledige verbinding string.",
"Insert Row": "Rijen invoegen",
"Insert Rows": "Insert Rows",
"Run Custom SQL": "Aangepaste SQL uitvoeren",
"Update Row": "Rij bijwerken",
"Delete Row": "Verwijder rij",
"Find Row": "Rij zoeken",
"Insert a row into an Oracle table": "Plaats een rij in een Oracle tafel",
"Insert multiple rows into an Oracle table": "Meerdere rijen invoegen in een Oracle tabel",
"Execute custom SQL or PL/SQL in Oracle": "Aangepaste SQL of PL/SQL in Oracle uitvoeren",
"Update rows in an Oracle table": "Rijen bijwerken in een Oracle tabel",
"Delete rows from an Oracle table": "Rijen uit een Oracle tabel verwijderen",
"Find rows in an Oracle table": "Vind rijen in een Oracle tafel",
"Table Name": "Tafel naam",
"Row": "Rij",
"Rows": "Rijen",
"Markdown": "Markdown",
"SQL Query": "SQL query",
"Bind Parameters": "Bind parameters",
"Values": "Waarden",
"Filter (WHERE)": "Filter (WHERE)",
"Column names and values to insert": "Kolomnamen en waarden om in te voegen",
"Array of objects with column names and values": "Array of objecten met kolomnamen en waarden",
"**DO NOT** insert dynamic input directly into the query. Use bind parameters (:param) to prevent **SQL injection**.": "**NIET** voeg dynamische invoer direct in de query in. Gebruik bindparameters (:param) om **SQL injectie te voorkomen**.",
"SQL or PL/SQL to execute. Use :param for bind parameters.": "SQL of PL/SQL uit te voeren. Gebruik :param voor bind parameters.",
"Key-value pairs for bind variables": "Sleutelwaarde paren voor bind variabelen",
"Column names and new values to set": "Kolomnamen en nieuwe waarden om in te stellen",
"Conditions to match rows. Empty object updates ALL rows.": "Voorwaarden om rijen aan te passen. Leeg object update ALLE rijen.",
"Conditions to match rows for deletion": "Voorwaarden om rijen af te stemmen voor verwijdering",
"Conditions to match rows": "Voorwaarden om rijen aan te passen",
"New Row": "Nieuwe rij",
"Triggers when a new row is created": "Triggert wanneer een nieuwe rij wordt gemaakt",
"Order By Column": "Op kolom sorteren",
"Order Direction": "Order richting",
"**NOTE:** Fetches latest rows using the order column (newest first), then keeps polling for new rows.": "**Let op:** Fetches de nieuwste rijen met behulp van de orderkolom (nieuwste eerst), en blijft de polling voor nieuwe rijen houden.",
"Column that increases over time (ID or timestamp)": "Kolom die verhoogt in de tijd (ID of tijdstempel)",
"Sort direction to fetch newest rows first": "Sorteer richting om de nieuwste rijen eerst op te halen",
"Ascending": "Oplopend",
"Descending": "Aflopend"
}

View File

@@ -0,0 +1,54 @@
{
"Enterprise-grade relational database": "Banco de dados relativo à empresa",
"Connection Type": "Tipo de conexão",
"Host": "Servidor",
"Port": "Porta",
"Service Name": "Nome do Serviço",
"Connection String": "String de conexão",
"Username": "Usuário:",
"Password": "Senha",
"How you want to connect": "Como você deseja se conectar",
"Database server hostname or IP": "hostname do servidor de banco de dados ou IP",
"Database port": "Porta do Banco",
"Oracle service name": "Nome do serviço Oracle",
"Full connection string (e.g., host:port/serviceName)": "String completa de conexão (ex.: host:porto/serviceName)",
"Connect to Oracle Database using either Service Name (host/port/service) or a full connection string.": "Conectar à base de dados Oracle usando o Nome do Serviço (host/porta/serviço) ou uma string de conexão completa.",
"Insert Row": "Insert Row",
"Insert Rows": "Insert Rows",
"Run Custom SQL": "Executar SQL personalizada",
"Update Row": "Atualizar linha",
"Delete Row": "Excluir linha",
"Find Row": "Encontrar linha",
"Insert a row into an Oracle table": "Inserir uma linha em uma tabela Oracle",
"Insert multiple rows into an Oracle table": "Inserir várias linhas em uma tabela Oracle",
"Execute custom SQL or PL/SQL in Oracle": "Executar SQL ou PL/SQL personalizado em Oracle",
"Update rows in an Oracle table": "Atualizar linhas em uma tabela Oracle",
"Delete rows from an Oracle table": "Excluir linhas de uma tabela Oracle",
"Find rows in an Oracle table": "Encontre linhas em uma mesa Oracle",
"Table Name": "Nome da Tabela",
"Row": "Linha",
"Rows": "Linhas",
"Markdown": "Markdown",
"SQL Query": "Consulta SQL",
"Bind Parameters": "Parâmetros de Vinculação",
"Values": "Valores",
"Filter (WHERE)": "Filtro (WHERE)",
"Column names and values to insert": "Nomes de coluna e valores para inserir",
"Array of objects with column names and values": "Array de objetos com nomes e valores de coluna",
"**DO NOT** insert dynamic input directly into the query. Use bind parameters (:param) to prevent **SQL injection**.": "**NÃO** insira entrada dinâmica diretamente na consulta. Use parâmetros de vínculo (:param) para evitar **SQL injection**.",
"SQL or PL/SQL to execute. Use :param for bind parameters.": "SQL ou PL/SQL para executar. Use :param para parâmetros de vinculação.",
"Key-value pairs for bind variables": "Pares chave-valor para variáveis de compilação",
"Column names and new values to set": "Nomes de coluna e novos valores para definir",
"Conditions to match rows. Empty object updates ALL rows.": "Condições para combinar linhas. Objeto vazio atualiza TODAS as linhas.",
"Conditions to match rows for deletion": "Condições para combinar linhas para exclusão",
"Conditions to match rows": "Condições para combinar linhas",
"New Row": "Nova linha",
"Triggers when a new row is created": "Dispara quando uma nova linha é criada",
"Order By Column": "Ordenar por coluna",
"Order Direction": "Direção da ordem",
"**NOTE:** Fetches latest rows using the order column (newest first), then keeps polling for new rows.": "**NOTA:** Busca linhas mais recentes usando a coluna de ordem (mais recente primeiro), então continua fazendo sondagem por novas linhas.",
"Column that increases over time (ID or timestamp)": "Coluna que aumenta ao longo do tempo (ID ou timestamp)",
"Sort direction to fetch newest rows first": "Ordenar direção para buscar linhas mais recentes primeiro",
"Ascending": "Crescente",
"Descending": "Decrescente"
}

View File

@@ -0,0 +1,54 @@
{
"Enterprise-grade relational database": "Enterprise-grade relational database",
"Connection Type": "Connection Type",
"Host": "Host",
"Port": "Port",
"Service Name": "Service Name",
"Connection String": "Connection String",
"Username": "Username",
"Password": "Password",
"How you want to connect": "How you want to connect",
"Database server hostname or IP": "Database server hostname or IP",
"Database port": "Database port",
"Oracle service name": "Oracle service name",
"Full connection string (e.g., host:port/serviceName)": "Full connection string (e.g., host:port/serviceName)",
"Connect to Oracle Database using either Service Name (host/port/service) or a full connection string.": "Connect to Oracle Database using either Service Name (host/port/service) or a full connection string.",
"Insert Row": "Insert Row",
"Insert Rows": "Insert Rows",
"Run Custom SQL": "Run Custom SQL",
"Update Row": "Update Row",
"Delete Row": "Delete Row",
"Find Row": "Find Row",
"Insert a row into an Oracle table": "Insert a row into an Oracle table",
"Insert multiple rows into an Oracle table": "Insert multiple rows into an Oracle table",
"Execute custom SQL or PL/SQL in Oracle": "Execute custom SQL or PL/SQL in Oracle",
"Update rows in an Oracle table": "Update rows in an Oracle table",
"Delete rows from an Oracle table": "Delete rows from an Oracle table",
"Find rows in an Oracle table": "Find rows in an Oracle table",
"Table Name": "Table Name",
"Row": "Row",
"Rows": "Rows",
"Markdown": "Markdown",
"SQL Query": "SQL Query",
"Bind Parameters": "Bind Parameters",
"Values": "Values",
"Filter (WHERE)": "Filter (WHERE)",
"Column names and values to insert": "Column names and values to insert",
"Array of objects with column names and values": "Array of objects with column names and values",
"**DO NOT** insert dynamic input directly into the query. Use bind parameters (:param) to prevent **SQL injection**.": "**DO NOT** insert dynamic input directly into the query. Use bind parameters (:param) to prevent **SQL injection**.",
"SQL or PL/SQL to execute. Use :param for bind parameters.": "SQL or PL/SQL to execute. Use :param for bind parameters.",
"Key-value pairs for bind variables": "Key-value pairs for bind variables",
"Column names and new values to set": "Column names and new values to set",
"Conditions to match rows. Empty object updates ALL rows.": "Conditions to match rows. Empty object updates ALL rows.",
"Conditions to match rows for deletion": "Conditions to match rows for deletion",
"Conditions to match rows": "Conditions to match rows",
"New Row": "New Row",
"Triggers when a new row is created": "Triggers when a new row is created",
"Order By Column": "Order By Column",
"Order Direction": "Order Direction",
"**NOTE:** Fetches latest rows using the order column (newest first), then keeps polling for new rows.": "**NOTE:** Fetches latest rows using the order column (newest first), then keeps polling for new rows.",
"Column that increases over time (ID or timestamp)": "Column that increases over time (ID or timestamp)",
"Sort direction to fetch newest rows first": "Sort direction to fetch newest rows first",
"Ascending": "Ascending",
"Descending": "Descending"
}

View File

@@ -0,0 +1,54 @@
{
"Enterprise-grade relational database": "Enterprise-grade relational database",
"Connection Type": "Connection Type",
"Host": "主机",
"Port": "端口",
"Service Name": "Service Name",
"Connection String": "Connection String",
"Username": "用户名",
"Password": "Password",
"How you want to connect": "How you want to connect",
"Database server hostname or IP": "Database server hostname or IP",
"Database port": "Database port",
"Oracle service name": "Oracle service name",
"Full connection string (e.g., host:port/serviceName)": "Full connection string (e.g., host:port/serviceName)",
"Connect to Oracle Database using either Service Name (host/port/service) or a full connection string.": "Connect to Oracle Database using either Service Name (host/port/service) or a full connection string.",
"Insert Row": "Insert Row",
"Insert Rows": "Insert Rows",
"Run Custom SQL": "Run Custom SQL",
"Update Row": "更新行",
"Delete Row": "删除行",
"Find Row": "Find Row",
"Insert a row into an Oracle table": "Insert a row into an Oracle table",
"Insert multiple rows into an Oracle table": "Insert multiple rows into an Oracle table",
"Execute custom SQL or PL/SQL in Oracle": "Execute custom SQL or PL/SQL in Oracle",
"Update rows in an Oracle table": "Update rows in an Oracle table",
"Delete rows from an Oracle table": "Delete rows from an Oracle table",
"Find rows in an Oracle table": "Find rows in an Oracle table",
"Table Name": "Table Name",
"Row": "Row",
"Rows": "Rows",
"Markdown": "Markdown",
"SQL Query": "SQL Query",
"Bind Parameters": "Bind Parameters",
"Values": "值",
"Filter (WHERE)": "Filter (WHERE)",
"Column names and values to insert": "Column names and values to insert",
"Array of objects with column names and values": "Array of objects with column names and values",
"**DO NOT** insert dynamic input directly into the query. Use bind parameters (:param) to prevent **SQL injection**.": "**DO NOT** insert dynamic input directly into the query. Use bind parameters (:param) to prevent **SQL injection**.",
"SQL or PL/SQL to execute. Use :param for bind parameters.": "SQL or PL/SQL to execute. Use :param for bind parameters.",
"Key-value pairs for bind variables": "Key-value pairs for bind variables",
"Column names and new values to set": "Column names and new values to set",
"Conditions to match rows. Empty object updates ALL rows.": "Conditions to match rows. Empty object updates ALL rows.",
"Conditions to match rows for deletion": "Conditions to match rows for deletion",
"Conditions to match rows": "Conditions to match rows",
"New Row": "New Row",
"Triggers when a new row is created": "Triggers when a new row is created",
"Order By Column": "Order By Column",
"Order Direction": "Order Direction",
"**NOTE:** Fetches latest rows using the order column (newest first), then keeps polling for new rows.": "**NOTE:** Fetches latest rows using the order column (newest first), then keeps polling for new rows.",
"Column that increases over time (ID or timestamp)": "Column that increases over time (ID or timestamp)",
"Sort direction to fetch newest rows first": "Sort direction to fetch newest rows first",
"Ascending": "升序",
"Descending": "降序"
}

View File

@@ -0,0 +1,29 @@
import { createPiece, PieceAuth } from '@activepieces/pieces-framework';
import { PieceCategory } from '@activepieces/shared';
import { oracleDbAuth } from './lib/common/auth';
import { insertRowAction } from './lib/actions/insert-row';
import { insertRowsAction } from './lib/actions/insert-rows';
import { runCustomSqlAction } from './lib/actions/run-custom-sql';
import { updateRowAction } from './lib/actions/update-row';
import { deleteRowAction } from './lib/actions/delete-row';
import { findRowAction } from './lib/actions/find-row';
import { newRowTrigger } from './lib/triggers/new-row';
export const oracleDatabase = createPiece({
displayName: 'Oracle Database',
description: 'Enterprise-grade relational database',
auth: oracleDbAuth,
minimumSupportedRelease: '0.36.1',
logoUrl: 'https://cdn.activepieces.com/pieces/oracle-database.png',
categories: [PieceCategory.DEVELOPER_TOOLS],
authors: ['Prabhukiran161', 'onyedikachi-david'],
actions: [
insertRowAction,
insertRowsAction,
runCustomSqlAction,
updateRowAction,
deleteRowAction,
findRowAction,
],
triggers: [newRowTrigger],
});

View File

@@ -0,0 +1,46 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { oracleDbAuth } from '../common/auth';
import { OracleDbClient } from '../common/client';
import { oracleDbProps } from '../common/props';
export const deleteRowAction = createAction({
auth: oracleDbAuth,
name: 'delete_row',
displayName: 'Delete Row',
description: 'Delete rows from an Oracle table',
props: {
tableName: oracleDbProps.tableName(),
filter: Property.Object({
displayName: 'Filter (WHERE)',
description: 'Conditions to match rows for deletion',
required: true,
defaultValue: { ID: 101 },
}),
},
async run(context) {
const { tableName, filter } = context.propsValue;
if (
typeof filter !== 'object' ||
filter === null ||
Array.isArray(filter)
) {
throw new Error('Filter must be a valid object');
}
if (Object.keys(filter).length === 0) {
throw new Error(
'Filter cannot be empty. Use Run Custom SQL action to delete all rows.'
);
}
try {
const client = new OracleDbClient(context.auth.props);
return await client.deleteRow(tableName, filter as Record<string, unknown>);
} catch (error) {
throw new Error(
`Failed to delete rows from ${tableName}: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
},
});

View File

@@ -0,0 +1,49 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { oracleDbAuth } from '../common/auth';
import { OracleDbClient } from '../common/client';
import { oracleDbProps } from '../common/props';
export const findRowAction = createAction({
auth: oracleDbAuth,
name: 'find_row',
displayName: 'Find Row',
description: 'Find rows in an Oracle table',
props: {
tableName: oracleDbProps.tableName(),
filter: Property.Object({
displayName: 'Filter (WHERE)',
description: 'Conditions to match rows',
required: true,
defaultValue: { ID: 101 },
}),
},
async run(context) {
const { tableName, filter } = context.propsValue;
if (
typeof filter !== 'object' ||
filter === null ||
Array.isArray(filter)
) {
throw new Error('Filter must be a valid object');
}
if (Object.keys(filter).length === 0) {
throw new Error(
'Filter cannot be empty. Use Run Custom SQL action to fetch all rows.'
);
}
try {
const client = new OracleDbClient(context.auth.props);
return await client.findRow(
tableName,
filter as Record<string, unknown>
);
} catch (error) {
throw new Error(
`Failed to find rows in ${tableName}: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
},
});

View File

@@ -0,0 +1,38 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { oracleDbAuth } from '../common/auth';
import { OracleDbClient } from '../common/client';
import { oracleDbProps } from '../common/props';
export const insertRowAction = createAction({
auth: oracleDbAuth,
name: 'insert_row',
displayName: 'Insert Row',
description: 'Insert a row into an Oracle table',
props: {
tableName: oracleDbProps.tableName(),
row: Property.Object({
displayName: 'Row',
description: 'Column names and values to insert',
required: true,
defaultValue: {
COLUMN_NAME: 'value',
},
}),
},
async run(context) {
const { tableName, row } = context.propsValue;
if (typeof row !== 'object' || row === null || Array.isArray(row)) {
throw new Error("Row must be a valid object with column names as keys");
}
try {
const client = new OracleDbClient(context.auth.props);
return await client.insertRow(tableName, row as Record<string, unknown>);
} catch (error) {
throw new Error(
`Failed to insert row into ${tableName}: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
},
});

View File

@@ -0,0 +1,42 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { oracleDbAuth } from '../common/auth';
import { OracleDbClient } from '../common/client';
import { oracleDbProps } from '../common/props';
export const insertRowsAction = createAction({
auth: oracleDbAuth,
name: 'insert_rows',
displayName: 'Insert Rows',
description: 'Insert multiple rows into an Oracle table',
props: {
tableName: oracleDbProps.tableName(),
rows: Property.Array({
displayName: 'Rows',
description: 'Array of objects with column names and values',
required: true,
defaultValue: [
{ COLUMN_1: 'value_a', COLUMN_2: 1 },
{ COLUMN_1: 'value_b', COLUMN_2: 2 },
],
}),
},
async run(context) {
const { tableName, rows } = context.propsValue;
if (!Array.isArray(rows) || rows.length === 0) {
throw new Error('Rows must be a non-empty array of objects');
}
try {
const client = new OracleDbClient(context.auth.props);
return await client.insertRows(
tableName,
rows as Record<string, unknown>[]
);
} catch (error) {
throw new Error(
`Failed to insert rows into ${tableName}: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
},
});

View File

@@ -0,0 +1,41 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { oracleDbAuth } from '../common/auth';
import { OracleDbClient } from '../common/client';
import oracledb from 'oracledb';
export const runCustomSqlAction = createAction({
auth: oracleDbAuth,
name: 'run_custom_sql',
displayName: 'Run Custom SQL',
description: 'Execute custom SQL or PL/SQL in Oracle',
props: {
markdown: Property.MarkDown({
value: `**DO NOT** insert dynamic input directly into the query. Use bind parameters (:param) to prevent **SQL injection**.`,
}),
sql: Property.LongText({
displayName: 'SQL Query',
description: 'SQL or PL/SQL to execute. Use :param for bind parameters.',
required: true,
defaultValue: 'SELECT * FROM employees WHERE department_id = :dept_id',
}),
binds: Property.Object({
displayName: 'Bind Parameters',
description: 'Key-value pairs for bind variables',
required: false,
defaultValue: { dept_id: 90 },
}),
},
async run(context) {
const { sql, binds } = context.propsValue;
try {
const client = new OracleDbClient(context.auth.props);
const bindParams = (binds as oracledb.BindParameters) || {};
return await client.execute(sql, bindParams);
} catch (error) {
throw new Error(
`Failed to execute SQL: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
},
});

View File

@@ -0,0 +1,58 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { oracleDbAuth } from '../common/auth';
import { OracleDbClient } from '../common/client';
import { oracleDbProps } from '../common/props';
export const updateRowAction = createAction({
auth: oracleDbAuth,
name: 'update_row',
displayName: 'Update Row',
description: 'Update rows in an Oracle table',
props: {
tableName: oracleDbProps.tableName(),
values: Property.Object({
displayName: 'Values',
description: 'Column names and new values to set',
required: true,
defaultValue: { SALARY: 8000 },
}),
filter: Property.Object({
displayName: 'Filter (WHERE)',
description: 'Conditions to match rows. Empty object updates ALL rows.',
required: true,
defaultValue: { ID: 101 },
}),
},
async run(context) {
const { tableName, values, filter } = context.propsValue;
if (
typeof values !== 'object' ||
values === null ||
Array.isArray(values) ||
Object.keys(values).length === 0
) {
throw new Error('Values must be a non-empty object');
}
if (
typeof filter !== 'object' ||
filter === null ||
Array.isArray(filter)
) {
throw new Error('Filter must be a valid object');
}
try {
const client = new OracleDbClient(context.auth.props);
return await client.updateRow(
tableName,
values as Record<string, unknown>,
filter as Record<string, unknown>
);
} catch (error) {
throw new Error(
`Failed to update rows in ${tableName}: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
},
});

View File

@@ -0,0 +1,110 @@
import {
PieceAuth,
Property,
StaticPropsValue,
} from '@activepieces/pieces-framework';
import oracledb from 'oracledb';
try {
oracledb.initOracleClient();
} catch (e) {
console.log('Oracle client already initialized or failed to initialize.');
}
export const oracleDbAuth = PieceAuth.CustomAuth({
description: `Connect to Oracle Database using either Service Name (host/port/service) or a full connection string.`,
required: true,
props: {
connectionType: Property.StaticDropdown({
displayName: 'Connection Type',
description: 'How you want to connect',
required: true,
options: {
options: [
{ label: 'Service Name', value: 'serviceName' },
{ label: 'Connection String', value: 'connectionString' },
],
},
defaultValue: 'serviceName',
}),
host: Property.ShortText({
displayName: 'Host',
description: 'Database server hostname or IP',
required: false,
}),
port: Property.Number({
displayName: 'Port',
description: 'Database port',
required: false,
defaultValue: 1521,
}),
serviceName: Property.ShortText({
displayName: 'Service Name',
description: 'Oracle service name',
required: false,
}),
connectionString: Property.LongText({
displayName: 'Connection String',
description: 'Full connection string (e.g., host:port/serviceName)',
required: false,
}),
user: Property.ShortText({
displayName: 'Username',
required: true,
}),
password: PieceAuth.SecretText({
displayName: 'Password',
required: true,
}),
},
validate: async ({ auth }) => {
let connection: oracledb.Connection | undefined;
const typedAuth = auth as StaticPropsValue<(typeof oracleDbAuth)['props']>;
try {
let connectString: string | undefined;
if (typedAuth.connectionType === 'serviceName') {
if (!typedAuth.host || !typedAuth.port || !typedAuth.serviceName) {
return {
valid: false,
error: 'Host, Port, and Service Name are required for this connection type.',
};
}
connectString = `${typedAuth.host}:${typedAuth.port}/${typedAuth.serviceName}`;
} else {
if (!typedAuth.connectionString) {
return {
valid: false,
error: 'Connection String is required for this connection type.',
};
}
connectString = typedAuth.connectionString;
}
connection = await oracledb.getConnection({
user: typedAuth.user,
password: typedAuth.password,
connectString: connectString,
});
return { valid: true };
} catch (e) {
return {
valid: false,
error: (e as Error)?.message || 'Unknown connection error.',
};
} finally {
if (connection) {
try {
await connection.close();
} catch (e) {
console.error('Failed to close Oracle DB connection:', e);
}
}
}
},
});
export type OracleDbAuth = StaticPropsValue<typeof oracleDbAuth.props>;

View File

@@ -0,0 +1,389 @@
import { OracleDbAuth } from './types';
import oracledb from 'oracledb';
interface ExecuteManyResult {
rowsAffected?: number;
}
export class OracleDbClient {
private readonly auth: OracleDbAuth;
private connection: oracledb.Connection | undefined;
constructor(auth: OracleDbAuth) {
this.auth = auth;
}
private async connect(): Promise<void> {
const connectString =
this.auth.connectionType === 'serviceName'
? `${this.auth.host}:${this.auth.port}/${this.auth.serviceName}`
: this.auth.connectionString;
this.connection = await oracledb.getConnection({
user: this.auth.user,
password: this.auth.password,
connectString: connectString,
});
}
public async getTables(): Promise<{ label: string; value: string }[]> {
await this.connect();
if (!this.connection) {
throw new Error('Database connection failed and was not established.');
}
const result = await this.connection.execute<{ TABLE_NAME: string }>(
`SELECT table_name FROM user_tables ORDER BY table_name`,
[],
{ outFormat: oracledb.OUT_FORMAT_OBJECT }
);
await this.close();
return (
result.rows?.map((row) => ({
label: row.TABLE_NAME,
value: row.TABLE_NAME,
})) || []
);
}
public async insertRow(
tableName: string,
rowData: Record<string, unknown>
): Promise<{ success: boolean; rowsAffected: number }> {
await this.connect();
if (!this.connection) {
throw new Error('Database connection failed and was not established.');
}
const columns = Object.keys(rowData);
const values = Object.values(rowData);
const placeholders = columns.map((_, i) => `:${i + 1}`).join(', ');
const quotedColumns = columns.map((c) => `"${c}"`).join(', ');
const quotedTableName = `"${tableName}"`;
const sql = `INSERT INTO ${quotedTableName} (${quotedColumns}) VALUES (${placeholders})`;
try {
const result = await this.connection.execute(sql, values, {
autoCommit: true,
});
await this.close();
return {
success: true,
rowsAffected: result.rowsAffected || 0,
};
} catch (error: any) {
await this.close();
throw this.handleOracleError(error);
}
}
public async insertRows(
tableName: string,
rowsData: Record<string, unknown>[]
): Promise<{ success: boolean; rowsAffected: number }> {
await this.connect();
if (!this.connection) {
throw new Error('Database connection failed and was not established.');
}
const columns = Object.keys(rowsData[0]);
const quotedColumns = columns.map((c) => `"${c}"`).join(', ');
const quotedTableName = `"${tableName}"`;
const placeholders = columns.map((_, i) => `:${i + 1}`).join(', ');
const sql = `INSERT INTO ${quotedTableName} (${quotedColumns}) VALUES (${placeholders})`;
const bindData = rowsData.map((row) => columns.map((col) => row[col]));
try {
const result = await this.connection.executeMany(sql, bindData, {
autoCommit: true,
});
await this.close();
return {
success: true,
rowsAffected: result.rowsAffected || 0,
};
} catch (error: any) {
await this.close();
throw this.handleOracleError(error);
}
}
public async updateRow(
tableName: string,
values: Record<string, unknown>,
filter: Record<string, unknown>
): Promise<{ success: boolean; rowsAffected: number }> {
await this.connect();
if (!this.connection) {
throw new Error('Database connection failed and was not established.');
}
const valueKeys = Object.keys(values);
const filterKeys = Object.keys(filter);
const setClause = valueKeys.map((k) => `"${k}" = :set_${k}`).join(', ');
const whereClause = filterKeys
.map((k) => `"${k}" = :whr_${k}`)
.join(' AND ');
const binds: oracledb.BindParameters = {};
for (const key of valueKeys) {
binds[`set_${key}`] = values[key] as any;
}
for (const key of filterKeys) {
binds[`whr_${key}`] = filter[key] as any;
}
let sql = `UPDATE "${tableName}" SET ${setClause}`;
if (whereClause) {
sql += ` WHERE ${whereClause}`;
}
try {
const result = await this.connection.execute(sql, binds, {
autoCommit: true,
});
await this.close();
return {
success: true,
rowsAffected: result.rowsAffected || 0,
};
} catch (error: any) {
await this.close();
throw this.handleOracleError(error);
}
}
public async deleteRow(
tableName: string,
filter: Record<string, unknown>
): Promise<{ success: boolean; rowsAffected: number }> {
await this.connect();
if (!this.connection) {
throw new Error('Database connection failed and was not established.');
}
const filterKeys = Object.keys(filter);
const whereClause = filterKeys
.map((k) => `"${k}" = :whr_${k}`)
.join(' AND ');
const binds: oracledb.BindParameters = {};
for (const key of filterKeys) {
binds[`whr_${key}`] = filter[key] as any;
}
const sql = `DELETE FROM "${tableName}" WHERE ${whereClause}`;
try {
const result = await this.connection.execute(sql, binds, {
autoCommit: true,
});
await this.close();
return {
success: true,
rowsAffected: result.rowsAffected || 0,
};
} catch (error: any) {
await this.close();
throw this.handleOracleError(error);
}
}
public async findRow(
tableName: string,
filter: Record<string, unknown>
): Promise<unknown[]> {
await this.connect();
if (!this.connection) {
throw new Error('Database connection failed and was not established.');
}
const filterKeys = Object.keys(filter);
const whereClause = filterKeys
.map((k) => `"${k}" = :whr_${k}`)
.join(' AND ');
const binds: oracledb.BindParameters = {};
for (const key of filterKeys) {
binds[`whr_${key}`] = filter[key] as any;
}
const sql = `SELECT * FROM "${tableName}" WHERE ${whereClause}`;
try {
const result = await this.connection.execute(sql, binds, {
outFormat: oracledb.OUT_FORMAT_OBJECT,
});
await this.close();
return (result.rows as unknown[]) || [];
} catch (error: any) {
await this.close();
throw this.handleOracleError(error);
}
}
public async execute(
sql: string,
binds: oracledb.BindParameters
): Promise<{ rows: unknown[]; rowsAffected?: number }> {
await this.connect();
if (!this.connection) {
throw new Error('Database connection failed and was not established.');
}
try {
const result = await this.connection.execute(sql, binds, {
autoCommit: true,
outFormat: oracledb.OUT_FORMAT_OBJECT,
});
await this.close();
return {
rows: (result.rows as unknown[]) || [],
rowsAffected: result.rowsAffected,
};
} catch (error: any) {
await this.close();
throw this.handleOracleError(error);
}
}
public async getNewRows(
tableName: string,
orderByColumn: string,
lastValue: unknown,
filter: Record<string, unknown>
): Promise<Record<string, unknown>[]> {
await this.connect();
if (!this.connection) {
throw new Error('Database connection failed and was not established.');
}
const filterKeys = Object.keys(filter);
const whereConditions = filterKeys.map((k) => `"${k}" = :whr_${k}`);
whereConditions.push(`"${orderByColumn}" > :lastValue`);
const whereClause = whereConditions.join(' AND ');
const binds: Record<string, any> = { lastValue };
for (const key of filterKeys) {
binds[`whr_${key}`] = filter[key];
}
const sql = `SELECT * FROM "${tableName}" WHERE ${whereClause} ORDER BY "${orderByColumn}" ASC`;
const result = await this.connection.execute(sql, binds, {
outFormat: oracledb.OUT_FORMAT_OBJECT,
});
await this.close();
return result.rows as Record<string, unknown>[];
}
public async getLatestRows(
tableName: string,
orderByColumn: string,
filter: Record<string, unknown>
): Promise<oracledb.Result<unknown>> {
await this.connect();
if (!this.connection) {
throw new Error('Database connection failed and was not established.');
}
const filterKeys = Object.keys(filter);
const whereClause = filterKeys
.map((k) => `"${k}" = :whr_${k}`)
.join(' AND ');
const binds: oracledb.BindParameters = {};
for (const key of filterKeys) {
binds[`whr_${key}`] = filter[key] as any;
}
let sql = `SELECT * FROM "${tableName}"`;
if (whereClause) {
sql += ` WHERE ${whereClause}`;
}
sql += ` ORDER BY "${orderByColumn}" DESC FETCH FIRST 5 ROWS ONLY`;
const result = await this.connection.execute(sql, binds, {
outFormat: oracledb.OUT_FORMAT_OBJECT,
});
await this.close();
return result;
}
public async getColumns(
tableName: string
): Promise<{ label: string; value: string }[]> {
await this.connect();
if (!this.connection) {
throw new Error('Database connection failed and was not established.');
}
const result = await this.connection.execute<{ COLUMN_NAME: string }>(
`SELECT column_name FROM user_tab_columns WHERE table_name = :tableName ORDER BY column_id`,
{ tableName },
{ outFormat: oracledb.OUT_FORMAT_OBJECT }
);
await this.close();
return (
result.rows?.map((row) => ({
label: row.COLUMN_NAME,
value: row.COLUMN_NAME,
})) || []
);
}
public async close(): Promise<void> {
if (this.connection) {
await this.connection.close();
this.connection = undefined;
}
}
private handleOracleError(error: any): Error {
const errorNum = error?.errorNum;
let message = error?.message || 'Unknown Oracle error';
// Common Oracle error codes
if (errorNum === 1) {
message = `Unique constraint violated: ${message}`;
} else if (errorNum === 2290 || errorNum === 2291 || errorNum === 2292) {
message = `Constraint violation: ${message}`;
} else if (errorNum === 1400) {
message = `Required column missing: ${message}`;
} else if (errorNum === 904 || errorNum === 942) {
message = `Invalid column or table: ${message}`;
} else if (errorNum === 1722) {
message = `Invalid number format: ${message}`;
} else if (errorNum === 12899) {
message = `Value too large for column: ${message}`;
}
return new Error(message);
}
}

View File

@@ -0,0 +1,64 @@
import { Property } from '@activepieces/pieces-framework';
import { OracleDbClient } from './client';
import { OracleDbAuth } from './types';
import { oracleDbAuth } from './auth';
export const oracleDbProps = {
tableName: () =>
Property.Dropdown({
auth: oracleDbAuth,
displayName: 'Table Name',
required: true,
refreshers: [],
options: async (propsValue) => {
const auth = propsValue.auth;
if (!auth) {
return {
disabled: true,
placeholder: 'Please authenticate first',
options: [],
};
}
const client = new OracleDbClient(auth.props);
const tables = await client.getTables();
return {
disabled: false,
options: tables.map((table) => ({
label: table.label,
value: table.value,
})),
};
},
}),
orderBy: () =>
Property.Dropdown({
auth: oracleDbAuth,
displayName: 'Order By Column',
description: 'Column that increases over time (ID or timestamp)',
required: true,
refreshers: ['tableName'],
options: async (propsValue) => {
const tableName = propsValue['tableName'] as string | undefined;
const auth = propsValue.auth;
if (!auth || !tableName) {
return {
disabled: true,
placeholder: 'Please select a table first',
options: [],
};
}
const client = new OracleDbClient(auth.props);
const columns = await client.getColumns(tableName);
return {
disabled: false,
options: columns.map((col: { label: string; value: string }) => ({
label: col.label,
value: col.value,
})),
};
},
}),
};

View File

@@ -0,0 +1,4 @@
import { StaticPropsValue } from '@activepieces/pieces-framework';
import { oracleDbAuth } from '../common/auth';
export type OracleDbAuth = StaticPropsValue<(typeof oracleDbAuth)['props']>;

View File

@@ -0,0 +1,130 @@
import {
createTrigger,
TriggerStrategy,
PiecePropValueSchema,
Property,
AppConnectionValueForAuthProperty,
} from '@activepieces/pieces-framework';
import {
DedupeStrategy,
Polling,
pollingHelper,
} from '@activepieces/pieces-common';
import crypto from 'crypto';
import dayjs from 'dayjs';
import { oracleDbAuth } from '../common/auth';
import { OracleDbClient } from '../common/client';
import { oracleDbProps } from '../common/props';
import oracledb from 'oracledb';
type OrderDirection = 'ASC' | 'DESC';
const polling: Polling<
AppConnectionValueForAuthProperty<typeof oracleDbAuth>,
{
tableName: string;
orderBy: string;
orderDirection: OrderDirection | undefined;
}
> = {
strategy: DedupeStrategy.LAST_ITEM,
items: async ({ auth, propsValue, lastItemId }) => {
const client = new OracleDbClient(auth.props);
await client['connect']();
if (!client['connection']) {
throw new Error('Database connection failed');
}
const lastItem = lastItemId as string;
const lastOrderKey = lastItem ? lastItem.split('|')[0] : null;
const direction = propsValue.orderDirection || 'DESC';
let sql: string;
const binds: oracledb.BindParameters = {};
if (lastOrderKey === null) {
sql = `SELECT * FROM "${propsValue.tableName}" ORDER BY "${propsValue.orderBy}" ${direction} FETCH FIRST 5 ROWS ONLY`;
} else {
const operator = direction === 'DESC' ? '>=' : '<=';
sql = `SELECT * FROM "${propsValue.tableName}" WHERE "${propsValue.orderBy}" ${operator} :lastKey ORDER BY "${propsValue.orderBy}" ${direction}`;
binds['lastKey'] = lastOrderKey;
}
const result = await client['connection'].execute(sql, binds, {
outFormat: oracledb.OUT_FORMAT_OBJECT,
});
await client.close();
const rows = (result.rows as Record<string, any>[]) || [];
const items = rows.map((row) => {
const rowHash = crypto
.createHash('md5')
.update(JSON.stringify(row))
.digest('hex');
const isTimestamp = dayjs(row[propsValue.orderBy]).isValid();
const orderValue = isTimestamp
? dayjs(row[propsValue.orderBy]).toISOString()
: row[propsValue.orderBy];
return {
id: orderValue + '|' + rowHash,
data: row,
};
});
return items;
},
};
export const newRowTrigger = createTrigger({
auth: oracleDbAuth,
name: 'new_row',
displayName: 'New Row',
description: 'Triggers when a new row is created',
props: {
description: Property.MarkDown({
value: `**NOTE:** Fetches latest rows using the order column (newest first), then keeps polling for new rows.`,
}),
tableName: oracleDbProps.tableName(),
orderBy: oracleDbProps.orderBy(),
orderDirection: Property.StaticDropdown<OrderDirection>({
displayName: 'Order Direction',
description: 'Sort direction to fetch newest rows first',
required: true,
options: {
options: [
{ label: 'Ascending', value: 'ASC' },
{ label: 'Descending', value: 'DESC' },
],
},
defaultValue: 'DESC',
}),
},
type: TriggerStrategy.POLLING,
sampleData: {},
async onEnable(context) {
await pollingHelper.onEnable(polling, {
store: context.store,
propsValue: context.propsValue,
auth: context.auth,
});
},
async onDisable(context) {
await pollingHelper.onDisable(polling, {
store: context.store,
propsValue: context.propsValue,
auth: context.auth,
});
},
async run(context) {
return await pollingHelper.poll(polling, context);
},
async test(context) {
return await pollingHelper.test(polling, context);
},
});