first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled

This commit is contained in:
2026-03-17 16:22:57 +03:30
commit 3d5eaf9445
15349 changed files with 2847338 additions and 0 deletions
@@ -0,0 +1,201 @@
import type { INodeProperties } from 'n8n-workflow';
export const eventOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['event'],
},
},
options: [
{
name: 'Track',
value: 'track',
description: 'Track an event for a specific customer',
action: 'Track an event',
},
],
default: 'track',
},
];
export const eventFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* event:track */
/* -------------------------------------------------------------------------- */
{
displayName: 'ID',
name: 'id',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['event'],
operation: ['track'],
},
},
description: 'The unique identifier of the customer',
},
{
displayName: 'Email',
name: 'email',
type: 'string',
placeholder: 'name@email.com',
default: '',
required: true,
displayOptions: {
show: {
resource: ['event'],
operation: ['track'],
},
},
},
{
displayName: 'Event Name',
name: 'eventName',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
resource: ['event'],
operation: ['track'],
},
},
description: 'The name of the event tracked',
},
{
displayName: 'JSON Parameters',
name: 'jsonParameters',
type: 'boolean',
default: false,
displayOptions: {
show: {
resource: ['event'],
operation: ['track'],
},
},
},
{
displayName: 'Data',
name: 'dataAttributesUi',
placeholder: 'Add Data',
description: 'Key value pairs that represent any properties you want to track with this event',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
default: {},
displayOptions: {
show: {
resource: ['event'],
operation: ['track'],
jsonParameters: [false],
},
},
options: [
{
name: 'dataAttributesValues',
displayName: 'Data',
values: [
{
displayName: 'Key',
name: 'key',
type: 'string',
default: '',
description: 'Name of the property to set',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'Value of the property to set',
},
],
},
],
},
{
displayName: 'Extra',
name: 'extraAttributesUi',
placeholder: 'Add Extra',
description:
'Key value pairs that represent reserved, Vero-specific operators. Refer to the note on “deduplication” below.',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
default: {},
displayOptions: {
show: {
resource: ['event'],
operation: ['track'],
jsonParameters: [false],
},
},
options: [
{
name: 'extraAttributesValues',
displayName: 'Extra',
values: [
{
displayName: 'Key',
name: 'key',
type: 'string',
default: '',
description: 'Name of the property to set',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'Value of the property to set',
},
],
},
],
},
{
displayName: 'Data',
name: 'dataAttributesJson',
type: 'json',
default: '',
typeOptions: {
alwaysOpenEditWindow: true,
},
description: 'Key value pairs that represent the custom user properties you want to update',
displayOptions: {
show: {
resource: ['event'],
operation: ['track'],
jsonParameters: [true],
},
},
},
{
displayName: 'Extra',
name: 'extraAttributesJson',
type: 'json',
default: '',
typeOptions: {
alwaysOpenEditWindow: true,
},
description:
'Key value pairs that represent reserved, Vero-specific operators. Refer to the note on “deduplication” below.',
displayOptions: {
show: {
resource: ['event'],
operation: ['track'],
jsonParameters: [true],
},
},
},
];
@@ -0,0 +1,53 @@
import type {
IDataObject,
IExecuteFunctions,
IHttpRequestMethods,
ILoadOptionsFunctions,
IRequestOptions,
JsonObject,
} from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow';
export async function veroApiRequest(
this: IExecuteFunctions | ILoadOptionsFunctions,
method: IHttpRequestMethods,
resource: string,
body: any = {},
qs: IDataObject = {},
uri?: string,
option: IDataObject = {},
): Promise<any> {
const credentials = await this.getCredentials('veroApi');
let options: IRequestOptions = {
method,
qs,
body,
form: {
auth_token: credentials.authToken,
...body,
},
uri: uri || `https://api.getvero.com/api/v2${resource}`,
json: true,
};
options = Object.assign({}, options, option);
if (Object.keys(options.body as IDataObject).length === 0) {
delete options.body;
}
try {
return await this.helpers.request(options);
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
}
export function validateJSON(json: string | undefined): any {
let result;
try {
result = JSON.parse(json!);
} catch (exception) {
result = undefined;
}
return result;
}
@@ -0,0 +1,318 @@
import type { INodeProperties } from 'n8n-workflow';
export const userOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['user'],
},
},
options: [
{
name: 'Add Tags',
value: 'addTags',
description: 'Adds a tag to a users profile',
action: 'Add tags to a user',
},
{
name: 'Alias',
value: 'alias',
description: 'Change a users identifier',
action: "Change a user's alias",
},
{
name: 'Create or Update',
value: 'create',
description: 'Create or update a user profile',
action: 'Create or update a user',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a user',
action: 'Delete a user',
},
{
name: 'Re-Subscribe',
value: 'resubscribe',
description: 'Resubscribe a user',
action: 'Resubscribe a user',
},
{
name: 'Remove Tags',
value: 'removeTags',
description: 'Removes a tag from a users profile',
action: 'Remove tags from a user',
},
{
name: 'Unsubscribe',
value: 'unsubscribe',
description: 'Unsubscribe a user',
action: 'Unsubscribe a user',
},
],
default: 'create',
},
];
export const userFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* user:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'ID',
name: 'id',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['user'],
operation: ['create'],
},
},
description: 'The unique identifier of the customer',
},
{
displayName: 'JSON Parameters',
name: 'jsonParameters',
type: 'boolean',
default: false,
displayOptions: {
show: {
resource: ['user'],
operation: ['create'],
},
},
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['user'],
operation: ['create'],
},
},
options: [
{
displayName: 'Email',
name: 'email',
type: 'string',
placeholder: 'name@email.com',
default: '',
description: 'The table to create the row in',
},
],
},
{
displayName: 'Data',
name: 'dataAttributesUi',
placeholder: 'Add Data',
description: 'Key value pairs that represent the custom user properties you want to update',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
default: {},
displayOptions: {
show: {
resource: ['user'],
operation: ['create'],
jsonParameters: [false],
},
},
options: [
{
name: 'dataAttributesValues',
displayName: 'Data',
values: [
{
displayName: 'Key',
name: 'key',
type: 'string',
default: '',
description: 'Name of the property to set',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'Value of the property to set',
},
],
},
],
},
{
displayName: 'Data',
name: 'dataAttributesJson',
type: 'json',
default: '',
typeOptions: {
alwaysOpenEditWindow: true,
},
description: 'Key value pairs that represent the custom user properties you want to update',
displayOptions: {
show: {
resource: ['user'],
operation: ['create'],
jsonParameters: [true],
},
},
},
/* -------------------------------------------------------------------------- */
/* user:alias */
/* -------------------------------------------------------------------------- */
{
displayName: 'ID',
name: 'id',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['user'],
operation: ['alias'],
},
},
description: 'The old unique identifier of the user',
},
{
displayName: 'New ID',
name: 'newId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['user'],
operation: ['alias'],
},
},
description: 'The new unique identifier of the user',
},
/* -------------------------------------------------------------------------- */
/* user:unsubscribe */
/* -------------------------------------------------------------------------- */
{
displayName: 'ID',
name: 'id',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['user'],
operation: ['unsubscribe'],
},
},
description: 'The unique identifier of the user',
},
/* -------------------------------------------------------------------------- */
/* user:resubscribe */
/* -------------------------------------------------------------------------- */
{
displayName: 'ID',
name: 'id',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['user'],
operation: ['resubscribe'],
},
},
description: 'The unique identifier of the user',
},
/* -------------------------------------------------------------------------- */
/* user:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'ID',
name: 'id',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['user'],
operation: ['delete'],
},
},
description: 'The unique identifier of the user',
},
/* -------------------------------------------------------------------------- */
/* user:addTags */
/* -------------------------------------------------------------------------- */
{
displayName: 'ID',
name: 'id',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['user'],
operation: ['addTags'],
},
},
description: 'The unique identifier of the user',
},
{
displayName: 'Tags',
name: 'tags',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['user'],
operation: ['addTags'],
},
},
description: 'Tags to add separated by ","',
},
/* -------------------------------------------------------------------------- */
/* user:removeTags */
/* -------------------------------------------------------------------------- */
{
displayName: 'ID',
name: 'id',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['user'],
operation: ['removeTags'],
},
},
description: 'The unique identifier of the user',
},
{
displayName: 'Tags',
name: 'tags',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['user'],
operation: ['removeTags'],
},
},
description: 'Tags to remove separated by ","',
},
];
@@ -0,0 +1,18 @@
{
"node": "n8n-nodes-base.vero",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Communication"],
"resources": {
"credentialDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/credentials/vero/"
}
],
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.vero/"
}
]
}
}
+236
View File
@@ -0,0 +1,236 @@
import type {
IExecuteFunctions,
IDataObject,
INodeExecutionData,
INodeType,
INodeTypeDescription,
JsonObject,
} from 'n8n-workflow';
import { NodeConnectionTypes, NodeApiError } from 'n8n-workflow';
import { eventFields, eventOperations } from './EventDescripion';
import { validateJSON, veroApiRequest } from './GenericFunctions';
import { userFields, userOperations } from './UserDescription';
export class Vero implements INodeType {
description: INodeTypeDescription = {
displayName: 'Vero',
name: 'vero',
icon: 'file:vero.svg',
group: ['output'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Consume Vero API',
defaults: {
name: 'Vero',
},
usableAsTool: true,
inputs: [NodeConnectionTypes.Main],
outputs: [NodeConnectionTypes.Main],
credentials: [
{
name: 'veroApi',
required: true,
},
],
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options: [
{
name: 'User',
value: 'user',
description: 'Create, update and manage the subscription status of your users',
},
{
name: 'Event',
value: 'event',
description: 'Track events based on actions your customers take in real time',
},
],
default: 'user',
},
...userOperations,
...eventOperations,
...userFields,
...eventFields,
],
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: IDataObject[] = [];
const length = items.length;
let responseData;
for (let i = 0; i < length; i++) {
try {
const resource = this.getNodeParameter('resource', 0);
const operation = this.getNodeParameter('operation', 0);
//https://developers.getvero.com/?bash#users
if (resource === 'user') {
//https://developers.getvero.com/?bash#users-identify
if (operation === 'create') {
const id = this.getNodeParameter('id', i) as string;
const additionalFields = this.getNodeParameter('additionalFields', i);
const jsonActive = this.getNodeParameter('jsonParameters', i);
const body: IDataObject = {
id,
};
if (additionalFields.email) {
body.email = additionalFields.email as string;
}
if (!jsonActive) {
const dataAttributesValues = (
this.getNodeParameter('dataAttributesUi', i) as IDataObject
).dataAttributesValues as IDataObject[];
if (dataAttributesValues) {
const dataAttributes: IDataObject = {};
for (let index = 0; index < dataAttributesValues.length; index++) {
dataAttributes[dataAttributesValues[index].key as string] =
dataAttributesValues[index].value;
body.data = dataAttributes;
}
}
} else {
const dataAttributesJson = validateJSON(
this.getNodeParameter('dataAttributesJson', i) as string,
);
if (dataAttributesJson) {
body.data = dataAttributesJson;
}
}
try {
responseData = await veroApiRequest.call(this, 'POST', '/users/track', body);
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
}
//https://developers.getvero.com/?bash#users-alias
if (operation === 'alias') {
const id = this.getNodeParameter('id', i) as string;
const newId = this.getNodeParameter('newId', i) as string;
const body = {
id,
new_id: newId,
};
try {
responseData = await veroApiRequest.call(this, 'PUT', '/users/reidentify', body);
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
}
//https://developers.getvero.com/?bash#users-unsubscribe
//https://developers.getvero.com/?bash#users-resubscribe
//https://developers.getvero.com/?bash#users-delete
if (
operation === 'unsubscribe' ||
operation === 'resubscribe' ||
operation === 'delete'
) {
const id = this.getNodeParameter('id', i) as string;
const body = {
id,
};
try {
responseData = await veroApiRequest.call(this, 'POST', `/users/${operation}`, body);
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
}
//https://developers.getvero.com/?bash#tags-add
//https://developers.getvero.com/?bash#tags-remove
if (operation === 'addTags' || operation === 'removeTags') {
const id = this.getNodeParameter('id', i) as string;
const tags = (this.getNodeParameter('tags', i) as string).split(',');
const body: IDataObject = {
id,
};
if (operation === 'addTags') {
body.add = JSON.stringify(tags);
}
if (operation === 'removeTags') {
body.remove = JSON.stringify(tags);
}
try {
responseData = await veroApiRequest.call(this, 'PUT', '/users/tags/edit', body);
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
}
}
//https://developers.getvero.com/?bash#events
if (resource === 'event') {
//https://developers.getvero.com/?bash#events-track
if (operation === 'track') {
const id = this.getNodeParameter('id', i) as string;
const email = this.getNodeParameter('email', i) as string;
const eventName = this.getNodeParameter('eventName', i) as string;
const jsonActive = this.getNodeParameter('jsonParameters', i);
const body: IDataObject = {
identity: { id, email },
event_name: eventName,
email,
};
if (!jsonActive) {
const dataAttributesValues = (
this.getNodeParameter('dataAttributesUi', i) as IDataObject
).dataAttributesValues as IDataObject[];
if (dataAttributesValues) {
const dataAttributes: IDataObject = {};
for (let index = 0; index < dataAttributesValues.length; index++) {
dataAttributes[dataAttributesValues[index].key as string] =
dataAttributesValues[index].value;
body.data = JSON.stringify(dataAttributes);
}
}
const extraAttributesValues = (
this.getNodeParameter('extraAttributesUi', i) as IDataObject
).extraAttributesValues as IDataObject[];
if (extraAttributesValues) {
const extraAttributes: IDataObject = {};
for (let index = 0; index < extraAttributesValues.length; index++) {
extraAttributes[extraAttributesValues[index].key as string] =
extraAttributesValues[index].value;
body.extras = JSON.stringify(extraAttributes);
}
}
} else {
const dataAttributesJson = validateJSON(
this.getNodeParameter('dataAttributesJson', i) as string,
);
if (dataAttributesJson) {
body.data = JSON.stringify(dataAttributesJson);
}
const extraAttributesJson = validateJSON(
this.getNodeParameter('extraAttributesJson', i) as string,
);
if (extraAttributesJson) {
body.extras = JSON.stringify(extraAttributesJson);
}
}
try {
responseData = await veroApiRequest.call(this, 'POST', '/events/track', body);
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
}
}
if (Array.isArray(responseData)) {
returnData.push.apply(returnData, responseData as IDataObject[]);
} else {
returnData.push(responseData as IDataObject);
}
} catch (error) {
if (this.continueOnFail()) {
returnData.push({ error: error.message });
continue;
}
throw error;
}
}
return [this.helpers.returnJsonArray(returnData)];
}
}
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="225" height="225" fill="none"><defs><linearGradient id="a" x1="1.047" x2="-.159" y1=".454" y2=".535"><stop offset="0" stop-color="#2936a3"/><stop offset="1" stop-color="#162286"/></linearGradient><linearGradient id="b" x1="-.192" x2=".958" y1=".675" y2=".156"><stop offset="0" stop-color="#2297f4"/><stop offset="1" stop-color="#31a0f9"/></linearGradient></defs><path fill="url(#a)" stroke="null" d="M211.145 121.39c18.18-18.162 18.18-47.608 0-65.769s-47.656-18.161-65.836 0L112.39 88.505l32.817 32.783.101.101c18.161 18.142 47.594 18.162 65.779.058z"/><path fill="#17da88" stroke="null" d="m46.555 154.275-32.92-32.885c-18.18-18.162-18.18-47.607 0-65.768s47.657-18.162 65.837 0l32.92 32.886z"/><path fill="url(#b)" stroke="null" d="M211.145 121.39c-18.18 18.16-47.656 18.16-65.836 0l-.101-.102-32.816-32.78-65.836 65.767 18.808 18.789c25.972 25.945 68.081 25.945 94.053 0z"/></svg>

After

Width:  |  Height:  |  Size: 929 B