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,33 @@
{
"extends": [
"../../../../.eslintrc.base.json"
],
"ignorePatterns": [
"!**/*"
],
"overrides": [
{
"files": [
"*.ts",
"*.tsx",
"*.js",
"*.jsx"
],
"rules": {}
},
{
"files": [
"*.ts",
"*.tsx"
],
"rules": {}
},
{
"files": [
"*.js",
"*.jsx"
],
"rules": {}
}
]
}

View File

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

View File

@@ -0,0 +1,10 @@
{
"name": "@activepieces/piece-quickbase",
"version": "0.0.2",
"type": "commonjs",
"main": "./src/index.js",
"types": "./src/index.d.ts",
"dependencies": {
"tslib": "^2.3.0"
}
}

View File

@@ -0,0 +1,51 @@
{
"name": "pieces-quickbase",
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/pieces/community/quickbase/src",
"projectType": "library",
"release": {
"version": {
"manifestRootsToUpdate": [
"dist/{projectRoot}"
],
"currentVersionResolver": "git-tag",
"fallbackCurrentVersionResolver": "disk"
}
},
"tags": [],
"targets": {
"build": {
"executor": "@nx/js:tsc",
"outputs": [
"{options.outputPath}"
],
"options": {
"outputPath": "dist/packages/pieces/community/quickbase",
"tsConfig": "packages/pieces/community/quickbase/tsconfig.lib.json",
"packageJson": "packages/pieces/community/quickbase/package.json",
"main": "packages/pieces/community/quickbase/src/index.ts",
"assets": [
"packages/pieces/community/quickbase/*.md",
{
"input": "packages/pieces/community/quickbase/src/i18n",
"output": "./src/i18n",
"glob": "**/!(i18n.json)"
}
],
"buildableProjectDepsInPackageJsonType": "dependencies",
"updateBuildableProjectDepsInPackageJson": true
}
},
"nx-release-publish": {
"options": {
"packageRoot": "dist/{projectRoot}"
}
},
"lint": {
"executor": "@nx/eslint:lint",
"outputs": [
"{options.outputFile}"
]
}
}
}

View File

@@ -0,0 +1,69 @@
{
"Realm Hostname": "Realm Hostname",
"User Token": "Benutzer-Token",
"Enter your Quickbase Realm Hostname (e.g., yourrealm.quickbase.com)": "Geben Sie Ihren Quickbase Realm Hostnamen ein (z.B. deinrealm.quickbase.com)",
"Enter your Quickbase User Token": "Geben Sie Ihren Quickbase User Token ein",
"\n## Quickbase Authentication Setup\n\n### 1. Get Your User Token\n- Log in to your Quickbase account\n- Go to **My Preferences** → **My User Information**\n- Click on **Manage User Tokens**\n- Click **New User Token**\n- Enter a name for your token and click **Create**\n- Copy the generated token (it will only be shown once)\n\n### 2. Required Permissions\nYour user token needs access to:\n- Read/write permissions for the apps and tables you want to use\n- Admin permissions for creating/deleting records (if needed)\n\n**S": "\n## Schnellbase-Authentifizierung einrichten\n\n### 1. Holen Sie sich Ihr Benutzertoken\n- Einloggen in Ihr Quickbase-Konto\n- Gehen Sie zu **Meine Einstellungen** → **Meine Benutzerinformationen**\n- Klicken Sie auf **Benutzertoken verwalten**\n- Klicken Sie auf **Neues Benutzerzeichen**\n- Geben Sie einen Namen für Ihr Token ein und klicken Sie auf **Erstellen**\n- Kopieren Sie das generierte Token (es wird nur einmal angezeigt)\n\n### 2. Benötigte Berechtigungen\nIhr Benutzertoken benötigt Zugriff auf:\n- Lese-/Schreibrechte für die Apps und Tabellen, die Sie verwenden möchten\n- Administratorrechte für das Erstellen/Löschen von Datensätzen (falls erforderlich)\n\n**Sicherheits-Hinweis:** Bewahren Sie Ihr Benutzertoken sicher - es bietet Zugriff auf Ihre Quickbase-Daten.\n",
"Create Record": "Datensatz erstellen",
"Update Record": "Datensatz aktualisieren",
"Delete Record": "Datensatz löschen",
"Find Record": "Datensatz finden",
"Find or Create Record": "Datensatz suchen oder erstellen",
"Create / Update Records From Array": "Datensätze aus Array erstellen / aktualisieren",
"Custom API Call": "Eigener API-Aufruf",
"Create a new record in a Quickbase table": "Neuen Datensatz in einer Quickbase-Tabelle erstellen",
"Update an existing record in a Quickbase table": "Aktualisiere einen vorhandenen Datensatz in einer Quickbase-Tabelle",
"Delete a record from a Quickbase table": "Datensatz aus einer Quickbase-Tabelle löschen",
"Search for records in a Quickbase table using filters": "Suchen Sie nach Datensätzen in einer Quickbase-Tabelle mit Filtern",
"Find an existing record or create a new one if not found": "Finde einen vorhandenen Eintrag oder erstelle einen neuen wenn nicht gefunden",
"Bulk create or update multiple records based on a merge key": "Mehrere Datensätze erstellen oder aktualisieren basierend auf einem Merge Key",
"Make a custom API call to a specific endpoint": "Einen benutzerdefinierten API-Aufruf an einen bestimmten Endpunkt machen",
"App": "App",
"Table": "Tisch",
"Field Values": "Feldwerte",
"Record": "Datensatz",
"Filters": "Filter",
"Sort Field": "Sortierfeld",
"Sort Order": "Sortierung",
"Maximum Records": "Maximale Datensätze",
"Merge Field": "Merge Feld",
"Fields": "Felder",
"Records": "Datensätze",
"Merge Behavior": "Merge Verhalten",
"Method": "Methode",
"Headers": "Kopfzeilen",
"Query Parameters": "Abfrageparameter",
"Body": "Körper",
"Response is Binary ?": "Antwort ist binär?",
"No Error on Failure": "Kein Fehler bei Fehler",
"Timeout (in seconds)": "Timeout (in Sekunden)",
"Select the Quickbase app": "Wählen Sie die Quickbase-App",
"Select the table": "Tabelle auswählen",
"Select and set values for table fields": "Wählen und setzen Sie Werte für Tabellenfelder",
"Select a record from the table": "Wählen Sie einen Datensatz aus der Tabelle",
"Filter criteria to find records. Use field names or field IDs as keys.": "Kriterien filtern, um Datensätze zu finden. Feldnamen oder Feld-IDs als Schlüssel verwenden.",
"Field to sort records by (optional)": "Feld zum Sortieren der Datensätze nach (optional)",
"Order to sort records": "Sortieren von Datensätzen",
"Maximum number of records to return": "Maximale Anzahl der zurückzusenden Datensätze",
"Field to use for matching existing records (for upsert operations)": "Feld für die Übereinstimmung mit existierenden Datensätzen (für Upsert-Operationen)",
"Map the fields to update. Use field names or field IDs.": "Die zu aktualisierenden Felder zuordnen. Feldnamen oder Feld-IDs verwenden.",
"Array of records to create or update": "Anordnung der zu erstellenden oder aktualisierenden Datensätze",
"How to handle existing records when upserting": "Umgang mit existierenden Datensätzen beim Hochladen",
"Authorization headers are injected automatically from your connection.": "Autorisierungs-Header werden automatisch von Ihrer Verbindung injiziert.",
"Enable for files like PDFs, images, etc..": "Aktivieren für Dateien wie PDFs, Bilder, etc..",
"Ascending (A-Z, 1-9)": "Aufsteigend (A-Z, 1-9)",
"Descending (Z-A, 9-1)": "Absteigend (Z-A, 9-1)",
"Merge and overwrite all fields": "Alle Felder zusammenführen und überschreiben",
"Merge and update only provided fields": "Nur angegebene Felder zusammenführen und aktualisieren",
"Always create new records (no merge)": "Immer neue Datensätze erstellen (keine Zusammenführung)",
"GET": "ERHALTEN",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "LÖSCHEN",
"HEAD": "HEAD",
"New Record": "Neuer Datensatz",
"New or Updated Record": "Neuer oder aktualisierter Datensatz",
"Triggers when a new record is created in a Quickbase table": "Löst aus, wenn ein neuer Datensatz in einer Quickbase-Tabelle erstellt wird",
"Triggers when a record is created or updated in a Quickbase table": "Löst aus, wenn ein Datensatz in einer Quickbase-Tabelle erstellt oder aktualisiert wird"
}

View File

@@ -0,0 +1,69 @@
{
"Realm Hostname": "Realm Hostname",
"User Token": "Token de usuario",
"Enter your Quickbase Realm Hostname (e.g., yourrealm.quickbase.com)": "Introduzca su nombre de host de Realm de Quickbase (por ejemplo, turealm.quickbase.com)",
"Enter your Quickbase User Token": "Introduzca su Token de usuario de Quickbase",
"\n## Quickbase Authentication Setup\n\n### 1. Get Your User Token\n- Log in to your Quickbase account\n- Go to **My Preferences** → **My User Information**\n- Click on **Manage User Tokens**\n- Click **New User Token**\n- Enter a name for your token and click **Create**\n- Copy the generated token (it will only be shown once)\n\n### 2. Required Permissions\nYour user token needs access to:\n- Read/write permissions for the apps and tables you want to use\n- Admin permissions for creating/deleting records (if needed)\n\n**S": "\n## Quickbase Authentication Setup\n\n### 1. Get Your User Token\n- Log in to your Quickbase account\n- Go to **My Preferences** → **My User Information**\n- Click on **Manage User Tokens**\n- Click **New User Token**\n- Enter a name for your token and click **Create**\n- Copy the generated token (it will only be shown once)\n\n### 2. Required Permissions\nYour user token needs access to:\n- Read/write permissions for the apps and tables you want to use\n- Admin permissions for creating/deleting records (if needed)\n\n**Security Note:** Keep your user token secure - it provides access to your Quickbase data.\n",
"Create Record": "Crear registro",
"Update Record": "Actualizar registro",
"Delete Record": "Eliminar registro",
"Find Record": "Buscar registro",
"Find or Create Record": "Buscar o crear registro",
"Create / Update Records From Array": "Crear / Actualizar registros desde matriz",
"Custom API Call": "Llamada API personalizada",
"Create a new record in a Quickbase table": "Crear un nuevo registro en una tabla de Quickbase",
"Update an existing record in a Quickbase table": "Actualizar un registro existente en una tabla de Quickbase",
"Delete a record from a Quickbase table": "Eliminar un registro de una tabla de Quickbase",
"Search for records in a Quickbase table using filters": "Buscar registros en una tabla de Quickbase usando filtros",
"Find an existing record or create a new one if not found": "Encontrar un registro existente o crear uno nuevo si no se encuentra",
"Bulk create or update multiple records based on a merge key": "Crear o actualizar múltiples registros en masa basándose en una clave de fusión",
"Make a custom API call to a specific endpoint": "Hacer una llamada API personalizada a un extremo específico",
"App": "App",
"Table": "Tabla",
"Field Values": "Valores de campo",
"Record": "Grabar",
"Filters": "Filtros",
"Sort Field": "Ordenar campo",
"Sort Order": "Ordenar",
"Maximum Records": "Máximo de registros",
"Merge Field": "Fusionar Campo",
"Fields": "Campos",
"Records": "Registros",
"Merge Behavior": "Combinar comportamiento",
"Method": "Método",
"Headers": "Encabezados",
"Query Parameters": "Parámetros de consulta",
"Body": "Cuerpo",
"Response is Binary ?": "¿Respuesta es binaria?",
"No Error on Failure": "No hay ningún error en fallo",
"Timeout (in seconds)": "Tiempo de espera (en segundos)",
"Select the Quickbase app": "Selecciona la aplicación de Quickbase",
"Select the table": "Seleccione la tabla",
"Select and set values for table fields": "Seleccionar y establecer valores para los campos de tabla",
"Select a record from the table": "Seleccione un registro de la tabla",
"Filter criteria to find records. Use field names or field IDs as keys.": "Filtrar criterios para encontrar registros. Utilice nombres de campos o identificadores de campo como claves.",
"Field to sort records by (optional)": "Campo para ordenar registros por (opcional)",
"Order to sort records": "Ordenar registros",
"Maximum number of records to return": "Número máximo de registros a devolver",
"Field to use for matching existing records (for upsert operations)": "Campo a usar para coincidir registros existentes (para operaciones de actualización)",
"Map the fields to update. Use field names or field IDs.": "Mapear los campos a actualizar. Utilice nombres de campos o ID de campos.",
"Array of records to create or update": "Arreglo de registros para crear o actualizar",
"How to handle existing records when upserting": "Cómo manejar registros existentes al actualizar",
"Authorization headers are injected automatically from your connection.": "Las cabeceras de autorización se inyectan automáticamente desde tu conexión.",
"Enable for files like PDFs, images, etc..": "Activar para archivos como PDF, imágenes, etc.",
"Ascending (A-Z, 1-9)": "Ascendente (A-Z, 1-9)",
"Descending (Z-A, 9-1)": "Descendente (Z-A, 9-1)",
"Merge and overwrite all fields": "Fusionar y sobrescribir todos los campos",
"Merge and update only provided fields": "Fusionar y actualizar sólo los campos proporcionados",
"Always create new records (no merge)": "Crear siempre nuevos registros (sin fusionar)",
"GET": "RECOGER",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "BORRAR",
"HEAD": "LIMPIO",
"New Record": "Nuevo registro",
"New or Updated Record": "Registro nuevo o actualizado",
"Triggers when a new record is created in a Quickbase table": "Dispara cuando se crea un nuevo registro en una tabla de Quickbase",
"Triggers when a record is created or updated in a Quickbase table": "Dispara cuando un registro es creado o actualizado en una tabla de Quickbase"
}

View File

@@ -0,0 +1,69 @@
{
"Realm Hostname": "Realm Hostname",
"User Token": "Jeton d'utilisateur",
"Enter your Quickbase Realm Hostname (e.g., yourrealm.quickbase.com)": "Entrez votre nom d'hôte du Realm Quickbase (par exemple, votrerealm.quickbase.com)",
"Enter your Quickbase User Token": "Entrez votre jeton d'utilisateur Quickbase",
"\n## Quickbase Authentication Setup\n\n### 1. Get Your User Token\n- Log in to your Quickbase account\n- Go to **My Preferences** → **My User Information**\n- Click on **Manage User Tokens**\n- Click **New User Token**\n- Enter a name for your token and click **Create**\n- Copy the generated token (it will only be shown once)\n\n### 2. Required Permissions\nYour user token needs access to:\n- Read/write permissions for the apps and tables you want to use\n- Admin permissions for creating/deleting records (if needed)\n\n**S": "\n## Quickbase Authentication Setup\n\n### 1. Get Your User Token\n- Log in to your Quickbase account\n- Go to **My Preferences** → **My User Information**\n- Click on **Manage User Tokens**\n- Click **New User Token**\n- Enter a name for your token and click **Create**\n- Copy the generated token (it will only be shown once)\n\n### 2. Required Permissions\nYour user token needs access to:\n- Read/write permissions for the apps and tables you want to use\n- Admin permissions for creating/deleting records (if needed)\n\n**Security Note:** Keep your user token secure - it provides access to your Quickbase data.\n",
"Create Record": "Créer un enregistrement",
"Update Record": "Mettre à jour l'enregistrement",
"Delete Record": "Supprimer l'enregistrement",
"Find Record": "Rechercher un enregistrement",
"Find or Create Record": "Trouver ou créer un enregistrement",
"Create / Update Records From Array": "Créer / Mettre à jour les enregistrements depuis un tableau",
"Custom API Call": "Appel d'API personnalisé",
"Create a new record in a Quickbase table": "Créer un nouvel enregistrement dans un tableau de base rapide",
"Update an existing record in a Quickbase table": "Mettre à jour un enregistrement existant dans une table Quickbase",
"Delete a record from a Quickbase table": "Supprimer un enregistrement d'une table de base rapide",
"Search for records in a Quickbase table using filters": "Rechercher des enregistrements dans une table Quickbase à l'aide de filtres",
"Find an existing record or create a new one if not found": "Trouver un enregistrement existant ou en créer un nouveau s'il n'est pas trouvé",
"Bulk create or update multiple records based on a merge key": "Créer ou mettre à jour en bloc plusieurs enregistrements basés sur une clé de fusion",
"Make a custom API call to a specific endpoint": "Passer un appel API personnalisé à un endpoint spécifique",
"App": "Application",
"Table": "Tableau",
"Field Values": "Valeurs du champ",
"Record": "Enregistrements",
"Filters": "Filtres",
"Sort Field": "Champ de tri",
"Sort Order": "Ordre de tri",
"Maximum Records": "Nombre maximum d'enregistrements",
"Merge Field": "Champ de fusion",
"Fields": "Champs",
"Records": "Enregistrements",
"Merge Behavior": "Comportement de fusion",
"Method": "Méthode",
"Headers": "Headers",
"Query Parameters": "Paramètres de requête",
"Body": "Body",
"Response is Binary ?": "La réponse est Binaire ?",
"No Error on Failure": "Aucune erreur en cas d'échec",
"Timeout (in seconds)": "Délai d'expiration (en secondes)",
"Select the Quickbase app": "Sélectionnez l'application Quickbase",
"Select the table": "Sélectionnez la table",
"Select and set values for table fields": "Sélectionner et définir des valeurs pour les champs du tableau",
"Select a record from the table": "Sélectionnez un enregistrement dans la table",
"Filter criteria to find records. Use field names or field IDs as keys.": "Filtrer les critères pour trouver les enregistrements. Utiliser les noms des champs ou les identifiants des champs comme clés.",
"Field to sort records by (optional)": "Champ à trier les enregistrements par (optionnel)",
"Order to sort records": "Ordre de tri des enregistrements",
"Maximum number of records to return": "Nombre maximum d'enregistrements à retourner",
"Field to use for matching existing records (for upsert operations)": "Champ à utiliser pour les enregistrements existants correspondants (pour les opérations de mise à jour)",
"Map the fields to update. Use field names or field IDs.": "Mappez les champs à mettre à jour. Utilisez les noms de champs ou les identifiants de champs.",
"Array of records to create or update": "Tableau des enregistrements à créer ou à mettre à jour",
"How to handle existing records when upserting": "Comment gérer les enregistrements existants lors de l'insertion",
"Authorization headers are injected automatically from your connection.": "Les Headers d'autorisation sont injectés automatiquement à partir de votre connexion.",
"Enable for files like PDFs, images, etc..": "Activer pour les fichiers comme les PDFs, les images, etc.",
"Ascending (A-Z, 1-9)": "Ascendant (A-Z, 1-9)",
"Descending (Z-A, 9-1)": "Décroissant (Z-A, 9-1)",
"Merge and overwrite all fields": "Fusionner et écraser tous les champs",
"Merge and update only provided fields": "Fusionner et mettre à jour uniquement les champs fournis",
"Always create new records (no merge)": "Toujours créer de nouveaux enregistrements (sans fusion)",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD",
"New Record": "Nouvel enregistrement",
"New or Updated Record": "Nouvel enregistrement ou mis à jour",
"Triggers when a new record is created in a Quickbase table": "Déclenche lorsqu'un nouvel enregistrement est créé dans une table de base rapide",
"Triggers when a record is created or updated in a Quickbase table": "Déclenche lorsqu'un enregistrement est créé ou mis à jour dans une table de base rapide"
}

View File

@@ -0,0 +1,69 @@
{
"Realm Hostname": "Realm Hostname",
"User Token": "User Token",
"Enter your Quickbase Realm Hostname (e.g., yourrealm.quickbase.com)": "Quickbase Realm のホスト名を入力してください(例:yourrealm.quickbase.com)",
"Enter your Quickbase User Token": "Quickbaseのユーザートークンを入力してください",
"\n## Quickbase Authentication Setup\n\n### 1. Get Your User Token\n- Log in to your Quickbase account\n- Go to **My Preferences** → **My User Information**\n- Click on **Manage User Tokens**\n- Click **New User Token**\n- Enter a name for your token and click **Create**\n- Copy the generated token (it will only be shown once)\n\n### 2. Required Permissions\nYour user token needs access to:\n- Read/write permissions for the apps and tables you want to use\n- Admin permissions for creating/deleting records (if needed)\n\n**S": "\n## Quickbase Authentication Setup\n\n### 1. Get Your User Token\n- Log in to your Quickbase account\n- Go to **My Preferences** → **My User Information**\n- Click on **Manage User Tokens**\n- Click **New User Token**\n- Enter a name for your token and click **Create**\n- Copy the generated token (it will only be shown once)\n\n### 2. Required Permissions\nYour user token needs access to:\n- Read/write permissions for the apps and tables you want to use\n- Admin permissions for creating/deleting records (if needed)\n\n**Security Note:** Keep your user token secure - it provides access to your Quickbase data.\n",
"Create Record": "レコードを作成",
"Update Record": "更新記録",
"Delete Record": "レコードを削除",
"Find Record": "レコードを検索",
"Find or Create Record": "レコードの検索または作成",
"Create / Update Records From Array": "配列からレコードを作成/更新",
"Custom API Call": "カスタムAPI通話",
"Create a new record in a Quickbase table": "クイックベースのテーブルに新しいレコードを作成します",
"Update an existing record in a Quickbase table": "Quickbase テーブル内の既存のレコードを更新",
"Delete a record from a Quickbase table": "クイックベーステーブルからレコードを削除します",
"Search for records in a Quickbase table using filters": "フィルタを使用してクイックベーステーブル内のレコードを検索",
"Find an existing record or create a new one if not found": "既存のレコードを見つけるか、見つからない場合は新しいレコードを作成します",
"Bulk create or update multiple records based on a merge key": "マージキーに基づいて複数のレコードを一括作成または更新します",
"Make a custom API call to a specific endpoint": "特定のエンドポイントへのカスタム API コールを実行します。",
"App": "App",
"Table": "表",
"Field Values": "フィールド値",
"Record": "レコード",
"Filters": "絞り込み",
"Sort Field": "ソートフィールド",
"Sort Order": "並び順",
"Maximum Records": "最大レコード",
"Merge Field": "統合フィールド",
"Fields": "フィールド",
"Records": "レコード",
"Merge Behavior": "統合の動作",
"Method": "方法",
"Headers": "ヘッダー",
"Query Parameters": "クエリパラメータ",
"Body": "本文",
"Response is Binary ?": "応答はバイナリですか?",
"No Error on Failure": "失敗時にエラーはありません",
"Timeout (in seconds)": "タイムアウト(秒)",
"Select the Quickbase app": "Quickbaseアプリを選択",
"Select the table": "テーブルを選択",
"Select and set values for table fields": "テーブルフィールドの値の選択と設定",
"Select a record from the table": "テーブルからレコードを選択",
"Filter criteria to find records. Use field names or field IDs as keys.": "レコードを検索するために条件をフィルタします。フィールド名またはフィールドIDをキーとして使用します。",
"Field to sort records by (optional)": "レコードをソートするフィールド (任意)",
"Order to sort records": "レコードを並べ替える注文",
"Maximum number of records to return": "Maximum number of records to return",
"Field to use for matching existing records (for upsert operations)": "既存のレコードの一致に使用するフィールド (アップサート操作用)",
"Map the fields to update. Use field names or field IDs.": "更新するフィールドをマップします。フィールド名またはフィールドIDを使用します。",
"Array of records to create or update": "作成または更新するレコードの配列",
"How to handle existing records when upserting": "アップサート時に既存のレコードを処理する方法",
"Authorization headers are injected automatically from your connection.": "認証ヘッダは接続から自動的に注入されます。",
"Enable for files like PDFs, images, etc..": "PDF、画像などのファイルを有効にします。",
"Ascending (A-Z, 1-9)": "昇順 (A-Z, 1-9)",
"Descending (Z-A, 9-1)": "降順",
"Merge and overwrite all fields": "すべてのフィールドをマージして上書きする",
"Merge and update only provided fields": "指定されたフィールドのみをマージして更新",
"Always create new records (no merge)": "常に新しいレコードを作成(マージなし)",
"GET": "取得",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "削除",
"HEAD": "頭",
"New Record": "新しいレコード",
"New or Updated Record": "新規または更新されたレコード",
"Triggers when a new record is created in a Quickbase table": "Quickbaseテーブルに新しいレコードが作成されたときにトリガーします",
"Triggers when a record is created or updated in a Quickbase table": "Quickbaseテーブルでレコードが作成または更新されたときにトリガーします"
}

View File

@@ -0,0 +1,69 @@
{
"Realm Hostname": "Realm Hostname",
"User Token": "Gebruiker Token",
"Enter your Quickbase Realm Hostname (e.g., yourrealm.quickbase.com)": "Voer uw Quickbase Realm Hostname in (bijv. uwrealm.quickbase.com)",
"Enter your Quickbase User Token": "Voer uw Quickbase User Token in",
"\n## Quickbase Authentication Setup\n\n### 1. Get Your User Token\n- Log in to your Quickbase account\n- Go to **My Preferences** → **My User Information**\n- Click on **Manage User Tokens**\n- Click **New User Token**\n- Enter a name for your token and click **Create**\n- Copy the generated token (it will only be shown once)\n\n### 2. Required Permissions\nYour user token needs access to:\n- Read/write permissions for the apps and tables you want to use\n- Admin permissions for creating/deleting records (if needed)\n\n**S": "\n## Quickbase Authentication Setup\n\n### 1. Get Your User Token\n- Log in to your Quickbase account\n- Go to **My Preferences** → **My User Information**\n- Click on **Manage User Tokens**\n- Click **New User Token**\n- Enter a name for your token and click **Create**\n- Copy the generated token (it will only be shown once)\n\n### 2. Required Permissions\nYour user token needs access to:\n- Read/write permissions for the apps and tables you want to use\n- Admin permissions for creating/deleting records (if needed)\n\n**Security Note:** Keep your user token secure - it provides access to your Quickbase data.\n",
"Create Record": "Record Maken",
"Update Record": "Update Record",
"Delete Record": "Record verwijderen",
"Find Record": "Vind Record",
"Find or Create Record": "Zoek of creëer Record",
"Create / Update Records From Array": "Maak / Update Records Uit Array",
"Custom API Call": "Custom API Call",
"Create a new record in a Quickbase table": "Maak een nieuw record in een Quickbase tabel",
"Update an existing record in a Quickbase table": "Een bestaand record in een Quickbase tabel bijwerken",
"Delete a record from a Quickbase table": "Verwijder een record uit een Quickbase tabel",
"Search for records in a Quickbase table using filters": "Zoek naar records in een Quickbase tabel met behulp van filters",
"Find an existing record or create a new one if not found": "Zoek een bestaand record of maak een nieuw record indien niet gevonden",
"Bulk create or update multiple records based on a merge key": "Bulk maken of bijwerken van meerdere records op basis van een samenvoeg-sleutel",
"Make a custom API call to a specific endpoint": "Maak een aangepaste API call naar een specifiek eindpunt",
"App": "App",
"Table": "Tabel",
"Field Values": "Veld Waarden",
"Record": "Opnemen",
"Filters": "Filters",
"Sort Field": "Sorteren op veld",
"Sort Order": "Sorteren bestelling",
"Maximum Records": "Maximum Records",
"Merge Field": "Veld samenvoegen",
"Fields": "Velden",
"Records": "Records",
"Merge Behavior": "Samenvoegen van gedrag",
"Method": "Methode",
"Headers": "Kopteksten",
"Query Parameters": "Query parameters",
"Body": "Lichaam",
"Response is Binary ?": "Antwoord is binair?",
"No Error on Failure": "Geen fout bij fout",
"Timeout (in seconds)": "Time-out (in seconden)",
"Select the Quickbase app": "Selecteer de Quickbase app",
"Select the table": "Selecteer de tabel",
"Select and set values for table fields": "Selecteer en stel waarden in voor de tabelvelden",
"Select a record from the table": "Selecteer een record uit de tabel",
"Filter criteria to find records. Use field names or field IDs as keys.": "Filter criteria om records te vinden. Gebruik veldnamen of veldIDs als sleutels.",
"Field to sort records by (optional)": "Veld voor het sorteren van records op (optioneel)",
"Order to sort records": "Orden om records te sorteren",
"Maximum number of records to return": "Maximum aantal records om te retourneren",
"Field to use for matching existing records (for upsert operations)": "Te gebruiken veld voor overeenkomende bestaande records (voor upsert bewerkingen)",
"Map the fields to update. Use field names or field IDs.": "Kaart van de velden om bij te werken. Gebruik veldnamen of veldID's.",
"Array of records to create or update": "Array van records om te maken of bij te werken",
"How to handle existing records when upserting": "Hoe om te gaan met bestaande records bij upserting",
"Authorization headers are injected automatically from your connection.": "Autorisatie headers worden automatisch geïnjecteerd vanuit uw verbinding.",
"Enable for files like PDFs, images, etc..": "Inschakelen voor bestanden zoals PDF's, afbeeldingen etc..",
"Ascending (A-Z, 1-9)": "Oplopend (A-Z, 1-9)",
"Descending (Z-A, 9-1)": "Aflopend (Z-A, 9-1)",
"Merge and overwrite all fields": "Alle velden samenvoegen en overschrijven",
"Merge and update only provided fields": "Alleen verstrekte velden samenvoegen en bijwerken",
"Always create new records (no merge)": "Altijd nieuwe records maken (geen samenvoegen)",
"GET": "KRIJG",
"POST": "POSTE",
"PATCH": "BEKIJK",
"PUT": "PUT",
"DELETE": "VERWIJDEREN",
"HEAD": "HOOFD",
"New Record": "Nieuwe Record",
"New or Updated Record": "Nieuwe of bijgewerkte record",
"Triggers when a new record is created in a Quickbase table": "Triggert wanneer een nieuw record wordt gemaakt in een Quickbase tabel",
"Triggers when a record is created or updated in a Quickbase table": "Triggert wanneer een record wordt aangemaakt of bijgewerkt in een Quickbase tabel"
}

View File

@@ -0,0 +1,69 @@
{
"Realm Hostname": "Realm Hostname",
"User Token": "Token do usuário",
"Enter your Quickbase Realm Hostname (e.g., yourrealm.quickbase.com)": "Digite seu nome de Host do Realm Quickbase (por exemplo, seurealm.quickbase.com)",
"Enter your Quickbase User Token": "Digite seu Token de usuário Quickbase",
"\n## Quickbase Authentication Setup\n\n### 1. Get Your User Token\n- Log in to your Quickbase account\n- Go to **My Preferences** → **My User Information**\n- Click on **Manage User Tokens**\n- Click **New User Token**\n- Enter a name for your token and click **Create**\n- Copy the generated token (it will only be shown once)\n\n### 2. Required Permissions\nYour user token needs access to:\n- Read/write permissions for the apps and tables you want to use\n- Admin permissions for creating/deleting records (if needed)\n\n**S": "\n## Quickbase Authentication Setup\n\n### 1. Get Your User Token\n- Log in to your Quickbase account\n- Go to **My Preferences** → **My User Information**\n- Click on **Manage User Tokens**\n- Click **New User Token**\n- Enter a name for your token and click **Create**\n- Copy the generated token (it will only be shown once)\n\n### 2. Required Permissions\nYour user token needs access to:\n- Read/write permissions for the apps and tables you want to use\n- Admin permissions for creating/deleting records (if needed)\n\n**Security Note:** Keep your user token secure - it provides access to your Quickbase data.\n",
"Create Record": "Criar Registro",
"Update Record": "Atualizar Registro",
"Delete Record": "Excluir registro",
"Find Record": "Localizar Registro",
"Find or Create Record": "Localizar ou Criar Registro",
"Create / Update Records From Array": "Criar / Atualizar Registros da Matriz",
"Custom API Call": "Chamada de API personalizada",
"Create a new record in a Quickbase table": "Criar um novo registro em uma tabela de base rápida",
"Update an existing record in a Quickbase table": "Atualizar um registro existente em uma tabela de base rápida",
"Delete a record from a Quickbase table": "Excluir um registro de uma tabela de base rápida",
"Search for records in a Quickbase table using filters": "Pesquisa por registros em uma tabela de base rápida usando filtros",
"Find an existing record or create a new one if not found": "Localizar um registro existente ou criar um novo se não for encontrado",
"Bulk create or update multiple records based on a merge key": "Criar ou atualizar vários registros com base em uma chave de mesclagem",
"Make a custom API call to a specific endpoint": "Faça uma chamada de API personalizada para um ponto de extremidade específico",
"App": "Aplicativo",
"Table": "Classificações",
"Field Values": "Valores do campo",
"Record": "Gravar",
"Filters": "Filtros",
"Sort Field": "Classificar Campo",
"Sort Order": "Ordem de classificação",
"Maximum Records": "Máximo de Registros",
"Merge Field": "Mesclar campo",
"Fields": "campos",
"Records": "registros",
"Merge Behavior": "Comportamento de merge",
"Method": "Método",
"Headers": "Cabeçalhos",
"Query Parameters": "Parâmetros da consulta",
"Body": "Conteúdo",
"Response is Binary ?": "A resposta é binária ?",
"No Error on Failure": "Nenhum erro no Failure",
"Timeout (in seconds)": "Tempo limite (em segundos)",
"Select the Quickbase app": "Selecione o aplicativo Quickbase",
"Select the table": "Selecione a tabela",
"Select and set values for table fields": "Selecionar e definir valores para campos de tabela",
"Select a record from the table": "Selecione um registro da tabela",
"Filter criteria to find records. Use field names or field IDs as keys.": "Critérios de filtro para encontrar registros. Use os nomes dos campos ou IDs de campo como chaves.",
"Field to sort records by (optional)": "Campo para ordenar os registros por (opcional)",
"Order to sort records": "Ordenar os registros",
"Maximum number of records to return": "Número máximo de registros para retornar",
"Field to use for matching existing records (for upsert operations)": "Campo a ser usado para corresponder a registros existentes (para operações de upsert)",
"Map the fields to update. Use field names or field IDs.": "Mapear os campos para atualizar. Use nomes de campos ou IDs de campo.",
"Array of records to create or update": "Array de registros para criar ou atualizar",
"How to handle existing records when upserting": "Como lidar com registros existentes ao upsering",
"Authorization headers are injected automatically from your connection.": "Os cabeçalhos de autorização são inseridos automaticamente a partir da sua conexão.",
"Enable for files like PDFs, images, etc..": "Habilitar para arquivos como PDFs, imagens, etc..",
"Ascending (A-Z, 1-9)": "Crescente (A-Z, 1-9)",
"Descending (Z-A, 9-1)": "Decrescente (Z-A, 9-1)",
"Merge and overwrite all fields": "Mesclar e substituir todos os campos",
"Merge and update only provided fields": "Mesclar e atualizar somente os campos fornecidos",
"Always create new records (no merge)": "Sempre criar novos registros (sem mesclagem)",
"GET": "OBTER",
"POST": "POSTAR",
"PATCH": "COMPRAR",
"PUT": "COLOCAR",
"DELETE": "EXCLUIR",
"HEAD": "CABEÇA",
"New Record": "Novo Registro",
"New or Updated Record": "Registro novo ou atualizado",
"Triggers when a new record is created in a Quickbase table": "Aciona quando um novo registro é criado em uma tabela de base rápida",
"Triggers when a record is created or updated in a Quickbase table": "Dispara quando um registro é criado ou atualizado em uma tabela de base rápida"
}

View File

@@ -0,0 +1,69 @@
{
"Realm Hostname": "Realm Hostname",
"User Token": "User Token",
"Enter your Quickbase Realm Hostname (e.g., yourrealm.quickbase.com)": "Enter your Quickbase Realm Hostname (e.g., yourrealm.quickbase.com)",
"Enter your Quickbase User Token": "Enter your Quickbase User Token",
"\n## Quickbase Authentication Setup\n\n### 1. Get Your User Token\n- Log in to your Quickbase account\n- Go to **My Preferences** → **My User Information**\n- Click on **Manage User Tokens**\n- Click **New User Token**\n- Enter a name for your token and click **Create**\n- Copy the generated token (it will only be shown once)\n\n### 2. Required Permissions\nYour user token needs access to:\n- Read/write permissions for the apps and tables you want to use\n- Admin permissions for creating/deleting records (if needed)\n\n**S": "\n## Quickbase Authentication Setup\n\n### 1. Get Your User Token\n- Log in to your Quickbase account\n- Go to **My Preferences** → **My User Information**\n- Click on **Manage User Tokens**\n- Click **New User Token**\n- Enter a name for your token and click **Create**\n- Copy the generated token (it will only be shown once)\n\n### 2. Required Permissions\nYour user token needs access to:\n- Read/write permissions for the apps and tables you want to use\n- Admin permissions for creating/deleting records (if needed)\n\n**Security Note:** Keep your user token secure - it provides access to your Quickbase data.\n",
"Create Record": "Create Record",
"Update Record": "Update Record",
"Delete Record": "Delete Record",
"Find Record": "Find Record",
"Find or Create Record": "Find or Create Record",
"Create / Update Records From Array": "Create / Update Records From Array",
"Custom API Call": "Custom API Call",
"Create a new record in a Quickbase table": "Create a new record in a Quickbase table",
"Update an existing record in a Quickbase table": "Update an existing record in a Quickbase table",
"Delete a record from a Quickbase table": "Delete a record from a Quickbase table",
"Search for records in a Quickbase table using filters": "Search for records in a Quickbase table using filters",
"Find an existing record or create a new one if not found": "Find an existing record or create a new one if not found",
"Bulk create or update multiple records based on a merge key": "Bulk create or update multiple records based on a merge key",
"Make a custom API call to a specific endpoint": "Make a custom API call to a specific endpoint",
"App": "App",
"Table": "Table",
"Field Values": "Field Values",
"Record": "Record",
"Filters": "Filters",
"Sort Field": "Sort Field",
"Sort Order": "Sort Order",
"Maximum Records": "Maximum Records",
"Merge Field": "Merge Field",
"Fields": "Fields",
"Records": "Records",
"Merge Behavior": "Merge Behavior",
"Method": "Method",
"Headers": "Headers",
"Query Parameters": "Query Parameters",
"Body": "Body",
"Response is Binary ?": "Response is Binary ?",
"No Error on Failure": "No Error on Failure",
"Timeout (in seconds)": "Timeout (in seconds)",
"Select the Quickbase app": "Select the Quickbase app",
"Select the table": "Select the table",
"Select and set values for table fields": "Select and set values for table fields",
"Select a record from the table": "Select a record from the table",
"Filter criteria to find records. Use field names or field IDs as keys.": "Filter criteria to find records. Use field names or field IDs as keys.",
"Field to sort records by (optional)": "Field to sort records by (optional)",
"Order to sort records": "Order to sort records",
"Maximum number of records to return": "Maximum number of records to return",
"Field to use for matching existing records (for upsert operations)": "Field to use for matching existing records (for upsert operations)",
"Map the fields to update. Use field names or field IDs.": "Map the fields to update. Use field names or field IDs.",
"Array of records to create or update": "Array of records to create or update",
"How to handle existing records when upserting": "How to handle existing records when upserting",
"Authorization headers are injected automatically from your connection.": "Authorization headers are injected automatically from your connection.",
"Enable for files like PDFs, images, etc..": "Enable for files like PDFs, images, etc..",
"Ascending (A-Z, 1-9)": "Ascending (A-Z, 1-9)",
"Descending (Z-A, 9-1)": "Descending (Z-A, 9-1)",
"Merge and overwrite all fields": "Merge and overwrite all fields",
"Merge and update only provided fields": "Merge and update only provided fields",
"Always create new records (no merge)": "Always create new records (no merge)",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD",
"New Record": "New Record",
"New or Updated Record": "New or Updated Record",
"Triggers when a new record is created in a Quickbase table": "Triggers when a new record is created in a Quickbase table",
"Triggers when a record is created or updated in a Quickbase table": "Triggers when a record is created or updated in a Quickbase table"
}

View File

@@ -0,0 +1,69 @@
{
"Realm Hostname": "Realm Hostname",
"User Token": "User Token",
"Enter your Quickbase Realm Hostname (e.g., yourrealm.quickbase.com)": "Enter your Quickbase Realm Hostname (e.g., yourrealm.quickbase.com)",
"Enter your Quickbase User Token": "Enter your Quickbase User Token",
"\n## Quickbase Authentication Setup\n\n### 1. Get Your User Token\n- Log in to your Quickbase account\n- Go to **My Preferences** → **My User Information**\n- Click on **Manage User Tokens**\n- Click **New User Token**\n- Enter a name for your token and click **Create**\n- Copy the generated token (it will only be shown once)\n\n### 2. Required Permissions\nYour user token needs access to:\n- Read/write permissions for the apps and tables you want to use\n- Admin permissions for creating/deleting records (if needed)\n\n**S": "\n## Quickbase Authentication Setup\n\n### 1. Get Your User Token\n- Log in to your Quickbase account\n- Go to **My Preferences** → **My User Information**\n- Click on **Manage User Tokens**\n- Click **New User Token**\n- Enter a name for your token and click **Create**\n- Copy the generated token (it will only be shown once)\n\n### 2. Required Permissions\nYour user token needs access to:\n- Read/write permissions for the apps and tables you want to use\n- Admin permissions for creating/deleting records (if needed)\n\n**Security Note:** Keep your user token secure - it provides access to your Quickbase data.\n",
"Create Record": "Create Record",
"Update Record": "Update Record",
"Delete Record": "Delete Record",
"Find Record": "Find Record",
"Find or Create Record": "Find or Create Record",
"Create / Update Records From Array": "Create / Update Records From Array",
"Custom API Call": "自定义 API 呼叫",
"Create a new record in a Quickbase table": "Create a new record in a Quickbase table",
"Update an existing record in a Quickbase table": "Update an existing record in a Quickbase table",
"Delete a record from a Quickbase table": "Delete a record from a Quickbase table",
"Search for records in a Quickbase table using filters": "Search for records in a Quickbase table using filters",
"Find an existing record or create a new one if not found": "Find an existing record or create a new one if not found",
"Bulk create or update multiple records based on a merge key": "Bulk create or update multiple records based on a merge key",
"Make a custom API call to a specific endpoint": "将一个自定义 API 调用到一个特定的终点",
"App": "App",
"Table": "表",
"Field Values": "Field Values",
"Record": "Record",
"Filters": "篩選條件",
"Sort Field": "Sort Field",
"Sort Order": "Sort Order",
"Maximum Records": "Maximum Records",
"Merge Field": "Merge Field",
"Fields": "Fields",
"Records": "Records",
"Merge Behavior": "Merge Behavior",
"Method": "方法",
"Headers": "信头",
"Query Parameters": "查询参数",
"Body": "正文内容",
"Response is Binary ?": "Response is Binary ?",
"No Error on Failure": "失败时没有错误",
"Timeout (in seconds)": "超时(秒)",
"Select the Quickbase app": "Select the Quickbase app",
"Select the table": "Select the table",
"Select and set values for table fields": "Select and set values for table fields",
"Select a record from the table": "Select a record from the table",
"Filter criteria to find records. Use field names or field IDs as keys.": "Filter criteria to find records. Use field names or field IDs as keys.",
"Field to sort records by (optional)": "Field to sort records by (optional)",
"Order to sort records": "Order to sort records",
"Maximum number of records to return": "Maximum number of records to return",
"Field to use for matching existing records (for upsert operations)": "Field to use for matching existing records (for upsert operations)",
"Map the fields to update. Use field names or field IDs.": "Map the fields to update. Use field names or field IDs.",
"Array of records to create or update": "Array of records to create or update",
"How to handle existing records when upserting": "How to handle existing records when upserting",
"Authorization headers are injected automatically from your connection.": "授权头自动从您的连接中注入。",
"Enable for files like PDFs, images, etc..": "Enable for files like PDFs, images, etc..",
"Ascending (A-Z, 1-9)": "Ascending (A-Z, 1-9)",
"Descending (Z-A, 9-1)": "Descending (Z-A, 9-1)",
"Merge and overwrite all fields": "Merge and overwrite all fields",
"Merge and update only provided fields": "Merge and update only provided fields",
"Always create new records (no merge)": "Always create new records (no merge)",
"GET": "获取",
"POST": "帖子",
"PATCH": "PATCH",
"PUT": "弹出",
"DELETE": "删除",
"HEAD": "黑色",
"New Record": "New Record",
"New or Updated Record": "New or Updated Record",
"Triggers when a new record is created in a Quickbase table": "Triggers when a new record is created in a Quickbase table",
"Triggers when a record is created or updated in a Quickbase table": "Triggers when a record is created or updated in a Quickbase table"
}

View File

@@ -0,0 +1,78 @@
import { createPiece, PieceAuth, Property } from '@activepieces/pieces-framework';
import { PieceCategory } from '@activepieces/shared';
import { createCustomApiCallAction, httpClient, HttpMethod } from '@activepieces/pieces-common';
import { createRecord } from './lib/actions/create-record';
import { updateRecord } from './lib/actions/update-record';
import { deleteRecord } from './lib/actions/delete-record';
import { findRecord } from './lib/actions/find-record';
import { findOrCreateRecord } from './lib/actions/find-or-create-record';
import { createUpdateRecordsBulk } from './lib/actions/create-update-records-bulk';
import { newRecord } from './lib/triggers/new-record';
import { newOrUpdatedRecord } from './lib/triggers/new-or-updated-record';
const markdown = `
## Quickbase Authentication Setup
### 1. Get Your User Token
- Log in to your Quickbase account
- Go to **My Preferences** → **My User Information**
- Click on **Manage User Tokens**
- Click **New User Token**
- Enter a name for your token and click **Create**
- Copy the generated token (it will only be shown once)
### 2. Required Permissions
Your user token needs access to:
- Read/write permissions for the apps and tables you want to use
- Admin permissions for creating/deleting records (if needed)
**Security Note:** Keep your user token secure - it provides access to your Quickbase data.
`;
export const quickbaseAuth = PieceAuth.CustomAuth({
description: markdown,
required: true,
props: {
realmHostname: Property.ShortText({
displayName: 'Realm Hostname',
description: 'Enter your Quickbase Realm Hostname (e.g., yourrealm.quickbase.com)',
required: true,
}),
userToken: Property.ShortText({
displayName: 'User Token',
description: 'Enter your Quickbase User Token',
required: true,
}),
},
});
export const quickbase = createPiece({
displayName: 'Quickbase',
auth: quickbaseAuth,
minimumSupportedRelease: '0.36.1',
logoUrl: 'https://cdn.activepieces.com/pieces/quickbase.png',
categories: [PieceCategory.PRODUCTIVITY],
authors: ['sparkybug','sanket-a11y'],
actions: [
createRecord,
updateRecord,
deleteRecord,
findRecord,
findOrCreateRecord,
createUpdateRecordsBulk,
createCustomApiCallAction({
auth: quickbaseAuth,
baseUrl: (auth) => {
return `https://api.quickbase.com/v1`;
},
authMapping: async (auth) => {
return {
'QB-Realm-Hostname': (auth).props.realmHostname,
'Authorization': `QB-USER-TOKEN ${(auth).props.userToken}`,
'Content-Type': 'application/json',
};
},
})
],
triggers: [newRecord, newOrUpdatedRecord],
});

View File

@@ -0,0 +1,46 @@
import { createAction } from '@activepieces/pieces-framework';
import { quickbaseAuth } from '../../index';
import { appIdProp, tableIdProp, fieldsMapperProp, dynamicFieldsMapperProp } from '../common/props';
import { QuickbaseClient } from '../common/client';
import { QuickbaseCreateRecordResponse, QuickbaseField } from '../common/types';
import { mapFieldsToRecord, validateRequiredFields } from '../common/utils';
export const createRecord = createAction({
name: 'create_record',
displayName: 'Create Record',
description: 'Create a new record in a Quickbase table',
auth: quickbaseAuth,
props: {
appId: appIdProp,
tableId: tableIdProp,
fields: dynamicFieldsMapperProp,
},
async run(context) {
const { appId, tableId, fields } = context.propsValue;
const client = new QuickbaseClient(context.auth.props.realmHostname, context.auth.props.userToken);
const tableFields = await client.get<QuickbaseField[]>(`/fields?tableId=${tableId}`);
const recordData: Record<string, { value: any }> = {};
for (const [key, value] of Object.entries(fields)) {
if (key.startsWith('field_') && value) {
const fieldId = key.replace('field_', '');
recordData[fieldId] = { value };
}
}
const response = await client.post<QuickbaseCreateRecordResponse>('/records', {
to: tableId,
data: [recordData],
fieldsToReturn: tableFields.map(f => f.id),
});
return {
recordId: response.metadata.createdRecordIds[0],
record: response.data[0],
success: true,
};
},
});

View File

@@ -0,0 +1,107 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { quickbaseAuth } from '../../index';
import { appIdProp, tableIdProp, mergeFieldProp } from '../common/props';
import { QuickbaseClient } from '../common/client';
import { QuickbaseCreateRecordResponse, QuickbaseField } from '../common/types';
import {
mapFieldsToRecord,
chunkArray,
validateRequiredFields,
} from '../common/utils';
export const createUpdateRecordsBulk = createAction({
name: 'create_update_records_bulk',
displayName: 'Create / Update Records From Array',
description: 'Bulk create or update multiple records based on a merge key',
auth: quickbaseAuth,
props: {
appId: appIdProp,
tableId: tableIdProp,
mergeField: mergeFieldProp,
records: Property.Array({
displayName: 'Records',
description: 'Array of records to create or update',
required: true,
}),
mergeFieldBehavior: Property.StaticDropdown({
displayName: 'Merge Behavior',
description: 'How to handle existing records when upserting',
required: true,
defaultValue: 'merge-overwrite',
options: {
options: [
{
label: 'Merge and overwrite all fields',
value: 'merge-overwrite',
},
{
label: 'Merge and update only provided fields',
value: 'merge-update',
},
{
label: 'Always create new records (no merge)',
value: 'always-create',
},
],
},
}),
},
async run(context) {
const { appId, tableId, mergeField, records, mergeFieldBehavior } =
context.propsValue;
const client = new QuickbaseClient(context.auth.props.realmHostname, context.auth.props.userToken);
if (!Array.isArray(records) || records.length === 0) {
throw new Error('Records array is required and cannot be empty');
}
const tableFields = await client.get<QuickbaseField[]>(
`/fields?tableId=${tableId}`
);
const requiredFields = tableFields
.filter((f) => f.required)
.map((f) => f.id.toString());
const processedRecords = records.map((record) => {
if (mergeFieldBehavior !== 'always-create') {
validateRequiredFields(record as Record<string, any>, requiredFields);
}
return mapFieldsToRecord(record as Record<string, any>, tableFields);
});
const chunks = chunkArray(processedRecords, 1000);
const results = {
createdRecords: [] as number[],
updatedRecords: [] as number[],
unchangedRecords: [] as number[],
totalProcessed: 0,
success: true,
};
for (const chunk of chunks) {
const requestData: any = {
to: tableId,
data: chunk,
fieldsToReturn: tableFields.map((f) => f.id),
};
if (mergeFieldBehavior !== 'always-create') {
requestData.mergeFieldId = parseInt(mergeField);
requestData.upsert =
mergeFieldBehavior === 'merge-overwrite' ? 'true' : 'false';
}
const response = await client.post<QuickbaseCreateRecordResponse>(
'/records',
requestData
);
results.createdRecords.push(...response.metadata.createdRecordIds);
results.updatedRecords.push(...response.metadata.updatedRecordIds);
results.unchangedRecords.push(...response.metadata.unchangedRecordIds);
results.totalProcessed += response.metadata.totalNumberOfRecordsProcessed;
}
return results;
},
});

View File

@@ -0,0 +1,33 @@
import { createAction } from '@activepieces/pieces-framework';
import { quickbaseAuth } from '../../index';
import { appIdProp, tableIdProp, recordIdDropdownProp } from '../common/props';
import { QuickbaseClient } from '../common/client';
import { QuickbaseDeleteRecordResponse } from '../common/types';
export const deleteRecord = createAction({
name: 'delete_record',
displayName: 'Delete Record',
description: 'Delete a record from a Quickbase table',
auth: quickbaseAuth,
props: {
appId: appIdProp,
tableId: tableIdProp,
recordId: recordIdDropdownProp,
},
async run(context) {
const { appId, tableId, recordId } = context.propsValue;
const client = new QuickbaseClient(context.auth.props.realmHostname, context.auth.props.userToken);
const response = await client.delete<QuickbaseDeleteRecordResponse>('/records', {
from: tableId,
where: `{3.EX.${recordId}}`,
});
return {
recordId: recordId,
deleted: response.numberDeleted > 0,
numberDeleted: response.numberDeleted,
success: true,
};
},
});

View File

@@ -0,0 +1,100 @@
import { createAction } from '@activepieces/pieces-framework';
import { quickbaseAuth } from '../../index';
import {
appIdProp,
tableIdProp,
fieldsMapperProp,
mergeFieldProp,
} from '../common/props';
import { QuickbaseClient } from '../common/client';
import {
QuickbaseRecordResponse,
QuickbaseCreateRecordResponse,
QuickbaseField,
} from '../common/types';
import {
mapFieldsToRecord,
extractRecordValues,
validateRequiredFields,
} from '../common/utils';
export const findOrCreateRecord = createAction({
name: 'find_or_create_record',
displayName: 'Find or Create Record',
description: 'Find an existing record or create a new one if not found',
auth: quickbaseAuth,
props: {
appId: appIdProp,
tableId: tableIdProp,
mergeField: mergeFieldProp,
fields: fieldsMapperProp,
},
async run(context) {
const { appId, tableId, mergeField, fields } = context.propsValue;
const client = new QuickbaseClient(context.auth.props.realmHostname, context.auth.props.userToken);
const tableFields = await client.get<QuickbaseField[]>(
`/fields?tableId=${tableId}`
);
const mergeValue =
fields[mergeField] ||
fields[
tableFields.find((f) => f.id.toString() === mergeField)?.label || ''
];
if (!mergeValue) {
throw new Error(`Merge field value is required`);
}
const searchQuery = {
from: tableId,
select: tableFields.map((f) => f.id),
where: `{${mergeField}.EX.'${mergeValue}'}`,
options: { top: 1 },
};
try {
const searchResponse = await client.post<QuickbaseRecordResponse>(
'/records/query',
searchQuery
);
if (searchResponse.data.length > 0) {
const existingRecord = searchResponse.data[0];
return {
recordId: existingRecord['3']?.value,
record: extractRecordValues(existingRecord),
created: false,
found: true,
success: true,
};
}
} catch (error) {
console.log(error)
}
const requiredFields = tableFields
.filter((f) => f.required)
.map((f) => f.id.toString());
validateRequiredFields(fields, requiredFields);
const recordData = mapFieldsToRecord(fields, tableFields);
const createResponse = await client.post<QuickbaseCreateRecordResponse>(
'/records',
{
to: tableId,
data: [recordData],
fieldsToReturn: tableFields.map((f) => f.id),
}
);
return {
recordId: createResponse.metadata.createdRecordIds[0],
record: createResponse.data[0],
created: true,
found: false,
success: true,
};
},
});

View File

@@ -0,0 +1,58 @@
import { createAction } from '@activepieces/pieces-framework';
import { quickbaseAuth } from '../../index';
import { appIdProp, tableIdProp, filtersProp, maxRecordsProp, sortFieldProp, sortOrderProp } from '../common/props';
import { QuickbaseClient } from '../common/client';
import { QuickbaseRecordResponse, QuickbaseField } from '../common/types';
import { buildWhereClause, extractRecordValues } from '../common/utils';
export const findRecord = createAction({
name: 'find_record',
displayName: 'Find Record',
description: 'Search for records in a Quickbase table using filters',
auth: quickbaseAuth,
props: {
appId: appIdProp,
tableId: tableIdProp,
filters: filtersProp,
sortField: sortFieldProp,
sortOrder: sortOrderProp,
maxRecords: maxRecordsProp,
},
async run(context) {
const { appId, tableId, filters, sortField, sortOrder, maxRecords } = context.propsValue;
const client = new QuickbaseClient(context.auth.props.realmHostname, context.auth.props.userToken);
const tableFields = await client.get<QuickbaseField[]>(`/fields?tableId=${tableId}`);
const whereClause = filters ? buildWhereClause(filters) : '';
const query: any = {
from: tableId,
select: tableFields.map(f => f.id),
where: whereClause,
options: {
top: maxRecords || 100,
},
};
if (sortField) {
query.sortBy = [{
fieldId: parseInt(sortField),
order: sortOrder || 'ASC',
}];
}
const response = await client.post<QuickbaseRecordResponse>('/records/query', query);
const records = response.data.map(record => ({
id: record['3']?.value,
displayName: record['3']?.value,
raw: extractRecordValues(record),
}));
return {
records,
totalRecords: response.metadata.totalRecords,
success: true,
};
},
});

View File

@@ -0,0 +1,50 @@
import { createAction } from '@activepieces/pieces-framework';
import { quickbaseAuth } from '../../index';
import { appIdProp, tableIdProp, recordIdDropdownProp, dynamicFieldsMapperProp } from '../common/props';
import { QuickbaseClient } from '../common/client';
import { QuickbaseUpdateRecordResponse, QuickbaseField } from '../common/types';
import { mapFieldsToRecord } from '../common/utils';
export const updateRecord = createAction({
name: 'update_record',
displayName: 'Update Record',
description: 'Update an existing record in a Quickbase table',
auth: quickbaseAuth,
props: {
appId: appIdProp,
tableId: tableIdProp,
recordId: recordIdDropdownProp,
fields: dynamicFieldsMapperProp,
},
async run(context) {
const { appId, tableId, recordId, fields } = context.propsValue;
const client = new QuickbaseClient(context.auth.props.realmHostname, context.auth.props.userToken);
const tableFields = await client.get<QuickbaseField[]>(`/fields?tableId=${tableId}`);
const recordData: Record<string, { value: any }> = {
'3': { value: recordId },
};
for (const [key, value] of Object.entries(fields)) {
if (key.startsWith('field_') && value) {
const fieldId = key.replace('field_', '');
recordData[fieldId] = { value };
}
}
const response = await client.post<QuickbaseUpdateRecordResponse>('/records', {
to: tableId,
data: [recordData],
fieldsToReturn: tableFields.map(f => f.id),
});
return {
recordId: recordId,
record: response.data[0],
updated: response.metadata.updatedRecordIds.includes(Number(recordId)),
success: true,
};
},
});

View File

@@ -0,0 +1,121 @@
import {
httpClient,
HttpMethod,
HttpRequest,
} from '@activepieces/pieces-common';
import { QuickbaseApiError } from './types';
export class QuickbaseClient {
private readonly baseUrl = 'https://api.quickbase.com/v1';
private readonly userToken: string;
private readonly realmHostname: string;
constructor(realmHostname: string, userToken: string) {
this.realmHostname = realmHostname;
this.userToken = userToken;
}
private async makeRequest<T>(
endpoint: string,
method: HttpMethod,
data?: any
): Promise<T> {
const url = `${this.baseUrl}${endpoint}`;
const request: HttpRequest = {
method,
url,
headers: {
'QB-Realm-Hostname': this.realmHostname,
'Authorization': `QB-USER-TOKEN ${this.userToken}`,
'Content-Type': 'application/json',
},
body: data ? data : undefined,
};
let lastError: Error;
for (let attempt = 1; attempt <= 3; attempt++) {
try {
const response = await httpClient.sendRequest<T>(request);
if (response.status === 429) {
const retryAfter = response.headers?.['retry-after'];
const retryAfterValue = Array.isArray(retryAfter)
? retryAfter[0]
: retryAfter;
const delay = retryAfterValue
? parseInt(retryAfterValue) * 1000
: Math.pow(2, attempt) * 1000;
await this.sleep(delay);
continue;
}
if (response.status >= 500) {
if (attempt === 3) {
throw new Error(`Server error: ${response.status}`);
}
await this.sleep(Math.pow(2, attempt) * 1000);
continue;
}
if (response.status >= 400) {
let errorMessage = `HTTP ${response.status}`;
try {
const errorData = response.body as QuickbaseApiError;
if (errorData.errors && errorData.errors.length > 0) {
errorMessage = errorData.errors[0].message;
if (errorData.errors[0].description) {
errorMessage += ` - ${errorData.errors[0].description}`;
}
}
} catch {
errorMessage = response.body ? String(response.body) : errorMessage;
}
throw new Error(errorMessage);
}
return response.body;
} catch (error) {
lastError = error instanceof Error ? error : new Error(String(error));
if (attempt === 3 || !this.isRetryableError(error)) {
throw lastError;
}
await this.sleep(Math.pow(2, attempt) * 1000);
}
}
throw lastError!;
}
private isRetryableError(error: unknown): boolean {
if (error instanceof Error && error.message.includes('network')) {
return true;
}
return false;
}
private sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async get<T>(endpoint: string): Promise<T> {
return this.makeRequest<T>(endpoint, HttpMethod.GET);
}
async post<T>(endpoint: string, data?: any): Promise<T> {
return this.makeRequest<T>(endpoint, HttpMethod.POST, data);
}
async patch<T>(endpoint: string, data: any): Promise<T> {
return this.makeRequest<T>(endpoint, HttpMethod.PATCH, data);
}
async delete<T>(endpoint: string, data?: any): Promise<T> {
return this.makeRequest<T>(endpoint, HttpMethod.DELETE, data);
}
}

View File

@@ -0,0 +1,322 @@
import { Property } from '@activepieces/pieces-framework';
import { QuickbaseClient } from './client';
import { QuickbaseApp, QuickbaseTable, QuickbaseField } from './types';
import { quickbaseAuth } from '../..';
export const recordIdProp = Property.ShortText({
displayName: 'Record ID',
description: 'The ID of the record',
required: true,
});
export const fieldsMapperProp = Property.Object({
displayName: 'Fields',
description: 'Map the fields to update. Use field names or field IDs.',
required: true,
});
export const createDynamicFieldsMapperProp = () => Property.DynamicProperties({
displayName: 'Field Values',
auth: quickbaseAuth,
description: 'Select and set values for table fields',
required: true,
refreshers: ['appId', 'tableId'],
props: async ({ auth, appId, tableId }) => {
if (!auth || !appId || !tableId) {
return {};
}
try {
const client = new QuickbaseClient(auth.props.realmHostname, auth.props.userToken);
const fields = await client.get<QuickbaseField[]>(`/fields?tableId=${tableId}`);
const props: Record<string, any> = {};
console.log("fields", JSON.stringify(fields, null, 2));
for (const field of fields) {
if (field.id === 3) continue;
const propKey = `field_${field.id}`;
const isRequired = field.required || false;
if (field.fieldType === 'checkbox') {
props[propKey] = Property.Checkbox({
displayName: `${field.label}${isRequired ? ' *' : ''}`,
description: `${field.fieldType} field${isRequired ? ' (required)' : ''}`,
required: isRequired,
});
} else if (field.fieldType === 'number') {
props[propKey] = Property.Number({
displayName: `${field.label}${isRequired ? ' *' : ''}`,
description: `${field.fieldType} field${isRequired ? ' (required)' : ''}`,
required: isRequired,
});
} else if (field.fieldType === 'date ' || field.fieldType === 'timestamp') {
props[propKey] = Property.DateTime({
displayName: `${field.label}${isRequired ? ' *' : ''}`,
description: `${field.fieldType} field${isRequired ? ' (required)' : ''}`,
required: isRequired,
});
} else {
props[propKey] = Property.ShortText({
displayName: `${field.label}${isRequired ? ' *' : ''}`,
description: `${field.fieldType} field${isRequired ? ' (required)' : ''}`,
required: isRequired,
});
}
}
return props;
} catch (error) {
return {};
}
},
});
export const filtersProp = Property.Object({
displayName: 'Filters',
description: 'Filter criteria to find records. Use field names or field IDs as keys.',
required: false,
});
export const recordsArrayProp = Property.Array({
displayName: 'Records',
description: 'Array of records to create or update',
required: true,
});
export const maxRecordsProp = Property.Number({
displayName: 'Maximum Records',
description: 'Maximum number of records to return',
required: false,
defaultValue: 100,
});
export const createAppIdProp = () => Property.Dropdown({
auth: quickbaseAuth,
displayName: 'App',
description: 'Select the Quickbase app',
required: true,
refreshers: [],
options: async ({ auth }) => {
if (!auth) {
return {
disabled: true,
placeholder: 'Please connect your account first',
options: [],
};
}
try {
const client = new QuickbaseClient(auth.props.realmHostname, auth.props.userToken);
const apps = await client.get<QuickbaseApp[]>('/apps');
return {
options: apps.map(app => ({
label: app.name,
value: app.id,
})),
};
} catch (error) {
return {
disabled: true,
placeholder: 'Failed to load apps',
options: [],
};
}
},
});
export const createTableIdProp = () => Property.Dropdown({
auth: quickbaseAuth,
displayName: 'Table',
description: 'Select the table',
required: true,
refreshers: ['appId'],
options: async ({ auth, appId }) => {
if (!auth || !appId) {
return {
disabled: true,
placeholder: 'Please select an app first',
options: [],
};
}
try {
const client = new QuickbaseClient(auth.props.realmHostname, auth.props.userToken);
const tables = await client.get<QuickbaseTable[]>(`/tables?appId=${appId}`);
return {
options: tables.map(table => ({
label: table.name,
value: table.id,
})),
};
} catch (error) {
return {
disabled: true,
placeholder: 'Failed to load tables',
options: [],
};
}
},
});
export const createMergeFieldProp = () => Property.Dropdown({
auth: quickbaseAuth,
displayName: 'Merge Field',
description: 'Field to use for matching existing records (for upsert operations)',
required: true,
refreshers: ['appId', 'tableId'],
options: async ({ auth, appId, tableId }) => {
if (!auth || !appId || !tableId) {
return {
disabled: true,
placeholder: 'Please select app and table first',
options: [],
};
}
try {
const client = new QuickbaseClient(auth.props.realmHostname, auth.props.userToken);
const fields = await client.get<QuickbaseField[]>(`/fields?tableId=${tableId}`);
return {
options: fields
.filter(field => field.unique || field.fieldType === 'text' || field.fieldType === 'email')
.map(field => ({
label: field.label,
value: field.id.toString(),
})),
};
} catch (error) {
return {
disabled: true,
placeholder: 'Failed to load fields',
options: [],
};
}
},
});
export const createSortFieldProp = () => Property.Dropdown({
auth: quickbaseAuth,
displayName: 'Sort Field',
description: 'Field to sort records by (optional)',
required: false,
refreshers: ['appId', 'tableId'],
options: async ({ auth, appId, tableId }) => {
if (!auth || !appId || !tableId) {
return {
disabled: true,
placeholder: 'Please select app and table first',
options: [],
};
}
try {
const client = new QuickbaseClient(auth.props.realmHostname, auth.props.userToken);
const fields = await client.get<QuickbaseField[]>(`/fields?tableId=${tableId}`);
return {
options: [
{ label: 'No sorting', value: '' },
...fields
.filter(field => ['text', 'number', 'date', 'timestamp'].includes(field.fieldType))
.map(field => ({
label: `${field.label} (${field.fieldType})`,
value: field.id.toString(),
}))
],
};
} catch (error) {
return {
disabled: true,
placeholder: 'Failed to load fields',
options: [],
};
}
},
});
export const createSortOrderProp = () => Property.StaticDropdown({
displayName: 'Sort Order',
description: 'Order to sort records',
required: false,
defaultValue: 'ASC',
options: {
options: [
{ label: 'Ascending (A-Z, 1-9)', value: 'ASC' },
{ label: 'Descending (Z-A, 9-1)', value: 'DESC' },
],
},
});
export const createRecordIdProp = () => Property.Dropdown({
auth: quickbaseAuth,
displayName: 'Record',
description: 'Select a record from the table',
required: true,
refreshers: ['appId', 'tableId'],
options: async ({ auth, appId, tableId }) => {
if (!auth || !appId || !tableId) {
return {
disabled: true,
placeholder: 'Please select app and table first',
options: [],
};
}
try {
const client = new QuickbaseClient(auth.props.realmHostname, auth.props.userToken);
const fields = await client.get<QuickbaseField[]>(`/fields?tableId=${tableId}`);
const displayField = fields.find(f => f.fieldType === 'text' && f.label.toLowerCase().includes('name'))
|| fields.find(f => f.fieldType === 'text')
|| fields[1];
const query = {
from: tableId,
select: [3, displayField?.id || 6],
options: { top: 50 },
};
const response = await client.post<any>('/records/query', query);
return {
options: response.data.map((record: any) => {
const recordId = record['3']?.value;
const displayValue = record[displayField?.id.toString() || '6']?.value || `Record ${recordId}`;
return {
label: `${displayValue} (ID: ${recordId})`,
value: recordId.toString(),
};
}),
};
} catch (error) {
return {
disabled: true,
placeholder: 'Failed to load records',
options: [],
};
}
},
});
export const appIdProp = createAppIdProp();
export const tableIdProp = createTableIdProp();
export const mergeFieldProp = createMergeFieldProp();
export const sortFieldProp = createSortFieldProp();
export const sortOrderProp = createSortOrderProp();
export const recordIdDropdownProp = createRecordIdProp();
export const dynamicFieldsMapperProp = createDynamicFieldsMapperProp();

View File

@@ -0,0 +1,111 @@
export interface QuickbaseApp {
id: string;
name: string;
description?: string;
created: string;
updated: string;
}
export interface QuickbaseTable {
id: string;
name: string;
alias?: string;
description?: string;
created: string;
updated: string;
nextRecordId: number;
nextFieldId: number;
defaultSortFieldId: number;
defaultSortOrder: string;
keyFieldId: number;
singleRecordName: string;
pluralRecordName: string;
}
export interface QuickbaseField {
id: number;
label: string;
fieldType: string;
mode?: string;
required?: boolean;
unique?: boolean;
properties?: Record<string, any>;
}
export interface QuickbaseRecord {
[fieldId: string]: {
value: any;
};
}
export interface QuickbaseRecordResponse {
data: QuickbaseRecord[];
fields: QuickbaseField[];
metadata: {
numRecords: number;
numFields: number;
skip: number;
top: number;
totalRecords: number;
};
}
export interface QuickbaseCreateRecordResponse {
data: Array<{
[fieldId: string]: {
value: any;
};
}>;
metadata: {
createdRecordIds: number[];
totalNumberOfRecordsProcessed: number;
unchangedRecordIds: number[];
updatedRecordIds: number[];
};
}
export interface QuickbaseUpdateRecordResponse {
data: Array<{
[fieldId: string]: {
value: any;
};
}>;
metadata: {
totalNumberOfRecordsProcessed: number;
unchangedRecordIds: number[];
updatedRecordIds: number[];
};
}
export interface QuickbaseDeleteRecordResponse {
numberDeleted: number;
}
export interface QuickbaseError {
message: string;
description?: string;
code?: string;
}
export interface QuickbaseApiError {
errors: QuickbaseError[];
}
export interface QuickbaseQuery {
from: string;
select?: number[];
where?: string;
sortBy?: Array<{
fieldId: number;
order: 'ASC' | 'DESC';
}>;
groupBy?: Array<{
fieldId: number;
grouping: string;
}>;
options?: {
skip?: number;
top?: number;
compareWithAppLocalTime?: boolean;
};
}

View File

@@ -0,0 +1,89 @@
import { QuickbaseRecord, QuickbaseField } from './types';
export function generateDeduplicationKey(recordId: string | number, lastModified: string): string {
return `${recordId}-${lastModified}`;
}
export function mapFieldsToRecord(
fieldsData: Record<string, any>,
fields: QuickbaseField[]
): Record<string, { value: any }> {
const record: Record<string, { value: any }> = {};
for (const [key, value] of Object.entries(fieldsData)) {
let fieldId: string;
if (/^\d+$/.test(key)) {
fieldId = key;
} else {
const field = fields.find(f => f.label.toLowerCase() === key.toLowerCase());
if (field) {
fieldId = field.id.toString();
} else {
throw new Error(`Field not found: ${key}`);
}
}
record[fieldId] = { value };
}
return record;
}
export function extractRecordValues(record: QuickbaseRecord): Record<string, any> {
const values: Record<string, any> = {};
for (const [fieldId, fieldData] of Object.entries(record)) {
values[fieldId] = fieldData.value;
}
return values;
}
export function chunkArray<T>(array: T[], chunkSize: number): T[][] {
const chunks: T[][] = [];
for (let i = 0; i < array.length; i += chunkSize) {
chunks.push(array.slice(i, i + chunkSize));
}
return chunks;
}
export function buildWhereClause(filters: Record<string, any>): string {
const conditions: string[] = [];
for (const [fieldId, value] of Object.entries(filters)) {
if (value === null || value === undefined) {
continue;
}
if (typeof value === 'string') {
conditions.push(`{${fieldId}.EX.'${value.replace(/'/g, "''")}'}`);
} else if (typeof value === 'number') {
conditions.push(`{${fieldId}.EX.${value}}`);
} else if (typeof value === 'boolean') {
conditions.push(`{${fieldId}.EX.${value}}`);
} else if (Array.isArray(value)) {
const arrayConditions = value.map(v =>
typeof v === 'string'
? `{${fieldId}.EX.'${v.replace(/'/g, "''")}'}`
: `{${fieldId}.EX.${v}}`
);
conditions.push(`(${arrayConditions.join('OR')})`);
}
}
return conditions.join('AND');
}
export function validateRequiredFields(
data: Record<string, any>,
requiredFields: string[]
): void {
const missingFields = requiredFields.filter(field =>
data[field] === undefined || data[field] === null || data[field] === ''
);
if (missingFields.length > 0) {
throw new Error(`Missing required fields: ${missingFields.join(', ')}`);
}
}

View File

@@ -0,0 +1,95 @@
import { AppConnectionValueForAuthProperty, createTrigger, StaticPropsValue, TriggerStrategy } from "@activepieces/pieces-framework";
import { quickbaseAuth } from "../..";
import { DedupeStrategy, Polling, pollingHelper } from "@activepieces/pieces-common";
import { QuickbaseClient } from "../common/client";
import { QuickbaseRecordResponse, QuickbaseField } from "../common/types";
import { appIdProp, tableIdProp } from "../common/props";
type QuickbaseAuth = AppConnectionValueForAuthProperty<typeof quickbaseAuth>;
const props = {
appId: appIdProp,
tableId: tableIdProp,
}
const polling: Polling<QuickbaseAuth, StaticPropsValue<typeof props>> = {
strategy: DedupeStrategy.TIMEBASED,
items: async ({ auth, propsValue, lastFetchEpochMS }) => {
const { tableId, } = propsValue;
const client = new QuickbaseClient(auth.props.realmHostname, auth.props.userToken);
const fields = await client.get<QuickbaseField[]>(`/fields?tableId=${tableId}`);
const select = fields.map((f) => f.id);
const modifiedField = fields.find(
(f) => /modified/i.test(f.label) && (f.fieldType === 'timestamp' || f.fieldType === 'date')
);
const anyTimeField = fields.find((f) => f.fieldType === 'timestamp' || f.fieldType === 'date');
const timeFieldId = (modifiedField || anyTimeField)?.id || 6;
const query = {
from: tableId,
select,
sortBy: [
{
fieldId: timeFieldId,
order: 'DESC',
},
],
options: {
top: 100,
},
};
const response = await client.post<QuickbaseRecordResponse>('/records/query', query);
const isTest = lastFetchEpochMS === 0;
return response.data
.map((record) => {
const timeFieldKey = timeFieldId.toString();
const rawValue = record[timeFieldKey]?.value;
let epoch: number;
if (rawValue) {
epoch = new Date(rawValue).getTime();
} else {
epoch = Date.now();
}
return {
epochMilliSeconds: epoch,
data: record,
};
})
.filter((item) => item.epochMilliSeconds > lastFetchEpochMS);
},
};
export const newOrUpdatedRecord = createTrigger({
auth: quickbaseAuth,
name: 'new_or_updated_record',
displayName: 'New or Updated Record',
description: 'Triggers when a record is created or updated in a Quickbase table',
props,
type: TriggerStrategy.POLLING,
sampleData: {
},
async onEnable(context) {
const { store, auth, propsValue } = context;
await pollingHelper.onEnable(polling, { store, auth, propsValue });
},
async onDisable(context) {
const { store, auth, propsValue } = context;
await pollingHelper.onDisable(polling, { store, auth, propsValue });
},
async run(context) {
const { store, auth, propsValue, files } = context;
return await pollingHelper.poll(polling, { store, auth, propsValue, files });
},
async test(context) {
const { store, auth, propsValue, files } = context;
return await pollingHelper.test(polling, { store, auth, propsValue, files });
},
});

View File

@@ -0,0 +1,97 @@
import { createTrigger, TriggerStrategy, StaticPropsValue, AppConnectionValueForAuthProperty } from '@activepieces/pieces-framework';
import { quickbaseAuth } from '../../index';
import { appIdProp, tableIdProp } from '../common/props';
import { QuickbaseClient } from '../common/client';
import { QuickbaseRecordResponse, QuickbaseField } from '../common/types';
import { generateDeduplicationKey, extractRecordValues } from '../common/utils';
import { DedupeStrategy, Polling, pollingHelper } from '@activepieces/pieces-common';
const props = {
appId: appIdProp,
tableId: tableIdProp,
};
type QuickbaseAuth = AppConnectionValueForAuthProperty<typeof quickbaseAuth>;
const polling: Polling<QuickbaseAuth, StaticPropsValue<typeof props>> = {
strategy: DedupeStrategy.TIMEBASED,
items: async ({ auth, propsValue, lastFetchEpochMS }: { auth: QuickbaseAuth; propsValue: StaticPropsValue<typeof props>; lastFetchEpochMS: number }) => {
const { appId, tableId } = propsValue;
const client = new QuickbaseClient(auth.props.realmHostname, auth.props.userToken);
const tableFields = await client.get<QuickbaseField[]>(`/fields?tableId=${tableId}`);
const dateCreatedField = tableFields.find(
(f) => f.fieldType === 'timestamp' && f.label.toLowerCase().includes('created')
);
if (!dateCreatedField) {
throw new Error(
'No date created field found in table. A timestamp field with "created" in the name is required.'
);
}
const query = {
from: tableId,
select: tableFields.map((f) => f.id),
sortBy: [{ fieldId: dateCreatedField.id, order: 'DESC' as const }],
options: { top: 100 },
};
const response = await client.post<QuickbaseRecordResponse>('/records/query', query);
const isTest = lastFetchEpochMS === 0;
return response.data
.map((record) => {
const recordId = record['3']?.value;
const dateCreated = record[dateCreatedField.id.toString()]?.value;
const epoch = dateCreated ? new Date(dateCreated).getTime() : Date.now();
return {
epochMilliSeconds: epoch,
data: {
id: generateDeduplicationKey(recordId, dateCreated),
created_at: dateCreated,
updated_at:
record[
tableFields.find(
(f) => f.fieldType === 'timestamp' && f.label.toLowerCase().includes('modified')
)?.id.toString() || ''
]?.value || dateCreated,
recordId,
tableId,
appId,
fields: extractRecordValues(record),
},
};
})
.filter((item) => isTest || item.epochMilliSeconds > lastFetchEpochMS);
},
};
export const newRecord = createTrigger({
name: 'new_record',
displayName: 'New Record',
description: 'Triggers when a new record is created in a Quickbase table',
auth: quickbaseAuth,
props,
type: TriggerStrategy.POLLING,
sampleData: {},
async onEnable(context) {
const { store, auth, propsValue } = context;
await pollingHelper.onEnable(polling, { store, auth, propsValue });
},
async onDisable(context) {
const { store, auth, propsValue } = context;
await pollingHelper.onDisable(polling, { store, auth, propsValue });
},
async run(context) {
const { store, auth, propsValue, files } = context;
return await pollingHelper.poll(polling, { store, auth, propsValue, files });
},
async test(context) {
const { store, auth, propsValue, files } = context;
return await pollingHelper.test(polling, { store, auth, propsValue, files });
},
});

View File

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

View File

@@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../../dist/out-tsc",
"declaration": true,
"types": ["node"]
},
"include": ["src/**/*.ts"]
}