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-formstack
This library was generated with [Nx](https://nx.dev).
## Building
Run `nx build pieces-formstack` to build the library.

View File

@@ -0,0 +1,4 @@
{
"name": "@activepieces/piece-formstack",
"version": "0.0.10"
}

View File

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

View File

@@ -0,0 +1,66 @@
{
"Connect your Formstack account": "Verbinden Sie Ihren Formstack Account",
"Create Submission": "Einreichung erstellen",
"Find Form by Name or ID": "Formular nach Name oder ID suchen",
"Get Submission Details": "Einreichungsdetails abrufen",
"Find Submission by Field Value": "Suche Einreichung nach Feldwert",
"Custom API Call": "Eigener API-Aufruf",
"Submit data to a Formstack form": "Daten an ein Formstack-Formular senden",
"Find a form by name or ID": "Suche ein Formular mit Namen oder ID",
"Get details of a form submission": "Details zu einer Formularabgabe erhalten",
"Search submissions by field values": "Durchsuche Einreichungen nach Feldwerten",
"Make a custom API call to a specific endpoint": "Einen benutzerdefinierten API-Aufruf an einen bestimmten Endpunkt machen",
"Forms ": "Formulare ",
"User Agent": "User-Agent",
"IP Address": "IP-Adresse",
"Payment Status": "Zahlungsstatus",
"Mark as Read": "Als gelesen markieren",
"Encryption Password": "Verschlüsselungspasswort",
"Form Fields": "Formularfelder",
"Form Name or ID": "Formularname oder ID",
"Group by Folders": "Nach Ordnern gruppieren",
"Exact Name Match": "Genaues Namensmatch",
"Submission": "Einreichung",
"Include Technical Metadata": "Technische Metadaten einbeziehen",
"Search Value": "Suchwert",
"Results Per Page": "Ergebnisse pro Seite",
"Page Number": "Seitennummer",
"Sort Order": "Sortierung",
"Include Form Names": "Formularnamen einschließen",
"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)",
"Browser user agent to record": "Browser-User-Agent zum Aufzeichnen",
"IP address to record": "Zu erfassende IP-Adresse",
"Payment integration status": "Status der Zahlungsintegration",
"Mark submission as read when created": "Vorlage als gelesen markieren, wenn erstellt",
"Password for encrypted forms": "Passwort für verschlüsselte Formulare",
"Fill out the form fields": "Formularfelder ausfüllen",
"Enter form name or ID to search": "Formularname oder ID zur Suche eingeben",
"Organize results by folders": "Ergebnisse nach Ordnern organisieren",
"Only exact name matches": "Nur exakte Namensübereinstimmung",
"Include IP, user agent, and location data": "IP, User-Agent und Standortdaten einbeziehen",
"Value to search for (minimum 3 characters)": "Wert für die Suche (mindestens 3 Zeichen)",
"Number of results per page (1-100)": "Anzahl der Ergebnisse pro Seite (1-100)",
"Page number to return": "Rückzugebende Seitennummer",
"Sort results by submission ID": "Ergebnisse nach Einreichungs-ID sortieren",
"Include form names in results": "Formularnamen in Ergebnisse einfügen",
"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..",
"Newest First (DESC)": "Neueste zuerst (DESC)",
"Oldest First (ASC)": "Älteste zuerst (ASC)",
"GET": "ERHALTEN",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "LÖSCHEN",
"HEAD": "HEAD",
"New Submission": "Neue Einreichung",
"New Form": "Neues Formular",
"Triggers when a form receives a new submission": "Wird ausgelöst, wenn ein Formular eine neue Einreichung erhält",
"Triggers when a new form is created": "Wird ausgelöst, wenn ein neues Formular erstellt wird"
}

View File

@@ -0,0 +1,66 @@
{
"Connect your Formstack account": "Conecte su cuenta Formstack",
"Create Submission": "Crear Envío",
"Find Form by Name or ID": "Buscar formulario por nombre o ID",
"Get Submission Details": "Obtener detalles del envío",
"Find Submission by Field Value": "Encontrar envío por valor de campo",
"Custom API Call": "Llamada API personalizada",
"Submit data to a Formstack form": "Enviar datos a un Formstack",
"Find a form by name or ID": "Encontrar un formulario por nombre o ID",
"Get details of a form submission": "Obtener detalles del envío de un formulario",
"Search submissions by field values": "Buscar envíos por valores de campo",
"Make a custom API call to a specific endpoint": "Hacer una llamada API personalizada a un extremo específico",
"Forms ": "Formularios ",
"User Agent": "Agente de usuario",
"IP Address": "Dirección IP",
"Payment Status": "Estado del pago",
"Mark as Read": "Marcar como leído",
"Encryption Password": "Contraseña del cifrado",
"Form Fields": "Campos de Formulario",
"Form Name or ID": "Nombre del formulario o ID",
"Group by Folders": "Agrupar por carpetas",
"Exact Name Match": "Nombre exacto coincidencia",
"Submission": "Envío",
"Include Technical Metadata": "Incluye metadatos técnicos",
"Search Value": "Valor de búsqueda",
"Results Per Page": "Resultados por página",
"Page Number": "Número de página",
"Sort Order": "Ordenar",
"Include Form Names": "Incluye nombres de formulario",
"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)",
"Browser user agent to record": "Navegar agente de usuario para registrar",
"IP address to record": "Dirección IP a grabar",
"Payment integration status": "Estado de integración de pago",
"Mark submission as read when created": "Marcar envío como leído cuando se crea",
"Password for encrypted forms": "Contraseña para formularios cifrados",
"Fill out the form fields": "Rellene los campos del formulario",
"Enter form name or ID to search": "Introduzca nombre de formulario o ID para buscar",
"Organize results by folders": "Organizar resultados por carpetas",
"Only exact name matches": "Sólo coincidencias de nombres exactos",
"Include IP, user agent, and location data": "Incluye IP, agente de usuario y datos de ubicación",
"Value to search for (minimum 3 characters)": "Valor para buscar (mínimo 3 caracteres)",
"Number of results per page (1-100)": "Número de resultados por página (1-100)",
"Page number to return": "Número de página a devolver",
"Sort results by submission ID": "Ordenar resultados por ID de envío",
"Include form names in results": "Incluye nombres de formulario en los resultados",
"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 PDFs, imágenes, etc.",
"Newest First (DESC)": "Más reciente primero (DESC)",
"Oldest First (ASC)": "Lo más antiguo primero (ASC)",
"GET": "RECOGER",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "BORRAR",
"HEAD": "LIMPIO",
"New Submission": "Nuevo envío",
"New Form": "Nuevo Formulario",
"Triggers when a form receives a new submission": "Dispara cuando un formulario recibe un nuevo envío",
"Triggers when a new form is created": "Dispara cuando se crea un nuevo formulario"
}

View File

@@ -0,0 +1,66 @@
{
"Connect your Formstack account": "Connectez votre compte Formstack",
"Create Submission": "Créer une soumission",
"Find Form by Name or ID": "Formulaire de recherche par nom ou ID",
"Get Submission Details": "Obtenir les détails de la soumission",
"Find Submission by Field Value": "Rechercher la soumission par valeur de champ",
"Custom API Call": "Appel d'API personnalisé",
"Submit data to a Formstack form": "Soumettre des données à un formulaire Formstack",
"Find a form by name or ID": "Trouver un formulaire par nom ou ID",
"Get details of a form submission": "Obtenir les détails d'une soumission de formulaire",
"Search submissions by field values": "Rechercher les soumissions par valeur de champ",
"Make a custom API call to a specific endpoint": "Passer un appel API personnalisé à un endpoint spécifique",
"Forms ": "Formulaires ",
"User Agent": "Agent Utilisateur",
"IP Address": "Adresse IP",
"Payment Status": "Statut du paiement",
"Mark as Read": "Marquer comme lu",
"Encryption Password": "Mot de passe de chiffrement",
"Form Fields": "Champs de formulaire",
"Form Name or ID": "Nom du formulaire ou ID",
"Group by Folders": "Grouper par dossiers",
"Exact Name Match": "Correspondance de nom exacte",
"Submission": "Soumission",
"Include Technical Metadata": "Inclure les métadonnées techniques",
"Search Value": "Valeur de la recherche",
"Results Per Page": "Résultats par page",
"Page Number": "Numéro de page",
"Sort Order": "Ordre de tri",
"Include Form Names": "Inclure les noms de formulaire",
"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)",
"Browser user agent to record": "Navigateur utilisateur à enregistrer",
"IP address to record": "Adresse IP à enregistrer",
"Payment integration status": "Statut de l'intégration du paiement",
"Mark submission as read when created": "Marquer la soumission comme lue lors de la création",
"Password for encrypted forms": "Mot de passe pour les formulaires chiffrés",
"Fill out the form fields": "Remplissez les champs du formulaire",
"Enter form name or ID to search": "Entrez le nom du formulaire ou l'ID à rechercher",
"Organize results by folders": "Organiser les résultats par dossiers",
"Only exact name matches": "Seul le nom exact correspond",
"Include IP, user agent, and location data": "Inclure les données IP, l'agent utilisateur et la localisation",
"Value to search for (minimum 3 characters)": "Valeur à rechercher (minimum 3 caractères)",
"Number of results per page (1-100)": "Nombre de résultats par page (1-100)",
"Page number to return": "Numéro de page à retourner",
"Sort results by submission ID": "Trier les résultats par ID de remise",
"Include form names in results": "Inclure les noms des formulaires dans les résultats",
"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.",
"Newest First (DESC)": "Plus récent en premier (DESC)",
"Oldest First (ASC)": "Le plus ancien en premier (ASC)",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD",
"New Submission": "Nouvelle soumission",
"New Form": "Nouveau formulaire",
"Triggers when a form receives a new submission": "Déclenche lorsqu'un formulaire reçoit une nouvelle soumission",
"Triggers when a new form is created": "Déclenche lorsqu'un nouveau formulaire est créé"
}

View File

@@ -0,0 +1,66 @@
{
"Connect your Formstack account": "Formstackアカウントに接続",
"Create Submission": "提出物を作成",
"Find Form by Name or ID": "名前またはIDでフォームを検索",
"Get Submission Details": "提出物の詳細を取得する",
"Find Submission by Field Value": "フィールド値で提出物を検索",
"Custom API Call": "カスタムAPI通話",
"Submit data to a Formstack form": "フォームスタックフォームにデータを送信",
"Find a form by name or ID": "名前またはIDでフォームを検索",
"Get details of a form submission": "フォーム送信の詳細を取得します。",
"Search submissions by field values": "フィールド値で提出物を検索",
"Make a custom API call to a specific endpoint": "特定のエンドポイントへのカスタム API コールを実行します。",
"Forms ": "フォーム ",
"User Agent": "ユーザーエージェント",
"IP Address": "IP アドレス",
"Payment Status": "支払ステータス",
"Mark as Read": "既読にする",
"Encryption Password": "暗号化パスワード",
"Form Fields": "フォームフィールド",
"Form Name or ID": "フォーム名またはID",
"Group by Folders": "フォルダ別にグループ化",
"Exact Name Match": "完全な名前一致",
"Submission": "提出",
"Include Technical Metadata": "技術的なメタデータを含める",
"Search Value": "検索値",
"Results Per Page": "ページ毎の結果",
"Page Number": "ページ番号",
"Sort Order": "並び順",
"Include Form Names": "フォーム名を含める",
"Method": "方法",
"Headers": "ヘッダー",
"Query Parameters": "クエリパラメータ",
"Body": "本文",
"Response is Binary ?": "応答はバイナリですか?",
"No Error on Failure": "失敗時にエラーはありません",
"Timeout (in seconds)": "タイムアウト(秒)",
"Browser user agent to record": "録画するブラウザーユーザーエージェント",
"IP address to record": "記録するIPアドレス",
"Payment integration status": "Payment integration status",
"Mark submission as read when created": "作成時に提出物を既読にする",
"Password for encrypted forms": "暗号化されたフォームのパスワード",
"Fill out the form fields": "フォームフィールドに入力してください",
"Enter form name or ID to search": "検索するフォーム名またはIDを入力してください",
"Organize results by folders": "フォルダごとに結果を整理する",
"Only exact name matches": "一致する名前のみです",
"Include IP, user agent, and location data": "IP、ユーザーエージェント、位置情報データを含める",
"Value to search for (minimum 3 characters)": "検索する値 (最低3文字)",
"Number of results per page (1-100)": "Number of results per page (1-100)",
"Page number to return": "戻るページ番号",
"Sort results by submission ID": "提出物IDで結果をソート",
"Include form names in results": "フォーム名を結果に含める",
"Authorization headers are injected automatically from your connection.": "認証ヘッダは接続から自動的に注入されます。",
"Enable for files like PDFs, images, etc..": "PDF、画像などのファイルを有効にします。",
"Newest First (DESC)": "最近の最初(降順)",
"Oldest First (ASC)": "古い順 (ASC)",
"GET": "取得",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "削除",
"HEAD": "頭",
"New Submission": "新しい提出",
"New Form": "新しいフォーム",
"Triggers when a form receives a new submission": "フォームが新しい提出物を受け取ったときにトリガーします",
"Triggers when a new form is created": "新しいフォームが作成されたときにトリガーします"
}

View File

@@ -0,0 +1,66 @@
{
"Connect your Formstack account": "Verbind uw Formstack account",
"Create Submission": "Indiening aanmaken",
"Find Form by Name or ID": "Formulier zoeken op naam of ID",
"Get Submission Details": "Krijg inzending details",
"Find Submission by Field Value": "Zoek inzending op veldwaarde",
"Custom API Call": "Custom API Call",
"Submit data to a Formstack form": "Verstuur gegevens naar een Formstack formulier",
"Find a form by name or ID": "Zoek een formulier op naam of ID",
"Get details of a form submission": "Krijg details van een formulier uitwerking",
"Search submissions by field values": "Zoek inzendingen op veldwaarden",
"Make a custom API call to a specific endpoint": "Maak een aangepaste API call naar een specifiek eindpunt",
"Forms ": "Formulieren ",
"User Agent": "User Agent",
"IP Address": "IP adres",
"Payment Status": "Betalings Status",
"Mark as Read": "Markeren als gelezen",
"Encryption Password": "Encryptie wachtwoord",
"Form Fields": "Formulier velden",
"Form Name or ID": "Formulier naam of ID",
"Group by Folders": "Groeperen op mappen",
"Exact Name Match": "Exacte naam overeenkomst",
"Submission": "Inzending",
"Include Technical Metadata": "Inclusief technische metadata",
"Search Value": "Waarde zoeken",
"Results Per Page": "Resultaten per pagina",
"Page Number": "Pagina Nummer",
"Sort Order": "Sorteren bestelling",
"Include Form Names": "Formuliernamen opnemen",
"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)",
"Browser user agent to record": "Browser gebruikersagent om te registreren",
"IP address to record": "IP-adres om op te nemen",
"Payment integration status": "Integratiestatus van betaling",
"Mark submission as read when created": "Markeer inzending als gelezen wanneer gemaakt",
"Password for encrypted forms": "Wachtwoord voor versleutelde formulieren",
"Fill out the form fields": "Vul de formuliervelden in",
"Enter form name or ID to search": "Voer formuliernaam of ID in om te zoeken",
"Organize results by folders": "Resultaten organiseren door mappen",
"Only exact name matches": "Alleen exacte naam overeenkomsten",
"Include IP, user agent, and location data": "Inclusief IP-, gebruikers- en locatiegegevens",
"Value to search for (minimum 3 characters)": "Waarde om naar te zoeken (minimum 3 tekens)",
"Number of results per page (1-100)": "Aantal resultaten per pagina (1-100)",
"Page number to return": "Pagina nummer om te retourneren",
"Sort results by submission ID": "Sorteer resultaten op indienings-ID",
"Include form names in results": "Formuliernamen in zoekresultaten opnemen",
"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..",
"Newest First (DESC)": "Nieuwste eerst (AFLOPEND)",
"Oldest First (ASC)": "Oudste eerst (ASC)",
"GET": "KRIJG",
"POST": "POSTE",
"PATCH": "BEKIJK",
"PUT": "PUT",
"DELETE": "VERWIJDEREN",
"HEAD": "HOOFD",
"New Submission": "Nieuwe inzending",
"New Form": "Nieuw formulier",
"Triggers when a form receives a new submission": "Triggert wanneer een formulier een nieuwe uitwerking ontvangt",
"Triggers when a new form is created": "Triggert wanneer een nieuw formulier wordt aangemaakt"
}

View File

@@ -0,0 +1,66 @@
{
"Connect your Formstack account": "Conecte sua conta do Formstack",
"Create Submission": "Criar Submissão",
"Find Form by Name or ID": "Localizar Formulário por Nome ou ID",
"Get Submission Details": "Obter detalhes do envio",
"Find Submission by Field Value": "Encontrar Submissão por Valor do Campo",
"Custom API Call": "Chamada de API personalizada",
"Submit data to a Formstack form": "Enviar dados a um formulário de Formstack",
"Find a form by name or ID": "Encontrar um formulário por nome ou ID",
"Get details of a form submission": "Obter detalhes de um envio do formulário",
"Search submissions by field values": "Pesquisar submissões por valores de campo",
"Make a custom API call to a specific endpoint": "Faça uma chamada de API personalizada para um ponto de extremidade específico",
"Forms ": "Formulários ",
"User Agent": "Agente do usuário",
"IP Address": "Endereço IP",
"Payment Status": "Status do pagamento",
"Mark as Read": "Marcar Como Lida",
"Encryption Password": "Senha de Criptografia",
"Form Fields": "Campos do formulário",
"Form Name or ID": "Nome do formulário ou ID",
"Group by Folders": "Agrupar por pastas",
"Exact Name Match": "Correspondência de nome exata",
"Submission": "Submissão",
"Include Technical Metadata": "Incluir Metadados Técnicos",
"Search Value": "Pesquisar Valor",
"Results Per Page": "Resultados por Página",
"Page Number": "Número da página",
"Sort Order": "Ordem de classificação",
"Include Form Names": "Incluir Nomes dos Formulários",
"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)",
"Browser user agent to record": "Agente de usuário do navegador para gravar",
"IP address to record": "Endereço de IP para gravar",
"Payment integration status": "Integração de pagamento",
"Mark submission as read when created": "Marcar submissão como lida quando criada",
"Password for encrypted forms": "Senha para formulários criptografados",
"Fill out the form fields": "Preencha os campos do formulário",
"Enter form name or ID to search": "Digite o nome do formulário ou ID para pesquisar",
"Organize results by folders": "Organizar resultados por pastas",
"Only exact name matches": "Apenas nomes exatos correspondem",
"Include IP, user agent, and location data": "Incluir IP, usuário agente e dados de localização",
"Value to search for (minimum 3 characters)": "Valor a ser pesquisado (mínimo de 3 caracteres)",
"Number of results per page (1-100)": "Número de resultados por página (1-100)",
"Page number to return": "Número da página a retornar",
"Sort results by submission ID": "Ordenar resultados por ID da submissão",
"Include form names in results": "Incluir nomes de formulários nos resultados",
"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..",
"Newest First (DESC)": "Mais recentes (DESC)",
"Oldest First (ASC)": "Mais antigos primeiro (ASC)",
"GET": "OBTER",
"POST": "POSTAR",
"PATCH": "COMPRAR",
"PUT": "COLOCAR",
"DELETE": "EXCLUIR",
"HEAD": "CABEÇA",
"New Submission": "Nova Submissão",
"New Form": "Novo Formulário",
"Triggers when a form receives a new submission": "Aciona quando um formulário recebe uma nova submissão",
"Triggers when a new form is created": "Dispara quando um novo formulário é criado"
}

View File

@@ -0,0 +1,50 @@
{
"Formstack": "Формастек",
"Connect your Formstack account": "Подключите ваш аккаунт Formstack",
"Create Submission": "Создать сообщение",
"Find Form by Name or ID": "Найти форму по имени или ID",
"Get Submission Details": "Получить детали сданной работы",
"Find Submission by Field Value": "Поиск сообщения по значению поля",
"Submit data to a Formstack form": "Отправить данные в форму Formstack",
"Find a form by name or ID": "Найти форму по имени или ID",
"Get details of a form submission": "Получить подробную информацию об отправке формы",
"Search submissions by field values": "Поиск по значениям полей",
"Forms ": "Формы ",
"User Agent": "User Agent",
"IP Address": "IP-адрес",
"Payment Status": "Статус платежа",
"Mark as Read": "Отметить как прочитанное",
"Encryption Password": "Пароль шифрования",
"Form Fields": "Поля формы",
"Form Name or ID": "Имя или ID формы",
"Group by Folders": "Группировать по папкам",
"Exact Name Match": "Точное соответствие имен",
"Submission": "Представление",
"Include Technical Metadata": "Включить технические метаданные",
"Search Value": "Поисковое значение",
"Results Per Page": "Результаты на странице",
"Page Number": "Номер страницы",
"Sort Order": "Порядок сортировки",
"Include Form Names": "Включить имена форм",
"Browser user agent to record": "Браузер пользователя агента для записи",
"IP address to record": "IP адрес для записи",
"Payment integration status": "Статус интеграции платежа",
"Mark submission as read when created": "Отметить сабмит как прочитанный при создании",
"Password for encrypted forms": "Пароль для зашифрованных форм",
"Fill out the form fields": "Заполните поля формы",
"Enter form name or ID to search": "Введите имя формы или ID для поиска",
"Organize results by folders": "Организовать результаты по папкам",
"Only exact name matches": "Только точное имя совпадает",
"Include IP, user agent, and location data": "Включить IP, агент пользователя и данные местоположения",
"Value to search for (minimum 3 characters)": "Значение для поиска (минимум 3 символа)",
"Number of results per page (1-100)": "Количество результатов на странице (1-100)",
"Page number to return": "Возвращаемый номер страницы",
"Sort results by submission ID": "Сортировать результаты по ID сдачи",
"Include form names in results": "Включить имена форм в результаты",
"Newest First (DESC)": "Первое новое (DESC)",
"Oldest First (ASC)": "Сначала старые (ASC)",
"New Submission": "Новая заявка",
"New Form": "Новая форма",
"Triggers when a form receives a new submission": "Срабатывает при получении формы",
"Triggers when a new form is created": "Триггеры при создании новой формы"
}

View File

@@ -0,0 +1,66 @@
{
"Connect your Formstack account": "Connect your Formstack account",
"Create Submission": "Create Submission",
"Find Form by Name or ID": "Find Form by Name or ID",
"Get Submission Details": "Get Submission Details",
"Find Submission by Field Value": "Find Submission by Field Value",
"Custom API Call": "Custom API Call",
"Submit data to a Formstack form": "Submit data to a Formstack form",
"Find a form by name or ID": "Find a form by name or ID",
"Get details of a form submission": "Get details of a form submission",
"Search submissions by field values": "Search submissions by field values",
"Make a custom API call to a specific endpoint": "Make a custom API call to a specific endpoint",
"Forms ": "Forms ",
"User Agent": "User Agent",
"IP Address": "IP Address",
"Payment Status": "Payment Status",
"Mark as Read": "Mark as Read",
"Encryption Password": "Encryption Password",
"Form Fields": "Form Fields",
"Form Name or ID": "Form Name or ID",
"Group by Folders": "Group by Folders",
"Exact Name Match": "Exact Name Match",
"Submission": "Submission",
"Include Technical Metadata": "Include Technical Metadata",
"Search Value": "Search Value",
"Results Per Page": "Results Per Page",
"Page Number": "Page Number",
"Sort Order": "Sort Order",
"Include Form Names": "Include Form Names",
"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)",
"Browser user agent to record": "Browser user agent to record",
"IP address to record": "IP address to record",
"Payment integration status": "Payment integration status",
"Mark submission as read when created": "Mark submission as read when created",
"Password for encrypted forms": "Password for encrypted forms",
"Fill out the form fields": "Fill out the form fields",
"Enter form name or ID to search": "Enter form name or ID to search",
"Organize results by folders": "Organize results by folders",
"Only exact name matches": "Only exact name matches",
"Include IP, user agent, and location data": "Include IP, user agent, and location data",
"Value to search for (minimum 3 characters)": "Value to search for (minimum 3 characters)",
"Number of results per page (1-100)": "Number of results per page (1-100)",
"Page number to return": "Page number to return",
"Sort results by submission ID": "Sort results by submission ID",
"Include form names in results": "Include form names in results",
"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..",
"Newest First (DESC)": "Newest First (DESC)",
"Oldest First (ASC)": "Oldest First (ASC)",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD",
"New Submission": "New Submission",
"New Form": "New Form",
"Triggers when a form receives a new submission": "Triggers when a form receives a new submission",
"Triggers when a new form is created": "Triggers when a new form is created"
}

View File

@@ -0,0 +1,50 @@
{
"Formstack": "Formstack",
"Connect your Formstack account": "Connect your Formstack account",
"Create Submission": "Create Submission",
"Find Form by Name or ID": "Find Form by Name or ID",
"Get Submission Details": "Get Submission Details",
"Find Submission by Field Value": "Find Submission by Field Value",
"Submit data to a Formstack form": "Submit data to a Formstack form",
"Find a form by name or ID": "Find a form by name or ID",
"Get details of a form submission": "Get details of a form submission",
"Search submissions by field values": "Search submissions by field values",
"Forms ": "Forms ",
"User Agent": "User Agent",
"IP Address": "IP Address",
"Payment Status": "Payment Status",
"Mark as Read": "Mark as Read",
"Encryption Password": "Encryption Password",
"Form Fields": "Form Fields",
"Form Name or ID": "Form Name or ID",
"Group by Folders": "Group by Folders",
"Exact Name Match": "Exact Name Match",
"Submission": "Submission",
"Include Technical Metadata": "Include Technical Metadata",
"Search Value": "Search Value",
"Results Per Page": "Results Per Page",
"Page Number": "Page Number",
"Sort Order": "Sort Order",
"Include Form Names": "Include Form Names",
"Browser user agent to record": "Browser user agent to record",
"IP address to record": "IP address to record",
"Payment integration status": "Payment integration status",
"Mark submission as read when created": "Mark submission as read when created",
"Password for encrypted forms": "Password for encrypted forms",
"Fill out the form fields": "Fill out the form fields",
"Enter form name or ID to search": "Enter form name or ID to search",
"Organize results by folders": "Organize results by folders",
"Only exact name matches": "Only exact name matches",
"Include IP, user agent, and location data": "Include IP, user agent, and location data",
"Value to search for (minimum 3 characters)": "Value to search for (minimum 3 characters)",
"Number of results per page (1-100)": "Number of results per page (1-100)",
"Page number to return": "Page number to return",
"Sort results by submission ID": "Sort results by submission ID",
"Include form names in results": "Include form names in results",
"Newest First (DESC)": "Newest First (DESC)",
"Oldest First (ASC)": "Oldest First (ASC)",
"New Submission": "New Submission",
"New Form": "New Form",
"Triggers when a form receives a new submission": "Triggers when a form receives a new submission",
"Triggers when a new form is created": "Triggers when a new form is created"
}

View File

@@ -0,0 +1,66 @@
{
"Connect your Formstack account": "Connect your Formstack account",
"Create Submission": "Create Submission",
"Find Form by Name or ID": "Find Form by Name or ID",
"Get Submission Details": "Get Submission Details",
"Find Submission by Field Value": "Find Submission by Field Value",
"Custom API Call": "自定义 API 呼叫",
"Submit data to a Formstack form": "Submit data to a Formstack form",
"Find a form by name or ID": "Find a form by name or ID",
"Get details of a form submission": "Get details of a form submission",
"Search submissions by field values": "Search submissions by field values",
"Make a custom API call to a specific endpoint": "将一个自定义 API 调用到一个特定的终点",
"Forms ": "Forms ",
"User Agent": "User Agent",
"IP Address": "IP 地址",
"Payment Status": "Payment Status",
"Mark as Read": "Mark as Read",
"Encryption Password": "Encryption Password",
"Form Fields": "Form Fields",
"Form Name or ID": "Form Name or ID",
"Group by Folders": "Group by Folders",
"Exact Name Match": "Exact Name Match",
"Submission": "Submission",
"Include Technical Metadata": "Include Technical Metadata",
"Search Value": "搜索值",
"Results Per Page": "Results Per Page",
"Page Number": "Page Number",
"Sort Order": "Sort Order",
"Include Form Names": "Include Form Names",
"Method": "方法",
"Headers": "信头",
"Query Parameters": "查询参数",
"Body": "正文内容",
"Response is Binary ?": "Response is Binary ?",
"No Error on Failure": "失败时没有错误",
"Timeout (in seconds)": "超时(秒)",
"Browser user agent to record": "Browser user agent to record",
"IP address to record": "IP address to record",
"Payment integration status": "Payment integration status",
"Mark submission as read when created": "Mark submission as read when created",
"Password for encrypted forms": "Password for encrypted forms",
"Fill out the form fields": "Fill out the form fields",
"Enter form name or ID to search": "Enter form name or ID to search",
"Organize results by folders": "Organize results by folders",
"Only exact name matches": "Only exact name matches",
"Include IP, user agent, and location data": "Include IP, user agent, and location data",
"Value to search for (minimum 3 characters)": "Value to search for (minimum 3 characters)",
"Number of results per page (1-100)": "Number of results per page (1-100)",
"Page number to return": "Page number to return",
"Sort results by submission ID": "Sort results by submission ID",
"Include form names in results": "Include form names in results",
"Authorization headers are injected automatically from your connection.": "授权头自动从您的连接中注入。",
"Enable for files like PDFs, images, etc..": "Enable for files like PDFs, images, etc..",
"Newest First (DESC)": "Newest First (DESC)",
"Oldest First (ASC)": "Oldest First (ASC)",
"GET": "获取",
"POST": "帖子",
"PATCH": "PATCH",
"PUT": "弹出",
"DELETE": "删除",
"HEAD": "黑色",
"New Submission": "New Submission",
"New Form": "New Form",
"Triggers when a form receives a new submission": "Triggers when a form receives a new submission",
"Triggers when a new form is created": "Triggers when a new form is created"
}

View File

@@ -0,0 +1,41 @@
import {
createPiece,
OAuth2PropertyValue,
} from '@activepieces/pieces-framework';
import { createCustomApiCallAction } from '@activepieces/pieces-common';
import { formStackAuth } from './lib/common/auth';
import { createSubmission } from './lib/actions/create-submission';
import { findFormByNameOrId } from './lib/actions/find-form-by-name-or-id';
import { getSubmissionDetails } from './lib/actions/get-submission-details';
import { findSubmissionByFieldValue } from './lib/actions/find-submission-by-field-value';
import { newSubmission } from './lib/triggers/new-submission';
import { newForm } from './lib/triggers/new-form';
import { BASE_URL } from './lib/common/client';
export const formstack = createPiece({
displayName: 'Formstack',
auth: formStackAuth,
minimumSupportedRelease: '0.36.1',
logoUrl: 'https://cdn.activepieces.com/pieces/formstack.png',
authors: ['Sanket6652', 'onyedikachi-david'],
actions: [
createSubmission,
findFormByNameOrId,
getSubmissionDetails,
findSubmissionByFieldValue,
createCustomApiCallAction({
baseUrl: () => BASE_URL,
auth: formStackAuth,
authMapping: async (auth) => {
const authValue = auth as OAuth2PropertyValue;
return {
Authorization: `Bearer ${authValue.access_token}`,
};
},
}),
],
triggers: [newSubmission, newForm],
});

View File

@@ -0,0 +1,427 @@
import {
createAction,
OAuth2PropertyValue,
Property,
DynamicPropsValue,
} from '@activepieces/pieces-framework';
import { formIdDropdown } from '../common/props';
import { formStackAuth } from '../common/auth';
import { makeFormRequest, makeRequest } from '../common/client';
import { HttpMethod } from '@activepieces/pieces-common';
export const createSubmission = createAction({
auth: formStackAuth,
name: 'createSubmission',
displayName: 'Create Submission',
description: 'Submit data to a Formstack form',
props: {
form_id: formIdDropdown,
user_agent: Property.ShortText({
displayName: 'User Agent',
description: 'Browser user agent to record',
required: false,
}),
remote_addr: Property.ShortText({
displayName: 'IP Address',
description: 'IP address to record',
required: false,
}),
payment_status: Property.ShortText({
displayName: 'Payment Status',
description: 'Payment integration status',
required: false,
}),
read: Property.Checkbox({
displayName: 'Mark as Read',
description: 'Mark submission as read when created',
defaultValue: false,
required: false,
}),
encryption_password: Property.ShortText({
displayName: 'Encryption Password',
description: 'Password for encrypted forms',
required: false,
}),
form_fields: Property.DynamicProperties({
auth: formStackAuth,
displayName: 'Form Fields',
description: 'Fill out the form fields',
required: true,
refreshers: ['form_id'],
props: async ({ auth, form_id }) => {
if (!auth || !form_id) {
return {};
}
const authentication = auth;
const accessToken = authentication['access_token'];
try {
const formDetails = await makeRequest(
accessToken,
HttpMethod.GET,
`/form/${form_id}.json`
);
const fields: Record<string, any> = {};
if (formDetails?.fields) {
for (const field of formDetails.fields) {
const fieldKey = `field_${field.id}`;
const isRequired = field.required === '1' || field.required === 1;
const fieldLabel = field.label || field.name || field.title || `Field ${field.id}`;
if (field.type?.toLowerCase() === 'section') {
continue;
}
switch (field.type?.toLowerCase()) {
case 'select':
case 'radio':
case 'checkbox':{
const optionsData = field.options || field.choices || field.option_choices;
if (optionsData) {
let options: Array<{ label: string; value: string }> = [];
if (typeof optionsData === 'string') {
options = optionsData.split('\n').filter(opt => opt.trim()).map((option: string) => ({
label: option.trim(),
value: option.trim(),
}));
} else if (Array.isArray(optionsData)) {
options = optionsData.map((option: any) => {
if (typeof option === 'string') {
return { label: option, value: option };
} else if (option && typeof option === 'object') {
const label = option.label || option.text || option.option || option.value;
const value = option.value || option.label || option.text || option.option;
if (label && value) {
return { label: String(label), value: String(value) };
}
return null;
}
return { label: String(option), value: String(option) };
}).filter((opt: any): opt is { label: string; value: string } => opt !== null && opt.label && opt.value);
} else if (typeof optionsData === 'object') {
options = Object.entries(optionsData).map(([key, value]) => ({
label: String(value),
value: key,
}));
}
if (options.length > 0) {
if (field.type?.toLowerCase() === 'checkbox') {
fields[fieldKey] = Property.StaticMultiSelectDropdown({
displayName: fieldLabel,
description: field.description || undefined,
required: isRequired,
options: {
options,
},
});
} else {
fields[fieldKey] = Property.StaticDropdown({
displayName: fieldLabel,
description: field.description || undefined,
required: isRequired,
options: {
options,
},
});
}
} else {
fields[fieldKey] = Property.ShortText({
displayName: fieldLabel,
description: field.description || undefined,
required: isRequired,
defaultValue: field.default || undefined,
});
}
} else {
fields[fieldKey] = Property.ShortText({
displayName: fieldLabel,
description: field.description || undefined,
required: isRequired,
defaultValue: field.default || undefined,
});
}
break;
}
case 'text':
fields[fieldKey] = Property.ShortText({
displayName: fieldLabel,
description: field.description || undefined,
required: isRequired,
defaultValue: field.default || undefined,
});
break;
case 'textarea':
case 'richtext':
fields[fieldKey] = Property.LongText({
displayName: fieldLabel,
description: field.description || undefined,
required: isRequired,
defaultValue: field.default || undefined,
});
break;
case 'email':
fields[fieldKey] = Property.ShortText({
displayName: fieldLabel,
description: field.description || undefined,
required: isRequired,
defaultValue: field.default || undefined,
});
break;
case 'phone':
fields[fieldKey] = Property.ShortText({
displayName: fieldLabel,
description: field.description ? `${field.description} (Format: (XXX) XXX-XXXX)` : 'Phone number (Format: (XXX) XXX-XXXX)',
required: isRequired,
defaultValue: field.default || undefined,
});
break;
case 'number':
fields[fieldKey] = Property.Number({
displayName: fieldLabel,
description: field.description || undefined,
required: isRequired,
defaultValue: field.default ? Number(field.default) : undefined,
});
break;
case 'creditcard':
fields[fieldKey] = Property.ShortText({
displayName: fieldLabel,
description: field.description || 'Credit card number',
required: isRequired,
});
break;
case 'datetime':
fields[fieldKey] = Property.DateTime({
displayName: fieldLabel,
description: field.description || undefined,
required: isRequired,
});
break;
case 'file':
fields[fieldKey] = Property.File({
displayName: fieldLabel,
description: field.description ? `${field.description} (File will be base64 encoded automatically)` : 'Upload a file (will be base64 encoded automatically)',
required: isRequired,
});
break;
case 'name':{
const nameSubfields = field.visible_subfields || ['first', 'last'];
if (nameSubfields.includes('prefix')) {
fields[`${fieldKey}[prefix]`] = Property.ShortText({
displayName: `${fieldLabel} - Prefix`,
description: 'Title (Mr., Mrs., Dr., etc.)',
required: false,
});
}
if (nameSubfields.includes('first')) {
fields[`${fieldKey}[first]`] = Property.ShortText({
displayName: `${fieldLabel} - First Name`,
description: 'First name',
required: isRequired,
});
}
if (nameSubfields.includes('middle')) {
fields[`${fieldKey}[middle]`] = Property.ShortText({
displayName: `${fieldLabel} - Middle Name`,
description: 'Middle name',
required: false,
});
}
if (nameSubfields.includes('initial')) {
fields[`${fieldKey}[initial]`] = Property.ShortText({
displayName: `${fieldLabel} - Middle Initial`,
description: 'Middle initial',
required: false,
});
}
if (nameSubfields.includes('last')) {
fields[`${fieldKey}[last]`] = Property.ShortText({
displayName: `${fieldLabel} - Last Name`,
description: 'Last name',
required: isRequired,
});
}
if (nameSubfields.includes('suffix')) {
fields[`${fieldKey}[suffix]`] = Property.ShortText({
displayName: `${fieldLabel} - Suffix`,
description: 'Suffix (Jr., Sr., III, etc.)',
required: false,
});
}
break;
}
case 'address': {
const addressSubfields = field.visible_subfields || ['address', 'city', 'state', 'zip'];
if (addressSubfields.includes('address')) {
fields[`${fieldKey}[address]`] = Property.ShortText({
displayName: `${fieldLabel} - Address Line 1`,
description: 'Street address',
required: isRequired,
});
}
if (addressSubfields.includes('address2')) {
fields[`${fieldKey}[address2]`] = Property.ShortText({
displayName: `${fieldLabel} - Address Line 2`,
description: 'Apartment, suite, etc.',
required: false,
});
}
if (addressSubfields.includes('city')) {
fields[`${fieldKey}[city]`] = Property.ShortText({
displayName: `${fieldLabel} - City`,
description: 'City',
required: isRequired,
});
}
if (addressSubfields.includes('state')) {
fields[`${fieldKey}[state]`] = Property.ShortText({
displayName: `${fieldLabel} - State`,
description: 'State/Province',
required: isRequired,
});
}
if (addressSubfields.includes('zip')) {
fields[`${fieldKey}[zip]`] = Property.ShortText({
displayName: `${fieldLabel} - ZIP Code`,
description: 'ZIP/Postal code',
required: isRequired,
});
}
if (addressSubfields.includes('country')) {
fields[`${fieldKey}[country]`] = Property.ShortText({
displayName: `${fieldLabel} - Country`,
description: 'Country',
required: false,
});
}
break;
}
default:
fields[fieldKey] = Property.ShortText({
displayName: fieldLabel,
description: field.description || field.hint || undefined,
required: isRequired,
defaultValue: field.default || field.defaultValue || undefined,
});
break;
}
}
}
return fields;
} catch (error) {
return {};
}
},
}),
},
async run(context) {
const authentication = context.auth as OAuth2PropertyValue;
const accessToken = authentication['access_token'];
const {
form_id,
user_agent,
remote_addr,
payment_status,
read,
encryption_password,
form_fields,
} = context.propsValue;
const formData: Record<string, any> = {};
if (form_fields) {
for (const [key, value] of Object.entries(form_fields as DynamicPropsValue)) {
if (value !== undefined && value !== null && value !== '') {
if (typeof value === 'object' && value.base64) {
const filename = value.filename || 'upload.file';
formData[key] = `${filename};${value.base64}`;
} else if (Array.isArray(value)) {
formData[key] = value.join(',');
} else {
let processedValue = value;
if (key.includes('phone') || key.includes('Phone')) {
const phoneStr = String(value);
const digits = phoneStr.replace(/\D/g, '');
if (digits.length === 10) {
processedValue = `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`;
} else if (digits.length === 11 && digits[0] === '1') {
processedValue = `(${digits.slice(1, 4)}) ${digits.slice(4, 7)}-${digits.slice(7)}`;
} else {
processedValue = phoneStr;
}
}
if (typeof value === 'string' && value.includes('T') && value.includes(':')) {
const date = new Date(value);
if (!isNaN(date.getTime())) {
processedValue = date.toLocaleString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
hour: 'numeric',
minute: '2-digit',
hour12: true
});
}
}
formData[key] = processedValue;
}
}
}
}
if (user_agent) formData['user_agent'] = user_agent;
if (remote_addr) formData['remote_addr'] = remote_addr;
if (payment_status) formData['payment_status'] = payment_status;
if (read !== undefined) formData['read'] = read;
if (encryption_password) formData['encryption_password'] = encryption_password;
const response = await makeFormRequest(
accessToken,
HttpMethod.POST,
`/form/${form_id}/submission`,
formData
);
return {
success: true,
submission_id: response.id,
message: 'Submission created successfully',
details: response,
};
},
});

View File

@@ -0,0 +1,141 @@
import {
createAction,
OAuth2PropertyValue,
Property,
} from '@activepieces/pieces-framework';
import { formStackAuth } from '../common/auth';
import { makeRequest } from '../common/client';
import { HttpMethod } from '@activepieces/pieces-common';
export const findFormByNameOrId = createAction({
auth: formStackAuth,
name: 'findFormByNameOrId',
displayName: 'Find Form by Name or ID',
description: 'Find a form by name or ID',
props: {
search_query: Property.ShortText({
displayName: 'Form Name or ID',
description: 'Enter form name or ID to search',
required: true,
}),
include_folders: Property.Checkbox({
displayName: 'Group by Folders',
description: 'Organize results by folders',
defaultValue: false,
required: false,
}),
exact_match: Property.Checkbox({
displayName: 'Exact Name Match',
description: 'Only exact name matches',
defaultValue: false,
required: false,
}),
},
async run(context) {
const authentication = context.auth;
const accessToken = authentication['access_token'];
const { search_query, include_folders, exact_match } = context.propsValue;
const isNumericId = /^\d+$/.test(search_query.trim());
if (isNumericId) {
try {
const response = await makeRequest(
accessToken,
HttpMethod.GET,
`/form/${search_query}.json`,
{}
);
return {
search_type: 'id',
query: search_query,
found: true,
form: {
id: response.id,
name: response.name,
url: response.url,
created: response.created,
updated: response.updated,
submissions: response.submissions,
submissions_unread: response.submissions_unread,
last_submission_time: response.last_submission_time,
timezone: response.timezone,
folder: response.folder,
},
message: `Form found with ID: ${search_query}`,
};
} catch (error) {
return {
search_type: 'id',
query: search_query,
found: false,
form: null,
message: `No form found with ID: ${search_query}`,
error: 'Form not found or access denied',
};
}
} else {
try {
const queryParams = new URLSearchParams();
queryParams.append('search', search_query);
if (include_folders) {
queryParams.append('folders', 'true');
}
const response = await makeRequest(
accessToken,
HttpMethod.GET,
`/form.json?${queryParams.toString()}`,
{}
);
let matchingForms = response.forms || [];
if (exact_match) {
matchingForms = matchingForms.filter((form: any) =>
form.name.toLowerCase() === search_query.toLowerCase()
);
}
const foundCount = matchingForms.length;
return {
search_type: 'name',
query: search_query,
found: foundCount > 0,
total_found: foundCount,
forms: matchingForms.map((form: any) => ({
id: form.id,
name: form.name,
url: form.url,
created: form.created,
updated: form.updated,
submissions: form.submissions,
submissions_unread: form.submissions_unread,
last_submission_time: form.last_submission_time,
views: form.views,
})),
message: foundCount > 0
? `Found ${foundCount} form${foundCount === 1 ? '' : 's'} matching "${search_query}"`
: `No forms found matching "${search_query}"`,
search_options: {
exact_match_used: exact_match,
folders_included: include_folders,
},
};
} catch (error) {
return {
search_type: 'name',
query: search_query,
found: false,
forms: [],
message: `Search failed for "${search_query}"`,
error: 'Unable to search forms or access denied',
};
}
}
},
});

View File

@@ -0,0 +1,198 @@
import {
createAction,
OAuth2PropertyValue,
Property,
} from '@activepieces/pieces-framework';
import { formStackAuth } from '../common/auth';
import { makeRequest } from '../common/client';
import { HttpMethod } from '@activepieces/pieces-common';
export const findSubmissionByFieldValue = createAction({
auth: formStackAuth,
name: 'findSubmissionByFieldValue',
displayName: 'Find Submission by Field Value',
description: 'Search submissions by field values',
props: {
search_value: Property.LongText({
displayName: 'Search Value',
description: 'Value to search for (minimum 3 characters)',
required: true,
}),
per_page: Property.Number({
displayName: 'Results Per Page',
description: 'Number of results per page (1-100)',
required: false,
defaultValue: 25,
}),
page: Property.Number({
displayName: 'Page Number',
description: 'Page number to return',
required: false,
defaultValue: 1,
}),
sort_order: Property.StaticDropdown({
displayName: 'Sort Order',
description: 'Sort results by submission ID',
required: false,
defaultValue: 'DESC',
options: {
options: [
{ label: 'Newest First (DESC)', value: 'DESC' },
{ label: 'Oldest First (ASC)', value: 'ASC' },
],
},
}),
include_form_names: Property.Checkbox({
displayName: 'Include Form Names',
description: 'Include form names in results',
defaultValue: true,
required: false,
}),
},
async run(context) {
const authentication = context.auth;
const accessToken = authentication['access_token'];
const {
search_value,
per_page = 25,
page = 1,
sort_order = 'DESC',
include_form_names = true,
} = context.propsValue;
if (!search_value || search_value.trim().length < 3) {
return {
success: false,
error: 'invalid_search_length',
message: 'Search value must be at least 3 characters long.',
search_value,
};
}
const validatedPerPage = Math.min(Math.max(1, per_page || 25), 100);
const validatedPage = Math.max(1, page || 1);
try {
const params = new URLSearchParams();
params.append('search', search_value.trim());
params.append('per_page', validatedPerPage.toString());
params.append('page', validatedPage.toString());
if (sort_order) {
params.append('sort', `id-${sort_order}`);
}
const response = await makeRequest(
accessToken,
HttpMethod.GET,
`/submission?${params.toString()}`,
{}
);
const submissions = response.submissions || [];
const total = response.total || 0;
const pages = response.pages || 0;
let formNames: Record<string, string> = {};
if (include_form_names && submissions.length > 0) {
try {
const formIds = [...new Set(submissions.map((sub: any) => sub.formId))];
const formRequests = formIds.map(async (formId: any) => {
try {
const formDetails = await makeRequest(
accessToken,
HttpMethod.GET,
`/form/${formId}.json`,
{}
);
return { id: formId, name: formDetails.name };
} catch {
return { id: formId, name: `Form ${formId}` };
}
});
const formResults = await Promise.all(formRequests);
formNames = formResults.reduce((acc, form) => {
acc[form.id] = form.name;
return acc;
}, {} as Record<string, string>);
} catch (error) {
console.warn('Could not fetch form names:', error);
}
}
const formattedSubmissions = submissions.map((submission: any) => {
const result: any = {
id: submission.id,
form_id: submission.formId,
submitted_at: submission.timestamp,
submitter_info: {
ip_address: submission.remote_addr,
user_agent: submission.user_agent,
location: submission.latitude && submission.longitude ? {
latitude: submission.latitude,
longitude: submission.longitude,
} : null,
},
};
if (include_form_names && formNames[submission.formId]) {
result.form_name = formNames[submission.formId];
}
return result;
});
return {
success: true,
search_query: search_value.trim(),
results: {
submissions: formattedSubmissions,
pagination: {
current_page: validatedPage,
per_page: validatedPerPage,
total_results: total,
total_pages: pages,
has_next_page: validatedPage < pages,
has_previous_page: validatedPage > 1,
},
search_options: {
sort_order,
include_form_names,
},
},
message: `Found ${total} submission${total === 1 ? '' : 's'} matching "${search_value.trim()}"`,
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
if (errorMessage.includes('400')) {
return {
success: false,
error: 'invalid_search',
message: 'Invalid search parameters. Please check your search value and try again.',
search_value,
};
}
if (errorMessage.includes('403') || errorMessage.includes('unauthorized')) {
return {
success: false,
error: 'access_denied',
message: 'Access denied. You may not have permission to search submissions.',
search_value,
};
}
return {
success: false,
error: 'search_failed',
message: `Search failed: ${errorMessage}`,
search_value,
};
}
},
});

View File

@@ -0,0 +1,137 @@
import {
createAction,
OAuth2PropertyValue,
Property,
} from '@activepieces/pieces-framework';
import { formIdDropdown, submissionIdDropdown } from '../common/props';
import { formStackAuth } from '../common/auth';
import { makeRequest } from '../common/client';
import { HttpMethod } from '@activepieces/pieces-common';
export const getSubmissionDetails = createAction({
auth: formStackAuth,
name: 'getSubmissionDetails',
displayName: 'Get Submission Details',
description: 'Get details of a form submission',
props: {
form_id: formIdDropdown,
submission_id: submissionIdDropdown,
encryption_password: Property.ShortText({
displayName: 'Encryption Password',
description: 'Password for encrypted forms',
required: false,
}),
include_metadata: Property.Checkbox({
displayName: 'Include Technical Metadata',
description: 'Include IP, user agent, and location data',
defaultValue: false,
required: false,
}),
},
async run(context) {
const authentication = context.auth;
const accessToken = authentication['access_token'];
const {
form_id,
submission_id,
encryption_password,
include_metadata,
} = context.propsValue;
try {
const queryParams: Record<string, string> = {};
if (encryption_password) {
queryParams['encryption_password'] = encryption_password;
}
const response = await makeRequest(
accessToken,
HttpMethod.GET,
`/submission/${submission_id}.json`,
{},
queryParams
);
const formattedData: Record<string, any> = {};
if (response.data && Array.isArray(response.data)) {
for (const item of response.data) {
if (item.field && item.value !== undefined) {
formattedData[`field_${item.field}`] = item.value;
}
}
}
const result = {
submission: {
id: response.id,
form_id: response.form,
submitted_at: response.timestamp,
data: formattedData,
raw_data: response.data,
},
form_data_count: response.data ? response.data.length : 0,
success: true,
message: `Successfully retrieved submission ${submission_id} from form ${form_id}`,
};
if (include_metadata) {
(result.submission as any).metadata = {
user_agent: response.user_agent,
ip_address: response.remote_addr,
latitude: response.latitude,
longitude: response.longitude,
portal_info: response.portal_name ? {
portal_name: response.portal_name,
portal_form_name: response.portal_form_name,
participant_email: response.portal_participant_email,
} : null,
};
}
return result;
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
if (errorMessage.includes('404') || errorMessage.includes('not found')) {
return {
success: false,
error: 'submission_not_found',
message: `Submission with ID "${submission_id}" was not found. Please check the submission ID and try again.`,
submission_id,
form_id,
};
}
if (errorMessage.includes('403') || errorMessage.includes('unauthorized')) {
return {
success: false,
error: 'access_denied',
message: `Access denied to submission "${submission_id}". You may not have permission to view this submission.`,
submission_id,
form_id,
};
}
if (errorMessage.includes('encryption')) {
return {
success: false,
error: 'encryption_required',
message: `This submission is encrypted and requires a decryption password. Please provide the encryption password and try again.`,
submission_id,
form_id,
};
}
return {
success: false,
error: 'retrieval_failed',
message: `Failed to retrieve submission "${submission_id}": ${errorMessage}`,
submission_id,
form_id,
};
}
},
});

View File

@@ -0,0 +1,10 @@
import { PieceAuth } from "@activepieces/pieces-framework";
export const formStackAuth = PieceAuth.OAuth2({
description: 'Connect your Formstack account',
authUrl: 'https://www.formstack.com/api/v2/oauth2/authorize',
tokenUrl: 'https://www.formstack.com/api/v2/oauth2/token',
required: true,
scope: [],
});

View File

@@ -0,0 +1,117 @@
import { HttpMethod, httpClient } from '@activepieces/pieces-common';
export const BASE_URL = 'https://www.formstack.com/api/v2';
export const RATE_LIMIT = {
DAILY_LIMIT: 14400, // 14,400 calls per access token per day
WINDOW: 24 * 60 * 60 * 1000, // 24 hours in milliseconds
};
export enum RequestFormat {
JSON = 'json',
URL_ENCODED = 'url-encoded',
}
interface RequestOptions {
format?: RequestFormat;
useQueryAuth?: boolean;
}
export async function makeRequest(
accessToken: string,
method: HttpMethod,
path: string,
body?: unknown,
queryParams?: Record<string, any>,
options: RequestOptions = {}
) {
const {
format = RequestFormat.JSON,
useQueryAuth = false,
} = options;
try {
const headers: Record<string, string> = {};
if (useQueryAuth) {
queryParams = { ...queryParams, oauth_token: accessToken };
} else {
headers['Authorization'] = `Bearer ${accessToken}`;
}
// Only set content type and process body for requests that should have a body
let requestBody = undefined;
const shouldHaveBody = method !== HttpMethod.GET && method !== HttpMethod.DELETE && body !== undefined;
if (shouldHaveBody) {
if (format === RequestFormat.JSON) {
headers['Content-Type'] = 'application/json';
requestBody = body;
} else {
headers['Content-Type'] = 'application/x-www-form-urlencoded';
if (typeof body === 'object') {
requestBody = Object.entries(body as Record<string, any>)
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)
.join('&');
} else {
requestBody = body;
}
}
}
const response = await httpClient.sendRequest({
method,
url: `${BASE_URL}${path}`,
headers,
body: requestBody,
queryParams,
});
return response.body;
} catch (error: any) {
const status = error.response?.status || error.status;
const errorMessage = error.response?.body?.error || error.message || 'Unknown error';
switch (status) {
case 400:
throw new Error(`Bad Request: The request URI was invalid. ${errorMessage}`);
case 401:
throw new Error(`Unauthorized: Valid OAuth2 credentials were not supplied. Please check your access token.`);
case 403:
throw new Error(`Forbidden: You do not have access to this resource. Check your user permissions in Formstack.`);
case 404:
throw new Error(`Not Found: The requested resource could not be found. ${errorMessage}`);
case 405:
throw new Error(`Method Not Allowed: The requested HTTP method is not supported for this endpoint.`);
case 415:
throw new Error(`Unsupported Media Type: Please use a valid media type (JSON or URL-encoded).`);
case 429:
throw new Error(`Rate Limit Exceeded: You have hit the daily limit of ${RATE_LIMIT.DAILY_LIMIT} API calls. Try again in 24 hours.`);
case 500:
throw new Error(`Internal Server Error: An error occurred while processing the request. Please try again later.`);
default:
throw new Error(`Request failed (${status}): ${errorMessage}`);
}
}
}
export async function makeFormRequest(
accessToken: string,
method: HttpMethod,
path: string,
formData: Record<string, any>,
queryParams?: Record<string, any>
) {
return makeRequest(accessToken, method, path, formData, queryParams, {
format: RequestFormat.URL_ENCODED,
});
}

View File

@@ -0,0 +1,122 @@
import { OAuth2PropertyValue, Property } from '@activepieces/pieces-framework';
import { makeRequest } from './client';
import { HttpMethod } from '@activepieces/pieces-common';
import { formStackAuth } from './auth';
export const formIdDropdown = Property.Dropdown({
displayName: 'Forms ',
required: true,
auth: formStackAuth,
refreshers: ['auth'],
options: async ({ auth }) => {
if (!auth) {
return {
disabled: true,
placeholder: 'Connect your account',
options: [],
};
}
const authentication = auth;
const accessToken = authentication['access_token'];
const forms = await makeRequest(
accessToken,
HttpMethod.GET,
'/form.json'
);
const options = forms.forms.map((field: { id: string; name: string }) => {
return {
label: field.name,
value: field.id,
};
});
return {
options,
};
},
});
export const submissionIdDropdown = Property.Dropdown({
auth: formStackAuth,
displayName: 'Submission',
required: true,
refreshers: ['auth', 'form_id'],
options: async ({ auth, form_id }) => {
if (!auth) {
return {
disabled: true,
placeholder: 'Connect your account',
options: [],
};
}
if (!form_id) {
return {
disabled: true,
placeholder: 'Please select a form first',
options: [],
};
}
const authentication = auth;
const accessToken = authentication['access_token'];
try {
const response = await makeRequest(
accessToken,
HttpMethod.GET,
`/form/${form_id}/submission.json`
);
if (!response.submissions || response.submissions.length === 0) {
return {
disabled: true,
placeholder: 'No submissions found for this form',
options: [],
};
}
const options = response.submissions.map((submission: any) => {
let label = `ID: ${submission.id}`;
if (submission.timestamp) {
const date = new Date(submission.timestamp).toLocaleDateString();
label += ` (${date})`;
}
if (submission.data && Array.isArray(submission.data) && submission.data.length > 0) {
const previewFields = submission.data
.slice(0, 2)
.map((field: any) => {
if (field.value && typeof field.value === 'string' && field.value.length > 0) {
const value = field.value.length > 20
? field.value.substring(0, 20) + '...'
: field.value;
return value;
}
return null;
})
.filter(Boolean);
if (previewFields.length > 0) {
label += ` - ${previewFields.join(', ')}`;
}
}
return {
label,
value: submission.id,
};
});
return {
options,
};
} catch (error) {
return {
disabled: true,
placeholder: 'Error loading submissions',
options: [],
};
}
},
});

View File

@@ -0,0 +1,162 @@
import {
createTrigger,
TriggerStrategy,
PiecePropValueSchema,
OAuth2PropertyValue,
AppConnectionValueForAuthProperty
} from '@activepieces/pieces-framework';
import {
DedupeStrategy,
Polling,
pollingHelper,
HttpMethod
} from '@activepieces/pieces-common';
import dayjs from 'dayjs';
import { formStackAuth } from '../common/auth';
import { makeRequest } from '../common/client';
const polling: Polling<AppConnectionValueForAuthProperty<typeof formStackAuth>, any> = {
strategy: DedupeStrategy.TIMEBASED,
items: async ({ auth, propsValue, lastFetchEpochMS }) => {
const authentication = auth as OAuth2PropertyValue;
const accessToken = authentication['access_token'];
try {
const formsResponse = await makeRequest(
accessToken,
HttpMethod.GET,
'/form.json'
);
const forms = formsResponse.forms || [];
console.log('Raw forms API response:', JSON.stringify(formsResponse, null, 2));
const newForms = forms.filter((form: any) => {
if (!form.created) return false;
const createdTime = dayjs(form.created, 'YYYY-MM-DD HH:mm:ss').valueOf();
return createdTime > (lastFetchEpochMS ?? 0);
});
return newForms.map((form: any) => ({
epochMilliSeconds: dayjs(form.created, 'YYYY-MM-DD HH:mm:ss').valueOf(),
data: {
id: form.id,
name: form.name,
views: parseInt(form.views) || 0,
created: form.created,
updated: form.updated,
submissions: parseInt(form.submissions) || 0,
submissions_unread: parseInt(form.submissions_unread) || 0,
last_submission_id: form.last_submission_id,
last_submission_time: form.last_submission_time,
url: form.url,
data_url: form.data_url,
summary_url: form.summary_url,
rss_url: form.rss_url,
timezone: form.timezone,
status: form.status,
folder: form.folder,
language: form.language,
encrypted: form.encrypted,
ssl: form.ssl,
viewkey: form.viewkey,
},
}));
} catch (error) {
console.error('Error fetching forms:', error);
return [];
}
}
};
export const newForm = createTrigger({
auth: formStackAuth,
name: 'newForm',
displayName: 'New Form',
description: 'Triggers when a new form is created',
props: {},
sampleData: {
id: "6244510",
name: "Customer Registration Form",
views: 342,
created: "2024-12-15 14:22:18",
updated: "2024-12-20 09:45:33",
submissions: 127,
submissions_unread: 8,
last_submission_id: "1361884054",
last_submission_time: "2025-01-14 14:16:49",
url: "https://activepieces.formstack.com/forms/customer_registration",
data_url: "https://www.formstack.com/admin/form/summary/6244510",
summary_url: "https://www.formstack.com/admin/form/data/6244510",
rss_url: "https://www.formstack.com/forms/index.php?6244510-rss",
timezone: "America/New_York",
status: "1",
folder: "Marketing Forms",
language: "en",
encrypted: "0",
ssl: "1",
viewkey: "K6244510MjPqRt"
},
type: TriggerStrategy.POLLING,
async test(context) {
try {
const result = await pollingHelper.test(polling, context);
if (result && result.length > 0) {
return result;
}
return [{
epochMilliSeconds: Date.now(),
data: {
message: 'No recently created forms found. The trigger is working and will detect new forms when they are created.',
note: 'This trigger monitors for newly created forms in your Formstack account.'
}
}];
} catch (error) {
console.error('Error in test function:', error);
return [{
epochMilliSeconds: Date.now(),
data: {
error: 'Unable to fetch real data. Here is sample data to show the expected structure.',
sample: true,
id: "6244510",
name: "Customer Registration Form",
views: 342,
created: "2024-12-15 14:22:18",
updated: "2024-12-20 09:45:33",
submissions: 127,
submissions_unread: 8,
last_submission_id: "1361884054",
last_submission_time: "2025-01-14 14:16:49",
url: "https://activepieces.formstack.com/forms/customer_registration",
data_url: "https://www.formstack.com/admin/form/summary/6244510",
summary_url: "https://www.formstack.com/admin/form/data/6244510",
rss_url: "https://www.formstack.com/forms/index.php?6244510-rss",
timezone: "America/New_York",
status: "1",
folder: "Marketing Forms",
language: "en",
encrypted: "0",
ssl: "1",
viewkey: "K6244510MjPqRt"
}
}];
}
},
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) {
return await pollingHelper.poll(polling, context);
},
});

View File

@@ -0,0 +1,261 @@
import {
createTrigger,
TriggerStrategy,
PiecePropValueSchema,
OAuth2PropertyValue,
AppConnectionValueForAuthProperty,
} from '@activepieces/pieces-framework';
import {
DedupeStrategy,
Polling,
pollingHelper,
HttpMethod,
} from '@activepieces/pieces-common';
import dayjs from 'dayjs';
import timezone from 'dayjs/plugin/timezone';
import utc from 'dayjs/plugin/utc';
import { formStackAuth } from '../common/auth';
import { formIdDropdown } from '../common/props';
import { makeRequest } from '../common/client';
dayjs.extend(timezone);
dayjs.extend(utc);
const polling: Polling<AppConnectionValueForAuthProperty<typeof formStackAuth>, any> = {
strategy: DedupeStrategy.TIMEBASED,
items: async ({ auth, propsValue, lastFetchEpochMS }) => {
const authentication = auth as OAuth2PropertyValue;
const accessToken = authentication['access_token'];
const formId = propsValue.form_id;
if (!formId) {
return [];
}
try {
const isTest = lastFetchEpochMS === 0;
// use form timezone for storing epochMilliSeconds
const formResponse = await makeRequest(
accessToken,
HttpMethod.GET,
`/form/${formId}.json`
);
const formTimezone = formResponse.timezone;
const queryParams: Record<string, any> = {
data: 'true',
sort: 'DESC',
page: 1,
};
if (isTest) {
queryParams['per_page'] = 10;
} else {
queryParams['per_page'] = 100;
// YYYY-MM-DD HH:MM:SS expected (Based around Eastern Time)
queryParams['min_time'] = dayjs
.utc(lastFetchEpochMS)
.tz('America/New_York')
.format('YYYY-MM-DD HH:mm:ss');
}
const detailedSubmissions: any[] = [];
let hasMorePages = true;
while (hasMorePages) {
const submissionsResponse = await makeRequest(
accessToken,
HttpMethod.GET,
`/form/${formId}/submission.json`,
undefined,
queryParams
);
const submissions: any[] = submissionsResponse.submissions || [];
submissions.forEach((submission: any) => {
detailedSubmissions.push({
epochMilliSeconds: dayjs
.tz(submission.timestamp, formTimezone)
.valueOf(),
data: {
id: submission.id,
form_id: formId,
timestamp: submission.timestamp,
user_agent: submission.user_agent,
remote_addr: submission.remote_addr,
latitude: submission.latitude,
longitude: submission.longitude,
payment_status: submission.payment_status || '',
data: Object.values(submission.data),
},
});
});
if (isTest || queryParams['page'] >= submissionsResponse['pages']) {
hasMorePages = false;
} else {
queryParams['page'] += 1;
}
}
return detailedSubmissions;
} catch (error) {
console.error('Error fetching submissions:', error);
return [];
}
},
};
export const newSubmission = createTrigger({
auth: formStackAuth,
name: 'newSubmission',
displayName: 'New Submission',
description: 'Triggers when a form receives a new submission',
props: {
form_id: formIdDropdown,
},
sampleData: {
id: '185502901',
form_id: '5439876',
timestamp: '2024-01-15T14:32:18Z',
user_agent:
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
remote_addr: '198.51.100.42',
latitude: '37.7749',
longitude: '-122.4194',
payment_status: '',
data: [
{
field: '185502595',
value: 'John',
},
{
field: '185502596',
value: 'Doe',
},
{
field: '185502597',
value: 'john.doe@company.com',
},
{
field: '185502598',
value: '(555) 123-4567',
},
{
field: '185502599',
value: 'Software Engineer',
},
{
field: '185502600',
value: 'Premium Plan',
},
{
field: '185502601',
value: 'Newsletter, Product Updates',
},
{
field: '185502602',
value: '2',
},
{
field: '185502603',
value: '4532123456789012',
},
{
field: '185502604',
value:
'https://files.formstack.com/uploads/5439876/185502901/987654321/resume.pdf',
},
{
field: '185502605',
value:
'I would like to learn more about your enterprise solutions and discuss potential partnership opportunities.',
},
{
field: '185502606',
value: '01/15/2024',
},
{
field: '185502607',
value: 'San Francisco',
},
{
field: '185502608',
value: 'CA',
},
{
field: '185502609',
value: '94105',
},
],
},
type: TriggerStrategy.POLLING,
async test(context) {
try {
const result = await pollingHelper.test(polling, context);
if (result && result.length > 0) {
return result;
}
return [
{
epochMilliSeconds: Date.now(),
data: {
message:
'No recent submissions found. The trigger is working and will detect new submissions when they arrive.',
form_id: context.propsValue.form_id,
},
},
];
} catch (error) {
console.error('Error in test function:', error);
return [
{
epochMilliSeconds: Date.now(),
data: {
error:
'Unable to fetch real data. Here is sample data to show the expected structure.',
sample: true,
id: '185502901',
form_id: '5439876',
timestamp: '2024-01-15T14:32:18Z',
user_agent:
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
remote_addr: '198.51.100.42',
latitude: '37.7749',
longitude: '-122.4194',
payment_status: '',
data: [
{
field: '185502595',
value: 'John',
},
{
field: '185502596',
value: 'Doe',
},
{
field: '185502597',
value: 'john.doe@company.com',
},
],
},
},
];
}
},
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) {
return await pollingHelper.poll(polling, context);
},
});

View File

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

View File

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