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:
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"extends": [
|
||||
"../../../../.eslintrc.base.json"
|
||||
],
|
||||
"ignorePatterns": [
|
||||
"!**/*"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"*.ts",
|
||||
"*.tsx",
|
||||
"*.js",
|
||||
"*.jsx"
|
||||
],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"*.ts",
|
||||
"*.tsx"
|
||||
],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"*.js",
|
||||
"*.jsx"
|
||||
],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# pieces-drupal
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Building
|
||||
|
||||
Run `nx build pieces-drupal` to build the library.
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "@activepieces/piece-drupal",
|
||||
"version": "1.0.3",
|
||||
"type": "commonjs",
|
||||
"main": "./src/index.js",
|
||||
"types": "./src/index.d.ts",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@activepieces/shared": "*",
|
||||
"@activepieces/pieces-framework": "*",
|
||||
"@activepieces/pieces-common": "*"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"name": "pieces-drupal",
|
||||
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "packages/pieces/community/drupal/src",
|
||||
"projectType": "library",
|
||||
"release": {
|
||||
"version": {
|
||||
"manifestRootsToUpdate": [
|
||||
"dist/{projectRoot}"
|
||||
],
|
||||
"currentVersionResolver": "git-tag",
|
||||
"fallbackCurrentVersionResolver": "disk"
|
||||
}
|
||||
},
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nx/js:tsc",
|
||||
"outputs": [
|
||||
"{options.outputPath}"
|
||||
],
|
||||
"options": {
|
||||
"outputPath": "dist/packages/pieces/community/drupal",
|
||||
"tsConfig": "packages/pieces/community/drupal/tsconfig.lib.json",
|
||||
"packageJson": "packages/pieces/community/drupal/package.json",
|
||||
"main": "packages/pieces/community/drupal/src/index.ts",
|
||||
"assets": [
|
||||
"packages/pieces/community/drupal/*.md",
|
||||
{
|
||||
"input": "packages/pieces/community/drupal/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/drupal",
|
||||
"command": "bun install --no-save --silent"
|
||||
},
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"Website URL": "Website-URL",
|
||||
"Username": "Benutzername",
|
||||
"Password": "Kennwort",
|
||||
"URL of your Drupal site": "URL Ihrer Drupal-Seite",
|
||||
"\n**Using Drupal's JSON:API**\n\nYour Drupal site comes with JSON:API built-in. Authentication to access relevant parts requires the HTTP Basic Authentication module, which is also part of your Drupal site. Just ensure both are enabled and configure user authentication:\n\n1. Enable the JSON:API and the HTTP Basic Authentication modules\n2. Create a user account and give it the permissions you want Activepieces to have\n3. Use that account's credentials for authentication\n\nProvide the website URL in the format htt": "\n**Benutze Drupals JSON:API**\n\nDeine Drupal-Seite wird mit JSON:API eingebaut. Die Authentifizierung für den Zugriff auf relevante Teile erfordert das HTTP Basic Authentication Modul, das auch Teil Ihrer Drupal Site ist. Stellen Sie sicher, dass beide aktiviert sind und konfigurieren Sie Benutzerauthentifizierung:\n\n1. Aktivieren Sie JSON:API und die HTTP-Basic Authentifizierungsmodule\n2. Erstellen Sie ein Benutzerkonto und geben Sie ihm die Berechtigungen, die Activepieces\nhaben sollen. Benutzen Sie die Zugangsdaten für die Authentifizierung\n\nGeben Sie die URL der Webseite im Format https://www an. xample.com.\n\nFür zusätzliche Funktionalität kannst du das [Drupal Orchestration](https://www.drupal.org/project/orchestration) Modul verwenden.\n",
|
||||
"Call Service": "Anruf-Service",
|
||||
"Create Entity": "Entität erstellen",
|
||||
"List Entities": "Listen Entitäten",
|
||||
"Get Entity": "Get Entity",
|
||||
"Update Entity": "Entität aktualisieren",
|
||||
"Delete Entity": "Entität löschen",
|
||||
"Call a service on the Drupal site": "Einen Dienst auf der Drupal-Seite anrufen",
|
||||
"Create a new entity in Drupal with smart field discovery and validation": "Erstellen Sie eine neue Entität in Drupal mit intelligenter Felderkennung und Validierung",
|
||||
"List entities from Drupal using JSON:API": "Entitäten von Drupal mit JSON:API auflisten",
|
||||
"Retrieve a single entity by UUID": "Eine einzelne Entität von UUID abrufen",
|
||||
"Update an existing entity in Drupal with smart field discovery and validation": "Aktualisieren Sie eine existierende Entität in Drupal mit intelligenter Felderkennung und Validierung",
|
||||
"Delete an entity from Drupal": "Eine Entität aus Drupal löschen",
|
||||
"Service": "Service",
|
||||
"Service configuration": "Dienstkonfiguration",
|
||||
"Entity Type": "Entitätstyp",
|
||||
"Entity Fields": "Entitäts-Felder",
|
||||
"Published Status": "Veröffentlichter Status",
|
||||
"Sort Options": "Sortieroptionen",
|
||||
"Sort Direction": "Sortierrichtung",
|
||||
"Limit": "Limit",
|
||||
"Output Options": "Ausgabeoptionen",
|
||||
"Entity UUID": "Entitäts-UUID",
|
||||
"The service to call.": "Der Service zum Anrufen.",
|
||||
"Choose the type of content to create.": "Wählen Sie den zu erstellenden Inhaltstyp aus.",
|
||||
"Fill in the content fields. Available fields depend on the entity type selected above.": "Füllen Sie die Felder aus. Verfügbare Felder hängen vom oben ausgewählten Entitätstyp ab.",
|
||||
"Choose what type of content to list.": "Wählen Sie aus, welche Art von Inhalt aufgelistet werden soll.",
|
||||
"Filter by publication status": "Nach Publikationsstatus filtern",
|
||||
"Choose how to sort the entities": "Sortieren der Entitäten",
|
||||
"Maximum number of entities to retrieve (0 = all entities)": "Maximale Anzahl der abzuholenden Entitäten (0 = alle Entitäten)",
|
||||
"Choose the type of content to retrieve.": "Wählen Sie die Art des abzufragenden Inhalts.",
|
||||
"The unique identifier (UUID) of the specific content item to retrieve.": "Der eindeutige Bezeichner (UUID) des zu ermittelnden Inhaltselements.",
|
||||
"Select the entity type and bundle": "Entitätstyp und Bundle auswählen",
|
||||
"The UUID of the entity to update": "Die UUID der zu aktualisierenden Entität",
|
||||
"Update the values for the entity fields (only provide values for fields you want to change)": "Werte für die Entitäts-Felder aktualisieren (nur Werte für Felder, die Sie ändern möchten)",
|
||||
"Choose the type of content to delete.": "Wählen Sie den zu löschenden Inhaltstyp aus.",
|
||||
"The unique identifier (UUID) of the specific content item to delete.": "Der eindeutige Bezeichner (UUID) des zu löschenden Inhaltselements.",
|
||||
"All": "Alle",
|
||||
"Published only": "Nur veröffentlicht",
|
||||
"Unpublished only": "Nur unveröffentlicht",
|
||||
"Newest first": "Neueste zuerst",
|
||||
"Oldest first": "Älteste zuerst",
|
||||
"Polling by ID": "Abfrage nach ID",
|
||||
"Polling by timestamp": "Abfragen nach Zeitstempel",
|
||||
"Webhook": "Webhook",
|
||||
"A trigger that polls the Drupal site by ID.": "Ein Trigger, der die Drupal-Seite per ID abfragt.",
|
||||
"A trigger that polls the Drupal site by timestamp.": "Ein Trigger, der die Drupal-Seite von Zeitstempeln abfragt.",
|
||||
"A webhook that the Drupal site can call to trigger a flow.": "Ein Webhook, den die Drupal-Seite aufrufen kann, um einen Fluss auszulösen.",
|
||||
"Name": "Name",
|
||||
"The name identifies the poll. It must be unique. It will be used to identify the poll in the Drupal site, e.g. if you use ECA to respond to this poll, you need to use the same name in the configuration of its poll event.": "Der Name identifiziert die Umfrage. Er muss eindeutig sein. Er wird verwendet, um die Umfrage in der Drupal-Website zu identifizieren, z. wenn Sie ECA verwenden, um auf diese Umfrage zu reagieren, müssen Sie den gleichen Namen in der Konfiguration des Umfragevorgangs verwenden.",
|
||||
"This name identifies the webhook. It must be unique. It will be used to identify the webhook in the Drupal site, e.g. if you use ECA to call this webhook, you will find this name in the list of available webhooks.": "Dieser Name identifiziert den Webhook. Er muss eindeutig sein. Er wird verwendet, um den Webhook in der Drupal-Website zu identifizieren, z. . Wenn Sie ECA benutzen, um diesen Webhook aufzurufen, finden Sie diesen Namen in der Liste der verfügbaren Webhooks."
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"Website URL": "URL del sitio web",
|
||||
"Username": "Usuario",
|
||||
"Password": "Contraseña",
|
||||
"URL of your Drupal site": "URL de su sitio Drupal",
|
||||
"\n**Using Drupal's JSON:API**\n\nYour Drupal site comes with JSON:API built-in. Authentication to access relevant parts requires the HTTP Basic Authentication module, which is also part of your Drupal site. Just ensure both are enabled and configure user authentication:\n\n1. Enable the JSON:API and the HTTP Basic Authentication modules\n2. Create a user account and give it the permissions you want Activepieces to have\n3. Use that account's credentials for authentication\n\nProvide the website URL in the format htt": "\n**Using Drupal's JSON:API**\n\nYour Drupal site comes with JSON:API built-in. Authentication to access relevant parts requires the HTTP Basic Authentication module, which is also part of your Drupal site. Just ensure both are enabled and configure user authentication:\n\n1. Enable the JSON:API and the HTTP Basic Authentication modules\n2. Create a user account and give it the permissions you want Activepieces to have\n3. Use that account's credentials for authentication\n\nProvide the website URL in the format https://www.example.com.\n\nFor extra functionality, you can use the [Drupal Orchestration](https://www.drupal.org/project/orchestration) module.\n",
|
||||
"Call Service": "Servicio de Llamadas",
|
||||
"Create Entity": "Crear Entidad",
|
||||
"List Entities": "Listar entidades",
|
||||
"Get Entity": "Get Entity",
|
||||
"Update Entity": "Actualizar entidad",
|
||||
"Delete Entity": "Eliminar entidad",
|
||||
"Call a service on the Drupal site": "Llamar a un servicio en el sitio de Drupal",
|
||||
"Create a new entity in Drupal with smart field discovery and validation": "Crear una nueva entidad en Drupal con descubrimiento de campo inteligente y validación",
|
||||
"List entities from Drupal using JSON:API": "Listar entidades de Drupal usando JSON:API",
|
||||
"Retrieve a single entity by UUID": "Recuperar una única entidad por UUID",
|
||||
"Update an existing entity in Drupal with smart field discovery and validation": "Actualizar una entidad existente en Drupal con descubrimiento de campo inteligente y validación",
|
||||
"Delete an entity from Drupal": "Eliminar una entidad de Drupal",
|
||||
"Service": "Servicio",
|
||||
"Service configuration": "Configuración del servicio",
|
||||
"Entity Type": "Tipo de entidad",
|
||||
"Entity Fields": "Campos de Entidad",
|
||||
"Published Status": "Estado publicado",
|
||||
"Sort Options": "Opciones de orden",
|
||||
"Sort Direction": "Ordenar dirección",
|
||||
"Limit": "Límite",
|
||||
"Output Options": "Opciones de salida",
|
||||
"Entity UUID": "UUID de la entidad",
|
||||
"The service to call.": "El servicio a llamar.",
|
||||
"Choose the type of content to create.": "Elija el tipo de contenido a crear.",
|
||||
"Fill in the content fields. Available fields depend on the entity type selected above.": "Rellene los campos de contenido. Los campos disponibles dependen del tipo de entidad seleccionado arriba.",
|
||||
"Choose what type of content to list.": "Elija qué tipo de contenido listar.",
|
||||
"Filter by publication status": "Filtrar por estado de publicación",
|
||||
"Choose how to sort the entities": "Elija cómo ordenar las entidades",
|
||||
"Maximum number of entities to retrieve (0 = all entities)": "Número máximo de entidades a recuperar (0 = todas las entidades)",
|
||||
"Choose the type of content to retrieve.": "Elija el tipo de contenido a recuperar.",
|
||||
"The unique identifier (UUID) of the specific content item to retrieve.": "El identificador único (UUID) del elemento de contenido específico a recuperar.",
|
||||
"Select the entity type and bundle": "Seleccione el tipo de entidad y paquete",
|
||||
"The UUID of the entity to update": "El UUID de la entidad a actualizar",
|
||||
"Update the values for the entity fields (only provide values for fields you want to change)": "Actualizar los valores para los campos de la entidad (sólo proporcione valores para los campos que desea cambiar)",
|
||||
"Choose the type of content to delete.": "Elija el tipo de contenido a eliminar.",
|
||||
"The unique identifier (UUID) of the specific content item to delete.": "El identificador único (UUID) del elemento de contenido específico a eliminar.",
|
||||
"All": "Todos",
|
||||
"Published only": "Sólo publicados",
|
||||
"Unpublished only": "Sólo sin publicar",
|
||||
"Newest first": "Más nuevo primero",
|
||||
"Oldest first": "Lo más antiguo primero",
|
||||
"Polling by ID": "Encuestas por ID",
|
||||
"Polling by timestamp": "Sondeo por marca de tiempo",
|
||||
"Webhook": "Webhook",
|
||||
"A trigger that polls the Drupal site by ID.": "Un disparador que encuesta el sitio Drupal por ID.",
|
||||
"A trigger that polls the Drupal site by timestamp.": "Un disparador que sondea el sitio de Drupal por marca de tiempo.",
|
||||
"A webhook that the Drupal site can call to trigger a flow.": "Un webhook al que el sitio de Drupal puede llamar para activar un flujo.",
|
||||
"Name": "Nombre",
|
||||
"The name identifies the poll. It must be unique. It will be used to identify the poll in the Drupal site, e.g. if you use ECA to respond to this poll, you need to use the same name in the configuration of its poll event.": "El nombre identifica la encuesta. Debe ser único. Se utilizará para identificar la encuesta en el sitio de Drupal, p.ej. si utiliza ECA para responder a esta encuesta, necesita utilizar el mismo nombre en la configuración de su evento de encuesta.",
|
||||
"This name identifies the webhook. It must be unique. It will be used to identify the webhook in the Drupal site, e.g. if you use ECA to call this webhook, you will find this name in the list of available webhooks.": "Este nombre identifica el webhook. Debe ser único. Se utilizará para identificar el webhook en el sitio de Drupal. Si utiliza ECA para llamar a este webhook, encontrará este nombre en la lista de webhooks disponibles."
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"Website URL": "URL du site web",
|
||||
"Username": "Nom d'utilisateur",
|
||||
"Password": "Password",
|
||||
"URL of your Drupal site": "URL de votre site Drupal",
|
||||
"\n**Using Drupal's JSON:API**\n\nYour Drupal site comes with JSON:API built-in. Authentication to access relevant parts requires the HTTP Basic Authentication module, which is also part of your Drupal site. Just ensure both are enabled and configure user authentication:\n\n1. Enable the JSON:API and the HTTP Basic Authentication modules\n2. Create a user account and give it the permissions you want Activepieces to have\n3. Use that account's credentials for authentication\n\nProvide the website URL in the format htt": "\n**Using Drupal's JSON:API**\n\nYour Drupal site comes with JSON:API built-in. Authentication to access relevant parts requires the HTTP Basic Authentication module, which is also part of your Drupal site. Just ensure both are enabled and configure user authentication:\n\n1. Enable the JSON:API and the HTTP Basic Authentication modules\n2. Create a user account and give it the permissions you want Activepieces to have\n3. Use that account's credentials for authentication\n\nProvide the website URL in the format https://www.example.com.\n\nFor extra functionality, you can use the [Drupal Orchestration](https://www.drupal.org/project/orchestration) module.\n",
|
||||
"Call Service": "Service d'appel",
|
||||
"Create Entity": "Créer une entité",
|
||||
"List Entities": "Lister les entités",
|
||||
"Get Entity": "Get Entity",
|
||||
"Update Entity": "Mettre à jour l'entité",
|
||||
"Delete Entity": "Supprimer l'entité",
|
||||
"Call a service on the Drupal site": "Appeler un service sur le site de Drupal",
|
||||
"Create a new entity in Drupal with smart field discovery and validation": "Créer une nouvelle entité dans Drupal avec découverte et validation de champs intelligents",
|
||||
"List entities from Drupal using JSON:API": "Lister les entités de Drupal en utilisant JSON:API",
|
||||
"Retrieve a single entity by UUID": "Récupérer une seule entité par UUID",
|
||||
"Update an existing entity in Drupal with smart field discovery and validation": "Mettre à jour une entité existante dans Drupal avec découverte et validation de champs intelligents",
|
||||
"Delete an entity from Drupal": "Supprimer une entité de Drupal",
|
||||
"Service": "Service",
|
||||
"Service configuration": "Configuration du service",
|
||||
"Entity Type": "Type d'entité",
|
||||
"Entity Fields": "Champs d'Entité",
|
||||
"Published Status": "Statut publié",
|
||||
"Sort Options": "Options de tri",
|
||||
"Sort Direction": "Direction de tri",
|
||||
"Limit": "Limite",
|
||||
"Output Options": "Options de sortie",
|
||||
"Entity UUID": "UUID de l'entité",
|
||||
"The service to call.": "Le service à appeler.",
|
||||
"Choose the type of content to create.": "Choisissez le type de contenu à créer.",
|
||||
"Fill in the content fields. Available fields depend on the entity type selected above.": "Remplissez les champs de contenu. Les champs disponibles dépendent du type d'entité sélectionné ci-dessus.",
|
||||
"Choose what type of content to list.": "Choisissez le type de contenu à lister.",
|
||||
"Filter by publication status": "Filtrer par statut de publication",
|
||||
"Choose how to sort the entities": "Choisir comment trier les entités",
|
||||
"Maximum number of entities to retrieve (0 = all entities)": "Nombre maximum d'entités à récupérer (0 = toutes les entités)",
|
||||
"Choose the type of content to retrieve.": "Choisissez le type de contenu à récupérer.",
|
||||
"The unique identifier (UUID) of the specific content item to retrieve.": "L'identifiant unique (UUID) de l'élément de contenu spécifique à récupérer.",
|
||||
"Select the entity type and bundle": "Sélectionnez le type d'entité et le bundle",
|
||||
"The UUID of the entity to update": "UUID de l'entité à mettre à jour",
|
||||
"Update the values for the entity fields (only provide values for fields you want to change)": "Mettre à jour les valeurs pour les champs de l'entité (ne fournir que des valeurs pour les champs que vous voulez modifier)",
|
||||
"Choose the type of content to delete.": "Choisissez le type de contenu à supprimer.",
|
||||
"The unique identifier (UUID) of the specific content item to delete.": "L'identifiant unique (UUID) de l'élément de contenu spécifique à supprimer.",
|
||||
"All": "Tous",
|
||||
"Published only": "Publié uniquement",
|
||||
"Unpublished only": "Non publié seulement",
|
||||
"Newest first": "Plus récent en premier",
|
||||
"Oldest first": "Le plus ancien en premier",
|
||||
"Polling by ID": "Vote par ID",
|
||||
"Polling by timestamp": "Sondage par horodatage",
|
||||
"Webhook": "Webhook",
|
||||
"A trigger that polls the Drupal site by ID.": "Un déclencheur qui interroge le site Drupal par ID.",
|
||||
"A trigger that polls the Drupal site by timestamp.": "Un déclencheur qui interroge le site Drupal par timestamp.",
|
||||
"A webhook that the Drupal site can call to trigger a flow.": "Un webhook que le site Drupal peut appeler pour déclencher un flux.",
|
||||
"Name": "Nom",
|
||||
"The name identifies the poll. It must be unique. It will be used to identify the poll in the Drupal site, e.g. if you use ECA to respond to this poll, you need to use the same name in the configuration of its poll event.": "Le nom identifie le sondage. Il doit être unique. Il sera utilisé pour identifier le sondage dans le site Drupal, par ex. si vous utilisez ECA pour répondre à ce sondage, vous devez utiliser le même nom dans la configuration de son événement de sondage.",
|
||||
"This name identifies the webhook. It must be unique. It will be used to identify the webhook in the Drupal site, e.g. if you use ECA to call this webhook, you will find this name in the list of available webhooks.": "Ce nom identifie le webhook. Il doit être unique. Il sera utilisé pour identifier le webhook dans le site Drupal. . Si vous utilisez ECA pour appeler ce webhook, vous trouverez ce nom dans la liste des webhooks disponibles."
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"Website URL": "Website URL",
|
||||
"Username": "ユーザー名",
|
||||
"Password": "Password",
|
||||
"URL of your Drupal site": "DrupalサイトのURL",
|
||||
"\n**Using Drupal's JSON:API**\n\nYour Drupal site comes with JSON:API built-in. Authentication to access relevant parts requires the HTTP Basic Authentication module, which is also part of your Drupal site. Just ensure both are enabled and configure user authentication:\n\n1. Enable the JSON:API and the HTTP Basic Authentication modules\n2. Create a user account and give it the permissions you want Activepieces to have\n3. Use that account's credentials for authentication\n\nProvide the website URL in the format htt": "\n**Using Drupal's JSON:API**\n\nYour Drupal site comes with JSON:API built-in. Authentication to access relevant parts requires the HTTP Basic Authentication module, which is also part of your Drupal site. Just ensure both are enabled and configure user authentication:\n\n1. Enable the JSON:API and the HTTP Basic Authentication modules\n2. Create a user account and give it the permissions you want Activepieces to have\n3. Use that account's credentials for authentication\n\nProvide the website URL in the format https://www.example.com.\n\nFor extra functionality, you can use the [Drupal Orchestration](https://www.drupal.org/project/orchestration) module.\n",
|
||||
"Call Service": "通話サービス",
|
||||
"Create Entity": "エンティティを作成",
|
||||
"List Entities": "エンティティの一覧",
|
||||
"Get Entity": "Get Entity",
|
||||
"Update Entity": "エンティティを更新",
|
||||
"Delete Entity": "エンティティを削除",
|
||||
"Call a service on the Drupal site": "Drupalサイトでサービスを呼び出す",
|
||||
"Create a new entity in Drupal with smart field discovery and validation": "スマートフィールドの検出と検証を使用してDrupalに新しいエンティティを作成します",
|
||||
"List entities from Drupal using JSON:API": "JSON:APIを使用してDrupalからエンティティを一覧表示する",
|
||||
"Retrieve a single entity by UUID": "UUID で単一のエンティティを取得",
|
||||
"Update an existing entity in Drupal with smart field discovery and validation": "スマートフィールドの検出と検証を使用してDrupalの既存のエンティティを更新します",
|
||||
"Delete an entity from Drupal": "Drupal からエンティティを削除する",
|
||||
"Service": "サービス",
|
||||
"Service configuration": "サービスの設定",
|
||||
"Entity Type": "エンティティタイプ",
|
||||
"Entity Fields": "エンティティフィールド",
|
||||
"Published Status": "公開状況",
|
||||
"Sort Options": "ソートオプション",
|
||||
"Sort Direction": "並べ替え方向",
|
||||
"Limit": "制限",
|
||||
"Output Options": "出力オプション",
|
||||
"Entity UUID": "エンティティUUID",
|
||||
"The service to call.": "通話するサービス",
|
||||
"Choose the type of content to create.": "作成するコンテンツの種類を選択します。",
|
||||
"Fill in the content fields. Available fields depend on the entity type selected above.": "コンテンツフィールドを入力します。利用可能なフィールドは上で選択したエンティティタイプによって異なります。",
|
||||
"Choose what type of content to list.": "表示するコンテンツの種類を選択します。",
|
||||
"Filter by publication status": "公開状態でフィルター",
|
||||
"Choose how to sort the entities": "エンティティのソート方法を選択してください",
|
||||
"Maximum number of entities to retrieve (0 = all entities)": "取得するエンティティの最大数 (0 = すべてのエンティティ)",
|
||||
"Choose the type of content to retrieve.": "取得するコンテンツの種類を選択します。",
|
||||
"The unique identifier (UUID) of the specific content item to retrieve.": "取得する特定のコンテンツ項目の一意識別子(UUID)。",
|
||||
"Select the entity type and bundle": "図形の種類とバンドルを選択します。",
|
||||
"The UUID of the entity to update": "更新するエンティティの UUID",
|
||||
"Update the values for the entity fields (only provide values for fields you want to change)": "エンティティフィールドの値を更新します (変更したいフィールドの値のみを提供します)",
|
||||
"Choose the type of content to delete.": "削除するコンテンツの種類を選択します。",
|
||||
"The unique identifier (UUID) of the specific content item to delete.": "削除する特定のコンテンツ項目の一意識別子(UUID)。",
|
||||
"All": "すべて",
|
||||
"Published only": "公開のみ",
|
||||
"Unpublished only": "未公開のみ",
|
||||
"Newest first": "最も新しい順",
|
||||
"Oldest first": "古い順",
|
||||
"Polling by ID": "IDでポーリング中",
|
||||
"Polling by timestamp": "タイムスタンプでポーリング",
|
||||
"Webhook": "Webhook",
|
||||
"A trigger that polls the Drupal site by ID.": "DrupalサイトをIDでポーリングするトリガー。",
|
||||
"A trigger that polls the Drupal site by timestamp.": "Drupalサイトをタイムスタンプでポーリングするトリガー。",
|
||||
"A webhook that the Drupal site can call to trigger a flow.": "Drupalサイトがフローをトリガーするために呼び出すことができるWebhook。",
|
||||
"Name": "Name",
|
||||
"The name identifies the poll. It must be unique. It will be used to identify the poll in the Drupal site, e.g. if you use ECA to respond to this poll, you need to use the same name in the configuration of its poll event.": "名前は投票を識別します。それは一意でなければなりません。Drupal サイト内の投票を識別するために使用されます。例えば、 この投票に応答するためにECAを使用する場合は、その投票イベントの設定で同じ名前を使用する必要があります。",
|
||||
"This name identifies the webhook. It must be unique. It will be used to identify the webhook in the Drupal site, e.g. if you use ECA to call this webhook, you will find this name in the list of available webhooks.": "This name identify the webhook. It must be unique. It will be used to identify the webhook in the Drupal site, e. をクリックします。 このWebhookを呼び出すためにECAを使用すると、利用可能なWebhookのリストにこの名前が表示されます。"
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"Website URL": "Website URL",
|
||||
"Username": "Gebruikersnaam",
|
||||
"Password": "Wachtwoord",
|
||||
"URL of your Drupal site": "URL van uw Drupal site",
|
||||
"\n**Using Drupal's JSON:API**\n\nYour Drupal site comes with JSON:API built-in. Authentication to access relevant parts requires the HTTP Basic Authentication module, which is also part of your Drupal site. Just ensure both are enabled and configure user authentication:\n\n1. Enable the JSON:API and the HTTP Basic Authentication modules\n2. Create a user account and give it the permissions you want Activepieces to have\n3. Use that account's credentials for authentication\n\nProvide the website URL in the format htt": "\n**Gebruik van Drupal's JSON:API**\n\nJe Drupal site komt met JSON:API ingebouwd. Authenticatie voor toegang tot relevante onderdelen vereist de HTTP basis authenticatie module, die ook deel uitmaakt van uw Drupal website. Zorg ervoor dat beide zijn ingeschakeld en configureer gebruikersauthenticatie:\n\n1. Schakel de JSON:API en de HTTP basis authenticatie modules\n2. Maak een gebruikersaccount aan en geef het de machtigingen die je wilt dat Activepieces\n3 hebben. Gebruik de inloggegevens van dat account voor authenticatie\n\nGeef de website URL in het formaat https://www. xample.com.\n\nVoor extra functionaliteit kun je de [Drupal Orchestration](https://www.drupal.org/project/orchestration) module gebruiken.\n",
|
||||
"Call Service": "Oproep service",
|
||||
"Create Entity": "Entiteit aanmaken",
|
||||
"List Entities": "Entiteiten weergeven",
|
||||
"Get Entity": "Get Entity",
|
||||
"Update Entity": "Entiteit bijwerken",
|
||||
"Delete Entity": "Verwijder Entiteit",
|
||||
"Call a service on the Drupal site": "Bel een dienst op de Drupal site",
|
||||
"Create a new entity in Drupal with smart field discovery and validation": "Maak een nieuwe entiteit in Drupal met smart field ontdekking en validatie",
|
||||
"List entities from Drupal using JSON:API": "Lijst van entiteiten van Drupal met behulp van JSON: API",
|
||||
"Retrieve a single entity by UUID": "Haal één entiteit op door UUID",
|
||||
"Update an existing entity in Drupal with smart field discovery and validation": "Update een bestaande entiteit in Drupal met smart field ontdekking en validatie",
|
||||
"Delete an entity from Drupal": "Verwijder een entiteit van Drupal",
|
||||
"Service": "Diensten",
|
||||
"Service configuration": "Service configuratie",
|
||||
"Entity Type": "Entiteit type",
|
||||
"Entity Fields": "Entiteit velden",
|
||||
"Published Status": "Gepubliceerde status",
|
||||
"Sort Options": "Sorteer opties",
|
||||
"Sort Direction": "Sorteer richting",
|
||||
"Limit": "Limiet",
|
||||
"Output Options": "Uitvoer opties",
|
||||
"Entity UUID": "Entiteit UUID",
|
||||
"The service to call.": "De te bellen dienst.",
|
||||
"Choose the type of content to create.": "Kies het type inhoud om aan te maken.",
|
||||
"Fill in the content fields. Available fields depend on the entity type selected above.": "Vul de inhoudsvelden in. Beschikbare velden zijn afhankelijk van het entiteittype dat hierboven is geselecteerd.",
|
||||
"Choose what type of content to list.": "Kies welk type inhoud u wilt weergeven.",
|
||||
"Filter by publication status": "Filteren op publicatiestatus",
|
||||
"Choose how to sort the entities": "Kies hoe de entiteiten te sorteren",
|
||||
"Maximum number of entities to retrieve (0 = all entities)": "Maximum aantal op te halen entiteiten (0 = alle entiteiten)",
|
||||
"Choose the type of content to retrieve.": "Kies het type inhoud om op te halen.",
|
||||
"The unique identifier (UUID) of the specific content item to retrieve.": "Het unieke id (UUID) van het specifieke inhoudsitem dat moet worden opgehaald.",
|
||||
"Select the entity type and bundle": "Selecteer het entiteittype en de bundel",
|
||||
"The UUID of the entity to update": "De UUID van de te updaten entiteit",
|
||||
"Update the values for the entity fields (only provide values for fields you want to change)": "Waarden voor de entiteit velden bijwerken (alleen waarden opgeven voor velden die u wilt wijzigen)",
|
||||
"Choose the type of content to delete.": "Kies het type inhoud om te verwijderen",
|
||||
"The unique identifier (UUID) of the specific content item to delete.": "Het unieke id (UUID) van het te verwijderen inhoudsitem",
|
||||
"All": "Allemaal",
|
||||
"Published only": "Alleen gepubliceerd",
|
||||
"Unpublished only": "Alleen gedepubliceerd",
|
||||
"Newest first": "Nieuwste eerst",
|
||||
"Oldest first": "Oudste eerst",
|
||||
"Polling by ID": "Polling via ID",
|
||||
"Polling by timestamp": "Stemmen op tijdstempel",
|
||||
"Webhook": "Webhook",
|
||||
"A trigger that polls the Drupal site by ID.": "Een trigger die de Drupal site opvraagt via ID.",
|
||||
"A trigger that polls the Drupal site by timestamp.": "Een trigger die de Drupal site opvraagt op tijdstempel.",
|
||||
"A webhook that the Drupal site can call to trigger a flow.": "Een webhook die de Drupal site kan aanroepen om een stroom te starten.",
|
||||
"Name": "Naam",
|
||||
"The name identifies the poll. It must be unique. It will be used to identify the poll in the Drupal site, e.g. if you use ECA to respond to this poll, you need to use the same name in the configuration of its poll event.": "De naam identificeert de poll. Het moet uniek zijn. Het zal worden gebruikt om de poll te identificeren in de Drupal site, bijvoorbeeld als je ECA gebruikt om te reageren op deze poll, moet je dezelfde naam gebruiken in de configuratie van het poll evenement.",
|
||||
"This name identifies the webhook. It must be unique. It will be used to identify the webhook in the Drupal site, e.g. if you use ECA to call this webhook, you will find this name in the list of available webhooks.": "Deze naam identificeert de webhook. Het moet uniek zijn. Het zal worden gebruikt om de webhook te identificeren op de Drupal site, bv. . Als je ECA gebruikt om deze webhook aan te roepen, vind je deze naam in de lijst van beschikbare webhooks."
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"Website URL": "URL do site",
|
||||
"Username": "Usuário:",
|
||||
"Password": "Senha",
|
||||
"URL of your Drupal site": "URL do seu site rupal",
|
||||
"\n**Using Drupal's JSON:API**\n\nYour Drupal site comes with JSON:API built-in. Authentication to access relevant parts requires the HTTP Basic Authentication module, which is also part of your Drupal site. Just ensure both are enabled and configure user authentication:\n\n1. Enable the JSON:API and the HTTP Basic Authentication modules\n2. Create a user account and give it the permissions you want Activepieces to have\n3. Use that account's credentials for authentication\n\nProvide the website URL in the format htt": "\n**Usando o JSON do Drupal: API**\n\nSeu site Drupal vem com JSON: API embutida. Autenticação para acessar partes relevantes requer o módulo de Autenticação Básica HTTP, que também faz parte do seu site Drupal. Apenas certifique-se de que ambos estão habilitados e configure a autenticação de usuário:\n\n1. Habilite o JSON:API e os módulos de autenticação básica HTTP\n2. Crie uma conta de usuário e conceda as permissões que você deseja que peças tenham\n3. Use credenciais dessa conta para autenticação\n\nfornecer a URL do site no formato https://www. xample.com.\n\nPara funcionalidades extras, você pode usar o módulo [Orquestração Drupal](https://www.drupal.org/project/orchestration) .\n",
|
||||
"Call Service": "Serviço de chamada",
|
||||
"Create Entity": "Criar entidade",
|
||||
"List Entities": "Lista de Entidades",
|
||||
"Get Entity": "Get Entity",
|
||||
"Update Entity": "Atualizar Entidade",
|
||||
"Delete Entity": "Excluir Entidade",
|
||||
"Call a service on the Drupal site": "Chame um serviço no site Drupal",
|
||||
"Create a new entity in Drupal with smart field discovery and validation": "Crie uma nova entidade no Drupal com a descoberta e validação de campo inteligente",
|
||||
"List entities from Drupal using JSON:API": "Lista entidades do Drupal usando JSON:API",
|
||||
"Retrieve a single entity by UUID": "Recuperar uma única entidade por UUID",
|
||||
"Update an existing entity in Drupal with smart field discovery and validation": "Atualizar uma entidade existente no Drupal com a descoberta e validação do campo inteligente",
|
||||
"Delete an entity from Drupal": "Deletar uma entidade do Drupal",
|
||||
"Service": "Serviço",
|
||||
"Service configuration": "Configuração do serviço",
|
||||
"Entity Type": "Tipo de entidade",
|
||||
"Entity Fields": "Campos de Entidade",
|
||||
"Published Status": "Status da publicação",
|
||||
"Sort Options": "Opções de ordenação",
|
||||
"Sort Direction": "Ordenar direção",
|
||||
"Limit": "Limitar",
|
||||
"Output Options": "Opções de saída",
|
||||
"Entity UUID": "UUID da entidade",
|
||||
"The service to call.": "O serviço a ser chamado.",
|
||||
"Choose the type of content to create.": "Escolha o tipo de conteúdo a criar.",
|
||||
"Fill in the content fields. Available fields depend on the entity type selected above.": "Preencha os campos de conteúdo. Campos disponíveis dependem do tipo de entidade selecionado acima.",
|
||||
"Choose what type of content to list.": "Escolha o tipo de conteúdo a listar.",
|
||||
"Filter by publication status": "Filtrar por estado da publicação",
|
||||
"Choose how to sort the entities": "Escolher como classificar as entidades",
|
||||
"Maximum number of entities to retrieve (0 = all entities)": "Número máximo de entidades a serem recuperadas (0 = todas as entidades)",
|
||||
"Choose the type of content to retrieve.": "Escolha o tipo de conteúdo a recuperar.",
|
||||
"The unique identifier (UUID) of the specific content item to retrieve.": "O identificador exclusivo (UUID) do item de conteúdo específico a recuperar.",
|
||||
"Select the entity type and bundle": "Selecione o tipo da entidade e o pacote",
|
||||
"The UUID of the entity to update": "O UUID da entidade para atualizar",
|
||||
"Update the values for the entity fields (only provide values for fields you want to change)": "Atualizar os valores para os campos de entidade (só fornecer valores para os campos que você deseja alterar)",
|
||||
"Choose the type of content to delete.": "Escolha o tipo de conteúdo a excluir.",
|
||||
"The unique identifier (UUID) of the specific content item to delete.": "O identificador exclusivo (UUID) do item de conteúdo específico para excluir.",
|
||||
"All": "TODOS",
|
||||
"Published only": "Publicado apenas",
|
||||
"Unpublished only": "Inédito apenas",
|
||||
"Newest first": "Mais recentes primeiro",
|
||||
"Oldest first": "Mais antigos primeiro",
|
||||
"Polling by ID": "Votação por ID",
|
||||
"Polling by timestamp": "Pesquisa por hora",
|
||||
"Webhook": "Webhook",
|
||||
"A trigger that polls the Drupal site by ID.": "Um gatilho que enquete o site do Drupal por ID.",
|
||||
"A trigger that polls the Drupal site by timestamp.": "Um gatilho que inicia o site Drupal por intervalo de tempo.",
|
||||
"A webhook that the Drupal site can call to trigger a flow.": "Um webhook que o site Drupal pode chamar para acionar um fluxo.",
|
||||
"Name": "Nome",
|
||||
"The name identifies the poll. It must be unique. It will be used to identify the poll in the Drupal site, e.g. if you use ECA to respond to this poll, you need to use the same name in the configuration of its poll event.": "O nome identifica a enquete. Ela deve ser única. Ela será usada para identificar a enquete no local do Drupal, por exemplo. se você usar ECA para responder a esta enquete, você precisa usar o mesmo nome na configuração do seu evento de enquete.",
|
||||
"This name identifies the webhook. It must be unique. It will be used to identify the webhook in the Drupal site, e.g. if you use ECA to call this webhook, you will find this name in the list of available webhooks.": "Este nome identifica o webhook. Ele deve ser único. Ele será usado para identificar o webhook no site Drupal. . se você usar o ECA para chamar esse webhook, você encontrará esse nome na lista de webhooks disponíveis."
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"Website URL": "Website URL",
|
||||
"Username": "Username",
|
||||
"Password": "Password",
|
||||
"URL of your Drupal site without a trailing \"/\" character": "URL of your Drupal site without a trailing \"/\" character",
|
||||
"\n**Using Drupal's JSON:API**\n\nYour Drupal site comes with JSON:API built-in. Authentication to access relevant parts requires the HTTP Basic Authentication module, which is also part of your Drupal site. Just ensure both are enabled and configure user authentication:\n\n1. Enable the JSON:API and the HTTP Basic Authentication modules\n2. Create a user account and give it the permissions you want Activepieces to have\n3. Use that account's credentials for authentication\n\nProvide the website URL in the format htt": "\n**Using Drupal's JSON:API**\n\nYour Drupal site comes with JSON:API built-in. Authentication to access relevant parts requires the HTTP Basic Authentication module, which is also part of your Drupal site. Just ensure both are enabled and configure user authentication:\n\n1. Enable the JSON:API and the HTTP Basic Authentication modules\n2. Create a user account and give it the permissions you want Activepieces to have\n3. Use that account's credentials for authentication\n\nProvide the website URL in the format https://www.example.com.\n\nFor extra functionality, you can use the [Drupal Orchestration](https://www.drupal.org/project/orchestration) module.\n",
|
||||
"Call Service": "Call Service",
|
||||
"Create Entity": "Create Entity",
|
||||
"List Entities": "List Entities",
|
||||
"Get Entity": "Get Entity",
|
||||
"Update Entity": "Update Entity",
|
||||
"Delete Entity": "Delete Entity",
|
||||
"Call a service on the Drupal site": "Call a service on the Drupal site",
|
||||
"Create a new entity in Drupal with smart field discovery and validation": "Create a new entity in Drupal with smart field discovery and validation",
|
||||
"List entities from Drupal using JSON:API": "List entities from Drupal using JSON:API",
|
||||
"Retrieve a single entity by UUID": "Retrieve a single entity by UUID",
|
||||
"Update an existing entity in Drupal with smart field discovery and validation": "Update an existing entity in Drupal with smart field discovery and validation",
|
||||
"Delete an entity from Drupal": "Delete an entity from Drupal",
|
||||
"Service": "Service",
|
||||
"Service configuration": "Service configuration",
|
||||
"Entity Type": "Entity Type",
|
||||
"Entity Fields": "Entity Fields",
|
||||
"Published Status": "Published Status",
|
||||
"Sort Options": "Sort Options",
|
||||
"Sort Direction": "Sort Direction",
|
||||
"Limit": "Limit",
|
||||
"Output Options": "Output Options",
|
||||
"Entity UUID": "Entity UUID",
|
||||
"The service to call.": "The service to call.",
|
||||
"Choose the type of content to create.": "Choose the type of content to create.",
|
||||
"Fill in the content fields. Available fields depend on the entity type selected above.": "Fill in the content fields. Available fields depend on the entity type selected above.",
|
||||
"Choose what type of content to list.": "Choose what type of content to list.",
|
||||
"Filter by publication status": "Filter by publication status",
|
||||
"Choose how to sort the entities": "Choose how to sort the entities",
|
||||
"Maximum number of entities to retrieve (0 = all entities)": "Maximum number of entities to retrieve (0 = all entities)",
|
||||
"Choose the type of content to retrieve.": "Choose the type of content to retrieve.",
|
||||
"The unique identifier (UUID) of the specific content item to retrieve.": "The unique identifier (UUID) of the specific content item to retrieve.",
|
||||
"Select the entity type and bundle": "Select the entity type and bundle",
|
||||
"The UUID of the entity to update": "The UUID of the entity to update",
|
||||
"Update the values for the entity fields (only provide values for fields you want to change)": "Update the values for the entity fields (only provide values for fields you want to change)",
|
||||
"Choose the type of content to delete.": "Choose the type of content to delete.",
|
||||
"The unique identifier (UUID) of the specific content item to delete.": "The unique identifier (UUID) of the specific content item to delete.",
|
||||
"All": "All",
|
||||
"Published only": "Published only",
|
||||
"Unpublished only": "Unpublished only",
|
||||
"Newest first": "Newest first",
|
||||
"Oldest first": "Oldest first",
|
||||
"Polling by ID": "Polling by ID",
|
||||
"Polling by timestamp": "Polling by timestamp",
|
||||
"Webhook": "Webhook",
|
||||
"A trigger that polls the Drupal site by ID.": "A trigger that polls the Drupal site by ID.",
|
||||
"A trigger that polls the Drupal site by timestamp.": "A trigger that polls the Drupal site by timestamp.",
|
||||
"A webhook that the Drupal site can call to trigger a flow.": "A webhook that the Drupal site can call to trigger a flow.",
|
||||
"Name": "Name",
|
||||
"The name identifies the poll. It must be unique. It will be used to identify the poll in the Drupal site, e.g. if you use ECA to respond to this poll, you need to use the same name in the configuration of its poll event.": "The name identifies the poll. It must be unique. It will be used to identify the poll in the Drupal site, e.g. if you use ECA to respond to this poll, you need to use the same name in the configuration of its poll event.",
|
||||
"This name identifies the webhook. It must be unique. It will be used to identify the webhook in the Drupal site, e.g. if you use ECA to call this webhook, you will find this name in the list of available webhooks.": "This name identifies the webhook. It must be unique. It will be used to identify the webhook in the Drupal site, e.g. if you use ECA to call this webhook, you will find this name in the list of available webhooks."
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"Website URL": "Website URL",
|
||||
"Username": "用户名",
|
||||
"Password": "密码",
|
||||
"URL of your Drupal site": "URL of your Drupal site",
|
||||
"\n**Using Drupal's JSON:API**\n\nYour Drupal site comes with JSON:API built-in. Authentication to access relevant parts requires the HTTP Basic Authentication module, which is also part of your Drupal site. Just ensure both are enabled and configure user authentication:\n\n1. Enable the JSON:API and the HTTP Basic Authentication modules\n2. Create a user account and give it the permissions you want Activepieces to have\n3. Use that account's credentials for authentication\n\nProvide the website URL in the format htt": "\n**Using Drupal's JSON:API**\n\nYour Drupal site comes with JSON:API built-in. Authentication to access relevant parts requires the HTTP Basic Authentication module, which is also part of your Drupal site. Just ensure both are enabled and configure user authentication:\n\n1. Enable the JSON:API and the HTTP Basic Authentication modules\n2. Create a user account and give it the permissions you want Activepieces to have\n3. Use that account's credentials for authentication\n\nProvide the website URL in the format https://www.example.com.\n\nFor extra functionality, you can use the [Drupal Orchestration](https://www.drupal.org/project/orchestration) module.\n",
|
||||
"Call Service": "Call Service",
|
||||
"Create Entity": "Create Entity",
|
||||
"List Entities": "List Entities",
|
||||
"Get Entity": "Get Entity",
|
||||
"Update Entity": "Update Entity",
|
||||
"Delete Entity": "Delete Entity",
|
||||
"Call a service on the Drupal site": "Call a service on the Drupal site",
|
||||
"Create a new entity in Drupal with smart field discovery and validation": "Create a new entity in Drupal with smart field discovery and validation",
|
||||
"List entities from Drupal using JSON:API": "List entities from Drupal using JSON:API",
|
||||
"Retrieve a single entity by UUID": "Retrieve a single entity by UUID",
|
||||
"Update an existing entity in Drupal with smart field discovery and validation": "Update an existing entity in Drupal with smart field discovery and validation",
|
||||
"Delete an entity from Drupal": "Delete an entity from Drupal",
|
||||
"Service": "Service",
|
||||
"Service configuration": "Service configuration",
|
||||
"Entity Type": "Entity Type",
|
||||
"Entity Fields": "Entity Fields",
|
||||
"Published Status": "Published Status",
|
||||
"Sort Options": "Sort Options",
|
||||
"Sort Direction": "Sort Direction",
|
||||
"Limit": "Limit",
|
||||
"Output Options": "Output Options",
|
||||
"Entity UUID": "Entity UUID",
|
||||
"The service to call.": "The service to call.",
|
||||
"Choose the type of content to create.": "Choose the type of content to create.",
|
||||
"Fill in the content fields. Available fields depend on the entity type selected above.": "Fill in the content fields. Available fields depend on the entity type selected above.",
|
||||
"Choose what type of content to list.": "Choose what type of content to list.",
|
||||
"Filter by publication status": "Filter by publication status",
|
||||
"Choose how to sort the entities": "Choose how to sort the entities",
|
||||
"Maximum number of entities to retrieve (0 = all entities)": "Maximum number of entities to retrieve (0 = all entities)",
|
||||
"Choose the type of content to retrieve.": "Choose the type of content to retrieve.",
|
||||
"The unique identifier (UUID) of the specific content item to retrieve.": "The unique identifier (UUID) of the specific content item to retrieve.",
|
||||
"Select the entity type and bundle": "Select the entity type and bundle",
|
||||
"The UUID of the entity to update": "The UUID of the entity to update",
|
||||
"Update the values for the entity fields (only provide values for fields you want to change)": "Update the values for the entity fields (only provide values for fields you want to change)",
|
||||
"Choose the type of content to delete.": "Choose the type of content to delete.",
|
||||
"The unique identifier (UUID) of the specific content item to delete.": "The unique identifier (UUID) of the specific content item to delete.",
|
||||
"All": "所有的",
|
||||
"Published only": "Published only",
|
||||
"Unpublished only": "Unpublished only",
|
||||
"Newest first": "Newest first",
|
||||
"Oldest first": "Oldest first",
|
||||
"Polling by ID": "Polling by ID",
|
||||
"Polling by timestamp": "Polling by timestamp",
|
||||
"Webhook": "Webhook",
|
||||
"A trigger that polls the Drupal site by ID.": "A trigger that polls the Drupal site by ID.",
|
||||
"A trigger that polls the Drupal site by timestamp.": "A trigger that polls the Drupal site by timestamp.",
|
||||
"A webhook that the Drupal site can call to trigger a flow.": "A webhook that the Drupal site can call to trigger a flow.",
|
||||
"Name": "名称",
|
||||
"The name identifies the poll. It must be unique. It will be used to identify the poll in the Drupal site, e.g. if you use ECA to respond to this poll, you need to use the same name in the configuration of its poll event.": "The name identifies the poll. It must be unique. It will be used to identify the poll in the Drupal site, e.g. if you use ECA to respond to this poll, you need to use the same name in the configuration of its poll event.",
|
||||
"This name identifies the webhook. It must be unique. It will be used to identify the webhook in the Drupal site, e.g. if you use ECA to call this webhook, you will find this name in the list of available webhooks.": "This name identifies the webhook. It must be unique. It will be used to identify the webhook in the Drupal site, e.g. if you use ECA to call this webhook, you will find this name in the list of available webhooks."
|
||||
}
|
||||
119
activepieces-fork/packages/pieces/community/drupal/src/index.ts
Normal file
119
activepieces-fork/packages/pieces/community/drupal/src/index.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import {
|
||||
HttpMethod,
|
||||
httpClient,
|
||||
} from '@activepieces/pieces-common';
|
||||
import {
|
||||
PieceAuth,
|
||||
Property,
|
||||
createPiece,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import { PieceCategory } from '@activepieces/shared';
|
||||
import { drupalCallServiceAction } from './lib/actions/services';
|
||||
import { drupalCreateEntityAction } from './lib/actions/create_entity';
|
||||
import { drupalListEntitiesAction } from './lib/actions/list_entities';
|
||||
import { drupalGetEntityAction } from './lib/actions/get_entity';
|
||||
import { drupalUpdateEntityAction } from './lib/actions/update_entity';
|
||||
import { drupalDeleteEntityAction } from './lib/actions/delete_entity';
|
||||
import { drupalPollingId } from './lib/triggers/polling-id';
|
||||
import { drupalPollingTimestamp } from './lib/triggers/polling-timestamp';
|
||||
import { drupalWebhook } from './lib/triggers/webhook';
|
||||
|
||||
const markdownPropertyDescription = `
|
||||
**Using Drupal's JSON:API**
|
||||
|
||||
Your Drupal site comes with JSON:API built-in. Authentication to access relevant parts requires the HTTP Basic Authentication module, which is also part of your Drupal site. Just ensure both are enabled and configure user authentication:
|
||||
|
||||
1. Enable the JSON:API and the HTTP Basic Authentication modules
|
||||
2. Create a user account and give it the permissions you want Activepieces to have
|
||||
3. Use that account's credentials for authentication
|
||||
|
||||
Provide the website URL in the format https://www.example.com.
|
||||
|
||||
For extra functionality, you can use the [Drupal Orchestration](https://www.drupal.org/project/orchestration) module.
|
||||
`;
|
||||
|
||||
export const drupalAuth = PieceAuth.CustomAuth({
|
||||
description: markdownPropertyDescription,
|
||||
required: true,
|
||||
props: {
|
||||
website_url: Property.ShortText({
|
||||
displayName: 'Website URL',
|
||||
required: true,
|
||||
description: 'URL of your Drupal site without a trailing "/" character',
|
||||
}),
|
||||
username: Property.ShortText({
|
||||
displayName: 'Username',
|
||||
required: true,
|
||||
}),
|
||||
password: Property.ShortText({
|
||||
displayName: 'Password',
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
validate: async ({ auth }) => {
|
||||
const { website_url, username, password } = auth;
|
||||
if (!website_url || !username || !password) {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Please fill all the fields [website_url, username, password]',
|
||||
};
|
||||
}
|
||||
if (website_url.endsWith('/')) {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'The website URL must not end with a trailing slash.',
|
||||
};
|
||||
}
|
||||
try {
|
||||
const basicAuth = Buffer.from(`${username}:${password}`).toString('base64');
|
||||
const response = await httpClient.sendRequest({
|
||||
method: HttpMethod.GET,
|
||||
url: website_url + `/jsonapi/user/user?filter[name]=` + username,
|
||||
headers: {
|
||||
'Authorization': `Basic ${basicAuth}`,
|
||||
'Accept': 'application/vnd.api+json',
|
||||
},
|
||||
});
|
||||
console.debug('Auth validation response', response);
|
||||
if (response.status === 200) {
|
||||
const data = (response.body as any).data;
|
||||
return {
|
||||
valid: (data && data.length > 0 && data[0].attributes.name === username),
|
||||
};
|
||||
}
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Authentication failed. Please check your credentials.',
|
||||
};
|
||||
} catch (e: any) {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Connection failed: ' + e.message,
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const drupal = createPiece({
|
||||
displayName: 'Drupal',
|
||||
auth: drupalAuth,
|
||||
minimumSupportedRelease: '0.36.1',
|
||||
logoUrl: 'https://cdn.activepieces.com/pieces/drupal.png',
|
||||
categories: [
|
||||
PieceCategory.BUSINESS_INTELLIGENCE,
|
||||
PieceCategory.COMMERCE,
|
||||
PieceCategory.CONTENT_AND_FILES,
|
||||
PieceCategory.FORMS_AND_SURVEYS,
|
||||
PieceCategory.MARKETING,
|
||||
],
|
||||
authors: ['dbuytaert', 'jurgenhaas'],
|
||||
actions: [
|
||||
drupalCallServiceAction,
|
||||
drupalCreateEntityAction,
|
||||
drupalListEntitiesAction,
|
||||
drupalGetEntityAction,
|
||||
drupalUpdateEntityAction,
|
||||
drupalDeleteEntityAction
|
||||
],
|
||||
triggers: [drupalPollingId, drupalPollingTimestamp, drupalWebhook],
|
||||
});
|
||||
@@ -0,0 +1,107 @@
|
||||
import {
|
||||
PiecePropValueSchema,
|
||||
Property,
|
||||
createAction,
|
||||
DynamicPropsValue,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { drupalAuth } from '../../';
|
||||
import { drupal } from '../common/jsonapi';
|
||||
import {
|
||||
fetchEntityTypesForEditing,
|
||||
buildFieldProperties
|
||||
} from '../common/drupal-entities';
|
||||
|
||||
type DrupalAuthType = PiecePropValueSchema<typeof drupalAuth>;
|
||||
|
||||
export const drupalCreateEntityAction = createAction({
|
||||
auth: drupalAuth,
|
||||
name: 'drupal-create-entity',
|
||||
displayName: 'Create Entity',
|
||||
description: 'Create a new entity in Drupal with smart field discovery and validation',
|
||||
props: {
|
||||
entity_type: Property.Dropdown({
|
||||
auth: drupalAuth,
|
||||
displayName: 'Entity Type',
|
||||
description: 'Choose the type of content to create.',
|
||||
required: true,
|
||||
refreshers: [],
|
||||
options: async ({ auth }) => fetchEntityTypesForEditing(auth),
|
||||
}),
|
||||
entity_fields: Property.DynamicProperties({
|
||||
auth: drupalAuth,
|
||||
displayName: 'Entity Fields',
|
||||
description: 'Fill in the content fields. Available fields depend on the entity type selected above.',
|
||||
required: false,
|
||||
refreshers: ['entity_type'],
|
||||
props: async (propsValue) => {
|
||||
|
||||
const { auth, entity_type } = propsValue;
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Please configure authentication first',
|
||||
};
|
||||
}
|
||||
return buildFieldProperties(auth, entity_type, true);
|
||||
}
|
||||
}),
|
||||
},
|
||||
async run({ auth, propsValue }) {
|
||||
const entityInfo = propsValue['entity_type'] as any;
|
||||
const fieldsData = propsValue['entity_fields'] as any;
|
||||
|
||||
// Extract field values, handling text fields with format correctly
|
||||
const fieldsToCreate: Record<string, any> = {};
|
||||
const processedFormatFields = new Set<string>();
|
||||
|
||||
for (const [key, value] of Object.entries(fieldsData)) {
|
||||
// Skip empty values and already processed format fields
|
||||
if (value === undefined || value === null || value === '' || processedFormatFields.has(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle format fields (they should be combined with their text field)
|
||||
if (key.endsWith('_format')) {
|
||||
const textFieldName = key.replace('_format', '');
|
||||
const textValue = fieldsData[textFieldName];
|
||||
|
||||
if (textValue) {
|
||||
fieldsToCreate[textFieldName] = {
|
||||
value: textValue,
|
||||
format: value
|
||||
};
|
||||
processedFormatFields.add(textFieldName);
|
||||
}
|
||||
processedFormatFields.add(key);
|
||||
}
|
||||
// Handle text fields (check if they have a format)
|
||||
else {
|
||||
const formatKey = `${key}_format`;
|
||||
const formatValue = fieldsData[formatKey];
|
||||
|
||||
if (formatValue && formatValue !== 'undefined') {
|
||||
fieldsToCreate[key] = {
|
||||
value: value,
|
||||
format: formatValue
|
||||
};
|
||||
processedFormatFields.add(formatKey);
|
||||
} else {
|
||||
fieldsToCreate[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(fieldsToCreate).length === 0) {
|
||||
throw new Error('At least one field must be provided to create an entity');
|
||||
}
|
||||
|
||||
return await drupal.createEntity(
|
||||
auth,
|
||||
entityInfo.entity_type,
|
||||
entityInfo.bundle,
|
||||
fieldsToCreate
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
import {
|
||||
PiecePropValueSchema,
|
||||
Property,
|
||||
createAction,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { drupalAuth } from '../../';
|
||||
import { drupal } from '../common/jsonapi';
|
||||
import { fetchEntityTypesForReading } from '../common/drupal-entities';
|
||||
|
||||
type DrupalAuthType = PiecePropValueSchema<typeof drupalAuth>;
|
||||
|
||||
export const drupalDeleteEntityAction = createAction({
|
||||
auth: drupalAuth,
|
||||
name: 'drupal-delete-entity',
|
||||
displayName: 'Delete Entity',
|
||||
description: 'Delete an entity from Drupal',
|
||||
props: {
|
||||
entity_type: Property.Dropdown({
|
||||
displayName: 'Entity Type',
|
||||
description: 'Choose the type of content to delete.',
|
||||
required: true,
|
||||
refreshers: [],
|
||||
auth: drupalAuth,
|
||||
options: async ({ auth }) => fetchEntityTypesForReading(auth),
|
||||
}),
|
||||
entity_uuid: Property.ShortText({
|
||||
displayName: 'Entity UUID',
|
||||
description: 'The unique identifier (UUID) of the specific content item to delete.',
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
async run({ auth, propsValue }) {
|
||||
const entityInfo = propsValue.entity_type as any;
|
||||
|
||||
return await drupal.deleteEntity(
|
||||
auth,
|
||||
entityInfo.entity_type,
|
||||
entityInfo.bundle,
|
||||
propsValue.entity_uuid
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
import {
|
||||
PiecePropValueSchema,
|
||||
Property,
|
||||
createAction,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { drupalAuth } from '../../';
|
||||
import { drupal } from '../common/jsonapi';
|
||||
import { fetchEntityTypesForReading } from '../common/drupal-entities';
|
||||
|
||||
type DrupalAuthType = PiecePropValueSchema<typeof drupalAuth>;
|
||||
|
||||
export const drupalGetEntityAction = createAction({
|
||||
auth: drupalAuth,
|
||||
name: 'drupal-get-entity',
|
||||
displayName: 'Get Entity',
|
||||
description: 'Retrieve a single entity by UUID',
|
||||
props: {
|
||||
entity_type: Property.Dropdown({
|
||||
displayName: 'Entity Type',
|
||||
description: 'Choose the type of content to retrieve.',
|
||||
required: true,
|
||||
refreshers: [],
|
||||
auth: drupalAuth,
|
||||
options: async ({ auth }) => fetchEntityTypesForReading(auth),
|
||||
}),
|
||||
entity_uuid: Property.ShortText({
|
||||
displayName: 'Entity UUID',
|
||||
description: 'The unique identifier (UUID) of the specific content item to retrieve.',
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
async run({ auth, propsValue }) {
|
||||
const entityInfo = propsValue.entity_type as any;
|
||||
|
||||
return await drupal.getEntity(
|
||||
auth,
|
||||
entityInfo.entity_type,
|
||||
entityInfo.bundle,
|
||||
propsValue.entity_uuid
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,175 @@
|
||||
import {
|
||||
PiecePropValueSchema,
|
||||
Property,
|
||||
createAction,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { drupalAuth } from '../../';
|
||||
import { drupal } from '../common/jsonapi';
|
||||
import { fetchEntityTypesForReading } from '../common/drupal-entities';
|
||||
|
||||
type DrupalAuthType = PiecePropValueSchema<typeof drupalAuth>;
|
||||
|
||||
export const drupalListEntitiesAction = createAction({
|
||||
auth: drupalAuth,
|
||||
name: 'drupal-list-entities',
|
||||
displayName: 'List Entities',
|
||||
description: 'List entities from Drupal using JSON:API',
|
||||
props: {
|
||||
entity_type: Property.Dropdown({
|
||||
displayName: 'Entity Type',
|
||||
description: 'Choose what type of content to list.',
|
||||
required: true,
|
||||
refreshers: [],
|
||||
auth: drupalAuth,
|
||||
options: async ({ auth }) => fetchEntityTypesForReading(auth),
|
||||
}),
|
||||
published_status: Property.StaticDropdown({
|
||||
displayName: 'Published Status',
|
||||
description: 'Filter by publication status',
|
||||
required: false,
|
||||
defaultValue: 'all',
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'All', value: 'all' },
|
||||
{ label: 'Published only', value: 'published' },
|
||||
{ label: 'Unpublished only', value: 'unpublished' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
sort_by: Property.DynamicProperties({
|
||||
displayName: 'Sort Options',
|
||||
description: 'Choose how to sort the entities',
|
||||
required: false,
|
||||
refreshers: ['entity_type'],
|
||||
auth: drupalAuth,
|
||||
props: async (propsValue) => {
|
||||
const entityInfo = propsValue['entity_type'] as any;
|
||||
if (!entityInfo) return {} as any;
|
||||
|
||||
let sortOptions: Array<{label: string; value: string}> = [];
|
||||
|
||||
if (entityInfo.entity_type === 'taxonomy_term') {
|
||||
sortOptions = [
|
||||
{ label: 'Updated date', value: 'changed' },
|
||||
{ label: 'Name', value: 'name' },
|
||||
];
|
||||
} else if (entityInfo.entity_type === 'user') {
|
||||
sortOptions = [
|
||||
{ label: 'Creation date', value: 'created' },
|
||||
{ label: 'Updated date', value: 'changed' },
|
||||
{ label: 'Name', value: 'name' },
|
||||
];
|
||||
} else {
|
||||
sortOptions = [
|
||||
{ label: 'Creation date', value: 'created' },
|
||||
{ label: 'Updated date', value: 'changed' },
|
||||
{ label: 'Title', value: 'title' },
|
||||
];
|
||||
}
|
||||
|
||||
return {
|
||||
sort_field: Property.StaticDropdown({
|
||||
displayName: 'Sort By',
|
||||
required: false,
|
||||
defaultValue: sortOptions[0].value,
|
||||
options: { options: sortOptions }
|
||||
})
|
||||
} as any;
|
||||
}
|
||||
}),
|
||||
sort_direction: Property.StaticDropdown({
|
||||
displayName: 'Sort Direction',
|
||||
required: false,
|
||||
defaultValue: 'DESC',
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'Newest first', value: 'DESC' },
|
||||
{ label: 'Oldest first', value: 'ASC' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
limit: Property.Number({
|
||||
displayName: 'Limit',
|
||||
description: 'Maximum number of entities to retrieve (0 = all entities)',
|
||||
required: true,
|
||||
defaultValue: 50,
|
||||
}),
|
||||
output_options: Property.DynamicProperties({
|
||||
displayName: 'Output Options',
|
||||
required: false,
|
||||
refreshers: ['entity_type'],
|
||||
auth: drupalAuth,
|
||||
props: async (propsValue) => {
|
||||
const entityInfo = propsValue['entity_type'] as any;
|
||||
if (!entityInfo) return {};
|
||||
|
||||
// Only show minimal output option for nodes (they can have many fields)
|
||||
if (entityInfo.entity_type === 'node') {
|
||||
return {
|
||||
minimal_output: Property.Checkbox({
|
||||
displayName: 'Minimal Output',
|
||||
description: 'Return only basic fields (UUID, title, status, dates) instead of all entity data',
|
||||
required: false,
|
||||
defaultValue: true,
|
||||
})
|
||||
} as any;
|
||||
}
|
||||
|
||||
return {} as any;
|
||||
}
|
||||
}),
|
||||
},
|
||||
async run({ auth, propsValue }) {
|
||||
const entityInfo = propsValue.entity_type as any;
|
||||
|
||||
const filters: Record<string, any> = {};
|
||||
|
||||
if (propsValue.published_status === 'published') {
|
||||
filters['status'] = '1';
|
||||
} else if (propsValue.published_status === 'unpublished') {
|
||||
filters['status'] = '0';
|
||||
}
|
||||
|
||||
let fields: string[] | undefined;
|
||||
const outputOptions = propsValue.output_options as any;
|
||||
|
||||
if (entityInfo.entity_type === 'node' && outputOptions?.minimal_output) {
|
||||
fields = ['id', 'title', 'status', 'created', 'changed', 'drupal_internal__nid', 'path'];
|
||||
} else if (entityInfo.entity_type === 'taxonomy_term') {
|
||||
fields = ['id', 'name', 'changed', 'created', 'path', 'drupal_internal__tid', 'status'];
|
||||
} else if (entityInfo.entity_type === 'user') {
|
||||
fields = ['id', 'name', 'mail', 'created', 'changed', 'status'];
|
||||
}
|
||||
|
||||
const sortField = (propsValue.sort_by as any)?.sort_field;
|
||||
|
||||
let entities = await drupal.listEntities(
|
||||
auth,
|
||||
entityInfo.entity_type,
|
||||
entityInfo.bundle,
|
||||
{
|
||||
filters,
|
||||
sort: sortField,
|
||||
sortDirection: propsValue.sort_direction,
|
||||
fields,
|
||||
limit: propsValue.limit,
|
||||
}
|
||||
);
|
||||
|
||||
// Remove type field for cleaner output
|
||||
const removeType = (entity: any) => {
|
||||
const { type, ...entityWithoutType } = entity;
|
||||
return entityWithoutType;
|
||||
};
|
||||
|
||||
entities = Array.isArray(entities)
|
||||
? entities.map(removeType)
|
||||
: removeType(entities);
|
||||
|
||||
return {
|
||||
entities: Array.isArray(entities) ? entities : [entities],
|
||||
count: Array.isArray(entities) ? entities.length : 1,
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,169 @@
|
||||
import {
|
||||
PiecePropValueSchema,
|
||||
Property,
|
||||
createAction,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import {
|
||||
httpClient,
|
||||
HttpMethod,
|
||||
HttpRequest,
|
||||
} from '@activepieces/pieces-common';
|
||||
import { drupalAuth } from '../../';
|
||||
type DrupalAuthType = PiecePropValueSchema<typeof drupalAuth>;
|
||||
|
||||
export const drupalCallServiceAction = createAction({
|
||||
auth: drupalAuth,
|
||||
name: 'drupal-call-service',
|
||||
displayName: 'Call Service',
|
||||
description: 'Call a service on the Drupal site',
|
||||
props: {
|
||||
service: Property.Dropdown({
|
||||
displayName: 'Service',
|
||||
description: 'The service to call.',
|
||||
required: true,
|
||||
refreshers: [],
|
||||
auth: drupalAuth,
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Please authenticate first.',
|
||||
};
|
||||
}
|
||||
const { website_url, username, password } = auth.props;
|
||||
|
||||
|
||||
try {
|
||||
const response = await httpClient.sendRequest<DrupalService[]>({
|
||||
method: HttpMethod.GET,
|
||||
url: website_url + `/orchestration/services`,
|
||||
headers: {
|
||||
'Authorization': `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`,
|
||||
'Accept': 'application/vnd.api+json',
|
||||
},
|
||||
});
|
||||
console.debug('Service response', response);
|
||||
if (response.status === 200) {
|
||||
return {
|
||||
disabled: false,
|
||||
options: response.body.map((service) => {
|
||||
return {
|
||||
label: service.label,
|
||||
description: service.description,
|
||||
value: service,
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.debug('Service error', e);
|
||||
}
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Error processing services',
|
||||
};
|
||||
},
|
||||
}),
|
||||
config: Property.DynamicProperties({
|
||||
displayName: 'Service configuration',
|
||||
refreshers: ['service'],
|
||||
required: true,
|
||||
auth: drupalAuth,
|
||||
props: async ({ service }) => {
|
||||
console.debug('Service config input', service);
|
||||
const fields: Record<string, any> = {};
|
||||
const items = (service as {config: DrupalServiceConfig[]}).config;
|
||||
items.forEach((config: any) => {
|
||||
if (config.type === 'boolean') {
|
||||
fields[config.key] = Property.Checkbox({
|
||||
displayName: config.label,
|
||||
description: config.description,
|
||||
required: config.required,
|
||||
defaultValue: config.default_value,
|
||||
});
|
||||
} else if (config.type === 'integer' || config.type === 'float') {
|
||||
fields[config.key] = Property.Number({
|
||||
displayName: config.label,
|
||||
description: config.description,
|
||||
required: config.required,
|
||||
defaultValue: config.default_value,
|
||||
});
|
||||
} else if (config.type === 'timestamp' || config.type === 'datetime_iso8601') {
|
||||
fields[config.key] = Property.DateTime({
|
||||
displayName: config.label,
|
||||
description: config.description,
|
||||
required: config.required,
|
||||
defaultValue: config.default_value,
|
||||
});
|
||||
} else if (config.options.length > 0) {
|
||||
fields[config.key] = Property.StaticDropdown({
|
||||
displayName: config.label,
|
||||
description: config.description,
|
||||
required: config.required,
|
||||
defaultValue: config.default_value,
|
||||
options: {
|
||||
options: config.options.map((option: any) => ({
|
||||
label: option.name,
|
||||
value: option.key,
|
||||
}))},
|
||||
});
|
||||
} else {
|
||||
|
||||
fields[config.key] = Property.ShortText({
|
||||
displayName: config.label,
|
||||
description: config.description,
|
||||
required: config.required,
|
||||
defaultValue: config.default_value,
|
||||
});
|
||||
}
|
||||
});
|
||||
console.debug('Field for this service', fields);
|
||||
return fields;
|
||||
},
|
||||
}),
|
||||
},
|
||||
async run({ auth, propsValue }) {
|
||||
const { website_url, username, password } = auth.props;
|
||||
const request: HttpRequest = {
|
||||
method: HttpMethod.POST,
|
||||
url: website_url + `/orchestration/service/execute`,
|
||||
body: {
|
||||
id: propsValue.service.id,
|
||||
config: propsValue.config,
|
||||
},
|
||||
headers: {
|
||||
'Authorization': `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`,
|
||||
'Accept': 'application/vnd.api+json',
|
||||
},
|
||||
};
|
||||
|
||||
const result = await httpClient.sendRequest<DrupalService>(request);
|
||||
console.debug('Service call completed', result);
|
||||
|
||||
if (result.status === 200 || result.status === 202) {
|
||||
return result.body;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
interface DrupalService {
|
||||
id: string;
|
||||
label: string;
|
||||
description: string;
|
||||
config: DrupalServiceConfig[];
|
||||
}
|
||||
|
||||
interface DrupalServiceConfig {
|
||||
key: string;
|
||||
label: string;
|
||||
description: string;
|
||||
required: boolean;
|
||||
type: string;
|
||||
default_value: string;
|
||||
options: string[];
|
||||
editable: boolean;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
import {
|
||||
PiecePropValueSchema,
|
||||
Property,
|
||||
createAction,
|
||||
DynamicPropsValue,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { drupalAuth } from '../../';
|
||||
import { drupal } from '../common/jsonapi';
|
||||
import {
|
||||
fetchEntityTypesForEditing,
|
||||
buildFieldProperties
|
||||
} from '../common/drupal-entities';
|
||||
|
||||
type DrupalAuthType = PiecePropValueSchema<typeof drupalAuth>;
|
||||
|
||||
export const drupalUpdateEntityAction = createAction({
|
||||
auth: drupalAuth,
|
||||
name: 'drupal-update-entity',
|
||||
displayName: 'Update Entity',
|
||||
description: 'Update an existing entity in Drupal with smart field discovery and validation',
|
||||
props: {
|
||||
entity_type: Property.Dropdown({
|
||||
auth: drupalAuth,
|
||||
displayName: 'Entity Type',
|
||||
description: 'Select the entity type and bundle',
|
||||
required: true,
|
||||
refreshers: [],
|
||||
options: async ({ auth }) => fetchEntityTypesForEditing(auth),
|
||||
}),
|
||||
entity_uuid: Property.ShortText({
|
||||
displayName: 'Entity UUID',
|
||||
description: 'The UUID of the entity to update',
|
||||
required: true,
|
||||
}),
|
||||
entity_fields: Property.DynamicProperties({
|
||||
auth: drupalAuth,
|
||||
displayName: 'Entity Fields',
|
||||
description: 'Update the values for the entity fields (only provide values for fields you want to change)',
|
||||
required: false,
|
||||
refreshers: ['entity_type'],
|
||||
props: async (propsValue) => {
|
||||
const { auth, entity_type } = propsValue;
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Please configure authentication first',
|
||||
};
|
||||
}
|
||||
return buildFieldProperties(auth, entity_type, false);
|
||||
}
|
||||
}),
|
||||
},
|
||||
async run({ auth, propsValue }) {
|
||||
const entityInfo = propsValue['entity_type'] as any;
|
||||
|
||||
const fieldsData = propsValue['entity_fields'] as any;
|
||||
|
||||
// Extract field values, handling text fields with format correctly
|
||||
const fieldsToUpdate: Record<string, any> = {};
|
||||
const processedFormatFields = new Set<string>();
|
||||
|
||||
for (const [key, value] of Object.entries(fieldsData)) {
|
||||
// Skip empty values and already processed format fields
|
||||
if (value === undefined || value === null || value === '' || processedFormatFields.has(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle format fields (they should be combined with their text field)
|
||||
if (key.endsWith('_format')) {
|
||||
const textFieldName = key.replace('_format', '');
|
||||
const textValue = fieldsData[textFieldName];
|
||||
|
||||
if (textValue) {
|
||||
fieldsToUpdate[textFieldName] = {
|
||||
value: textValue,
|
||||
format: value
|
||||
};
|
||||
processedFormatFields.add(textFieldName);
|
||||
}
|
||||
processedFormatFields.add(key);
|
||||
}
|
||||
// Handle text fields (check if they have a format)
|
||||
else {
|
||||
const formatKey = `${key}_format`;
|
||||
const formatValue = fieldsData[formatKey];
|
||||
|
||||
if (formatValue && formatValue !== undefined && formatValue !== null && formatValue !== '') {
|
||||
fieldsToUpdate[key] = {
|
||||
value: value,
|
||||
format: formatValue
|
||||
};
|
||||
processedFormatFields.add(formatKey);
|
||||
} else {
|
||||
fieldsToUpdate[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(fieldsToUpdate).length === 0) {
|
||||
throw new Error('No fields provided to update');
|
||||
}
|
||||
|
||||
return await drupal.updateEntity(
|
||||
auth,
|
||||
entityInfo.entity_type,
|
||||
entityInfo.bundle,
|
||||
propsValue['entity_uuid'],
|
||||
fieldsToUpdate
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,492 @@
|
||||
import {
|
||||
HttpMethod,
|
||||
} from '@activepieces/pieces-common';
|
||||
import {
|
||||
DynamicPropsValue,
|
||||
Property,
|
||||
AppConnectionValueForAuthProperty,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import { drupalAuth } from '../../';
|
||||
import { DrupalAuthType, makeJsonApiRequest } from './jsonapi';
|
||||
|
||||
|
||||
// =============================================================================
|
||||
// ENTITY TYPE DISCOVERY
|
||||
// Functions for discovering what entity types are available in Drupal
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Discovers available entity types from Drupal's JSON:API endpoint
|
||||
*
|
||||
* This function queries the main JSON:API endpoint to see what entity types
|
||||
* and bundles are available, then filters them to only show content entities
|
||||
* that users would typically want to work with (not config entities).
|
||||
*
|
||||
* @param auth - Drupal authentication credentials
|
||||
* @param context - Whether entities will be used for 'reading' or 'editing'
|
||||
* @returns Dropdown options for entity type selection
|
||||
*/
|
||||
async function fetchEntityTypes(auth: DrupalAuthType, context: 'reading' | 'editing') {
|
||||
if (!auth || !auth.props.website_url) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Please configure authentication first',
|
||||
};
|
||||
}
|
||||
|
||||
// Get the list of entity types that are allowed for this context
|
||||
const type = context === 'editing'
|
||||
? 'form'
|
||||
: 'view';
|
||||
const response = await makeJsonApiRequest(auth, `${auth.props.website_url}/jsonapi/entity_${type}_display/entity_${type}_display`, HttpMethod.GET);
|
||||
const data = (response.body as any).data || [];
|
||||
const allowedEntityTypes: string[] = [];
|
||||
data.forEach((entityType: any) => {
|
||||
allowedEntityTypes.push(entityType.attributes.targetEntityType + '--' + entityType.attributes.bundle);
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await makeJsonApiRequest(auth, `${auth.props.website_url}/jsonapi`, HttpMethod.GET);
|
||||
|
||||
if (response.status === 200) {
|
||||
const entityTypes: Array<{label: string; value: any}> = [];
|
||||
const data = response.body as any;
|
||||
|
||||
if (data.links) {
|
||||
for (const [key, value] of Object.entries(data.links)) {
|
||||
if (key !== 'self' && typeof value === 'object' && (value as any).href) {
|
||||
const parts = key.split('--');
|
||||
if (parts.length === 2) {
|
||||
const [entityType, bundle] = parts;
|
||||
|
||||
if (allowedEntityTypes.includes(key)) {
|
||||
const bundleName = bundle.charAt(0).toUpperCase() + bundle.slice(1).replace(/_/g, ' ');
|
||||
const entityTypeName = entityType.charAt(0).toUpperCase() + entityType.slice(1).replace(/_/g, ' ');
|
||||
const label = `${bundleName} (${entityTypeName})`;
|
||||
|
||||
entityTypes.push({
|
||||
label,
|
||||
value: {
|
||||
id: key,
|
||||
entity_type: entityType,
|
||||
bundle,
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
disabled: false,
|
||||
options: entityTypes.sort((a, b) => a.label.localeCompare(b.label)),
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to fetch entity types', e);
|
||||
}
|
||||
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Error loading entity types',
|
||||
};
|
||||
}
|
||||
|
||||
export async function fetchEntityTypesForReading(auth?: DrupalAuthType) {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Please configure authentication first',
|
||||
};
|
||||
}
|
||||
return await fetchEntityTypes(auth, 'reading');
|
||||
}
|
||||
|
||||
export async function fetchEntityTypesForEditing(auth?: DrupalAuthType) {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Please configure authentication first',
|
||||
};
|
||||
}
|
||||
return await fetchEntityTypes(auth, 'editing');
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// FORM DISPLAY DISCOVERY
|
||||
// Functions for discovering how Drupal displays forms for entity editing
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Fetches the form display configuration for an entity bundle
|
||||
*
|
||||
* In Drupal, administrators configure which fields appear on edit forms,
|
||||
* their order, and how they're displayed. This function retrieves that
|
||||
* configuration so we can show the same fields in the same order.
|
||||
*
|
||||
* Without this, we might show fields that admins have intentionally hidden
|
||||
* or fields that are read-only and shouldn't be edited.
|
||||
*
|
||||
* @param auth - Drupal authentication credentials
|
||||
* @param entityType - The entity type (e.g., 'node', 'user')
|
||||
* @param bundle - The bundle (e.g., 'article', 'page')
|
||||
* @returns Form display configuration object
|
||||
*/
|
||||
export async function fetchEntityFormDisplay(auth: DrupalAuthType, entityType: string, bundle: string) {
|
||||
try {
|
||||
const formDisplayId = `${entityType}.${bundle}.default`;
|
||||
const response = await makeJsonApiRequest(
|
||||
auth,
|
||||
`${auth.props.website_url}/jsonapi/entity_form_display/entity_form_display?filter[drupal_internal__id]=${encodeURIComponent(formDisplayId)}`,
|
||||
HttpMethod.GET
|
||||
);
|
||||
|
||||
if (response.status === 200 && response.body) {
|
||||
const data = (response.body as any).data;
|
||||
if (data && data.length > 0) {
|
||||
return data[0].attributes.content || {};
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to fetch form display', e);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches available text formats for rich text fields
|
||||
*
|
||||
* Drupal allows different text formats (like 'basic_html', 'full_html')
|
||||
* for rich text fields. This function gets the available formats so users
|
||||
* can choose how their content should be processed.
|
||||
*/
|
||||
export async function fetchTextFormats(auth: DrupalAuthType) {
|
||||
try {
|
||||
const response = await makeJsonApiRequest(
|
||||
auth,
|
||||
`${auth.props.website_url}/jsonapi/filter_format/filter_format`,
|
||||
HttpMethod.GET
|
||||
);
|
||||
|
||||
if (response.status === 200 && response.body) {
|
||||
const formats = (response.body as any).data || [];
|
||||
return formats.reduce((acc: Record<string, string>, format: any) => {
|
||||
const formatId = format.attributes.drupal_internal__format;
|
||||
const formatName = format.attributes.name;
|
||||
if (formatId && formatName) {
|
||||
acc[formatId] = formatName;
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to fetch text formats', e);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// FIELD CONFIGURATION DISCOVERY
|
||||
// Functions for discovering field metadata and configuration
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Fetches detailed field configuration including labels and requirements
|
||||
*
|
||||
* This gets the actual field definitions including human-readable labels,
|
||||
* whether fields are required, field types, etc. This metadata is used
|
||||
* to create appropriate form inputs with proper validation.
|
||||
*
|
||||
* @param auth - Drupal authentication credentials
|
||||
* @param entityType - The entity type (e.g., 'node', 'user')
|
||||
* @param bundle - The bundle (e.g., 'article', 'page')
|
||||
* @returns Object mapping field names to their configuration
|
||||
*/
|
||||
export async function fetchEntityFieldConfig(auth: DrupalAuthType, entityType: string, bundle: string) {
|
||||
try {
|
||||
const response = await makeJsonApiRequest(
|
||||
auth,
|
||||
`${auth.props.website_url}/jsonapi/field_config/field_config?filter[entity_type]=${entityType}&filter[bundle]=${bundle}`,
|
||||
HttpMethod.GET
|
||||
);
|
||||
|
||||
if (response.status === 200 && response.body) {
|
||||
const fields = (response.body as any).data || [];
|
||||
const fieldConfig: Record<string, any> = {};
|
||||
|
||||
fields.forEach((field: any) => {
|
||||
const fieldName = field.attributes.field_name;
|
||||
fieldConfig[fieldName] = {
|
||||
label: field.attributes.label,
|
||||
required: field.attributes.required,
|
||||
fieldType: field.attributes.field_type,
|
||||
};
|
||||
});
|
||||
|
||||
return fieldConfig;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to fetch field config', e);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the entity type and bundle are supported by the workflow
|
||||
*
|
||||
* This gets the actual workflow configuration on the site and determines if the
|
||||
* given entity type and bundle is supported by the workflow.
|
||||
*
|
||||
* @param auth - Drupal authentication credentials
|
||||
* @param entityType - The entity type (e.g., 'node', 'user')
|
||||
* @param bundle - The bundle (e.g., 'article', 'page')
|
||||
* @returns True if the entity type and bundle are supported by the workflow,
|
||||
* false otherwise
|
||||
*/
|
||||
export async function isEntitySupportingWorkflow(auth: DrupalAuthType, entityType: string, bundle: string): Promise<boolean> {
|
||||
try {
|
||||
const response = await makeJsonApiRequest(
|
||||
auth,
|
||||
`${auth.props. website_url}/jsonapi/workflow/workflow`,
|
||||
HttpMethod.GET
|
||||
);
|
||||
|
||||
if (response.status === 200 && response.body) {
|
||||
const workflows = (response.body as any).data || [];
|
||||
let found = false;
|
||||
workflows.forEach((workflow: any) => {
|
||||
const attrs = workflow?.attributes ?? {};
|
||||
const typeSettings = attrs?.type_settings ?? {};
|
||||
const entityTypes = typeSettings?.entity_types ?? {};
|
||||
|
||||
if (!entityTypes) {
|
||||
return;
|
||||
}
|
||||
if (entityTypes[entityType].includes(bundle)) {
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
return found;
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore this as the workflow may not be supported or the endpoint missing.
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a field type can be edited with simple form inputs
|
||||
*
|
||||
* Some Drupal field types are too complex for simple text/checkbox inputs
|
||||
* (like entity references, file uploads). This function determines which
|
||||
* field types we can reasonably handle in a workflow interface.
|
||||
*/
|
||||
export function isEditableFieldType(fieldType: string): boolean {
|
||||
const editableTypes = [
|
||||
'string', 'string_long', 'text', 'text_long', 'text_with_summary',
|
||||
'integer', 'decimal', 'float', 'boolean', 'email', 'telephone', 'uri'
|
||||
];
|
||||
return editableTypes.includes(fieldType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets human-readable labels for Drupal base fields
|
||||
*
|
||||
* TODO: This should be fetched from the form display instead of hardcoded.
|
||||
* Base fields like 'title', 'status' have standard labels, but these could
|
||||
* be customized by site administrators. We should get the actual labels
|
||||
* from the form display configuration.
|
||||
*
|
||||
* @param fieldName - The machine name of the field
|
||||
* @returns Human-readable label for the field
|
||||
*/
|
||||
export function getBaseFieldLabel(fieldName: string): string {
|
||||
const baseFieldLabels: Record<string, string> = {
|
||||
'title': 'Title',
|
||||
'status': 'Published',
|
||||
'created': 'Authored on',
|
||||
'changed': 'Changed',
|
||||
'promote': 'Promoted to front page',
|
||||
'sticky': 'Sticky at top of lists',
|
||||
'name': 'Name',
|
||||
'mail': 'Email address',
|
||||
};
|
||||
|
||||
return baseFieldLabels[fieldName] || fieldName;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// FIELD PROCESSING & FORM GENERATION
|
||||
// Functions that combine the above data to generate form properties
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Extracts editable fields from form display configuration
|
||||
*
|
||||
* This combines form display configuration (what fields to show) with
|
||||
* field configuration (labels, types, requirements) to create a list
|
||||
* of fields that should be editable in the workflow interface.
|
||||
*
|
||||
* @param auth - Drupal authentication credentials
|
||||
* @param entityType - The entity type (e.g., 'node', 'user')
|
||||
* @param bundle - The bundle (e.g., 'article', 'page')
|
||||
* @param formDisplayContent - Form display configuration from fetchEntityFormDisplay
|
||||
* @returns Array of field objects with name, type, label, required, weight
|
||||
*/
|
||||
export async function getEditableFieldsWithLabels(
|
||||
auth: DrupalAuthType,
|
||||
entityType: string,
|
||||
bundle: string,
|
||||
formDisplayContent: Record<string, any>
|
||||
) {
|
||||
const fieldConfig = await fetchEntityFieldConfig(auth, entityType, bundle);
|
||||
const fields: Array<{
|
||||
name: string;
|
||||
type: string;
|
||||
label: string;
|
||||
required: boolean;
|
||||
weight: number;
|
||||
}> = [];
|
||||
|
||||
const baseFields = ['title', 'name'];
|
||||
if (!await isEntitySupportingWorkflow(auth, entityType, bundle)) {
|
||||
baseFields.push('status');
|
||||
}
|
||||
|
||||
for (const [fieldName, config] of Object.entries(formDisplayContent)) {
|
||||
if (config && typeof config === 'object' && config.type) {
|
||||
const configInfo = fieldConfig[fieldName];
|
||||
|
||||
if (configInfo) {
|
||||
// Custom field with configuration
|
||||
if (isEditableFieldType(configInfo.fieldType)) {
|
||||
fields.push({
|
||||
name: fieldName,
|
||||
type: configInfo.fieldType,
|
||||
label: configInfo.label,
|
||||
required: configInfo.required,
|
||||
weight: config.weight || 0
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Base field - check if it's editable
|
||||
if (baseFields.includes(fieldName)) {
|
||||
fields.push({
|
||||
name: fieldName,
|
||||
type: fieldName === 'status' ? 'boolean' : 'string',
|
||||
label: getBaseFieldLabel(fieldName),
|
||||
required: ['title', 'name'].includes(fieldName),
|
||||
weight: config.weight || 0
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by weight (form display order), then by label
|
||||
return fields.sort((a, b) => {
|
||||
if (a.weight !== b.weight) return a.weight - b.weight;
|
||||
return a.label.localeCompare(b.label);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds Activepieces Property objects for dynamic form generation
|
||||
*
|
||||
* This is the main function that combines all the field discovery and
|
||||
* configuration to create the actual form properties that users will
|
||||
* see in the Activepieces interface.
|
||||
*
|
||||
* @param auth - Drupal authentication credentials
|
||||
* @param entityType - Selected entity type from dropdown
|
||||
* @param isCreateAction - Whether this is for creating (true) or updating (false)
|
||||
* @returns Dynamic properties object for Activepieces form
|
||||
*/
|
||||
export async function buildFieldProperties(
|
||||
auth: DrupalAuthType,
|
||||
entityType: any,
|
||||
isCreateAction = false
|
||||
): Promise<DynamicPropsValue> {
|
||||
const properties: DynamicPropsValue = {};
|
||||
|
||||
if (!entityType) {
|
||||
return properties;
|
||||
}
|
||||
|
||||
try {
|
||||
const formDisplay = await fetchEntityFormDisplay(auth, entityType.entity_type, entityType.bundle);
|
||||
const textFormats = await fetchTextFormats(auth);
|
||||
const availableFields = await getEditableFieldsWithLabels(
|
||||
auth,
|
||||
entityType.entity_type,
|
||||
entityType.bundle,
|
||||
formDisplay
|
||||
);
|
||||
|
||||
if (availableFields.length === 0) {
|
||||
properties['no_fields'] = Property.MarkDown({
|
||||
value: 'No editable fields found for this entity type.'
|
||||
});
|
||||
return properties;
|
||||
}
|
||||
|
||||
// Generate properties for all editable fields
|
||||
for (const field of availableFields) {
|
||||
const displayName = field.label;
|
||||
const description = undefined;
|
||||
const isRequired = field.required && isCreateAction;
|
||||
|
||||
if (field.type === 'text_with_summary' || field.type === 'text_long') {
|
||||
properties[field.name] = Property.LongText({
|
||||
displayName,
|
||||
description,
|
||||
required: isRequired,
|
||||
});
|
||||
|
||||
// Add text format selection if formats are available
|
||||
if (Object.keys(textFormats).length > 0) {
|
||||
properties[`${field.name}_format`] = Property.StaticDropdown({
|
||||
displayName: `${displayName} Format`,
|
||||
required: false,
|
||||
options: {
|
||||
options: Object.entries(textFormats).map(([key, name]) => ({
|
||||
label: String(name),
|
||||
value: key,
|
||||
})),
|
||||
},
|
||||
});
|
||||
}
|
||||
} else if (field.type === 'boolean') {
|
||||
properties[field.name] = Property.Checkbox({
|
||||
displayName,
|
||||
description,
|
||||
required: isRequired,
|
||||
});
|
||||
} else {
|
||||
// Default to text input for most field types
|
||||
properties[field.name] = Property.ShortText({
|
||||
displayName,
|
||||
description,
|
||||
required: isRequired,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error('Failed to generate field properties', e);
|
||||
properties['error'] = Property.MarkDown({
|
||||
value: 'Failed to load fields. Please check your authentication and entity type selection.'
|
||||
});
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
@@ -0,0 +1,482 @@
|
||||
/**
|
||||
* JSON:API Client - Two-Layer Architecture
|
||||
*
|
||||
* This module implements a two-layer approach for JSON:API operations:
|
||||
*
|
||||
* Layer 1 (Generic): Pure JSON:API operations that work with any JSON:API server
|
||||
* - jsonApi.get(), jsonApi.list(), jsonApi.create(), etc.
|
||||
* - Could be extracted to @activepieces/pieces-common for reuse across pieces
|
||||
* - Handles raw JSON:API requests, URLs, and response formats
|
||||
*
|
||||
* Layer 2 (Drupal-specific): Convenience functions for Drupal entity operations
|
||||
* - drupal.getEntity(), drupal.listEntities(), drupal.createEntity(), etc.
|
||||
* - Abstracts away entity types, bundles, and URL construction
|
||||
* - Provides clean developer experience with simple objects instead of JSON:API format
|
||||
*/
|
||||
|
||||
import {
|
||||
HttpMethod,
|
||||
httpClient,
|
||||
} from '@activepieces/pieces-common';
|
||||
import {
|
||||
AppConnectionValueForAuthProperty,
|
||||
PiecePropValueSchema,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import { drupalAuth } from '../../';
|
||||
|
||||
export type DrupalAuthType = AppConnectionValueForAuthProperty<typeof drupalAuth>;
|
||||
|
||||
export interface JsonApiResource {
|
||||
type: string;
|
||||
id?: string;
|
||||
attributes: Record<string, any>;
|
||||
relationships?: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface JsonApiResponse {
|
||||
data: JsonApiResource | JsonApiResource[];
|
||||
included?: JsonApiResource[];
|
||||
links?: Record<string, string | { href: string }>;
|
||||
meta?: Record<string, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a JSON:API request with proper authentication
|
||||
*/
|
||||
export async function makeJsonApiRequest<T = JsonApiResponse>(
|
||||
auth: DrupalAuthType,
|
||||
endpoint: string,
|
||||
method: HttpMethod = HttpMethod.GET,
|
||||
body?: any
|
||||
) {
|
||||
const { website_url, username, password } = auth.props;
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
'Accept': 'application/vnd.api+json',
|
||||
};
|
||||
|
||||
if (username && password) {
|
||||
const basicAuth = Buffer.from(`${username}:${password}`).toString('base64');
|
||||
headers['Authorization'] = `Basic ${basicAuth}`;
|
||||
}
|
||||
|
||||
if (body) {
|
||||
headers['Content-Type'] = 'application/vnd.api+json';
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await httpClient.sendRequest({
|
||||
method,
|
||||
url: endpoint,
|
||||
headers,
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
});
|
||||
|
||||
// Sanitize response body if it's a string containing JSON
|
||||
if (response.body && typeof response.body === 'string') {
|
||||
try {
|
||||
// Remove invalid control characters that violate JSON specification (RFC 8259 Section 7)
|
||||
// Workaround for Drupal bug: https://www.drupal.org/project/drupal/issues/3549107
|
||||
// TODO: Remove this when Drupal issue is fixed
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const cleanedBody = response.body.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '');
|
||||
response.body = JSON.parse(cleanedBody);
|
||||
} catch (parseError) {
|
||||
console.warn('Failed to parse JSON response, returning raw body:', parseError);
|
||||
// Return response as-is if parsing fails
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('JSON:API request failed:', { endpoint, method, error });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds JSON:API URL for entity operations
|
||||
*/
|
||||
export function getJsonApiUrl(
|
||||
baseUrl: string,
|
||||
entityType: string,
|
||||
bundle: string,
|
||||
uuid?: string
|
||||
): string {
|
||||
const cleanBaseUrl = baseUrl.replace(/\/+$/, '');
|
||||
const resourceType = `${entityType}--${bundle}`;
|
||||
|
||||
let url = `${cleanBaseUrl}/jsonapi/${entityType}/${resourceType}`;
|
||||
|
||||
if (uuid) {
|
||||
url += `/${uuid}`;
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts simple object to JSON:API format
|
||||
*/
|
||||
export function toJsonApiFormat(
|
||||
entityType: string,
|
||||
bundle: string,
|
||||
data: Record<string, any>,
|
||||
id?: string
|
||||
): JsonApiResponse {
|
||||
const resourceType = `${entityType}--${bundle}`;
|
||||
|
||||
const attributes: Record<string, any> = {};
|
||||
const relationships: Record<string, any> = {};
|
||||
|
||||
const isRelationship = (value: any) => value && typeof value === 'object' && 'data' in value;
|
||||
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
if (isRelationship(value)) {
|
||||
relationships[key] = value;
|
||||
} else {
|
||||
attributes[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
const resource: JsonApiResource = {
|
||||
type: resourceType,
|
||||
attributes,
|
||||
};
|
||||
|
||||
if (id) {
|
||||
resource.id = id;
|
||||
}
|
||||
|
||||
if (Object.keys(relationships).length > 0) {
|
||||
resource.relationships = relationships;
|
||||
}
|
||||
|
||||
return { data: resource };
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts JSON:API response to simple object format
|
||||
*/
|
||||
export function fromJsonApiFormat(response: JsonApiResponse): any | any[] {
|
||||
if (!response.data) return null;
|
||||
|
||||
try {
|
||||
if (Array.isArray(response.data)) {
|
||||
return response.data
|
||||
.filter(resource => resource != null)
|
||||
.map(convertJsonApiResource);
|
||||
} else {
|
||||
return convertJsonApiResource(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
// Return empty array instead of throwing, so pagination can continue
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function convertJsonApiResource(resource: JsonApiResource) {
|
||||
if (!resource || typeof resource !== 'object') {
|
||||
throw new Error('Invalid resource: resource is not an object');
|
||||
}
|
||||
|
||||
if (!resource.type) {
|
||||
throw new Error('Invalid resource: missing required "type" field');
|
||||
}
|
||||
|
||||
const result: any = {
|
||||
id: resource.id,
|
||||
type: resource.type,
|
||||
};
|
||||
|
||||
// Safely copy attributes
|
||||
if (resource.attributes && typeof resource.attributes === 'object') {
|
||||
try {
|
||||
Object.assign(result, resource.attributes);
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to copy attributes: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Safely copy relationships
|
||||
if (resource.relationships && typeof resource.relationships === 'object') {
|
||||
try {
|
||||
for (const [key, value] of Object.entries(resource.relationships)) {
|
||||
result[key] = value;
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to copy relationships: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds query parameters for JSON:API requests
|
||||
*/
|
||||
function buildQueryParams(options: {
|
||||
filters?: Record<string, any>;
|
||||
sort?: string;
|
||||
sortDirection?: string;
|
||||
fields?: string[];
|
||||
resourceType?: string;
|
||||
limit?: number;
|
||||
}): string {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (options.filters) {
|
||||
for (const [key, value] of Object.entries(options.filters)) {
|
||||
params.append(`filter[${key}]`, String(value));
|
||||
}
|
||||
}
|
||||
|
||||
if (options.sort) {
|
||||
const sortParam = options.sortDirection === 'ASC'
|
||||
? options.sort
|
||||
: `-${options.sort}`;
|
||||
params.append('sort', sortParam);
|
||||
}
|
||||
|
||||
if (options.fields && options.resourceType) {
|
||||
const fieldsParam = options.fields.join(',');
|
||||
params.append(`fields[${options.resourceType}]`, fieldsParam);
|
||||
}
|
||||
|
||||
if (options.limit && options.limit > 0) {
|
||||
params.append('page[limit]', String(options.limit));
|
||||
}
|
||||
|
||||
const queryString = params.toString();
|
||||
return queryString ? `?${queryString}` : '';
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// LAYER 1: Generic JSON:API Operations
|
||||
//
|
||||
// These functions work with any JSON:API server and handle the raw JSON:API
|
||||
// specification. They could be extracted to @activepieces/pieces-common for
|
||||
// reuse across different pieces (Rails API, Laravel API, etc.)
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Generic JSON:API client for any JSON:API compliant server
|
||||
*/
|
||||
export const jsonApi = {
|
||||
/**
|
||||
* Fetch a single resource by full JSON:API path
|
||||
* @example jsonApi.get(auth, '/jsonapi/node/node--article/12345')
|
||||
*/
|
||||
async get(auth: DrupalAuthType, resourcePath: string) {
|
||||
const url = `${auth.props.website_url.replace(/\/+$/, '')}${resourcePath}`;
|
||||
const result = await makeJsonApiRequest(auth, url, HttpMethod.GET);
|
||||
|
||||
if (result.status === 200) {
|
||||
return fromJsonApiFormat(result.body as JsonApiResponse);
|
||||
} else if (result.status === 404) {
|
||||
throw new Error(`Resource not found: ${resourcePath}`);
|
||||
} else if (result.status === 403) {
|
||||
throw new Error(`Access denied: ${resourcePath}`);
|
||||
}
|
||||
|
||||
throw new Error(`Failed to get resource: ${result.status}`);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch a collection of resources with optional query parameters
|
||||
* Follows pagination links if present to retrieve all requested data
|
||||
* @example jsonApi.list(auth, '/jsonapi/node/node--article', { sort: 'created', filters: { status: '1' } })
|
||||
*/
|
||||
async list(auth: DrupalAuthType, collectionPath: string, options?: {
|
||||
filters?: Record<string, any>;
|
||||
sort?: string;
|
||||
sortDirection?: string;
|
||||
fields?: string[];
|
||||
resourceType?: string;
|
||||
limit?: number;
|
||||
}) {
|
||||
const allEntities: any[] = [];
|
||||
const query = options ? buildQueryParams(options) : '';
|
||||
let url: string | null = `${auth.props.website_url.replace(/\/+$/, '')}${collectionPath}${query}`;
|
||||
|
||||
do {
|
||||
const result = await makeJsonApiRequest(auth, url, HttpMethod.GET);
|
||||
|
||||
if (result.status !== 200) {
|
||||
throw new Error(`Failed to list resources: ${result.status}`);
|
||||
}
|
||||
|
||||
if (!result.body) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Parse JSON if response body is a string
|
||||
let parsedBody: JsonApiResponse;
|
||||
if (typeof result.body === 'string') {
|
||||
try {
|
||||
parsedBody = JSON.parse(result.body);
|
||||
} catch (parseError) {
|
||||
console.warn('Skipping page due to corrupted data in Drupal database:', parseError);
|
||||
url = null; // Stop pagination
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
parsedBody = result.body as JsonApiResponse;
|
||||
}
|
||||
|
||||
const response = parsedBody;
|
||||
const entities = fromJsonApiFormat(response);
|
||||
|
||||
// Add entities from this page
|
||||
if (Array.isArray(entities)) {
|
||||
allEntities.push(...entities);
|
||||
} else if (entities) {
|
||||
allEntities.push(entities);
|
||||
}
|
||||
|
||||
// Continue to next page if it exists
|
||||
const nextLink = response.links?.['next'];
|
||||
url = typeof nextLink === 'string' ? nextLink : nextLink?.href || null;
|
||||
|
||||
} while (url);
|
||||
|
||||
return allEntities;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a new resource with JSON:API formatted data
|
||||
* @example jsonApi.create(auth, '/jsonapi/node/node--article', jsonApiFormattedData)
|
||||
*/
|
||||
async create(auth: DrupalAuthType, collectionPath: string, jsonApiData: JsonApiResponse) {
|
||||
const url = `${auth.props.website_url.replace(/\/+$/, '')}${collectionPath}`;
|
||||
const result = await makeJsonApiRequest(auth, url, HttpMethod.POST, jsonApiData);
|
||||
|
||||
if (result.status === 201 || result.status === 200) {
|
||||
return fromJsonApiFormat(result.body as JsonApiResponse);
|
||||
} else if (result.status === 422) {
|
||||
const errors = (result.body as any).errors || [];
|
||||
const errorMsg = errors.map((e: any) => e.detail || e.title).join(', ');
|
||||
throw new Error(`Validation failed: ${errorMsg}`);
|
||||
} else if (result.status === 403) {
|
||||
throw new Error('Permission denied to create resource');
|
||||
}
|
||||
|
||||
throw new Error(`Failed to create resource: ${result.status}`);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update a resource with JSON:API formatted data
|
||||
* @example jsonApi.update(auth, '/jsonapi/node/node--article/12345', jsonApiFormattedData)
|
||||
*/
|
||||
async update(auth: DrupalAuthType, resourcePath: string, jsonApiData: JsonApiResponse) {
|
||||
const url = `${auth.props.website_url.replace(/\/+$/, '')}${resourcePath}`;
|
||||
const result = await makeJsonApiRequest(auth, url, HttpMethod.PATCH, jsonApiData);
|
||||
|
||||
if (result.status === 200) {
|
||||
return fromJsonApiFormat(result.body as JsonApiResponse);
|
||||
} else if (result.status === 422) {
|
||||
const errors = (result.body as any).errors || [];
|
||||
const errorMsg = errors.map((e: any) => e.detail || e.title).join(', ');
|
||||
throw new Error(`Validation failed: ${errorMsg}`);
|
||||
} else if (result.status === 404) {
|
||||
throw new Error(`Resource not found: ${resourcePath}`);
|
||||
} else if (result.status === 403) {
|
||||
throw new Error('Permission denied to update resource');
|
||||
}
|
||||
|
||||
throw new Error(`Failed to update resource: ${result.status}`);
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete a resource
|
||||
* @example jsonApi.delete(auth, '/jsonapi/node/node--article/12345')
|
||||
*/
|
||||
async delete(auth: DrupalAuthType, resourcePath: string) {
|
||||
const url = `${auth.props.website_url.replace(/\/+$/, '')}${resourcePath}`;
|
||||
const result = await makeJsonApiRequest(auth, url, HttpMethod.DELETE);
|
||||
|
||||
if (result.status === 204 || result.status === 200) {
|
||||
return { success: true, message: `Deleted resource: ${resourcePath}` };
|
||||
} else if (result.status === 404) {
|
||||
throw new Error(`Resource not found: ${resourcePath}`);
|
||||
} else if (result.status === 403) {
|
||||
throw new Error('Permission denied to delete resource');
|
||||
}
|
||||
|
||||
throw new Error(`Failed to delete resource: ${result.status}`);
|
||||
}
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// LAYER 2: Drupal-Specific Operations
|
||||
//
|
||||
// These functions provide a clean developer experience by abstracting away
|
||||
// Drupal-specific concepts like entity types, bundles, and JSON:API formatting.
|
||||
// They use the generic JSON:API layer internally.
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Drupal-specific entity operations with simplified API
|
||||
* Handles entity types, bundles, URL construction, and data format conversion
|
||||
*/
|
||||
export const drupal = {
|
||||
/**
|
||||
* Get a single Drupal entity by entity type, bundle, and UUID
|
||||
* @example drupal.getEntity(auth, 'node', 'article', '12345-uuid')
|
||||
*/
|
||||
async getEntity(auth: DrupalAuthType, entityType: string, bundle: string, uuid: string) {
|
||||
const resourcePath = `/jsonapi/${entityType}/${bundle}/${uuid}`;
|
||||
return await jsonApi.get(auth, resourcePath);
|
||||
},
|
||||
|
||||
/**
|
||||
* List Drupal entities with optional filtering, sorting, and field selection
|
||||
* @example drupal.listEntities(auth, 'node', 'article', { filters: { status: '1' }, sort: 'created' })
|
||||
*/
|
||||
async listEntities(auth: DrupalAuthType, entityType: string, bundle: string, options?: {
|
||||
filters?: Record<string, any>;
|
||||
sort?: string;
|
||||
sortDirection?: string;
|
||||
fields?: string[];
|
||||
limit?: number;
|
||||
}) {
|
||||
const collectionPath = `/jsonapi/${entityType}/${bundle}`;
|
||||
const queryOptions = options ? {
|
||||
...options,
|
||||
resourceType: `${entityType}--${bundle}`
|
||||
} : undefined;
|
||||
|
||||
return await jsonApi.list(auth, collectionPath, queryOptions);
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a new Drupal entity with simple object data (automatically converts to JSON:API format)
|
||||
* @example drupal.createEntity(auth, 'node', 'article', { title: 'My Article', body: 'Content...' })
|
||||
*/
|
||||
async createEntity(auth: DrupalAuthType, entityType: string, bundle: string, entityData: Record<string, any>) {
|
||||
const collectionPath = `/jsonapi/${entityType}/${bundle}`;
|
||||
const jsonApiData = toJsonApiFormat(entityType, bundle, entityData);
|
||||
|
||||
return await jsonApi.create(auth, collectionPath, jsonApiData);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update a Drupal entity with simple object data (automatically converts to JSON:API format)
|
||||
* @example drupal.updateEntity(auth, 'node', 'article', '12345-uuid', { title: 'Updated Title' })
|
||||
*/
|
||||
async updateEntity(auth: DrupalAuthType, entityType: string, bundle: string, uuid: string, entityData: Record<string, any>) {
|
||||
const resourcePath = `/jsonapi/${entityType}/${bundle}/${uuid}`;
|
||||
const jsonApiData = toJsonApiFormat(entityType, bundle, entityData, uuid);
|
||||
|
||||
return await jsonApi.update(auth, resourcePath, jsonApiData);
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete a Drupal entity
|
||||
* @example drupal.deleteEntity(auth, 'node', 'article', '12345-uuid')
|
||||
*/
|
||||
async deleteEntity(auth: DrupalAuthType, entityType: string, bundle: string, uuid: string) {
|
||||
const resourcePath = `/jsonapi/${entityType}/${bundle}/${uuid}`;
|
||||
return await jsonApi.delete(auth, resourcePath);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
import {
|
||||
createTrigger,
|
||||
TriggerStrategy,
|
||||
PiecePropValueSchema,
|
||||
Property,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import {
|
||||
DedupeStrategy,
|
||||
httpClient,
|
||||
HttpMethod,
|
||||
Polling,
|
||||
pollingHelper,
|
||||
} from '@activepieces/pieces-common';
|
||||
import { drupalAuth } from '../../';
|
||||
import { DrupalAuthType } from '../common/jsonapi';
|
||||
|
||||
const polling: Polling<DrupalAuthType, { name: string }> = {
|
||||
strategy: DedupeStrategy.LAST_ITEM,
|
||||
items: async ({ auth, propsValue, lastItemId }) => {
|
||||
if (lastItemId === undefined || lastItemId === null) {
|
||||
lastItemId = '0';
|
||||
}
|
||||
console.debug('Polling by ID', propsValue['name'], lastItemId);
|
||||
const { website_url, username, password } = auth.props;
|
||||
const body: any = {
|
||||
name: propsValue['name'],
|
||||
id: lastItemId,
|
||||
};
|
||||
const response = await httpClient.sendRequest<DrupalPollItemId[]>({
|
||||
method: HttpMethod.POST,
|
||||
url: website_url + `/orchestration/poll`,
|
||||
body: body,
|
||||
headers: {
|
||||
'Authorization': `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`,
|
||||
'Accept': 'application/vnd.api+json',
|
||||
},
|
||||
});
|
||||
console.debug('Poll response', response);
|
||||
console.debug('Poll response', JSON.stringify(response.body));
|
||||
return response.body.reverse().map((item) => ({
|
||||
id: item.id,
|
||||
data: item.data,
|
||||
}));
|
||||
},
|
||||
};
|
||||
|
||||
export const drupalPollingId = createTrigger({
|
||||
auth: drupalAuth,
|
||||
name: 'drupalPollingId',
|
||||
displayName: 'Polling by ID',
|
||||
description: 'A trigger that polls the Drupal site by ID.',
|
||||
props: {
|
||||
name: Property.ShortText({
|
||||
displayName: 'Name',
|
||||
description: 'The name identifies the poll. It must be unique. It will be used to identify the poll in the Drupal site, e.g. if you use ECA to respond to this poll, you need to use the same name in the configuration of its poll event.',
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
sampleData: {},
|
||||
type: TriggerStrategy.POLLING,
|
||||
async test(context) {
|
||||
const { auth, propsValue, store, files } = context;
|
||||
return await pollingHelper.test(polling, { store, auth, propsValue, files });
|
||||
},
|
||||
async onEnable(context) {
|
||||
const { auth, propsValue, store } = context;
|
||||
await pollingHelper.onEnable(polling, { store, auth, propsValue });
|
||||
},
|
||||
async onDisable(context) {
|
||||
const { auth, propsValue, store } = context;
|
||||
await pollingHelper.onDisable(polling, { store, auth, propsValue });
|
||||
},
|
||||
async run(context) {
|
||||
const { auth, propsValue, store, files } = context;
|
||||
return await pollingHelper.poll(polling, { store, auth, propsValue, files });
|
||||
},
|
||||
});
|
||||
|
||||
interface DrupalPollItemId {
|
||||
data: any;
|
||||
id: string;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import {
|
||||
createTrigger,
|
||||
TriggerStrategy,
|
||||
PiecePropValueSchema,
|
||||
Property,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import {
|
||||
DedupeStrategy,
|
||||
httpClient,
|
||||
HttpMethod,
|
||||
Polling,
|
||||
pollingHelper,
|
||||
} from '@activepieces/pieces-common';
|
||||
import { drupalAuth } from '../../';
|
||||
import { DrupalAuthType } from '../common/jsonapi';
|
||||
|
||||
const polling: Polling<DrupalAuthType, { name: string }> = {
|
||||
strategy: DedupeStrategy.TIMEBASED,
|
||||
items: async ({ auth, propsValue, lastFetchEpochMS }) => {
|
||||
if (lastFetchEpochMS === undefined || lastFetchEpochMS === null) {
|
||||
lastFetchEpochMS = 0;
|
||||
}
|
||||
console.debug('Polling by timestamp', propsValue['name'], lastFetchEpochMS);
|
||||
const { website_url, username, password } = auth.props;
|
||||
const body: any = {
|
||||
name: propsValue['name'],
|
||||
timestamp: lastFetchEpochMS / 1000,
|
||||
};
|
||||
const response = await httpClient.sendRequest<DrupalPollItemTimestamp[]>({
|
||||
method: HttpMethod.POST,
|
||||
url: website_url + `/orchestration/poll`,
|
||||
body: body,
|
||||
headers: {
|
||||
'Authorization': `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`,
|
||||
'Accept': 'application/vnd.api+json',
|
||||
},
|
||||
});
|
||||
console.debug('Poll response', response);
|
||||
console.debug('Poll response', JSON.stringify(response.body));
|
||||
return response.body.map((item) => ({
|
||||
epochMilliSeconds: item.timestamp * 1000,
|
||||
data: item.data,
|
||||
}));
|
||||
},
|
||||
};
|
||||
|
||||
export const drupalPollingTimestamp = createTrigger({
|
||||
auth: drupalAuth,
|
||||
name: 'drupalPollingTimestamp',
|
||||
displayName: 'Polling by timestamp',
|
||||
description: 'A trigger that polls the Drupal site by timestamp.',
|
||||
props: {
|
||||
name: Property.ShortText({
|
||||
displayName: 'Name',
|
||||
description: 'The name identifies the poll. It must be unique. It will be used to identify the poll in the Drupal site, e.g. if you use ECA to respond to this poll, you need to use the same name in the configuration of its poll event.',
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
sampleData: {},
|
||||
type: TriggerStrategy.POLLING,
|
||||
async test(context) {
|
||||
const { auth, propsValue, store, files } = context;
|
||||
return await pollingHelper.test(polling, { store, auth, propsValue, files });
|
||||
},
|
||||
async onEnable(context) {
|
||||
const { auth, propsValue, store } = context;
|
||||
await pollingHelper.onEnable(polling, { store, auth, propsValue });
|
||||
},
|
||||
async onDisable(context) {
|
||||
const { auth, propsValue, store } = context;
|
||||
await pollingHelper.onDisable(polling, { store, auth, propsValue });
|
||||
},
|
||||
async run(context) {
|
||||
const { auth, propsValue, store, files } = context;
|
||||
return await pollingHelper.poll(polling, { store, auth, propsValue, files });
|
||||
},
|
||||
});
|
||||
|
||||
interface DrupalPollItemTimestamp {
|
||||
data: any;
|
||||
timestamp: number;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import {
|
||||
httpClient,
|
||||
HttpMethod,
|
||||
} from '@activepieces/pieces-common';
|
||||
import {
|
||||
createTrigger,
|
||||
Property,
|
||||
TriggerStrategy,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import { drupalAuth } from '../../';
|
||||
import { DrupalAuthType } from '../common/jsonapi';
|
||||
|
||||
export const drupalWebhook = createTrigger({
|
||||
auth: drupalAuth,
|
||||
name: 'drupalWebhook',
|
||||
displayName: 'Webhook',
|
||||
description: 'A webhook that the Drupal site can call to trigger a flow.',
|
||||
props: {
|
||||
id: Property.ShortText({
|
||||
displayName: 'Name',
|
||||
description: 'This name identifies the webhook. It must be unique. It will be used to identify the webhook in the Drupal site, e.g. if you use ECA to call this webhook, you will find this name in the list of available webhooks.',
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
sampleData: {},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
async onEnable(context) {
|
||||
const { website_url, username, password } = context.auth.props;
|
||||
const body: any = {
|
||||
id: context.propsValue.id,
|
||||
webHookUrl: context.webhookUrl,
|
||||
};
|
||||
const response = await httpClient.sendRequest({
|
||||
method: HttpMethod.POST,
|
||||
url: website_url + `/orchestration/webhook/register`,
|
||||
body: body,
|
||||
headers: {
|
||||
'Authorization': `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`,
|
||||
'Accept': 'application/vnd.api+json',
|
||||
},
|
||||
});
|
||||
console.debug('Webhook register response', response);
|
||||
await context.store.put(`_drupal_webhook_trigger_` + context.propsValue.id, response.body);
|
||||
},
|
||||
async onDisable(context) {
|
||||
const { website_url, username, password } = context.auth.props;
|
||||
const webhook = await context.store.get(`_drupal_webhook_trigger` + context.propsValue.id);
|
||||
if (webhook) {
|
||||
const response = await httpClient.sendRequest({
|
||||
method: HttpMethod.POST,
|
||||
url: website_url + `/orchestration/webhook/unregister`,
|
||||
body: webhook,
|
||||
headers: {
|
||||
'Authorization': `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`,
|
||||
'Accept': 'application/vnd.api+json',
|
||||
},
|
||||
});
|
||||
console.debug('Webhook unregister response', response);
|
||||
}
|
||||
},
|
||||
async run(context) {
|
||||
return [context.payload.body];
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "../../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"importHelpers": true,
|
||||
"noImplicitOverride": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noPropertyAccessFromIndexSignature": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user