@hostwebhook/node-types 1.64.0 → 1.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/connections.js +1 -0
- package/dist/dispatch.js +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -2
- package/dist/jira-operations.d.ts +164 -0
- package/dist/jira-operations.js +474 -0
- package/dist/registry.js +19 -0
- package/dist/types.d.ts +1 -1
- package/dist/ui.js +2 -0
- package/package.json +1 -1
package/dist/connections.js
CHANGED
|
@@ -80,6 +80,7 @@ exports.NODE_CONNECTIONS = {
|
|
|
80
80
|
mailchimpAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
81
81
|
shopifyAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
82
82
|
githubAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
83
|
+
jiraAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
83
84
|
googleContactsAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
84
85
|
googleAnalyticsAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
85
86
|
notionAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
package/dist/dispatch.js
CHANGED
|
@@ -55,6 +55,7 @@ exports.NODE_DISPATCH = {
|
|
|
55
55
|
mailchimpAction: { service: 'mailchimpActionsService', collection: 'mailchimpactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
56
56
|
shopifyAction: { service: 'shopifyActionsService', collection: 'shopifyactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
57
57
|
githubAction: { service: 'githubActionsService', collection: 'githubactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
58
|
+
jiraAction: { service: 'jiraActionsService', collection: 'jiraactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
58
59
|
slackAction: { service: 'slackActionsService', collection: 'slackactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
59
60
|
googleContactsAction: { service: 'googleContactsActionsService', collection: 'googlecontactsactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
|
60
61
|
googleAnalyticsAction: { service: 'googleAnalyticsActionsService', collection: 'googleanalyticsactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded' },
|
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,8 @@ export { SHOPIFY_OPERATIONS, SHOPIFY_OPERATION_SPECS, SHOPIFY_TAGGABLE_RESOURCES
|
|
|
30
30
|
export type { ShopifyOperation, ShopifyTaggableResource, ShopifySearchableResource, ShopifyParamSpec, ShopifyOperationSpec, } from './shopify-operations';
|
|
31
31
|
export { GITHUB_OPERATIONS, GITHUB_OPERATION_SPECS, GITHUB_DROPDOWN_OPERATIONS, GITHUB_ITERABLE_OPERATIONS, isGithubOperation, } from './github-operations';
|
|
32
32
|
export type { GithubOperation, GithubParamType, GithubParamSpec, GithubOperationSpec, } from './github-operations';
|
|
33
|
+
export { JIRA_OPERATIONS, JIRA_OPERATION_SPECS, JIRA_DROPDOWN_OPERATIONS, JIRA_ITERABLE_OPERATIONS, isJiraOperation, } from './jira-operations';
|
|
34
|
+
export type { JiraOperation, JiraParamType, JiraParamSpec, JiraOperationSpec, } from './jira-operations';
|
|
33
35
|
export { SLACK_OPERATIONS, SLACK_OPERATION_SPECS, isSlackOperation, } from './slack-operations';
|
|
34
36
|
export type { SlackOperation, SlackParamSpec, SlackOperationSpec, } from './slack-operations';
|
|
35
37
|
export { SHEETS_OPERATIONS, SHEETS_OPERATION_SPECS, isSheetsOperation, } from './sheets-operations';
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DRIVE_OPERATION_SPECS = exports.DRIVE_OPERATIONS = exports.GOOGLE_CALENDAR_TOOLKIT_BY_TOOL_NAME = exports.GOOGLE_CALENDAR_TOOLKIT_SPECS = exports.isGoogleCalendarOperation = exports.GOOGLE_CALENDAR_OPERATION_SPECS = exports.GOOGLE_CALENDAR_OPERATIONS = exports.isGmailOperation = exports.resolveGmailSendFields = exports.GMAIL_SEND_LEGACY_FIELDS = exports.NATIVE_EMAIL_TOOLKIT_BY_TOOL_NAME = exports.GMAIL_TOOLKIT_BY_TOOL_NAME = exports.NATIVE_EMAIL_TOOLKIT_SPECS = exports.GMAIL_SEND_AND_WAIT_TOOL_SPEC = exports.GMAIL_ALL_TOOLKIT_SPECS = exports.GMAIL_TOOLKIT_SPECS = exports.GMAIL_TOOLKIT_OPERATIONS = exports.GMAIL_DROPDOWN_OPERATIONS = exports.GMAIL_OPERATION_GROUPS = exports.GMAIL_OPERATION_SPECS = exports.GMAIL_OPERATIONS = exports.versionCatalogErrors = exports.fieldsLost = exports.fieldsLostBetween = exports.currentVersion = exports.versionSpec = exports.versionsOf = exports.isVersioned = exports.NODE_TYPE_TO_PREFIX = exports.PREFIX_TO_NODE_TYPE = exports.NODE_STATE_KEYS = exports.NODE_COLORS = exports.NODE_DETAIL_PATHS = exports.getNodeRegistryEntry = exports.NODE_REGISTRY = exports.getNodeDispatchConfig = exports.getAllNodeCollections = exports.NODE_DISPATCH = exports.resolveNodeId = exports.PREFIX_TO_TYPE = exports.NODE_UI = exports.ALL_NODE_TYPES = exports.isNodeType = exports.isTerminal = exports.canSendToNodes = exports.canReceiveFromNodes = exports.canReceiveFrom = exports.NODE_CONNECTIONS = exports.iterableMeta = exports.singleMeta = void 0;
|
|
4
|
-
exports.
|
|
5
|
-
exports.isCredentialType = exports.getCredentialType = exports.credentialTypeValues = exports.CREDENTIAL_TYPE_VALUES = exports.CREDENTIAL_TYPES = exports.getModelLabel = exports.getDefaultModel = exports.getModelsFor = exports.MODEL_CONTEXT_WINDOWS = exports.LLM_MODELS = exports.LLM_PROVIDERS = exports.DOCS_TOOLKIT_DEFAULTABLE = exports.DOCS_TOOLKIT_BY_TOOL_NAME = exports.DOCS_TOOLKIT_SPECS = exports.isDocsOperation = exports.DOCS_OPERATION_SPECS = exports.DOCS_OPERATIONS = exports.isMongoOperation = exports.MONGO_OPERATION_SPECS = exports.MONGO_OPERATIONS = exports.isPostgresOperation = exports.POSTGRES_OPERATION_SPECS = exports.POSTGRES_MODES = exports.POSTGRES_OPERATIONS = exports.isNotionOperation = void 0;
|
|
4
|
+
exports.GOOGLE_ANALYTICS_OPERATION_SPECS = exports.GOOGLE_ANALYTICS_OPERATIONS = exports.isGoogleContactsOperation = exports.GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS = exports.GOOGLE_CONTACTS_OPERATION_GROUPS = exports.GOOGLE_CONTACTS_OPERATION_SPECS = exports.GOOGLE_CONTACTS_OPERATIONS_V2 = exports.GOOGLE_CONTACTS_OPERATIONS_V1 = exports.GOOGLE_CONTACTS_OPERATIONS = exports.SHEETS_TOOLKIT_DEFAULTABLE = exports.SHEETS_TOOLKIT_BY_TOOL_NAME = exports.SHEETS_TOOLKIT_SPECS = exports.isSheetsOperation = exports.SHEETS_OPERATION_SPECS = exports.SHEETS_OPERATIONS = exports.isSlackOperation = exports.SLACK_OPERATION_SPECS = exports.SLACK_OPERATIONS = exports.isJiraOperation = exports.JIRA_ITERABLE_OPERATIONS = exports.JIRA_DROPDOWN_OPERATIONS = exports.JIRA_OPERATION_SPECS = exports.JIRA_OPERATIONS = exports.isGithubOperation = exports.GITHUB_ITERABLE_OPERATIONS = exports.GITHUB_DROPDOWN_OPERATIONS = exports.GITHUB_OPERATION_SPECS = exports.GITHUB_OPERATIONS = exports.isShopifyOperation = exports.SHOPIFY_SEARCHABLE_RESOURCES = exports.SHOPIFY_TAGGABLE_RESOURCES = exports.SHOPIFY_OPERATION_SPECS = exports.SHOPIFY_OPERATIONS = exports.isMailchimpOperation = exports.MAILCHIMP_CONTACT_STATUSES = exports.MAILCHIMP_OPERATION_SPECS = exports.MAILCHIMP_OPERATIONS = exports.isDiscordOperation = exports.DISCORD_OPERATION_SPECS = exports.DISCORD_OPERATIONS = exports.isWhatsAppOperation = exports.WHATSAPP_OPERATIONS = exports.TELEGRAM_TOOLKIT_BY_TOOL_NAME = exports.TELEGRAM_TOOLKIT_SPECS = exports.isTelegramOperation = exports.TELEGRAM_OPERATION_SPECS = exports.TELEGRAM_OPERATIONS = exports.DRIVE_TOOLKIT_BY_TOOL_NAME = exports.DRIVE_TOOLKIT_SPECS = exports.isDriveOperation = void 0;
|
|
5
|
+
exports.isCredentialType = exports.getCredentialType = exports.credentialTypeValues = exports.CREDENTIAL_TYPE_VALUES = exports.CREDENTIAL_TYPES = exports.getModelLabel = exports.getDefaultModel = exports.getModelsFor = exports.MODEL_CONTEXT_WINDOWS = exports.LLM_MODELS = exports.LLM_PROVIDERS = exports.DOCS_TOOLKIT_DEFAULTABLE = exports.DOCS_TOOLKIT_BY_TOOL_NAME = exports.DOCS_TOOLKIT_SPECS = exports.isDocsOperation = exports.DOCS_OPERATION_SPECS = exports.DOCS_OPERATIONS = exports.isMongoOperation = exports.MONGO_OPERATION_SPECS = exports.MONGO_OPERATIONS = exports.isPostgresOperation = exports.POSTGRES_OPERATION_SPECS = exports.POSTGRES_MODES = exports.POSTGRES_OPERATIONS = exports.isNotionOperation = exports.NOTION_DROPDOWN_OPERATIONS = exports.NOTION_OPERATION_SPECS = exports.NOTION_OPERATIONS = exports.isGoogleAnalyticsOperation = exports.GOOGLE_ANALYTICS_DROPDOWN_OPERATIONS = void 0;
|
|
6
6
|
var types_1 = require("./types");
|
|
7
7
|
Object.defineProperty(exports, "singleMeta", { enumerable: true, get: function () { return types_1.singleMeta; } });
|
|
8
8
|
Object.defineProperty(exports, "iterableMeta", { enumerable: true, get: function () { return types_1.iterableMeta; } });
|
|
@@ -104,6 +104,12 @@ Object.defineProperty(exports, "GITHUB_OPERATION_SPECS", { enumerable: true, get
|
|
|
104
104
|
Object.defineProperty(exports, "GITHUB_DROPDOWN_OPERATIONS", { enumerable: true, get: function () { return github_operations_1.GITHUB_DROPDOWN_OPERATIONS; } });
|
|
105
105
|
Object.defineProperty(exports, "GITHUB_ITERABLE_OPERATIONS", { enumerable: true, get: function () { return github_operations_1.GITHUB_ITERABLE_OPERATIONS; } });
|
|
106
106
|
Object.defineProperty(exports, "isGithubOperation", { enumerable: true, get: function () { return github_operations_1.isGithubOperation; } });
|
|
107
|
+
var jira_operations_1 = require("./jira-operations");
|
|
108
|
+
Object.defineProperty(exports, "JIRA_OPERATIONS", { enumerable: true, get: function () { return jira_operations_1.JIRA_OPERATIONS; } });
|
|
109
|
+
Object.defineProperty(exports, "JIRA_OPERATION_SPECS", { enumerable: true, get: function () { return jira_operations_1.JIRA_OPERATION_SPECS; } });
|
|
110
|
+
Object.defineProperty(exports, "JIRA_DROPDOWN_OPERATIONS", { enumerable: true, get: function () { return jira_operations_1.JIRA_DROPDOWN_OPERATIONS; } });
|
|
111
|
+
Object.defineProperty(exports, "JIRA_ITERABLE_OPERATIONS", { enumerable: true, get: function () { return jira_operations_1.JIRA_ITERABLE_OPERATIONS; } });
|
|
112
|
+
Object.defineProperty(exports, "isJiraOperation", { enumerable: true, get: function () { return jira_operations_1.isJiraOperation; } });
|
|
107
113
|
var slack_operations_1 = require("./slack-operations");
|
|
108
114
|
Object.defineProperty(exports, "SLACK_OPERATIONS", { enumerable: true, get: function () { return slack_operations_1.SLACK_OPERATIONS; } });
|
|
109
115
|
Object.defineProperty(exports, "SLACK_OPERATION_SPECS", { enumerable: true, get: function () { return slack_operations_1.SLACK_OPERATION_SPECS; } });
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Las operaciones del nodo `jiraAction` — la lista que comparten la api, el
|
|
3
|
+
* dashboard y el broker.
|
|
4
|
+
*
|
|
5
|
+
* Mismo patrón que `github-operations.ts` y `shopify-operations.ts`.
|
|
6
|
+
*
|
|
7
|
+
* ── Contra qué está escrito ──
|
|
8
|
+
*
|
|
9
|
+
* Jira Cloud, **REST API v3**, servida en
|
|
10
|
+
* `https://api.atlassian.com/ex/jira/{cloudId}/rest/api/3/…`. El `cloudId` sale
|
|
11
|
+
* de la credencial `oauth2_atlassian`, que ya existe: la montó el trigger de
|
|
12
|
+
* Jira y aquí se reutiliza entera.
|
|
13
|
+
*
|
|
14
|
+
* El permiso que hace falta para escribir es **`write:jira-work`**. Se añadió a
|
|
15
|
+
* los de por defecto en api#384; toda credencial anterior a eso es de sólo
|
|
16
|
+
* lectura hasta que su dueño la reconecte, y el síntoma es un 403 al ejecutar,
|
|
17
|
+
* no un fallo al guardar.
|
|
18
|
+
*
|
|
19
|
+
* ── La decisión que ordena todo lo demás: ADF ──
|
|
20
|
+
*
|
|
21
|
+
* En la v3, `description` y el `body` de un comentario **no son texto**: son
|
|
22
|
+
* Atlassian Document Format, un árbol JSON de nodos tipados. Mandar una cadena
|
|
23
|
+
* da 400.
|
|
24
|
+
*
|
|
25
|
+
* La salida fácil sería escribir con la v2, que sí acepta texto plano y sigue
|
|
26
|
+
* soportada. No se hace, y el motivo está escrito en nuestro propio repositorio
|
|
27
|
+
* desde antes de este nodo: `jira-watch.service.ts` excluye `description` al
|
|
28
|
+
* hidratar campos y explica por qué — *"la REST devuelve ADF —un objeto— pero
|
|
29
|
+
* el webhook manda texto plano; mezclar formas rompería las plantillas de
|
|
30
|
+
* abajo, que esperan cadenas"*.
|
|
31
|
+
*
|
|
32
|
+
* O sea: si este nodo leyera con una versión y escribiera con otra,
|
|
33
|
+
* `{{payload.fields.description}}` significaría una cosa cuando el flujo lo
|
|
34
|
+
* dispara un trigger de Jira y otra cuando lo consulta este nodo. Por eso:
|
|
35
|
+
*
|
|
36
|
+
* - **Todo en v3**, para leer y para escribir.
|
|
37
|
+
* - Al escribir, el texto del usuario se envuelve en ADF.
|
|
38
|
+
* - Al leer, el ADF se aplana a texto, y el árbol original viaja en una clave
|
|
39
|
+
* hermana para quien lo necesite.
|
|
40
|
+
* - `bodyFormat` deja pegar ADF crudo a quien quiera tablas o paneles.
|
|
41
|
+
*
|
|
42
|
+
* ── La otra trampa: la búsqueda cambió de sitio ──
|
|
43
|
+
*
|
|
44
|
+
* `GET/POST /rest/api/3/search` está **deprecado**. El sustituto es
|
|
45
|
+
* `POST /rest/api/3/search/jql`, y no es un renombre: pagina con
|
|
46
|
+
* `nextPageToken` en vez de `startAt`, **no devuelve `total`** (para contar hay
|
|
47
|
+
* `/search/approximate-count`) y los campos hay que pedirlos explícitamente.
|
|
48
|
+
* Casi todo ejemplo que hay por internet usa el endpoint muerto.
|
|
49
|
+
*
|
|
50
|
+
* ── Lo que NO es una operación ──
|
|
51
|
+
*
|
|
52
|
+
* Listar proyectos, tipos de incidencia, transiciones, estados o usuarios
|
|
53
|
+
* asignables **no** son operaciones: son endpoints del controlador que alimentan
|
|
54
|
+
* los selectores, como Drive alimenta su selector de ficheros. Una operación
|
|
55
|
+
* existe para producir payload en una corrida; esto otro es para rellenar el
|
|
56
|
+
* formulario. Mezclarlos llena el desplegable de cosas que nadie quiere
|
|
57
|
+
* ejecutar.
|
|
58
|
+
*
|
|
59
|
+
* ── Decisiones propias de Jira ──
|
|
60
|
+
*
|
|
61
|
+
* - El proyecto y el tipo de incidencia se ELIGEN de una lista viva. Un
|
|
62
|
+
* `issuetype` se identifica por un id numérico que nadie sabe de memoria.
|
|
63
|
+
* - **Las transiciones no son una lista fija.** Dependen del flujo de trabajo
|
|
64
|
+
* Y del estado actual de la incidencia, así que el selector las pide para
|
|
65
|
+
* esa incidencia concreta. Ofrecer una lista global sería ofrecer
|
|
66
|
+
* transiciones que fallan al ejecutarse.
|
|
67
|
+
* - Los campos personalizados van en JSON crudo. Cada tipo tiene su forma
|
|
68
|
+
* (`select`, `date`, `relation`, usuario…) y fingir que caben en tres
|
|
69
|
+
* campos genéricos produce un 400 que no nombra el campo.
|
|
70
|
+
* - Ninguna operación declarada sin implementar: el enum ES el contrato de la
|
|
71
|
+
* pantalla, y una operación declarada se pinta en el desplegable y falla al
|
|
72
|
+
* ejecutarse.
|
|
73
|
+
*/
|
|
74
|
+
export declare const JIRA_OPERATIONS: readonly ["createIssue", "getIssue", "updateIssue", "deleteIssue", "assignIssue", "transitionIssue", "addComment", "getComments", "addAttachment", "addWorklog", "linkIssues", "searchIssues"];
|
|
75
|
+
export type JiraOperation = (typeof JIRA_OPERATIONS)[number];
|
|
76
|
+
/** Type guard — para validar entrada no fiable (DTOs, tool calls). */
|
|
77
|
+
export declare function isJiraOperation(value: unknown): value is JiraOperation;
|
|
78
|
+
/**
|
|
79
|
+
* Las operaciones que devuelven un ARRAY y por tanto obligan a marcar
|
|
80
|
+
* `_meta.iterable` en `getOutputPayload`.
|
|
81
|
+
*
|
|
82
|
+
* Vive aquí y no sólo en la api porque olvidarlo no falla al compilar: falla
|
|
83
|
+
* en producción, callado, con el nodo de abajo recibiendo el sobre entero en
|
|
84
|
+
* vez de un elemento y el PayloadViewer pintando rutas con corchetes que nadie
|
|
85
|
+
* puede usar.
|
|
86
|
+
*/
|
|
87
|
+
export declare const JIRA_ITERABLE_OPERATIONS: readonly JiraOperation[];
|
|
88
|
+
export type JiraParamType =
|
|
89
|
+
/** Selector vivo de proyectos que alcanza la credencial. Guarda la CLAVE
|
|
90
|
+
* (`ACME`), no el id: es lo que se lee en las URLs y en el JQL. */
|
|
91
|
+
'jiraProject'
|
|
92
|
+
/** Selector de tipos de incidencia del proyecto elegido. Guarda el id, que
|
|
93
|
+
* es lo que pide la API — el nombre no es único entre proyectos. */
|
|
94
|
+
| 'jiraIssueType'
|
|
95
|
+
/** La incidencia sobre la que se actúa. Se escribe o se resuelve de una
|
|
96
|
+
* plantilla: `{{payload.issueKey}}` es el caso del 90%. */
|
|
97
|
+
| 'jiraIssueKey'
|
|
98
|
+
/** Transiciones legales para la incidencia elegida. Depende del flujo de
|
|
99
|
+
* trabajo Y del estado actual, así que se piden en el momento. */
|
|
100
|
+
| 'jiraTransition'
|
|
101
|
+
/** Usuarios asignables en el proyecto elegido. Guarda el `accountId`. */
|
|
102
|
+
| 'jiraUser'
|
|
103
|
+
/** Tipos de enlace entre incidencias (`Blocks`, `Relates`…). */
|
|
104
|
+
| 'jiraLinkType'
|
|
105
|
+
/** Texto con autocompletado de `{{payload.*}}`. */
|
|
106
|
+
| 'template'
|
|
107
|
+
/** Área de texto larga, también con autocompletado. */
|
|
108
|
+
| 'textarea'
|
|
109
|
+
/** `<select>` normal; las etiquetas no son los valores, `options` obliga. */
|
|
110
|
+
| 'select' | 'number'
|
|
111
|
+
/**
|
|
112
|
+
* `<select>` de `''` / `'true'` / `'false'` que se guarda como BOOLEANO, o
|
|
113
|
+
* no se guarda si es `''`. "— sin tocar —" tiene que significar que la clave
|
|
114
|
+
* no viaja, no que viaja en `false`.
|
|
115
|
+
*/
|
|
116
|
+
| 'booleanSelect'
|
|
117
|
+
/** Área de texto para JSON crudo (campos personalizados, ADF). */
|
|
118
|
+
| 'json';
|
|
119
|
+
export interface JiraParamSpec {
|
|
120
|
+
/** La clave de `operationConfig`. Es lo que lee la api, no un nombre de UI. */
|
|
121
|
+
name: string;
|
|
122
|
+
label: string;
|
|
123
|
+
type: JiraParamType;
|
|
124
|
+
required?: boolean;
|
|
125
|
+
/** Ayuda bajo el campo. Texto plano: el paquete no lleva React. */
|
|
126
|
+
description?: string;
|
|
127
|
+
placeholder?: string;
|
|
128
|
+
/** Prerrellenado cuando la config guardada no trae valor para esta clave. */
|
|
129
|
+
default?: string | number;
|
|
130
|
+
/** Obligatorio en `select` y `booleanSelect` — etiqueta y valor difieren. */
|
|
131
|
+
options?: ReadonlyArray<{
|
|
132
|
+
value: string;
|
|
133
|
+
label: string;
|
|
134
|
+
}>;
|
|
135
|
+
min?: number;
|
|
136
|
+
max?: number;
|
|
137
|
+
/** Se pinta en la pestaña "Advanced" en vez de en "Config". */
|
|
138
|
+
advanced?: boolean;
|
|
139
|
+
/** Sólo visible cuando un campo hermano vale uno de estos. */
|
|
140
|
+
showWhen?: {
|
|
141
|
+
field: string;
|
|
142
|
+
in: ReadonlyArray<string>;
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
export interface JiraOperationSpec {
|
|
146
|
+
/** Etiqueta de la fila en el desplegable de operación. */
|
|
147
|
+
label: string;
|
|
148
|
+
/** Sub-línea bajo la etiqueta. */
|
|
149
|
+
description: string;
|
|
150
|
+
/** Ruta a la que mapea, tal cual, para poder cotejarla con los docs. */
|
|
151
|
+
apiRoute: string;
|
|
152
|
+
/** Esquema de parámetros. El orden ES el orden en pantalla. */
|
|
153
|
+
params: JiraParamSpec[];
|
|
154
|
+
}
|
|
155
|
+
export declare const JIRA_OPERATION_SPECS: Record<JiraOperation, JiraOperationSpec>;
|
|
156
|
+
/**
|
|
157
|
+
* Lo que se ofrece en el desplegable de operación.
|
|
158
|
+
*
|
|
159
|
+
* Hoy son todas. Existe como constante aparte —y no como alias de
|
|
160
|
+
* `JIRA_OPERATIONS`— porque es el punto donde otros nodos han necesitado
|
|
161
|
+
* esconder una operación sin sacarla del enum: retirarla del enum rompe las que
|
|
162
|
+
* ya estaban guardadas en la base de datos.
|
|
163
|
+
*/
|
|
164
|
+
export declare const JIRA_DROPDOWN_OPERATIONS: readonly JiraOperation[];
|
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.JIRA_DROPDOWN_OPERATIONS = exports.JIRA_OPERATION_SPECS = exports.JIRA_ITERABLE_OPERATIONS = exports.JIRA_OPERATIONS = void 0;
|
|
4
|
+
exports.isJiraOperation = isJiraOperation;
|
|
5
|
+
/**
|
|
6
|
+
* Las operaciones del nodo `jiraAction` — la lista que comparten la api, el
|
|
7
|
+
* dashboard y el broker.
|
|
8
|
+
*
|
|
9
|
+
* Mismo patrón que `github-operations.ts` y `shopify-operations.ts`.
|
|
10
|
+
*
|
|
11
|
+
* ── Contra qué está escrito ──
|
|
12
|
+
*
|
|
13
|
+
* Jira Cloud, **REST API v3**, servida en
|
|
14
|
+
* `https://api.atlassian.com/ex/jira/{cloudId}/rest/api/3/…`. El `cloudId` sale
|
|
15
|
+
* de la credencial `oauth2_atlassian`, que ya existe: la montó el trigger de
|
|
16
|
+
* Jira y aquí se reutiliza entera.
|
|
17
|
+
*
|
|
18
|
+
* El permiso que hace falta para escribir es **`write:jira-work`**. Se añadió a
|
|
19
|
+
* los de por defecto en api#384; toda credencial anterior a eso es de sólo
|
|
20
|
+
* lectura hasta que su dueño la reconecte, y el síntoma es un 403 al ejecutar,
|
|
21
|
+
* no un fallo al guardar.
|
|
22
|
+
*
|
|
23
|
+
* ── La decisión que ordena todo lo demás: ADF ──
|
|
24
|
+
*
|
|
25
|
+
* En la v3, `description` y el `body` de un comentario **no son texto**: son
|
|
26
|
+
* Atlassian Document Format, un árbol JSON de nodos tipados. Mandar una cadena
|
|
27
|
+
* da 400.
|
|
28
|
+
*
|
|
29
|
+
* La salida fácil sería escribir con la v2, que sí acepta texto plano y sigue
|
|
30
|
+
* soportada. No se hace, y el motivo está escrito en nuestro propio repositorio
|
|
31
|
+
* desde antes de este nodo: `jira-watch.service.ts` excluye `description` al
|
|
32
|
+
* hidratar campos y explica por qué — *"la REST devuelve ADF —un objeto— pero
|
|
33
|
+
* el webhook manda texto plano; mezclar formas rompería las plantillas de
|
|
34
|
+
* abajo, que esperan cadenas"*.
|
|
35
|
+
*
|
|
36
|
+
* O sea: si este nodo leyera con una versión y escribiera con otra,
|
|
37
|
+
* `{{payload.fields.description}}` significaría una cosa cuando el flujo lo
|
|
38
|
+
* dispara un trigger de Jira y otra cuando lo consulta este nodo. Por eso:
|
|
39
|
+
*
|
|
40
|
+
* - **Todo en v3**, para leer y para escribir.
|
|
41
|
+
* - Al escribir, el texto del usuario se envuelve en ADF.
|
|
42
|
+
* - Al leer, el ADF se aplana a texto, y el árbol original viaja en una clave
|
|
43
|
+
* hermana para quien lo necesite.
|
|
44
|
+
* - `bodyFormat` deja pegar ADF crudo a quien quiera tablas o paneles.
|
|
45
|
+
*
|
|
46
|
+
* ── La otra trampa: la búsqueda cambió de sitio ──
|
|
47
|
+
*
|
|
48
|
+
* `GET/POST /rest/api/3/search` está **deprecado**. El sustituto es
|
|
49
|
+
* `POST /rest/api/3/search/jql`, y no es un renombre: pagina con
|
|
50
|
+
* `nextPageToken` en vez de `startAt`, **no devuelve `total`** (para contar hay
|
|
51
|
+
* `/search/approximate-count`) y los campos hay que pedirlos explícitamente.
|
|
52
|
+
* Casi todo ejemplo que hay por internet usa el endpoint muerto.
|
|
53
|
+
*
|
|
54
|
+
* ── Lo que NO es una operación ──
|
|
55
|
+
*
|
|
56
|
+
* Listar proyectos, tipos de incidencia, transiciones, estados o usuarios
|
|
57
|
+
* asignables **no** son operaciones: son endpoints del controlador que alimentan
|
|
58
|
+
* los selectores, como Drive alimenta su selector de ficheros. Una operación
|
|
59
|
+
* existe para producir payload en una corrida; esto otro es para rellenar el
|
|
60
|
+
* formulario. Mezclarlos llena el desplegable de cosas que nadie quiere
|
|
61
|
+
* ejecutar.
|
|
62
|
+
*
|
|
63
|
+
* ── Decisiones propias de Jira ──
|
|
64
|
+
*
|
|
65
|
+
* - El proyecto y el tipo de incidencia se ELIGEN de una lista viva. Un
|
|
66
|
+
* `issuetype` se identifica por un id numérico que nadie sabe de memoria.
|
|
67
|
+
* - **Las transiciones no son una lista fija.** Dependen del flujo de trabajo
|
|
68
|
+
* Y del estado actual de la incidencia, así que el selector las pide para
|
|
69
|
+
* esa incidencia concreta. Ofrecer una lista global sería ofrecer
|
|
70
|
+
* transiciones que fallan al ejecutarse.
|
|
71
|
+
* - Los campos personalizados van en JSON crudo. Cada tipo tiene su forma
|
|
72
|
+
* (`select`, `date`, `relation`, usuario…) y fingir que caben en tres
|
|
73
|
+
* campos genéricos produce un 400 que no nombra el campo.
|
|
74
|
+
* - Ninguna operación declarada sin implementar: el enum ES el contrato de la
|
|
75
|
+
* pantalla, y una operación declarada se pinta en el desplegable y falla al
|
|
76
|
+
* ejecutarse.
|
|
77
|
+
*/
|
|
78
|
+
exports.JIRA_OPERATIONS = [
|
|
79
|
+
// Incidencias
|
|
80
|
+
'createIssue', // POST /issue
|
|
81
|
+
'getIssue', // GET /issue/{key}
|
|
82
|
+
'updateIssue', // PUT /issue/{key}
|
|
83
|
+
'deleteIssue', // DELETE /issue/{key}
|
|
84
|
+
'assignIssue', // PUT /issue/{key}/assignee
|
|
85
|
+
'transitionIssue', // POST /issue/{key}/transitions
|
|
86
|
+
// Comentarios
|
|
87
|
+
'addComment', // POST /issue/{key}/comment
|
|
88
|
+
'getComments', // GET /issue/{key}/comment → iterable
|
|
89
|
+
// Adjuntos y trabajo
|
|
90
|
+
'addAttachment', // POST /issue/{key}/attachments
|
|
91
|
+
'addWorklog', // POST /issue/{key}/worklog
|
|
92
|
+
// Relaciones
|
|
93
|
+
'linkIssues', // POST /issueLink
|
|
94
|
+
// Transversal
|
|
95
|
+
'searchIssues', // POST /search/jql → iterable
|
|
96
|
+
];
|
|
97
|
+
/** Type guard — para validar entrada no fiable (DTOs, tool calls). */
|
|
98
|
+
function isJiraOperation(value) {
|
|
99
|
+
return (typeof value === 'string' &&
|
|
100
|
+
exports.JIRA_OPERATIONS.includes(value));
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Las operaciones que devuelven un ARRAY y por tanto obligan a marcar
|
|
104
|
+
* `_meta.iterable` en `getOutputPayload`.
|
|
105
|
+
*
|
|
106
|
+
* Vive aquí y no sólo en la api porque olvidarlo no falla al compilar: falla
|
|
107
|
+
* en producción, callado, con el nodo de abajo recibiendo el sobre entero en
|
|
108
|
+
* vez de un elemento y el PayloadViewer pintando rutas con corchetes que nadie
|
|
109
|
+
* puede usar.
|
|
110
|
+
*/
|
|
111
|
+
exports.JIRA_ITERABLE_OPERATIONS = [
|
|
112
|
+
'getComments',
|
|
113
|
+
'searchIssues',
|
|
114
|
+
];
|
|
115
|
+
/* Ayudantes — repetir el mismo objeto doce veces es cómo se desincronizan las
|
|
116
|
+
descripciones. */
|
|
117
|
+
const project = (required = true) => ({
|
|
118
|
+
name: 'projectKey',
|
|
119
|
+
label: 'Project',
|
|
120
|
+
type: 'jiraProject',
|
|
121
|
+
required,
|
|
122
|
+
description: 'De la lista de proyectos que alcanza esta credencial. Se guarda la clave (ACME).',
|
|
123
|
+
});
|
|
124
|
+
const issueKey = (label = 'Issue') => ({
|
|
125
|
+
name: 'issueKey',
|
|
126
|
+
label,
|
|
127
|
+
type: 'jiraIssueKey',
|
|
128
|
+
required: true,
|
|
129
|
+
placeholder: '{{payload.issueKey}}',
|
|
130
|
+
description: 'La clave que se ve en la URL (ACME-42), no el id interno. El trigger de Jira la deja en issueKey.',
|
|
131
|
+
});
|
|
132
|
+
/**
|
|
133
|
+
* El interruptor de formato para todo campo que Jira quiere en ADF.
|
|
134
|
+
*
|
|
135
|
+
* Por defecto `text`: se escribe normal y el nodo lo envuelve. `adf` es la
|
|
136
|
+
* salida para quien necesita tablas, paneles o menciones — cosas que el texto
|
|
137
|
+
* plano no sabe expresar y que no vale la pena inventarse un lenguaje para
|
|
138
|
+
* cubrir.
|
|
139
|
+
*/
|
|
140
|
+
const bodyFormat = () => ({
|
|
141
|
+
name: 'bodyFormat',
|
|
142
|
+
label: 'Format',
|
|
143
|
+
type: 'select',
|
|
144
|
+
default: 'text',
|
|
145
|
+
advanced: true,
|
|
146
|
+
options: [
|
|
147
|
+
{ value: 'text', label: 'Plain text (se convierte a ADF)' },
|
|
148
|
+
{ value: 'adf', label: 'Atlassian Document Format en crudo' },
|
|
149
|
+
],
|
|
150
|
+
description: 'Jira v3 guarda estos campos como ADF. En "plain text" el nodo lo envuelve por ti: un párrafo por bloque y salto de línea donde lo escribas.',
|
|
151
|
+
});
|
|
152
|
+
const customFields = () => ({
|
|
153
|
+
name: 'customFields',
|
|
154
|
+
label: 'Other fields (JSON)',
|
|
155
|
+
type: 'json',
|
|
156
|
+
advanced: true,
|
|
157
|
+
placeholder: '{ "customfield_10042": { "accountId": "..." }, "labels": ["urgente"] }',
|
|
158
|
+
description: 'Se mezcla tal cual dentro de `fields`. Cada tipo de campo de Jira tiene su forma; el esquema real se lee con el selector de campos de la pantalla.',
|
|
159
|
+
});
|
|
160
|
+
const maxResults = () => ({
|
|
161
|
+
name: 'maxResults',
|
|
162
|
+
label: 'How many at most',
|
|
163
|
+
type: 'number',
|
|
164
|
+
default: 50,
|
|
165
|
+
min: 1,
|
|
166
|
+
max: 100,
|
|
167
|
+
advanced: true,
|
|
168
|
+
description: 'Jira no devuelve más de 100 por página.',
|
|
169
|
+
});
|
|
170
|
+
exports.JIRA_OPERATION_SPECS = {
|
|
171
|
+
createIssue: {
|
|
172
|
+
label: 'Create issue',
|
|
173
|
+
description: 'Crea una incidencia en un proyecto.',
|
|
174
|
+
apiRoute: 'POST /rest/api/3/issue',
|
|
175
|
+
params: [
|
|
176
|
+
project(),
|
|
177
|
+
{
|
|
178
|
+
name: 'issueTypeId',
|
|
179
|
+
label: 'Issue type',
|
|
180
|
+
type: 'jiraIssueType',
|
|
181
|
+
required: true,
|
|
182
|
+
description: 'Los tipos son del proyecto, no globales: se piden después de elegirlo.',
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
name: 'summary',
|
|
186
|
+
label: 'Summary',
|
|
187
|
+
type: 'template',
|
|
188
|
+
required: true,
|
|
189
|
+
placeholder: 'Fallo al exportar el informe',
|
|
190
|
+
description: 'El título. Es el único campo que Jira exige siempre.',
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
name: 'description',
|
|
194
|
+
label: 'Description',
|
|
195
|
+
type: 'textarea',
|
|
196
|
+
placeholder: 'Lo que pasó, con {{payload.x}} si hace falta.',
|
|
197
|
+
},
|
|
198
|
+
bodyFormat(),
|
|
199
|
+
{
|
|
200
|
+
name: 'assigneeAccountId',
|
|
201
|
+
label: 'Assignee',
|
|
202
|
+
type: 'jiraUser',
|
|
203
|
+
advanced: true,
|
|
204
|
+
description: 'Se guarda el accountId, que es como lo pide Jira.',
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
name: 'parentKey',
|
|
208
|
+
label: 'Parent issue',
|
|
209
|
+
type: 'template',
|
|
210
|
+
advanced: true,
|
|
211
|
+
placeholder: 'ACME-10',
|
|
212
|
+
description: 'Sólo para subtareas: la incidencia padre. Con un tipo que no sea subtarea, Jira lo rechaza.',
|
|
213
|
+
},
|
|
214
|
+
customFields(),
|
|
215
|
+
],
|
|
216
|
+
},
|
|
217
|
+
getIssue: {
|
|
218
|
+
label: 'Get issue',
|
|
219
|
+
description: 'Lee una incidencia por su clave.',
|
|
220
|
+
apiRoute: 'GET /rest/api/3/issue/{issueIdOrKey}',
|
|
221
|
+
params: [
|
|
222
|
+
issueKey(),
|
|
223
|
+
{
|
|
224
|
+
name: 'fields',
|
|
225
|
+
label: 'Fields',
|
|
226
|
+
type: 'template',
|
|
227
|
+
advanced: true,
|
|
228
|
+
placeholder: 'summary,status,assignee',
|
|
229
|
+
description: 'Separados por comas. Vacío trae los de siempre. Pedir sólo lo que usas hace la respuesta mucho más pequeña.',
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
},
|
|
233
|
+
updateIssue: {
|
|
234
|
+
label: 'Update issue',
|
|
235
|
+
description: 'Cambia campos de una incidencia.',
|
|
236
|
+
apiRoute: 'PUT /rest/api/3/issue/{issueIdOrKey}',
|
|
237
|
+
params: [
|
|
238
|
+
issueKey(),
|
|
239
|
+
{
|
|
240
|
+
name: 'summary',
|
|
241
|
+
label: 'Summary',
|
|
242
|
+
type: 'template',
|
|
243
|
+
description: 'Vacío = no se toca.',
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
name: 'description',
|
|
247
|
+
label: 'Description',
|
|
248
|
+
type: 'textarea',
|
|
249
|
+
description: 'Vacío = no se toca. REEMPLAZA la descripción entera.',
|
|
250
|
+
},
|
|
251
|
+
bodyFormat(),
|
|
252
|
+
customFields(),
|
|
253
|
+
],
|
|
254
|
+
},
|
|
255
|
+
deleteIssue: {
|
|
256
|
+
label: 'Delete issue',
|
|
257
|
+
description: 'Borra una incidencia. No hay papelera.',
|
|
258
|
+
apiRoute: 'DELETE /rest/api/3/issue/{issueIdOrKey}',
|
|
259
|
+
params: [
|
|
260
|
+
issueKey(),
|
|
261
|
+
{
|
|
262
|
+
name: 'deleteSubtasks',
|
|
263
|
+
label: 'Delete its subtasks too',
|
|
264
|
+
type: 'booleanSelect',
|
|
265
|
+
options: [
|
|
266
|
+
{ value: '', label: '— sin tocar —' },
|
|
267
|
+
{ value: 'true', label: 'Sí, borrar las subtareas' },
|
|
268
|
+
{ value: 'false', label: 'No' },
|
|
269
|
+
],
|
|
270
|
+
description: 'Con subtareas y sin esto, Jira se niega a borrar y devuelve 400.',
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
},
|
|
274
|
+
assignIssue: {
|
|
275
|
+
label: 'Assign issue',
|
|
276
|
+
description: 'Cambia a quién está asignada.',
|
|
277
|
+
apiRoute: 'PUT /rest/api/3/issue/{issueIdOrKey}/assignee',
|
|
278
|
+
params: [
|
|
279
|
+
issueKey(),
|
|
280
|
+
{
|
|
281
|
+
name: 'accountId',
|
|
282
|
+
label: 'Assignee',
|
|
283
|
+
type: 'jiraUser',
|
|
284
|
+
required: true,
|
|
285
|
+
description: 'Vacío deja la incidencia sin asignar.',
|
|
286
|
+
},
|
|
287
|
+
],
|
|
288
|
+
},
|
|
289
|
+
transitionIssue: {
|
|
290
|
+
label: 'Move issue',
|
|
291
|
+
description: 'Mueve la incidencia de columna (transición del flujo).',
|
|
292
|
+
apiRoute: 'POST /rest/api/3/issue/{issueIdOrKey}/transitions',
|
|
293
|
+
params: [
|
|
294
|
+
issueKey(),
|
|
295
|
+
{
|
|
296
|
+
name: 'transitionId',
|
|
297
|
+
label: 'Transition',
|
|
298
|
+
type: 'jiraTransition',
|
|
299
|
+
required: true,
|
|
300
|
+
description: 'Las transiciones legales dependen del flujo de trabajo Y del estado actual, así que se piden para esta incidencia. Una que no aplique falla al ejecutarse.',
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
name: 'comment',
|
|
304
|
+
label: 'Comment (optional)',
|
|
305
|
+
type: 'textarea',
|
|
306
|
+
advanced: true,
|
|
307
|
+
description: 'Jira permite comentar en el mismo paso de la transición.',
|
|
308
|
+
},
|
|
309
|
+
bodyFormat(),
|
|
310
|
+
],
|
|
311
|
+
},
|
|
312
|
+
addComment: {
|
|
313
|
+
label: 'Add comment',
|
|
314
|
+
description: 'Comenta en una incidencia.',
|
|
315
|
+
apiRoute: 'POST /rest/api/3/issue/{issueIdOrKey}/comment',
|
|
316
|
+
params: [
|
|
317
|
+
issueKey(),
|
|
318
|
+
{
|
|
319
|
+
name: 'body',
|
|
320
|
+
label: 'Comment',
|
|
321
|
+
type: 'textarea',
|
|
322
|
+
required: true,
|
|
323
|
+
placeholder: 'Desplegado en producción: {{payload.version}}',
|
|
324
|
+
},
|
|
325
|
+
bodyFormat(),
|
|
326
|
+
],
|
|
327
|
+
},
|
|
328
|
+
getComments: {
|
|
329
|
+
label: 'Get comments',
|
|
330
|
+
description: 'Trae los comentarios de una incidencia.',
|
|
331
|
+
apiRoute: 'GET /rest/api/3/issue/{issueIdOrKey}/comment',
|
|
332
|
+
params: [
|
|
333
|
+
issueKey(),
|
|
334
|
+
{
|
|
335
|
+
name: 'orderBy',
|
|
336
|
+
label: 'Order',
|
|
337
|
+
type: 'select',
|
|
338
|
+
default: '-created',
|
|
339
|
+
advanced: true,
|
|
340
|
+
options: [
|
|
341
|
+
{ value: '-created', label: 'Los más nuevos primero' },
|
|
342
|
+
{ value: 'created', label: 'Los más viejos primero' },
|
|
343
|
+
],
|
|
344
|
+
},
|
|
345
|
+
maxResults(),
|
|
346
|
+
],
|
|
347
|
+
},
|
|
348
|
+
addAttachment: {
|
|
349
|
+
label: 'Attach file',
|
|
350
|
+
description: 'Sube un fichero a una incidencia.',
|
|
351
|
+
apiRoute: 'POST /rest/api/3/issue/{issueIdOrKey}/attachments',
|
|
352
|
+
params: [
|
|
353
|
+
issueKey(),
|
|
354
|
+
{
|
|
355
|
+
name: 'fileField',
|
|
356
|
+
label: 'File from payload',
|
|
357
|
+
type: 'template',
|
|
358
|
+
required: true,
|
|
359
|
+
placeholder: '{{payload.file}}',
|
|
360
|
+
description: 'La ruta del payload donde viene el fichero — lo que dejan el nodo de ficheros o un adjunto de Gmail.',
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
name: 'fileName',
|
|
364
|
+
label: 'File name',
|
|
365
|
+
type: 'template',
|
|
366
|
+
advanced: true,
|
|
367
|
+
placeholder: 'informe.pdf',
|
|
368
|
+
description: 'Vacío usa el nombre que traiga el propio fichero.',
|
|
369
|
+
},
|
|
370
|
+
],
|
|
371
|
+
},
|
|
372
|
+
addWorklog: {
|
|
373
|
+
label: 'Log work',
|
|
374
|
+
description: 'Apunta tiempo trabajado en una incidencia.',
|
|
375
|
+
apiRoute: 'POST /rest/api/3/issue/{issueIdOrKey}/worklog',
|
|
376
|
+
params: [
|
|
377
|
+
issueKey(),
|
|
378
|
+
{
|
|
379
|
+
name: 'timeSpent',
|
|
380
|
+
label: 'Time spent',
|
|
381
|
+
type: 'template',
|
|
382
|
+
required: true,
|
|
383
|
+
placeholder: '3h 30m',
|
|
384
|
+
description: 'En el formato de Jira: 2d, 3h, 30m, o combinados. No admite decimales.',
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
name: 'comment',
|
|
388
|
+
label: 'Comment',
|
|
389
|
+
type: 'textarea',
|
|
390
|
+
advanced: true,
|
|
391
|
+
},
|
|
392
|
+
bodyFormat(),
|
|
393
|
+
{
|
|
394
|
+
name: 'started',
|
|
395
|
+
label: 'When',
|
|
396
|
+
type: 'template',
|
|
397
|
+
advanced: true,
|
|
398
|
+
placeholder: '{{payload.timestamp}}',
|
|
399
|
+
description: 'Vacío usa el momento de la ejecución.',
|
|
400
|
+
},
|
|
401
|
+
],
|
|
402
|
+
},
|
|
403
|
+
linkIssues: {
|
|
404
|
+
label: 'Link issues',
|
|
405
|
+
description: 'Relaciona dos incidencias entre sí.',
|
|
406
|
+
apiRoute: 'POST /rest/api/3/issueLink',
|
|
407
|
+
params: [
|
|
408
|
+
{
|
|
409
|
+
name: 'inwardIssueKey',
|
|
410
|
+
label: 'From issue',
|
|
411
|
+
type: 'jiraIssueKey',
|
|
412
|
+
required: true,
|
|
413
|
+
placeholder: '{{payload.issueKey}}',
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
name: 'linkType',
|
|
417
|
+
label: 'Relationship',
|
|
418
|
+
type: 'jiraLinkType',
|
|
419
|
+
required: true,
|
|
420
|
+
description: 'Los tipos los define cada sitio: "Blocks", "Relates", "Duplicates"… Se piden a Jira.',
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
name: 'outwardIssueKey',
|
|
424
|
+
label: 'To issue',
|
|
425
|
+
type: 'jiraIssueKey',
|
|
426
|
+
required: true,
|
|
427
|
+
placeholder: 'ACME-99',
|
|
428
|
+
},
|
|
429
|
+
],
|
|
430
|
+
},
|
|
431
|
+
searchIssues: {
|
|
432
|
+
label: 'Search issues (JQL)',
|
|
433
|
+
description: 'Busca incidencias con JQL. Devuelve una lista iterable.',
|
|
434
|
+
/* ⚠️ `/search/jql`, NO `/search`. El viejo está deprecado y pagina distinto:
|
|
435
|
+
`nextPageToken` en vez de `startAt`, y sin `total` en la respuesta. */
|
|
436
|
+
apiRoute: 'POST /rest/api/3/search/jql',
|
|
437
|
+
params: [
|
|
438
|
+
{
|
|
439
|
+
name: 'jql',
|
|
440
|
+
label: 'JQL',
|
|
441
|
+
type: 'textarea',
|
|
442
|
+
required: true,
|
|
443
|
+
placeholder: 'project = ACME AND status != Done ORDER BY created DESC',
|
|
444
|
+
description: 'La misma consulta que escribirías en el buscador de Jira. Admite {{payload.x}}.',
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
name: 'fields',
|
|
448
|
+
label: 'Fields',
|
|
449
|
+
type: 'template',
|
|
450
|
+
advanced: true,
|
|
451
|
+
placeholder: 'summary,status,assignee',
|
|
452
|
+
description: 'Separados por comas. En este endpoint los campos hay que pedirlos: sin esto sólo llegan las claves.',
|
|
453
|
+
},
|
|
454
|
+
maxResults(),
|
|
455
|
+
{
|
|
456
|
+
name: 'nextPageToken',
|
|
457
|
+
label: 'Page token',
|
|
458
|
+
type: 'template',
|
|
459
|
+
advanced: true,
|
|
460
|
+
placeholder: '{{payload.nextPageToken}}',
|
|
461
|
+
description: 'Para pedir la página siguiente desde un Loop. La salida de esta operación lo devuelve.',
|
|
462
|
+
},
|
|
463
|
+
],
|
|
464
|
+
},
|
|
465
|
+
};
|
|
466
|
+
/**
|
|
467
|
+
* Lo que se ofrece en el desplegable de operación.
|
|
468
|
+
*
|
|
469
|
+
* Hoy son todas. Existe como constante aparte —y no como alias de
|
|
470
|
+
* `JIRA_OPERATIONS`— porque es el punto donde otros nodos han necesitado
|
|
471
|
+
* esconder una operación sin sacarla del enum: retirarla del enum rompe las que
|
|
472
|
+
* ya estaban guardadas en la base de datos.
|
|
473
|
+
*/
|
|
474
|
+
exports.JIRA_DROPDOWN_OPERATIONS = exports.JIRA_OPERATIONS;
|
package/dist/registry.js
CHANGED
|
@@ -301,6 +301,25 @@ exports.NODE_REGISTRY = {
|
|
|
301
301
|
ningún otro nodo — el MCP oficial de GitHub ya cubre ese caso dentro del
|
|
302
302
|
AI Node. Los rieles son este nodo; la improvisación es el MCP. */
|
|
303
303
|
},
|
|
304
|
+
jiraAction: {
|
|
305
|
+
type: 'jiraAction', prefix: 'jr', label: 'Jira',
|
|
306
|
+
group: 'Actions', detailPath: '/dashboard/jira-actions', apiPath: '/jira-actions',
|
|
307
|
+
stateKey: 'jiraActions', allStateKey: 'allJiraActions',
|
|
308
|
+
// El azul de marca de Jira, y estaba libre. La banda azul es la segunda
|
|
309
|
+
// mas poblada de este registro —nueve nodos— pero ninguno tan profundo:
|
|
310
|
+
// #3b82f6 y #60a5fa son claramente mas claros, y el #336791 de Postgres es
|
|
311
|
+
// mas apagado. En el minimapa es el azul oscuro, y eso lo distingue.
|
|
312
|
+
//
|
|
313
|
+
// Sigue valiendo lo que escribio GitHub aqui al lado: **la decision se
|
|
314
|
+
// cierra mirando el minimapa**, no leyendo este comentario.
|
|
315
|
+
color: '#0052CC', testable: true,
|
|
316
|
+
/* Sin isToolOnly: la v1 no trae modo toolkit de IA. Cada toolkit vive dos
|
|
317
|
+
veces —specs del dashboard para el AI Node y specs de la api para MCP— y
|
|
318
|
+
esa duplicacion no se paga antes de ver el nodo en pantalla. Ademas
|
|
319
|
+
Atlassian tiene servidor MCP propio, ya registrado en
|
|
320
|
+
`MCP_OAUTH_PRESETS.atlassian`, que cubre la cola larga dentro del AI
|
|
321
|
+
Node. Los rieles son este nodo. */
|
|
322
|
+
},
|
|
304
323
|
telegramAction: {
|
|
305
324
|
type: 'telegramAction', prefix: 'tg', label: 'Telegram Action',
|
|
306
325
|
group: 'Actions', detailPath: '/dashboard/telegram-actions', apiPath: '/telegram-actions',
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** All valid node type identifiers */
|
|
2
|
-
export type NodeType = 'webhook' | 'scheduledWorkflow' | 'chatTrigger' | 'trigger' | 'voiceAgent' | 'filter' | 'transform' | 'schemaValidator' | 'conditional' | 'delay' | 'rateLimiter' | 'aggregator' | 'cache' | 'code' | 'ai' | 'merge' | 'approval' | 'split' | 'loop' | 'markdown' | 'fileTransform' | 'limit' | 'router' | 'emailAction' | 'gmailAction' | 'httpAction' | 'mongoAction' | 'postgresAction' | 'notificationAction' | 'sheetsAction' | 'calendarAction' | 'docsAction' | 'driveAction' | 'firecrawlAction' | 'telegramAction' | 'whatsappAction' | 'discordAction' | 'slackAction' | 'googleContactsAction' | 'googleAnalyticsAction' | 'notionAction' | 'vectorStore' | 'rssAction' | 'socialMediaAction' | 'mailchimpAction' | 'shopifyAction' | 'githubAction' | 'stickyNote';
|
|
2
|
+
export type NodeType = 'webhook' | 'scheduledWorkflow' | 'chatTrigger' | 'trigger' | 'voiceAgent' | 'filter' | 'transform' | 'schemaValidator' | 'conditional' | 'delay' | 'rateLimiter' | 'aggregator' | 'cache' | 'code' | 'ai' | 'merge' | 'approval' | 'split' | 'loop' | 'markdown' | 'fileTransform' | 'limit' | 'router' | 'emailAction' | 'gmailAction' | 'httpAction' | 'mongoAction' | 'postgresAction' | 'notificationAction' | 'sheetsAction' | 'calendarAction' | 'docsAction' | 'driveAction' | 'firecrawlAction' | 'telegramAction' | 'whatsappAction' | 'discordAction' | 'slackAction' | 'googleContactsAction' | 'googleAnalyticsAction' | 'notionAction' | 'vectorStore' | 'rssAction' | 'socialMediaAction' | 'mailchimpAction' | 'shopifyAction' | 'githubAction' | 'jiraAction' | 'stickyNote';
|
|
3
3
|
/** Node role in the pipeline */
|
|
4
4
|
export type NodeRole = 'source' | 'processing' | 'flowControl' | 'routing' | 'action' | 'monitoring';
|
|
5
5
|
/** Handle positions on the canvas node */
|
package/dist/ui.js
CHANGED
|
@@ -78,6 +78,7 @@ exports.NODE_UI = {
|
|
|
78
78
|
mailchimpAction: { fromNodes: true, toNodes: true, inputHandles: ['left'], dotHandles: { input: ['left-in'], output: ['right-out'] }, special: { loopBackAllowed: true, routerTargetLabel: 'mailchimp' } },
|
|
79
79
|
shopifyAction: { fromNodes: true, toNodes: true, inputHandles: ['left'], dotHandles: { input: ['left-in'], output: ['right-out'] }, special: { loopBackAllowed: true, routerTargetLabel: 'shopify' } },
|
|
80
80
|
githubAction: { fromNodes: true, toNodes: true, inputHandles: ['left'], dotHandles: { input: ['left-in'], output: ['right-out'] }, special: { loopBackAllowed: true, routerTargetLabel: 'github' } },
|
|
81
|
+
jiraAction: { fromNodes: true, toNodes: true, inputHandles: ['left'], dotHandles: { input: ['left-in'], output: ['right-out'] }, special: { loopBackAllowed: true, routerTargetLabel: 'jira' } },
|
|
81
82
|
slackAction: { fromNodes: true, toNodes: true, inputHandles: ['left'], dotHandles: { input: ['left-in'], output: ['right-out'] }, special: { loopBackAllowed: true, routerTargetLabel: 'slack' } },
|
|
82
83
|
googleContactsAction: { fromNodes: true, toNodes: true, inputHandles: ['left'], dotHandles: { input: ['left-in'], output: ['right-out'] }, special: { loopBackAllowed: true } },
|
|
83
84
|
googleAnalyticsAction: { fromNodes: true, toNodes: true, inputHandles: ['left'], dotHandles: { input: ['left-in'], output: ['right-out'] }, special: { loopBackAllowed: true } },
|
|
@@ -104,6 +105,7 @@ exports.PREFIX_TO_TYPE = [
|
|
|
104
105
|
{ prefix: 'mc-', type: 'mailchimpAction', canvasType: 'mailchimpAction' },
|
|
105
106
|
{ prefix: 'shp-', type: 'shopifyAction', canvasType: 'shopifyAction' },
|
|
106
107
|
{ prefix: 'gh-', type: 'githubAction', canvasType: 'githubAction' },
|
|
108
|
+
{ prefix: 'jr-', type: 'jiraAction', canvasType: 'jiraAction' },
|
|
107
109
|
{ prefix: 'gc-', type: 'googleContactsAction', canvasType: 'googleContactsAction' },
|
|
108
110
|
{ prefix: 'gaa-', type: 'googleAnalyticsAction', canvasType: 'googleAnalyticsAction' },
|
|
109
111
|
{ prefix: 'ntn-', type: 'notionAction', canvasType: 'notionAction' },
|
package/package.json
CHANGED