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,48 @@
{
"open source spreadsheet": "Open Source Tabellenkalkulation",
"API Key": "API-Schlüssel",
"Domain URL": "Domänen-URL",
"\n\tLog in to your Grist account. Navigate to the account menu at the top right, and select **Profile Settings** to manage or create your API Key.\n\tIn the **Domain URL** field, enter the domain URL of your Grist instance.For example,if you have team site it will be \"https://team.getgist.com\".": "\n\tMelde dich bei deinem Grist Account an. Navigieren Sie zum Konten-Menü oben rechts und wählen Sie **Profileinstellungen** um Ihren API-Schlüssel zu verwalten oder zu erstellen.\n\tIm Feld **Domain URL** geben Sie die Domain URL Ihrer Grist Instanz ein. Wenn Sie eine Team-Seite haben, lautet sie zum Beispiel \"https://team.getgist.com\".",
"Create Record": "Datensatz erstellen",
"Search Record": "Datensatz suchen",
"Update Record": "Datensatz aktualisieren",
"Upload Attachment to Document": "Anhang zum Dokument hochladen",
"Custom API Call": "Eigener API-Aufruf",
"Creates a new record in specific table.": "Erstellt einen neuen Datensatz in einer bestimmten Tabelle.",
"Search record by matching criteria.": "Datensatz nach passenden Kriterien suchen.",
"Updates an existing record in specific table.": "Aktualisiert einen vorhandenen Datensatz in einer bestimmten Tabelle.",
"Uploads attachments to specific document.": "Anhänge in ein bestimmtes Dokument hochladen.",
"Make a custom API call to a specific endpoint": "Einen benutzerdefinierten API-Aufruf an einen bestimmten Endpunkt machen",
"Workspace": "Arbeitsbereich",
"Document": "Dokument",
"Table": "Tisch",
"Table Columns": "Tabellenspalten",
"Column": "Spalte",
"Value": "Wert",
"Record ID": "Datensatz-ID",
"Attachment": "Anhang",
"Attachment Name": "Anhangname",
"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)",
"Enter the search text. The search operation is case sensitive, exact text match, and supports column-type Text only.": "Geben Sie den Suchtext ein. Groß-/Kleinschreibung und exakte Textpassung werden nur von einem Spalten unterstützt.",
"In case you want to change the name of the attachment.": "Falls Sie den Namen des Anhangs ändern möchten.",
"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..",
"GET": "ERHALTEN",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "LÖSCHEN",
"HEAD": "HEAD",
"New Record": "Neuer Datensatz",
"Updated Record": "Aktualisierte Datensatz",
"Triggers when a new record is added to the table.": "Wird ausgelöst, wenn ein neuer Datensatz zur Tabelle hinzugefügt wird.",
"Triggers when a record is updated in the table.": "Wird ausgelöst, wenn ein Datensatz in der Tabelle aktualisiert wird.",
"Readiness Column": "Bereitschaftsspalte",
"A toggle (boolean) column which is True when the record is ready. The trigger will only be activated when that record becomes ready.Please follow [guideline](https://support.getgrist.com/integrators/#readiness-column) to create readiness column in table.": "Eine Schalter (boolean) Spalte, die wahr ist, wenn der Datensatz bereit ist. Der Trigger wird erst aktiviert, wenn der Datensatz bereit ist. leasen folgen [guideline](https://support.getgrist.com/integrators/#readiness-column), um die Bereitschaftsspalte in der Tabelle zu erstellen."
}

View File

@@ -0,0 +1,48 @@
{
"open source spreadsheet": "hoja de cálculo de código abierto",
"API Key": "Clave API",
"Domain URL": "URL del dominio",
"\n\tLog in to your Grist account. Navigate to the account menu at the top right, and select **Profile Settings** to manage or create your API Key.\n\tIn the **Domain URL** field, enter the domain URL of your Grist instance.For example,if you have team site it will be \"https://team.getgist.com\".": "\n\tLog in to your Grist account. Navigate to the account menu at the top right, and select **Profile Settings** to manage or create your API Key.\n\tIn the **Domain URL** field, enter the domain URL of your Grist instance.For example,if you have team site it will be \"https://team.getgist.com\".",
"Create Record": "Crear registro",
"Search Record": "Registro de búsqueda",
"Update Record": "Actualizar registro",
"Upload Attachment to Document": "Subir adjunto al documento",
"Custom API Call": "Llamada API personalizada",
"Creates a new record in specific table.": "Crea un nuevo registro en una tabla específica.",
"Search record by matching criteria.": "Buscar registros por criterios coincidentes.",
"Updates an existing record in specific table.": "Actualiza un registro existente en una tabla específica.",
"Uploads attachments to specific document.": "Sube archivos adjuntos al documento específico.",
"Make a custom API call to a specific endpoint": "Hacer una llamada API personalizada a un extremo específico",
"Workspace": "Espacio de trabajo",
"Document": "Documento",
"Table": "Tabla",
"Table Columns": "Columnas de tabla",
"Column": "Columna",
"Value": "Valor",
"Record ID": "ID de registro",
"Attachment": "Adjunto",
"Attachment Name": "Nombre del archivo adjunto",
"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)",
"Enter the search text. The search operation is case sensitive, exact text match, and supports column-type Text only.": "Introduzca el texto de búsqueda. La operación de búsqueda es sensible a mayúsculas y exacta coincidencia de texto, y sólo soporta texto de tipo columna.",
"In case you want to change the name of the attachment.": "En caso de que desee cambiar el nombre del archivo adjunto.",
"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.",
"GET": "RECOGER",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "BORRAR",
"HEAD": "LIMPIO",
"New Record": "Nuevo registro",
"Updated Record": "Registro actualizado",
"Triggers when a new record is added to the table.": "Se activa cuando se añade un nuevo registro a la tabla.",
"Triggers when a record is updated in the table.": "Dispara cuando un registro se actualiza en la tabla.",
"Readiness Column": "Columna de Realidad",
"A toggle (boolean) column which is True when the record is ready. The trigger will only be activated when that record becomes ready.Please follow [guideline](https://support.getgrist.com/integrators/#readiness-column) to create readiness column in table.": "Una columna (booleana) que es True cuando el registro está listo. El disparador sólo se activará cuando ese registro esté listo. siga el leasing [guideline](https://support.getgrist.com/integrators/#readiness-column) para crear la columna de preparación en la tabla."
}

View File

@@ -0,0 +1,48 @@
{
"open source spreadsheet": "feuille de calcul open source",
"API Key": "Clé API",
"Domain URL": "URL du domaine",
"\n\tLog in to your Grist account. Navigate to the account menu at the top right, and select **Profile Settings** to manage or create your API Key.\n\tIn the **Domain URL** field, enter the domain URL of your Grist instance.For example,if you have team site it will be \"https://team.getgist.com\".": "\n\tConnectez-vous à votre compte Grist. Naviguez dans le menu du compte en haut à droite et sélectionnez **Paramètres du profil** pour gérer ou créer votre clé API.\n\tDans le champ **URL de domaine**, entrez l'URL du domaine de votre instance Grist. Par exemple, si vous avez un site d'équipe, il sera \"https://team.getgist.com\".",
"Create Record": "Créer un enregistrement",
"Search Record": "Rechercher un enregistrement",
"Update Record": "Mettre à jour l'enregistrement",
"Upload Attachment to Document": "Télécharger la pièce jointe au document",
"Custom API Call": "Appel API personnalisé",
"Creates a new record in specific table.": "Crée un nouvel enregistrement dans une table spécifique.",
"Search record by matching criteria.": "Rechercher un enregistrement par critères correspondants.",
"Updates an existing record in specific table.": "Met à jour un enregistrement existant dans une table spécifique.",
"Uploads attachments to specific document.": "Téléverse des pièces jointes à un document spécifique.",
"Make a custom API call to a specific endpoint": "Passez un appel API personnalisé à un point de terminaison spécifique",
"Workspace": "Espace de travail",
"Document": "Document",
"Table": "Tableau",
"Table Columns": "Colonnes de table",
"Column": "Colonne",
"Value": "Valeur",
"Record ID": "ID de l'enregistrement",
"Attachment": "Pièce jointe",
"Attachment Name": "Nom de la pièce jointe",
"Method": "Méthode",
"Headers": "En-têtes",
"Query Parameters": "Paramètres de requête",
"Body": "Corps",
"Response is Binary ?": "La réponse est Binaire ?",
"No Error on Failure": "Aucune erreur en cas d'échec",
"Timeout (in seconds)": "Délai d'attente (en secondes)",
"Enter the search text. The search operation is case sensitive, exact text match, and supports column-type Text only.": "Entrez le texte de la recherche. L'opération de recherche est sensible à la casse, le texte exact correspond et ne supporte que le texte de type colonne.",
"In case you want to change the name of the attachment.": "Si vous voulez changer le nom de la pièce jointe.",
"Authorization headers are injected automatically from your connection.": "Les en-têtes 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.",
"GET": "OBTENIR",
"POST": "POSTER",
"PATCH": "PATCH",
"PUT": "EFFACER",
"DELETE": "SUPPRIMER",
"HEAD": "TÊTE",
"New Record": "Nouvel enregistrement",
"Updated Record": "Enregistrement mis à jour",
"Triggers when a new record is added to the table.": "Déclenche lorsqu'un nouvel enregistrement est ajouté à la table.",
"Triggers when a record is updated in the table.": "Déclenche lorsqu'un enregistrement est mis à jour dans le tableau.",
"Readiness Column": "Colonne de préparation",
"A toggle (boolean) column which is True when the record is ready. The trigger will only be activated when that record becomes ready.Please follow [guideline](https://support.getgrist.com/integrators/#readiness-column) to create readiness column in table.": "Une colonne de bascule (booléen) qui est True lorsque l'enregistrement est prêt. Le déclencheur ne sera activé que lorsque cet enregistrement sera prêt. baillez suivez [guideline](https://support.getgrist.com/integrators/#readiness-column) pour créer une colonne de préparation dans le tableau."
}

View File

@@ -0,0 +1,48 @@
{
"open source spreadsheet": "オープンソースの表計算ドキュメント",
"API Key": "API キー",
"Domain URL": "ドメインURL",
"\n\tLog in to your Grist account. Navigate to the account menu at the top right, and select **Profile Settings** to manage or create your API Key.\n\tIn the **Domain URL** field, enter the domain URL of your Grist instance.For example,if you have team site it will be \"https://team.getgist.com\".": "\n\tLog in to your Grist account. Navigate to the account menu at the top right, and select **Profile Settings** to manage or create your API Key.\n\tIn the **Domain URL** field, enter the domain URL of your Grist instance.For example,if you have team site it will be \"https://team.getgist.com\".",
"Create Record": "レコードを作成",
"Search Record": "レコードを検索",
"Update Record": "更新記録",
"Upload Attachment to Document": "添付ファイルをドキュメントにアップロード",
"Custom API Call": "カスタムAPI通話",
"Creates a new record in specific table.": "特定のテーブルに新しいレコードを作成します。",
"Search record by matching criteria.": "一致する条件でレコードを検索します。",
"Updates an existing record in specific table.": "特定のテーブルの既存のレコードを更新します。",
"Uploads attachments to specific document.": "特定のドキュメントに添付ファイルをアップロードします。",
"Make a custom API call to a specific endpoint": "特定のエンドポイントへのカスタム API コールを実行します。",
"Workspace": "ワークスペース",
"Document": "ドキュメント",
"Table": "表",
"Table Columns": "表の列",
"Column": "列",
"Value": "値",
"Record ID": "レコードID",
"Attachment": "添付ファイル",
"Attachment Name": "添付ファイル名",
"Method": "方法",
"Headers": "ヘッダー",
"Query Parameters": "クエリパラメータ",
"Body": "本文",
"Response is Binary ?": "応答はバイナリですか?",
"No Error on Failure": "失敗時にエラーはありません",
"Timeout (in seconds)": "タイムアウト(秒)",
"Enter the search text. The search operation is case sensitive, exact text match, and supports column-type Text only.": "検索テキストを入力します。検索操作は大文字小文字を区別し、正確なテキストマッチを行い、列型のテキストのみをサポートします。",
"In case you want to change the name of the attachment.": "添付ファイルの名前を変更したい場合.",
"Authorization headers are injected automatically from your connection.": "認証ヘッダは接続から自動的に注入されます。",
"Enable for files like PDFs, images, etc..": "PDF、画像などのファイルを有効にします。",
"GET": "取得",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "削除",
"HEAD": "頭",
"New Record": "新しいレコード",
"Updated Record": "更新されたレコード",
"Triggers when a new record is added to the table.": "テーブルに新しいレコードが追加されたときにトリガーします。",
"Triggers when a record is updated in the table.": "テーブルでレコードが更新されたときにトリガーされます。",
"Readiness Column": "準備列",
"A toggle (boolean) column which is True when the record is ready. The trigger will only be activated when that record becomes ready.Please follow [guideline](https://support.getgrist.com/integrators/#readiness-column) to create readiness column in table.": "レコードの準備ができたときに true に設定されたトグル(boolean) カラムを切り替えます。トリガーは、そのレコードが準備できたときにのみ有効になります。 リースは [guideline](https://support.getgrist.com/integrators/#readiness-column) に従ってテーブルに準備列を作成します。"
}

View File

@@ -0,0 +1,48 @@
{
"open source spreadsheet": "open source rekenblad",
"API Key": "API Sleutel",
"Domain URL": "Domein URL",
"\n\tLog in to your Grist account. Navigate to the account menu at the top right, and select **Profile Settings** to manage or create your API Key.\n\tIn the **Domain URL** field, enter the domain URL of your Grist instance.For example,if you have team site it will be \"https://team.getgist.com\".": "\n\t→Log in op je Grist account. Navigeer naar het accountmenu rechtsboven en selecteer **Profielinstellingen** om je API-sleutel te beheren of te maken.\n\t. In het **Domein-URL** veld, voer je de domein-URL van je Grist instantie in. Bijvoorbeeld, als je teamsite hebt, dan is het \"https://team.getgist.com\".",
"Create Record": "Record Maken",
"Search Record": "Zoek Record",
"Update Record": "Update Record",
"Upload Attachment to Document": "Bijlage uploaden naar document",
"Custom API Call": "Custom API Call",
"Creates a new record in specific table.": "Maakt een nieuw record aan in specifieke tabel.",
"Search record by matching criteria.": "Zoek record op overeenkomende criteria.",
"Updates an existing record in specific table.": "Werkt een bestaand record in specifieke tabel bij.",
"Uploads attachments to specific document.": "Upload bijlagen naar een specifiek document.",
"Make a custom API call to a specific endpoint": "Maak een aangepaste API call naar een specifiek eindpunt",
"Workspace": "werkruimte",
"Document": "Document",
"Table": "Tabel",
"Table Columns": "Tabel kolommen",
"Column": "Kolom",
"Value": "Waarde",
"Record ID": "Record ID",
"Attachment": "Bijlage",
"Attachment Name": "Bijlage naam",
"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)",
"Enter the search text. The search operation is case sensitive, exact text match, and supports column-type Text only.": "Voer de zoektekst in. De zoekactie is hoofdlettergevoelig, exacte tekst overeenkomend en ondersteunt alleen kolomtypes.",
"In case you want to change the name of the attachment.": "In het geval dat u de naam van de bijlage wilt wijzigen.",
"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..",
"GET": "KRIJG",
"POST": "POSTE",
"PATCH": "BEKIJK",
"PUT": "PUT",
"DELETE": "VERWIJDEREN",
"HEAD": "HOOFD",
"New Record": "Nieuwe Record",
"Updated Record": "Bijgewerkt Record",
"Triggers when a new record is added to the table.": "Triggert wanneer een nieuwe record wordt toegevoegd aan de tabel.",
"Triggers when a record is updated in the table.": "Activeert wanneer een record wordt bijgewerkt in de tabel.",
"Readiness Column": "Gereedschap kolom",
"A toggle (boolean) column which is True when the record is ready. The trigger will only be activated when that record becomes ready.Please follow [guideline](https://support.getgrist.com/integrators/#readiness-column) to create readiness column in table.": "Een schakelaar (boolean) kolom die waar is wanneer het record klaar is. De trigger wordt alleen geactiveerd wanneer die record klaar is. lease follow [guideline](https://support.getgrist.com/integrators/#readiness-column) om gereedheidskolom in tabel te maken."
}

View File

@@ -0,0 +1,48 @@
{
"open source spreadsheet": "planilha de código aberto",
"API Key": "Chave de API",
"Domain URL": "URL do domínio",
"\n\tLog in to your Grist account. Navigate to the account menu at the top right, and select **Profile Settings** to manage or create your API Key.\n\tIn the **Domain URL** field, enter the domain URL of your Grist instance.For example,if you have team site it will be \"https://team.getgist.com\".": "\n\tfaça login na sua conta Grist. Navegue até o menu da conta no canto superior direito e selecione **Configurações do Perfil** para gerenciar ou criar a sua chave de API.\n\t★No campo **URL de Domínio**, digite a URL de domínio de sua instância Grist. Por exemplo, se você tiver o site da equipe, será \"https://team.getgist.com\".",
"Create Record": "Criar Registro",
"Search Record": "Registro de Busca",
"Update Record": "Atualizar Registro",
"Upload Attachment to Document": "Carregar Anexo para o Documento",
"Custom API Call": "Chamada de API personalizada",
"Creates a new record in specific table.": "Cria um novo registro na tabela específica.",
"Search record by matching criteria.": "Pesquisar registro por critérios correspondentes.",
"Updates an existing record in specific table.": "Atualiza um registro existente em uma tabela específica.",
"Uploads attachments to specific document.": "Carrega anexos para um documento específico.",
"Make a custom API call to a specific endpoint": "Faça uma chamada de API personalizada para um ponto de extremidade específico",
"Workspace": "Workspace",
"Document": "Documento",
"Table": "Classificações",
"Table Columns": "Colunas da Tabela",
"Column": "Coluna",
"Value": "Valor",
"Record ID": "ID do Registro",
"Attachment": "Anexo",
"Attachment Name": "Nome do anexo",
"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)",
"Enter the search text. The search operation is case sensitive, exact text match, and supports column-type Text only.": "Digite o texto de busca. A operação de busca é sensível a maiúsculas e minúsculas e suporta apenas texto de tipo coluna.",
"In case you want to change the name of the attachment.": "Caso queira alterar o nome do anexo.",
"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..",
"GET": "OBTER",
"POST": "POSTAR",
"PATCH": "COMPRAR",
"PUT": "COLOCAR",
"DELETE": "EXCLUIR",
"HEAD": "CABEÇA",
"New Record": "Novo Registro",
"Updated Record": "Registro atualizado",
"Triggers when a new record is added to the table.": "Dispara quando um novo registro é adicionado à tabela.",
"Triggers when a record is updated in the table.": "Dispara quando um registro é atualizado na tabela.",
"Readiness Column": "Coluna de Preparação",
"A toggle (boolean) column which is True when the record is ready. The trigger will only be activated when that record becomes ready.Please follow [guideline](https://support.getgrist.com/integrators/#readiness-column) to create readiness column in table.": "Uma coluna de alternância (booleana) que é Verdadeiro quando o registro estiver pronto. O acionamento só será ativado quando esse registro ficar pronto. alugar segue [guideline](https://support.getgrist.com/integrators/#readiness-column) para criar coluna de prontidão na tabela."
}

View File

@@ -0,0 +1,47 @@
{
"Grist": "Грист",
"open source spreadsheet": "таблица с открытым исходным кодом",
"API Key": "Ключ API",
"Domain URL": "URL домена",
"\n\tLog in to your Grist account. Navigate to the account menu at the top right, and select **Profile Settings** to manage or create your API Key.\n\tIn the **Domain URL** field, enter the domain URL of your Grist instance.For example,if you have team site it will be \"https://team.getgist.com\".": "\n\tLog in to your Grist account. Navigate to the account menu at the top right, and select **Profile Settings** to manage or create your API Key.\n\tIn the **Domain URL** field, enter the domain URL of your Grist instance.For example,if you have team site it will be \"https://team.getgist.com\".",
"Create Record": "Создать запись",
"Search Record": "Поиск записи",
"Update Record": "Обновить запись",
"Upload Attachment to Document": "Загрузить вложение в документ",
"Custom API Call": "Пользовательский вызов API",
"Creates a new record in specific table.": "Создает новую запись в указанной таблице.",
"Search record by matching criteria.": "Поиск записи по критериям.",
"Updates an existing record in specific table.": "Обновляет существующую запись в определенной таблице.",
"Uploads attachments to specific document.": "Загружает вложения в конкретный документ.",
"Make a custom API call to a specific endpoint": "Сделать пользовательский API вызов к определенной конечной точке",
"Workspace": "Рабочая область",
"Document": "Документ",
"Table": "Таблица",
"Table Columns": "Столбцы таблицы",
"Column": "Столбец",
"Value": "Значение",
"Record ID": "ID записи",
"Attachment": "Вложение",
"Attachment Name": "Название вложения",
"Method": "Метод",
"Headers": "Заголовки",
"Query Parameters": "Параметры запроса",
"Body": "Тело",
"No Error on Failure": "Нет ошибок при ошибке",
"Timeout (in seconds)": "Таймаут (в секундах)",
"Enter the search text. The search operation is case sensitive, exact text match, and supports column-type Text only.": "Введите текст поиска. Операция поиска чувствительна к регистру, точное совпадение текста и поддерживает только тип столбца.",
"In case you want to change the name of the attachment.": "В случае, если вы хотите изменить имя вложения.",
"Authorization headers are injected automatically from your connection.": "Заголовки авторизации включаются автоматически из вашего соединения.",
"GET": "ПОЛУЧИТЬ",
"POST": "ПОСТ",
"PATCH": "ПАТЧ",
"PUT": "ПОКУПИТЬ",
"DELETE": "УДАЛИТЬ",
"HEAD": "HEAD",
"New Record": "Новая запись",
"Updated Record": "Запись обновлена",
"Triggers when a new record is added to the table.": "Триггеры при добавлении новой записи в таблицу.",
"Triggers when a record is updated in the table.": "Триггеры при обновлении записи в таблице.",
"Readiness Column": "Столбец готовности",
"A toggle (boolean) column which is True when the record is ready. The trigger will only be activated when that record becomes ready.Please follow [guideline](https://support.getgrist.com/integrators/#readiness-column) to create readiness column in table.": "Столбец (boolean) - True когда запись готова. Триггер будет активирован, только когда эта запись будет готова. арендовать следуйте за [guideline](https://support.getgrist.com/integrators/#readiness-column), чтобы создать столбец готовности в таблице."
}

View File

@@ -0,0 +1,48 @@
{
"open source spreadsheet": "open source spreadsheet",
"API Key": "API Key",
"Domain URL": "Domain URL",
"\n\tLog in to your Grist account. Navigate to the account menu at the top right, and select **Profile Settings** to manage or create your API Key.\n\tIn the **Domain URL** field, enter the domain URL of your Grist instance.For example,if you have team site it will be \"https://team.getgist.com\".": "\n\tLog in to your Grist account. Navigate to the account menu at the top right, and select **Profile Settings** to manage or create your API Key.\n\tIn the **Domain URL** field, enter the domain URL of your Grist instance.For example,if you have team site it will be \"https://team.getgist.com\".",
"Create Record": "Create Record",
"Search Record": "Search Record",
"Update Record": "Update Record",
"Upload Attachment to Document": "Upload Attachment to Document",
"Custom API Call": "Custom API Call",
"Creates a new record in specific table.": "Creates a new record in specific table.",
"Search record by matching criteria.": "Search record by matching criteria.",
"Updates an existing record in specific table.": "Updates an existing record in specific table.",
"Uploads attachments to specific document.": "Uploads attachments to specific document.",
"Make a custom API call to a specific endpoint": "Make a custom API call to a specific endpoint",
"Workspace": "Workspace",
"Document": "Document",
"Table": "Table",
"Table Columns": "Table Columns",
"Column": "Column",
"Value": "Value",
"Record ID": "Record ID",
"Attachment": "Attachment",
"Attachment Name": "Attachment Name",
"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)",
"Enter the search text. The search operation is case sensitive, exact text match, and supports column-type Text only.": "Enter the search text. The search operation is case sensitive, exact text match, and supports column-type Text only.",
"In case you want to change the name of the attachment.": "In case you want to change the name of the attachment.",
"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..",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD",
"New Record": "New Record",
"Updated Record": "Updated Record",
"Triggers when a new record is added to the table.": "Triggers when a new record is added to the table.",
"Triggers when a record is updated in the table.": "Triggers when a record is updated in the table.",
"Readiness Column": "Readiness Column",
"A toggle (boolean) column which is True when the record is ready. The trigger will only be activated when that record becomes ready.Please follow [guideline](https://support.getgrist.com/integrators/#readiness-column) to create readiness column in table.": "A toggle (boolean) column which is True when the record is ready. The trigger will only be activated when that record becomes ready.Please follow [guideline](https://support.getgrist.com/integrators/#readiness-column) to create readiness column in table."
}

View File

@@ -0,0 +1,47 @@
{
"Grist": "Grist",
"open source spreadsheet": "open source spreadsheet",
"API Key": "API Key",
"Domain URL": "Domain URL",
"\n\tLog in to your Grist account. Navigate to the account menu at the top right, and select **Profile Settings** to manage or create your API Key.\n\tIn the **Domain URL** field, enter the domain URL of your Grist instance.For example,if you have team site it will be \"https://team.getgist.com\".": "\n\tLog in to your Grist account. Navigate to the account menu at the top right, and select **Profile Settings** to manage or create your API Key.\n\tIn the **Domain URL** field, enter the domain URL of your Grist instance.For example,if you have team site it will be \"https://team.getgist.com\".",
"Create Record": "Create Record",
"Search Record": "Search Record",
"Update Record": "Update Record",
"Upload Attachment to Document": "Upload Attachment to Document",
"Custom API Call": "Custom API Call",
"Creates a new record in specific table.": "Creates a new record in specific table.",
"Search record by matching criteria.": "Search record by matching criteria.",
"Updates an existing record in specific table.": "Updates an existing record in specific table.",
"Uploads attachments to specific document.": "Uploads attachments to specific document.",
"Make a custom API call to a specific endpoint": "Make a custom API call to a specific endpoint",
"Workspace": "Workspace",
"Document": "Document",
"Table": "Table",
"Table Columns": "Table Columns",
"Column": "Column",
"Value": "Value",
"Record ID": "Record ID",
"Attachment": "Attachment",
"Attachment Name": "Attachment Name",
"Method": "Method",
"Headers": "Headers",
"Query Parameters": "Query Parameters",
"Body": "Body",
"No Error on Failure": "No Error on Failure",
"Timeout (in seconds)": "Timeout (in seconds)",
"Enter the search text. The search operation is case sensitive, exact text match, and supports column-type Text only.": "Enter the search text. The search operation is case sensitive, exact text match, and supports column-type Text only.",
"In case you want to change the name of the attachment.": "In case you want to change the name of the attachment.",
"Authorization headers are injected automatically from your connection.": "Authorization headers are injected automatically from your connection.",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD",
"New Record": "New Record",
"Updated Record": "Updated Record",
"Triggers when a new record is added to the table.": "Triggers when a new record is added to the table.",
"Triggers when a record is updated in the table.": "Triggers when a record is updated in the table.",
"Readiness Column": "Readiness Column",
"A toggle (boolean) column which is True when the record is ready. The trigger will only be activated when that record becomes ready.Please follow [guideline](https://support.getgrist.com/integrators/#readiness-column) to create readiness column in table.": "A toggle (boolean) column which is True when the record is ready. The trigger will only be activated when that record becomes ready.Please follow [guideline](https://support.getgrist.com/integrators/#readiness-column) to create readiness column in table."
}

View File

@@ -0,0 +1,48 @@
{
"open source spreadsheet": "open source spreadsheet",
"API Key": "API 密钥",
"Domain URL": "Domain URL",
"\n\tLog in to your Grist account. Navigate to the account menu at the top right, and select **Profile Settings** to manage or create your API Key.\n\tIn the **Domain URL** field, enter the domain URL of your Grist instance.For example,if you have team site it will be \"https://team.getgist.com\".": "\n\tLog in to your Grist account. Navigate to the account menu at the top right, and select **Profile Settings** to manage or create your API Key.\n\tIn the **Domain URL** field, enter the domain URL of your Grist instance.For example,if you have team site it will be \"https://team.getgist.com\".",
"Create Record": "Create Record",
"Search Record": "Search Record",
"Update Record": "Update Record",
"Upload Attachment to Document": "Upload Attachment to Document",
"Custom API Call": "自定义 API 呼叫",
"Creates a new record in specific table.": "Creates a new record in specific table.",
"Search record by matching criteria.": "Search record by matching criteria.",
"Updates an existing record in specific table.": "Updates an existing record in specific table.",
"Uploads attachments to specific document.": "Uploads attachments to specific document.",
"Make a custom API call to a specific endpoint": "将一个自定义 API 调用到一个特定的终点",
"Workspace": "Workspace",
"Document": "Document",
"Table": "表",
"Table Columns": "Table Columns",
"Column": "Column",
"Value": "值",
"Record ID": "Record ID",
"Attachment": "Attachment",
"Attachment Name": "Attachment Name",
"Method": "方法",
"Headers": "信头",
"Query Parameters": "查询参数",
"Body": "正文内容",
"Response is Binary ?": "Response is Binary ?",
"No Error on Failure": "失败时没有错误",
"Timeout (in seconds)": "超时(秒)",
"Enter the search text. The search operation is case sensitive, exact text match, and supports column-type Text only.": "Enter the search text. The search operation is case sensitive, exact text match, and supports column-type Text only.",
"In case you want to change the name of the attachment.": "In case you want to change the name of the attachment.",
"Authorization headers are injected automatically from your connection.": "授权头自动从您的连接中注入。",
"Enable for files like PDFs, images, etc..": "Enable for files like PDFs, images, etc..",
"GET": "获取",
"POST": "帖子",
"PATCH": "PATCH",
"PUT": "弹出",
"DELETE": "删除",
"HEAD": "黑色",
"New Record": "New Record",
"Updated Record": "Updated Record",
"Triggers when a new record is added to the table.": "Triggers when a new record is added to the table.",
"Triggers when a record is updated in the table.": "Triggers when a record is updated in the table.",
"Readiness Column": "Readiness Column",
"A toggle (boolean) column which is True when the record is ready. The trigger will only be activated when that record becomes ready.Please follow [guideline](https://support.getgrist.com/integrators/#readiness-column) to create readiness column in table.": "A toggle (boolean) column which is True when the record is ready. The trigger will only be activated when that record becomes ready.Please follow [guideline](https://support.getgrist.com/integrators/#readiness-column) to create readiness column in table."
}

View File

@@ -0,0 +1,88 @@
import {
createPiece,
PieceAuth,
PiecePropValueSchema,
Property,
} from '@activepieces/pieces-framework';
import { gristCreateRecordAction } from './lib/actions/create-record.action';
import { gristUpdateRecordAction } from './lib/actions/update-record.action';
import { gristUploadAttachmentsToDocumnetAction } from './lib/actions/upload-attachments-to-document.action';
import { createCustomApiCallAction } from '@activepieces/pieces-common';
import { gristNewRecordTrigger } from './lib/triggers/new-record.trigger';
import { gristUpdatedRecordTrigger } from './lib/triggers/updated-record.trigger';
import { gristSearchRecordAction } from './lib/actions/search-record.action';
import { GristAPIClient } from './lib/common/helpers';
import { PieceCategory } from '@activepieces/shared';
export const gristAuth = PieceAuth.CustomAuth({
required: true,
description: `
Log in to your Grist account. Navigate to the account menu at the top right, and select **Profile Settings** to manage or create your API Key.
In the **Domain URL** field, enter the domain URL of your Grist instance.For example,if you have team site it will be "https://team.getgist.com".`,
props: {
apiKey: PieceAuth.SecretText({
displayName: 'API Key',
required: true,
}),
domain: Property.ShortText({
displayName: 'Domain URL',
required: true,
defaultValue: 'https://docs.getgrist.com',
}),
},
validate: async ({ auth }) => {
try {
const authValue = auth ;
const client = new GristAPIClient({
domainUrl: authValue.domain,
apiKey: authValue.apiKey,
});
// https://support.getgrist.com/api/#tag/orgs
await client.listOrgs();
return {
valid: true,
};
} catch (error) {
return {
valid: false,
error: 'Please provide valid API key and domain URL.',
};
}
},
});
export const grist = createPiece({
displayName: 'Grist',
auth: gristAuth,
minimumSupportedRelease: '0.30.0',
logoUrl: 'https://cdn.activepieces.com/pieces/grist.png',
description: 'open source spreadsheet',
categories: [PieceCategory.PRODUCTIVITY],
authors: ['kishanprmr'],
actions: [
gristCreateRecordAction,
gristSearchRecordAction,
gristUpdateRecordAction,
gristUploadAttachmentsToDocumnetAction,
createCustomApiCallAction({
auth: gristAuth,
baseUrl: (auth) => {
if (!auth) {
return '';
}
return `${
auth.props.domain
}/api/`;
},
authMapping: async (auth) => ({
Authorization: `Bearer ${
auth.props.apiKey
}`,
}),
}),
],
triggers: [gristNewRecordTrigger, gristUpdatedRecordTrigger],
});

View File

@@ -0,0 +1,50 @@
import { gristAuth } from '../..';
import { createAction } from '@activepieces/pieces-framework';
import { commonProps } from '../common/props';
import { GristAPIClient, transformTableColumnValues } from '../common/helpers';
export const gristCreateRecordAction = createAction({
auth: gristAuth,
name: 'grist-create-record',
displayName: 'Create Record',
description: 'Creates a new record in specific table.',
props: {
workspace_id: commonProps.workspace_id,
document_id: commonProps.document_id,
table_id: commonProps.table_id,
table_columns: commonProps.table_columns,
},
async run(context) {
const documentId = context.propsValue.document_id;
const tableId = context.propsValue.table_id;
const tableColumnValues = context.propsValue.table_columns;
const client = new GristAPIClient({
domainUrl: context.auth.props.domain,
apiKey: context.auth.props.apiKey,
});
const tableColumnSchema = await client.listTableColumns(
documentId as string,
tableId as string
);
const transformedColumnValues = transformTableColumnValues({
tableColumnSchema,
tableColumnValues,
});
const createRecordResponse = await client.addRecordsToTable(
documentId,
tableId,
{
records: [{ fields: transformedColumnValues }],
}
);
return {
id: createRecordResponse.records[0].id,
fields: transformedColumnValues,
};
},
});

View File

@@ -0,0 +1,87 @@
import { gristAuth } from '../..';
import {
createAction,
DropdownOption,
PiecePropValueSchema,
Property,
} from '@activepieces/pieces-framework';
import { commonProps } from '../common/props';
import { GristAPIClient } from '../common/helpers';
import { HttpMethod } from '@activepieces/pieces-common';
export const gristSearchRecordAction = createAction({
auth: gristAuth,
name: 'grist-search-record',
displayName: 'Search Record',
description: 'Search record by matching criteria.',
props: {
workspace_id: commonProps.workspace_id,
document_id: commonProps.document_id,
table_id: commonProps.table_id,
column: Property.Dropdown({
displayName: 'Column',
auth: gristAuth,
refreshers: ['document_id', 'table_id'],
required: true,
options: async ({ auth, document_id, table_id }) => {
if (!auth || !document_id || !table_id) {
return {
disabled: true,
placeholder: 'Please connect account and select document.',
options: [],
};
}
const authValue = auth.props;
const client = new GristAPIClient({
domainUrl: authValue.domain,
apiKey: authValue.apiKey,
});
const response = await client.listTableColumns(
document_id as string,
table_id as string
);
const options: DropdownOption<string>[] = [];
for (const column of response.columns) {
options.push({ value: column.id, label: column.fields.label });
}
return {
disabled: false,
options,
};
},
}),
value: Property.ShortText({
displayName: 'Value',
required: true,
description:
'Enter the search text. The search operation is case sensitive, exact text match, and supports column-type Text only.',
}),
},
async run(context) {
const documentId = context.propsValue.document_id;
const tableId = context.propsValue.table_id;
const columnName = context.propsValue.column;
const columnValue = context.propsValue.value;
const client = new GristAPIClient({
domainUrl: context.auth.props.domain,
apiKey: context.auth.props.apiKey,
});
const encodedQuery = encodeURIComponent(
JSON.stringify({
[columnName]: [columnValue],
})
);
return await client.makeRequest(
HttpMethod.GET,
`/docs/${documentId}/tables/${tableId}/records?filter=${encodedQuery}`
);
},
});

View File

@@ -0,0 +1,55 @@
import { gristAuth } from '../..';
import { createAction, Property } from '@activepieces/pieces-framework';
import { commonProps } from '../common/props';
import { GristAPIClient, transformTableColumnValues } from '../common/helpers';
export const gristUpdateRecordAction = createAction({
auth: gristAuth,
name: 'grist-update-record',
displayName: 'Update Record',
description: 'Updates an existing record in specific table.',
props: {
workspace_id: commonProps.workspace_id,
document_id: commonProps.document_id,
table_id: commonProps.table_id,
record_id: Property.Number({
displayName: 'Record ID',
required: true,
}),
table_columns: commonProps.table_columns,
},
async run(context) {
const documentId = context.propsValue.document_id;
const tableId = context.propsValue.table_id;
const recordId = context.propsValue.record_id;
const tableColumnValues = context.propsValue.table_columns;
const client = new GristAPIClient({
domainUrl: context.auth.props.domain,
apiKey: context.auth.props.apiKey,
});
const tableColumnSchema = await client.listTableColumns(
documentId as string,
tableId as string
);
const transformedColumnValues = transformTableColumnValues({
tableColumnSchema,
tableColumnValues,
});
const updateRecordResponse = await client.updateRcordsInTable(
documentId,
tableId,
{
records: [{ id: recordId, fields: transformedColumnValues }],
}
);
return {
id: recordId,
fields: transformedColumnValues,
};
},
});

View File

@@ -0,0 +1,70 @@
import { gristAuth } from '../..';
import { createAction, Property } from '@activepieces/pieces-framework';
import { commonProps } from '../common/props';
import FormData from 'form-data';
import {
AuthenticationType,
httpClient,
HttpMethod,
} from '@activepieces/pieces-common';
import { GristAPIClient } from '../common/helpers';
export const gristUploadAttachmentsToDocumnetAction = createAction({
auth: gristAuth,
name: 'grist-upload-attachments-to-document',
displayName: 'Upload Attachment to Document',
description: 'Uploads attachments to specific document.',
props: {
workspace_id: commonProps.workspace_id,
document_id: commonProps.document_id,
attachment: Property.File({
displayName: 'Attachment',
required: true,
}),
attachment_name: Property.ShortText({
displayName: 'Attachment Name',
description: 'In case you want to change the name of the attachment.',
required: false,
}),
},
async run(context) {
const documentId = context.propsValue.document_id;
const attachment = context.propsValue.attachment;
const attachmentName = context.propsValue.attachment_name;
const client = new GristAPIClient({
domainUrl: context.auth.props.domain,
apiKey: context.auth.props.apiKey,
});
const formData = new FormData();
formData.append(
'upload',
Buffer.from(attachment.base64, 'base64'),
attachmentName || attachment.filename
);
const response = await httpClient.sendRequest<Array<number>>({
method: HttpMethod.POST,
url: context.auth.props.domain + '/api' + `/docs/${documentId}/attachments`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.props.apiKey,
},
headers: { ...formData.getHeaders() },
body: formData,
});
const attachmentId = response.body[0];
const attachmentMetadata = await client.getDocumentAttachmentMetadata(
documentId,
attachmentId
);
return {
id: attachmentId,
fields: attachmentMetadata,
};
},
});

View File

@@ -0,0 +1,219 @@
import { DynamicPropsValue } from '@activepieces/pieces-framework';
import {
GristAPIClientOptions,
GristCreateRecordsRequest,
GristCreateRecordsResponse,
GristCreateWebhookRequest,
GristCreateWebhookResponse,
GristListRecordsResponse,
GristOrgResponse,
GristTableColumnsResponse,
GristTableResponse,
GristUpdateRecordsRequest,
GristWorkspaceResponse,
} from './types';
import {
AuthenticationType,
httpClient,
HttpHeaders,
HttpMessageBody,
HttpMethod,
HttpRequest,
QueryParams,
} from '@activepieces/pieces-common';
type Query = Record<string, string | number | string[] | undefined>;
export class GristAPIClient {
#domaiUrl: string;
#apiKey: string;
constructor(options: GristAPIClientOptions) {
this.#domaiUrl = options.domainUrl;
this.#apiKey = options.apiKey;
}
async makeRequest<T extends HttpMessageBody>(
method: HttpMethod,
resourceUri: string,
headers?: HttpHeaders,
query?: Query,
body: any | undefined = undefined
): Promise<T> {
const baseUrl = this.#domaiUrl.replace(/\/$/, '');
const params: QueryParams = {};
if (query) {
for (const [key, value] of Object.entries(query)) {
if (value !== null && value !== undefined) {
params[key] = String(value);
}
}
}
const request: HttpRequest = {
method: method,
url: baseUrl + '/api' + resourceUri,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: this.#apiKey,
},
headers,
queryParams: params,
body: body,
};
const response = await httpClient.sendRequest<T>(request);
return response.body;
}
async listOrgs() {
return await this.makeRequest<GristOrgResponse[]>(
HttpMethod.GET,
`/orgs`
);
}
async listWorkspaces(orgId: string) {
return await this.makeRequest<GristWorkspaceResponse[]>(
HttpMethod.GET,
`/orgs/${orgId}/workspaces`
);
}
async getWorkspace(workspaceId: number) {
return await this.makeRequest<GristWorkspaceResponse>(
HttpMethod.GET,
`/workspaces/${workspaceId}`
);
}
async listDocumentTables(docId: string) {
return await this.makeRequest<{ tables: GristTableResponse[] }>(
HttpMethod.GET,
`/docs/${docId}/tables`
);
}
async listTableColumns(docId: string, tableId: string) {
return await this.makeRequest<{ columns: GristTableColumnsResponse[] }>(
HttpMethod.GET,
`/docs/${docId}/tables/${tableId}/columns`
);
}
async addRecordsToTable(
docId: string,
tableId: string,
request: GristCreateRecordsRequest
) {
return await this.makeRequest<GristCreateRecordsResponse>(
HttpMethod.POST,
`/docs/${docId}/tables/${tableId}/records`,
undefined,
{},
request
);
}
async updateRcordsInTable(
docId: string,
tableId: string,
request: GristUpdateRecordsRequest
) {
return await this.makeRequest<GristCreateRecordsResponse>(
HttpMethod.PATCH,
`/docs/${docId}/tables/${tableId}/records`,
undefined,
{},
request
);
}
async listRecordsFromTable(docId: string, tableId: string, query: Query) {
return await this.makeRequest<GristListRecordsResponse>(
HttpMethod.GET,
`/docs/${docId}/tables/${tableId}/records`,
undefined,
query
);
}
async listDocumentAttachments(docId: string, query: Query) {
return await this.makeRequest<GristListRecordsResponse>(
HttpMethod.GET,
`/docs/${docId}/attachments`,
{},
query
);
}
async getDocumentAttachmentMetadata(docId: string, attachmentId: number) {
return await this.makeRequest<GristListRecordsResponse>(
HttpMethod.GET,
`/docs/${docId}/attachments/${attachmentId}`
);
}
async createDocumentWebhook(
docId: string,
request: GristCreateWebhookRequest
) {
return await this.makeRequest<GristCreateWebhookResponse>(
HttpMethod.POST,
`/docs/${docId}/webhooks`,
undefined,
{},
request
);
}
async deleteDocumentWebhook(docId: string, webhookId: number) {
return await this.makeRequest(
HttpMethod.DELETE,
`/docs/${docId}/webhooks/${webhookId}`
);
}
}
export function transformTableColumnValues({
tableColumnSchema,
tableColumnValues,
}: {
tableColumnSchema: { columns: GristTableColumnsResponse[] };
tableColumnValues: DynamicPropsValue;
}): DynamicPropsValue {
const fields: DynamicPropsValue = {};
for (const column of tableColumnSchema.columns) {
const columnId = column.id;
const columnType = column.fields.type;
const columnValue = tableColumnValues[columnId];
if (columnValue !== undefined && columnValue !== '') {
if (columnType === 'Attachments') {
const attachmentsArray = Array.isArray(columnValue)
? columnValue
: JSON.parse(columnValue);
if (Array.isArray(attachmentsArray) && attachmentsArray.length > 0) {
fields[columnId] = ['L', ...attachmentsArray.map(Number)];
}
} else if (columnType === 'ChoiceList') {
if (Array.isArray(columnValue) && columnValue.length > 0) {
fields[columnId] = ['L', ...columnValue];
}
} else if (columnType === 'Int' || columnType === 'Numeric') {
fields[columnId] = Number(columnValue);
} else if (columnType.startsWith('RefList')) {
const refListArray = Array.isArray(columnValue)
? columnValue
: JSON.parse(columnValue);
if (Array.isArray(refListArray) && refListArray.length > 0) {
fields[columnId] = ['L', ...refListArray.map(Number)];
}
} else if (columnType.startsWith('Ref')) {
fields[columnId] = Number(columnValue);
} else {
fields[columnId] = columnValue;
}
}
}
return fields;
}

View File

@@ -0,0 +1,284 @@
import {
DropdownOption,
DynamicPropsValue,
PiecePropValueSchema,
Property,
} from '@activepieces/pieces-framework';
import { gristAuth } from '../..';
import { GristAPIClient } from './helpers';
export const commonProps = {
workspace_id: Property.Dropdown({
displayName: 'Workspace',
refreshers: [],
required: true,
auth: gristAuth,
options: async ({ auth }) => {
if (!auth) {
return {
disabled: true,
placeholder: 'Please connect account first.',
options: [],
};
}
const authValue = auth.props;
const client = new GristAPIClient({
domainUrl: authValue.domain,
apiKey: authValue.apiKey,
});
const response = await client.listWorkspaces('current');
const options: DropdownOption<number>[] = [];
for (const workspace of response) {
options.push({ label: workspace.name, value: workspace.id });
}
return {
disabled: false,
options,
};
},
}),
document_id: Property.Dropdown({
displayName: 'Document',
refreshers: ['workspace_id'],
required: true,
auth: gristAuth,
options: async ({ auth, workspace_id }) => {
if (!auth || !workspace_id) {
return {
disabled: true,
placeholder: 'Please connect account and select workspace.',
options: [],
};
}
const authValue = auth.props;
const client = new GristAPIClient({
domainUrl: authValue.domain,
apiKey: authValue.apiKey,
});
const response = await client.getWorkspace(
workspace_id as unknown as number
);
const options: DropdownOption<string>[] = [];
for (const document of response.docs) {
options.push({ label: document.name, value: document.id });
}
return {
disabled: false,
options,
};
},
}),
table_id: Property.Dropdown({
displayName: 'Table',
refreshers: ['document_id'],
required: true,
auth: gristAuth,
options: async ({ auth, document_id }) => {
if (!auth || !document_id) {
return {
disabled: true,
placeholder: 'Please connect account and select document.',
options: [],
};
}
const authValue = auth.props;
const client = new GristAPIClient({
domainUrl: authValue.domain,
apiKey: authValue.apiKey,
});
const response = await client.listDocumentTables(
document_id as unknown as string
);
const options: DropdownOption<string>[] = [];
for (const table of response.tables) {
options.push({ label: table.id, value: table.id });
}
return {
disabled: false,
options,
};
},
}),
table_columns: Property.DynamicProperties({
displayName: 'Table Columns',
refreshers: ['document_id', 'table_id'],
required: true,
auth: gristAuth,
props: async ({ auth, document_id, table_id }) => {
if (!auth) return {};
if (!document_id) return {};
if (!table_id) return {};
const fields: DynamicPropsValue = {};
const authValue = auth.props;
const client = new GristAPIClient({
domainUrl: authValue.domain,
apiKey: authValue.apiKey,
});
const response = await client.listTableColumns(
document_id as unknown as string,
table_id as unknown as string
);
for (const column of response.columns) {
if (!column.fields.isFormula) {
switch (column.fields.type) {
case 'Any':
fields[column.id] = Property.ShortText({
displayName: column.fields.label || column.id,
required: false,
});
break;
case 'Attachments':
fields[column.id] = Property.Array({
displayName: column.fields.label || column.id,
description: `Use the **Upload Attachments to Document** action and provide the attachment ID from the response.`,
required: false,
});
break;
case 'Bool':
fields[column.id] = Property.Checkbox({
displayName: column.fields.label || column.id,
required: false,
});
break;
case 'Choice':
case 'ChoiceList': {
let options = [];
try {
const optionsObject = JSON.parse(column.fields.widgetOptions);
options = optionsObject['choices'] as any[];
} catch (error) {
options = [];
}
const dropdownConfig = {
displayName: column.fields.label || column.id,
required: false,
options: {
disabled: false,
options: options.map((choice) => {
return {
label: choice,
value: choice,
};
}),
},
};
fields[column.id] =
column.fields.type === 'Choice'
? Property.StaticDropdown(dropdownConfig)
: Property.StaticMultiSelectDropdown(dropdownConfig);
break;
}
case 'Date':
fields[column.id] = Property.DateTime({
displayName: column.fields.label || column.id,
required: false,
});
break;
case 'Int':
case 'Numeric':
fields[column.id] = Property.Number({
displayName: column.fields.label || column.id,
required: false,
});
break;
case 'Text':
fields[column.id] = Property.LongText({
displayName: column.fields.label || column.id,
required: false,
});
break;
default:
if (column.fields.type.startsWith('DateTime')) {
fields[column.id] = Property.DateTime({
displayName: column.fields.label || column.id,
required: false,
});
} else if (column.fields.type.startsWith('RefList')) {
const refTable = column.fields.type.split(':')[1];
fields[column.id] = Property.Array({
displayName: column.fields.label || column.id,
description: refTable
? `Please provide the row ID from the reference table ${refTable}.`
: '',
required: false,
});
} else if (column.fields.type.startsWith('Ref')) {
const refTable = column.fields.type.split(':')[1];
fields[column.id] = Property.Number({
displayName: column.fields.label || column.id,
description: refTable
? `Please provide the row ID from the reference table ${refTable}.`
: '',
required: false,
});
}
break;
}
}
}
return fields;
},
}),
readiness_column: Property.Dropdown({
displayName: 'Readiness Column',
description: `A toggle (boolean) column which is True when the record is ready. The trigger will only be activated when that record becomes ready.Please follow [guideline](https://support.getgrist.com/integrators/#readiness-column) to create readiness column in table.`,
refreshers: ['document_id', 'table_id'],
required: false,
auth: gristAuth,
options: async ({ auth, document_id, table_id }) => {
if (!auth || !document_id || !table_id) {
return {
disabled: true,
placeholder: 'Please connect account and select document.',
options: [],
};
}
const authValue = auth.props;
const client = new GristAPIClient({
domainUrl: authValue.domain,
apiKey: authValue.apiKey,
});
const response = await client.listTableColumns(
document_id as string,
table_id as string
);
const options: DropdownOption<string>[] = [];
for (const column of response.columns) {
if (column.fields.type === 'Bool') {
options.push({ value: column.id, label: column.fields.label });
}
}
return {
disabled: false,
options,
};
},
}),
};

View File

@@ -0,0 +1,108 @@
export type GristAPIClientOptions = {
domainUrl: string;
apiKey: string;
};
export type GristOrganizationResponse = {
name: string;
id: string;
createdAt: string;
updatedAt: string;
};
export type GristDocumentResponse = {
name: string;
id: string;
createdAt: string;
updatedAt: string;
};
export type GristWorkspaceResponse = {
name: string;
id: number;
createdAt: string;
updatedAt: string;
docs: Array<GristDocumentResponse>;
};
export type GristTableResponse = {
id: string;
};
export type GristTableColumnsResponse = {
id: string;
fields: {
type:
| 'Any'
| 'Text'
| 'Numeric'
| 'Int'
| 'Bool'
| 'Date'
| `DateTime:${string}`
| `Ref:${string}`
| `RefList:${string}`
| 'Choice'
| 'ChoiceList'
| 'Attachments';
label: string;
widgetOptions: string;
isFormula: boolean;
};
};
export type GristTableRecordResponse = {
id: number;
fields: Record<string, any>;
};
export type GristCreateRecordsRequest = {
records: Array<Omit<GristTableRecordResponse, 'id'>>;
};
export type GristUpdateRecordsRequest = {
records: Array<GristTableRecordResponse>;
};
export type GristCreateRecordsResponse = {
records: Array<{ id: number }>;
};
export type GristListRecordsResponse = {
records: Array<GristTableRecordResponse>;
};
export type GristCreateWebhookRequest = {
webhooks: Array<{
fields: {
name?: string;
memo?: string;
url: string;
enabled: boolean;
eventTypes: Array<string>;
isReadyColumn?: string;
tableId: string;
};
}>;
};
export type GristCreateWebhookResponse = {
webhooks: Array<{ id: number }>;
};
export type GristWebhookPayload = Record<string, any>;
export type GristOrgResponse = {
id: number
name: string
domain: string
owner: {
id: number
name: string
picture: any
}
access: string
createdAt: string
updatedAt: string
}

View File

@@ -0,0 +1,85 @@
import { gristAuth } from '../..';
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
import { commonProps } from '../common/props';
import { GristAPIClient } from '../common/helpers';
import { GristWebhookPayload } from '../common/types';
export const gristNewRecordTrigger = createTrigger({
auth: gristAuth,
name: 'grist-new-record',
displayName: 'New Record',
description: 'Triggers when a new record is added to the table.',
props: {
workspace_id: commonProps.workspace_id,
document_id: commonProps.document_id,
table_id: commonProps.table_id,
readiness_column: commonProps.readiness_column,
},
type: TriggerStrategy.WEBHOOK,
sampleData: {},
async onEnable(context) {
const documentId = context.propsValue.document_id;
const tableId = context.propsValue.table_id;
const readinessColumn = context.propsValue.readiness_column;
const client = new GristAPIClient({
domainUrl: context.auth.props.domain,
apiKey: context.auth.props.apiKey,
});
const response = await client.createDocumentWebhook(documentId, {
webhooks: [
{
fields: {
url: context.webhookUrl,
enabled: true,
eventTypes: ['add'],
tableId,
isReadyColumn: readinessColumn,
},
},
],
});
await context.store.put<number>(
'grist-new-record',
response.webhooks[0].id
);
},
async onDisable(context) {
const documentId = context.propsValue.document_id;
const webhookId = await context.store.get<number>('grist-new-record');
if (webhookId != null) {
const client = new GristAPIClient({
domainUrl: context.auth.props.domain,
apiKey: context.auth.props.apiKey,
});
await client.deleteDocumentWebhook(documentId, webhookId);
}
},
async run(context) {
const payload = context.payload.body as GristWebhookPayload[];
return payload;
},
async test(context) {
const documentId = context.propsValue.document_id;
const tableId = context.propsValue.table_id;
const client = new GristAPIClient({
domainUrl: context.auth.props.domain,
apiKey: context.auth.props.apiKey,
});
const response = await client.listRecordsFromTable(documentId, tableId, {
limit: '10',
});
return response.records.map((record) => {
return {
id: record.id,
...record.fields,
};
});
},
});

View File

@@ -0,0 +1,85 @@
import { gristAuth } from '../..';
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
import { commonProps } from '../common/props';
import { GristAPIClient } from '../common/helpers';
import { GristWebhookPayload } from '../common/types';
export const gristUpdatedRecordTrigger = createTrigger({
auth: gristAuth,
name: 'grist-updated-record',
displayName: 'Updated Record',
description: 'Triggers when a record is updated in the table.',
props: {
workspace_id: commonProps.workspace_id,
document_id: commonProps.document_id,
table_id: commonProps.table_id,
readiness_column: commonProps.readiness_column,
},
type: TriggerStrategy.WEBHOOK,
sampleData: {},
async onEnable(context) {
const documentId = context.propsValue.document_id;
const tableId = context.propsValue.table_id;
const readinessColumn = context.propsValue.readiness_column;
const client = new GristAPIClient({
domainUrl: context.auth.props.domain,
apiKey: context.auth.props.apiKey,
});
const response = await client.createDocumentWebhook(documentId, {
webhooks: [
{
fields: {
url: context.webhookUrl,
enabled: true,
eventTypes: ['update'],
tableId,
isReadyColumn: readinessColumn,
},
},
],
});
await context.store.put<number>(
'grist-updated-record',
response.webhooks[0].id
);
},
async onDisable(context) {
const documentId = context.propsValue.document_id;
const webhookId = await context.store.get<number>('grist-updated-record');
if (webhookId != null) {
const client = new GristAPIClient({
domainUrl: context.auth.props.domain,
apiKey: context.auth.props.apiKey,
});
await client.deleteDocumentWebhook(documentId, webhookId);
}
},
async run(context) {
const payload = context.payload.body as GristWebhookPayload[];
return payload;
},
async test(context) {
const documentId = context.propsValue.document_id;
const tableId = context.propsValue.table_id;
const client = new GristAPIClient({
domainUrl: context.auth.props.domain,
apiKey: context.auth.props.apiKey,
});
const response = await client.listRecordsFromTable(documentId, tableId, {
limit: '10',
});
return response.records.map((record) => {
return {
id: record.id,
...record.fields,
};
});
},
});