@hostwebhook/node-types 1.69.0 → 1.70.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/calendar-toolkit.d.ts +1 -1
- package/dist/connections.d.ts +1 -1
- package/dist/discord-operations.d.ts +1 -1
- package/dist/discord-operations.js +4 -4
- package/dist/discord-toolkit.d.ts +1 -1
- package/dist/discord-toolkit.js +3 -3
- package/dist/dispatch.d.ts +1 -1
- package/dist/docs-toolkit.d.ts +1 -1
- package/dist/drive-toolkit.d.ts +1 -1
- package/dist/esm/calendar-operations.d.ts +67 -0
- package/dist/esm/calendar-operations.js +176 -0
- package/dist/esm/calendar-toolkit.d.ts +50 -0
- package/dist/esm/calendar-toolkit.js +152 -0
- package/dist/esm/capacidad-de-credencial.d.ts +75 -0
- package/dist/esm/capacidad-de-credencial.js +68 -0
- package/dist/esm/connections.d.ts +17 -0
- package/dist/esm/connections.js +106 -0
- package/dist/esm/credentials.d.ts +144 -0
- package/dist/esm/credentials.js +199 -0
- package/dist/esm/discord-operations.d.ts +104 -0
- package/dist/esm/discord-operations.js +407 -0
- package/dist/esm/discord-toolkit.d.ts +64 -0
- package/dist/esm/discord-toolkit.js +260 -0
- package/dist/esm/dispatch.d.ts +16 -0
- package/dist/esm/dispatch.js +76 -0
- package/dist/esm/docs-operations.d.ts +99 -0
- package/dist/esm/docs-operations.js +127 -0
- package/dist/esm/docs-toolkit.d.ts +42 -0
- package/dist/esm/docs-toolkit.js +97 -0
- package/dist/esm/drive-operations.d.ts +103 -0
- package/dist/esm/drive-operations.js +319 -0
- package/dist/esm/drive-toolkit.d.ts +54 -0
- package/dist/esm/drive-toolkit.js +177 -0
- package/dist/esm/github-operations.d.ts +147 -0
- package/dist/esm/github-operations.js +551 -0
- package/dist/esm/gmail-operations.d.ts +190 -0
- package/dist/esm/gmail-operations.js +560 -0
- package/dist/esm/google-analytics-operations.d.ts +99 -0
- package/dist/esm/google-analytics-operations.js +222 -0
- package/dist/esm/google-contacts-operations.d.ts +93 -0
- package/dist/esm/google-contacts-operations.js +363 -0
- package/dist/esm/index.d.ts +66 -0
- package/dist/esm/index.js +50 -0
- package/dist/esm/jira-operations.d.ts +164 -0
- package/dist/esm/jira-operations.js +470 -0
- package/dist/esm/llm-models.d.ts +101 -0
- package/dist/esm/llm-models.js +230 -0
- package/dist/esm/mailchimp-operations.d.ts +56 -0
- package/dist/esm/mailchimp-operations.js +155 -0
- package/dist/esm/mongo-operations.d.ts +61 -0
- package/dist/esm/mongo-operations.js +120 -0
- package/dist/esm/notion-operations.d.ts +125 -0
- package/dist/esm/notion-operations.js +511 -0
- package/dist/esm/openrouter.d.ts +142 -0
- package/dist/esm/openrouter.js +99 -0
- package/dist/esm/package.json +4 -0
- package/dist/esm/postgres-operations.d.ts +60 -0
- package/dist/esm/postgres-operations.js +75 -0
- package/dist/esm/registry.d.ts +160 -0
- package/dist/esm/registry.js +555 -0
- package/dist/esm/sheets-operations.d.ts +78 -0
- package/dist/esm/sheets-operations.js +150 -0
- package/dist/esm/sheets-toolkit.d.ts +49 -0
- package/dist/esm/sheets-toolkit.js +127 -0
- package/dist/esm/shopify-operations.d.ts +95 -0
- package/dist/esm/shopify-operations.js +259 -0
- package/dist/esm/slack-operations.d.ts +81 -0
- package/dist/esm/slack-operations.js +244 -0
- package/dist/esm/slack-toolkit.d.ts +60 -0
- package/dist/esm/slack-toolkit.js +235 -0
- package/dist/esm/telegram-operations.d.ts +104 -0
- package/dist/esm/telegram-operations.js +351 -0
- package/dist/esm/telegram-toolkit.d.ts +52 -0
- package/dist/esm/telegram-toolkit.js +183 -0
- package/dist/esm/types.d.ts +149 -0
- package/dist/esm/types.js +8 -0
- package/dist/esm/ui.d.ts +13 -0
- package/dist/esm/ui.js +150 -0
- package/dist/esm/whatsapp-operations.d.ts +23 -0
- package/dist/esm/whatsapp-operations.js +36 -0
- package/dist/index.d.ts +66 -66
- package/dist/index.js +185 -185
- package/dist/openrouter.d.ts +1 -1
- package/dist/registry.d.ts +1 -1
- package/dist/sheets-toolkit.d.ts +1 -1
- package/dist/slack-operations.d.ts +1 -1
- package/dist/slack-operations.js +4 -4
- package/dist/slack-toolkit.d.ts +1 -1
- package/dist/slack-toolkit.js +3 -3
- package/dist/telegram-toolkit.d.ts +1 -1
- package/dist/ui.d.ts +1 -1
- package/package.json +21 -3
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Analytics 4 operation enum — single source of truth across the API,
|
|
3
|
+
* Dashboard, and Message Broker. Used by:
|
|
4
|
+
* - googleAnalyticsAction entity / DTO (api): operation field + validation
|
|
5
|
+
* - dashboard GoogleAnalyticsAction operation type
|
|
6
|
+
* - MCP toolkit specs, si algún día el nodo gana modo toolkit
|
|
7
|
+
*
|
|
8
|
+
* Mismo patrón que `drive-operations.ts` y `sheets-operations.ts`.
|
|
9
|
+
*
|
|
10
|
+
* ── Por qué sólo dos operaciones ──
|
|
11
|
+
*
|
|
12
|
+
* GA4 tiene más superficie (`runPivotReport`, `batchRunReports`,
|
|
13
|
+
* `checkCompatibility`), pero declarar una op que el servicio todavía no
|
|
14
|
+
* implementa la pinta en el desplegable y la deja fallar al ejecutarse: el
|
|
15
|
+
* enum ES el contrato de la pantalla. Entran cuando entre su código.
|
|
16
|
+
*
|
|
17
|
+
* ── Lo que NO es una operación ──
|
|
18
|
+
*
|
|
19
|
+
* Listar propiedades y listar las dimensiones/métricas disponibles NO son
|
|
20
|
+
* ops del nodo: son endpoints del controlador, que es como Drive alimenta su
|
|
21
|
+
* `DriveFilePicker` (`@Get('files/:credentialId')`). Una op existe para
|
|
22
|
+
* producir payload en una corrida; esto otro es para rellenar el formulario.
|
|
23
|
+
*
|
|
24
|
+
* ── Decisiones propias de GA (anti-n8n) ──
|
|
25
|
+
*
|
|
26
|
+
* - La propiedad se ELIGE de una lista, no se teclea. `properties/123456789`
|
|
27
|
+
* y `G-XXXXXXX` se confunden todo el rato y el error de GA no lo aclara.
|
|
28
|
+
* - Las fechas admiten `NdaysAgo` / `yesterday` / `today` además de
|
|
29
|
+
* `YYYY-MM-DD`, que es lo que acepta la API, y son el sitio donde avisar
|
|
30
|
+
* de que el dato no está cerrado hasta pasadas ~48 h.
|
|
31
|
+
* - El filtro común (una dimensión, un valor) son tres campos; la
|
|
32
|
+
* `FilterExpression` cruda queda de escape en Advanced y gana si está.
|
|
33
|
+
*/
|
|
34
|
+
export const GOOGLE_ANALYTICS_OPERATIONS = [
|
|
35
|
+
'runReport', // informe estándar sobre un rango de fechas
|
|
36
|
+
'runRealtimeReport', // los últimos ~30 minutos, sin rango de fechas
|
|
37
|
+
];
|
|
38
|
+
/** Type guard — para validar entrada no fiable (DTOs, tool calls). */
|
|
39
|
+
export function isGoogleAnalyticsOperation(value) {
|
|
40
|
+
return (typeof value === 'string' &&
|
|
41
|
+
GOOGLE_ANALYTICS_OPERATIONS.includes(value));
|
|
42
|
+
}
|
|
43
|
+
/* Fábricas de los campos que se repiten, para que un cambio de copy no haya
|
|
44
|
+
que perseguirlo por las dos operaciones —y por las que vengan. */
|
|
45
|
+
const propertyId = () => ({
|
|
46
|
+
name: 'propertyId',
|
|
47
|
+
label: 'GA4 Property',
|
|
48
|
+
type: 'analyticsProperty',
|
|
49
|
+
required: true,
|
|
50
|
+
description: 'Pick a property from the connected account, or paste a templated value like {{payload.propertyId}}. This is the numeric property id (properties/123456789), not the G-XXXXXXX measurement id.',
|
|
51
|
+
});
|
|
52
|
+
const metrics = () => ({
|
|
53
|
+
name: 'metrics',
|
|
54
|
+
label: 'Metrics',
|
|
55
|
+
type: 'analyticsMetrics',
|
|
56
|
+
required: true,
|
|
57
|
+
description: 'What to measure — activeUsers, sessions, screenPageViews, conversions. At least one is required.',
|
|
58
|
+
});
|
|
59
|
+
const dimensions = () => ({
|
|
60
|
+
name: 'dimensions',
|
|
61
|
+
label: 'Dimensions',
|
|
62
|
+
type: 'analyticsDimensions',
|
|
63
|
+
description: 'How to break the metrics down — date, country, pagePath, sessionSource. Leave empty for a single total row.',
|
|
64
|
+
});
|
|
65
|
+
const limit = (max, def) => ({
|
|
66
|
+
name: 'limit',
|
|
67
|
+
label: 'Row limit',
|
|
68
|
+
type: 'number',
|
|
69
|
+
default: def,
|
|
70
|
+
min: 1,
|
|
71
|
+
max,
|
|
72
|
+
description: `Maximum rows to return. The API caps a single request at ${max.toLocaleString('en-US')}.`,
|
|
73
|
+
});
|
|
74
|
+
/** Los tipos de coincidencia de una `stringFilter` de GA4. */
|
|
75
|
+
const MATCH_TYPE_OPTIONS = [
|
|
76
|
+
{ value: 'EXACT', label: 'is exactly' },
|
|
77
|
+
{ value: 'CONTAINS', label: 'contains' },
|
|
78
|
+
{ value: 'BEGINS_WITH', label: 'begins with' },
|
|
79
|
+
{ value: 'ENDS_WITH', label: 'ends with' },
|
|
80
|
+
{ value: 'FULL_REGEXP', label: 'matches regex' },
|
|
81
|
+
];
|
|
82
|
+
/* El filtro simple: una dimensión, una comparación, un valor. Cubre el caso
|
|
83
|
+
que pide todo el mundo ("country es Spain") sin obligar a escribir una
|
|
84
|
+
FilterExpression anidada. Quien necesite más tiene `dimensionFilterJson`. */
|
|
85
|
+
const simpleFilter = () => [
|
|
86
|
+
{
|
|
87
|
+
name: 'filterDimension',
|
|
88
|
+
label: 'Filter by dimension',
|
|
89
|
+
type: 'analyticsDimensions',
|
|
90
|
+
description: 'Optional. Restrict the report to rows where this dimension matches the value below.',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: 'filterMatchType',
|
|
94
|
+
label: 'Match',
|
|
95
|
+
type: 'select',
|
|
96
|
+
default: 'EXACT',
|
|
97
|
+
options: MATCH_TYPE_OPTIONS,
|
|
98
|
+
showWhen: { field: 'filterDimension', in: ['*'] },
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'filterValue',
|
|
102
|
+
label: 'Value',
|
|
103
|
+
type: 'template',
|
|
104
|
+
placeholder: 'Spain',
|
|
105
|
+
description: 'Templates work — e.g. {{payload.country}}.',
|
|
106
|
+
showWhen: { field: 'filterDimension', in: ['*'] },
|
|
107
|
+
},
|
|
108
|
+
];
|
|
109
|
+
const dimensionFilterJson = () => ({
|
|
110
|
+
name: 'dimensionFilterJson',
|
|
111
|
+
label: 'Raw dimension filter (FilterExpression JSON)',
|
|
112
|
+
type: 'json',
|
|
113
|
+
advanced: true,
|
|
114
|
+
placeholder: '{"andGroup":{"expressions":[{"filter":{"fieldName":"country","stringFilter":{"value":"Spain"}}}]}}',
|
|
115
|
+
description: 'Escape hatch for AND / OR / NOT groups the simple filter above cannot express. When set, this WINS and the three simple filter fields are ignored.',
|
|
116
|
+
});
|
|
117
|
+
const orderBy = () => [
|
|
118
|
+
{
|
|
119
|
+
name: 'orderByField',
|
|
120
|
+
label: 'Sort by',
|
|
121
|
+
type: 'template',
|
|
122
|
+
advanced: true,
|
|
123
|
+
placeholder: 'activeUsers',
|
|
124
|
+
description: 'Metric or dimension name to sort on. Must be one of the metrics or dimensions selected above. Empty = the API default order.',
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: 'orderByDesc',
|
|
128
|
+
label: 'Sort direction',
|
|
129
|
+
type: 'booleanSelect',
|
|
130
|
+
advanced: true,
|
|
131
|
+
options: [
|
|
132
|
+
{ value: '', label: '— API default —' },
|
|
133
|
+
{ value: 'true', label: 'Descending (highest first)' },
|
|
134
|
+
{ value: 'false', label: 'Ascending (lowest first)' },
|
|
135
|
+
],
|
|
136
|
+
showWhen: { field: 'orderByField', in: ['*'] },
|
|
137
|
+
},
|
|
138
|
+
];
|
|
139
|
+
export const GOOGLE_ANALYTICS_OPERATION_SPECS = {
|
|
140
|
+
runReport: {
|
|
141
|
+
label: 'Run report',
|
|
142
|
+
description: 'Metrics over a date range, broken down by dimensions.',
|
|
143
|
+
apiMethod: 'properties.runReport',
|
|
144
|
+
params: [
|
|
145
|
+
propertyId(),
|
|
146
|
+
metrics(),
|
|
147
|
+
dimensions(),
|
|
148
|
+
{
|
|
149
|
+
name: 'startDate',
|
|
150
|
+
label: 'Start date',
|
|
151
|
+
type: 'template',
|
|
152
|
+
required: true,
|
|
153
|
+
default: '28daysAgo',
|
|
154
|
+
description: 'YYYY-MM-DD, or a relative value: today, yesterday, NdaysAgo. Note GA4 keeps refining recent data for up to 48h, so a window ending at "today" can change between two runs.',
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: 'endDate',
|
|
158
|
+
label: 'End date',
|
|
159
|
+
type: 'template',
|
|
160
|
+
required: true,
|
|
161
|
+
default: 'today',
|
|
162
|
+
description: 'Same formats as the start date. Inclusive.',
|
|
163
|
+
},
|
|
164
|
+
...simpleFilter(),
|
|
165
|
+
limit(250000, 100),
|
|
166
|
+
{
|
|
167
|
+
name: 'offset',
|
|
168
|
+
label: 'Row offset',
|
|
169
|
+
type: 'number',
|
|
170
|
+
advanced: true,
|
|
171
|
+
default: 0,
|
|
172
|
+
min: 0,
|
|
173
|
+
description: 'Skip this many rows before returning. Use with the row limit to page through a large report.',
|
|
174
|
+
},
|
|
175
|
+
...orderBy(),
|
|
176
|
+
dimensionFilterJson(),
|
|
177
|
+
{
|
|
178
|
+
name: 'keepEmptyRows',
|
|
179
|
+
label: 'Keep empty rows',
|
|
180
|
+
type: 'booleanSelect',
|
|
181
|
+
advanced: true,
|
|
182
|
+
options: [
|
|
183
|
+
{ value: '', label: '— API default (drop them) —' },
|
|
184
|
+
{ value: 'true', label: 'Keep rows whose metrics are all zero' },
|
|
185
|
+
{ value: 'false', label: 'Drop rows whose metrics are all zero' },
|
|
186
|
+
],
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
},
|
|
190
|
+
runRealtimeReport: {
|
|
191
|
+
label: 'Run realtime report',
|
|
192
|
+
description: 'What is happening right now — the last 30 minutes.',
|
|
193
|
+
apiMethod: 'properties.runRealtimeReport',
|
|
194
|
+
params: [
|
|
195
|
+
propertyId(),
|
|
196
|
+
metrics(),
|
|
197
|
+
dimensions(),
|
|
198
|
+
{
|
|
199
|
+
name: 'minutesAgo',
|
|
200
|
+
label: 'Window',
|
|
201
|
+
type: 'select',
|
|
202
|
+
default: '29',
|
|
203
|
+
options: [
|
|
204
|
+
{ value: '4', label: 'Last 5 minutes' },
|
|
205
|
+
{ value: '14', label: 'Last 15 minutes' },
|
|
206
|
+
{ value: '29', label: 'Last 30 minutes' },
|
|
207
|
+
],
|
|
208
|
+
description: 'Realtime has no date range — it always looks back from now, and GA4 caps the lookback at 30 minutes.',
|
|
209
|
+
},
|
|
210
|
+
...simpleFilter(),
|
|
211
|
+
limit(100000, 100),
|
|
212
|
+
...orderBy(),
|
|
213
|
+
dimensionFilterJson(),
|
|
214
|
+
],
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
/**
|
|
218
|
+
* Las operaciones tal y como se listan en el desplegable. Hoy es el enum
|
|
219
|
+
* entero; existe para que añadir una op interna (sin fila propia) no obligue
|
|
220
|
+
* a que la página vuelva a decidir por su cuenta qué enseñar.
|
|
221
|
+
*/
|
|
222
|
+
export const GOOGLE_ANALYTICS_DROPDOWN_OPERATIONS = GOOGLE_ANALYTICS_OPERATIONS;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google People API operation enum — single source of truth across the
|
|
3
|
+
* API, Dashboard, and Message Broker. Used by:
|
|
4
|
+
* - googleContactsAction entity / DTO (api): operation field + validation
|
|
5
|
+
* - dashboard OperationSection
|
|
6
|
+
* - broker hostwebhook types + tool schemas
|
|
7
|
+
*
|
|
8
|
+
* Two tiers because the action shipped V1 first (read + simple writes)
|
|
9
|
+
* and V2 second (bulk + group management). Both ship in the same package
|
|
10
|
+
* version; the api-side DTO gates which tier its accept-list includes.
|
|
11
|
+
*/
|
|
12
|
+
export declare const GOOGLE_CONTACTS_OPERATIONS_V1: readonly ["listContacts", "searchContacts", "getContact", "createContact", "updateContact", "deleteContact", "listContactGroups", "addContactToGroup", "removeContactFromGroup"];
|
|
13
|
+
export declare const GOOGLE_CONTACTS_OPERATIONS_V2: readonly ["batchCreateContacts", "batchUpdateContacts", "batchDeleteContacts", "createContactGroup", "updateContactGroup", "deleteContactGroup"];
|
|
14
|
+
export declare const GOOGLE_CONTACTS_OPERATIONS: readonly ["listContacts", "searchContacts", "getContact", "createContact", "updateContact", "deleteContact", "listContactGroups", "addContactToGroup", "removeContactFromGroup", "batchCreateContacts", "batchUpdateContacts", "batchDeleteContacts", "createContactGroup", "updateContactGroup", "deleteContactGroup"];
|
|
15
|
+
export type GoogleContactsOperation = (typeof GOOGLE_CONTACTS_OPERATIONS)[number];
|
|
16
|
+
export declare function isGoogleContactsOperation(value: unknown): value is GoogleContactsOperation;
|
|
17
|
+
/**
|
|
18
|
+
* Per-operation form schema, same idea as SLACK_OPERATION_SPECS and
|
|
19
|
+
* DISCORD_OPERATION_SPECS: the dashboard detail page renders it with one loop
|
|
20
|
+
* instead of a branch per operation.
|
|
21
|
+
*
|
|
22
|
+
* Unlike Slack's, this one drives the **form only**. The AI-toolkit / MCP tool
|
|
23
|
+
* schema for this node lives apart (dashboard
|
|
24
|
+
* `google-contacts-operations-schemas.ts`, api `mcp-servers/toolkit-specs.ts`),
|
|
25
|
+
* which is why `description` is optional here — a handful of fields have never
|
|
26
|
+
* shown help text and inventing copy for them is not this change's job.
|
|
27
|
+
*/
|
|
28
|
+
export interface GoogleContactsParamSpec {
|
|
29
|
+
/** Field key — also the property name on operationConfig / payload. */
|
|
30
|
+
name: string;
|
|
31
|
+
/** UI label, shown as the section title. Carries any "(optional)" suffix. */
|
|
32
|
+
label: string;
|
|
33
|
+
/**
|
|
34
|
+
* Param type for form rendering.
|
|
35
|
+
*
|
|
36
|
+
* `string`, `text`, `number` and `boolean` mean the same as in
|
|
37
|
+
* SlackParamSpec. The rest are this node's own:
|
|
38
|
+
* - `csvList` — stored as a string **array**, edited as comma-separated
|
|
39
|
+
* text. Losing the array on save breaks the node at run time, so the
|
|
40
|
+
* renderer must join on the way in and split on the way out.
|
|
41
|
+
* - `json` — stored parsed when the text is valid JSON, and as the raw
|
|
42
|
+
* string when it is not, so a half-typed array survives a keystroke.
|
|
43
|
+
* - `plain` — a bare input, no templating. Same control Discord calls
|
|
44
|
+
* `emoji`, a name inherited from Slack that fits neither field here.
|
|
45
|
+
* Unifying the two is a coordinated rename, so for now both exist.
|
|
46
|
+
*/
|
|
47
|
+
type: 'string' | 'text' | 'plain' | 'csvList' | 'json' | 'number' | 'boolean';
|
|
48
|
+
/** Required by the People API. Metadata — the form does not gate on it. */
|
|
49
|
+
required?: boolean;
|
|
50
|
+
/** Help text under the input. Absent where the form has never shown any. */
|
|
51
|
+
description?: string;
|
|
52
|
+
/** Hint shown inside the input. */
|
|
53
|
+
placeholder?: string;
|
|
54
|
+
/** Value to show when the stored one is undefined. */
|
|
55
|
+
default?: string | number | boolean;
|
|
56
|
+
/** `number` only — and enforced on change, not just by the input. */
|
|
57
|
+
min?: number;
|
|
58
|
+
max?: number;
|
|
59
|
+
/** `text` / `json` only — height of the textarea. */
|
|
60
|
+
rows?: number;
|
|
61
|
+
/** `boolean` only — the line beside the switch. */
|
|
62
|
+
switchLabel?: string;
|
|
63
|
+
}
|
|
64
|
+
/** Group headings of the operation dropdown, in display order. */
|
|
65
|
+
export declare const GOOGLE_CONTACTS_OPERATION_GROUPS: readonly ["Read", "Write", "Groups", "Bulk", "Group management"];
|
|
66
|
+
export type GoogleContactsOperationGroup = (typeof GOOGLE_CONTACTS_OPERATION_GROUPS)[number];
|
|
67
|
+
export interface GoogleContactsOperationSpec {
|
|
68
|
+
/** UI label for the operation picker. */
|
|
69
|
+
label: string;
|
|
70
|
+
/** Line under the picker, and the picker's own description. */
|
|
71
|
+
description: string;
|
|
72
|
+
/** googleapis call the operation maps to (people v1). */
|
|
73
|
+
apiMethod: string;
|
|
74
|
+
/** Which half of the OAuth scope the operation needs. */
|
|
75
|
+
scope: 'read' | 'write';
|
|
76
|
+
/** Group heading it sits under in the dropdown. */
|
|
77
|
+
group: GoogleContactsOperationGroup;
|
|
78
|
+
/** Parameter schema. Order matters for form rendering. */
|
|
79
|
+
params: GoogleContactsParamSpec[];
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* personFields the read operations request when nobody overrides them. Lived in
|
|
83
|
+
* the dashboard page until this schema landed; it belongs next to the field
|
|
84
|
+
* that defaults to it.
|
|
85
|
+
*/
|
|
86
|
+
export declare const GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS = "names,emailAddresses,phoneNumbers,addresses,organizations,memberships";
|
|
87
|
+
/**
|
|
88
|
+
* Key order below is the dropdown order **within each group** — most-common
|
|
89
|
+
* operation first, which is why Read starts at searchContacts and not at the
|
|
90
|
+
* enum's listContacts. The dashboard groups these entries by `group` and keeps
|
|
91
|
+
* this order, so moving a key moves the dropdown.
|
|
92
|
+
*/
|
|
93
|
+
export declare const GOOGLE_CONTACTS_OPERATION_SPECS: Record<GoogleContactsOperation, GoogleContactsOperationSpec>;
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google People API operation enum — single source of truth across the
|
|
3
|
+
* API, Dashboard, and Message Broker. Used by:
|
|
4
|
+
* - googleContactsAction entity / DTO (api): operation field + validation
|
|
5
|
+
* - dashboard OperationSection
|
|
6
|
+
* - broker hostwebhook types + tool schemas
|
|
7
|
+
*
|
|
8
|
+
* Two tiers because the action shipped V1 first (read + simple writes)
|
|
9
|
+
* and V2 second (bulk + group management). Both ship in the same package
|
|
10
|
+
* version; the api-side DTO gates which tier its accept-list includes.
|
|
11
|
+
*/
|
|
12
|
+
export const GOOGLE_CONTACTS_OPERATIONS_V1 = [
|
|
13
|
+
'listContacts',
|
|
14
|
+
'searchContacts',
|
|
15
|
+
'getContact',
|
|
16
|
+
'createContact',
|
|
17
|
+
'updateContact',
|
|
18
|
+
'deleteContact',
|
|
19
|
+
'listContactGroups',
|
|
20
|
+
'addContactToGroup',
|
|
21
|
+
'removeContactFromGroup',
|
|
22
|
+
];
|
|
23
|
+
export const GOOGLE_CONTACTS_OPERATIONS_V2 = [
|
|
24
|
+
'batchCreateContacts',
|
|
25
|
+
'batchUpdateContacts',
|
|
26
|
+
'batchDeleteContacts',
|
|
27
|
+
'createContactGroup',
|
|
28
|
+
'updateContactGroup',
|
|
29
|
+
'deleteContactGroup',
|
|
30
|
+
];
|
|
31
|
+
export const GOOGLE_CONTACTS_OPERATIONS = [
|
|
32
|
+
...GOOGLE_CONTACTS_OPERATIONS_V1,
|
|
33
|
+
...GOOGLE_CONTACTS_OPERATIONS_V2,
|
|
34
|
+
];
|
|
35
|
+
export function isGoogleContactsOperation(value) {
|
|
36
|
+
return (typeof value === 'string' &&
|
|
37
|
+
GOOGLE_CONTACTS_OPERATIONS.includes(value));
|
|
38
|
+
}
|
|
39
|
+
/** Group headings of the operation dropdown, in display order. */
|
|
40
|
+
export const GOOGLE_CONTACTS_OPERATION_GROUPS = [
|
|
41
|
+
'Read',
|
|
42
|
+
'Write',
|
|
43
|
+
'Groups',
|
|
44
|
+
'Bulk',
|
|
45
|
+
'Group management',
|
|
46
|
+
];
|
|
47
|
+
/**
|
|
48
|
+
* personFields the read operations request when nobody overrides them. Lived in
|
|
49
|
+
* the dashboard page until this schema landed; it belongs next to the field
|
|
50
|
+
* that defaults to it.
|
|
51
|
+
*/
|
|
52
|
+
export const GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS = 'names,emailAddresses,phoneNumbers,addresses,organizations,memberships';
|
|
53
|
+
/* Fields several operations share. Each takes overrides because the same key
|
|
54
|
+
can mean different things: `resourceName` is a contact in five operations
|
|
55
|
+
and a contact group in two. */
|
|
56
|
+
const contactResourceName = (over = {}) => ({
|
|
57
|
+
name: 'resourceName',
|
|
58
|
+
label: 'Contact resourceName',
|
|
59
|
+
type: 'string',
|
|
60
|
+
required: true,
|
|
61
|
+
placeholder: 'people/c12345 OR {{payload.resourceName}}',
|
|
62
|
+
description: 'The People API id of the contact (e.g. people/c123456789012345). Search or list a contact upstream to discover it, then pass it through.',
|
|
63
|
+
...over,
|
|
64
|
+
});
|
|
65
|
+
/** The **group** under its own key, for the two group-management operations. */
|
|
66
|
+
const groupResourceNameAsResourceName = (over = {}) => ({
|
|
67
|
+
name: 'resourceName',
|
|
68
|
+
label: 'Group resourceName',
|
|
69
|
+
type: 'string',
|
|
70
|
+
required: true,
|
|
71
|
+
placeholder: 'contactGroups/12345',
|
|
72
|
+
...over,
|
|
73
|
+
});
|
|
74
|
+
/** The group alongside a contact, so it needs its own key. */
|
|
75
|
+
const groupResourceName = (over = {}) => ({
|
|
76
|
+
name: 'groupResourceName',
|
|
77
|
+
label: 'Group resourceName',
|
|
78
|
+
type: 'string',
|
|
79
|
+
required: true,
|
|
80
|
+
placeholder: 'contactGroups/12345 OR {{payload.groupId}}',
|
|
81
|
+
description: "The contactGroup's resourceName. Use listContactGroups upstream to discover it, or hardcode if you always target the same group.",
|
|
82
|
+
...over,
|
|
83
|
+
});
|
|
84
|
+
const pageSize = () => ({
|
|
85
|
+
name: 'pageSize',
|
|
86
|
+
label: 'Page size',
|
|
87
|
+
type: 'number',
|
|
88
|
+
default: 100,
|
|
89
|
+
min: 1,
|
|
90
|
+
max: 1000,
|
|
91
|
+
description: 'How many results per page. Use a Loop node downstream to paginate further with `nextPageToken`.',
|
|
92
|
+
});
|
|
93
|
+
/* Shown last on the three read operations, pre-filled with the default rather
|
|
94
|
+
than merely placeholder-hinted — that is how it has always looked. */
|
|
95
|
+
const personFields = () => ({
|
|
96
|
+
name: 'personFields',
|
|
97
|
+
label: 'Person fields (advanced, optional)',
|
|
98
|
+
type: 'plain',
|
|
99
|
+
default: GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS,
|
|
100
|
+
placeholder: GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS,
|
|
101
|
+
description: 'Comma-separated People API fields to include in the response. Default covers the 95% of cases — only change when you need photos, biographies, or to slim the payload.',
|
|
102
|
+
});
|
|
103
|
+
/** The contact body, identical on create and update. */
|
|
104
|
+
const contactBody = () => [
|
|
105
|
+
{
|
|
106
|
+
name: 'fullName',
|
|
107
|
+
label: 'Full name',
|
|
108
|
+
type: 'string',
|
|
109
|
+
placeholder: '{{payload.name}} OR Ariel Pérez',
|
|
110
|
+
description: 'Auto-splits into given + family name. Override with the separate fields below if the heuristic mangles compound surnames.',
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: 'givenName',
|
|
114
|
+
label: 'Given name (optional)',
|
|
115
|
+
type: 'string',
|
|
116
|
+
placeholder: '{{payload.firstName}}',
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: 'familyName',
|
|
120
|
+
label: 'Family name (optional)',
|
|
121
|
+
type: 'string',
|
|
122
|
+
placeholder: '{{payload.lastName}}',
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
name: 'emailAddresses',
|
|
126
|
+
label: 'Email addresses (comma-separated)',
|
|
127
|
+
type: 'csvList',
|
|
128
|
+
placeholder: '{{payload.email}}',
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: 'phoneNumbers',
|
|
132
|
+
label: 'Phone numbers (comma-separated)',
|
|
133
|
+
type: 'csvList',
|
|
134
|
+
placeholder: '{{payload.phone}}',
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: 'notes',
|
|
138
|
+
label: 'Notes (optional)',
|
|
139
|
+
type: 'text',
|
|
140
|
+
rows: 3,
|
|
141
|
+
placeholder: 'VIP customer · {{payload.context}}',
|
|
142
|
+
},
|
|
143
|
+
];
|
|
144
|
+
const BATCH_CONTACTS_TEMPLATING = 'Templating works inside string values ({{payload.x}}).';
|
|
145
|
+
/**
|
|
146
|
+
* Key order below is the dropdown order **within each group** — most-common
|
|
147
|
+
* operation first, which is why Read starts at searchContacts and not at the
|
|
148
|
+
* enum's listContacts. The dashboard groups these entries by `group` and keeps
|
|
149
|
+
* this order, so moving a key moves the dropdown.
|
|
150
|
+
*/
|
|
151
|
+
export const GOOGLE_CONTACTS_OPERATION_SPECS = {
|
|
152
|
+
// ── Read ───────────────────────────────────────────────────────────
|
|
153
|
+
searchContacts: {
|
|
154
|
+
label: 'Search contacts',
|
|
155
|
+
description: 'Free-text search by name, email, phone, or organization.',
|
|
156
|
+
apiMethod: 'people.searchContacts',
|
|
157
|
+
scope: 'read',
|
|
158
|
+
group: 'Read',
|
|
159
|
+
params: [
|
|
160
|
+
{
|
|
161
|
+
name: 'query',
|
|
162
|
+
label: 'Query',
|
|
163
|
+
type: 'string',
|
|
164
|
+
required: true,
|
|
165
|
+
placeholder: '{{payload.email}} OR Ariel OR @company.com',
|
|
166
|
+
description: 'Free-text. Matches against names, email addresses, phone numbers, and organizations.',
|
|
167
|
+
},
|
|
168
|
+
personFields(),
|
|
169
|
+
],
|
|
170
|
+
},
|
|
171
|
+
listContacts: {
|
|
172
|
+
label: 'List contacts',
|
|
173
|
+
description: "Paginated list of every contact in the user's address book.",
|
|
174
|
+
apiMethod: 'people.connections.list',
|
|
175
|
+
scope: 'read',
|
|
176
|
+
group: 'Read',
|
|
177
|
+
params: [pageSize(), personFields()],
|
|
178
|
+
},
|
|
179
|
+
getContact: {
|
|
180
|
+
label: 'Get contact',
|
|
181
|
+
description: 'Fetch a single contact by its resourceName (e.g. people/c12345).',
|
|
182
|
+
apiMethod: 'people.get',
|
|
183
|
+
scope: 'read',
|
|
184
|
+
group: 'Read',
|
|
185
|
+
params: [contactResourceName(), personFields()],
|
|
186
|
+
},
|
|
187
|
+
// ── Write ──────────────────────────────────────────────────────────
|
|
188
|
+
createContact: {
|
|
189
|
+
label: 'Create contact',
|
|
190
|
+
description: 'Add a new contact. Pass fullName for auto split into given/family.',
|
|
191
|
+
apiMethod: 'people.createContact',
|
|
192
|
+
scope: 'write',
|
|
193
|
+
group: 'Write',
|
|
194
|
+
params: contactBody(),
|
|
195
|
+
},
|
|
196
|
+
updateContact: {
|
|
197
|
+
label: 'Update contact',
|
|
198
|
+
description: 'Update fields on an existing contact. Update mask is auto-derived.',
|
|
199
|
+
apiMethod: 'people.updateContact',
|
|
200
|
+
scope: 'write',
|
|
201
|
+
group: 'Write',
|
|
202
|
+
params: [contactResourceName(), ...contactBody()],
|
|
203
|
+
},
|
|
204
|
+
deleteContact: {
|
|
205
|
+
label: 'Delete contact',
|
|
206
|
+
description: 'Remove a contact by resourceName. Irreversible.',
|
|
207
|
+
apiMethod: 'people.deleteContact',
|
|
208
|
+
scope: 'write',
|
|
209
|
+
group: 'Write',
|
|
210
|
+
params: [contactResourceName()],
|
|
211
|
+
},
|
|
212
|
+
// ── Groups ─────────────────────────────────────────────────────────
|
|
213
|
+
listContactGroups: {
|
|
214
|
+
label: 'List groups',
|
|
215
|
+
description: 'List all contact groups (labels) the user has.',
|
|
216
|
+
apiMethod: 'contactGroups.list',
|
|
217
|
+
scope: 'read',
|
|
218
|
+
group: 'Groups',
|
|
219
|
+
params: [pageSize()],
|
|
220
|
+
},
|
|
221
|
+
addContactToGroup: {
|
|
222
|
+
label: 'Add to group',
|
|
223
|
+
description: 'Idempotent — no error if the contact is already in the group.',
|
|
224
|
+
apiMethod: 'contactGroups.members.modify',
|
|
225
|
+
scope: 'write',
|
|
226
|
+
group: 'Groups',
|
|
227
|
+
params: [contactResourceName(), groupResourceName()],
|
|
228
|
+
},
|
|
229
|
+
removeContactFromGroup: {
|
|
230
|
+
label: 'Remove from group',
|
|
231
|
+
description: "Idempotent — no error if the contact wasn't in the group.",
|
|
232
|
+
apiMethod: 'contactGroups.members.modify',
|
|
233
|
+
scope: 'write',
|
|
234
|
+
group: 'Groups',
|
|
235
|
+
params: [contactResourceName(), groupResourceName()],
|
|
236
|
+
},
|
|
237
|
+
// ── Bulk ───────────────────────────────────────────────────────────
|
|
238
|
+
batchCreateContacts: {
|
|
239
|
+
label: 'Batch create',
|
|
240
|
+
description: 'Bulk-create up to 200 contacts in one call. Pass a `contacts` array.',
|
|
241
|
+
apiMethod: 'people.batchCreateContacts',
|
|
242
|
+
scope: 'write',
|
|
243
|
+
group: 'Bulk',
|
|
244
|
+
params: [
|
|
245
|
+
{
|
|
246
|
+
name: 'contacts',
|
|
247
|
+
label: 'Contacts (JSON array)',
|
|
248
|
+
type: 'json',
|
|
249
|
+
required: true,
|
|
250
|
+
rows: 10,
|
|
251
|
+
placeholder: `[
|
|
252
|
+
{ "fullName": "Ariel Pérez", "emailAddresses": ["a@b.c"] },
|
|
253
|
+
{ "fullName": "Bob Smith", "phoneNumbers": ["+15551234"] }
|
|
254
|
+
]`,
|
|
255
|
+
description: `Up to 200 contacts per call. Each entry uses the same shape as createContact (fullName / emailAddresses / phoneNumbers / notes). ${BATCH_CONTACTS_TEMPLATING}`,
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
},
|
|
259
|
+
batchUpdateContacts: {
|
|
260
|
+
label: 'Batch update',
|
|
261
|
+
description: 'Bulk-update up to 200 contacts. Each entry needs a resourceName.',
|
|
262
|
+
apiMethod: 'people.batchUpdateContacts',
|
|
263
|
+
scope: 'write',
|
|
264
|
+
group: 'Bulk',
|
|
265
|
+
params: [
|
|
266
|
+
{
|
|
267
|
+
name: 'contacts',
|
|
268
|
+
label: 'Contacts (JSON array)',
|
|
269
|
+
type: 'json',
|
|
270
|
+
required: true,
|
|
271
|
+
rows: 10,
|
|
272
|
+
placeholder: `[
|
|
273
|
+
{ "resourceName": "people/c12345", "fullName": "New Name" }
|
|
274
|
+
]`,
|
|
275
|
+
description: `Up to 200 contacts per call. Each entry uses the same shape as updateContact — must include \`resourceName\`. ${BATCH_CONTACTS_TEMPLATING}`,
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
name: 'updateMask',
|
|
279
|
+
label: 'Update mask',
|
|
280
|
+
type: 'plain',
|
|
281
|
+
required: true,
|
|
282
|
+
placeholder: 'names,emailAddresses',
|
|
283
|
+
description: 'Comma-separated People API field paths that the bulk update is allowed to replace. Required.',
|
|
284
|
+
},
|
|
285
|
+
],
|
|
286
|
+
},
|
|
287
|
+
batchDeleteContacts: {
|
|
288
|
+
label: 'Batch delete',
|
|
289
|
+
description: 'Bulk-delete up to 500 contacts by resourceName. Irreversible.',
|
|
290
|
+
apiMethod: 'people.batchDeleteContacts',
|
|
291
|
+
scope: 'write',
|
|
292
|
+
group: 'Bulk',
|
|
293
|
+
params: [
|
|
294
|
+
{
|
|
295
|
+
name: 'resourceNames',
|
|
296
|
+
label: 'Resource names (JSON array)',
|
|
297
|
+
type: 'json',
|
|
298
|
+
required: true,
|
|
299
|
+
rows: 6,
|
|
300
|
+
placeholder: `[
|
|
301
|
+
"people/c12345",
|
|
302
|
+
"people/c67890"
|
|
303
|
+
]`,
|
|
304
|
+
description: 'Up to 500 resourceNames per call. Irreversible.',
|
|
305
|
+
},
|
|
306
|
+
],
|
|
307
|
+
},
|
|
308
|
+
// ── Group management ───────────────────────────────────────────────
|
|
309
|
+
createContactGroup: {
|
|
310
|
+
label: 'Create group',
|
|
311
|
+
description: 'Create a new contact group (label).',
|
|
312
|
+
apiMethod: 'contactGroups.create',
|
|
313
|
+
scope: 'write',
|
|
314
|
+
group: 'Group management',
|
|
315
|
+
params: [
|
|
316
|
+
{
|
|
317
|
+
name: 'name',
|
|
318
|
+
label: 'Group name',
|
|
319
|
+
type: 'string',
|
|
320
|
+
required: true,
|
|
321
|
+
placeholder: '{{payload.groupName}} OR Customers',
|
|
322
|
+
description: 'Display name for the new group.',
|
|
323
|
+
},
|
|
324
|
+
],
|
|
325
|
+
},
|
|
326
|
+
updateContactGroup: {
|
|
327
|
+
label: 'Rename group',
|
|
328
|
+
description: 'Rename an existing group.',
|
|
329
|
+
apiMethod: 'contactGroups.update',
|
|
330
|
+
scope: 'write',
|
|
331
|
+
group: 'Group management',
|
|
332
|
+
/* Name before resourceName: odd, but it is the order the page has always
|
|
333
|
+
rendered, and reordering is a separate decision. */
|
|
334
|
+
params: [
|
|
335
|
+
{
|
|
336
|
+
name: 'name',
|
|
337
|
+
label: 'Group name',
|
|
338
|
+
type: 'string',
|
|
339
|
+
required: true,
|
|
340
|
+
placeholder: '{{payload.groupName}} OR Customers',
|
|
341
|
+
description: 'New display name (renames the existing group).',
|
|
342
|
+
},
|
|
343
|
+
groupResourceNameAsResourceName(),
|
|
344
|
+
],
|
|
345
|
+
},
|
|
346
|
+
deleteContactGroup: {
|
|
347
|
+
label: 'Delete group',
|
|
348
|
+
description: 'Delete a group. Optionally delete its contacts too.',
|
|
349
|
+
apiMethod: 'contactGroups.delete',
|
|
350
|
+
scope: 'write',
|
|
351
|
+
group: 'Group management',
|
|
352
|
+
params: [
|
|
353
|
+
groupResourceNameAsResourceName(),
|
|
354
|
+
{
|
|
355
|
+
name: 'deleteContacts',
|
|
356
|
+
label: 'Also delete contacts in the group?',
|
|
357
|
+
type: 'boolean',
|
|
358
|
+
switchLabel: 'Delete the contacts that are members of this group too.',
|
|
359
|
+
description: 'Off (default) — contacts stay, just lose this group membership. On — every contact in the group is also deleted. Irreversible.',
|
|
360
|
+
},
|
|
361
|
+
],
|
|
362
|
+
},
|
|
363
|
+
};
|