Add Activepieces integration for workflow automation

- Add Activepieces fork with SmoothSchedule custom piece
- Create integrations app with Activepieces service layer
- Add embed token endpoint for iframe integration
- Create Automations page with embedded workflow builder
- Add sidebar visibility fix for embed mode
- Add list inactive customers endpoint to Public API
- Include SmoothSchedule triggers: event created/updated/cancelled
- Include SmoothSchedule actions: create/update/cancel events, list resources/services/customers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
poduck
2025-12-18 22:59:37 -05:00
parent 9848268d34
commit 3aa7199503
16292 changed files with 1284892 additions and 4708 deletions

View File

@@ -0,0 +1,66 @@
import {
AppConnectionValueForAuthProperty,
PieceAuth,
Property,
ShortTextProperty,
StaticPropsValue,
} from '@activepieces/pieces-framework';
import { getUsers, sendJiraRequest } from './lib/common';
import { HttpError, HttpMethod } from '@activepieces/pieces-common';
import { z } from 'zod';
import { propsValidation } from '@activepieces/pieces-common';
import { AppConnectionType } from '@activepieces/shared';
export const jiraCloudAuth = PieceAuth.CustomAuth({
description: `
You can generate your API token from:
***https://id.atlassian.com/manage-profile/security/api-tokens***
`,
required: true,
props: {
instanceUrl: Property.ShortText({
displayName: 'Instance URL',
description:
'The link of your Jira instance (e.g https://example.atlassian.net)',
required: true,
}),
email: Property.ShortText({
displayName: 'Email',
description: 'The email you use to login to Jira',
required: true,
}),
apiToken: PieceAuth.SecretText({
displayName: 'API Token',
description: 'Your Jira API Token',
required: true,
}),
},
validate: async ({ auth }) => {
try {
await propsValidation.validateZod(auth, {
instanceUrl: z.string().url(),
email: z.string().email(),
});
await sendJiraRequest({
auth: {
type: AppConnectionType.CUSTOM_AUTH,
props: auth,
},
method: HttpMethod.GET,
url: 'myself',
});
return {
valid: true,
};
} catch (e) {
const message = ((e as HttpError).response?.body as any)?.message;
return {
valid: false,
error: message ?? 'Invalid credentials',
};
}
},
});
export type JiraAuth = AppConnectionValueForAuthProperty<typeof jiraCloudAuth>;

View File

@@ -0,0 +1,83 @@
{
"Jira Cloud": "Jira Cloud",
"Issue tracking and project management": "Issue tracking and project management",
"Instance URL": "Instance URL",
"Email": "Email",
"API Token": "API Token",
"The link of your Jira instance (e.g https://example.atlassian.net)": "The link of your Jira instance (e.g https://example.atlassian.net)",
"The email you use to login to Jira": "The email you use to login to Jira",
"Your Jira API Token": "Your Jira API Token",
"\nYou can generate your API token from:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ": "\nYou can generate your API token from:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ",
"Create Issue": "Create Issue",
"Update Issue": "Update Issue",
"Find User": "Find User",
"Search Issues": "Search Issues",
"Assign Issue": "Assign Issue",
"Add Attachment to Issue": "Add Attachment to Issue",
"Get Issue Attachment": "Get Issue Attachment",
"Add Watcher to Issue": "Add Watcher to Issue",
"Add Issue Comment": "Add Issue Comment",
"Update Issue Comment": "Update Issue Comment",
"Link Issues": "Link Issues",
"List Issue Comments": "List Issue Comments",
"Delete Issue Comment": "Delete Issue Comment",
"Custom API Call": "Custom API Call",
"Creates a new issue in a project.": "Creates a new issue in a project.",
"Updates an existing issue.": "Updates an existing issue.",
"Finds an existing user.": "Finds an existing user.",
"Search for issues with JQL": "Search for issues with JQL",
"Assigns an issue to a user.": "Assigns an issue to a user.",
"Adds an attachment to an issue.": "Adds an attachment to an issue.",
"Retrieves an attachment from an issue.": "Retrieves an attachment from an issue.",
"Adds a new watcher to an issue.": "Adds a new watcher to an issue.",
"Adds a comment to an issue.": "Adds a comment to an issue.",
"Updates a comment to a specific issue.": "Updates a comment to a specific issue.",
"Creates a link between two issues.": "Creates a link between two issues.",
"Returns all comments for an issue.": "Returns all comments for an issue.",
"Deletes a comment on a specific issue.": "Deletes a comment on a specific issue.",
"Make a custom API call to a specific endpoint": "Make a custom API call to a specific endpoint",
"Project ID or Key": "Project ID or Key",
"Issue Type": "Issue Type",
"Fields": "Fields",
"Issue ID or Key": "Issue ID or Key",
"Status": "Status",
"Keyword": "Keyword",
"JQL": "JQL",
"Max Results": "Max Results",
"Sanitize JQL": "Sanitize JQL",
"Assignee": "Assignee",
"Attachment": "Attachment",
"Attachment ID": "Attachment ID",
"User": "User",
"Comment Body": "Comment Body",
"Comment ID": "Comment ID",
"First Issue": "First Issue",
"Link Type": "Link Type",
"Second Issue": "Second Issue",
"Order By": "Order By",
"Limit": "Limit",
"Method": "Method",
"Headers": "Headers",
"Query Parameters": "Query Parameters",
"Body": "Body",
"No Error on Failure": "No Error on Failure",
"Timeout (in seconds)": "Timeout (in seconds)",
"The JQL query to use in the search": "The JQL query to use in the search",
"Maximum number of results": "Maximum number of results",
"Authorization headers are injected automatically from your connection.": "Authorization headers are injected automatically from your connection.",
"Created (Descending)": "Created (Descending)",
"Created (Ascending)": "Created (Ascending)",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD",
"New Issue": "New Issue",
"Updated Issue": "Updated Issue",
"Updated Issue Status": "Updated Issue Status",
"Triggers when a new issue is created": "Triggers when a new issue is created",
"Triggers when an issue is updated": "Triggers when an issue is updated",
"Triggers when an issue status is updated": "Triggers when an issue status is updated",
"Use to filter issues watched": "Use to filter issues watched"
}

View File

@@ -0,0 +1,90 @@
{
"Issue tracking and project management": "Fehlerverfolgung und Projektmanagement",
"Instance URL": "Instanz URL",
"Email": "E-Mail",
"API Token": "API Token",
"The link of your Jira instance (e.g https://example.atlassian.net)": "Der Link deiner Jira-Instanz (z.B. https://example.atlassian.net)",
"The email you use to login to Jira": "Die E-Mail-Adresse, mit der Sie sich bei Jira anmelden",
"Your Jira API Token": "Ihr Jira API Token",
"\nYou can generate your API token from:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ": "\nDu kannst deinen API-Token generieren von:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ",
"Create Issue": "Ticket erstellen",
"Update Issue": "Problem aktualisieren",
"Find User": "Benutzer finden",
"Search Issues": "Probleme durchsuchen",
"Assign Issue": "Ticket zuweisen",
"Add Attachment to Issue": "Anhang zum Thema hinzufügen",
"Get Issue Attachment": "Fehleranhang erhalten",
"Add Watcher to Issue": "Beobachter zum Problem hinzufügen",
"Add Issue Comment": "Ticket Kommentar hinzufügen",
"Update Issue Comment": "Ticket Kommentar aktualisieren",
"Link Issues": "Tickets verknüpfen",
"List Issue Comments": "Ticket-Kommentare auflisten",
"Delete Issue Comment": "Ticket-Kommentar löschen",
"Markdown to Jira format": "Markdown zum Jira-Format",
"Custom API Call": "Eigener API-Aufruf",
"Creates a new issue in a project.": "Erstellt ein neues Problem in einem Projekt.",
"Updates an existing issue.": "Aktualisiert ein existierendes Problem.",
"Finds an existing user.": "Findet einen existierenden Benutzer.",
"Search for issues with JQL": "Suche nach Problemen mit JQL",
"Assigns an issue to a user.": "Weist einem Benutzer ein Problem zu.",
"Adds an attachment to an issue.": "Fügt einen Anhang zu einem Problem hinzu.",
"Retrieves an attachment from an issue.": "Ruft einen Anhang von einem Problem ab.",
"Adds a new watcher to an issue.": "Fügt einen neuen Beobachter zu einem Problem hinzu.",
"Adds a comment to an issue.": "Fügt einen Kommentar zu einem Problem hinzu.",
"Updates a comment to a specific issue.": "Aktualisiert einen Kommentar zu einem bestimmten Problem.",
"Creates a link between two issues.": "Erzeugt eine Verbindung zwischen zwei Problemen.",
"Returns all comments for an issue.": "Gibt alle Kommentare für ein Problem zurück.",
"Deletes a comment on a specific issue.": "Löscht einen Kommentar zu einem bestimmten Thema.",
"Convert Markdown-formatted text to Jira's ADF syntax for use in comments and descriptions etc": "Konvertieren Sie Markdown-formatierten Text in Jiras ADF-Syntax für Kommentare und Beschreibungen usw.",
"Make a custom API call to a specific endpoint": "Einen benutzerdefinierten API-Aufruf an einen bestimmten Endpunkt machen",
"Project ID or Key": "Projekt-ID oder Schlüssel",
"Issue Type": "Ticket-Typ",
"Fields": "Felder",
"Fields in JSON Atlassian Document Format": "Felder im JSON Atlassian Document Format",
"Issue ID or Key": "Ticket-ID oder Schlüssel",
"Status": "Status",
"Keyword": "Keyword",
"JQL": "JQL",
"Max Results": "Maximale Ergebnisse",
"Sanitize JQL": "JQL sanitizen",
"Assignee": "Assignee",
"Attachment": "Anhang",
"Attachment ID": "Anhang-ID",
"User": "Benutzer",
"Comment Body": "Kommentar-Text",
"Comment is in JSON Atlassian Document Format": "Kommentar ist im JSON Atlassian Document Format",
"Comment ID": "Kommentar-ID",
"First Issue": "Erster Fall",
"Link Type": "Link Type",
"Second Issue": "Zweiter Fall",
"Order By": "Sortieren nach",
"Limit": "Limit",
"Markdown text": "Markdown Text",
"Method": "Methode",
"Headers": "Kopfzeilen",
"Query Parameters": "Abfrageparameter",
"Body": "Körper",
"Response is Binary ?": "Antwort ist binär?",
"No Error on Failure": "Kein Fehler bei Fehler",
"Timeout (in seconds)": "Timeout (in Sekunden)",
"https://developer.atlassian.com/cloud/jira/platform/apis/document/structure": "https://developer.atlassian.com/cloud/jira/platform/apis/document/structure",
"The JQL query to use in the search": "Die JQL-Abfrage für die Suche",
"Maximum number of results": "Maximale Anzahl der Ergebnisse",
"Authorization headers are injected automatically from your connection.": "Autorisierungs-Header werden automatisch von Ihrer Verbindung injiziert.",
"Enable for files like PDFs, images, etc..": "Aktivieren für Dateien wie PDFs, Bilder, etc..",
"Created (Descending)": "Erstellt (absteigend)",
"Created (Ascending)": "Erstellt (absteigend)",
"GET": "ERHALTEN",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "LÖSCHEN",
"HEAD": "HEAD",
"New Issue": "Neuer Fall",
"Updated Issue": "Aktualisierter Fall",
"Updated Issue Status": "Aktualisierter Ticket Status",
"Triggers when a new issue is created": "Wird ausgelöst, wenn ein neues Problem erstellt wird",
"Triggers when an issue is updated": "Wird ausgelöst, wenn ein Problem aktualisiert wird",
"Triggers when an issue status is updated": "Wird ausgelöst, wenn ein Ticket Status aktualisiert wird",
"Use to filter issues watched": "Verwende es, um beobachtete Probleme zu filtern"
}

View File

@@ -0,0 +1,90 @@
{
"Issue tracking and project management": "Seguimiento de incidencias y gestión de proyectos",
"Instance URL": "URL de instancia",
"Email": "E-mail",
"API Token": "API Token",
"The link of your Jira instance (e.g https://example.atlassian.net)": "El enlace de su instancia de Jira (ej. https://example.atlassian.net)",
"The email you use to login to Jira": "El correo electrónico que utiliza para iniciar sesión en Jira",
"Your Jira API Token": "Tu token de API de Jira",
"\nYou can generate your API token from:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ": "\nYou can generate your API token from:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ",
"Create Issue": "Crear asunto",
"Update Issue": "Actualizar problema",
"Find User": "Buscar usuario",
"Search Issues": "Buscar problemas",
"Assign Issue": "Asignar asunto",
"Add Attachment to Issue": "Añadir adjunto al problema",
"Get Issue Attachment": "Obtener archivo adjunto del problema",
"Add Watcher to Issue": "Añadir Watcher al asunto",
"Add Issue Comment": "Añadir comentario del asunto",
"Update Issue Comment": "Actualizar comentario del problema",
"Link Issues": "Problemas de enlace",
"List Issue Comments": "Listar comentarios del problema",
"Delete Issue Comment": "Eliminar Comentario del Problema",
"Markdown to Jira format": "Formato Markdown a Jira",
"Custom API Call": "Llamada API personalizada",
"Creates a new issue in a project.": "Crea un nuevo asunto en un proyecto.",
"Updates an existing issue.": "Actualiza un problema existente.",
"Finds an existing user.": "Encuentra un usuario existente.",
"Search for issues with JQL": "Buscar problemas con JQL",
"Assigns an issue to a user.": "Asigna un problema a un usuario.",
"Adds an attachment to an issue.": "Añade un archivo adjunto a un problema.",
"Retrieves an attachment from an issue.": "Recuperar un archivo adjunto de un problema.",
"Adds a new watcher to an issue.": "Añade un nuevo observador a un problema.",
"Adds a comment to an issue.": "Añade un comentario a un problema.",
"Updates a comment to a specific issue.": "Actualiza un comentario a un asunto específico.",
"Creates a link between two issues.": "Crea un enlace entre dos cuestiones.",
"Returns all comments for an issue.": "Devuelve todos los comentarios para un problema.",
"Deletes a comment on a specific issue.": "Elimina un comentario sobre un asunto específico.",
"Convert Markdown-formatted text to Jira's ADF syntax for use in comments and descriptions etc": "Convierte el texto con formato Markdown a la sintaxis ADF de Jira para usarlo en comentarios y descripciones, etc.",
"Make a custom API call to a specific endpoint": "Hacer una llamada API personalizada a un extremo específico",
"Project ID or Key": "ID o clave del proyecto",
"Issue Type": "Tipo de asunto",
"Fields": "Campos",
"Fields in JSON Atlassian Document Format": "Campos en formato JSON Atlassian Document",
"Issue ID or Key": "Emitir ID o Clave",
"Status": "Estado",
"Keyword": "Keyword",
"JQL": "JQL",
"Max Results": "Resultados máximos",
"Sanitize JQL": "Sanitizar JQL",
"Assignee": "Assignee",
"Attachment": "Adjunto",
"Attachment ID": "ID de adjunto",
"User": "Usuario",
"Comment Body": "Comentar cuerpo",
"Comment is in JSON Atlassian Document Format": "El comentario está en formato JSON Atlassian Document",
"Comment ID": "Comentario ID",
"First Issue": "Primer asunto",
"Link Type": "Link Type",
"Second Issue": "Segundo problema",
"Order By": "Ordenar por",
"Limit": "Límite",
"Markdown text": "Texto Markdown",
"Method": "Método",
"Headers": "Encabezados",
"Query Parameters": "Parámetros de consulta",
"Body": "Cuerpo",
"Response is Binary ?": "¿Respuesta es binaria?",
"No Error on Failure": "No hay ningún error en fallo",
"Timeout (in seconds)": "Tiempo de espera (en segundos)",
"https://developer.atlassian.com/cloud/jira/platform/apis/document/structure": "https://developer.atlassian.com/cloud/jira/platform/apis/document/structure",
"The JQL query to use in the search": "La consulta JQL a usar en la búsqueda",
"Maximum number of results": "Número máximo de resultados",
"Authorization headers are injected automatically from your connection.": "Las cabeceras de autorización se inyectan automáticamente desde tu conexión.",
"Enable for files like PDFs, images, etc..": "Activar para archivos como PDFs, imágenes, etc.",
"Created (Descending)": "Creado (Descendente)",
"Created (Ascending)": "Creado (Ascendente)",
"GET": "RECOGER",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "BORRAR",
"HEAD": "LIMPIO",
"New Issue": "Nuevo problema",
"Updated Issue": "Problema actualizado",
"Updated Issue Status": "Estado del asunto actualizado",
"Triggers when a new issue is created": "Desencadena cuando se crea un nuevo problema",
"Triggers when an issue is updated": "Dispara cuando se actualiza un problema",
"Triggers when an issue status is updated": "Dispara cuando se actualiza el estado de una incidencia",
"Use to filter issues watched": "Utilizar para filtrar problemas vistos"
}

View File

@@ -0,0 +1,90 @@
{
"Issue tracking and project management": "Suivi des anomalies et gestion des projets",
"Instance URL": "URL de l'instance",
"Email": "Courriel",
"API Token": "API Token",
"The link of your Jira instance (e.g https://example.atlassian.net)": "Le lien de votre instance Jira (par exemple https://example.atlassian.net)",
"The email you use to login to Jira": "L'e-mail que vous utilisez pour vous connecter à Jira",
"Your Jira API Token": "Votre jeton API Jira",
"\nYou can generate your API token from:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ": "\nVous pouvez générer votre jeton API depuis :\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ",
"Create Issue": "Créer un problème",
"Update Issue": "Mettre à jour le problème",
"Find User": "Trouver un utilisateur",
"Search Issues": "Rechercher des problèmes",
"Assign Issue": "Assigner un ticket",
"Add Attachment to Issue": "Ajouter une pièce jointe au ticket",
"Get Issue Attachment": "Récupérer la pièce jointe",
"Add Watcher to Issue": "Ajouter un observateur au ticket",
"Add Issue Comment": "Ajouter un commentaire à un problème",
"Update Issue Comment": "Commentaire de mise à jour du problème",
"Link Issues": "Lier les tickets",
"List Issue Comments": "Lister les commentaires de problèmes",
"Delete Issue Comment": "Supprimer le commentaire du problème",
"Markdown to Jira format": "Format Markdown vers Jira",
"Custom API Call": "Appel API personnalisé",
"Creates a new issue in a project.": "Crée un nouveau ticket dans un projet.",
"Updates an existing issue.": "Met à jour un ticket existant.",
"Finds an existing user.": "Trouve un utilisateur existant.",
"Search for issues with JQL": "Rechercher des problèmes avec JQL",
"Assigns an issue to a user.": "Assigne un ticket à un utilisateur.",
"Adds an attachment to an issue.": "Ajoute une pièce jointe à un ticket.",
"Retrieves an attachment from an issue.": "Récupère une pièce jointe à partir d'un ticket.",
"Adds a new watcher to an issue.": "Ajoute un nouveau observateur à un ticket.",
"Adds a comment to an issue.": "Ajoute un commentaire à un ticket.",
"Updates a comment to a specific issue.": "Met à jour un commentaire à un problème spécifique.",
"Creates a link between two issues.": "Crée un lien entre deux tickets.",
"Returns all comments for an issue.": "Renvoie tous les commentaires pour un ticket.",
"Deletes a comment on a specific issue.": "Supprime un commentaire sur un problème spécifique.",
"Convert Markdown-formatted text to Jira's ADF syntax for use in comments and descriptions etc": "Convertir le texte formaté par Markdown en syntaxe ADF de Jira pour l'utiliser dans les commentaires et descriptions etc",
"Make a custom API call to a specific endpoint": "Passez un appel API personnalisé à un point de terminaison spécifique",
"Project ID or Key": "ID ou clé du projet",
"Issue Type": "Type de problème",
"Fields": "Champs",
"Fields in JSON Atlassian Document Format": "Champs au format JSON Atlassian Document",
"Issue ID or Key": "ID du ticket ou clé",
"Status": "Statut",
"Keyword": "Keyword",
"JQL": "JQL",
"Max Results": "Nombre maximum de résultats",
"Sanitize JQL": "Assainissement JQL",
"Assignee": "Assignee",
"Attachment": "Pièce jointe",
"Attachment ID": "ID de la pièce jointe",
"User": "Utilisateur",
"Comment Body": "Corps du commentaire",
"Comment is in JSON Atlassian Document Format": "Le commentaire est en format JSON Atlassian Document",
"Comment ID": "ID du commentaire",
"First Issue": "Premier problème",
"Link Type": "Link Type",
"Second Issue": "Deuxième problème",
"Order By": "Trier par",
"Limit": "Limite",
"Markdown text": "Texte Markdown",
"Method": "Méthode",
"Headers": "En-têtes",
"Query Parameters": "Paramètres de requête",
"Body": "Corps",
"Response is Binary ?": "La réponse est Binaire ?",
"No Error on Failure": "Aucune erreur en cas d'échec",
"Timeout (in seconds)": "Délai d'attente (en secondes)",
"https://developer.atlassian.com/cloud/jira/platform/apis/document/structure": "https://developer.atlassian.com/cloud/jira/platform/apis/document/structure",
"The JQL query to use in the search": "La requête JQL à utiliser dans la recherche",
"Maximum number of results": "Nombre maximum de résultats",
"Authorization headers are injected automatically from your connection.": "Les en-têtes d'autorisation sont injectés automatiquement à partir de votre connexion.",
"Enable for files like PDFs, images, etc..": "Activer pour les fichiers comme les PDFs, les images, etc.",
"Created (Descending)": "Créé (Descendant)",
"Created (Ascending)": "Créé (Ascendant)",
"GET": "OBTENIR",
"POST": "POSTER",
"PATCH": "PATCH",
"PUT": "EFFACER",
"DELETE": "SUPPRIMER",
"HEAD": "TÊTE",
"New Issue": "Nouveau problème",
"Updated Issue": "Problème mis à jour",
"Updated Issue Status": "Statut du problème mis à jour",
"Triggers when a new issue is created": "Déclenche quand un nouveau ticket est créé",
"Triggers when an issue is updated": "Déclenche quand un problème est mis à jour",
"Triggers when an issue status is updated": "Déclenche quand un statut de ticket est mis à jour",
"Use to filter issues watched": "Utiliser pour filtrer les problèmes vus"
}

View File

@@ -0,0 +1,83 @@
{
"Jira Cloud": "Jira Cloud",
"Issue tracking and project management": "Issue tracking and project management",
"Instance URL": "Instance URL",
"Email": "Email",
"API Token": "API Token",
"The link of your Jira instance (e.g https://example.atlassian.net)": "The link of your Jira instance (e.g https://example.atlassian.net)",
"The email you use to login to Jira": "The email you use to login to Jira",
"Your Jira API Token": "Your Jira API Token",
"\nYou can generate your API token from:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ": "\nYou can generate your API token from:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ",
"Create Issue": "Create Issue",
"Update Issue": "Update Issue",
"Find User": "Find User",
"Search Issues": "Search Issues",
"Assign Issue": "Assign Issue",
"Add Attachment to Issue": "Add Attachment to Issue",
"Get Issue Attachment": "Get Issue Attachment",
"Add Watcher to Issue": "Add Watcher to Issue",
"Add Issue Comment": "Add Issue Comment",
"Update Issue Comment": "Update Issue Comment",
"Link Issues": "Link Issues",
"List Issue Comments": "List Issue Comments",
"Delete Issue Comment": "Delete Issue Comment",
"Custom API Call": "Custom API Call",
"Creates a new issue in a project.": "Creates a new issue in a project.",
"Updates an existing issue.": "Updates an existing issue.",
"Finds an existing user.": "Finds an existing user.",
"Search for issues with JQL": "Search for issues with JQL",
"Assigns an issue to a user.": "Assigns an issue to a user.",
"Adds an attachment to an issue.": "Adds an attachment to an issue.",
"Retrieves an attachment from an issue.": "Retrieves an attachment from an issue.",
"Adds a new watcher to an issue.": "Adds a new watcher to an issue.",
"Adds a comment to an issue.": "Adds a comment to an issue.",
"Updates a comment to a specific issue.": "Updates a comment to a specific issue.",
"Creates a link between two issues.": "Creates a link between two issues.",
"Returns all comments for an issue.": "Returns all comments for an issue.",
"Deletes a comment on a specific issue.": "Deletes a comment on a specific issue.",
"Make a custom API call to a specific endpoint": "Make a custom API call to a specific endpoint",
"Project ID or Key": "Project ID or Key",
"Issue Type": "Issue Type",
"Fields": "Fields",
"Issue ID or Key": "Issue ID or Key",
"Status": "Status",
"Keyword": "Keyword",
"JQL": "JQL",
"Max Results": "Max Results",
"Sanitize JQL": "Sanitize JQL",
"Assignee": "Assignee",
"Attachment": "Attachment",
"Attachment ID": "Attachment ID",
"User": "User",
"Comment Body": "Comment Body",
"Comment ID": "Comment ID",
"First Issue": "First Issue",
"Link Type": "Link Type",
"Second Issue": "Second Issue",
"Order By": "Order By",
"Limit": "Limit",
"Method": "Method",
"Headers": "Headers",
"Query Parameters": "Query Parameters",
"Body": "Body",
"No Error on Failure": "No Error on Failure",
"Timeout (in seconds)": "Timeout (in seconds)",
"The JQL query to use in the search": "The JQL query to use in the search",
"Maximum number of results": "Maximum number of results",
"Authorization headers are injected automatically from your connection.": "Authorization headers are injected automatically from your connection.",
"Created (Descending)": "Created (Descending)",
"Created (Ascending)": "Created (Ascending)",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD",
"New Issue": "New Issue",
"Updated Issue": "Updated Issue",
"Updated Issue Status": "Updated Issue Status",
"Triggers when a new issue is created": "Triggers when a new issue is created",
"Triggers when an issue is updated": "Triggers when an issue is updated",
"Triggers when an issue status is updated": "Triggers when an issue status is updated",
"Use to filter issues watched": "Use to filter issues watched"
}

View File

@@ -0,0 +1,83 @@
{
"Jira Cloud": "Jira Cloud",
"Issue tracking and project management": "Issue tracking and project management",
"Instance URL": "Instance URL",
"Email": "Email",
"API Token": "API Token",
"The link of your Jira instance (e.g https://example.atlassian.net)": "The link of your Jira instance (e.g https://example.atlassian.net)",
"The email you use to login to Jira": "The email you use to login to Jira",
"Your Jira API Token": "Your Jira API Token",
"\nYou can generate your API token from:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ": "\nYou can generate your API token from:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ",
"Create Issue": "Create Issue",
"Update Issue": "Update Issue",
"Find User": "Find User",
"Search Issues": "Search Issues",
"Assign Issue": "Assign Issue",
"Add Attachment to Issue": "Add Attachment to Issue",
"Get Issue Attachment": "Get Issue Attachment",
"Add Watcher to Issue": "Add Watcher to Issue",
"Add Issue Comment": "Add Issue Comment",
"Update Issue Comment": "Update Issue Comment",
"Link Issues": "Link Issues",
"List Issue Comments": "List Issue Comments",
"Delete Issue Comment": "Delete Issue Comment",
"Custom API Call": "Custom API Call",
"Creates a new issue in a project.": "Creates a new issue in a project.",
"Updates an existing issue.": "Updates an existing issue.",
"Finds an existing user.": "Finds an existing user.",
"Search for issues with JQL": "Search for issues with JQL",
"Assigns an issue to a user.": "Assigns an issue to a user.",
"Adds an attachment to an issue.": "Adds an attachment to an issue.",
"Retrieves an attachment from an issue.": "Retrieves an attachment from an issue.",
"Adds a new watcher to an issue.": "Adds a new watcher to an issue.",
"Adds a comment to an issue.": "Adds a comment to an issue.",
"Updates a comment to a specific issue.": "Updates a comment to a specific issue.",
"Creates a link between two issues.": "Creates a link between two issues.",
"Returns all comments for an issue.": "Returns all comments for an issue.",
"Deletes a comment on a specific issue.": "Deletes a comment on a specific issue.",
"Make a custom API call to a specific endpoint": "Make a custom API call to a specific endpoint",
"Project ID or Key": "Project ID or Key",
"Issue Type": "Issue Type",
"Fields": "Fields",
"Issue ID or Key": "Issue ID or Key",
"Status": "Status",
"Keyword": "Keyword",
"JQL": "JQL",
"Max Results": "Max Results",
"Sanitize JQL": "Sanitize JQL",
"Assignee": "Assignee",
"Attachment": "Attachment",
"Attachment ID": "Attachment ID",
"User": "User",
"Comment Body": "Comment Body",
"Comment ID": "Comment ID",
"First Issue": "First Issue",
"Link Type": "Link Type",
"Second Issue": "Second Issue",
"Order By": "Order By",
"Limit": "Limit",
"Method": "Method",
"Headers": "Headers",
"Query Parameters": "Query Parameters",
"Body": "Body",
"No Error on Failure": "No Error on Failure",
"Timeout (in seconds)": "Timeout (in seconds)",
"The JQL query to use in the search": "The JQL query to use in the search",
"Maximum number of results": "Maximum number of results",
"Authorization headers are injected automatically from your connection.": "Authorization headers are injected automatically from your connection.",
"Created (Descending)": "Created (Descending)",
"Created (Ascending)": "Created (Ascending)",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD",
"New Issue": "New Issue",
"Updated Issue": "Updated Issue",
"Updated Issue Status": "Updated Issue Status",
"Triggers when a new issue is created": "Triggers when a new issue is created",
"Triggers when an issue is updated": "Triggers when an issue is updated",
"Triggers when an issue status is updated": "Triggers when an issue status is updated",
"Use to filter issues watched": "Use to filter issues watched"
}

View File

@@ -0,0 +1,90 @@
{
"Issue tracking and project management": "課題の追跡とプロジェクト管理",
"Instance URL": "インスタンス URL",
"Email": "Eメールアドレス",
"API Token": "API Token",
"The link of your Jira instance (e.g https://example.atlassian.net)": "Jiraインスタンスのリンク (https://example.atlassian.net)",
"The email you use to login to Jira": "Jiraにログインするために使用するメールアドレス",
"Your Jira API Token": "Jira API トークン",
"\nYou can generate your API token from:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ": "\n以下からAPIトークンを生成できます:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ",
"Create Issue": "課題を作成",
"Update Issue": "問題を更新",
"Find User": "ユーザーを検索",
"Search Issues": "問題を検索",
"Assign Issue": "課題を割り当てる",
"Add Attachment to Issue": "課題に添付ファイルを追加",
"Get Issue Attachment": "課題の添付を取得",
"Add Watcher to Issue": "ワッチャーを課題に追加",
"Add Issue Comment": "課題コメントを追加",
"Update Issue Comment": "課題のコメントを更新",
"Link Issues": "問題をリンクする",
"List Issue Comments": "課題のコメントを一覧表示",
"Delete Issue Comment": "課題のコメントを削除",
"Markdown to Jira format": "Markdown to Jira format",
"Custom API Call": "カスタムAPI通話",
"Creates a new issue in a project.": "プロジェクトで新しい課題を作成します。",
"Updates an existing issue.": "既存の問題を更新します。",
"Finds an existing user.": "既存のユーザーを検索します。",
"Search for issues with JQL": "JQL での課題を検索",
"Assigns an issue to a user.": "ユーザに課題を割り当てます。",
"Adds an attachment to an issue.": "課題に添付ファイルを追加します。",
"Retrieves an attachment from an issue.": "課題から添付ファイルを取得します。",
"Adds a new watcher to an issue.": "新しいウォッチャーを課題に追加します。",
"Adds a comment to an issue.": "課題にコメントを追加します。",
"Updates a comment to a specific issue.": "特定の課題にコメントを更新します。",
"Creates a link between two issues.": "2つの課題間のリンクを作成します。",
"Returns all comments for an issue.": "課題のすべてのコメントを返します。",
"Deletes a comment on a specific issue.": "特定の課題に関するコメントを削除します。",
"Convert Markdown-formatted text to Jira's ADF syntax for use in comments and descriptions etc": "コメントや説明などに使用するために、Markdown形式のテキストをJiraのADF構文に変換する",
"Make a custom API call to a specific endpoint": "特定のエンドポイントへのカスタム API コールを実行します。",
"Project ID or Key": "プロジェクト ID またはキー",
"Issue Type": "課題の種類",
"Fields": "フィールド",
"Fields in JSON Atlassian Document Format": "JSON Atlassian ドキュメント形式のフィールド",
"Issue ID or Key": "課題 ID またはキー",
"Status": "ステータス",
"Keyword": "Keyword",
"JQL": "JQL",
"Max Results": "最大結果",
"Sanitize JQL": "JQL サニタイズ",
"Assignee": "Assignee",
"Attachment": "添付ファイル",
"Attachment ID": "添付ファイルID",
"User": "ユーザー",
"Comment Body": "コメント本文",
"Comment is in JSON Atlassian Document Format": "コメントは JSON Atlassian ドキュメントフォーマットにあります",
"Comment ID": "コメント ID",
"First Issue": "最初の課題",
"Link Type": "Link Type",
"Second Issue": "2号目",
"Order By": "並び替え",
"Limit": "制限",
"Markdown text": "Markdown テキスト",
"Method": "方法",
"Headers": "ヘッダー",
"Query Parameters": "クエリパラメータ",
"Body": "本文",
"Response is Binary ?": "応答はバイナリですか?",
"No Error on Failure": "失敗時にエラーはありません",
"Timeout (in seconds)": "タイムアウト(秒)",
"https://developer.atlassian.com/cloud/jira/platform/apis/document/structure": "https://developer.atlassian.com/cloud/jira/platform/apis/document/structure",
"The JQL query to use in the search": "検索で使用する JQL クエリ",
"Maximum number of results": "結果の最大数",
"Authorization headers are injected automatically from your connection.": "認証ヘッダは接続から自動的に注入されます。",
"Enable for files like PDFs, images, etc..": "PDF、画像などのファイルを有効にします。",
"Created (Descending)": "作成済み (降順)",
"Created (Ascending)": "作成されました (昇順)",
"GET": "取得",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "削除",
"HEAD": "頭",
"New Issue": "新しい課題",
"Updated Issue": "更新された課題",
"Updated Issue Status": "更新された課題ステータス",
"Triggers when a new issue is created": "新しい課題が作成されたときにトリガーする",
"Triggers when an issue is updated": "課題が更新されたときに発生する",
"Triggers when an issue status is updated": "課題の状態が更新されたときに発行されます",
"Use to filter issues watched": "監視されている課題をフィルタリングするために使用します"
}

View File

@@ -0,0 +1,90 @@
{
"Issue tracking and project management": "Probleem bijhouden en projectbeheer",
"Instance URL": "Aanleg URL",
"Email": "E-mail",
"API Token": "API Token",
"The link of your Jira instance (e.g https://example.atlassian.net)": "De link van uw Jira exemplaar (bijv. https://example.atlassian.net)",
"The email you use to login to Jira": "De e-mail die u gebruikt om in te loggen op Jira",
"Your Jira API Token": "Je Jira API Token",
"\nYou can generate your API token from:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ": "\nU kunt uw API-token genereren van:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ",
"Create Issue": "Probleem aanmaken",
"Update Issue": "Probleem bijwerken",
"Find User": "Gebruiker zoeken",
"Search Issues": "Problemen zoeken",
"Assign Issue": "Issue toewijzen",
"Add Attachment to Issue": "Bijlage toevoegen aan issue",
"Get Issue Attachment": "Verkrijg issue-bijlage",
"Add Watcher to Issue": "Voeg Watcher toe aan Issue",
"Add Issue Comment": "Issue Opmerking toevoegen",
"Update Issue Comment": "Wijzig probleemreactie",
"Link Issues": "Koppel problemen",
"List Issue Comments": "Issue reacties weergeven",
"Delete Issue Comment": "Issue Opmerking verwijderen",
"Markdown to Jira format": "Markdown naar Jira formaat",
"Custom API Call": "Custom API Call",
"Creates a new issue in a project.": "Maakt een nieuw issue aan in een project.",
"Updates an existing issue.": "Werkt een bestaand probleem bij.",
"Finds an existing user.": "Vindt een bestaande gebruiker.",
"Search for issues with JQL": "Zoek naar issues met JQL",
"Assigns an issue to a user.": "Wijst een issue toe aan een gebruiker.",
"Adds an attachment to an issue.": "Voegt een bijlage toe aan een probleem.",
"Retrieves an attachment from an issue.": "Haal een bijlage op van een probleem.",
"Adds a new watcher to an issue.": "Voegt een nieuwe watcher toe aan een probleem.",
"Adds a comment to an issue.": "Voegt een reactie toe aan een probleem.",
"Updates a comment to a specific issue.": "Werkt een opmerking bij een specifiek probleem.",
"Creates a link between two issues.": "Maakt een verband tussen twee problemen.",
"Returns all comments for an issue.": "Geeft als resultaat alle reacties op een probleem.",
"Deletes a comment on a specific issue.": "Verwijdert een reactie op een specifiek probleem.",
"Convert Markdown-formatted text to Jira's ADF syntax for use in comments and descriptions etc": "Converteer markdown-geformatteerde tekst naar Jira's ADF-syntaxis voor gebruik in commentaren en beschrijvingen enz",
"Make a custom API call to a specific endpoint": "Maak een aangepaste API call naar een specifiek eindpunt",
"Project ID or Key": "Project ID of sleutel",
"Issue Type": "Type probleem",
"Fields": "Velden",
"Fields in JSON Atlassian Document Format": "Velden in JSON Atlassian Document Formaat",
"Issue ID or Key": "Issue ID of sleutel",
"Status": "status",
"Keyword": "Keyword",
"JQL": "JQL",
"Max Results": "Max. aantal resultaten",
"Sanitize JQL": "Sanitiseer JQL",
"Assignee": "Assignee",
"Attachment": "Bijlage",
"Attachment ID": "Bijlage ID",
"User": "Gebruiker",
"Comment Body": "Commentaar inhoud",
"Comment is in JSON Atlassian Document Format": "Commentaar is in JSON Atlassian Document Formaat",
"Comment ID": "Commentaar ID",
"First Issue": "Eerste probleem",
"Link Type": "Link Type",
"Second Issue": "Tweede probleem",
"Order By": "Sorteer op",
"Limit": "Limiet",
"Markdown text": "Markdown tekst",
"Method": "Methode",
"Headers": "Kopteksten",
"Query Parameters": "Query parameters",
"Body": "Lichaam",
"Response is Binary ?": "Antwoord is binair?",
"No Error on Failure": "Geen fout bij fout",
"Timeout (in seconds)": "Time-out (in seconden)",
"https://developer.atlassian.com/cloud/jira/platform/apis/document/structure": "https://developer.atlassian.com/cloud/jira/platform/apis/document/structuur",
"The JQL query to use in the search": "De JQL query te gebruiken in de zoekopdracht",
"Maximum number of results": "Maximum aantal resultaten",
"Authorization headers are injected automatically from your connection.": "Autorisatie headers worden automatisch geïnjecteerd vanuit uw verbinding.",
"Enable for files like PDFs, images, etc..": "Inschakelen voor bestanden zoals PDF's, afbeeldingen etc..",
"Created (Descending)": "Gemaakt (aflopend)",
"Created (Ascending)": "Aangemaakt (Ascending)",
"GET": "KRIJG",
"POST": "POSTE",
"PATCH": "BEKIJK",
"PUT": "PUT",
"DELETE": "VERWIJDEREN",
"HEAD": "HOOFD",
"New Issue": "Nieuw probleem",
"Updated Issue": "Probleem bijgewerkt",
"Updated Issue Status": "Bijgewerkte probleemstatus",
"Triggers when a new issue is created": "Triggert wanneer een nieuw issue wordt aangemaakt",
"Triggers when an issue is updated": "Triggert wanneer een issue wordt bijgewerkt",
"Triggers when an issue status is updated": "Triggert wanneer een issuestatus wordt bijgewerkt",
"Use to filter issues watched": "Gebruik om problemen te filteren"
}

View File

@@ -0,0 +1,90 @@
{
"Issue tracking and project management": "Acompanhamento de problemas e gerenciamento de projetos",
"Instance URL": "Instância URL",
"Email": "e-mail",
"API Token": "API Token",
"The link of your Jira instance (e.g https://example.atlassian.net)": "O link da sua instância do Jira (ex: https://example.atlassian.net)",
"The email you use to login to Jira": "O e-mail que você usa para acessar o Jira",
"Your Jira API Token": "Token de API Jira",
"\nYou can generate your API token from:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ": "\nVocê pode gerar o seu token de API em:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ",
"Create Issue": "Criar issue",
"Update Issue": "Atualizar issue",
"Find User": "Localizar usuário",
"Search Issues": "Pesquisar Problemas",
"Assign Issue": "Atribuir Issue",
"Add Attachment to Issue": "Adicionar anexo à issue",
"Get Issue Attachment": "Obter Anexo da Solicitação",
"Add Watcher to Issue": "Adicionar Watcher à Issue",
"Add Issue Comment": "Adicionar comentário à issue",
"Update Issue Comment": "Atualizar comentário da issue",
"Link Issues": "Ligar issues",
"List Issue Comments": "Listar Comentários da Solicitação",
"Delete Issue Comment": "Excluir comentário da issue",
"Markdown to Jira format": "Formato Jira Markdown",
"Custom API Call": "Chamada de API personalizada",
"Creates a new issue in a project.": "Cria uma nova issue em um projeto.",
"Updates an existing issue.": "Atualiza uma issue existente.",
"Finds an existing user.": "Localiza um usuário existente.",
"Search for issues with JQL": "Pesquisar por problemas com JQL",
"Assigns an issue to a user.": "Atribui uma issue a um usuário.",
"Adds an attachment to an issue.": "Adiciona um anexo a uma issue.",
"Retrieves an attachment from an issue.": "Recupera um anexo de uma issue.",
"Adds a new watcher to an issue.": "Adiciona um novo observador a uma issue.",
"Adds a comment to an issue.": "Adiciona um comentário a uma issue.",
"Updates a comment to a specific issue.": "Atualiza um comentário para uma issue específica.",
"Creates a link between two issues.": "Cria uma ligação entre duas issues.",
"Returns all comments for an issue.": "Retorna todos os comentários para uma issue.",
"Deletes a comment on a specific issue.": "Exclui um comentário em uma issue específica.",
"Convert Markdown-formatted text to Jira's ADF syntax for use in comments and descriptions etc": "Converter texto formatado em Markdown-formatado para sintaxe ADF da Jaria para uso em comentários e descrições, etc.",
"Make a custom API call to a specific endpoint": "Faça uma chamada de API personalizada para um ponto de extremidade específico",
"Project ID or Key": "ID ou Chave do Projeto",
"Issue Type": "Tipo de problema",
"Fields": "campos",
"Fields in JSON Atlassian Document Format": "Campos no formato de documento JSON Atlassian",
"Issue ID or Key": "ID ou Chave de Problema",
"Status": "Estado",
"Keyword": "Keyword",
"JQL": "QEQ",
"Max Results": "Resultados no Máx.",
"Sanitize JQL": "JQL Sanitizado",
"Assignee": "Assignee",
"Attachment": "Anexo",
"Attachment ID": "ID do anexo",
"User": "Usuário",
"Comment Body": "Corpo do Comentário",
"Comment is in JSON Atlassian Document Format": "Comentário está em formato de documento JSON Atlassian",
"Comment ID": "ID do comentário",
"First Issue": "Primeira Solicitação",
"Link Type": "Link Type",
"Second Issue": "Segunda questão",
"Order By": "Ordenar Por",
"Limit": "Limitar",
"Markdown text": "Texto Markdown",
"Method": "Método",
"Headers": "Cabeçalhos",
"Query Parameters": "Parâmetros da consulta",
"Body": "Conteúdo",
"Response is Binary ?": "A resposta é binária ?",
"No Error on Failure": "Nenhum erro no Failure",
"Timeout (in seconds)": "Tempo limite (em segundos)",
"https://developer.atlassian.com/cloud/jira/platform/apis/document/structure": "https://developer.atlassian.com/cloud/jira/platform/apis/document/structure",
"The JQL query to use in the search": "A consulta JQL a ser usada na busca",
"Maximum number of results": "Número máximo de resultados",
"Authorization headers are injected automatically from your connection.": "Os cabeçalhos de autorização são inseridos automaticamente a partir da sua conexão.",
"Enable for files like PDFs, images, etc..": "Habilitar para arquivos como PDFs, imagens, etc..",
"Created (Descending)": "Criado (decrescente)",
"Created (Ascending)": "Criado (Ascendente)",
"GET": "OBTER",
"POST": "POSTAR",
"PATCH": "COMPRAR",
"PUT": "COLOCAR",
"DELETE": "EXCLUIR",
"HEAD": "CABEÇA",
"New Issue": "Nova issue",
"Updated Issue": "Issue atualizada",
"Updated Issue Status": "Status atualizado da tarefa",
"Triggers when a new issue is created": "Dispara quando uma nova issue é criada",
"Triggers when an issue is updated": "Dispara quando uma issue é atualizada",
"Triggers when an issue status is updated": "Aciona quando um status de issue é atualizado",
"Use to filter issues watched": "Use para filtrar issues assistidas"
}

View File

@@ -0,0 +1,83 @@
{
"Jira Cloud": "Облако Jira",
"Issue tracking and project management": "Отслеживание проблем и управление проектами",
"Instance URL": "URL экземпляра",
"Email": "Почта",
"API Token": "API Token",
"The link of your Jira instance (e.g https://example.atlassian.net)": "Ссылка вашего экземпляра Jira (например https://example.atlassian.net)",
"The email you use to login to Jira": "Электронная почта, которую вы используете для входа в Jira",
"Your Jira API Token": "Ваш Jira API токен",
"\nYou can generate your API token from:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ": "\nВы можете сгенерировать ваш API токен из:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ",
"Create Issue": "Создать запрос",
"Update Issue": "Проблема с обновлением",
"Find User": "Найти пользователя",
"Search Issues": "Поиск проблем",
"Assign Issue": "Назначить задачу",
"Add Attachment to Issue": "Добавить вложение в запрос",
"Get Issue Attachment": "Получить вложение задач",
"Add Watcher to Issue": "Добавить наблюдателя к проблеме",
"Add Issue Comment": "Добавить комментарий проблемы",
"Update Issue Comment": "Комментарий об ошибке обновления",
"Link Issues": "Ссылка на Замечания",
"List Issue Comments": "Комментарии к выпуску",
"Delete Issue Comment": "Удалить комментарий к проблеме",
"Custom API Call": "Пользовательский вызов API",
"Creates a new issue in a project.": "Создает новый запрос в проекте.",
"Updates an existing issue.": "Обновляет существующую проблему.",
"Finds an existing user.": "Поиск существующего пользователя.",
"Search for issues with JQL": "Поиск проблем с JQL",
"Assigns an issue to a user.": "Присвоить запрос пользователю.",
"Adds an attachment to an issue.": "Добавляет вложение к задаче.",
"Retrieves an attachment from an issue.": "Извлекает вложение из проблемы.",
"Adds a new watcher to an issue.": "Добавляет нового наблюдателя к задаче.",
"Adds a comment to an issue.": "Добавляет комментарий к проблеме.",
"Updates a comment to a specific issue.": "Обновляет комментарий к конкретной проблеме.",
"Creates a link between two issues.": "Создает связь между двумя задачами.",
"Returns all comments for an issue.": "Возвращает все комментарии к проблеме.",
"Deletes a comment on a specific issue.": "Удаляет комментарий к конкретной задаче.",
"Make a custom API call to a specific endpoint": "Сделать пользовательский API вызов к определенной конечной точке",
"Project ID or Key": "ID проекта или ключ",
"Issue Type": "Тип задачи",
"Fields": "Поля",
"Issue ID or Key": "ID задачи или ключ",
"Status": "Статус",
"Keyword": "Keyword",
"JQL": "JQL",
"Max Results": "Макс. результатов",
"Sanitize JQL": "Санитизировать JQL",
"Assignee": "Assignee",
"Attachment": "Вложение",
"Attachment ID": "ID вложения",
"User": "Пользователь",
"Comment Body": "Тело комментария",
"Comment ID": "ID комментария",
"First Issue": "Первая задача",
"Link Type": "Link Type",
"Second Issue": "Вторая задача",
"Order By": "Сортировать по",
"Limit": "Лимит",
"Method": "Метод",
"Headers": "Заголовки",
"Query Parameters": "Параметры запроса",
"Body": "Тело",
"No Error on Failure": "Нет ошибок при ошибке",
"Timeout (in seconds)": "Таймаут (в секундах)",
"The JQL query to use in the search": "JQL запрос для использования в поиске",
"Maximum number of results": "Максимальное количество результатов",
"Authorization headers are injected automatically from your connection.": "Заголовки авторизации включаются автоматически из вашего соединения.",
"Created (Descending)": "Создано (по убыванию)",
"Created (Ascending)": "Создано (по возрастанию)",
"GET": "ПОЛУЧИТЬ",
"POST": "ПОСТ",
"PATCH": "ПАТЧ",
"PUT": "ПОКУПИТЬ",
"DELETE": "УДАЛИТЬ",
"HEAD": "HEAD",
"New Issue": "Новая задача",
"Updated Issue": "Задача обновлена",
"Updated Issue Status": "Статус задачи обновлен",
"Triggers when a new issue is created": "Триггеры при создании новой задачи",
"Triggers when an issue is updated": "Триггеры при обновлении задачи",
"Triggers when an issue status is updated": "Триггеры при обновлении статуса задачи",
"Use to filter issues watched": "Использовать для фильтрации задач просмотрено"
}

View File

@@ -0,0 +1,90 @@
{
"Issue tracking and project management": "Issue tracking and project management",
"Instance URL": "Instance URL",
"Email": "Email",
"API Token": "API Token",
"The link of your Jira instance (e.g https://example.atlassian.net)": "The link of your Jira instance (e.g https://example.atlassian.net)",
"The email you use to login to Jira": "The email you use to login to Jira",
"Your Jira API Token": "Your Jira API Token",
"\nYou can generate your API token from:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ": "\nYou can generate your API token from:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ",
"Create Issue": "Create Issue",
"Update Issue": "Update Issue",
"Find User": "Find User",
"Search Issues": "Search Issues",
"Assign Issue": "Assign Issue",
"Add Attachment to Issue": "Add Attachment to Issue",
"Get Issue Attachment": "Get Issue Attachment",
"Add Watcher to Issue": "Add Watcher to Issue",
"Add Issue Comment": "Add Issue Comment",
"Update Issue Comment": "Update Issue Comment",
"Link Issues": "Link Issues",
"List Issue Comments": "List Issue Comments",
"Delete Issue Comment": "Delete Issue Comment",
"Markdown to Jira format": "Markdown to Jira format",
"Custom API Call": "Custom API Call",
"Creates a new issue in a project.": "Creates a new issue in a project.",
"Updates an existing issue.": "Updates an existing issue.",
"Finds an existing user.": "Finds an existing user.",
"Search for issues with JQL": "Search for issues with JQL",
"Assigns an issue to a user.": "Assigns an issue to a user.",
"Adds an attachment to an issue.": "Adds an attachment to an issue.",
"Retrieves an attachment from an issue.": "Retrieves an attachment from an issue.",
"Adds a new watcher to an issue.": "Adds a new watcher to an issue.",
"Adds a comment to an issue.": "Adds a comment to an issue.",
"Updates a comment to a specific issue.": "Updates a comment to a specific issue.",
"Creates a link between two issues.": "Creates a link between two issues.",
"Returns all comments for an issue.": "Returns all comments for an issue.",
"Deletes a comment on a specific issue.": "Deletes a comment on a specific issue.",
"Convert Markdown-formatted text to Jira's ADF syntax for use in comments and descriptions etc": "Convert Markdown-formatted text to Jira's ADF syntax for use in comments and descriptions etc",
"Make a custom API call to a specific endpoint": "Make a custom API call to a specific endpoint",
"Project ID or Key": "Project ID or Key",
"Issue Type": "Issue Type",
"Fields": "Fields",
"Fields in JSON Atlassian Document Format": "Fields in JSON Atlassian Document Format",
"Issue ID or Key": "Issue ID or Key",
"Status": "Status",
"Keyword": "Keyword",
"JQL": "JQL",
"Max Results": "Max Results",
"Sanitize JQL": "Sanitize JQL",
"Assignee": "Assignee",
"Attachment": "Attachment",
"Attachment ID": "Attachment ID",
"User": "User",
"Comment Body": "Comment Body",
"Comment is in JSON Atlassian Document Format": "Comment is in JSON Atlassian Document Format",
"Comment ID": "Comment ID",
"First Issue": "First Issue",
"Link Type": "Link Type",
"Second Issue": "Second Issue",
"Order By": "Order By",
"Limit": "Limit",
"Markdown text": "Markdown text",
"Method": "Method",
"Headers": "Headers",
"Query Parameters": "Query Parameters",
"Body": "Body",
"Response is Binary ?": "Response is Binary ?",
"No Error on Failure": "No Error on Failure",
"Timeout (in seconds)": "Timeout (in seconds)",
"https://developer.atlassian.com/cloud/jira/platform/apis/document/structure": "https://developer.atlassian.com/cloud/jira/platform/apis/document/structure",
"The JQL query to use in the search": "The JQL query to use in the search",
"Maximum number of results": "Maximum number of results",
"Authorization headers are injected automatically from your connection.": "Authorization headers are injected automatically from your connection.",
"Enable for files like PDFs, images, etc..": "Enable for files like PDFs, images, etc..",
"Created (Descending)": "Created (Descending)",
"Created (Ascending)": "Created (Ascending)",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD",
"New Issue": "New Issue",
"Updated Issue": "Updated Issue",
"Updated Issue Status": "Updated Issue Status",
"Triggers when a new issue is created": "Triggers when a new issue is created",
"Triggers when an issue is updated": "Triggers when an issue is updated",
"Triggers when an issue status is updated": "Triggers when an issue status is updated",
"Use to filter issues watched": "Use to filter issues watched"
}

View File

@@ -0,0 +1,83 @@
{
"Jira Cloud": "Jira Cloud",
"Issue tracking and project management": "Issue tracking and project management",
"Instance URL": "Instance URL",
"Email": "Email",
"API Token": "API Token",
"The link of your Jira instance (e.g https://example.atlassian.net)": "The link of your Jira instance (e.g https://example.atlassian.net)",
"The email you use to login to Jira": "The email you use to login to Jira",
"Your Jira API Token": "Your Jira API Token",
"\nYou can generate your API token from:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ": "\nYou can generate your API token from:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ",
"Create Issue": "Create Issue",
"Update Issue": "Update Issue",
"Find User": "Find User",
"Search Issues": "Search Issues",
"Assign Issue": "Assign Issue",
"Add Attachment to Issue": "Add Attachment to Issue",
"Get Issue Attachment": "Get Issue Attachment",
"Add Watcher to Issue": "Add Watcher to Issue",
"Add Issue Comment": "Add Issue Comment",
"Update Issue Comment": "Update Issue Comment",
"Link Issues": "Link Issues",
"List Issue Comments": "List Issue Comments",
"Delete Issue Comment": "Delete Issue Comment",
"Custom API Call": "Custom API Call",
"Creates a new issue in a project.": "Creates a new issue in a project.",
"Updates an existing issue.": "Updates an existing issue.",
"Finds an existing user.": "Finds an existing user.",
"Search for issues with JQL": "Search for issues with JQL",
"Assigns an issue to a user.": "Assigns an issue to a user.",
"Adds an attachment to an issue.": "Adds an attachment to an issue.",
"Retrieves an attachment from an issue.": "Retrieves an attachment from an issue.",
"Adds a new watcher to an issue.": "Adds a new watcher to an issue.",
"Adds a comment to an issue.": "Adds a comment to an issue.",
"Updates a comment to a specific issue.": "Updates a comment to a specific issue.",
"Creates a link between two issues.": "Creates a link between two issues.",
"Returns all comments for an issue.": "Returns all comments for an issue.",
"Deletes a comment on a specific issue.": "Deletes a comment on a specific issue.",
"Make a custom API call to a specific endpoint": "Make a custom API call to a specific endpoint",
"Project ID or Key": "Project ID or Key",
"Issue Type": "Issue Type",
"Fields": "Fields",
"Issue ID or Key": "Issue ID or Key",
"Status": "Status",
"Keyword": "Keyword",
"JQL": "JQL",
"Max Results": "Max Results",
"Sanitize JQL": "Sanitize JQL",
"Assignee": "Assignee",
"Attachment": "Attachment",
"Attachment ID": "Attachment ID",
"User": "User",
"Comment Body": "Comment Body",
"Comment ID": "Comment ID",
"First Issue": "First Issue",
"Link Type": "Link Type",
"Second Issue": "Second Issue",
"Order By": "Order By",
"Limit": "Limit",
"Method": "Method",
"Headers": "Headers",
"Query Parameters": "Query Parameters",
"Body": "Body",
"No Error on Failure": "No Error on Failure",
"Timeout (in seconds)": "Timeout (in seconds)",
"The JQL query to use in the search": "The JQL query to use in the search",
"Maximum number of results": "Maximum number of results",
"Authorization headers are injected automatically from your connection.": "Authorization headers are injected automatically from your connection.",
"Created (Descending)": "Created (Descending)",
"Created (Ascending)": "Created (Ascending)",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD",
"New Issue": "New Issue",
"Updated Issue": "Updated Issue",
"Updated Issue Status": "Updated Issue Status",
"Triggers when a new issue is created": "Triggers when a new issue is created",
"Triggers when an issue is updated": "Triggers when an issue is updated",
"Triggers when an issue status is updated": "Triggers when an issue status is updated",
"Use to filter issues watched": "Use to filter issues watched"
}

View File

@@ -0,0 +1,90 @@
{
"Issue tracking and project management": "Issue tracking and project management",
"Instance URL": "Instance URL",
"Email": "电子邮件地址",
"API Token": "API Token",
"The link of your Jira instance (e.g https://example.atlassian.net)": "The link of your Jira instance (e.g https://example.atlassian.net)",
"The email you use to login to Jira": "The email you use to login to Jira",
"Your Jira API Token": "Your Jira API Token",
"\nYou can generate your API token from:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ": "\nYou can generate your API token from:\n***https://id.atlassian.com/manage-profile/security/api-tokens***\n ",
"Create Issue": "Create Issue",
"Update Issue": "Update Issue",
"Find User": "Find User",
"Search Issues": "Search Issues",
"Assign Issue": "Assign Issue",
"Add Attachment to Issue": "Add Attachment to Issue",
"Get Issue Attachment": "Get Issue Attachment",
"Add Watcher to Issue": "Add Watcher to Issue",
"Add Issue Comment": "Add Issue Comment",
"Update Issue Comment": "Update Issue Comment",
"Link Issues": "Link Issues",
"List Issue Comments": "List Issue Comments",
"Delete Issue Comment": "Delete Issue Comment",
"Markdown to Jira format": "Markdown to Jira format",
"Custom API Call": "自定义 API 呼叫",
"Creates a new issue in a project.": "Creates a new issue in a project.",
"Updates an existing issue.": "Updates an existing issue.",
"Finds an existing user.": "Finds an existing user.",
"Search for issues with JQL": "Search for issues with JQL",
"Assigns an issue to a user.": "Assigns an issue to a user.",
"Adds an attachment to an issue.": "Adds an attachment to an issue.",
"Retrieves an attachment from an issue.": "Retrieves an attachment from an issue.",
"Adds a new watcher to an issue.": "Adds a new watcher to an issue.",
"Adds a comment to an issue.": "Adds a comment to an issue.",
"Updates a comment to a specific issue.": "Updates a comment to a specific issue.",
"Creates a link between two issues.": "Creates a link between two issues.",
"Returns all comments for an issue.": "Returns all comments for an issue.",
"Deletes a comment on a specific issue.": "Deletes a comment on a specific issue.",
"Convert Markdown-formatted text to Jira's ADF syntax for use in comments and descriptions etc": "Convert Markdown-formatted text to Jira's ADF syntax for use in comments and descriptions etc",
"Make a custom API call to a specific endpoint": "将一个自定义 API 调用到一个特定的终点",
"Project ID or Key": "Project ID or Key",
"Issue Type": "Issue Type",
"Fields": "Fields",
"Fields in JSON Atlassian Document Format": "Fields in JSON Atlassian Document Format",
"Issue ID or Key": "Issue ID or Key",
"Status": "状态",
"Keyword": "Keyword",
"JQL": "JQL",
"Max Results": "Max Results",
"Sanitize JQL": "Sanitize JQL",
"Assignee": "Assignee",
"Attachment": "Attachment",
"Attachment ID": "Attachment ID",
"User": "用户",
"Comment Body": "Comment Body",
"Comment is in JSON Atlassian Document Format": "Comment is in JSON Atlassian Document Format",
"Comment ID": "Comment ID",
"First Issue": "First Issue",
"Link Type": "Link Type",
"Second Issue": "Second Issue",
"Order By": "Order By",
"Limit": "Limit",
"Markdown text": "Markdown text",
"Method": "方法",
"Headers": "信头",
"Query Parameters": "查询参数",
"Body": "正文内容",
"Response is Binary ?": "Response is Binary ?",
"No Error on Failure": "失败时没有错误",
"Timeout (in seconds)": "超时(秒)",
"https://developer.atlassian.com/cloud/jira/platform/apis/document/structure": "https://developer.atlassian.com/cloud/jira/platform/apis/document/structure",
"The JQL query to use in the search": "The JQL query to use in the search",
"Maximum number of results": "Maximum number of results",
"Authorization headers are injected automatically from your connection.": "授权头自动从您的连接中注入。",
"Enable for files like PDFs, images, etc..": "Enable for files like PDFs, images, etc..",
"Created (Descending)": "Created (Descending)",
"Created (Ascending)": "Created (Ascending)",
"GET": "获取",
"POST": "帖子",
"PATCH": "PATCH",
"PUT": "弹出",
"DELETE": "删除",
"HEAD": "黑色",
"New Issue": "New Issue",
"Updated Issue": "Updated Issue",
"Updated Issue Status": "Updated Issue Status",
"Triggers when a new issue is created": "Triggers when a new issue is created",
"Triggers when an issue is updated": "Triggers when an issue is updated",
"Triggers when an issue status is updated": "Triggers when an issue status is updated",
"Use to filter issues watched": "Use to filter issues watched"
}

View File

@@ -0,0 +1,66 @@
import { createPiece } from '@activepieces/pieces-framework';
import { createCustomApiCallAction } from '@activepieces/pieces-common';
import { PieceCategory } from '@activepieces/shared';
import { JiraAuth, jiraCloudAuth } from './auth';
import { createIssueAction } from './lib/actions/create-issue';
import { searchIssues } from './lib/actions/search-issues';
import { newIssue } from './lib/triggers/new-issue';
import { updatedIssue } from './lib/triggers/updated-issue';
import { updatedIssueStatus } from './lib/triggers/updated-issue-status';
import { addCommentToIssueAction } from './lib/actions/add-comment-to-issue';
import { addAttachmentToIssueAction } from './lib/actions/add-attachment-to-issue';
import { updateIssueCommentAction } from './lib/actions/update-issue-comment';
import { deleteIssueCommentAction } from './lib/actions/delete-issue-comment';
import { updateIssueAction } from './lib/actions/update-issue';
import { assignIssueAction } from './lib/actions/assign-issue';
import { listIssueCommentsAction } from './lib/actions/list-issue-comments';
import { findUserAction } from './lib/actions/find-user';
import { addWatcherToIssueAction } from './lib/actions/add-watcher-to-issue';
import { linkIssuesAction } from './lib/actions/link-issues';
import { getIssueAttachmentAction } from './lib/actions/get-issue-attachment';
import { markdownToJiraFormat } from './lib/actions/markdown-to-jira-format';
import { getIssueAction } from './lib/actions/get-issue';
export const jiraCloud = createPiece({
displayName: 'Jira Cloud',
description: 'Issue tracking and project management',
auth: jiraCloudAuth,
minimumSupportedRelease: '0.30.0',
logoUrl: 'https://cdn.activepieces.com/pieces/jira.png',
categories: [PieceCategory.PRODUCTIVITY],
authors: ['kishanprmr', 'MoShizzle', 'abuaboud', 'prasanna2000-max'],
actions: [
createIssueAction,
updateIssueAction,
findUserAction,
searchIssues,
assignIssueAction,
addAttachmentToIssueAction,
getIssueAttachmentAction,
addWatcherToIssueAction,
addCommentToIssueAction,
updateIssueCommentAction,
linkIssuesAction,
listIssueCommentsAction,
deleteIssueCommentAction,
markdownToJiraFormat,
getIssueAction,
createCustomApiCallAction({
baseUrl: (auth) => {
return auth ? `${(auth).props.instanceUrl}/rest/api/3` : '';
},
auth: jiraCloudAuth,
authMapping: async (auth) => {
const typedAuth = auth as JiraAuth;
return {
Authorization: `Basic ${Buffer.from(`${typedAuth.props.email}:${typedAuth.props.apiToken}`).toString(
'base64',
)}`,
};
},
}),
],
triggers: [newIssue, updatedIssue, updatedIssueStatus],
});

View File

@@ -0,0 +1,39 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { jiraCloudAuth } from '../../auth';
import { HttpMethod } from '@activepieces/pieces-common';
import { sendJiraRequest } from '../common';
import FormData from 'form-data';
import { getProjectIdDropdown, getIssueIdDropdown } from '../common/props';
export const addAttachmentToIssueAction = createAction({
auth: jiraCloudAuth,
name: 'add_issue_attachment',
displayName: 'Add Attachment to Issue',
description: 'Adds an attachment to an issue.',
props: {
projectId: getProjectIdDropdown(),
issueId: getIssueIdDropdown({ refreshers: ['projectId'] }),
attachment: Property.File({
displayName: 'Attachment',
required: true,
}),
},
async run(context) {
const { issueId, attachment } = context.propsValue;
const formData = new FormData();
const fileBuffer = Buffer.from(attachment.base64, 'base64');
formData.append('file', fileBuffer, attachment.filename);
const response = await sendJiraRequest({
method: HttpMethod.POST,
url: `issue/${issueId}/attachments`,
auth: context.auth,
headers: {
'X-Atlassian-Token': 'no-check',
...formData.getHeaders(),
},
body: formData,
});
return response.body;
},
});

View File

@@ -0,0 +1,61 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { jiraCloudAuth } from '../../auth';
import { HttpMethod } from '@activepieces/pieces-common';
import { sendJiraRequest } from '../common';
import { getIssueIdDropdown, getProjectIdDropdown } from '../common/props';
export const addCommentToIssueAction = createAction({
auth: jiraCloudAuth,
name: 'add_issue_comment',
displayName: 'Add Issue Comment',
description: 'Adds a comment to an issue.',
props: {
projectId: getProjectIdDropdown(),
issueId: getIssueIdDropdown({ refreshers: ['projectId'] }),
comment: Property.LongText({
displayName: 'Comment Body',
required: true,
}),
isADF: Property.Checkbox({
displayName: 'Comment is in JSON Atlassian Document Format',
description: 'https://developer.atlassian.com/cloud/jira/platform/apis/document/structure',
required: false,
defaultValue: false,
}),
},
async run(context) {
const { issueId, comment, isADF } = context.propsValue;
let commentBody = {}
if (isADF) {
commentBody = JSON.parse(comment);
} else {
commentBody = {
version: 1,
type: 'doc',
content: [
{
type: 'paragraph',
content: [
{
type: 'text',
text: comment,
},
],
},
],
};
}
const response = await sendJiraRequest({
method: HttpMethod.POST,
url: `issue/${issueId}/comment`,
auth: context.auth,
body: {
body: commentBody,
},
});
return response.body;
},
});

View File

@@ -0,0 +1,43 @@
import { createAction } from '@activepieces/pieces-framework';
import { jiraCloudAuth } from '../../auth';
import { getUsersDropdown, issueIdOrKeyProp } from '../common/props';
import { isNil } from '@activepieces/shared';
import { jiraApiCall } from '../common';
import { HttpError, HttpMethod } from '@activepieces/pieces-common';
export const addWatcherToIssueAction = createAction({
auth: jiraCloudAuth,
name: 'add-watcher-to-issue',
displayName: 'Add Watcher to Issue',
description: 'Adds a new watcher to an issue.',
props: {
issueId: issueIdOrKeyProp('Issue ID or Key', true),
userId: getUsersDropdown({
displayName: 'User',
refreshers: [],
required: true,
}),
},
async run(context) {
const { issueId, userId } = context.propsValue;
if (isNil(issueId)) {
throw new Error('Issue ID is required');
}
if (isNil(userId)) {
throw new Error('User ID is required');
}
try {
const response = await jiraApiCall({
auth: context.auth,
method: HttpMethod.POST,
resourceUri: `/issue/${issueId}/watchers`,
body: `"${userId}"`,
});
return { success: true };
} catch (e) {
return { success: false, error: (e as HttpError).message };
}
},
});

View File

@@ -0,0 +1,33 @@
import { createAction } from '@activepieces/pieces-framework';
import { jiraCloudAuth } from '../../auth';
import { HttpMethod } from '@activepieces/pieces-common';
import { sendJiraRequest } from '../common';
import { getIssueIdDropdown, getProjectIdDropdown, getUsersDropdown } from '../common/props';
export const assignIssueAction = createAction({
auth: jiraCloudAuth,
name: 'assign_issue',
displayName: 'Assign Issue',
description: 'Assigns an issue to a user.',
props: {
projectId: getProjectIdDropdown(),
issueId: getIssueIdDropdown({ refreshers: ['projectId'] }),
assignee: getUsersDropdown({
displayName: 'Assignee',
refreshers: ['projectId'],
required: true,
}),
},
async run(context) {
const { issueId, assignee } = context.propsValue;
const response = await sendJiraRequest({
method: HttpMethod.PUT,
url: `issue/${issueId}/assignee`,
auth: context.auth,
body: {
accountId: assignee,
},
});
return response.body;
},
});

View File

@@ -0,0 +1,160 @@
import { createAction, DynamicPropsValue, Property } from '@activepieces/pieces-framework';
import { JiraAuth, jiraCloudAuth } from '../../auth';
import {
getProjectIdDropdown,
formatIssueFields,
issueTypeIdProp,
createPropertyDefinition,
transformCustomFields,
isFieldAdfCompatible,
} from '../common/props';
import { jiraApiCall, jiraPaginatedApiCall } from '../common';
import { IssueFieldMetaData, VALID_CUSTOM_FIELD_TYPES } from '../common/types';
import { HttpMethod } from '@activepieces/pieces-common';
import { isNil } from '@activepieces/shared';
async function getFields(auth: JiraAuth, projectId: string, issueTypeId: string): Promise<IssueFieldMetaData[]> {
const fields = await jiraPaginatedApiCall<IssueFieldMetaData, 'fields'>({
auth: auth,
method: HttpMethod.GET,
resourceUri: `/issue/createmeta/${projectId}/issuetypes/${issueTypeId}`,
propertyName: 'fields',
});
if (!fields || !Array.isArray(fields)) {
return [];
}
return fields;
}
export const createIssueAction = createAction({
name: 'create_issue',
displayName: 'Create Issue',
description: 'Creates a new issue in a project.',
auth: jiraCloudAuth,
props: {
projectId: getProjectIdDropdown(),
issueTypeId: issueTypeIdProp('Issue Type'),
issueFields: Property.DynamicProperties({
auth: jiraCloudAuth,
displayName: 'Fields',
required: true,
refreshers: ['projectId', 'issueTypeId'],
props: async ({ auth, projectId, issueTypeId }) => {
if (!auth || !issueTypeId || !projectId) {
return {};
}
const props: DynamicPropsValue = {};
const authValue = auth as JiraAuth;
const projectIdValue = projectId as unknown as string;
const issueTypeIdValue = issueTypeId as unknown as string;
const fields = await getFields(authValue, projectIdValue, issueTypeIdValue);
for (const field of fields) {
// skip invalid custom fields
if (field.schema.custom) {
const customFieldType = field.schema.custom.split(':')[1];
if (!VALID_CUSTOM_FIELD_TYPES.includes(customFieldType)) {
continue;
}
}
if (['project', 'issuetype'].includes(field.key)) {
continue;
}
props[field.key] = await createPropertyDefinition(authValue, field, field.required);
}
// Remove null props
return Object.fromEntries(Object.entries(props).filter(([_, prop]) => prop !== null));
},
}),
adfFields: Property.MultiSelectDropdown({
auth: jiraCloudAuth,
displayName: 'Fields in JSON Atlassian Document Format',
description: 'https://developer.atlassian.com/cloud/jira/platform/apis/document/structure',
required: false,
refreshers: ['projectId', 'issueTypeId'],
options: async ({ auth, projectId, issueTypeId }) => {
if (!auth || !issueTypeId || !projectId) {
return {
disabled: true,
options: [],
};
}
const authValue = auth as JiraAuth;
const projectIdValue = projectId as unknown as string;
const issueTypeIdValue = issueTypeId as unknown as string;
const fields = await getFields(authValue, projectIdValue, issueTypeIdValue);
const adfCompatibleFields = fields.filter(isFieldAdfCompatible);
const fieldOptions = adfCompatibleFields.map(field => ({
label: field.name,
value: field.key,
}))
return {
disabled: false,
options: fieldOptions,
};
},
}),
},
async run(context) {
const { projectId, issueTypeId, adfFields } = context.propsValue;
const inputIssueFields = context.propsValue.issueFields ?? {};
if (isNil(projectId) || isNil(issueTypeId)) {
throw new Error('Project ID and Issue Type ID are required');
}
const issueTypeFields = await jiraPaginatedApiCall<IssueFieldMetaData, 'fields'>({
auth: context.auth,
method: HttpMethod.GET,
resourceUri: `/issue/createmeta/${projectId}/issuetypes/${issueTypeId}`,
propertyName: 'fields',
});
const formattedAdfFields = adfFields || [];
const formattedFields = formatIssueFields(issueTypeFields, inputIssueFields, formattedAdfFields);
const response = await jiraApiCall<{ id: string; key: string }>({
auth: context.auth,
method: HttpMethod.POST,
resourceUri: `/issue`,
body: {
fields: {
issuetype: {
id: issueTypeId,
},
project: {
id: projectId,
},
...formattedFields,
},
},
});
const issue = await jiraApiCall<{
expand: string;
id: string;
key: string;
fields: Record<string, any>;
}>({
auth: context.auth,
method: HttpMethod.GET,
resourceUri: `/issue/${response.id}`,
});
const updatedIssueProperties = transformCustomFields(issueTypeFields, issue.fields);
return {
...issue,
fields: updatedIssueProperties,
};
},
});

View File

@@ -0,0 +1,59 @@
import { createAction, PiecePropValueSchema, Property } from '@activepieces/pieces-framework';
import { jiraCloudAuth } from '../../auth';
import { HttpMethod } from '@activepieces/pieces-common';
import { sendJiraRequest } from '../common';
import { getIssueIdDropdown, getProjectIdDropdown } from '../common/props';
export const deleteIssueCommentAction = createAction({
auth: jiraCloudAuth,
name: 'delete_issue_comment',
displayName: 'Delete Issue Comment',
description: 'Deletes a comment on a specific issue.',
props: {
projectId: getProjectIdDropdown(),
issueId: getIssueIdDropdown({ refreshers: ['projectId'] }),
commentId: Property.Dropdown<string, true, typeof jiraCloudAuth>({
auth: jiraCloudAuth,
displayName: 'Comment ID',
refreshers: ['issueId'],
required: true,
options: async ({ auth, issueId }) => {
if (!auth || !issueId) {
return {
disabled: true,
placeholder: 'Please connect your account and select issue.',
options: [],
};
}
const response = await sendJiraRequest({
method: HttpMethod.GET,
url: `issue/${issueId}/comment`,
auth: auth,
queryParams: {
orderBy: '-created',
expand: 'renderedBody',
},
});
return {
disabled: false,
options: response.body.comments.map((comment: { id: string; renderedBody: string }) => {
return {
label: comment.renderedBody,
value: comment.id,
};
}),
};
},
}),
},
async run(context) {
const { issueId, commentId } = context.propsValue;
const response = await sendJiraRequest({
method: HttpMethod.DELETE,
url: `issue/${issueId}/comment/${commentId}`,
auth: context.auth,
});
return response.body;
},
});

View File

@@ -0,0 +1,32 @@
import { createAction, Property } from "@activepieces/pieces-framework";
import { jiraCloudAuth } from "../../auth";
import { jiraApiCall } from "../common";
import { HttpMethod } from "@activepieces/pieces-common";
export const findUserAction = createAction({
auth:jiraCloudAuth,
name:'find-user',
displayName:'Find User',
description:'Finds an existing user.',
props:{
keyword:Property.ShortText({
displayName:'Keyword',
required:true,
})
},
async run(context){
const response = await jiraApiCall<Array<Record<string,any>>>({
auth:context.auth,
method:HttpMethod.GET,
resourceUri:'/user/search',
query:{
query:context.propsValue.keyword
}
})
return{
found:response.length>0,
data:response
}
}
})

View File

@@ -0,0 +1,48 @@
import { createAction, Property } from "@activepieces/pieces-framework";
import { jiraCloudAuth } from "../../auth";
import { jiraApiCall } from "../common";
import { AuthenticationType, httpClient, HttpMethod } from "@activepieces/pieces-common";
export const getIssueAttachmentAction = createAction({
auth: jiraCloudAuth,
name: 'get-issue-attachment',
displayName: 'Get Issue Attachment',
description: 'Retrieves an attachment from an issue.',
props: {
attachmentId: Property.ShortText({
displayName: 'Attachment ID',
required: true
})
},
async run(context) {
const { attachmentId } = context.propsValue;
// https://community.developer.atlassian.com/t/download-attachment-from-rest-api/40860/2
const attachmentResponse = await jiraApiCall<{ filename: string, content: string }>({
method: HttpMethod.GET,
resourceUri: `/attachment/${attachmentId}`,
auth: context.auth,
})
const { filename, content } = attachmentResponse;
const response = await httpClient.sendRequest({
url: content,
method: HttpMethod.GET,
authentication: {
type: AuthenticationType.BASIC,
username: context.auth.props.email,
password: context.auth.props.apiToken,
},
responseType:'arraybuffer'
})
return {
...attachmentResponse,
file: await context.files.write({
fileName: filename,
data: Buffer.from(response.body)
})
}
}
})

View File

@@ -0,0 +1,158 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { jiraCloudAuth } from '../../auth';
import { HttpMethod, QueryParams } from '@activepieces/pieces-common';
import { sendJiraRequest } from '../common';
import { getIssueIdDropdown, getProjectIdDropdown } from '../common/props';
function mapFieldNames(
fields: Record<string, any>,
fieldNames: Record<string, string>
) {
const mappedFields = {} as Record<string, any>;
for (const [fieldId, fieldValue] of Object.entries(fields)) {
const fieldName = fieldNames?.[fieldId];
if (fieldName) {
mappedFields[fieldName] = fieldValue;
} else {
// fallback in case field cannot be mapped (but this should not happen)
mappedFields[fieldId] = fieldValue;
}
}
return mappedFields;
}
export const getIssueAction = createAction({
auth: jiraCloudAuth,
name: 'get_issue',
displayName: 'Get Issue',
description: 'Get issue data.',
props: {
projectId: getProjectIdDropdown(),
issueId: getIssueIdDropdown({ refreshers: ['projectId'] }),
expand: Property.StaticMultiSelectDropdown({
displayName: 'Expand',
description:
'Include additional information about the issue in the response',
required: false,
options: {
options: [
{
label: 'Rendered Fields',
value: 'renderedFields',
},
{
label: 'Names',
value: 'names',
},
{
label: 'Schema',
value: 'schema',
},
{
label: 'Transitions',
value: 'transitions',
},
{
label: 'Edit Meta',
value: 'editmeta',
},
{
label: 'Changelog',
value: 'changelog',
},
],
},
}),
mapNames: Property.Checkbox({
displayName: 'Map Field Names',
description: `
Map human readable names to Fields, Rendered Fields, Schema and Edit Meta.
Notes:
- This would implicitly add "names" to the expand field
- If there are fields with the same name, they may be overridden
`.trim(),
required: true,
defaultValue: false,
}),
mapTransitions: Property.Checkbox({
displayName: 'Map Transition Names',
description: `
Map human readable names to Transitions.
Notes:
- If there are transitions with the same name, they may be overridden
- This changes the original data structure from list to map
`.trim(),
required: true,
defaultValue: false,
}),
},
async run(context) {
const { issueId, expand, mapNames, mapTransitions } = context.propsValue;
const queryParams = {} as QueryParams;
let expandParams = expand as string[];
// implicitly expand names which is needed for mapping
if (mapNames) {
expandParams = [...new Set(expandParams).add('names')];
}
if (expandParams) {
queryParams['expand'] = expandParams.join(',');
}
// https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-issue-issueidorkey-get
const response = await sendJiraRequest({
method: HttpMethod.GET,
url: `issue/${issueId}`,
auth: context.auth,
queryParams: queryParams,
});
const data = response.body;
if (mapNames) {
const fieldNames = data.names || {};
const mappedFields = mapFieldNames(data.fields, fieldNames);
data['fields'] = mappedFields;
if (data.renderedFields) {
const mappedRenderedFields = mapFieldNames(
data.renderedFields,
fieldNames
);
data['renderedFields'] = mappedRenderedFields;
}
if (data.schema) {
const mappedSchemaFields = mapFieldNames(data.schema, fieldNames);
data['schema'] = mappedSchemaFields;
}
if (data.editmeta?.fields) {
const mappedEditmetaFields = mapFieldNames(
data.editmeta.fields,
fieldNames
);
data['editmeta']['fields'] = mappedEditmetaFields;
}
}
if (mapTransitions && data.transitions) {
const mappedTransitions = data.transitions.reduce(
(acc: Record<string, any>, transition: any) => {
acc[transition.name] = transition;
return acc;
},
{}
);
data['transitions'] = mappedTransitions;
}
return data;
},
});

View File

@@ -0,0 +1,47 @@
import { createAction } from '@activepieces/pieces-framework';
import { jiraCloudAuth } from '../../auth';
import { issueIdOrKeyProp, issueLinkTypeIdProp } from '../common/props';
import { isNil } from '@activepieces/shared';
import { HttpError, HttpMethod } from '@activepieces/pieces-common';
import { jiraApiCall } from '../common';
export const linkIssuesAction = createAction({
auth: jiraCloudAuth,
name: 'link-issues',
displayName: 'Link Issues',
description: 'Creates a link between two issues.',
props: {
firstIssueId: issueIdOrKeyProp('First Issue', true),
issueLinkTypeId: issueLinkTypeIdProp('Link Type', true),
secondIssueId: issueIdOrKeyProp('Second Issue', true),
},
async run(context) {
const { firstIssueId, issueLinkTypeId, secondIssueId } = context.propsValue;
if (isNil(firstIssueId) || isNil(issueLinkTypeId) || isNil(secondIssueId)) {
throw new Error('First Issue, Link Type, and Second Issue are required');
}
try {
const response = await jiraApiCall({
method: HttpMethod.POST,
resourceUri: '/issueLink',
auth: context.auth,
body: {
type: {
id: issueLinkTypeId,
},
inwardIssue: {
id: secondIssueId,
},
outwardIssue: {
id: firstIssueId,
},
},
});
return { success: true };
} catch (e) {
return { success: false, error: (e as HttpError).message };
}
},
});

View File

@@ -0,0 +1,55 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { jiraCloudAuth } from '../../auth';
import { HttpMethod } from '@activepieces/pieces-common';
import { sendJiraRequest } from '../common';
import { getProjectIdDropdown, getIssueIdDropdown } from '../common/props';
export const listIssueCommentsAction = createAction({
auth: jiraCloudAuth,
name: 'list_issue_comments',
displayName: 'List Issue Comments',
description: 'Returns all comments for an issue.',
props: {
projectId: getProjectIdDropdown(),
issueId: getIssueIdDropdown({ refreshers: ['projectId'] }),
orderBy: Property.StaticDropdown({
displayName: 'Order By',
required: true,
defaultValue: '-created',
options: {
disabled: false,
options: [
{
label: 'Created (Descending)',
value: '-created',
},
{
label: 'Created (Ascending)',
value: '+created',
},
],
},
}),
limit: Property.Number({
displayName: 'Limit',
description: 'Maximum number of results',
required: true,
defaultValue: 10,
}),
},
async run(context) {
const { issueId, orderBy, limit } = context.propsValue;
const response = await sendJiraRequest({
method: HttpMethod.GET,
url: `issue/${issueId}/comment`,
auth: context.auth,
queryParams: {
orderBy: orderBy,
maxResults: limit.toString(),
expand: 'renderedBody',
},
});
return response.body;
},
});

View File

@@ -0,0 +1,36 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { defaultSchema } from '@atlaskit/adf-schema/schema-default';
import { JSONTransformer } from '@atlaskit/editor-json-transformer';
import { MarkdownTransformer } from '@atlaskit/editor-markdown-transformer';
export const markdownToJiraFormat = createAction({
name: 'markdownToJiraFormat',
displayName: 'Markdown to Jira format',
description:
"Convert Markdown-formatted text to Jira's ADF syntax for use in comments and descriptions etc",
requireAuth: false,
props: {
markdown: Property.LongText({
displayName: 'Markdown text',
required: true,
}),
},
errorHandlingOptions: {
continueOnFailure: {
defaultValue: false,
},
retryOnFailure: {
hide: true,
},
},
async run({ propsValue }) {
const jsonTransformer = new JSONTransformer();
const markdownTransformer = new MarkdownTransformer(defaultSchema);
const adfDocument = jsonTransformer.encode(
markdownTransformer.parse(propsValue.markdown)
);
return adfDocument;
},
});

View File

@@ -0,0 +1,45 @@
import {
Property,
createAction,
} from '@activepieces/pieces-framework';
import { jiraCloudAuth } from '../../auth';
import { searchIssuesByJql } from '../common';
import { z } from 'zod';
import { propsValidation } from '@activepieces/pieces-common';
export const searchIssues = createAction({
name: 'search_issues',
displayName: 'Search Issues',
description: 'Search for issues with JQL',
auth: jiraCloudAuth,
props: {
jql: Property.LongText({
displayName: 'JQL',
description: 'The JQL query to use in the search',
defaultValue: `type = story and created > '2023-12-13 14:00'`,
required: true,
}),
maxResults: Property.Number({
displayName: 'Max Results',
defaultValue: 50,
required: true,
}),
sanitizeJql: Property.Checkbox({
displayName: 'Sanitize JQL',
required: true,
defaultValue: true,
}),
},
run: async ({ auth, propsValue }) => {
await propsValidation.validateZod(propsValue, {
maxResults: z.number().min(1).max(100),
});
const { jql, maxResults, sanitizeJql } = propsValue;
return await searchIssuesByJql({
auth,
jql,
maxResults: maxResults,
sanitizeJql,
});
},
});

View File

@@ -0,0 +1,95 @@
import { createAction, PiecePropValueSchema, Property } from '@activepieces/pieces-framework';
import { jiraCloudAuth } from '../../auth';
import { HttpMethod } from '@activepieces/pieces-common';
import { sendJiraRequest } from '../common';
import { getIssueIdDropdown, getProjectIdDropdown } from '../common/props';
export const updateIssueCommentAction = createAction({
auth: jiraCloudAuth,
name: 'update_issue_comment',
displayName: 'Update Issue Comment',
description: 'Updates a comment to a specific issue.',
props: {
projectId: getProjectIdDropdown(),
issueId: getIssueIdDropdown({ refreshers: ['projectId'] }),
commentId: Property.Dropdown({
auth: jiraCloudAuth,
displayName: 'Comment ID',
refreshers: ['issueId'],
required: true,
options: async ({ auth, issueId }) => {
if (!auth || !issueId) {
return {
disabled: true,
placeholder: 'Please connect your account and select issue.',
options: [],
};
}
const response = await sendJiraRequest({
method: HttpMethod.GET,
url: `issue/${issueId}/comment`,
auth: auth,
queryParams: {
orderBy: '-created',
expand: 'renderedBody',
},
});
return {
disabled: false,
options: response.body.comments.map((comment: { id: string; renderedBody: string }) => {
return {
label: comment.renderedBody,
value: comment.id,
};
}),
};
},
}),
comment: Property.LongText({
displayName: 'Comment Body',
required: true,
}),
isADF: Property.Checkbox({
displayName: 'Comment is in JSON Atlassian Document Format',
description: 'https://developer.atlassian.com/cloud/jira/platform/apis/document/structure',
required: false,
defaultValue: false,
}),
},
async run(context) {
const { issueId, comment, commentId, isADF } = context.propsValue;
let commentBody = {}
if (isADF) {
commentBody = JSON.parse(comment);
} else {
commentBody = {
version: 1,
type: 'doc',
content: [
{
type: 'paragraph',
content: [
{
type: 'text',
text: comment,
},
],
},
],
};
}
const response = await sendJiraRequest({
method: HttpMethod.PUT,
url: `issue/${issueId}/comment/${commentId}`,
auth: context.auth,
body: {
body: commentBody,
},
});
return response.body;
},
});

View File

@@ -0,0 +1,182 @@
import { DynamicPropsValue, Property, createAction } from '@activepieces/pieces-framework';
import { JiraAuth, jiraCloudAuth } from '../../auth';
import {
createPropertyDefinition,
formatIssueFields,
issueIdOrKeyProp,
issueStatusIdProp,
transformCustomFields,
isFieldAdfCompatible,
} from '../common/props';
import { jiraApiCall } from '../common';
import { IssueFieldMetaData, VALID_CUSTOM_FIELD_TYPES } from '../common/types';
import { HttpMethod } from '@activepieces/pieces-common';
import { isNil } from '@activepieces/shared';
async function getFields(auth: JiraAuth, issueId: string): Promise<IssueFieldMetaData[]> {
const response = await jiraApiCall<{ fields: { [x: string]: IssueFieldMetaData } }>({
auth: auth,
method: HttpMethod.GET,
resourceUri: `/issue/${issueId}/editmeta`,
});
const fields: IssueFieldMetaData[] = [];
for (const key in response.fields) {
fields.push(response.fields[key]);
}
if (!fields || !Array.isArray(fields)) {
return [];
}
return fields;
}
export const updateIssueAction = createAction({
name: 'update_issue',
displayName: 'Update Issue',
description: 'Updates an existing issue.',
auth: jiraCloudAuth,
props: {
issueId: issueIdOrKeyProp('Issue ID or Key', true),
statusId: issueStatusIdProp('Status', false),
issueFields: Property.DynamicProperties({
auth: jiraCloudAuth,
displayName: 'Fields',
required: true,
refreshers: ['issueId'],
props: async ({ auth, issueId }) => {
if (!auth || !issueId) {
return {};
}
const props: DynamicPropsValue = {};
const authValue = auth as JiraAuth;
const issueIdValue = issueId as unknown as string;
const fields = await getFields(authValue, issueIdValue);
for (const field of fields) {
// skip invalid custom fields
if (field.schema.custom) {
const customFieldType = field.schema.custom.split(':')[1];
if (!VALID_CUSTOM_FIELD_TYPES.includes(customFieldType)) {
continue;
}
}
if (field.key === 'issuetype') {
props[field.key] = Property.StaticDropdown({
displayName: field.name,
required: false,
options: {
disabled: false,
options: field.allowedValues
? field.allowedValues.map((option) => ({
label: option.name,
value: option.id,
}))
: [],
},
});
} else {
props[field.key] = await createPropertyDefinition(authValue, field, false);
}
}
// Remove null props
return Object.fromEntries(Object.entries(props).filter(([_, prop]) => prop !== null));
},
}),
adfFields: Property.MultiSelectDropdown({
auth: jiraCloudAuth,
displayName: 'Fields in JSON Atlassian Document Format',
description: 'https://developer.atlassian.com/cloud/jira/platform/apis/document/structure',
required: false,
refreshers: ['issueId'],
options: async ({ auth, issueId }) => {
if (!auth || !issueId) {
return {
disabled: true,
options: [],
};
}
const authValue = auth as JiraAuth;
const issueIdValue = issueId as unknown as string;
const fields = await getFields(authValue, issueIdValue);
const adfCompatibleFields = fields.filter(isFieldAdfCompatible);
const fieldOptions = adfCompatibleFields.map(field => ({
label: field.name,
value: field.key,
}))
return {
disabled: false,
options: fieldOptions,
};
},
}),
},
async run(context) {
const { issueId, statusId, adfFields } = context.propsValue;
const inputIssueFields = context.propsValue.issueFields ?? {};
if (isNil(issueId)) {
throw new Error('Issue ID is required');
}
if (!isNil(statusId) && statusId !== '') {
await jiraApiCall({
auth: context.auth,
method: HttpMethod.POST,
resourceUri: `/issue/${issueId}/transitions`,
body: {
transition: {
id: statusId,
},
},
});
}
const issueTypeFields = await jiraApiCall<{ fields: { [x: string]: IssueFieldMetaData } }>({
auth: context.auth,
method: HttpMethod.GET,
resourceUri: `/issue/${issueId}/editmeta`,
});
const flattenedFields = Object.values(issueTypeFields.fields);
const formattedAdfFields = adfFields || [];
const formattedFields = formatIssueFields(flattenedFields, inputIssueFields, formattedAdfFields);
const response = await jiraApiCall({
auth: context.auth,
method: HttpMethod.PUT,
resourceUri: `/issue/${issueId}`,
body: {
fields: formattedFields,
},
query: { returnIssue: 'true' },
});
const issue = await jiraApiCall<{
expand: string;
id: string;
key: string;
fields: Record<string, any>;
}>({
auth: context.auth,
method: HttpMethod.GET,
resourceUri: `/issue/${issueId}`,
});
const updatedIssueProperties = transformCustomFields(flattenedFields, issue.fields);
return {
...issue,
fields: updatedIssueProperties,
};
},
});

View File

@@ -0,0 +1,382 @@
import {
AuthenticationType,
HttpMessageBody,
HttpMethod,
HttpRequest,
QueryParams,
httpClient,
} from '@activepieces/pieces-common';
import { JiraAuth } from '../../auth';
import { isNil } from '@activepieces/shared';
export async function sendJiraRequest(request: HttpRequest & { auth: JiraAuth }) {
return httpClient.sendRequest({
...request,
url: `${request.auth.props.instanceUrl}/rest/api/3/${request.url}`,
authentication: {
type: AuthenticationType.BASIC,
username: request.auth.props.email,
password: request.auth.props.apiToken,
},
});
}
export async function getUsers(auth: JiraAuth) {
const response = await sendJiraRequest({
url: 'users/search',
method: HttpMethod.GET,
auth: auth,
queryParams: {
maxResults: '1000',
},
});
return response.body as any[];
}
export async function getProjects(auth: JiraAuth): Promise<JiraProject[]> {
const response = await jiraPaginatedApiCall<JiraProject,'values'>({
auth,
method:HttpMethod.GET,
resourceUri:'/project/search',
propertyName:'values'
})
return response;
}
export async function getIssueTypes({ auth, projectId }: { auth: JiraAuth; projectId: string }) {
const response = await sendJiraRequest({
url: 'issuetype/project',
method: HttpMethod.GET,
auth: auth,
queryParams: {
projectId,
},
});
return response.body as any[];
}
export async function getPriorities({ auth }: { auth: JiraAuth }) {
const response = await sendJiraRequest({
url: 'priority',
method: HttpMethod.GET,
auth: auth,
});
return response.body as any[];
}
export async function executeJql({
auth,
jql,
sanitizeJql,
url,
method,
queryParams,
body,
}: {
auth: JiraAuth;
jql: string;
sanitizeJql: boolean;
url: string;
method: HttpMethod;
queryParams?: QueryParams;
body?: HttpMessageBody;
}) {
let reqJql = jql;
if (sanitizeJql) {
const sanitizeResult = (
await sendJiraRequest({
auth: auth,
url: 'jql/sanitize',
method: HttpMethod.POST,
body: {
queries: [
{
query: jql,
},
],
},
})
).body as {
queries: {
initialQuery: string;
sanitizedQuery: string;
}[];
};
reqJql = sanitizeResult.queries[0].sanitizedQuery;
}
const response = await sendJiraRequest({
auth,
url,
method,
body: {
...body,
jql: reqJql,
},
queryParams,
});
return response.body;
}
export async function searchIssuesByJql({
auth,
jql,
maxResults,
sanitizeJql,
}: {
auth: JiraAuth;
jql: string;
maxResults: number;
sanitizeJql: boolean;
}) {
const respJql = (
(await executeJql({
auth,
url: 'search/jql',
method: HttpMethod.POST,
jql,
body: {
maxResults,
},
sanitizeJql,
})) as { issues: any[] }
).issues;
const issueIds = respJql.map(issue => issue['id']);
if (issueIds.length === 0) {
return [];
}
return (
(await sendJiraRequest({
auth,
url: 'issue/bulkfetch',
method: HttpMethod.POST,
body: {
issueIdsOrKeys: issueIds,
},
})).body as any as { issues: any[] }
).issues;
}
export async function createJiraIssue(data: CreateIssueParams) {
const fields: any = {
project: {
id: data.projectId,
},
summary: data.summary,
issuetype: {
id: data.issueTypeId,
},
};
if (data.assignee) fields.assignee = { id: data.assignee };
if (data.priority) fields.priority = { id: data.priority };
if (data.description)
fields.description = {
content: [
{
content: [
{
text: data.description,
type: 'text',
},
],
type: 'paragraph',
},
],
type: 'doc',
version: 1,
};
if (data.parentKey) {
fields.parent = { key: data.parentKey };
}
const response = await sendJiraRequest({
url: 'issue',
method: HttpMethod.POST,
auth: data.auth,
body: {
fields: fields,
},
});
return response.body;
}
export async function updateJiraIssue(data: UpdateIssueParams) {
const fields: any = {};
if (data.summary) fields.summary = data.summary;
if (data.issueTypeId) fields.issuetype = { id: data.issueTypeId };
if (data.assignee) fields.assignee = { id: data.assignee };
if (data.priority) fields.priority = { id: data.priority };
if (data.description)
fields.description = {
content: [
{
content: [
{
text: data.description,
type: 'text',
},
],
type: 'paragraph',
},
],
type: 'doc',
version: 1,
};
if (data.parentKey) {
fields.parent = { key: data.parentKey };
}
const response = await sendJiraRequest({
url: `issue/${data.issueId}`,
method: HttpMethod.PUT,
auth: data.auth,
queryParams: {
returnIssue: 'true',
},
body: {
fields: fields,
},
});
return response.body;
}
export interface JiraIssueType {
id: string;
description: string;
name: string;
}
export interface JiraProject {
id: string;
key: string;
name: string;
expand: string;
self: string;
projectTypeKey: string;
simplified: boolean;
style: string;
isPrivate: boolean;
properties: any;
}
export interface CreateIssueParams {
auth: JiraAuth;
projectId: string;
summary: string;
description?: string;
issueTypeId: string;
assignee?: string;
priority?: string;
parentKey?: string;
}
export interface UpdateIssueParams {
auth: JiraAuth;
issueId?: string;
summary?: string;
description?: string;
issueTypeId: string;
assignee?: string;
priority?: string;
parentKey?: string;
}
export type RequestParams = Record<string, string | number | string[] | undefined>;
export type JiraApiCallParams = {
auth:JiraAuth,
method: HttpMethod;
resourceUri: string;
query?: RequestParams;
body?: any;
};
export async function jiraApiCall<T extends HttpMessageBody>({
auth,
method,
resourceUri,
query,
body,
}: JiraApiCallParams): Promise<T> {
const baseUrl = `${auth.props.instanceUrl}/rest/api/3`;
const qs: QueryParams = {};
if (query) {
for (const [key, value] of Object.entries(query)) {
if (value !== null && value !== undefined) {
qs[key] = String(value);
}
}
}
const request: HttpRequest = {
method,
url: baseUrl + resourceUri,
queryParams: qs,
body,
authentication: {
type: AuthenticationType.BASIC,
username:auth.props.email,
password:auth.props.apiToken,
},
};
const response = await httpClient.sendRequest<T>(request);
return response.body;
}
export async function jiraPaginatedApiCall<T extends HttpMessageBody, K extends string>({
auth,
method,
resourceUri,
query,
body,
propertyName,
}: JiraApiCallParams & { propertyName: K }): Promise<T[]> {
const qs = query ? query : {};
qs['startAt'] = 0;
qs['maxResults'] = 100;
const resultData: T[] = [];
let hasMore = true;
type PaginatedResponse<T, K extends string> = {
startAt: number;
maxResults: number;
total: number;
isLast?: boolean;
} & Record<K, T[]>;
do {
const response = await jiraApiCall<PaginatedResponse<T, K>>({
auth,
method,
resourceUri,
query: qs,
body,
});
if (isNil(response[propertyName])) {
break;
}
if (Array.isArray(response[propertyName])) {
resultData.push(...response[propertyName]);
}
qs['startAt'] += 100;
hasMore =
response.isLast === undefined
? response.startAt + response.maxResults < response.total
: !response.isLast;
} while (hasMore);
return resultData;
}

View File

@@ -0,0 +1,681 @@
import {
getIssueTypes,
getProjects,
getUsers,
jiraApiCall,
jiraPaginatedApiCall,
sendJiraRequest,
} from '.';
import { DropdownOption, Property } from '@activepieces/pieces-framework';
import { JiraAuth, jiraCloudAuth } from '../../auth';
import { HttpMethod } from '@activepieces/pieces-common';
import { IssueFieldMetaData, IssueTypeMetadata } from './types';
import { isNil } from '@activepieces/shared';
import dayjs from 'dayjs';
export function getProjectIdDropdown(data?: DropdownParams) {
return Property.Dropdown({
auth: jiraCloudAuth,
displayName: data?.displayName ?? 'Project ID or Key',
description: data?.description,
required: data?.required ?? true,
refreshers: data?.refreshers ?? [],
options: async ({ auth }) => {
if (!auth) {
return {
options: [],
};
}
const projects = await getProjects(auth as JiraAuth);
return {
options: projects.map((project) => {
return {
label: project.name,
value: project.id,
};
}),
};
},
});
}
export function getIssueIdDropdown(data?: DropdownParams) {
return Property.Dropdown({
auth: jiraCloudAuth,
displayName: data?.displayName ?? 'Issue ID or Key',
description: data?.description,
required: data?.required ?? true,
refreshers: data?.refreshers ?? [],
options: async ({ auth, projectId }) => {
if (!auth || !projectId) {
return {
disabled: true,
options: [],
};
}
let hasMore = true
let nextPageToken:string|undefined;
const options: DropdownOption<string>[] = [];
do {
const response = await sendJiraRequest({
method: HttpMethod.POST,
url: 'search/jql',
auth: auth as JiraAuth,
body: {
fields: ['summary'],
jql: `project=${projectId}`,
nextPageToken,
maxResults: 100,
},
});
const issueList = response.body as SearchIssuesResponse;
options.push(
...issueList.issues.map((issue) => {
return {
label: `[${issue.key}] ${issue.fields.summary}`,
value: issue.id,
};
}),
);
nextPageToken = issueList.nextPageToken;
hasMore = !issueList.isLast
} while (hasMore);
return {
disabled: false,
options,
};
},
});
}
export function getIssueTypeIdDropdown(data?: DropdownParams) {
return Property.Dropdown({
auth: jiraCloudAuth,
displayName: data?.displayName ?? 'Issue Type',
description: data?.description,
required: data?.required ?? true,
refreshers: data?.refreshers ?? ['projectId'],
options: async ({ auth, projectId }) => {
if (!auth || !projectId) {
return {
options: [],
};
}
const issueTypes = await getIssueTypes({
auth: auth as JiraAuth,
projectId: projectId as string,
});
return {
options: issueTypes.map((issueType) => {
return {
label: issueType.name,
value: issueType.id,
};
}),
};
},
});
}
export function getUsersDropdown(data?: DropdownParams) {
return Property.Dropdown({
auth: jiraCloudAuth,
displayName: data?.displayName ?? 'User',
description: data?.description,
required: data?.required ?? true,
refreshers: data?.refreshers ?? [],
options: async ({ auth }) => {
if (!auth) {
return {
options: [],
};
}
const users = (await getUsers(auth as JiraAuth)).filter(
(user) => user.accountType === 'atlassian',
);
return {
options: users.map((user) => {
return {
label: user.displayName,
value: user.accountId,
};
}),
};
},
});
}
export interface DropdownParams {
required?: boolean;
refreshers?: string[];
displayName?: string;
description?: string;
}
export interface SearchIssuesResponse {
nextPageToken?:string,
isLast: boolean;
issues: Array<{
id: string;
key: string;
fields: {
summary: string;
};
}>;
}
async function fetchGroupsOptions(auth: JiraAuth): Promise<DropdownOption<string>[]> {
const response = await jiraApiCall<{
groups: Array<{ groupId: string; name: string }>;
}>({
auth,
method: HttpMethod.GET,
resourceUri: `/groups/picker`,
});
const options: DropdownOption<string>[] = [];
for (const group of response.groups) {
options.push({
value: group.groupId,
label: group.name,
});
}
return options;
}
async function fetchProjectVersionsOptions(
auth: JiraAuth,
projectId: string,
): Promise<DropdownOption<string>[]> {
const response = await jiraApiCall<Array<{ id: string; name: string }>>({
auth,
method: HttpMethod.GET,
resourceUri: `/project/${projectId}/versions`,
});
const options: DropdownOption<string>[] = [];
for (const version of response) {
options.push({
value: version.id,
label: version.name,
});
}
return options;
}
async function fetchUsersOptions(auth: JiraAuth): Promise<DropdownOption<string>[]> {
const response = (await getUsers(auth)) as Array<{
accountId: string;
accountType: string;
displayName: string;
}>;
const options = response
.filter((user) => user.accountType === 'atlassian')
.map((user) => {
return {
label: user.displayName,
value: user.accountId,
};
});
return options;
}
export const issueTypeIdProp = (displayName: string, required = true) =>
Property.Dropdown({
auth: jiraCloudAuth,
displayName,
refreshers: ['projectId'],
required,
options: async ({ auth, projectId }) => {
if (!auth || !projectId) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your account first',
};
}
const authValue = auth as JiraAuth;
const response = await jiraPaginatedApiCall<IssueTypeMetadata, 'issueTypes'>({
auth: authValue,
resourceUri: `/issue/createmeta/${projectId}/issuetypes`,
propertyName: 'issueTypes',
method: HttpMethod.GET,
});
const options: DropdownOption<string>[] = [];
for (const issueType of response) {
options.push({
value: issueType.id,
label: issueType.name,
});
}
return {
disabled: false,
options,
};
},
});
export const issueLinkTypeIdProp = (displayName: string, required = true) =>
Property.Dropdown({
auth: jiraCloudAuth,
displayName,
refreshers: [],
required,
options: async ({ auth }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your account first',
};
}
const authValue = auth as JiraAuth;
const response = await jiraApiCall<{ issueLinkTypes: Array<{ id: string; inward: string }> }>({
auth: authValue,
resourceUri: `/issueLinkType`,
method: HttpMethod.GET,
});
const options: DropdownOption<string>[] = [];
for (const linkType of response.issueLinkTypes) {
options.push({
value: linkType.id,
label: linkType.inward,
});
}
return {
disabled: false,
options,
};
},
});
export const issueIdOrKeyProp = (displayName: string, required = true) =>
Property.Dropdown({
auth: jiraCloudAuth,
displayName,
refreshers: [],
required,
options: async ({ auth }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your account first',
};
}
const authValue = auth as JiraAuth;
const response = await jiraPaginatedApiCall<{ id: string; key: string }, 'issues'>({
auth: authValue,
resourceUri: '/search',
propertyName: 'issues',
query: { fields: 'summary' },
method: HttpMethod.GET,
});
const options: DropdownOption<string>[] = [];
for (const issue of response) {
options.push({
value: issue.id,
label: issue.key,
});
}
return {
disabled: false,
options,
};
},
});
export const issueStatusIdProp = (displayName: string, required = true) =>
Property.Dropdown({
auth: jiraCloudAuth,
displayName,
refreshers: ['issueId'],
required,
options: async ({ auth, issueId }) => {
if (!auth || !issueId) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your account first and select an issue.',
};
}
const authValue = auth as JiraAuth;
const response = await jiraApiCall<{ transitions: Array<{ id: string; name: string }> }>({
auth: authValue,
method: HttpMethod.GET,
resourceUri: `/issue/${issueId}/transitions`,
});
const options: DropdownOption<string>[] = [];
for (const status of response.transitions ?? []) {
options.push({
value: status.id,
label: status.name,
});
}
return {
disabled: false,
options,
};
},
});
export async function createPropertyDefinition(
auth: JiraAuth,
field: IssueFieldMetaData,
isRequired = false,
) {
// Determine if the field is an array type
const isArray = field.schema.type === 'array';
const fieldType = isArray ? field.schema.items : field.schema.type;
switch (fieldType) {
case 'user': {
const userOptions = await fetchUsersOptions(auth);
return isArray
? Property.StaticMultiSelectDropdown({
displayName: field.name,
required: isRequired,
options: { disabled: false, options: userOptions },
})
: Property.StaticDropdown({
displayName: field.name,
required: isRequired,
options: { disabled: false, options: userOptions },
});
}
case 'group': {
const groupOptions = await fetchGroupsOptions(auth);
return isArray
? Property.StaticMultiSelectDropdown({
displayName: field.name,
required: isRequired,
options: { disabled: false, options: groupOptions },
})
: Property.StaticDropdown({
displayName: field.name,
required: isRequired,
options: { disabled: false, options: groupOptions },
});
}
case 'version': {
const versionOptions = field.allowedValues
? field.allowedValues.map((option) => ({
label: option.name,
value: option.id,
}))
: [];
return isArray
? Property.StaticMultiSelectDropdown({
displayName: field.name,
required: isRequired,
options: { disabled: false, options: versionOptions },
})
: Property.StaticDropdown({
displayName: field.name,
required: isRequired,
options: { disabled: false, options: versionOptions },
});
}
case 'priority': {
const priorityOptions = field.allowedValues
? field.allowedValues.map((option) => ({
label: option.name,
value: option.id,
}))
: [];
return Property.StaticDropdown({
displayName: field.name,
required: isRequired,
options: { disabled: false, options: priorityOptions },
});
}
case 'component': {
const componentOptions = field.allowedValues
? field.allowedValues.map((option) => ({
label: option.name,
value: option.id,
}))
: [];
return isArray
? Property.StaticMultiSelectDropdown({
displayName: field.name,
required: isRequired,
options: { disabled: false, options: componentOptions },
})
: Property.StaticDropdown({
displayName: field.name,
required: isRequired,
options: { disabled: false, options: componentOptions },
});
}
case 'option': {
const options = field.allowedValues
? field.allowedValues.map((option) => ({
label: option.value,
value: option.id,
}))
: [];
return isArray
? Property.StaticMultiSelectDropdown({
displayName: field.name,
required: isRequired,
options: { disabled: false, options: options },
})
: Property.StaticDropdown({
displayName: field.name,
required: isRequired,
options: { disabled: false, options: options },
});
}
case 'string': {
return isArray
? Property.Array({
displayName: field.name,
required: isRequired,
})
: Property.LongText({
displayName: field.name,
required: isRequired,
});
}
case 'date':
return Property.DateTime({
displayName: field.name,
description: 'Provide date in YYYY-MM-DD format.',
required: isRequired,
});
case 'datetime':
return Property.DateTime({
displayName: field.name,
required: isRequired,
});
case 'number':
return Property.Number({
displayName: field.name,
required: isRequired,
});
case 'project':
return Property.ShortText({
displayName: field.name,
required: isRequired,
description: 'Provide project key.',
});
case 'issuelink':
return Property.ShortText({
displayName: field.name,
required: isRequired,
description: 'Provide issue key.',
});
default:
return null;
}
}
// Only certain fields support ADF in issues
// https://developer.atlassian.com/cloud/jira/platform/rest/v3/intro/#version
// - description and environment fields in issues.
// - textarea type custom fields (multi-line text fields) in issues. Single line custom fields (textfield) accept a string and don't handle Atlassian Document Format content.
export function isFieldAdfCompatible(field: IssueFieldMetaData) {
const standardFields = ['description', 'environment'];
return field.schema.custom?.includes('textarea') || standardFields.includes(field.key);
}
function parseArray(value: Array<string> | string): Array<string> {
try {
if (Array.isArray(value)) {
return value;
}
const parsedValue = JSON.parse(value);
if (Array.isArray(parsedValue)) {
return parsedValue;
}
return [];
} catch (e) {
return [];
}
}
// Function to format issue fields
// https://support.atlassian.com/cloud-automation/docs/advanced-field-editing-using-json/#Multi-user-picker-custom-field
export function formatIssueFields(
fieldsMetadata: IssueFieldMetaData[],
fieldsInput: Record<string, any>,
adfFields: string[],
) {
const fieldsOutput: Record<string, any> = {};
for (const field of fieldsMetadata) {
const key = field.key;
const fieldInputValue = fieldsInput[key];
// Skip if value is null, undefined, or empty string
if (isNil(fieldInputValue) || fieldInputValue === '') continue;
switch (field.schema.type) {
case 'array': {
const parsedArrayValue = parseArray(fieldInputValue);
if (parsedArrayValue.length === 0) continue;
fieldsOutput[key] =
field.schema.items === 'string'
? parsedArrayValue // Keep as flat array of strings
: parsedArrayValue.map((item) =>
field.schema.items === 'group' ? { groupId: item } : { id: item },
);
break;
}
case 'user':
fieldsOutput[key] = { accountId: fieldInputValue };
break;
case 'version':
case 'option':
case 'priority':
case 'issuetype':
case 'component':
fieldsOutput[key] = { id: fieldInputValue };
break;
case 'issuelink':
fieldsOutput[key] = { key: fieldInputValue };
break;
case 'group':
fieldsOutput[key] = { groupId: fieldInputValue };
break;
case 'date':
fieldsOutput[key] = dayjs(fieldInputValue).format('YYYY-MM-DD');
break;
case 'datetime':
fieldsOutput[key] = dayjs(fieldInputValue).toISOString();
break;
case 'number':
fieldsOutput[key] = Number(fieldInputValue);
break;
case 'project':
fieldsOutput[key] = { key: fieldInputValue };
break;
case 'string': {
if (isFieldAdfCompatible(field)) {
if (adfFields.includes(key)) {
fieldsOutput[key] = JSON.parse(fieldInputValue);
} else {
fieldsOutput[key] = {
type: 'doc',
version: 1,
content: [
{
type: 'paragraph',
content: [{ text: fieldInputValue, type: 'text' }],
},
],
};
}
} else {
fieldsOutput[key] = fieldInputValue;
}
break;
}
}
}
return fieldsOutput;
}
export function transformCustomFields(
fieldsMetadata: IssueFieldMetaData[],
fieldsInput: Record<string, any>,
): Record<string, any> {
const result: Record<string, any> = {};
const fieldsMapping = fieldsMetadata.reduce((acc, field) => {
acc[field.key] = field.name;
return acc;
}, {} as Record<string, string>);
for (const [key, value] of Object.entries(fieldsInput)) {
result[key.startsWith('customfield_') ? fieldsMapping[key] ?? key : key] = value;
}
return result;
}

View File

@@ -0,0 +1,40 @@
export type IssueTypeMetadata = {
id:string,
name:string
}
export type IssueFieldMetaData ={
required:boolean,
name:string,
key:string,
fieldId:string,
schema:{
type:"string"|"date"|"datetime"|"array"|"number"|"option"|"user"|"group"|"version"|"project"|"issuelink"|"priority"|"issuetype"|"component", // "option-with-child",
items:string,
custom?:string,
customId?:number,
},
allowedValues?:Array<{value:string,id:string,name:string}>
}
export const VALID_CUSTOM_FIELD_TYPES = [
'userpicker',
'participants',
'multiuserpicker',
'multiversion',
'version',
'multigrouppicker',
'grouppicker',
'multicheckboxes',
'multiselect',
'datepicker',
'datetime',
'labels',
'float',
'textarea',
'radiobuttons',
'select',
'textfield',
'url',
'project',
];

View File

@@ -0,0 +1,70 @@
import {
PiecePropValueSchema,
Property,
TriggerStrategy,
createTrigger,
} from '@activepieces/pieces-framework';
import {
Polling,
DedupeStrategy,
pollingHelper,
} from '@activepieces/pieces-common';
import { JiraAuth, jiraCloudAuth } from '../../auth';
import { searchIssuesByJql } from '../common';
import dayjs from 'dayjs';
const polling: Polling<
JiraAuth,
{ jql?: string; sanitizeJql?: boolean }
> = {
strategy: DedupeStrategy.TIMEBASED,
items: async ({ auth, lastFetchEpochMS, propsValue }) => {
const { jql, sanitizeJql } = propsValue;
const searchQuery = `${jql ? jql + ' AND ' : ''}created > '${dayjs(
lastFetchEpochMS
).format('YYYY-MM-DD HH:mm')}'`;
const issues = await searchIssuesByJql({
auth,
jql: searchQuery,
maxResults: 50,
sanitizeJql: sanitizeJql ?? false,
});
return issues.map((issue) => ({
epochMilliSeconds: Date.parse(issue.fields.created),
data: issue,
}));
},
};
export const newIssue = createTrigger({
name: 'new_issue',
displayName: 'New Issue',
description: 'Triggers when a new issue is created',
auth: jiraCloudAuth,
type: TriggerStrategy.POLLING,
props: {
jql: Property.LongText({
displayName: 'JQL',
description: 'Use to filter issues watched',
required: false,
}),
sanitizeJql: Property.Checkbox({
displayName: 'Sanitize JQL',
required: false,
defaultValue: true,
}),
},
sampleData: {},
async onEnable(context) {
await pollingHelper.onEnable(polling, context);
},
async onDisable(context) {
await pollingHelper.onDisable(polling, context);
},
async run(context) {
return await pollingHelper.poll(polling, context);
},
async test(context) {
return await pollingHelper.test(polling, context);
},
});

View File

@@ -0,0 +1,70 @@
import {
PiecePropValueSchema,
Property,
TriggerStrategy,
createTrigger,
} from '@activepieces/pieces-framework';
import {
Polling,
DedupeStrategy,
pollingHelper,
} from '@activepieces/pieces-common';
import { JiraAuth, jiraCloudAuth } from '../../auth';
import { searchIssuesByJql } from '../common';
import dayjs from 'dayjs';
const polling: Polling<
JiraAuth,
{ jql?: string; sanitizeJql?: boolean }
> = {
strategy: DedupeStrategy.TIMEBASED,
items: async ({ auth, lastFetchEpochMS, propsValue }) => {
const { jql, sanitizeJql } = propsValue;
const searchQuery = `${jql ? jql + ' AND ' : ''}updated > '${dayjs(
lastFetchEpochMS
).format('YYYY-MM-DD HH:mm')}'`;
const issues = await searchIssuesByJql({
auth,
jql: searchQuery,
maxResults: 50,
sanitizeJql: sanitizeJql ?? false,
});
return issues.map((issue) => ({
epochMilliSeconds: Date.parse(issue.fields.statuscategorychangedate),
data: issue,
}));
},
};
export const updatedIssueStatus = createTrigger({
name: 'updated_issue_status',
displayName: 'Updated Issue Status',
description: 'Triggers when an issue status is updated',
auth: jiraCloudAuth,
type: TriggerStrategy.POLLING,
props: {
jql: Property.LongText({
displayName: 'JQL',
description: 'Use to filter issues watched',
required: false,
}),
sanitizeJql: Property.Checkbox({
displayName: 'Sanitize JQL',
required: false,
defaultValue: true,
}),
},
sampleData: {},
async onEnable(context) {
await pollingHelper.onEnable(polling, context);
},
async onDisable(context) {
await pollingHelper.onDisable(polling, context);
},
async run(context) {
return await pollingHelper.poll(polling, context);
},
async test(context) {
return await pollingHelper.test(polling, context);
},
});

View File

@@ -0,0 +1,69 @@
import {
PiecePropValueSchema,
Property,
TriggerStrategy,
createTrigger,
} from '@activepieces/pieces-framework';
import {
Polling,
DedupeStrategy,
pollingHelper,
} from '@activepieces/pieces-common';
import { JiraAuth, jiraCloudAuth } from '../../auth';
import { searchIssuesByJql } from '../common';
import dayjs from 'dayjs';
const polling: Polling< JiraAuth,
{ jql?: string; sanitizeJql?: boolean }
> = {
strategy: DedupeStrategy.TIMEBASED,
items: async ({ auth, lastFetchEpochMS, propsValue }) => {
const { jql, sanitizeJql } = propsValue;
const searchQuery = `${jql ? jql + ' AND ' : ''}updated > '${dayjs(
lastFetchEpochMS
).format('YYYY-MM-DD HH:mm')}'`;
const issues = await searchIssuesByJql({
auth,
jql: searchQuery,
maxResults: 50,
sanitizeJql: sanitizeJql ?? false,
});
return issues.map((issue) => ({
epochMilliSeconds: Date.parse(issue.fields.updated),
data: issue,
}));
},
};
export const updatedIssue = createTrigger({
name: 'updated_issue',
displayName: 'Updated Issue',
description: 'Triggers when an issue is updated',
auth: jiraCloudAuth,
type: TriggerStrategy.POLLING,
props: {
jql: Property.LongText({
displayName: 'JQL',
description: 'Use to filter issues watched',
required: false,
}),
sanitizeJql: Property.Checkbox({
displayName: 'Sanitize JQL',
required: false,
defaultValue: false,
}),
},
sampleData: {},
async onEnable(context) {
await pollingHelper.onEnable(polling, context);
},
async onDisable(context) {
await pollingHelper.onDisable(polling, context);
},
async run(context) {
return await pollingHelper.poll(polling, context);
},
async test(context) {
return await pollingHelper.test(polling, context);
},
});