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,890 @@
import type { INodeProperties } from 'n8n-workflow';
export const accountOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['account'],
},
},
options: [
{
name: 'Add Note',
value: 'addNote',
description: 'Add note to an account',
action: 'Add a note to an account',
},
{
name: 'Create',
value: 'create',
description: 'Create an account',
action: 'Create an account',
},
{
name: 'Create or Update',
value: 'upsert',
description:
'Create a new account, or update the current one if it already exists (upsert)',
action: 'Create or update an account',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete an account',
action: 'Delete an account',
},
{
name: 'Get',
value: 'get',
description: 'Get an account',
action: 'Get an account',
},
{
name: 'Get Many',
value: 'getAll',
description: 'Get many accounts',
action: 'Get many accounts',
},
{
name: 'Get Summary',
value: 'getSummary',
description: "Returns an overview of account's metadata",
action: 'Get an account summary',
},
{
name: 'Update',
value: 'update',
description: 'Update an account',
action: 'Update an account',
},
],
default: 'create',
},
];
export const accountFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* account:create */
/* -------------------------------------------------------------------------- */
{
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options
displayName: 'Match Against',
name: 'externalId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getExternalIdFields',
loadOptionsDependsOn: ['resource'],
},
required: true,
default: '',
displayOptions: {
show: {
resource: ['account'],
operation: ['upsert'],
},
},
description:
'The field to check to see if the account already exists. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Value to Match',
name: 'externalIdValue',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['account'],
operation: ['upsert'],
},
},
description:
"If this value exists in the 'match against' field, update the account. Otherwise create a new one.",
},
{
displayName: 'Name',
name: 'name',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['account'],
operation: ['create', 'upsert'],
},
},
description: 'Name of the account. Maximum size is 255 characters.',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['account'],
operation: ['create', 'upsert'],
},
},
options: [
{
displayName: 'Account Number',
name: 'accountNumber',
type: 'string',
default: '',
description:
'Account number assigned to this account (not the unique ID). Maximum size is 40 characters.',
},
{
displayName: 'Annual Revenue',
name: 'annualRevenue',
type: 'number',
typeOptions: {
numberPrecision: 2,
},
default: '',
description: 'Estimated annual revenue of the account',
},
{
displayName: 'Account Source Name or ID',
name: 'accountSource',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getAccountSources',
},
default: '',
description:
'The source of the account record. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Billing City',
name: 'billingCity',
type: 'string',
default: '',
description:
'Details for the billing address of this account. Maximum size is 40 characters.',
},
{
displayName: 'Billing Country',
name: 'billingCountry',
type: 'string',
default: '',
description:
'Details for the billing address of this account. Maximum size is 80 characters.',
},
{
displayName: 'Billing Postal Code',
name: 'billingPostalCode',
type: 'string',
default: '',
description:
'Details for the billing address of this account. Maximum size is 20 characters.',
},
{
displayName: 'Billing State',
name: 'billingState',
type: 'string',
default: '',
description:
'Details for the billing address of this account. Maximum size is 80 characters.',
},
{
displayName: 'Billing Street',
name: 'billingStreet',
type: 'string',
default: '',
description: 'Street address for the billing address of this account',
},
{
displayName: 'Custom Fields',
name: 'customFieldsUi',
placeholder: 'Add Custom Field',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
description: 'Filter by custom fields',
default: {},
options: [
{
name: 'customFieldsValues',
displayName: 'Custom Field',
values: [
{
displayName: 'Field Name or ID',
name: 'fieldId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCustomFields',
},
default: '',
description:
'The ID of the field to add custom field to. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'The value to set on custom field',
},
],
},
],
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
description: 'Text description of the account. Limited to 32,000 KB.',
},
{
displayName: 'Fax',
name: 'fax',
type: 'string',
default: '',
description: 'Fax number for the account',
},
{
displayName: 'Jigsaw',
name: 'jigsaw',
type: 'string',
default: '',
description: 'References the ID of a company in Data.com',
},
{
displayName: 'Industry',
name: 'industry',
type: 'string',
default: '',
description: 'The website of this account. Maximum of 255 characters.',
},
{
displayName: 'Number Of Employees',
name: 'numberOfEmployees',
type: 'number',
default: '',
},
{
displayName: 'Owner Name or ID',
name: 'owner',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: '',
description:
'The owner of the account. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Parent ID',
name: 'parentId',
type: 'string',
default: '',
description: 'ID of the parent object, if any',
},
{
displayName: 'Phone',
name: 'phone',
type: 'string',
default: '',
description: 'Phone number for the account',
},
{
displayName: 'Record Type Name or ID',
name: 'recordTypeId',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getRecordTypes',
},
default: '',
},
{
displayName: 'SicDesc',
name: 'sicDesc',
type: 'string',
default: '',
description:
'A brief description of an organizations line of business, based on its SIC code',
},
{
displayName: 'Type Name or ID',
name: 'type',
type: 'options',
default: '',
typeOptions: {
loadOptionsMethod: 'getAccountTypes',
},
description:
'Type of account. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Shipping City',
name: 'shippingCity',
type: 'string',
default: '',
description:
'Details of the shipping address for this account. City maximum size is 40 characters.',
},
{
displayName: 'Shipping Country',
name: 'shippingCountry',
type: 'string',
default: '',
description:
'Details of the shipping address for this account. Country maximum size is 80 characters.',
},
{
displayName: 'Shipping Postal Code',
name: 'shippingPostalCode',
type: 'string',
default: '',
description:
'Details of the shipping address for this account. Postal code maximum size is 20 characters.',
},
{
displayName: 'Shipping State',
name: 'shippingState',
type: 'string',
default: '',
description:
'Details of the shipping address for this account. State maximum size is 80 characters.',
},
{
displayName: 'Shipping Street',
name: 'shippingStreet',
type: 'string',
default: '',
description:
'The street address of the shipping address for this account. Maximum of 255 characters.',
},
{
displayName: 'Website',
name: 'website',
type: 'string',
default: '',
description: 'The website of this account. Maximum of 255 characters.',
},
],
},
/* -------------------------------------------------------------------------- */
/* account:update */
/* -------------------------------------------------------------------------- */
{
displayName: 'Account ID',
name: 'accountId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['account'],
operation: ['update'],
},
},
description: 'ID of account that needs to be fetched',
},
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['account'],
operation: ['update'],
},
},
options: [
{
displayName: 'Account Number',
name: 'accountNumber',
type: 'string',
default: '',
description:
'Account number assigned to this account (not the unique ID). Maximum size is 40 characters.',
},
{
displayName: 'Account Source Name or ID',
name: 'accountSource',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getAccountSources',
},
default: '',
description:
'The source of the account record. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Annual Revenue',
name: 'annualRevenue',
type: 'number',
typeOptions: {
numberPrecision: 2,
},
default: '',
description: 'Estimated annual revenue of the account',
},
{
displayName: 'Billing City',
name: 'billingCity',
type: 'string',
default: '',
description:
'Details for the billing address of this account. Maximum size is 40 characters.',
},
{
displayName: 'Billing Country',
name: 'billingCountry',
type: 'string',
default: '',
description:
'Details for the billing address of this account. Maximum size is 80 characters.',
},
{
displayName: 'Billing Postal Code',
name: 'billingPostalCode',
type: 'string',
default: '',
description:
'Details for the billing address of this account. Maximum size is 20 characters.',
},
{
displayName: 'Billing State',
name: 'billingState',
type: 'string',
default: '',
description:
'Details for the billing address of this account. Maximum size is 80 characters.',
},
{
displayName: 'Billing Street',
name: 'billingStreet',
type: 'string',
default: '',
description: 'Street address for the billing address of this account',
},
{
displayName: 'Custom Fields',
name: 'customFieldsUi',
placeholder: 'Add Custom Field',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
description: 'Filter by custom fields',
default: {},
options: [
{
name: 'customFieldsValues',
displayName: 'Custom Field',
values: [
{
displayName: 'Field Name or ID',
name: 'fieldId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCustomFields',
},
default: '',
description:
'The ID of the field to add custom field to. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'The value to set on custom field',
},
],
},
],
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
description: 'Text description of the account. Limited to 32,000 KB.',
},
{
displayName: 'Fax',
name: 'fax',
type: 'string',
default: '',
description: 'Fax number for the account',
},
{
displayName: 'Industry',
name: 'industry',
type: 'string',
default: '',
description: 'The website of this account. Maximum of 255 characters.',
},
{
displayName: 'Jigsaw',
name: 'jigsaw',
type: 'string',
default: '',
description: 'References the ID of a company in Data.com',
},
{
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
description: 'Name of the account. Maximum size is 255 characters.',
},
{
displayName: 'Number Of Employees',
name: 'numberOfEmployees',
type: 'number',
default: '',
},
{
displayName: 'Owner Name or ID',
name: 'ownerId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: '',
description:
'The owner of the account. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Parent ID',
name: 'parentId',
type: 'string',
default: '',
description: 'ID of the parent object, if any',
},
{
displayName: 'Phone',
name: 'phone',
type: 'string',
default: '',
description: 'Phone number for the account',
},
{
displayName: 'Record Type Name or ID',
name: 'recordTypeId',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getRecordTypes',
},
default: '',
},
{
displayName: 'SicDesc',
name: 'sicDesc',
type: 'string',
default: '',
description:
'A brief description of an organizations line of business, based on its SIC code',
},
{
displayName: 'Shipping City',
name: 'shippingCity',
type: 'string',
default: '',
description:
'Details of the shipping address for this account. City maximum size is 40 characters.',
},
{
displayName: 'Shipping Country',
name: 'shippingCountry',
type: 'string',
default: '',
description:
'Details of the shipping address for this account. Country maximum size is 80 characters.',
},
{
displayName: 'Shipping Postal Code',
name: 'shippingPostalCode',
type: 'string',
default: '',
description:
'Details of the shipping address for this account. Postal code maximum size is 20 characters.',
},
{
displayName: 'Shipping State',
name: 'shippingState',
type: 'string',
default: '',
description:
'Details of the shipping address for this account. State maximum size is 80 characters.',
},
{
displayName: 'Shipping Street',
name: 'shippingStreet',
type: 'string',
default: '',
description:
'The street address of the shipping address for this account. Maximum of 255 characters.',
},
{
displayName: 'Type Name or ID',
name: 'type',
type: 'options',
default: '',
typeOptions: {
loadOptionsMethod: 'getAccountTypes',
},
description:
'Type of account. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Website',
name: 'website',
type: 'string',
default: '',
description: 'The website of this account. Maximum of 255 characters.',
},
],
},
/* -------------------------------------------------------------------------- */
/* account:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Account ID',
name: 'accountId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['account'],
operation: ['get'],
},
},
description: 'ID of account that needs to be fetched',
},
/* -------------------------------------------------------------------------- */
/* account:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'Account ID',
name: 'accountId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['account'],
operation: ['delete'],
},
},
description: 'ID of account that needs to be fetched',
},
/* -------------------------------------------------------------------------- */
/* account:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: ['account'],
operation: ['getAll'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: ['account'],
operation: ['getAll'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
description: 'Max number of results to return',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['account'],
operation: ['getAll'],
},
},
options: [
{
displayName: 'Fields',
name: 'fields',
type: 'string',
default: '',
description: 'Fields to include separated by ,',
},
{
displayName: 'Conditions',
name: 'conditionsUi',
placeholder: 'Add Condition',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
description: 'The condition to set',
default: {},
options: [
{
name: 'conditionValues',
displayName: 'Condition',
values: [
{
displayName: 'Field Name or ID',
name: 'field',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getAccountFields',
},
default: '',
description:
'For date, number, or boolean, please use expressions. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
options: [
{
name: '<',
value: '<',
},
{
name: '<=',
value: '<=',
},
{
name: '=',
value: 'equal',
},
{
name: '>',
value: '>',
},
{
name: '>=',
value: '>=',
},
],
default: 'equal',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
},
],
},
],
},
],
},
/* -------------------------------------------------------------------------- */
/* account:addNote */
/* -------------------------------------------------------------------------- */
{
displayName: 'Account ID',
name: 'accountId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['account'],
operation: ['addNote'],
},
},
description: 'ID of account that needs to be fetched',
},
{
displayName: 'Title',
name: 'title',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['account'],
operation: ['addNote'],
},
},
description: 'Title of the note',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['account'],
operation: ['addNote'],
},
},
options: [
{
displayName: 'Body',
name: 'body',
type: 'string',
default: '',
description: 'Body of the note. Limited to 32 KB.',
},
{
displayName: 'Is Private',
name: 'isPrivate',
type: 'boolean',
default: false,
description:
'Whether true, only the note owner or a user with the “Modify All Data” permission can view the note or query it via the API',
},
{
displayName: 'Owner Name or ID',
name: 'ownerId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: '',
description:
'ID of the user who owns the note. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
],
},
];
@@ -0,0 +1,28 @@
export interface IAccount {
[key: string]: any;
Name?: string;
Fax?: string;
Type?: string;
Phone?: string;
Jigsaw?: string;
OwnerId?: string;
SicDesc?: string;
Website?: string;
Industry?: string;
ParentId?: string;
BillingCity?: string;
Description?: string;
BillingState?: string;
ShippingStreet?: string;
ShippingCity?: string;
AccountNumber?: string;
AccountSource?: string;
AnnualRevenue?: number;
BillingStreet?: string;
ShippingState?: string;
BillingCountry?: string;
ShippingCountry?: string;
BillingPostalCode?: string;
NumberOfEmployees?: string;
ShippingPostalCode?: string;
}
@@ -0,0 +1,377 @@
import type { INodeProperties } from 'n8n-workflow';
export const attachmentOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['attachment'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a attachment',
action: 'Create an attachment',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a attachment',
action: 'Delete an attachment',
},
{
name: 'Get',
value: 'get',
description: 'Get a attachment',
action: 'Get an attachment',
},
{
name: 'Get Many',
value: 'getAll',
description: 'Get many attachments',
action: 'Get many attachments',
},
{
name: 'Get Summary',
value: 'getSummary',
description: "Returns an overview of attachment's metadata",
action: 'Get an attachment summary',
},
{
name: 'Update',
value: 'update',
description: 'Update a attachment',
action: 'Update an attachment',
},
],
default: 'create',
},
];
export const attachmentFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* attachment:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'Parent ID',
name: 'parentId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
resource: ['attachment'],
operation: ['create'],
},
},
},
{
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
resource: ['attachment'],
operation: ['create'],
},
},
description:
'Required. Name of the attached file. Maximum size is 255 characters. Label is File Name.',
},
{
displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
required: true,
displayOptions: {
show: {
resource: ['attachment'],
operation: ['create'],
},
},
placeholder: '',
hint: 'The name of the input binary field containing the file to be uploaded',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['attachment'],
operation: ['create'],
},
},
options: [
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
description: 'Text description of the Document. Limit: 255 characters.',
},
{
displayName: 'Is Private',
name: 'isPrivate',
type: 'boolean',
default: false,
description:
'Whether this record is viewable only by the owner and administrators (true) or viewable by all otherwise-allowed users (false)',
},
{
displayName: 'Owner Name or ID',
name: 'owner',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: '',
description:
'ID of the User who owns the attachment. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
],
},
/* -------------------------------------------------------------------------- */
/* attachment:update */
/* -------------------------------------------------------------------------- */
{
displayName: 'Attachment ID',
name: 'attachmentId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['attachment'],
operation: ['update'],
},
},
description: 'ID of attachment that needs to be fetched',
},
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['attachment'],
operation: ['update'],
},
},
options: [
{
displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
placeholder: '',
hint: 'The name of the input binary field containing the file to be uploaded',
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
description: 'Text description of the Document. Limit: 255 characters.',
},
{
displayName: 'Is Private',
name: 'isPrivate',
type: 'boolean',
default: false,
description:
'Whether this record is viewable only by the owner and administrators (true) or viewable by all otherwise-allowed users (false)',
},
{
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
description:
'Required. Name of the attached file. Maximum size is 255 characters. Label is File Name.',
},
{
displayName: 'Owner Name or ID',
name: 'owner',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: '',
description:
'ID of the User who owns the attachment. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
],
},
/* -------------------------------------------------------------------------- */
/* attachment:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Attachment ID',
name: 'attachmentId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['attachment'],
operation: ['get'],
},
},
description: 'ID of attachment that needs to be fetched',
},
/* -------------------------------------------------------------------------- */
/* attachment:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'Attachment ID',
name: 'attachmentId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['attachment'],
operation: ['delete'],
},
},
description: 'ID of attachment that needs to be fetched',
},
/* -------------------------------------------------------------------------- */
/* attachment:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: ['attachment'],
operation: ['getAll'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: ['attachment'],
operation: ['getAll'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
description: 'Max number of results to return',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['attachment'],
operation: ['getAll'],
},
},
options: [
{
displayName: 'Conditions',
name: 'conditionsUi',
placeholder: 'Add Condition',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
description: 'The condition to set',
default: {},
options: [
{
name: 'conditionValues',
displayName: 'Condition',
values: [
{
displayName: 'Field Name or ID',
name: 'field',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getAtachmentFields',
},
default: '',
description:
'For date, number, or boolean, please use expressions. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
// eslint-disable-next-line n8n-nodes-base/node-param-operation-without-no-data-expression
{
displayName: 'Operation',
name: 'operation',
type: 'options',
options: [
{
name: '<',
value: '<',
},
{
name: '<=',
value: '<=',
},
{
name: '=',
value: 'equal',
},
{
name: '>',
value: '>',
},
{
name: '>=',
value: '>=',
},
],
default: 'equal',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
},
],
},
],
},
{
displayName: 'Fields',
name: 'fields',
type: 'string',
default: '',
description: 'Fields to include separated by ,',
},
],
},
];
@@ -0,0 +1,9 @@
export interface IAttachment {
ParentId?: string;
Name?: string;
OwnerId?: string;
IsPrivate?: boolean;
ContentType?: string;
Description?: string;
Body?: string;
}
@@ -0,0 +1,6 @@
export interface ICampaignMember {
CampaignId?: string;
ContactId?: string;
LeadId?: string;
Status?: string;
}
@@ -0,0 +1,704 @@
import type { INodeProperties } from 'n8n-workflow';
export const caseOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['case'],
},
},
options: [
{
name: 'Add Comment',
value: 'addComment',
description: 'Add a comment to a case',
action: 'Add a comment to a case',
},
{
name: 'Create',
value: 'create',
description: 'Create a case',
action: 'Create a case',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a case',
action: 'Delete a case',
},
{
name: 'Get',
value: 'get',
description: 'Get a case',
action: 'Get a case',
},
{
name: 'Get Many',
value: 'getAll',
description: 'Get many cases',
action: 'Get many cases',
},
{
name: 'Get Summary',
value: 'getSummary',
description: "Returns an overview of case's metadata",
action: 'Get a case summary',
},
{
name: 'Update',
value: 'update',
description: 'Update a case',
action: 'Update a case',
},
],
default: 'create',
},
];
export const caseFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* case:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'Type Name or ID',
name: 'type',
type: 'options',
required: true,
typeOptions: {
loadOptionsMethod: 'getCaseTypes',
},
displayOptions: {
show: {
resource: ['case'],
operation: ['create'],
},
},
default: '',
description:
'The type of case. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['case'],
operation: ['create'],
},
},
options: [
{
displayName: 'Account ID',
name: 'accountId',
type: 'string',
default: '',
description: 'ID of the account associated with this case',
},
{
displayName: 'Contact ID',
name: 'contactId',
type: 'string',
default: '',
description: 'ID of the associated Contact',
},
{
displayName: 'Custom Fields',
name: 'customFieldsUi',
placeholder: 'Add Custom Field',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
description: 'Filter by custom fields',
default: {},
options: [
{
name: 'customFieldsValues',
displayName: 'Custom Field',
values: [
{
displayName: 'Field Name or ID',
name: 'fieldId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCustomFields',
},
default: '',
description:
'The ID of the field to add custom field to. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'The value to set on custom field',
},
],
},
],
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
description: 'A text description of the case. Limit: 32 KB.',
},
{
displayName: 'Is Escalated',
name: 'isEscalated',
type: 'boolean',
default: false,
description: 'Whether indicates whether the case has been escalated (true) or not',
},
{
displayName: 'Origin Name or ID',
name: 'origin',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCaseOrigins',
},
default: '',
description:
'The source of the case, such as Email, Phone, or Web. Label is Case Origin. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Owner Name or ID',
name: 'owner',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCaseOwners',
},
default: '',
description:
'The owner of the case. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Parent ID',
name: 'ParentId',
type: 'string',
default: '',
description: 'The ID of the parent case in the hierarchy. The label is Parent Case.',
},
{
displayName: 'Priority Name or ID',
name: 'priority',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCasePriorities',
},
default: '',
description:
'The importance or urgency of the case, such as High, Medium, or Low. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Reason Name or ID',
name: 'reason',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCaseReasons',
},
default: '',
description:
'The reason why the case was created, such as Instructions not clear, or User didnt attend training. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Record Type Name or ID',
name: 'recordTypeId',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getRecordTypes',
},
default: '',
},
{
displayName: 'Status Name or ID',
name: 'status',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCaseStatuses',
},
default: '',
description:
'The status of the case, such as “New,” “Closed,” or “Escalated.” This field directly controls the IsClosed flag. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Subject',
name: 'subject',
type: 'string',
default: '',
description: 'The subject of the case. Limit: 255 characters.',
},
{
displayName: 'Supplied Company',
name: 'suppliedCompany',
type: 'string',
default: '',
description:
"The company name that was entered when the case was created. This field can't be updated after the case has been created..",
},
{
displayName: 'Supplied Email',
name: 'suppliedEmail',
type: 'string',
default: '',
description:
"The email address that was entered when the case was created. This field can't be updated after the case has been created.",
},
{
displayName: 'Supplied Name',
name: 'suppliedName',
type: 'string',
default: '',
description:
"The name that was entered when the case was created. This field can't be updated after the case has been created.",
},
{
displayName: 'Supplied Phone',
name: 'suppliedPhone',
type: 'string',
default: '',
description:
"The phone number that was entered when the case was created. This field can't be updated after the case has been created.",
},
],
},
/* -------------------------------------------------------------------------- */
/* case:update */
/* -------------------------------------------------------------------------- */
{
displayName: 'Case ID',
name: 'caseId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['case'],
operation: ['update'],
},
},
description: 'ID of case that needs to be fetched',
},
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['case'],
operation: ['update'],
},
},
options: [
{
displayName: 'Account ID',
name: 'accountId',
type: 'string',
default: '',
description: 'ID of the account associated with this case',
},
{
displayName: 'Contact ID',
name: 'contactId',
type: 'string',
default: '',
description: 'ID of the associated Contact',
},
{
displayName: 'Custom Fields',
name: 'customFieldsUi',
placeholder: 'Add Custom Field',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
description: 'Filter by custom fields',
default: {},
options: [
{
name: 'customFieldsValues',
displayName: 'Custom Field',
values: [
{
displayName: 'Field Name or ID',
name: 'fieldId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCustomFields',
},
default: '',
description:
'The ID of the field to add custom field to. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'The value to set on custom field',
},
],
},
],
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
description: 'A text description of the case. Limit: 32 KB.',
},
{
displayName: 'Is Escalated',
name: 'isEscalated',
type: 'boolean',
default: false,
description: 'Whether the case has been escalated (true) or not',
},
{
displayName: 'Origin Name or ID',
name: 'origin',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCaseOrigins',
},
default: '',
description:
'The source of the case, such as Email, Phone, or Web. Label is Case Origin. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Owner Name or ID',
name: 'owner',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCaseOwners',
},
default: '',
description:
'The owner of the case. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Parent ID',
name: 'ParentId',
type: 'string',
default: '',
description: 'The ID of the parent case in the hierarchy. The label is Parent Case.',
},
{
displayName: 'Priority Name or ID',
name: 'priority',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCasePriorities',
},
default: '',
description:
'The importance or urgency of the case, such as High, Medium, or Low. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Reason Name or ID',
name: 'reason',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCaseReasons',
},
default: '',
description:
'The reason why the case was created, such as Instructions not clear, or User didnt attend training. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Record Type Name or ID',
name: 'recordTypeId',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getRecordTypes',
},
default: '',
},
{
displayName: 'Status Name or ID',
name: 'status',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCaseStatuses',
},
default: '',
description:
'The status of the case, such as “New,” “Closed,” or “Escalated.” This field directly controls the IsClosed flag. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Subject',
name: 'subject',
type: 'string',
default: '',
description: 'The subject of the case. Limit: 255 characters.',
},
{
displayName: 'Supplied Company',
name: 'suppliedCompany',
type: 'string',
default: '',
description:
"The company name that was entered when the case was created. This field can't be updated after the case has been created..",
},
{
displayName: 'Supplied Email',
name: 'suppliedEmail',
type: 'string',
default: '',
description:
"The email address that was entered when the case was created. This field can't be updated after the case has been created.",
},
{
displayName: 'Supplied Name',
name: 'suppliedName',
type: 'string',
default: '',
description:
"The name that was entered when the case was created. This field can't be updated after the case has been created.",
},
{
displayName: 'Supplied Phone',
name: 'suppliedPhone',
type: 'string',
default: '',
description:
"The phone number that was entered when the case was created. This field can't be updated after the case has been created.",
},
{
displayName: 'Type Name or ID',
name: 'type',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCaseTypes',
},
default: '',
description:
'The type of case. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
],
},
/* -------------------------------------------------------------------------- */
/* case:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Case ID',
name: 'caseId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['case'],
operation: ['get'],
},
},
description: 'ID of case that needs to be fetched',
},
/* -------------------------------------------------------------------------- */
/* case:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'Case ID',
name: 'caseId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['case'],
operation: ['delete'],
},
},
description: 'ID of case that needs to be fetched',
},
/* -------------------------------------------------------------------------- */
/* case:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: ['case'],
operation: ['getAll'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: ['case'],
operation: ['getAll'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
description: 'Max number of results to return',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['case'],
operation: ['getAll'],
},
},
options: [
{
displayName: 'Conditions',
name: 'conditionsUi',
placeholder: 'Add Condition',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
description: 'The condition to set',
default: {},
options: [
{
name: 'conditionValues',
displayName: 'Condition',
values: [
{
displayName: 'Field Name or ID',
name: 'field',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCaseFields',
},
default: '',
description:
'For date, number, or boolean, please use expressions. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
// eslint-disable-next-line n8n-nodes-base/node-param-operation-without-no-data-expression
{
displayName: 'Operation',
name: 'operation',
type: 'options',
options: [
{
name: '<',
value: '<',
},
{
name: '<=',
value: '<=',
},
{
name: '=',
value: 'equal',
},
{
name: '>',
value: '>',
},
{
name: '>=',
value: '>=',
},
],
default: 'equal',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
},
],
},
],
},
{
displayName: 'Fields',
name: 'fields',
type: 'string',
default: '',
description: 'Fields to include separated by ,',
},
],
},
/* -------------------------------------------------------------------------- */
/* case:addComment */
/* -------------------------------------------------------------------------- */
{
displayName: 'Case ID',
name: 'caseId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['case'],
operation: ['addComment'],
},
},
description: 'ID of case that needs to be fetched',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['case'],
operation: ['addComment'],
},
},
options: [
{
displayName: 'Comment Body',
name: 'commentBody',
type: 'string',
default: '',
description:
'Text of the CaseComment. The maximum size of the comment body is 4,000 bytes. Label is Body.',
},
{
displayName: 'Is Published',
name: 'isPublished',
type: 'boolean',
default: false,
description:
'Whether the CaseComment is visible to customers in the Self-Service portal (true) or not (false)',
},
],
},
];
@@ -0,0 +1,25 @@
export interface ICase {
Type?: string;
Origin?: string;
Reason?: string;
Status?: string;
OwnerId?: string;
Subject?: string;
ParentId?: string;
Priority?: string;
AccountId?: string;
ContactId?: string;
Description?: string;
IsEscalated?: boolean;
SuppliedName?: string;
SuppliedEmail?: string;
SuppliedPhone?: string;
SuppliedCompany?: string;
RecordTypeId?: string;
}
export interface ICaseComment {
CommentBody?: string;
ParentId?: string;
IsPublished?: boolean;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,34 @@
export interface IContact {
[key: string]: any;
LastName?: string;
Fax?: string;
Email?: string;
Phone?: string;
Title?: string;
Jigsaw?: string;
OwnerId?: string;
AccountId?: string;
Birthdate?: string;
FirstName?: string;
HomePhone?: string;
OtherCity?: string;
Department?: string;
LeadSource?: string;
OtherPhone?: string;
OtherState?: string;
Salutation?: string;
Description?: string;
MailingCity?: string;
MobilePhone?: string;
OtherStreet?: string;
MailingState?: string;
OtherCountry?: string;
AssistantName?: string;
MailingStreet?: string;
AssistantPhone?: string;
MailingCountry?: string;
OtherPostalCode?: string;
MailingPostalCode?: string;
EmailBouncedDate?: string;
EmailBouncedReason?: string;
}
@@ -0,0 +1,509 @@
import type { INodeProperties } from 'n8n-workflow';
export const customObjectOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['customObject'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a custom object record',
action: 'Create a custom object',
},
{
name: 'Create or Update',
value: 'upsert',
description: 'Create a new record, or update the current one if it already exists (upsert)',
action: 'Create or update a custom object',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a custom object record',
action: 'Delete a custom object',
},
{
name: 'Get',
value: 'get',
description: 'Get a custom object record',
action: 'Get a custom object',
},
{
name: 'Get Many',
value: 'getAll',
description: 'Get many custom object records',
action: 'Get many custom objects',
},
{
name: 'Update',
value: 'update',
description: 'Update a custom object record',
action: 'Update a custom object',
},
],
default: 'create',
},
];
export const customObjectFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* customObject:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'Custom Object Name or ID',
name: 'customObject',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCustomObjects',
},
required: true,
default: '',
displayOptions: {
show: {
resource: ['customObject'],
operation: ['create', 'upsert'],
},
},
description:
'Name of the custom object. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options
displayName: 'Match Against',
name: 'externalId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getExternalIdFields',
loadOptionsDependsOn: ['customObject'],
},
required: true,
default: '',
displayOptions: {
show: {
resource: ['customObject'],
operation: ['upsert'],
},
},
description:
'The field to check to see if the object already exists. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Value to Match',
name: 'externalIdValue',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['customObject'],
operation: ['upsert'],
},
},
description:
"If this value exists in the 'match against' field, update the object. Otherwise create a new one.",
},
{
displayName: 'Fields',
name: 'customFieldsUi',
placeholder: 'Add Field',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
displayOptions: {
show: {
resource: ['customObject'],
operation: ['create', 'upsert'],
},
},
description: 'Filter by custom fields',
default: {},
options: [
{
name: 'customFieldsValues',
displayName: 'Custom Field',
values: [
{
displayName: 'Field Name or ID',
name: 'fieldId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCustomObjectFields',
loadOptionsDependsOn: ['customObject'],
},
default: '',
description:
'The ID of the field. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'The value to set on custom field',
},
],
},
],
},
/* -------------------------------------------------------------------------- */
/* customObject:update */
/* -------------------------------------------------------------------------- */
{
displayName: 'Custom Object Name or ID',
name: 'customObject',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCustomObjects',
},
required: true,
default: '',
displayOptions: {
show: {
resource: ['customObject'],
operation: ['update'],
},
},
description:
'Name of the custom object. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Record ID',
name: 'recordId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['customObject'],
operation: ['update'],
},
},
description: 'Record ID to be updated',
},
{
displayName: 'Fields',
name: 'customFieldsUi',
placeholder: 'Add Field',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
description: 'Filter by custom fields',
default: {},
displayOptions: {
show: {
resource: ['customObject'],
operation: ['update'],
},
},
options: [
{
name: 'customFieldsValues',
displayName: 'Custom Field',
values: [
{
displayName: 'Field Name or ID',
name: 'fieldId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCustomObjectFields',
loadOptionsDependsOn: ['customObject'],
},
default: '',
description:
'The ID of the field. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'The value to set on custom field',
},
],
},
],
},
/* -------------------------------------------------------------------------- */
/* customObject:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Custom Object Name or ID',
name: 'customObject',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCustomObjects',
},
required: true,
default: '',
displayOptions: {
show: {
resource: ['customObject'],
operation: ['get'],
},
},
description:
'Name of the custom object. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Record ID',
name: 'recordId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['customObject'],
operation: ['get'],
},
},
description: 'Record ID to be retrieved',
},
/* -------------------------------------------------------------------------- */
/* customObject:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'Custom Object Name or ID',
name: 'customObject',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCustomObjects',
},
required: true,
default: '',
displayOptions: {
show: {
resource: ['customObject'],
operation: ['delete'],
},
},
description:
'Name of the custom object. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Record ID',
name: 'recordId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['customObject'],
operation: ['delete'],
},
},
description: 'Record ID to be deleted',
},
/* -------------------------------------------------------------------------- */
/* customObject:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Custom Object Name or ID',
name: 'customObject',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCustomObjects',
},
required: true,
default: '',
displayOptions: {
show: {
resource: ['customObject'],
operation: ['getAll'],
},
},
description:
'Name of the custom object. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: ['customObject'],
operation: ['getAll'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: ['customObject'],
operation: ['getAll'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
description: 'Max number of results to return',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['customObject'],
operation: ['getAll'],
},
},
options: [
{
displayName: 'Conditions',
name: 'conditionsUi',
placeholder: 'Add Condition',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
description: 'The condition to set',
default: {},
options: [
{
name: 'conditionValues',
displayName: 'Condition',
values: [
{
displayName: 'Field Name or ID',
name: 'field',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCustomObjectFields',
loadOptionsDependsOn: ['customObject'],
},
default: '',
description:
'For date, number, or boolean, please use expressions. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
// eslint-disable-next-line n8n-nodes-base/node-param-operation-without-no-data-expression
{
displayName: 'Operation',
name: 'operation',
type: 'options',
options: [
{
name: '<',
value: '<',
},
{
name: '<=',
value: '<=',
},
{
name: '=',
value: 'equal',
},
{
name: '>',
value: '>',
},
{
name: '>=',
value: '>=',
},
],
default: 'equal',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
},
],
},
],
},
{
displayName: 'Field Names or IDs',
name: 'fields',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getCustomObjectFields',
loadOptionsDependsOn: ['customObject'],
},
default: [],
description:
'Fields to include separated by commas. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
],
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
displayOptions: {
show: {
operation: ['create', 'upsert'],
resource: ['customObject'],
},
},
default: {},
placeholder: 'Add Field',
options: [
{
displayName: 'Record Type Name or ID',
name: 'recordTypeId',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getRecordTypes',
loadOptionsDependsOn: ['customObject'],
},
default: '',
},
],
},
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
displayOptions: {
show: {
operation: ['update'],
resource: ['customObject'],
},
},
default: {},
placeholder: 'Add Field',
options: [
{
displayName: 'Record Type Name or ID',
name: 'recordTypeId',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getRecordTypes',
loadOptionsDependsOn: ['customObject'],
},
default: '',
},
],
},
];
@@ -0,0 +1,101 @@
import type { INodeProperties } from 'n8n-workflow';
export const documentOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['document'],
},
},
options: [
{
name: 'Upload',
value: 'upload',
description: 'Upload a document',
action: 'Upload a document',
},
],
default: 'upload',
},
];
export const documentFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* document:upload */
/* -------------------------------------------------------------------------- */
{
displayName: 'Title',
name: 'title',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
resource: ['document'],
operation: ['upload'],
},
},
description: 'Name of the file',
},
{
displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
required: true,
displayOptions: {
show: {
resource: ['document'],
operation: ['upload'],
},
},
placeholder: '',
hint: 'The name of the input binary field containing the file to be uploaded',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['document'],
operation: ['upload'],
},
},
options: [
{
displayName: 'File Extension',
name: 'fileExtension',
type: 'string',
default: '',
placeholder: 'pdf',
description:
'File extension to use. If none is set, the value from the binary data will be used.',
},
{
displayName: 'Link To Object ID',
name: 'linkToObjectId',
type: 'string',
default: '',
description: 'ID of the object you want to link this document to',
},
{
displayName: 'Owner Name or ID',
name: 'ownerId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: '',
description:
'ID of the owner of this document. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
],
},
];
@@ -0,0 +1,152 @@
import type { INodeProperties } from 'n8n-workflow';
export const flowOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['flow'],
},
},
options: [
{
name: 'Get Many',
value: 'getAll',
description: 'Get many flows',
action: 'Get many flows',
},
{
name: 'Invoke',
value: 'invoke',
description: 'Invoke a flow',
action: 'Invoke a flow',
},
],
default: 'invoke',
},
];
export const flowFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* flow:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: ['getAll'],
resource: ['flow'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
operation: ['getAll'],
resource: ['flow'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 500,
},
default: 50,
description: 'Max number of results to return',
},
/* -------------------------------------------------------------------------- */
/* flow:invoke */
/* -------------------------------------------------------------------------- */
{
displayName: 'API Name',
name: 'apiName',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
resource: ['flow'],
operation: ['invoke'],
},
},
description: 'Required. API name of the flow.',
},
{
displayName: 'JSON Parameters',
name: 'jsonParameters',
type: 'boolean',
default: false,
displayOptions: {
show: {
resource: ['flow'],
operation: ['invoke'],
},
},
description: 'Whether the input variables should be set via the value-key pair UI or JSON/RAW',
},
{
displayName: 'Variables',
name: 'variablesJson',
type: 'json',
displayOptions: {
show: {
resource: ['flow'],
operation: ['invoke'],
jsonParameters: [true],
},
},
default: '',
description: 'Input variables as JSON object',
},
{
displayName: 'Variables',
name: 'variablesUi',
placeholder: 'Add Variable',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
displayOptions: {
show: {
resource: ['flow'],
operation: ['invoke'],
jsonParameters: [false],
},
},
description: 'The input variable to send',
default: {},
options: [
{
displayName: 'Variable',
name: 'variablesValues',
values: [
{
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
description: 'Name of the input variable',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'Value of the input variable',
},
],
},
],
},
];
@@ -0,0 +1,287 @@
import jwt from 'jsonwebtoken';
import moment from 'moment-timezone';
import { DateTime } from 'luxon';
import type {
IExecuteFunctions,
ILoadOptionsFunctions,
IDataObject,
INodePropertyOptions,
JsonObject,
IHttpRequestMethods,
IRequestOptions,
IPollFunctions,
} from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow';
function getOptions(
this: IExecuteFunctions | ILoadOptionsFunctions | IPollFunctions,
method: IHttpRequestMethods,
endpoint: string,
body: any,
qs: IDataObject,
instanceUrl: string,
): IRequestOptions {
const options: IRequestOptions = {
headers: {
'Content-Type': 'application/json',
},
method,
body,
qs,
uri: `${instanceUrl}/services/data/v59.0${endpoint}`,
json: true,
};
if (!Object.keys(options.body as IDataObject).length) {
delete options.body;
}
return options;
}
async function getAccessToken(
this: IExecuteFunctions | ILoadOptionsFunctions | IPollFunctions,
credentials: IDataObject,
): Promise<IDataObject> {
const now = moment().unix();
const authUrl =
credentials.environment === 'sandbox'
? 'https://test.salesforce.com'
: 'https://login.salesforce.com';
const signature = jwt.sign(
{
iss: credentials.clientId as string,
sub: credentials.username as string,
aud: authUrl,
exp: now + 3 * 60,
},
credentials.privateKey as string,
{
algorithm: 'RS256',
header: {
alg: 'RS256',
},
},
);
const options: IRequestOptions = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
method: 'POST',
form: {
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
assertion: signature,
},
uri: `${authUrl}/services/oauth2/token`,
json: true,
};
return await this.helpers.request(options);
}
export async function salesforceApiRequest(
this: IExecuteFunctions | ILoadOptionsFunctions | IPollFunctions,
method: IHttpRequestMethods,
endpoint: string,
body: any = {},
qs: IDataObject = {},
uri?: string,
option: IDataObject = {},
): Promise<any> {
const authenticationMethod = this.getNodeParameter('authentication', 0, 'oAuth2') as string;
try {
if (authenticationMethod === 'jwt') {
// https://help.salesforce.com/articleView?id=remoteaccess_oauth_jwt_flow.htm&type=5
const credentialsType = 'salesforceJwtApi';
const credentials = await this.getCredentials(credentialsType);
const response = await getAccessToken.call(this, credentials);
const { instance_url, access_token } = response;
const options = getOptions.call(
this,
method,
uri || endpoint,
body,
qs,
instance_url as string,
);
this.logger.debug(
`Authentication for "Salesforce" node is using "jwt". Invoking URI ${options.uri}`,
);
options.headers!.Authorization = `Bearer ${access_token}`;
Object.assign(options, option);
return await this.helpers.request(options);
} else {
// https://help.salesforce.com/articleView?id=remoteaccess_oauth_web_server_flow.htm&type=5
const credentialsType = 'salesforceOAuth2Api';
const credentials = await this.getCredentials<{
oauthTokenData: { instance_url: string };
}>(credentialsType);
const options = getOptions.call(
this,
method,
uri || endpoint,
body,
qs,
credentials.oauthTokenData.instance_url,
);
this.logger.debug(
`Authentication for "Salesforce" node is using "OAuth2". Invoking URI ${options.uri}`,
);
Object.assign(options, option);
return await this.helpers.requestOAuth2.call(this, credentialsType, options);
}
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
}
export async function salesforceApiRequestAllItems(
this: IExecuteFunctions | ILoadOptionsFunctions | IPollFunctions,
propertyName: string,
method: IHttpRequestMethods,
endpoint: string,
body: any = {},
query: IDataObject = {},
): Promise<any> {
const returnData: IDataObject[] = [];
let responseData;
let uri: string | undefined;
do {
responseData = await salesforceApiRequest.call(this, method, endpoint, body, query, uri);
uri = `${endpoint}/${responseData.nextRecordsUrl?.split('/')?.pop()}`;
returnData.push.apply(returnData, responseData[propertyName] as IDataObject[]);
} while (responseData.nextRecordsUrl !== undefined && responseData.nextRecordsUrl !== null);
return returnData;
}
/**
* Sorts the given options alphabetically
*
*/
export function sortOptions(options: INodePropertyOptions[]): void {
options.sort((a, b) => {
if (a.name < b.name) {
return -1;
}
if (a.name > b.name) {
return 1;
}
return 0;
});
}
export function getValue(value: any) {
if (moment(value as string).isValid()) {
return value;
} else if (typeof value === 'string') {
return `'${value}'`;
} else {
return value;
}
}
export function getConditions(options: IDataObject) {
const conditions = (options.conditionsUi as IDataObject)?.conditionValues as IDataObject[];
let data = undefined;
if (Array.isArray(conditions) && conditions.length !== 0) {
data = conditions.map(
(condition: IDataObject) =>
`${condition.field} ${
condition.operation === 'equal' ? '=' : condition.operation
} ${getValue(condition.value)}`,
);
data = `WHERE ${data.join(' AND ')}`;
}
return data;
}
export function getDefaultFields(sobject: string) {
return (
{
Account: 'id,name,type',
Lead: 'id,company,firstname,lastname,street,postalCode,city,email,status',
Contact: 'id,firstname,lastname,email',
Opportunity: 'id,accountId,amount,probability,type',
Case: 'id,accountId,contactId,priority,status,subject,type',
Task: 'id,subject,status,priority',
Attachment: 'id,name',
User: 'id,name,email',
} as IDataObject
)[sobject];
}
export function getQuery(options: IDataObject, sobject: string, returnAll: boolean, limit = 0) {
const fields: string[] = [];
if (options.fields) {
// options.fields is comma separated in standard Salesforce objects and array in custom Salesforce objects -- handle both cases
if (typeof options.fields === 'string') {
fields.push.apply(fields, options.fields.split(','));
} else {
fields.push.apply(fields, options.fields as string[]);
}
} else {
fields.push.apply(fields, ((getDefaultFields(sobject) as string) || 'id').split(','));
}
const conditions = getConditions(options);
let query = `SELECT ${fields.join(',')} FROM ${sobject} ${conditions ? conditions : ''}`;
if (!returnAll) {
query = `SELECT ${fields.join(',')} FROM ${sobject} ${
conditions ? conditions : ''
} LIMIT ${limit}`;
}
return query;
}
/**
* Calculates the polling start date with safety margin to account for Salesforce indexing delays
*/
export function getPollStartDate(lastTimeChecked: string | undefined): string {
if (!lastTimeChecked) {
return DateTime.now().toISO();
}
const safetyMarginMinutes = 15;
return DateTime.fromISO(lastTimeChecked).minus({ minutes: safetyMarginMinutes }).toISO();
}
/**
* Filters out already processed items and manages the processed IDs list
*/
export function filterAndManageProcessedItems(
responseData: IDataObject[],
processedIds: string[],
): { newItems: IDataObject[]; updatedProcessedIds: string[] } {
const processedIdsSet = new Set(processedIds);
const newItems: IDataObject[] = [];
const newItemIds: string[] = [];
for (const item of responseData) {
if (typeof item.Id !== 'string') continue;
const itemId = item.Id;
if (!processedIdsSet.has(itemId)) {
newItems.push(item);
newItemIds.push(itemId);
}
}
const remainingProcessedIds = Array.from(processedIdsSet);
const updatedProcessedIds = remainingProcessedIds.concat(newItemIds);
const MAX_IDS = 10000;
const trimmedProcessedIds = updatedProcessedIds.slice(-MAX_IDS);
return { newItems, updatedProcessedIds: trimmedProcessedIds };
}
@@ -0,0 +1,980 @@
import type { INodeProperties } from 'n8n-workflow';
export const leadOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['lead'],
},
},
options: [
{
name: 'Add Lead To Campaign',
value: 'addToCampaign',
description: 'Add lead to a campaign',
action: 'Add a lead to a campaign',
},
{
name: 'Add Note',
value: 'addNote',
description: 'Add note to a lead',
action: 'Add a note to a lead',
},
{
name: 'Create',
value: 'create',
description: 'Create a lead',
action: 'Create a lead',
},
{
name: 'Create or Update',
value: 'upsert',
description: 'Create a new lead, or update the current one if it already exists (upsert)',
action: 'Create or update a lead',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a lead',
action: 'Delete a lead',
},
{
name: 'Get',
value: 'get',
description: 'Get a lead',
action: 'Get a lead',
},
{
name: 'Get Many',
value: 'getAll',
description: 'Get many leads',
action: 'Get many leads',
},
{
name: 'Get Summary',
value: 'getSummary',
description: "Returns an overview of Lead's metadata",
action: 'Get a lead summary',
},
{
name: 'Update',
value: 'update',
description: 'Update a lead',
action: 'Update a lead',
},
],
default: 'create',
},
];
export const leadFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* lead:create */
/* -------------------------------------------------------------------------- */
{
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options
displayName: 'Match Against',
name: 'externalId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getExternalIdFields',
loadOptionsDependsOn: ['resource'],
},
required: true,
default: '',
displayOptions: {
show: {
resource: ['lead'],
operation: ['upsert'],
},
},
description:
'The field to check to see if the lead already exists. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Value to Match',
name: 'externalIdValue',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['lead'],
operation: ['upsert'],
},
},
description:
"If this value exists in the 'match against' field, update the lead. Otherwise create a new one.",
},
{
displayName: 'Company',
name: 'company',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['lead'],
operation: ['create', 'upsert'],
},
},
description:
'Company of the lead. If person account record types have been enabled, and if the value of Company is null, the lead converts to a person account.',
},
{
displayName: 'Last Name',
name: 'lastname',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['lead'],
operation: ['create', 'upsert'],
},
},
description: 'Required. Last name of the lead. Limited to 80 characters.',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['lead'],
operation: ['create', 'upsert'],
},
},
options: [
{
displayName: 'Annual Revenue',
name: 'annualRevenue',
type: 'number',
typeOptions: {
numberPrecision: 2,
},
default: '',
description: 'Annual revenue for the company of the lead',
},
{
displayName: 'City',
name: 'city',
type: 'string',
default: '',
description: 'City for the address of the lead',
},
{
displayName: 'Country',
name: 'country',
type: 'string',
default: '',
description: 'Country of the lead',
},
{
displayName: 'Custom Fields',
name: 'customFieldsUi',
placeholder: 'Add Custom Field',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
description: 'Filter by custom fields',
default: {},
options: [
{
name: 'customFieldsValues',
displayName: 'Custom Field',
values: [
{
displayName: 'Field Name or ID',
name: 'fieldId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCustomFields',
},
default: '',
description:
'The ID of the field to add custom field to. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'The value to set on custom field',
},
],
},
],
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
description: 'Description of the lead',
},
{
displayName: 'Email',
name: 'email',
type: 'string',
placeholder: 'name@email.com',
default: '',
description: 'Email address for the lead',
},
{
displayName: 'Fax',
name: 'fax',
type: 'number',
default: '',
description: 'Fax number of the lead',
},
{
displayName: 'First Name',
name: 'firstname',
type: 'string',
default: '',
description: 'First name of the lead. Limited to 40 characters.',
},
{
displayName: 'Has Opted Out of Email',
name: 'hasOptedOutOfEmail',
type: 'boolean',
default: false,
description:
'Whether the lead doesnt want to receive email from Salesforce (true) or does (false). Label is Email Opt Out.',
},
{
displayName: 'Has Opted Out of Fax',
name: 'hasOptedOutOfFax',
type: 'boolean',
default: false,
description:
'Whether the lead doesnt want to receive fax from Salesforce (true) or does (false). Label is Email Opt Out.',
},
{
displayName: 'Industry',
name: 'industry',
type: 'string',
default: '',
description: 'Website for the lead',
},
{
displayName: 'Is Unread By Owner',
name: 'IsUnreadByOwner',
type: 'boolean',
default: false,
description:
'Whether true, lead has been assigned, but not yet viewed. See Unread Leads for more information. Label is Unread By Owner.',
},
{
displayName: 'Jigsaw',
name: 'jigsaw',
type: 'string',
default: '',
description:
'References the ID of a contact in Data.com. If a lead has a value in this field, it means that a contact was imported as a lead from Data.com.',
},
{
displayName: 'Lead Source Name or ID',
name: 'leadSource',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getLeadSources',
},
default: '',
description:
'Source from which the lead was obtained. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Mobile Phone',
name: 'mobilePhone',
type: 'string',
default: '',
description: 'Contacts mobile phone number',
},
{
displayName: 'Number Of Employees',
name: 'numberOfEmployees',
type: 'number',
default: '',
description: 'Number of employees at the leads company. Label is Employees.',
},
{
displayName: 'Owner Name or ID',
name: 'owner',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getLeadOwners',
},
default: '',
description:
'The owner of the lead. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Phone',
name: 'phone',
type: 'string',
default: '',
description: 'Phone number for the lead',
},
{
displayName: 'Postal Code',
name: 'postalCode',
type: 'string',
default: '',
description: 'Postal code for the address of the lead. Label is Zip/Postal Code.',
},
{
displayName: 'Record Type Name or ID',
name: 'recordTypeId',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getRecordTypes',
},
default: '',
},
{
displayName: 'Rating',
name: 'rating',
type: 'string',
default: '',
description: 'Rating of the lead',
},
{
displayName: 'Salutation',
name: 'salutation',
type: 'string',
default: '',
description: 'Salutation for the lead',
},
{
displayName: 'State',
name: 'state',
type: 'string',
default: '',
description: 'State for the address of the lead',
},
{
displayName: 'Status Name or ID',
name: 'status',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getLeadStatuses',
},
default: '',
description:
'Status code for this converted lead. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Street',
name: 'street',
type: 'string',
default: '',
description: 'Street number and name for the address of the lead',
},
{
displayName: 'Title',
name: 'title',
type: 'string',
default: '',
description: 'Title for the lead, for example CFO or CEO',
},
{
displayName: 'Website',
name: 'website',
type: 'string',
default: '',
description: 'Website for the lead',
},
],
},
/* -------------------------------------------------------------------------- */
/* lead:update */
/* -------------------------------------------------------------------------- */
{
displayName: 'Lead ID',
name: 'leadId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['lead'],
operation: ['update'],
},
},
description: 'ID of Lead that needs to be fetched',
},
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['lead'],
operation: ['update'],
},
},
options: [
{
displayName: 'Annual Revenue',
name: 'annualRevenue',
type: 'number',
typeOptions: {
numberPrecision: 2,
},
default: '',
description: 'Annual revenue for the company of the lead',
},
{
displayName: 'City',
name: 'city',
type: 'string',
default: '',
description: 'City for the address of the lead',
},
{
displayName: 'Company',
name: 'company',
type: 'string',
default: '',
description:
'Company of the lead. If person account record types have been enabled, and if the value of Company is null, the lead converts to a person account.',
},
{
displayName: 'Country',
name: 'country',
type: 'string',
default: '',
description: 'Country of the lead',
},
{
displayName: 'Custom Fields',
name: 'customFieldsUi',
placeholder: 'Add Custom Field',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
description: 'Filter by custom fields',
default: {},
options: [
{
name: 'customFieldsValues',
displayName: 'Custom Field',
values: [
{
displayName: 'Field Name or ID',
name: 'fieldId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCustomFields',
},
default: '',
description:
'The ID of the field to add custom field to. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'The value to set on custom field',
},
],
},
],
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
description: 'Description of the lead',
},
{
displayName: 'Email',
name: 'email',
type: 'string',
placeholder: 'name@email.com',
default: '',
description: 'Email address for the lead',
},
{
displayName: 'Fax',
name: 'fax',
type: 'number',
default: '',
description: 'Fax Number of the lead',
},
{
displayName: 'First Name',
name: 'firstname',
type: 'string',
default: '',
description: 'First name of the lead. Limited to 40 characters.',
},
{
displayName: 'Has Opted Out of Email',
name: 'hasOptedOutOfEmail',
type: 'boolean',
default: false,
description:
'Whether the lead doesnt want to receive email from Salesforce (true) or does (false). Label is Email Opt Out.',
},
{
displayName: 'Has Opted Out of Fax',
name: 'HasOptedOutOfFax',
type: 'boolean',
default: false,
description:
'Whether the lead doesnt want to receive fax from Salesforce (true) or does (false). Label is Fax Opt Out.',
},
{
displayName: 'Industry',
name: 'industry',
type: 'string',
default: '',
description: 'Website for the lead',
},
{
displayName: 'Is Unread By Owner',
name: 'IsUnreadByOwner',
type: 'boolean',
default: false,
description:
'Whether true, lead has been assigned, but not yet viewed. See Unread Leads for more information. Label is Unread By Owner.',
},
{
displayName: 'Jigsaw',
name: 'jigsaw',
type: 'string',
default: '',
description:
'References the ID of a contact in Data.com. If a lead has a value in this field, it means that a contact was imported as a lead from Data.com.',
},
{
displayName: 'Last Name',
name: 'lastname',
type: 'string',
default: '',
description: 'Required. Last name of the lead. Limited to 80 characters.',
},
{
displayName: 'Lead Source Name or ID',
name: 'leadSource',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getLeadSources',
},
default: '',
description:
'Source from which the lead was obtained. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Mobile Phone',
name: 'mobilePhone',
type: 'string',
default: '',
description: 'Contacts mobile phone number',
},
{
displayName: 'Number Of Employees',
name: 'numberOfEmployees',
type: 'number',
default: '',
description: 'Number of employees at the leads company. Label is Employees.',
},
{
displayName: 'Owner Name or ID',
name: 'owner',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getLeadOwners',
},
default: '',
description:
'The owner of the lead. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Postal Code',
name: 'postalCode',
type: 'string',
default: '',
description: 'Postal code for the address of the lead. Label is Zip/Postal Code.',
},
{
displayName: 'Phone',
name: 'phone',
type: 'string',
default: '',
description: 'Phone number for the lead',
},
{
displayName: 'Record Type Name or ID',
name: 'recordTypeId',
type: 'options',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'getRecordTypes',
},
default: '',
},
{
displayName: 'Rating',
name: 'rating',
type: 'string',
default: '',
description: 'Rating of the lead',
},
{
displayName: 'Salutation',
name: 'salutation',
type: 'string',
default: '',
description: 'Salutation for the lead',
},
{
displayName: 'State',
name: 'state',
type: 'string',
default: '',
description: 'State for the address of the lead',
},
{
displayName: 'Status Name or ID',
name: 'status',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getLeadStatuses',
},
default: '',
description:
'Status code for this converted lead. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Street',
name: 'street',
type: 'string',
default: '',
description: 'Street number and name for the address of the lead',
},
{
displayName: 'Title',
name: 'title',
type: 'string',
default: '',
description: 'Title for the lead, for example CFO or CEO',
},
{
displayName: 'Website',
name: 'website',
type: 'string',
default: '',
description: 'Website for the lead',
},
],
},
/* -------------------------------------------------------------------------- */
/* lead:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Lead ID',
name: 'leadId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['lead'],
operation: ['get'],
},
},
description: 'ID of Lead that needs to be fetched',
},
/* -------------------------------------------------------------------------- */
/* lead:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'Lead ID',
name: 'leadId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['lead'],
operation: ['delete'],
},
},
description: 'ID of Lead that needs to be fetched',
},
/* -------------------------------------------------------------------------- */
/* lead:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: ['lead'],
operation: ['getAll'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: ['lead'],
operation: ['getAll'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
description: 'Max number of results to return',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['lead'],
operation: ['getAll'],
},
},
options: [
{
displayName: 'Conditions',
name: 'conditionsUi',
placeholder: 'Add Condition',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
description: 'The condition to set',
default: {},
options: [
{
name: 'conditionValues',
displayName: 'Condition',
values: [
{
displayName: 'Field Name or ID',
name: 'field',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getLeadFields',
},
default: '',
description:
'For date, number, or boolean, please use expressions. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
// eslint-disable-next-line n8n-nodes-base/node-param-operation-without-no-data-expression
{
displayName: 'Operation',
name: 'operation',
type: 'options',
options: [
{
name: '<',
value: '<',
},
{
name: '<=',
value: '<=',
},
{
name: '=',
value: 'equal',
},
{
name: '>',
value: '>',
},
{
name: '>=',
value: '>=',
},
],
default: 'equal',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
},
],
},
],
},
{
displayName: 'Fields',
name: 'fields',
type: 'string',
default: '',
description: 'Fields to include separated by ,',
},
],
},
/* -------------------------------------------------------------------------- */
/* contact:addToCampaign */
/* -------------------------------------------------------------------------- */
{
displayName: 'Lead ID',
name: 'leadId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['lead'],
operation: ['addToCampaign'],
},
},
description: 'ID of contact that needs to be fetched',
},
{
displayName: 'Campaign Name or ID',
name: 'campaignId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCampaigns',
},
required: true,
default: '',
displayOptions: {
show: {
resource: ['lead'],
operation: ['addToCampaign'],
},
},
description:
'ID of the campaign that needs to be fetched. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['lead'],
operation: ['addToCampaign'],
},
},
options: [
{
displayName: 'Status',
name: 'status',
type: 'string',
default: '',
description: 'Controls the HasResponded flag on this object',
},
],
},
/* -------------------------------------------------------------------------- */
/* lead:addNote */
/* -------------------------------------------------------------------------- */
{
displayName: 'Lead ID',
name: 'leadId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['lead'],
operation: ['addNote'],
},
},
description: 'ID of lead that needs to be fetched',
},
{
displayName: 'Title',
name: 'title',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['lead'],
operation: ['addNote'],
},
},
description: 'Title of the note',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['lead'],
operation: ['addNote'],
},
},
options: [
{
displayName: 'Body',
name: 'body',
type: 'string',
default: '',
description: 'Body of the note. Limited to 32 KB.',
},
{
displayName: 'Is Private',
name: 'isPrivate',
type: 'boolean',
default: false,
description:
'Whether true, only the note owner or a user with the “Modify All Data” permission can view the note or query it via the API',
},
{
displayName: 'Owner Name or ID',
name: 'owner',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: '',
description:
'ID of the user who owns the note. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
],
},
];
@@ -0,0 +1,30 @@
export interface ILead {
[key: string]: any;
Company?: string;
LastName?: string;
Email?: string;
Fax?: number;
City?: string;
Phone?: string;
State?: string;
Title?: string;
Jigsaw?: string;
Rating?: string;
Status?: string;
Street?: string;
Country?: string;
OwnerId?: string;
Website?: string;
Industry?: string;
FirstName?: string;
LeadSource?: string;
PostalCode?: string;
Salutation?: string;
Description?: string;
AnnualRevenue?: number;
IsUnreadByOwner?: boolean;
NumberOfEmployees?: number;
MobilePhone?: string;
HasOptedOutOfEmail?: boolean;
HasOptedOutOfFax?: boolean;
}
@@ -0,0 +1,7 @@
export interface INote {
Title?: string;
ParentId?: string;
Body?: string;
OwnerId?: string;
IsPrivate?: boolean;
}
@@ -0,0 +1,768 @@
import type { INodeProperties } from 'n8n-workflow';
export const opportunityOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['opportunity'],
},
},
options: [
{
name: 'Add Note',
value: 'addNote',
description: 'Add note to an opportunity',
action: 'Add a note to an opportunity',
},
{
name: 'Create',
value: 'create',
description: 'Create an opportunity',
action: 'Create an opportunity',
},
{
name: 'Create or Update',
value: 'upsert',
description:
'Create a new opportunity, or update the current one if it already exists (upsert)',
action: 'Create or update an opportunity',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete an opportunity',
action: 'Delete an opportunity',
},
{
name: 'Get',
value: 'get',
description: 'Get an opportunity',
action: 'Get an opportunity',
},
{
name: 'Get Many',
value: 'getAll',
description: 'Get many opportunities',
action: 'Get many opportunities',
},
{
name: 'Get Summary',
value: 'getSummary',
description: "Returns an overview of opportunity's metadata",
action: 'Get an opportunity summary',
},
{
name: 'Update',
value: 'update',
description: 'Update an opportunity',
action: 'Update an opportunity',
},
],
default: 'create',
},
];
export const opportunityFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* opportunity:create */
/* -------------------------------------------------------------------------- */
{
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options
displayName: 'Match Against',
name: 'externalId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getExternalIdFields',
loadOptionsDependsOn: ['resource'],
},
required: true,
default: '',
displayOptions: {
show: {
resource: ['opportunity'],
operation: ['upsert'],
},
},
description:
'The field to check to see if the opportunity already exists. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Value to Match',
name: 'externalIdValue',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['opportunity'],
operation: ['upsert'],
},
},
description:
"If this value exists in the 'match against' field, update the opportunity. Otherwise create a new one.",
},
{
displayName: 'Name',
name: 'name',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['opportunity'],
operation: ['create', 'upsert'],
},
},
description: 'Required. Last name of the opportunity. Limited to 80 characters.',
},
{
displayName: 'Close Date',
name: 'closeDate',
type: 'dateTime',
required: true,
default: '',
displayOptions: {
show: {
resource: ['opportunity'],
operation: ['create', 'upsert'],
},
},
description: 'Required. Date when the opportunity is expected to close.',
},
{
displayName: 'Stage Name or ID',
name: 'stageName',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getStages',
},
required: true,
default: '',
displayOptions: {
show: {
resource: ['opportunity'],
operation: ['create', 'upsert'],
},
},
description:
'Required. Date when the opportunity is expected to close. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['opportunity'],
operation: ['create', 'upsert'],
},
},
options: [
{
displayName: 'Account Name or ID',
name: 'accountId',
type: 'options',
default: '',
typeOptions: {
loadOptionsMethod: 'getAccounts',
},
description:
'ID of the account associated with this opportunity. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Amount',
name: 'amount',
type: 'number',
typeOptions: {
numberPrecision: 2,
},
default: '',
description: 'Estimated total sale amount',
},
{
displayName: 'Campaign Name or ID',
name: 'campaignId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCampaigns',
},
default: '',
description:
'ID of the campaign that needs to be fetched. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Custom Fields',
name: 'customFieldsUi',
placeholder: 'Add Custom Field',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
description: 'Filter by custom fields',
default: {},
options: [
{
name: 'customFieldsValues',
displayName: 'Custom Field',
values: [
{
displayName: 'Field Name or ID',
name: 'fieldId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCustomFields',
},
default: '',
description:
'The ID of the field to add custom field to. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'The value to set on custom field',
},
],
},
],
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
description:
'A description of the opportunity. Label is Contact Description. Limit: 32 KB.',
},
{
displayName: 'Forecast Category Name',
name: 'forecastCategoryName',
type: 'string',
default: '',
description: 'It is implied, but not directly controlled, by the StageName field',
},
{
displayName: 'Lead Source Name or ID',
name: 'leadSource',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getLeadSources',
},
default: '',
description:
'Source from which the lead was obtained. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Next Step',
name: 'nextStep',
type: 'string',
default: '',
description: 'Description of next task in closing opportunity. Limit: 255 characters.',
},
{
displayName: 'Owner Name or ID',
name: 'owner',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: '',
description:
'The owner of the opportunity. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Phone',
name: 'phone',
type: 'string',
default: '',
description: 'Phone number for the opportunity',
},
{
displayName: 'Pricebook2 ID',
name: 'pricebook2Id',
type: 'string',
default: '',
description: 'ID of a related Pricebook2 object',
},
{
displayName: 'Probability',
name: 'probability',
type: 'number',
typeOptions: {
numberPrecision: 1,
},
default: '',
description: 'Percentage of estimated confidence in closing the opportunity',
},
{
displayName: 'Type',
name: 'type',
type: 'options',
default: '',
options: [
{
name: 'Business',
value: 'Business',
},
{
name: 'New Business',
value: 'New Business',
},
],
description:
'Type of opportunity. For example, Existing Business or New Business. Label is Opportunity Type.',
},
],
},
/* -------------------------------------------------------------------------- */
/* opportunity:update */
/* -------------------------------------------------------------------------- */
{
displayName: 'Opportunity ID',
name: 'opportunityId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['opportunity'],
operation: ['update'],
},
},
description: 'ID of opportunity that needs to be fetched',
},
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['opportunity'],
operation: ['update'],
},
},
options: [
{
displayName: 'Account Name or ID',
name: 'accountId',
type: 'options',
default: '',
typeOptions: {
loadOptionsMethod: 'getAccounts',
},
description:
'ID of the account associated with this opportunity. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Amount',
name: 'amount',
type: 'number',
typeOptions: {
numberPrecision: 2,
},
default: '',
description: 'Estimated total sale amount',
},
{
displayName: 'Campaign Name or ID',
name: 'campaignId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCampaigns',
},
default: '',
description:
'ID of the campaign that needs to be fetched. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Close Date',
name: 'closeDate',
type: 'dateTime',
default: '',
description: 'Required. Date when the opportunity is expected to close.',
},
{
displayName: 'Custom Fields',
name: 'customFieldsUi',
placeholder: 'Add Custom Field',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
description: 'Filter by custom fields',
default: {},
options: [
{
name: 'customFieldsValues',
displayName: 'Custom Field',
values: [
{
displayName: 'Field Name or ID',
name: 'fieldId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCustomFields',
},
default: '',
description:
'The ID of the field to add custom field to. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'The value to set on custom field',
},
],
},
],
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
description:
'A description of the opportunity. Label is Contact Description. Limit: 32 KB.',
},
{
displayName: 'Forecast Category Name',
name: 'forecastCategoryName',
type: 'string',
default: '',
description: 'It is implied, but not directly controlled, by the StageName field',
},
{
displayName: 'Lead Source Name or ID',
name: 'leadSource',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getLeadSources',
},
default: '',
description:
'Source from which the lead was obtained. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
description: 'Required. Last name of the opportunity. Limited to 80 characters.',
},
{
displayName: 'Next Step',
name: 'nextStep',
type: 'string',
default: '',
description: 'Description of next task in closing opportunity. Limit: 255 characters.',
},
{
displayName: 'Owner Name or ID',
name: 'owner',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: '',
description:
'The owner of the opportunity. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Phone',
name: 'phone',
type: 'string',
default: '',
description: 'Phone number for the opportunity',
},
{
displayName: 'Pricebook2 ID',
name: 'pricebook2Id',
type: 'string',
default: '',
description: 'ID of a related Pricebook2 object',
},
{
displayName: 'Probability',
name: 'probability',
type: 'number',
typeOptions: {
numberPrecision: 1,
},
default: '',
description: 'Percentage of estimated confidence in closing the opportunity',
},
{
displayName: 'Stage Name or ID',
name: 'stageName',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getStages',
},
default: '',
description:
'Required. Date when the opportunity is expected to close. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Type',
name: 'type',
type: 'options',
default: '',
options: [
{
name: 'Business',
value: 'Business',
},
{
name: 'New Business',
value: 'New Business',
},
],
description:
'Type of opportunity. For example, Existing Business or New Business. Label is Opportunity Type.',
},
],
},
/* -------------------------------------------------------------------------- */
/* opportunity:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Opportunity ID',
name: 'opportunityId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['opportunity'],
operation: ['get'],
},
},
description: 'ID of opportunity that needs to be fetched',
},
/* -------------------------------------------------------------------------- */
/* opportunity:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'Opportunity ID',
name: 'opportunityId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['opportunity'],
operation: ['delete'],
},
},
description: 'ID of opportunity that needs to be fetched',
},
/* -------------------------------------------------------------------------- */
/* opportunity:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: ['opportunity'],
operation: ['getAll'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: ['opportunity'],
operation: ['getAll'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
description: 'Max number of results to return',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['opportunity'],
operation: ['getAll'],
},
},
options: [
{
displayName: 'Conditions',
name: 'conditionsUi',
placeholder: 'Add Condition',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
description: 'The condition to set',
default: {},
options: [
{
name: 'conditionValues',
displayName: 'Condition',
values: [
{
displayName: 'Field Name or ID',
name: 'field',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getOpportunityFields',
},
default: '',
description:
'For date, number, or boolean, please use expressions. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
// eslint-disable-next-line n8n-nodes-base/node-param-operation-without-no-data-expression
{
displayName: 'Operation',
name: 'operation',
type: 'options',
options: [
{
name: '<',
value: '<',
},
{
name: '<=',
value: '<=',
},
{
name: '=',
value: 'equal',
},
{
name: '>',
value: '>',
},
{
name: '>=',
value: '>=',
},
],
default: 'equal',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
},
],
},
],
},
{
displayName: 'Fields',
name: 'fields',
type: 'string',
default: '',
description: 'Fields to include separated by ,',
},
],
},
/* -------------------------------------------------------------------------- */
/* opportunity:addNote */
/* -------------------------------------------------------------------------- */
{
displayName: 'Opportunity ID',
name: 'opportunityId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['opportunity'],
operation: ['addNote'],
},
},
description: 'ID of opportunity that needs to be fetched',
},
{
displayName: 'Title',
name: 'title',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['opportunity'],
operation: ['addNote'],
},
},
description: 'Title of the note',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['opportunity'],
operation: ['addNote'],
},
},
options: [
{
displayName: 'Body',
name: 'body',
type: 'string',
default: '',
description: 'Body of the note. Limited to 32 KB.',
},
{
displayName: 'Is Private',
name: 'isPrivate',
type: 'boolean',
default: false,
description:
'Whether true, only the note owner or a user with the “Modify All Data” permission can view the note or query it via the API',
},
{
displayName: 'Owner Name or ID',
name: 'owner',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: '',
description:
'ID of the user who owns the note. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
],
},
];
@@ -0,0 +1,17 @@
export interface IOpportunity {
[key: string]: any;
Name?: string;
StageName?: string;
CloseDate?: string;
Type?: string;
Amount?: number;
OwnerId?: string;
NextStep?: string;
AccountId?: string;
CampaignId?: string;
LeadSource?: string;
Description?: string;
Probability?: number;
Pricebook2Id?: string;
ForecastCategoryName?: string;
}
@@ -0,0 +1,18 @@
{
"node": "n8n-nodes-base.salesforce",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Sales", "Communication"],
"resources": {
"credentialDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/credentials/salesforce/"
}
],
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.salesforce/"
}
]
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,18 @@
{
"node": "n8n-nodes-base.salesforceTrigger",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Sales", "Communication"],
"resources": {
"credentialDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/credentials/salesforce/"
}
],
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.salesforcetrigger/"
}
]
}
}
@@ -0,0 +1,303 @@
import { DateTime } from 'luxon';
import { NodeApiError, NodeConnectionTypes } from 'n8n-workflow';
import type {
IDataObject,
IPollFunctions,
INodeExecutionData,
INodeType,
INodeTypeDescription,
ILoadOptionsFunctions,
INodePropertyOptions,
JsonObject,
} from 'n8n-workflow';
import {
getQuery,
salesforceApiRequest,
salesforceApiRequestAllItems,
sortOptions,
getPollStartDate,
filterAndManageProcessedItems,
} from './GenericFunctions';
export class SalesforceTrigger implements INodeType {
description: INodeTypeDescription = {
displayName: 'Salesforce Trigger',
name: 'salesforceTrigger',
icon: 'file:salesforce.svg',
group: ['trigger'],
version: 1,
description:
'Fetches data from Salesforce and starts the workflow on specified polling intervals.',
subtitle: '={{($parameter["triggerOn"])}}',
defaults: {
name: 'Salesforce Trigger',
},
credentials: [
{
name: 'salesforceOAuth2Api',
required: true,
},
],
polling: true,
inputs: [],
outputs: [NodeConnectionTypes.Main],
properties: [
{
displayName: 'Trigger On',
name: 'triggerOn',
description: 'Which Salesforce event should trigger the node',
type: 'options',
default: '',
options: [
{
name: 'Account Created',
value: 'accountCreated',
description: 'When a new account is created',
},
{
name: 'Account Updated',
value: 'accountUpdated',
description: 'When an existing account is modified',
},
{
name: 'Attachment Created',
value: 'attachmentCreated',
description: 'When a file is uploaded and attached to an object',
},
{
name: 'Attachment Updated',
value: 'attachmentUpdated',
description: 'When an existing file is modified',
},
{
name: 'Case Created',
value: 'caseCreated',
description: 'When a new case is created',
},
{
name: 'Case Updated',
value: 'caseUpdated',
description: 'When an existing case is modified',
},
{
name: 'Contact Created',
value: 'contactCreated',
description: 'When a new contact is created',
},
{
name: 'Contact Updated',
value: 'contactUpdated',
description: 'When an existing contact is modified',
},
{
name: 'Custom Object Created',
value: 'customObjectCreated',
description: 'When a new object of a given type is created',
},
{
name: 'Custom Object Updated',
value: 'customObjectUpdated',
description: 'When an object of a given type is modified',
},
{
name: 'Lead Created',
value: 'leadCreated',
description: 'When a new lead is created',
},
{
name: 'Lead Updated',
value: 'leadUpdated',
description: 'When an existing lead is modified',
},
{
name: 'Opportunity Created',
value: 'opportunityCreated',
description: 'When a new opportunity is created',
},
{
name: 'Opportunity Updated',
value: 'opportunityUpdated',
description: 'When an existing opportunity is modified',
},
{
name: 'Task Created',
value: 'taskCreated',
description: 'When a new task is created',
},
{
name: 'Task Updated',
value: 'taskUpdated',
description: 'When an existing task is modified',
},
{
name: 'User Created',
value: 'userCreated',
description: 'When a new user is created',
},
{
name: 'User Updated',
value: 'userUpdated',
description: 'When an existing user is modified',
},
],
},
{
displayName: 'Custom Object Name or ID',
name: 'customObject',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCustomObjects',
},
required: true,
default: '',
displayOptions: {
show: {
triggerOn: ['customObjectUpdated', 'customObjectCreated'],
},
},
description:
'Name of the custom object. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
],
};
methods = {
loadOptions: {
// Get all the custom objects recurrence instances to display them to user so that they can
// select them easily
async getCustomObjects(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
// TODO: find a way to filter this object to get just the lead sources instead of the whole object
const { sobjects: objects } = await salesforceApiRequest.call(this, 'GET', '/sobjects');
for (const object of objects) {
if (object.custom === true) {
const objectName = object.label;
const objectId = object.name;
returnData.push({
name: objectName,
value: objectId,
});
}
}
sortOptions(returnData);
return returnData;
},
},
};
async poll(this: IPollFunctions): Promise<INodeExecutionData[][] | null> {
const workflowData: { processedIds?: string[]; lastTimeChecked?: string } =
this.getWorkflowStaticData('node');
let responseData;
const qs: IDataObject = {};
const triggerOn = this.getNodeParameter('triggerOn') as string;
let triggerResource = triggerOn.slice(0, 1).toUpperCase() + triggerOn.slice(1, -7);
const changeType = triggerOn.slice(-7);
if (triggerResource === 'CustomObject') {
triggerResource = this.getNodeParameter('customObject') as string;
}
const endDate = DateTime.now().toISO();
if (!workflowData.processedIds) {
workflowData.processedIds = [];
}
const processedIds = workflowData.processedIds;
try {
const pollStartDate = getPollStartDate(workflowData.lastTimeChecked);
const pollEndDate = endDate;
const options = {
conditionsUi: {
conditionValues: [] as IDataObject[],
},
};
if (this.getMode() !== 'manual') {
if (changeType === 'Created') {
options.conditionsUi.conditionValues.push({
field: 'CreatedDate',
operation: '>=',
value: pollStartDate,
});
options.conditionsUi.conditionValues.push({
field: 'CreatedDate',
operation: '<',
value: pollEndDate,
});
} else {
options.conditionsUi.conditionValues.push({
field: 'LastModifiedDate',
operation: '>=',
value: pollStartDate,
});
options.conditionsUi.conditionValues.push({
field: 'LastModifiedDate',
operation: '<',
value: pollEndDate,
});
// make sure the resource wasn't just created.
options.conditionsUi.conditionValues.push({
field: 'CreatedDate',
operation: '<',
value: pollStartDate,
});
}
}
try {
if (this.getMode() === 'manual') {
qs.q = getQuery(options, triggerResource, false, 1);
} else {
qs.q = getQuery(options, triggerResource, true);
}
responseData = await salesforceApiRequestAllItems.call(
this,
'records',
'GET',
'/query',
{},
qs,
);
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
if (!responseData?.length) {
workflowData.lastTimeChecked = endDate;
return null;
}
const { newItems, updatedProcessedIds } = filterAndManageProcessedItems(
responseData,
processedIds,
);
workflowData.processedIds = updatedProcessedIds;
workflowData.lastTimeChecked = endDate;
if (newItems.length > 0) {
return [this.helpers.returnJsonArray(newItems as IDataObject[])];
}
return null;
} catch (error) {
if (this.getMode() === 'manual' || !workflowData.lastTimeChecked) {
throw error;
}
const workflow = this.getWorkflow();
const node = this.getNode();
this.logger.error(
`There was a problem in '${node.name}' node in workflow '${workflow.id}': '${error.description}'`,
{
node: node.name,
workflowId: workflow.id,
error,
},
);
throw error;
}
}
}
@@ -0,0 +1,45 @@
import type { INodeProperties } from 'n8n-workflow';
export const searchOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['search'],
},
},
options: [
{
name: 'Query',
value: 'query',
description: 'Execute a SOQL query that returns all the results in a single response',
action: 'Perform a query',
},
],
default: 'query',
},
];
export const searchFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* search:query */
/* -------------------------------------------------------------------------- */
{
displayName: 'Query',
name: 'query',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['search'],
operation: ['query'],
},
},
description:
'A SOQL query. An example query parameter string might look like: “SELECT+Name+FROM+MyObject”. If the SOQL query string is invalid, a MALFORMED_QUERY response is returned.',
},
];
@@ -0,0 +1,915 @@
import type { INodeProperties } from 'n8n-workflow';
export const taskOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['task'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a task',
action: 'Create a task',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a task',
action: 'Delete a task',
},
{
name: 'Get',
value: 'get',
description: 'Get a task',
action: 'Get a task',
},
{
name: 'Get Many',
value: 'getAll',
description: 'Get many tasks',
action: 'Get many tasks',
},
{
name: 'Get Summary',
value: 'getSummary',
description: "Returns an overview of task's metadata",
action: 'Get a task summary',
},
{
name: 'Update',
value: 'update',
description: 'Update a task',
action: 'Update a task',
},
],
default: 'create',
},
];
export const taskFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* task:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'Status Name or ID',
name: 'status',
type: 'options',
required: true,
default: '',
displayOptions: {
show: {
resource: ['task'],
operation: ['create'],
},
},
typeOptions: {
loadOptionsMethod: 'getTaskStatuses',
},
description:
'The current status of the task, such as In Progress or Completed. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['task'],
operation: ['create'],
},
},
options: [
{
displayName: 'Activity Date',
name: 'activityDate',
type: 'dateTime',
default: '',
description:
'Represents the due date of the task. This field has a timestamp that is always set to midnight in the Coordinated Universal Time (UTC) time zone.',
},
{
displayName: 'Call Disposition',
name: 'callDisposition',
type: 'string',
default: '',
description:
"Represents the result of a given call, for example, “we'll call back,” or “call unsuccessful.” Limit is 255 characters. Not subject to field-level security, available for any user in an organization with Salesforce CRM Call Center.",
},
{
displayName: 'Call Duration In Seconds',
name: 'callDurationInSeconds',
type: 'number',
default: '',
description:
'Duration of the call in seconds. Not subject to field-level security, available for any user in an organization with Salesforce CRM Call Center.',
},
{
displayName: 'Call Object',
name: 'callObject',
type: 'string',
default: '',
description:
'Name of a call center. Limit is 255 characters. Not subject to field-level security, available for any user in an organization with Salesforce CRM Call Center.',
},
{
displayName: 'Call Type Name or ID',
name: 'callType',
type: 'options',
default: '',
typeOptions: {
loadOptionsMethod: 'getTaskCallTypes',
},
description:
'The type of call being answered: Inbound, Internal, or Outbound. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Custom Fields',
name: 'customFieldsUi',
placeholder: 'Add Custom Field',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
description: 'Filter by custom fields',
default: {},
options: [
{
name: 'customFieldsValues',
displayName: 'Custom Field',
values: [
{
displayName: 'Field Name or ID',
name: 'fieldId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCustomFields',
},
default: '',
description:
'The ID of the field to add custom field to. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'The value to set on custom field',
},
],
},
],
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
description: 'Contains a text description of the task',
},
{
displayName: 'Is ReminderSet',
name: 'isReminderSet',
type: 'boolean',
default: false,
description: 'Whether a popup reminder has been set for the task (true) or not (false)',
},
{
displayName: 'Owner Name or ID',
name: 'owner',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: '',
description:
'ID of the User who owns the record. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Priority Name or ID',
name: 'priority',
type: 'options',
default: '',
typeOptions: {
loadOptionsMethod: 'getTaskPriorities',
},
description:
'Indicates the importance or urgency of a task, such as high or low. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Recurrence Type Name or ID',
name: 'recurrenceType',
type: 'options',
default: '',
typeOptions: {
loadOptionsMethod: 'getTaskRecurrenceTypes',
},
description:
'Recurrence Type of the task. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Recurrence Instance Name or ID',
name: 'recurrenceInstance',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getTaskRecurrenceInstances',
},
default: '',
description:
'The frequency of the recurring task. For example, “2nd” or “3rd.”. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Recurrence Interval',
name: 'recurrenceInterval',
type: 'number',
default: '',
description: 'The interval between recurring tasks',
},
{
displayName: 'Recurrence Day Of Month',
name: 'recurrenceDayOfMonth',
type: 'number',
default: '',
description: 'The day of the month in which the task repeats',
},
{
displayName: 'Recurrence Day Of Week Mask',
name: 'recurrenceDayOfWeekMask',
type: 'number',
default: '',
description:
'The day or days of the week on which the task repeats. This field contains a bitmask. The values are as follows: Sunday = 1 Monday = 2 Tuesday = 4 Wednesday = 8 Thursday = 16 Friday = 32 Saturday = 64 Multiple days are represented as the sum of their numerical values. For example, Tuesday and Thursday = 4 + 16 = 20.',
},
{
displayName: 'Recurrence End Date Only',
name: 'recurrenceEndDateOnly',
type: 'dateTime',
default: '',
description:
'The last date on which the task repeats. This field has a timestamp that is always set to midnight in the Coordinated Universal Time (UTC) time zone.',
},
{
displayName: 'Recurrence Month Of Year',
name: 'recurrenceMonthOfYear',
type: 'options',
// eslint-disable-next-line n8n-nodes-base/node-param-options-type-unsorted-items
options: [
{
name: 'January',
value: 'January',
},
{
name: 'February',
value: 'February',
},
{
name: 'March',
value: 'March',
},
{
name: 'April',
value: 'April',
},
{
name: 'May',
value: 'May',
},
{
name: 'June',
value: 'June',
},
{
name: 'July',
value: 'July',
},
{
name: 'August',
value: 'August',
},
{
name: 'September',
value: 'September',
},
{
name: 'October',
value: 'October',
},
{
name: 'November',
value: 'November',
},
{
name: 'December',
value: 'December',
},
],
default: '',
description: 'The month of the year in which the task repeats',
},
{
displayName: 'Recurrence Regenerated Type',
name: 'recurrenceRegeneratedType',
type: 'options',
default: '',
options: [
{
name: 'After Due Date',
value: 'RecurrenceRegenerateAfterDueDate',
},
{
name: 'After Date Completed',
value: 'RecurrenceRegenerateAfterToday',
},
{
name: '(Task Closed)',
value: 'RecurrenceRegenerated',
},
],
description:
'Represents what triggers a repeating task to repeat. Add this field to a page layout together with the RecurrenceInterval field, which determines the number of days between the triggering date (due date or close date) and the due date of the next repeating task in the series. Label is Repeat This Task.',
},
{
displayName: 'Recurrence Start Date Only',
name: 'recurrenceEndDateOnly',
type: 'dateTime',
default: '',
description:
'The date when the recurring task begins. Must be a date and time before RecurrenceEndDateOnly.',
},
{
displayName: 'Recurrence TimeZone SidKey',
name: 'recurrenceTimeZoneSidKey',
type: 'string',
default: '',
description:
'The time zone associated with the recurring task. For example, “UTC-8:00” for Pacific Standard Time.',
},
{
displayName: 'Reminder Date Time',
name: 'reminderDateTime',
type: 'dateTime',
default: '',
description:
'Represents the time when the reminder is scheduled to fire, if IsReminderSet is set to true. If IsReminderSet is set to false, then the user may have deselected the reminder checkbox in the Salesforce user interface, or the reminder has already fired at the time indicated by the value.',
},
{
displayName: 'Subject Name or ID',
name: 'subject',
type: 'options',
default: '',
typeOptions: {
loadOptionsMethod: 'getTaskSubjects',
},
description:
'The subject line of the task, such as “Call” or “Send Quote.” Limit: 255 characters. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Type Name or ID',
name: 'type',
type: 'options',
default: '',
typeOptions: {
loadOptionsMethod: 'getTaskTypes',
},
description:
'Represents Type of the task, such as Call or Meeting. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'What ID',
name: 'whatId',
type: 'string',
default: '',
description:
'The WhatId represents nonhuman objects such as accounts, opportunities, campaigns, cases, or custom objects. WhatIds are polymorphic. Polymorphic means a WhatId is equivalent to the ID of a related object.',
},
{
displayName: 'Who ID',
name: 'whoId',
type: 'string',
default: '',
description:
'The WhoId represents a human such as a lead or a contact. WhoIds are polymorphic. Polymorphic means a WhoId is equivalent to a contacts ID or a leads ID.',
},
],
},
/* -------------------------------------------------------------------------- */
/* task:update */
/* -------------------------------------------------------------------------- */
{
displayName: 'Task ID',
name: 'taskId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['task'],
operation: ['update'],
},
},
description: 'ID of task that needs to be fetched',
},
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['task'],
operation: ['update'],
},
},
options: [
{
displayName: 'Activity Date',
name: 'activityDate',
type: 'dateTime',
default: '',
description:
'Represents the due date of the task. This field has a timestamp that is always set to midnight in the Coordinated Universal Time (UTC) time zone.',
},
{
displayName: 'Call Disposition',
name: 'callDisposition',
type: 'string',
default: '',
description:
"Represents the result of a given call, for example, “we'll call back,” or “call unsuccessful.” Limit is 255 characters. Not subject to field-level security, available for any user in an organization with Salesforce CRM Call Center.",
},
{
displayName: 'Call Duration In Seconds',
name: 'callDurationInSeconds',
type: 'number',
default: '',
description:
'Duration of the call in seconds. Not subject to field-level security, available for any user in an organization with Salesforce CRM Call Center.',
},
{
displayName: 'Call Object',
name: 'callObject',
type: 'string',
default: '',
description:
'Name of a call center. Limit is 255 characters. Not subject to field-level security, available for any user in an organization with Salesforce CRM Call Center.',
},
{
displayName: 'Call Type Name or ID',
name: 'callType',
type: 'options',
default: '',
typeOptions: {
loadOptionsMethod: 'getTaskCallTypes',
},
description:
'The type of call being answered: Inbound, Internal, or Outbound. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Custom Fields',
name: 'customFieldsUi',
placeholder: 'Add Custom Field',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
description: 'Filter by custom fields',
default: {},
options: [
{
name: 'customFieldsValues',
displayName: 'Custom Field',
values: [
{
displayName: 'Field Name or ID',
name: 'fieldId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getCustomFields',
},
default: '',
description:
'The ID of the field to add custom field to. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'The value to set on custom field',
},
],
},
],
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
description: 'Contains a text description of the task',
},
{
displayName: 'Is ReminderSet',
name: 'isReminderSet',
type: 'boolean',
default: false,
description: 'Whether a popup reminder has been set for the task (true) or not (false)',
},
{
displayName: 'Owner Name or ID',
name: 'owner',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: '',
description:
'ID of the User who owns the record. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Priority Name or ID',
name: 'priority',
type: 'options',
default: '',
typeOptions: {
loadOptionsMethod: 'getTaskPriorities',
},
description:
'Indicates the importance or urgency of a task, such as high or low. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Status Name or ID',
name: 'status',
type: 'options',
default: '',
typeOptions: {
loadOptionsMethod: 'getTaskStatuses',
},
description:
'The current status of the task, such as In Progress or Completed. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Subject Name or ID',
name: 'subject',
type: 'options',
default: '',
typeOptions: {
loadOptionsMethod: 'getTaskSubjects',
},
description:
'The subject line of the task, such as “Call” or “Send Quote.” Limit: 255 characters. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Recurrence Day Of Month',
name: 'recurrenceDayOfMonth',
type: 'number',
default: '',
description: 'The day of the month in which the task repeats',
},
{
displayName: 'Recurrence Day Of Week Mask',
name: 'recurrenceDayOfWeekMask',
type: 'number',
default: '',
description:
'The day or days of the week on which the task repeats. This field contains a bitmask. The values are as follows: Sunday = 1 Monday = 2 Tuesday = 4 Wednesday = 8 Thursday = 16 Friday = 32 Saturday = 64. Multiple days are represented as the sum of their numerical values. For example, Tuesday and Thursday = 4 + 16 = 20.',
},
{
displayName: 'Recurrence End Date Only',
name: 'recurrenceEndDateOnly',
type: 'dateTime',
default: '',
description:
'The last date on which the task repeats. This field has a timestamp that is always set to midnight in the Coordinated Universal Time (UTC) time zone.',
},
{
displayName: 'Recurrence Instance Name or ID',
name: 'recurrenceInstance',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getTaskRecurrenceInstances',
},
default: '',
description:
'The frequency of the recurring task. For example, “2nd” or “3rd.”. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Recurrence Interval',
name: 'recurrenceInterval',
type: 'number',
default: '',
description: 'The interval between recurring tasks',
},
{
displayName: 'Recurrence Month Of Year',
name: 'recurrenceMonthOfYear',
type: 'options',
// eslint-disable-next-line n8n-nodes-base/node-param-options-type-unsorted-items
options: [
{
name: 'January',
value: 'January',
},
{
name: 'February',
value: 'February',
},
{
name: 'March',
value: 'March',
},
{
name: 'April',
value: 'April',
},
{
name: 'May',
value: 'May',
},
{
name: 'June',
value: 'June',
},
{
name: 'July',
value: 'July',
},
{
name: 'August',
value: 'August',
},
{
name: 'September',
value: 'September',
},
{
name: 'October',
value: 'October',
},
{
name: 'November',
value: 'November',
},
{
name: 'December',
value: 'December',
},
],
default: '',
description: 'The month of the year in which the task repeats',
},
{
displayName: 'Recurrence Start Date Only',
name: 'recurrenceEndDateOnly',
type: 'dateTime',
default: '',
description:
'The date when the recurring task begins. Must be a date and time before RecurrenceEndDateOnly.',
},
{
displayName: 'Recurrence Regenerated Type',
name: 'recurrenceRegeneratedType',
type: 'options',
default: '',
options: [
{
name: 'After Due Date',
value: 'RecurrenceRegenerateAfterDueDate',
},
{
name: 'After Date Completed',
value: 'RecurrenceRegenerateAfterToday',
},
{
name: '(Task Closed)',
value: 'RecurrenceRegenerated',
},
],
description:
'Represents what triggers a repeating task to repeat. Add this field to a page layout together with the RecurrenceInterval field, which determines the number of days between the triggering date (due date or close date) and the due date of the next repeating task in the series. Label is Repeat This Task.',
},
{
displayName: 'Recurrence Type Name or ID',
name: 'recurrenceType',
type: 'options',
default: '',
typeOptions: {
loadOptionsMethod: 'getTaskRecurrenceTypes',
},
description:
'Website for the task. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Recurrence TimeZone SidKey',
name: 'recurrenceTimeZoneSidKey',
type: 'string',
default: '',
description:
'The time zone associated with the recurring task. For example, “UTC-8:00” for Pacific Standard Time.',
},
{
displayName: 'Reminder Date Time',
name: 'reminderDateTime',
type: 'dateTime',
default: '',
description:
'Represents the time when the reminder is scheduled to fire, if IsReminderSet is set to true. If IsReminderSet is set to false, then the user may have deselected the reminder checkbox in the Salesforce user interface, or the reminder has already fired at the time indicated by the value.',
},
{
displayName: 'Type Name or ID',
name: 'type',
type: 'options',
default: '',
typeOptions: {
loadOptionsMethod: 'getTaskTypes',
},
description:
'Represents Type of the task, such as Call or Meeting. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'What ID',
name: 'whatId',
type: 'string',
default: '',
description:
'The WhatId represents nonhuman objects such as accounts, opportunities, campaigns, cases, or custom objects. WhatIds are polymorphic. Polymorphic means a WhatId is equivalent to the ID of a related object.',
},
{
displayName: 'Who ID',
name: 'whoId',
type: 'string',
default: '',
description:
'The WhoId represents a human such as a lead or a contact. WhoIds are polymorphic. Polymorphic means a WhoId is equivalent to a contacts ID or a leads ID.',
},
],
},
/* -------------------------------------------------------------------------- */
/* task:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Task ID',
name: 'taskId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['task'],
operation: ['get'],
},
},
description: 'ID of task that needs to be fetched',
},
/* -------------------------------------------------------------------------- */
/* task:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'Task ID',
name: 'taskId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['task'],
operation: ['delete'],
},
},
description: 'ID of task that needs to be fetched',
},
/* -------------------------------------------------------------------------- */
/* task:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: ['task'],
operation: ['getAll'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: ['task'],
operation: ['getAll'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
description: 'Max number of results to return',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['task'],
operation: ['getAll'],
},
},
options: [
{
displayName: 'Conditions',
name: 'conditionsUi',
placeholder: 'Add Condition',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
description: 'The condition to set',
default: {},
options: [
{
name: 'conditionValues',
displayName: 'Condition',
values: [
{
displayName: 'Field Name or ID',
name: 'field',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getTaskFields',
},
default: '',
description:
'For date, number, or boolean, please use expressions. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
// eslint-disable-next-line n8n-nodes-base/node-param-operation-without-no-data-expression
{
displayName: 'Operation',
name: 'operation',
type: 'options',
options: [
{
name: '<',
value: '<',
},
{
name: '<=',
value: '<=',
},
{
name: '=',
value: 'equal',
},
{
name: '>',
value: '>',
},
{
name: '>=',
value: '>=',
},
],
default: 'equal',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
},
],
},
],
},
{
displayName: 'Fields',
name: 'fields',
type: 'string',
default: '',
description: 'Fields to include separated by ,',
},
],
},
];
@@ -0,0 +1,27 @@
export interface ITask {
TaskSubtype?: string;
WhoId?: string;
Status?: string;
WhatId?: string;
OwnerId?: string;
Subject?: string;
CallType?: string;
Priority?: string;
CallObject?: string;
Description?: string;
ActivityDate?: string;
IsReminderSet?: boolean;
RecurrenceType?: string;
CallDisposition?: string;
ReminderDateTime?: string;
RecurrenceInstance?: string;
RecurrenceInterval?: number;
RecurrenceDayOfMonth?: number;
CallDurationInSeconds?: number;
RecurrenceEndDateOnly?: string;
RecurrenceMonthOfYear?: string;
RecurrenceDayOfWeekMask?: string;
RecurrenceStartDateOnly?: string;
RecurrenceTimeZoneSidKey?: string;
RecurrenceRegeneratedType?: string;
}
@@ -0,0 +1,172 @@
import type { INodeProperties } from 'n8n-workflow';
export const userOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['user'],
},
},
options: [
{
name: 'Get',
value: 'get',
description: 'Get a user',
action: 'Get a user',
},
{
name: 'Get Many',
value: 'getAll',
description: 'Get many users',
action: 'Get many users',
},
],
default: 'get',
},
];
export const userFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* user:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'User ID',
name: 'userId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['user'],
operation: ['get'],
},
},
description: 'ID of user that needs to be fetched',
},
/* -------------------------------------------------------------------------- */
/* user:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: ['user'],
operation: ['getAll'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: ['user'],
operation: ['getAll'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
description: 'Max number of results to return',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['user'],
operation: ['getAll'],
},
},
options: [
{
displayName: 'Conditions',
name: 'conditionsUi',
placeholder: 'Add Condition',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
description: 'The condition to set',
default: {},
options: [
{
name: 'conditionValues',
displayName: 'Condition',
values: [
{
displayName: 'Field Name or ID',
name: 'field',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getUserFields',
},
default: '',
description:
'For date, number, or boolean, please use expressions. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
// eslint-disable-next-line n8n-nodes-base/node-param-operation-without-no-data-expression
{
displayName: 'Operation',
name: 'operation',
type: 'options',
options: [
{
name: '<',
value: '<',
},
{
name: '<=',
value: '<=',
},
{
name: '=',
value: 'equal',
},
{
name: '>',
value: '>',
},
{
name: '>=',
value: '>=',
},
],
default: 'equal',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
},
],
},
],
},
{
displayName: 'Fields',
name: 'fields',
type: 'string',
default: '',
description: 'Fields to include separated by ,',
},
],
},
];
@@ -0,0 +1,10 @@
export interface IUser {
Alias?: string;
Department?: string;
Division?: string;
Email?: string;
IsActive?: boolean;
MobilePhone?: string;
Title?: string;
Username?: string;
}
@@ -0,0 +1,12 @@
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"success": {
"type": "boolean"
}
},
"version": 1
}
@@ -0,0 +1,9 @@
{
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
},
"version": 1
}
@@ -0,0 +1,73 @@
{
"type": "object",
"properties": {
"attributes": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"BillingAddress": {
"type": "object",
"properties": {
"geocodeAccuracy": {
"type": "null"
}
}
},
"BillingGeocodeAccuracy": {
"type": "null"
},
"CreatedById": {
"type": "string"
},
"CreatedDate": {
"type": "string"
},
"error": {
"type": "string"
},
"Id": {
"type": "string"
},
"IsDeleted": {
"type": "boolean"
},
"Jigsaw": {
"type": "null"
},
"JigsawCompanyId": {
"type": "null"
},
"LastModifiedById": {
"type": "string"
},
"LastModifiedDate": {
"type": "string"
},
"MasterRecordId": {
"type": "null"
},
"Name": {
"type": "string"
},
"OwnerId": {
"type": "string"
},
"ShippingGeocodeAccuracy": {
"type": "null"
},
"SicDesc": {
"type": "null"
},
"SystemModstamp": {
"type": "string"
}
},
"version": 3
}
@@ -0,0 +1,23 @@
{
"type": "object",
"properties": {
"attributes": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"Id": {
"type": "string"
},
"Name": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1,9 @@
{
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
},
"version": 1
}
@@ -0,0 +1,23 @@
{
"type": "object",
"properties": {
"attributes": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"Id": {
"type": "string"
},
"Name": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1,12 @@
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"success": {
"type": "boolean"
}
},
"version": 1
}
@@ -0,0 +1,71 @@
{
"type": "object",
"properties": {
"AssetId": {
"type": "null"
},
"attributes": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"BusinessHoursId": {
"type": "string"
},
"CaseNumber": {
"type": "string"
},
"Comments": {
"type": "null"
},
"CreatedById": {
"type": "string"
},
"CreatedDate": {
"type": "string"
},
"Id": {
"type": "string"
},
"IsClosed": {
"type": "boolean"
},
"IsDeleted": {
"type": "boolean"
},
"IsEscalated": {
"type": "boolean"
},
"Language": {
"type": "null"
},
"LastModifiedById": {
"type": "string"
},
"LastModifiedDate": {
"type": "string"
},
"MasterRecordId": {
"type": "null"
},
"OwnerId": {
"type": "string"
},
"RecordTypeId": {
"type": "string"
},
"Status": {
"type": "string"
},
"SystemModstamp": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1,26 @@
{
"type": "object",
"properties": {
"attributes": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"Id": {
"type": "string"
},
"OwnerId": {
"type": "string"
},
"Status": {
"type": "string"
}
},
"version": 4
}
@@ -0,0 +1,9 @@
{
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
},
"version": 1
}
@@ -0,0 +1,9 @@
{
"type": "object",
"properties": {
"error": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1,12 @@
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"success": {
"type": "boolean"
}
},
"version": 1
}
@@ -0,0 +1,9 @@
{
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
},
"version": 1
}
@@ -0,0 +1,80 @@
{
"type": "object",
"properties": {
"attributes": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"CreatedById": {
"type": "string"
},
"CreatedDate": {
"type": "string"
},
"HomePhone": {
"type": "null"
},
"Id": {
"type": "string"
},
"IsDeleted": {
"type": "boolean"
},
"IsEmailBounced": {
"type": "boolean"
},
"IsPriorityRecord": {
"type": "boolean"
},
"Jigsaw": {
"type": "null"
},
"JigsawContactId": {
"type": "null"
},
"LastCURequestDate": {
"type": "null"
},
"LastCUUpdateDate": {
"type": "null"
},
"LastModifiedById": {
"type": "string"
},
"LastModifiedDate": {
"type": "string"
},
"LastName": {
"type": "string"
},
"MasterRecordId": {
"type": "null"
},
"MiddleName": {
"type": "null"
},
"Name": {
"type": "string"
},
"OtherPhone": {
"type": "null"
},
"OwnerId": {
"type": "string"
},
"Suffix": {
"type": "null"
},
"SystemModstamp": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1,23 @@
{
"type": "object",
"properties": {
"attributes": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"Id": {
"type": "string"
},
"LastName": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1,9 @@
{
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
},
"version": 1
}
@@ -0,0 +1,15 @@
{
"type": "object",
"properties": {
"created": {
"type": "boolean"
},
"id": {
"type": "string"
},
"success": {
"type": "boolean"
}
},
"version": 1
}
@@ -0,0 +1,12 @@
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"success": {
"type": "boolean"
}
},
"version": 3
}
@@ -0,0 +1,9 @@
{
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
},
"version": 1
}
@@ -0,0 +1,47 @@
{
"type": "object",
"properties": {
"attributes": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"CreatedById": {
"type": "string"
},
"CreatedDate": {
"type": "string"
},
"CurrencyIsoCode": {
"type": "string"
},
"Id": {
"type": "string"
},
"IsDeleted": {
"type": "boolean"
},
"LastModifiedById": {
"type": "string"
},
"LastModifiedDate": {
"type": "string"
},
"Name": {
"type": "string"
},
"OwnerId": {
"type": "string"
},
"SystemModstamp": {
"type": "string"
}
},
"version": 3
}
@@ -0,0 +1,20 @@
{
"type": "object",
"properties": {
"attributes": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"Id": {
"type": "string"
}
},
"version": 8
}
@@ -0,0 +1,9 @@
{
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
},
"version": 1
}
@@ -0,0 +1,15 @@
{
"type": "object",
"properties": {
"created": {
"type": "boolean"
},
"id": {
"type": "string"
},
"success": {
"type": "boolean"
}
},
"version": 2
}
@@ -0,0 +1,12 @@
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"success": {
"type": "boolean"
}
},
"version": 1
}
@@ -0,0 +1,18 @@
{
"type": "object",
"properties": {
"label": {
"type": "string"
},
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"url": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1,120 @@
{
"type": "object",
"properties": {
"actionName": {
"type": "string"
},
"errors": {
"type": "null"
},
"invocationId": {
"type": "null"
},
"isSuccess": {
"type": "boolean"
},
"outcome": {
"type": "null"
},
"outputValues": {
"type": "object",
"properties": {
"Flow__InterviewGuid": {
"type": "string"
},
"Flow__InterviewStatus": {
"type": "string"
},
"projects": {
"type": "array",
"items": {
"type": "object",
"properties": {
"attributes": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"CreatedById": {
"type": "string"
},
"CreatedDate": {
"type": "string"
},
"Id": {
"type": "string"
},
"IsDeleted": {
"type": "boolean"
},
"LastActivityDate": {
"type": "null"
},
"LastModifiedById": {
"type": "string"
},
"LastModifiedDate": {
"type": "string"
},
"LastReferencedDate": {
"type": "string"
},
"LastViewedDate": {
"type": "string"
},
"Name": {
"type": "string"
},
"OwnerId": {
"type": "string"
},
"Projectcloseddate__c": {
"type": "string"
},
"Projectcreateddate__c": {
"type": "null"
},
"Projectdescription__c": {
"type": "null"
},
"Projectduedate__c": {
"type": "null"
},
"Projectlink__c": {
"type": "null"
},
"Projectmanager__c": {
"type": "null"
},
"Projectpriority__c": {
"type": "null"
},
"Projectstatus__c": {
"type": "null"
},
"Projecttitle__c": {
"type": "string"
},
"SystemModstamp": {
"type": "string"
}
}
}
}
}
},
"sortOrder": {
"type": "integer"
},
"version": {
"type": "integer"
}
},
"version": 3
}
@@ -0,0 +1,12 @@
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"success": {
"type": "boolean"
}
},
"version": 1
}
@@ -0,0 +1,12 @@
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"success": {
"type": "boolean"
}
},
"version": 1
}
@@ -0,0 +1,12 @@
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"success": {
"type": "boolean"
}
},
"version": 1
}
@@ -0,0 +1,92 @@
{
"type": "object",
"properties": {
"attributes": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"CreatedById": {
"type": "string"
},
"CreatedDate": {
"type": "string"
},
"EmailBouncedDate": {
"type": "null"
},
"EmailBouncedReason": {
"type": "null"
},
"GeocodeAccuracy": {
"type": "null"
},
"HasOptedOutOfEmail": {
"type": "boolean"
},
"Id": {
"type": "string"
},
"IndividualId": {
"type": "null"
},
"IsConverted": {
"type": "boolean"
},
"IsDeleted": {
"type": "boolean"
},
"IsPriorityRecord": {
"type": "boolean"
},
"IsUnreadByOwner": {
"type": "boolean"
},
"Jigsaw": {
"type": "null"
},
"JigsawContactId": {
"type": "null"
},
"LastModifiedById": {
"type": "string"
},
"LastModifiedDate": {
"type": "string"
},
"LastName": {
"type": "string"
},
"Latitude": {
"type": "null"
},
"Longitude": {
"type": "null"
},
"MasterRecordId": {
"type": "null"
},
"Name": {
"type": "string"
},
"OwnerId": {
"type": "string"
},
"Status": {
"type": "string"
},
"Suffix": {
"type": "null"
},
"SystemModstamp": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1,23 @@
{
"type": "object",
"properties": {
"attributes": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"Id": {
"type": "string"
},
"Status": {
"type": "string"
}
},
"version": 5
}
@@ -0,0 +1,9 @@
{
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
},
"version": 1
}
@@ -0,0 +1,15 @@
{
"type": "object",
"properties": {
"created": {
"type": "boolean"
},
"id": {
"type": "string"
},
"success": {
"type": "boolean"
}
},
"version": 1
}
@@ -0,0 +1,12 @@
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"success": {
"type": "boolean"
}
},
"version": 1
}
@@ -0,0 +1,89 @@
{
"type": "object",
"properties": {
"AccountId": {
"type": "string"
},
"attributes": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"CloseDate": {
"type": "string"
},
"CreatedById": {
"type": "string"
},
"CreatedDate": {
"type": "string"
},
"Fiscal": {
"type": "string"
},
"FiscalQuarter": {
"type": "integer"
},
"FiscalYear": {
"type": "integer"
},
"ForecastCategory": {
"type": "string"
},
"ForecastCategoryName": {
"type": "string"
},
"HasOpenActivity": {
"type": "boolean"
},
"HasOpportunityLineItem": {
"type": "boolean"
},
"HasOverdueTask": {
"type": "boolean"
},
"Id": {
"type": "string"
},
"IsClosed": {
"type": "boolean"
},
"IsDeleted": {
"type": "boolean"
},
"IsWon": {
"type": "boolean"
},
"LastModifiedById": {
"type": "string"
},
"LastModifiedDate": {
"type": "string"
},
"Name": {
"type": "string"
},
"OwnerId": {
"type": "string"
},
"Probability": {
"type": "integer"
},
"PushCount": {
"type": "integer"
},
"StageName": {
"type": "string"
},
"SystemModstamp": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1,23 @@
{
"type": "object",
"properties": {
"attributes": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"Id": {
"type": "string"
},
"Name": {
"type": "string"
}
},
"version": 8
}
@@ -0,0 +1,146 @@
{
"type": "object",
"properties": {
"objectDescribe": {
"type": "object",
"properties": {
"activateable": {
"type": "boolean"
},
"associateEntityType": {
"type": "null"
},
"associateParentEntity": {
"type": "null"
},
"createable": {
"type": "boolean"
},
"custom": {
"type": "boolean"
},
"customSetting": {
"type": "boolean"
},
"deepCloneable": {
"type": "boolean"
},
"deletable": {
"type": "boolean"
},
"deprecatedAndHidden": {
"type": "boolean"
},
"feedEnabled": {
"type": "boolean"
},
"hasSubtypes": {
"type": "boolean"
},
"isInterface": {
"type": "boolean"
},
"isSubtype": {
"type": "boolean"
},
"keyPrefix": {
"type": "string"
},
"label": {
"type": "string"
},
"labelPlural": {
"type": "string"
},
"layoutable": {
"type": "boolean"
},
"mergeable": {
"type": "boolean"
},
"mruEnabled": {
"type": "boolean"
},
"name": {
"type": "string"
},
"queryable": {
"type": "boolean"
},
"replicateable": {
"type": "boolean"
},
"retrieveable": {
"type": "boolean"
},
"searchable": {
"type": "boolean"
},
"triggerable": {
"type": "boolean"
},
"undeletable": {
"type": "boolean"
},
"updateable": {
"type": "boolean"
},
"urls": {
"type": "object",
"properties": {
"approvalLayouts": {
"type": "string"
},
"compactLayouts": {
"type": "string"
},
"describe": {
"type": "string"
},
"layouts": {
"type": "string"
},
"listviews": {
"type": "string"
},
"quickActions": {
"type": "string"
},
"rowTemplate": {
"type": "string"
},
"sobject": {
"type": "string"
}
}
}
}
},
"recentItems": {
"type": "array",
"items": {
"type": "object",
"properties": {
"attributes": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"Id": {
"type": "string"
},
"Name": {
"type": "string"
}
}
}
}
},
"version": 1
}
@@ -0,0 +1,9 @@
{
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
},
"version": 1
}
@@ -0,0 +1,20 @@
{
"type": "object",
"properties": {
"attributes": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"Id": {
"type": "string"
}
},
"version": 4
}
@@ -0,0 +1,12 @@
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"success": {
"type": "boolean"
}
},
"version": 1
}
@@ -0,0 +1,83 @@
{
"type": "object",
"properties": {
"attributes": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"CallDisposition": {
"type": "null"
},
"CallObject": {
"type": "null"
},
"CreatedById": {
"type": "string"
},
"CreatedDate": {
"type": "string"
},
"Id": {
"type": "string"
},
"IsArchived": {
"type": "boolean"
},
"IsClosed": {
"type": "boolean"
},
"IsDeleted": {
"type": "boolean"
},
"IsHighPriority": {
"type": "boolean"
},
"IsRecurrence": {
"type": "boolean"
},
"IsReminderSet": {
"type": "boolean"
},
"LastModifiedById": {
"type": "string"
},
"LastModifiedDate": {
"type": "string"
},
"OwnerId": {
"type": "string"
},
"Priority": {
"type": "string"
},
"RecurrenceDayOfMonth": {
"type": "null"
},
"RecurrenceInstance": {
"type": "null"
},
"RecurrenceMonthOfYear": {
"type": "null"
},
"RecurrenceRegeneratedType": {
"type": "null"
},
"Status": {
"type": "string"
},
"SystemModstamp": {
"type": "string"
},
"TaskSubtype": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1,20 @@
{
"type": "object",
"properties": {
"attributes": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"Id": {
"type": "string"
}
},
"version": 5
}
@@ -0,0 +1,9 @@
{
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
},
"version": 1
}
@@ -0,0 +1,26 @@
{
"type": "object",
"properties": {
"attributes": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"Email": {
"type": "string"
},
"Id": {
"type": "string"
},
"Name": {
"type": "string"
}
},
"version": 3
}
@@ -0,0 +1,964 @@
import { mockDeep } from 'jest-mock-extended';
import type { IDataObject, INodePropertyOptions, IExecuteFunctions } from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow';
import {
getValue,
getConditions,
sortOptions,
getDefaultFields,
getQuery,
filterAndManageProcessedItems,
salesforceApiRequest,
} from '../GenericFunctions';
// Mock external dependencies
jest.mock('jsonwebtoken');
jest.mock('moment-timezone', () => {
const mockMoment = (value?: any) => ({
unix: jest.fn(() => 1640995200), // Mock timestamp: 2022-01-01T00:00:00Z
isValid: jest.fn(() => {
// Mock moment validation logic to match real moment behavior
// Real moment considers many values "valid" even if they're not proper dates
if (typeof value === 'string') {
// ISO date formats
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value)) {
return true;
}
// Only consider obviously non-date strings as invalid
// Real moment would consider numeric strings like '123' as valid (with deprecation warning)
if (/^[a-zA-Z\s]+$/.test(value)) {
return false; // Pure text strings like 'foo bar baz' are invalid
}
// Everything else (including numeric strings) is considered "valid" by moment
return true;
}
return false;
}),
});
return {
__esModule: true,
default: mockMoment,
};
});
import * as jwt from 'jsonwebtoken';
const mockJwt = jwt as jest.Mocked<typeof jwt>;
describe('Salesforce -> GenericFunctions', () => {
describe('getValue', () => {
it('should return date string as is', () => {
const value = '2025-01-01T00:00:00Z';
const result = getValue(value) as string;
expect(result).toBe(value);
});
it('should return string in single quotes', () => {
const value = 'foo bar baz';
const result = getValue(value) as string;
expect(result).toBe("'foo bar baz'");
});
it('should return number as is', () => {
const value = 123;
const result = getValue(value) as number;
expect(result).toBe(value);
});
it('should return boolean as is', () => {
const value = false;
const result = getValue(value) as boolean;
expect(result).toBe(value);
});
});
describe('sortOptions', () => {
it('should sort options alphabetically by name', () => {
const unsorted: INodePropertyOptions[] = [
{
name: 'B Property',
value: 'foo',
},
{
name: 'C Property',
value: 'bar',
},
{
name: 'A Property',
value: 'baz',
},
];
const sorted: INodePropertyOptions[] = [
{
name: 'A Property',
value: 'baz',
},
{
name: 'B Property',
value: 'foo',
},
{
name: 'C Property',
value: 'bar',
},
];
sortOptions(unsorted);
expect(unsorted).toEqual(sorted);
});
});
describe('getConditions', () => {
it('should handle equals operation', () => {
const options: IDataObject = {
conditionsUi: {
conditionValues: [{ field: 'field_1', operation: 'equal', value: '123' }],
},
};
const result = getConditions(options);
expect(result).toBe('WHERE field_1 = 123');
});
it('should handle other operations', () => {
const options: IDataObject = {
conditionsUi: {
conditionValues: [
{ field: 'field_1', operation: '>', value: '123' },
{ field: 'field_2', operation: '<', value: '456' },
{ field: 'field_3', operation: '>=', value: '789' },
{ field: 'field_4', operation: '<=', value: '0' },
],
},
};
const result = getConditions(options);
expect(result).toBe(
'WHERE field_1 > 123 AND field_2 < 456 AND field_3 >= 789 AND field_4 <= 0',
);
});
it('should return undefined when conditions is not an array', () => {
const options: IDataObject = {
conditionsUi: {
conditionValues: 'not an array',
},
};
const result = getConditions(options);
expect(result).toBeUndefined();
});
it('should return undefined when conditions is an empty array', () => {
const options: IDataObject = {
conditionsUi: {
conditionValues: [],
},
};
const result = getConditions(options);
expect(result).toBeUndefined();
});
});
describe('getDefaultFields', () => {
it('should return default fields', () => {
expect(getDefaultFields('Account')).toBe('id,name,type');
expect(getDefaultFields('Lead')).toBe(
'id,company,firstname,lastname,street,postalCode,city,email,status',
);
expect(getDefaultFields('Contact')).toBe('id,firstname,lastname,email');
expect(getDefaultFields('Opportunity')).toBe('id,accountId,amount,probability,type');
expect(getDefaultFields('Case')).toBe('id,accountId,contactId,priority,status,subject,type');
expect(getDefaultFields('Task')).toBe('id,subject,status,priority');
expect(getDefaultFields('Attachment')).toBe('id,name');
expect(getDefaultFields('User')).toBe('id,name,email');
});
});
describe('getQuery', () => {
it('should return query when the fields are comma separated', () => {
const options: IDataObject = {
fields: 'id,name,email',
};
const result = getQuery(options, 'Account', true);
expect(result).toBe('SELECT id,name,email FROM Account ');
});
it('should return query when the fields are strings in an array', () => {
const options: IDataObject = {
fields: ['id', 'name', 'email'],
};
const result = getQuery(options, 'Account', true);
expect(result).toBe('SELECT id,name,email FROM Account ');
});
it('should return query with default fields when the fields are missing', () => {
const options: IDataObject = {};
const result = getQuery(options, 'Account', true);
expect(result).toBe('SELECT id,name,type FROM Account ');
});
it('should return query with a condition', () => {
const options: IDataObject = {
fields: 'id,name,email',
conditionsUi: {
conditionValues: [{ field: 'id', operation: 'equal', value: '123' }],
},
};
const result = getQuery(options, 'Account', true);
expect(result).toBe('SELECT id,name,email FROM Account WHERE id = 123');
});
it('should return query with a limit', () => {
const options: IDataObject = {
fields: 'id,name,email',
};
const result = getQuery(options, 'Account', false, 5);
expect(result).toBe('SELECT id,name,email FROM Account LIMIT 5');
});
it('should return query with a condition and a limit', () => {
const options: IDataObject = {
fields: 'id,name,email',
conditionsUi: {
conditionValues: [{ field: 'id', operation: 'equal', value: '123' }],
},
};
const result = getQuery(options, 'Account', false, 5);
expect(result).toBe('SELECT id,name,email FROM Account WHERE id = 123 LIMIT 5');
});
});
describe('filterAndManageProcessedItems', () => {
it('should filter out already processed items', () => {
const responseData: IDataObject[] = [
{ Id: '001', Name: 'Item 1' },
{ Id: '002', Name: 'Item 2' },
{ Id: '003', Name: 'Item 3' },
];
const processedIds = ['002'];
const result = filterAndManageProcessedItems(responseData, processedIds);
expect(result.newItems).toEqual([
{ Id: '001', Name: 'Item 1' },
{ Id: '003', Name: 'Item 3' },
]);
// All processed IDs are kept, plus new items are added
expect(result.updatedProcessedIds).toEqual(['002', '001', '003']);
});
it('should handle empty response data', () => {
const responseData: IDataObject[] = [];
const processedIds = ['001', '002'];
const result = filterAndManageProcessedItems(responseData, processedIds);
expect(result.newItems).toEqual([]);
expect(result.updatedProcessedIds).toEqual(['001', '002']);
});
it('should handle empty processed IDs', () => {
const responseData: IDataObject[] = [
{ Id: '001', Name: 'Item 1' },
{ Id: '002', Name: 'Item 2' },
];
const processedIds: string[] = [];
const result = filterAndManageProcessedItems(responseData, processedIds);
expect(result.newItems).toEqual(responseData);
expect(result.updatedProcessedIds).toEqual(['001', '002']);
});
it('should handle all items already processed', () => {
const responseData: IDataObject[] = [
{ Id: '001', Name: 'Item 1' },
{ Id: '002', Name: 'Item 2' },
];
const processedIds = ['001', '002', '003'];
const result = filterAndManageProcessedItems(responseData, processedIds);
expect(result.newItems).toEqual([]);
// All processed IDs are kept since no new items were found
expect(result.updatedProcessedIds).toEqual(['001', '002', '003']);
});
it('should handle large numbers of processed IDs efficiently', () => {
// Create 995 existing processed IDs
const processedIds = Array.from({ length: 995 }, (_, i) => `existing-${i}`);
// Add 10 new items
const responseData: IDataObject[] = Array.from({ length: 10 }, (_, i) => ({
Id: `new-${i}`,
Name: `New Item ${i}`,
}));
const result = filterAndManageProcessedItems(responseData, processedIds);
expect(result.newItems).toHaveLength(10);
// Should keep all existing IDs + 10 new IDs (no artificial limit)
expect(result.updatedProcessedIds).toHaveLength(1005);
// Should keep all existing IDs + new IDs
expect(result.updatedProcessedIds.slice(0, 995)).toEqual(processedIds);
expect(result.updatedProcessedIds.slice(-10)).toEqual(
responseData.map((item) => item.Id as string),
);
});
it('should handle very large batches of new items', () => {
const processedIds = ['existing-1', 'existing-2'];
// Create 1005 new items
const responseData: IDataObject[] = Array.from({ length: 1005 }, (_, i) => ({
Id: `new-${i}`,
Name: `New Item ${i}`,
}));
const result = filterAndManageProcessedItems(responseData, processedIds);
expect(result.newItems).toHaveLength(1005);
// Should keep all existing IDs + all new IDs (no artificial limit)
expect(result.updatedProcessedIds).toHaveLength(1007);
// Should keep all existing IDs + all new IDs
const expectedIds = processedIds.concat(responseData.map((item) => item.Id as string));
expect(result.updatedProcessedIds).toEqual(expectedIds);
});
it('should trim processed IDs to MAX_IDS limit (10000)', () => {
// Create 9995 existing processed IDs
const processedIds = Array.from({ length: 9995 }, (_, i) => `existing-${i}`);
// Add 10 new items (total would be 10005)
const responseData: IDataObject[] = Array.from({ length: 10 }, (_, i) => ({
Id: `new-${i}`,
Name: `New Item ${i}`,
}));
const result = filterAndManageProcessedItems(responseData, processedIds);
expect(result.newItems).toHaveLength(10);
// Should be trimmed to exactly 10000 items (MAX_IDS limit)
expect(result.updatedProcessedIds).toHaveLength(10000);
// Should keep the last 10000 items (trimmed from the beginning)
const expectedIds = processedIds
.slice(-9990)
.concat(responseData.map((item) => item.Id as string));
expect(result.updatedProcessedIds).toEqual(expectedIds);
});
it('should trim processed IDs when exceeding MAX_IDS with large batch', () => {
// Create 5000 existing processed IDs
const processedIds = Array.from({ length: 5000 }, (_, i) => `existing-${i}`);
// Add 6000 new items (total would be 11000, exceeding MAX_IDS)
const responseData: IDataObject[] = Array.from({ length: 6000 }, (_, i) => ({
Id: `new-${i}`,
Name: `New Item ${i}`,
}));
const result = filterAndManageProcessedItems(responseData, processedIds);
expect(result.newItems).toHaveLength(6000);
// Should be trimmed to exactly 10000 items (MAX_IDS limit)
expect(result.updatedProcessedIds).toHaveLength(10000);
// Should keep the last 10000 items (all new items + last 4000 existing)
const expectedIds = processedIds
.slice(-4000)
.concat(responseData.map((item) => item.Id as string));
expect(result.updatedProcessedIds).toEqual(expectedIds);
});
it('should not trim when under MAX_IDS limit', () => {
// Create 100 existing processed IDs (well under limit)
const processedIds = Array.from({ length: 100 }, (_, i) => `existing-${i}`);
// Add 50 new items
const responseData: IDataObject[] = Array.from({ length: 50 }, (_, i) => ({
Id: `new-${i}`,
Name: `New Item ${i}`,
}));
const result = filterAndManageProcessedItems(responseData, processedIds);
expect(result.newItems).toHaveLength(50);
// Should keep all items since under limit
expect(result.updatedProcessedIds).toHaveLength(150);
// Should keep all existing IDs + all new IDs
const expectedIds = processedIds.concat(responseData.map((item) => item.Id as string));
expect(result.updatedProcessedIds).toEqual(expectedIds);
});
it('should handle duplicate IDs in response data correctly', () => {
const responseData: IDataObject[] = [
{ Id: '001', Name: 'Item 1' },
{ Id: '002', Name: 'Item 2' },
{ Id: '001', Name: 'Item 1 Duplicate' }, // Duplicate ID
];
const processedIds = ['003'];
const result = filterAndManageProcessedItems(responseData, processedIds);
// Should include both items with ID '001' since they're not in processedIds
expect(result.newItems).toEqual([
{ Id: '001', Name: 'Item 1' },
{ Id: '002', Name: 'Item 2' },
{ Id: '001', Name: 'Item 1 Duplicate' },
]);
expect(result.updatedProcessedIds).toEqual(['003', '001', '002', '001']);
});
it('should maintain order of processed IDs', () => {
const responseData: IDataObject[] = [
{ Id: '003', Name: 'Item 3' },
{ Id: '001', Name: 'Item 1' },
{ Id: '004', Name: 'Item 4' },
];
const processedIds = ['100', '200'];
const result = filterAndManageProcessedItems(responseData, processedIds);
expect(result.updatedProcessedIds).toEqual(['100', '200', '003', '001', '004']);
});
});
describe('salesforceApiRequest - JWT Authentication', () => {
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
let mockRequest: jest.Mock;
beforeEach(() => {
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
mockRequest = jest.fn();
mockExecuteFunctions.helpers.request = mockRequest;
jest.clearAllMocks();
// Setup default mocks
(mockJwt.sign as jest.Mock).mockReturnValue('mock-jwt-signature');
mockExecuteFunctions.getNodeParameter.mockImplementation((param: string) => {
if (param === 'authentication') return 'jwt';
return undefined;
});
});
afterEach(() => {
jest.resetAllMocks();
});
describe('JWT Authentication Flow', () => {
it('should authenticate using JWT with production environment', async () => {
const mockCredentials = {
clientId: 'test-client-id',
username: 'test@example.com',
privateKey: 'mock-private-key',
environment: 'production',
};
const mockResponse = {
access_token: 'mock-access-token',
instance_url: 'https://test.salesforce.com',
};
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
mockRequest.mockResolvedValue(mockResponse);
mockExecuteFunctions.logger = {
debug: jest.fn(),
} as any;
await salesforceApiRequest.call(mockExecuteFunctions, 'GET', '/test-endpoint', {}, {});
// Verify JWT signature generation
expect(mockJwt.sign as jest.Mock).toHaveBeenCalledWith(
{
iss: 'test-client-id',
sub: 'test@example.com',
aud: 'https://login.salesforce.com',
exp: 1640995200 + 3 * 60, // Current timestamp + 3 minutes
},
'mock-private-key',
{
algorithm: 'RS256',
header: {
alg: 'RS256',
},
},
);
// Verify token exchange request
expect(mockRequest).toHaveBeenCalledWith({
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
method: 'POST',
form: {
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
assertion: 'mock-jwt-signature',
},
uri: 'https://login.salesforce.com/services/oauth2/token',
json: true,
});
// Verify API request with bearer token
const expectedApiOptions = {
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer mock-access-token',
},
method: 'GET',
qs: {},
uri: 'https://test.salesforce.com/services/data/v59.0/test-endpoint',
json: true,
};
expect(mockRequest).toHaveBeenCalledWith(expectedApiOptions);
expect(mockExecuteFunctions.logger.debug).toHaveBeenCalledWith(
'Authentication for "Salesforce" node is using "jwt". Invoking URI https://test.salesforce.com/services/data/v59.0/test-endpoint',
);
});
it('should authenticate using JWT with sandbox environment', async () => {
const mockCredentials = {
clientId: 'sandbox-client-id',
username: 'sandbox@example.com',
privateKey: 'sandbox-private-key',
environment: 'sandbox',
};
const mockResponse = {
access_token: 'sandbox-access-token',
instance_url: 'https://test.my.salesforce.com',
};
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
mockRequest.mockResolvedValue(mockResponse);
mockExecuteFunctions.logger = {
debug: jest.fn(),
} as any;
await salesforceApiRequest.call(
mockExecuteFunctions,
'POST',
'/sandbox-endpoint',
{ data: 'test' },
{ param: 'value' },
);
// Verify JWT uses sandbox URL
expect(mockJwt.sign as jest.Mock).toHaveBeenCalledWith(
{
iss: 'sandbox-client-id',
sub: 'sandbox@example.com',
aud: 'https://test.salesforce.com',
exp: 1640995200 + 3 * 60,
},
'sandbox-private-key',
{
algorithm: 'RS256',
header: {
alg: 'RS256',
},
},
);
// Verify token exchange request uses sandbox URL
expect(mockRequest).toHaveBeenCalledWith({
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
method: 'POST',
form: {
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
assertion: 'mock-jwt-signature',
},
uri: 'https://test.salesforce.com/services/oauth2/token',
json: true,
});
});
it('should handle JWT token exchange with body and query parameters', async () => {
const mockCredentials = {
clientId: 'test-client-id',
username: 'test@example.com',
privateKey: 'mock-private-key',
environment: 'production',
};
const mockResponse = {
access_token: 'mock-access-token',
instance_url: 'https://test.salesforce.com',
};
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
mockRequest.mockResolvedValue(mockResponse);
mockExecuteFunctions.logger = {
debug: jest.fn(),
} as any;
const testBody = { name: 'Test Account', type: 'Customer' };
const testQs = { fields: 'Id,Name', limit: '10' };
await salesforceApiRequest.call(
mockExecuteFunctions,
'POST',
'/test-endpoint',
testBody,
testQs,
);
// Verify API request includes body and query parameters
const expectedApiOptions = {
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer mock-access-token',
},
method: 'POST',
body: testBody,
qs: testQs,
uri: 'https://test.salesforce.com/services/data/v59.0/test-endpoint',
json: true,
};
expect(mockRequest).toHaveBeenCalledWith(expectedApiOptions);
});
it('should handle custom URI parameter', async () => {
const mockCredentials = {
clientId: 'test-client-id',
username: 'test@example.com',
privateKey: 'mock-private-key',
environment: 'production',
};
const mockResponse = {
access_token: 'mock-access-token',
instance_url: 'https://test.salesforce.com',
};
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
mockRequest.mockResolvedValue(mockResponse);
mockExecuteFunctions.logger = {
debug: jest.fn(),
} as any;
await salesforceApiRequest.call(
mockExecuteFunctions,
'GET',
'/default-endpoint',
{},
{},
'/custom-uri',
);
// Verify custom URI is used instead of endpoint
const expectedApiOptions = {
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer mock-access-token',
},
method: 'GET',
qs: {},
uri: 'https://test.salesforce.com/services/data/v59.0/custom-uri',
json: true,
};
expect(mockRequest).toHaveBeenCalledWith(expectedApiOptions);
});
it('should merge additional options', async () => {
const mockCredentials = {
clientId: 'test-client-id',
username: 'test@example.com',
privateKey: 'mock-private-key',
environment: 'production',
};
const mockResponse = {
access_token: 'mock-access-token',
instance_url: 'https://test.salesforce.com',
};
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
mockRequest.mockResolvedValue(mockResponse);
mockExecuteFunctions.logger = {
debug: jest.fn(),
} as any;
const additionalOptions = {
timeout: 30000,
resolveWithFullResponse: true,
};
await salesforceApiRequest.call(
mockExecuteFunctions,
'GET',
'/test-endpoint',
{},
{},
undefined,
additionalOptions,
);
// Verify additional options are merged
const expectedApiOptions = {
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer mock-access-token',
},
method: 'GET',
qs: {},
uri: 'https://test.salesforce.com/services/data/v59.0/test-endpoint',
json: true,
timeout: 30000,
resolveWithFullResponse: true,
};
expect(mockRequest).toHaveBeenCalledWith(expectedApiOptions);
});
});
describe('JWT Authentication Error Handling', () => {
it('should handle credential retrieval errors', async () => {
const credentialError = new Error('Failed to get credentials');
mockExecuteFunctions.getCredentials.mockRejectedValue(credentialError);
mockExecuteFunctions.getNode.mockReturnValue({
id: 'test-node',
name: 'Test Node',
type: 'n8n-nodes-base.salesforce',
typeVersion: 1,
position: [0, 0],
parameters: {},
});
await expect(
salesforceApiRequest.call(mockExecuteFunctions, 'GET', '/test-endpoint', {}, {}),
).rejects.toThrow(NodeApiError);
expect(mockExecuteFunctions.getCredentials).toHaveBeenCalledWith('salesforceJwtApi');
});
it('should handle JWT token exchange errors', async () => {
const mockCredentials = {
clientId: 'test-client-id',
username: 'test@example.com',
privateKey: 'mock-private-key',
environment: 'production',
};
const tokenError = new Error('Invalid JWT signature');
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
mockRequest.mockRejectedValue(tokenError);
mockExecuteFunctions.getNode.mockReturnValue({
id: 'test-node',
name: 'Test Node',
type: 'n8n-nodes-base.salesforce',
typeVersion: 1,
position: [0, 0],
parameters: {},
});
await expect(
salesforceApiRequest.call(mockExecuteFunctions, 'GET', '/test-endpoint', {}, {}),
).rejects.toThrow(NodeApiError);
});
it('should handle API request errors after successful authentication', async () => {
const mockCredentials = {
clientId: 'test-client-id',
username: 'test@example.com',
privateKey: 'mock-private-key',
environment: 'production',
};
const mockResponse = {
access_token: 'mock-access-token',
instance_url: 'https://test.salesforce.com',
};
const apiError = new Error('API rate limit exceeded');
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
mockRequest
.mockResolvedValueOnce(mockResponse) // First call succeeds (token exchange)
.mockRejectedValueOnce(apiError); // Second call fails (API request)
mockExecuteFunctions.getNode.mockReturnValue({
id: 'test-node',
name: 'Test Node',
type: 'n8n-nodes-base.salesforce',
typeVersion: 1,
position: [0, 0],
parameters: {},
});
mockExecuteFunctions.logger = {
debug: jest.fn(),
} as any;
await expect(
salesforceApiRequest.call(mockExecuteFunctions, 'GET', '/test-endpoint', {}, {}),
).rejects.toThrow(NodeApiError);
// Verify that token exchange succeeded but API request failed
expect(mockRequest).toHaveBeenCalledTimes(2);
});
it('should handle missing instance_url in token response', async () => {
const mockCredentials = {
clientId: 'test-client-id',
username: 'test@example.com',
privateKey: 'mock-private-key',
environment: 'production',
};
const mockResponse = {
access_token: 'mock-access-token',
// Missing instance_url
};
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
mockRequest.mockResolvedValue(mockResponse);
mockExecuteFunctions.getNode.mockReturnValue({
id: 'test-node',
name: 'Test Node',
type: 'n8n-nodes-base.salesforce',
typeVersion: 1,
position: [0, 0],
parameters: {},
});
mockExecuteFunctions.logger = {
debug: jest.fn(),
} as any;
// Should not throw error but handle gracefully
await salesforceApiRequest.call(mockExecuteFunctions, 'GET', '/test-endpoint', {}, {});
// Verify API call was made with undefined instance_url
expect(mockRequest).toHaveBeenCalledTimes(2);
});
});
describe('JWT Signature Generation', () => {
it('should generate correct JWT payload for production environment', async () => {
const mockCredentials = {
clientId: 'prod-client-id',
username: 'prod@example.com',
privateKey: 'prod-private-key',
environment: 'production',
};
const mockResponse = {
access_token: 'prod-access-token',
instance_url: 'https://prod.salesforce.com',
};
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
mockRequest.mockResolvedValue(mockResponse);
mockExecuteFunctions.logger = {
debug: jest.fn(),
} as any;
await salesforceApiRequest.call(mockExecuteFunctions, 'GET', '/test-endpoint', {}, {});
expect(mockJwt.sign as jest.Mock).toHaveBeenCalledWith(
{
iss: 'prod-client-id',
sub: 'prod@example.com',
aud: 'https://login.salesforce.com',
exp: 1640995200 + 180, // 3 minutes = 180 seconds
},
'prod-private-key',
{
algorithm: 'RS256',
header: {
alg: 'RS256',
},
},
);
});
it('should generate correct JWT payload for sandbox environment', async () => {
const mockCredentials = {
clientId: 'sandbox-client-id',
username: 'sandbox@example.com',
privateKey: 'sandbox-private-key',
environment: 'sandbox',
};
const mockResponse = {
access_token: 'sandbox-access-token',
instance_url: 'https://sandbox.salesforce.com',
};
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
mockRequest.mockResolvedValue(mockResponse);
mockExecuteFunctions.logger = {
debug: jest.fn(),
} as any;
await salesforceApiRequest.call(mockExecuteFunctions, 'GET', '/test-endpoint', {}, {});
expect(mockJwt.sign as jest.Mock).toHaveBeenCalledWith(
{
iss: 'sandbox-client-id',
sub: 'sandbox@example.com',
aud: 'https://test.salesforce.com', // Sandbox uses test.salesforce.com
exp: 1640995200 + 180,
},
'sandbox-private-key',
{
algorithm: 'RS256',
header: {
alg: 'RS256',
},
},
);
});
it('should use RS256 algorithm and proper header configuration', async () => {
const mockCredentials = {
clientId: 'test-client-id',
username: 'test@example.com',
privateKey: 'test-private-key',
environment: 'production',
};
const mockResponse = {
access_token: 'test-access-token',
instance_url: 'https://test.salesforce.com',
};
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
mockRequest.mockResolvedValue(mockResponse);
mockExecuteFunctions.logger = {
debug: jest.fn(),
} as any;
await salesforceApiRequest.call(mockExecuteFunctions, 'GET', '/test-endpoint', {}, {});
const jwtOptions = (mockJwt.sign as jest.Mock).mock.calls[0][2];
expect(jwtOptions.algorithm).toBe('RS256');
expect(jwtOptions.header?.alg).toBe('RS256');
});
});
});
});
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,586 @@
import { mockDeep } from 'jest-mock-extended';
import { DateTime } from 'luxon';
import type { IPollFunctions, INode, ILoadOptionsFunctions, IDataObject } from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow';
import * as GenericFunctions from '../GenericFunctions';
import { SalesforceTrigger } from '../SalesforceTrigger.node';
jest.mock('../GenericFunctions', () => ({
getQuery: jest.fn(),
salesforceApiRequest: jest.fn(),
salesforceApiRequestAllItems: jest.fn(),
sortOptions: jest.fn(),
getPollStartDate: jest.fn(),
filterAndManageProcessedItems: jest.fn(),
}));
describe('SalesforceTrigger', () => {
let trigger: SalesforceTrigger;
let mockPollFunctions: jest.Mocked<IPollFunctions>;
let mockNode: INode;
const getQuerySpy = jest.spyOn(GenericFunctions, 'getQuery');
const salesforceApiRequestSpy = jest.spyOn(GenericFunctions, 'salesforceApiRequest');
const salesforceApiRequestAllItemsSpy = jest.spyOn(
GenericFunctions,
'salesforceApiRequestAllItems',
);
const sortOptionsSpy = jest.spyOn(GenericFunctions, 'sortOptions');
const getPollStartDateSpy = jest.spyOn(GenericFunctions, 'getPollStartDate');
const filterAndManageProcessedItemsSpy = jest.spyOn(
GenericFunctions,
'filterAndManageProcessedItems',
);
beforeEach(() => {
trigger = new SalesforceTrigger();
mockPollFunctions = mockDeep<IPollFunctions>();
mockNode = {
id: 'test-node-id',
name: 'Salesforce Trigger Test',
type: 'n8n-nodes-base.salesforceTrigger',
typeVersion: 1,
position: [0, 0],
parameters: {},
};
jest.clearAllMocks();
mockPollFunctions.getNode.mockReturnValue(mockNode);
mockPollFunctions.getWorkflowStaticData.mockReturnValue({});
mockPollFunctions.getMode.mockReturnValue('trigger');
mockPollFunctions.getWorkflow.mockReturnValue({ id: 'test-workflow', active: true });
mockPollFunctions.logger.error = jest.fn();
(mockPollFunctions.helpers.returnJsonArray as jest.Mock).mockImplementation((data: unknown[]) =>
data.map((item: unknown, index: number) => ({ json: item, pairedItem: { item: index } })),
);
});
afterEach(() => {
jest.resetAllMocks();
});
describe('Methods', () => {
it('should have correct load options methods', () => {
expect(trigger.methods?.loadOptions?.getCustomObjects).toBeDefined();
});
describe('getCustomObjects', () => {
it('should return custom objects with sorted options', async () => {
const mockLoadOptionsFunctions = mockDeep<ILoadOptionsFunctions>();
const mockSobjects = {
sobjects: [
{ name: 'Account', label: 'Account', custom: false },
{ name: 'CustomObject1__c', label: 'Custom Object 1', custom: true },
{ name: 'Contact', label: 'Contact', custom: false },
{ name: 'CustomObject2__c', label: 'Custom Object 2', custom: true },
],
};
salesforceApiRequestSpy.mockResolvedValue(mockSobjects);
sortOptionsSpy.mockImplementation((options) => {
return options.sort((a, b) => a.name.localeCompare(b.name));
});
const result =
await trigger.methods!.loadOptions!.getCustomObjects.call(mockLoadOptionsFunctions);
expect(salesforceApiRequestSpy).toHaveBeenCalledWith('GET', '/sobjects');
expect(sortOptionsSpy).toHaveBeenCalled();
expect(result).toEqual([
{ name: 'Custom Object 1', value: 'CustomObject1__c' },
{ name: 'Custom Object 2', value: 'CustomObject2__c' },
]);
});
it('should handle empty sobjects response', async () => {
const mockLoadOptionsFunctions = mockDeep<ILoadOptionsFunctions>();
const mockSobjects = { sobjects: [] };
salesforceApiRequestSpy.mockResolvedValue(mockSobjects);
const result =
await trigger.methods!.loadOptions!.getCustomObjects.call(mockLoadOptionsFunctions);
expect(result).toEqual([]);
});
it('should filter out non-custom objects', async () => {
const mockLoadOptionsFunctions = mockDeep<ILoadOptionsFunctions>();
const mockSobjects = {
sobjects: [
{ name: 'Account', label: 'Account', custom: false },
{ name: 'Contact', label: 'Contact', custom: false },
{ name: 'Lead', label: 'Lead', custom: false },
],
};
salesforceApiRequestSpy.mockResolvedValue(mockSobjects);
const result =
await trigger.methods!.loadOptions!.getCustomObjects.call(mockLoadOptionsFunctions);
expect(result).toEqual([]);
});
});
});
describe('Poll Function - Parameter Setup', () => {
beforeEach(() => {
mockPollFunctions.getNodeParameter.mockImplementation((paramName: string) => {
const params: Record<string, unknown> = {
triggerOn: 'accountCreated',
customObject: 'CustomObject__c',
};
return params[paramName] ?? '';
});
});
it('should handle standard object creation trigger', async () => {
const mockWorkflowData: IDataObject = {};
mockPollFunctions.getWorkflowStaticData.mockReturnValue(mockWorkflowData);
getPollStartDateSpy.mockReturnValue('2023-01-01T00:00:00.000Z');
getQuerySpy.mockReturnValue(
'SELECT id,name,type FROM Account WHERE CreatedDate >= 2023-01-01T00:00:00.000Z',
);
salesforceApiRequestAllItemsSpy.mockResolvedValue([
{ Id: '001000000000001', Name: 'Test Account 1' },
{ Id: '001000000000002', Name: 'Test Account 2' },
]);
filterAndManageProcessedItemsSpy.mockReturnValue({
newItems: [
{ Id: '001000000000001', Name: 'Test Account 1' },
{ Id: '001000000000002', Name: 'Test Account 2' },
],
updatedProcessedIds: ['001000000000001', '001000000000002'],
});
const result = await trigger.poll.call(mockPollFunctions);
expect(getPollStartDateSpy).toHaveBeenCalledWith(undefined);
expect(getQuerySpy).toHaveBeenCalledWith(
expect.objectContaining({
conditionsUi: {
conditionValues: expect.arrayContaining([
expect.objectContaining({
field: 'CreatedDate',
operation: '>=',
value: '2023-01-01T00:00:00.000Z',
}),
]),
},
}),
'Account',
true,
);
expect(salesforceApiRequestAllItemsSpy).toHaveBeenCalledWith(
'records',
'GET',
'/query',
{},
expect.objectContaining({
q: 'SELECT id,name,type FROM Account WHERE CreatedDate >= 2023-01-01T00:00:00.000Z',
}),
);
expect(filterAndManageProcessedItemsSpy).toHaveBeenCalled();
expect(result).toBeDefined();
expect(result![0]).toHaveLength(2);
expect(result![0][0].json).toEqual({ Id: '001000000000001', Name: 'Test Account 1' });
});
it('should handle standard object update trigger', async () => {
mockPollFunctions.getNodeParameter.mockImplementation((paramName: string) => {
if (paramName === 'triggerOn') return 'accountUpdated';
return '';
});
const mockWorkflowData: IDataObject = { lastTimeChecked: '2023-01-01T00:00:00.000Z' };
mockPollFunctions.getWorkflowStaticData.mockReturnValue(mockWorkflowData);
getPollStartDateSpy.mockReturnValue('2022-12-31T23:45:00.000Z');
getQuerySpy.mockReturnValue(
'SELECT id,name,type FROM Account WHERE LastModifiedDate >= 2022-12-31T23:45:00.000Z',
);
salesforceApiRequestAllItemsSpy.mockResolvedValue([
{ Id: '001000000000001', Name: 'Updated Account' },
]);
filterAndManageProcessedItemsSpy.mockReturnValue({
newItems: [{ Id: '001000000000001', Name: 'Updated Account' }],
updatedProcessedIds: ['001000000000001'],
});
const result = await trigger.poll.call(mockPollFunctions);
expect(getQuerySpy).toHaveBeenCalledWith(
expect.objectContaining({
conditionsUi: {
conditionValues: expect.arrayContaining([
expect.objectContaining({
field: 'LastModifiedDate',
operation: '>=',
value: '2022-12-31T23:45:00.000Z',
}),
expect.objectContaining({
field: 'CreatedDate',
operation: '<',
value: '2022-12-31T23:45:00.000Z',
}),
]),
},
}),
'Account',
true,
);
expect(result).toBeDefined();
expect(result![0]).toHaveLength(1);
});
it('should handle custom object triggers', async () => {
mockPollFunctions.getNodeParameter.mockImplementation((paramName: string) => {
if (paramName === 'triggerOn') return 'customObjectCreated';
if (paramName === 'customObject') return 'CustomObject__c';
return '';
});
const mockWorkflowData: IDataObject = {};
mockPollFunctions.getWorkflowStaticData.mockReturnValue(mockWorkflowData);
getPollStartDateSpy.mockReturnValue('2023-01-01T00:00:00.000Z');
getQuerySpy.mockReturnValue(
'SELECT id FROM CustomObject__c WHERE CreatedDate >= 2023-01-01T00:00:00.000Z',
);
salesforceApiRequestAllItemsSpy.mockResolvedValue([
{ Id: 'a00000000000001', Name: 'Custom Object 1' },
]);
filterAndManageProcessedItemsSpy.mockReturnValue({
newItems: [{ Id: 'a00000000000001', Name: 'Custom Object 1' }],
updatedProcessedIds: ['a00000000000001'],
});
const result = await trigger.poll.call(mockPollFunctions);
expect(getQuerySpy).toHaveBeenCalledWith(expect.any(Object), 'CustomObject__c', true);
expect(result).toBeDefined();
expect(result![0]).toHaveLength(1);
});
it('should initialize processedIds when not present', async () => {
const mockWorkflowData: IDataObject = {};
mockPollFunctions.getWorkflowStaticData.mockReturnValue(mockWorkflowData);
getPollStartDateSpy.mockReturnValue('2023-01-01T00:00:00.000Z');
getQuerySpy.mockReturnValue('SELECT id,name,type FROM Account');
salesforceApiRequestAllItemsSpy.mockResolvedValue([]);
filterAndManageProcessedItemsSpy.mockReturnValue({
newItems: [],
updatedProcessedIds: [],
});
await trigger.poll.call(mockPollFunctions);
expect(mockWorkflowData.processedIds).toEqual([]);
});
it('should update lastTimeChecked after polling', async () => {
const mockWorkflowData: IDataObject = {};
mockPollFunctions.getWorkflowStaticData.mockReturnValue(mockWorkflowData);
getPollStartDateSpy.mockReturnValue('2023-01-01T00:00:00.000Z');
getQuerySpy.mockReturnValue('SELECT id,name,type FROM Account');
salesforceApiRequestAllItemsSpy.mockResolvedValue([]);
filterAndManageProcessedItemsSpy.mockReturnValue({
newItems: [],
updatedProcessedIds: [],
});
await trigger.poll.call(mockPollFunctions);
expect(mockWorkflowData.lastTimeChecked).toBeDefined();
expect(DateTime.fromISO(mockWorkflowData.lastTimeChecked as string).isValid).toBe(true);
});
});
describe('Poll Function - Manual Mode', () => {
beforeEach(() => {
mockPollFunctions.getMode.mockReturnValue('manual');
mockPollFunctions.getNodeParameter.mockImplementation((paramName: string) => {
if (paramName === 'triggerOn') return 'accountCreated';
return '';
});
});
it('should use limit 1 in manual mode', async () => {
const mockWorkflowData: IDataObject = {};
mockPollFunctions.getWorkflowStaticData.mockReturnValue(mockWorkflowData);
getQuerySpy.mockReturnValue('SELECT id,name,type FROM Account LIMIT 1');
salesforceApiRequestAllItemsSpy.mockResolvedValue([
{ Id: '001000000000001', Name: 'Test Account' },
]);
filterAndManageProcessedItemsSpy.mockReturnValue({
newItems: [{ Id: '001000000000001', Name: 'Test Account' }],
updatedProcessedIds: ['001000000000001'],
});
const result = await trigger.poll.call(mockPollFunctions);
expect(getQuerySpy).toHaveBeenCalledWith(
expect.objectContaining({
conditionsUi: {
conditionValues: [],
},
}),
'Account',
false,
1,
);
expect(result).toBeDefined();
expect(result![0]).toHaveLength(1);
});
it('should not add date conditions in manual mode', async () => {
const mockWorkflowData: IDataObject = {};
mockPollFunctions.getWorkflowStaticData.mockReturnValue(mockWorkflowData);
getQuerySpy.mockReturnValue('SELECT id,name,type FROM Account LIMIT 1');
salesforceApiRequestAllItemsSpy.mockResolvedValue([]);
filterAndManageProcessedItemsSpy.mockReturnValue({
newItems: [],
updatedProcessedIds: [],
});
await trigger.poll.call(mockPollFunctions);
expect(getQuerySpy).toHaveBeenCalledWith(
expect.objectContaining({
conditionsUi: {
conditionValues: [],
},
}),
'Account',
false,
1,
);
});
});
describe('Poll Function - Error Handling', () => {
beforeEach(() => {
mockPollFunctions.getNodeParameter.mockImplementation((paramName: string) => {
if (paramName === 'triggerOn') return 'accountCreated';
return '';
});
});
it('should throw NodeApiError for API request errors', async () => {
const mockWorkflowData: IDataObject = {};
mockPollFunctions.getWorkflowStaticData.mockReturnValue(mockWorkflowData);
getPollStartDateSpy.mockReturnValue('2023-01-01T00:00:00.000Z');
getQuerySpy.mockReturnValue('SELECT id,name,type FROM Account');
salesforceApiRequestAllItemsSpy.mockRejectedValue(new Error('API Error'));
await expect(trigger.poll.call(mockPollFunctions)).rejects.toThrow(NodeApiError);
});
it('should throw error in manual mode even with lastTimeChecked', async () => {
mockPollFunctions.getMode.mockReturnValue('manual');
const mockWorkflowData: IDataObject = { lastTimeChecked: '2023-01-01T00:00:00.000Z' };
mockPollFunctions.getWorkflowStaticData.mockReturnValue(mockWorkflowData);
getPollStartDateSpy.mockReturnValue('2023-01-01T00:00:00.000Z');
getQuerySpy.mockReturnValue('SELECT id,name,type FROM Account LIMIT 1');
salesforceApiRequestAllItemsSpy.mockRejectedValue(new Error('API Error'));
await expect(trigger.poll.call(mockPollFunctions)).rejects.toThrow('API Error');
});
it('should log error and rethrow in trigger mode with lastTimeChecked', async () => {
const mockWorkflowData: IDataObject = { lastTimeChecked: '2023-01-01T00:00:00.000Z' };
mockPollFunctions.getWorkflowStaticData.mockReturnValue(mockWorkflowData);
const testError = new Error('API Error');
getPollStartDateSpy.mockReturnValue('2022-12-31T23:45:00.000Z');
getQuerySpy.mockReturnValue('SELECT id,name,type FROM Account');
salesforceApiRequestAllItemsSpy.mockRejectedValue(testError);
await expect(trigger.poll.call(mockPollFunctions)).rejects.toThrow('API Error');
expect(mockPollFunctions.logger.error).toHaveBeenCalledWith(
expect.stringContaining(
"There was a problem in 'Salesforce Trigger Test' node in workflow 'test-workflow'",
),
expect.objectContaining({
node: 'Salesforce Trigger Test',
workflowId: 'test-workflow',
error: testError,
}),
);
});
it('should throw error immediately when no lastTimeChecked exists', async () => {
const mockWorkflowData: IDataObject = {};
mockPollFunctions.getWorkflowStaticData.mockReturnValue(mockWorkflowData);
const testError = new Error('API Error');
getPollStartDateSpy.mockReturnValue('2023-01-01T00:00:00.000Z');
getQuerySpy.mockReturnValue('SELECT id,name,type FROM Account');
salesforceApiRequestAllItemsSpy.mockRejectedValue(testError);
await expect(trigger.poll.call(mockPollFunctions)).rejects.toThrow('API Error');
expect(mockPollFunctions.logger.error).not.toHaveBeenCalled();
});
});
describe('Poll Function - Edge Cases', () => {
beforeEach(() => {
mockPollFunctions.getNodeParameter.mockImplementation((paramName: string) => {
if (paramName === 'triggerOn') return 'accountCreated';
return '';
});
});
it('should return null when no data is found', async () => {
const mockWorkflowData: IDataObject = {};
mockPollFunctions.getWorkflowStaticData.mockReturnValue(mockWorkflowData);
getPollStartDateSpy.mockReturnValue('2023-01-01T00:00:00.000Z');
getQuerySpy.mockReturnValue('SELECT id,name,type FROM Account');
salesforceApiRequestAllItemsSpy.mockResolvedValue([]);
filterAndManageProcessedItemsSpy.mockReturnValue({
newItems: [],
updatedProcessedIds: [],
});
const result = await trigger.poll.call(mockPollFunctions);
expect(result).toBeNull();
expect(mockWorkflowData.lastTimeChecked).toBeDefined();
});
it('should return null when no new items after filtering', async () => {
const mockWorkflowData: IDataObject = { processedIds: ['001000000000001'] };
mockPollFunctions.getWorkflowStaticData.mockReturnValue(mockWorkflowData);
getPollStartDateSpy.mockReturnValue('2023-01-01T00:00:00.000Z');
getQuerySpy.mockReturnValue('SELECT id,name,type FROM Account');
salesforceApiRequestAllItemsSpy.mockResolvedValue([
{ Id: '001000000000001', Name: 'Already Processed Account' },
]);
filterAndManageProcessedItemsSpy.mockReturnValue({
newItems: [],
updatedProcessedIds: ['001000000000001'],
});
const result = await trigger.poll.call(mockPollFunctions);
expect(result).toBeNull();
});
it('should handle null/undefined responseData', async () => {
const mockWorkflowData: IDataObject = {};
mockPollFunctions.getWorkflowStaticData.mockReturnValue(mockWorkflowData);
getPollStartDateSpy.mockReturnValue('2023-01-01T00:00:00.000Z');
getQuerySpy.mockReturnValue('SELECT id,name,type FROM Account');
salesforceApiRequestAllItemsSpy.mockResolvedValue(null);
const result = await trigger.poll.call(mockPollFunctions);
expect(result).toBeNull();
expect(mockWorkflowData.lastTimeChecked).toBeDefined();
});
it('should handle empty string triggerOn parameter', async () => {
mockPollFunctions.getNodeParameter.mockImplementation((paramName: string) => {
if (paramName === 'triggerOn') return '';
return '';
});
const mockWorkflowData: IDataObject = {};
mockPollFunctions.getWorkflowStaticData.mockReturnValue(mockWorkflowData);
getPollStartDateSpy.mockReturnValue('2023-01-01T00:00:00.000Z');
getQuerySpy.mockReturnValue('SELECT id FROM LIMIT 1');
salesforceApiRequestAllItemsSpy.mockResolvedValue([]);
filterAndManageProcessedItemsSpy.mockReturnValue({
newItems: [],
updatedProcessedIds: [],
});
await trigger.poll.call(mockPollFunctions);
// When triggerOn is empty, it results in 'Updated' operation and empty resource name
expect(getQuerySpy).toHaveBeenCalledWith(
expect.objectContaining({
conditionsUi: expect.objectContaining({
conditionValues: expect.arrayContaining([
expect.objectContaining({
field: 'LastModifiedDate',
}),
]),
}),
}),
'', // Empty resource name
true,
);
});
});
describe('Poll Function - Date Handling', () => {
beforeEach(() => {
mockPollFunctions.getNodeParameter.mockImplementation((paramName: string) => {
if (paramName === 'triggerOn') return 'leadUpdated';
return '';
});
});
it('should use getPollStartDate with safety margin', async () => {
const mockWorkflowData: IDataObject = { lastTimeChecked: '2023-01-01T12:00:00.000Z' };
mockPollFunctions.getWorkflowStaticData.mockReturnValue(mockWorkflowData);
getPollStartDateSpy.mockReturnValue('2023-01-01T11:45:00.000Z');
getQuerySpy.mockReturnValue('SELECT id FROM Lead');
salesforceApiRequestAllItemsSpy.mockResolvedValue([]);
filterAndManageProcessedItemsSpy.mockReturnValue({
newItems: [],
updatedProcessedIds: [],
});
await trigger.poll.call(mockPollFunctions);
expect(getPollStartDateSpy).toHaveBeenCalledWith('2023-01-01T12:00:00.000Z');
});
it('should set endDate to current time', async () => {
const mockWorkflowData: IDataObject = {};
mockPollFunctions.getWorkflowStaticData.mockReturnValue(mockWorkflowData);
const startTime = DateTime.now();
getPollStartDateSpy.mockReturnValue('2023-01-01T00:00:00.000Z');
getQuerySpy.mockReturnValue('SELECT id FROM Lead');
salesforceApiRequestAllItemsSpy.mockResolvedValue([]);
filterAndManageProcessedItemsSpy.mockReturnValue({
newItems: [],
updatedProcessedIds: [],
});
await trigger.poll.call(mockPollFunctions);
const endTime = DateTime.now();
const lastTimeChecked = DateTime.fromISO(mockWorkflowData.lastTimeChecked as string);
expect(lastTimeChecked >= startTime).toBe(true);
expect(lastTimeChecked <= endTime).toBe(true);
});
});
});
@@ -0,0 +1,175 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
import accountDetails from './fixtures/account-details.json';
import accounts from './fixtures/accounts.json';
import opportunitiesSummary from './fixtures/opportunities-summary.json';
import opportunities from './fixtures/opportunities.json';
import opportunityDetails from './fixtures/opportunity-details.json';
import taskDetails from './fixtures/task-details.json';
import taskSummary from './fixtures/task-summary.json';
import tasks from './fixtures/tasks.json';
import userDetails from './fixtures/user-details.json';
import users from './fixtures/users.json';
describe('Salesforce Node', () => {
const credentials = {
salesforceOAuth2Api: {
scope: 'full refresh_token',
oauthTokenData: {
access_token: 'ACCESSTOKEN',
instance_url: 'https://salesforce.instance',
},
},
};
const salesforceNock = nock('https://salesforce.instance/services/data/v59.0');
describe('users', () => {
beforeAll(() => {
salesforceNock
.get('/query')
.query({
q: 'SELECT id,name,email FROM User ',
})
.reply(200, { records: users })
.get('/sobjects/user/id1')
.reply(200, userDetails);
});
afterAll(() => salesforceNock.done());
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['users.workflow.json'],
});
});
describe('tasks', () => {
beforeAll(() => {
salesforceNock
.post('/sobjects/task', {
Status: 'In Progress',
Subject: 'Email',
Description: 'Sample description',
})
.reply(200, { id: 'id1', success: true, errors: [] })
.get('/query')
.query({
q: 'SELECT id,subject,status,priority FROM Task ',
})
.reply(200, { records: tasks })
.get('/sobjects/task/id1')
.reply(200, taskDetails)
.get('/sobjects/task')
.reply(200, taskSummary)
.patch('/sobjects/task/id1', { Description: 'New description' })
.reply(200, { success: true, errors: [] })
.delete('/sobjects/task/id1')
.reply(200, { success: true, errors: [] });
});
afterAll(() => salesforceNock.done());
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['tasks.workflow.json'],
});
});
describe('accounts', () => {
beforeAll(() => {
salesforceNock
.post('/sobjects/account', { Name: 'Test Account' })
.reply(200, { id: 'id1', success: true, errors: [] })
.get('/query')
.query({
q: 'SELECT id,name,type FROM Account ',
})
.reply(200, { records: accounts })
.post('/sobjects/note', { Title: 'New note', ParentId: 'id1' })
.reply(200, {
id: 'noteid1',
success: true,
errors: [],
})
.get('/sobjects/account/id1')
.reply(200, accountDetails)
.patch('/sobjects/account/id1', { Website: 'https://foo.bar.baz' })
.reply(200, { success: true, errors: [] })
.patch('/sobjects/account/Id/id1', { Name: 'New account' })
.reply(200, { id: 'id1', created: false, success: true, errors: [] })
.delete('/sobjects/account/id1')
.reply(200, { success: true, errors: [] });
});
afterAll(() => salesforceNock.done());
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['accounts.workflow.json'],
});
});
describe('search', () => {
beforeAll(() => {
salesforceNock
.get('/query')
.query({
q: 'SELECT id, name, type FROM Account',
})
.reply(200, { records: accounts });
});
afterAll(() => salesforceNock.done());
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['search.workflow.json'],
});
});
describe('opportunities', () => {
beforeAll(() => {
salesforceNock
.post('/sobjects/opportunity', {
Name: 'New Opportunity',
CloseDate: '2025-01-01T00:00:00',
StageName: 'Prospecting',
Amount: 1000,
})
.reply(200, { id: 'id1', success: true, errors: [] })
.get('/query')
.query({
q: 'SELECT id,accountId,amount,probability,type FROM Opportunity ',
})
.reply(200, { records: opportunities })
.post('/sobjects/note', { Title: 'New Note', ParentId: 'id1' })
.reply(200, {
id: 'noteid1',
success: true,
errors: [],
})
.get('/sobjects/opportunity/id1')
.reply(200, opportunityDetails)
.patch('/sobjects/opportunity/id1', { Amount: 123 })
.reply(200, { success: true, errors: [] })
.patch('/sobjects/opportunity/Name/SomeName', {
CloseDate: '2025-01-01T00:00:00',
StageName: 'Prospecting',
})
.reply(200, { id: 'id1', created: false, success: true, errors: [] })
.delete('/sobjects/opportunity/id1')
.reply(200, { success: true, errors: [] })
.get('/sobjects/opportunity')
.reply(200, opportunitiesSummary);
});
afterAll(() => salesforceNock.done());
new NodeTestHarness().setupTests({
credentials,
workflowFiles: ['opportunities.workflow.json'],
});
});
});
@@ -0,0 +1,506 @@
{
"name": "accounts.workflow",
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [-240, 700],
"id": "80ee49ee-d695-41d8-9f94-d59a499d46c1",
"name": "When clicking Execute workflow"
},
{
"parameters": {
"resource": "account",
"name": "Test Account",
"additionalFields": {}
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [-20, 100],
"id": "9256d15e-a14f-4b30-8600-99b64d2635d6",
"name": "Salesforce",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [200, 100],
"id": "53d96738-6778-410c-a1cd-e552fedc0f8a",
"name": "No Operation, do nothing"
},
{
"parameters": {
"resource": "account",
"operation": "getAll",
"returnAll": true,
"options": {}
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [-20, 300],
"id": "9369fffa-6679-4065-8748-7ba2449eecb7",
"name": "Salesforce1",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [200, 300],
"id": "55813438-79eb-4513-9586-7ac3213c7ac7",
"name": "No Operation, do nothing1"
},
{
"parameters": {
"resource": "account",
"operation": "addNote",
"accountId": "id1",
"title": "New note",
"options": {}
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [-20, 500],
"id": "387806dc-9f87-43fd-9e7d-ecf8619c327d",
"name": "Salesforce2",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [200, 500],
"id": "0a6bce2c-55b0-4b8f-a817-ce8d47723e95",
"name": "No Operation, do nothing2"
},
{
"parameters": {
"resource": "account",
"operation": "get",
"accountId": "id1"
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [-20, 700],
"id": "31060d8f-2353-44e1-83c1-86c626125e72",
"name": "Salesforce3",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [200, 700],
"id": "d9481d27-3cb0-4809-a9a4-d91cb5b7d37c",
"name": "No Operation, do nothing3"
},
{
"parameters": {
"resource": "account",
"operation": "update",
"accountId": "id1",
"updateFields": {
"website": "https://foo.bar.baz"
}
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [-20, 900],
"id": "294c3e5b-0e5a-4b13-8cb6-e74618e6e9bb",
"name": "Salesforce4",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [200, 900],
"id": "ed0920e2-a04b-46b3-8bdb-f8e81969001f",
"name": "No Operation, do nothing4"
},
{
"parameters": {
"resource": "account",
"operation": "upsert",
"externalId": "Id",
"externalIdValue": "id1",
"name": "New account",
"additionalFields": {}
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [-20, 1100],
"id": "fcaf7a7e-312d-40c2-a5ab-be4040fd32d9",
"name": "Salesforce5",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [200, 1100],
"id": "327971b2-6258-4cfd-b126-0905aa8054a9",
"name": "No Operation, do nothing5"
},
{
"parameters": {
"resource": "account",
"operation": "delete",
"accountId": "id1"
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [-20, 1300],
"id": "468b78f6-0a62-4c1c-94a4-9180d51ae660",
"name": "Salesforce6",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [200, 1300],
"id": "e4bf4b7e-8f49-4764-908c-ea393ffa8a24",
"name": "No Operation, do nothing6"
}
],
"pinData": {
"No Operation, do nothing": [
{
"json": {
"id": "id1",
"success": true,
"errors": []
}
}
],
"No Operation, do nothing1": [
{
"json": {
"attributes": {
"type": "Account",
"url": "/services/data/v59.0/sobjects/Account/id1"
},
"Id": "id1",
"Name": "Account 1",
"Type": "Customer - Direct"
}
},
{
"json": {
"attributes": {
"type": "Account",
"url": "/services/data/v59.0/sobjects/Account/id2"
},
"Id": "id2",
"Name": "Account 2",
"Type": "Customer - Direct"
}
}
],
"No Operation, do nothing2": [
{
"json": {
"id": "noteid1",
"success": true,
"errors": []
}
}
],
"No Operation, do nothing3": [
{
"json": {
"attributes": {
"type": "Account",
"url": "/services/data/v59.0/sobjects/Account/id1"
},
"Id": "id1",
"IsDeleted": false,
"MasterRecordId": null,
"Name": "Account 1",
"Type": "Customer - Direct",
"ParentId": null,
"BillingStreet": "Foo",
"BillingCity": "Bar",
"BillingState": "TX",
"BillingPostalCode": null,
"BillingCountry": "United States",
"BillingStateCode": "TX",
"BillingCountryCode": "US",
"BillingLatitude": null,
"BillingLongitude": null,
"BillingGeocodeAccuracy": null,
"BillingAddress": {
"city": "Bar",
"country": "United States",
"countryCode": "US",
"geocodeAccuracy": null,
"latitude": null,
"longitude": null,
"postalCode": null,
"state": "TX",
"stateCode": "TX",
"street": "Foo"
},
"ShippingStreet": "Foo Bar",
"ShippingCity": null,
"ShippingState": null,
"ShippingPostalCode": null,
"ShippingCountry": null,
"ShippingStateCode": null,
"ShippingCountryCode": null,
"ShippingLatitude": null,
"ShippingLongitude": null,
"ShippingGeocodeAccuracy": null,
"ShippingAddress": {
"city": null,
"country": null,
"countryCode": null,
"geocodeAccuracy": null,
"latitude": null,
"longitude": null,
"postalCode": null,
"state": null,
"stateCode": null,
"street": "Foo"
},
"Phone": "(123) 123-4567",
"Fax": "(123) 123-4567",
"AccountNumber": "AB12345",
"Website": "http://foo.com",
"PhotoUrl": "/services/images/photo/id1",
"Sic": "6576",
"Industry": "Electronics",
"AnnualRevenue": 139000000,
"NumberOfEmployees": 1000,
"Ownership": "Public",
"TickerSymbol": "FOO",
"Description": "Lorem ipsum",
"Rating": "Hot",
"Site": null,
"OwnerId": "005gL000001GYd4QAG",
"CreatedDate": "2025-03-30T17:57:34.000+0000",
"CreatedById": "005gL000001GYd4QAG",
"LastModifiedDate": "2025-03-30T17:57:34.000+0000",
"LastModifiedById": "005gL000001GYd4QAG",
"SystemModstamp": "2025-03-30T17:57:34.000+0000",
"LastActivityDate": null,
"LastViewedDate": "2025-04-11T12:22:06.000+0000",
"LastReferencedDate": "2025-04-11T12:22:06.000+0000",
"Jigsaw": null,
"JigsawCompanyId": null,
"CleanStatus": "Pending",
"AccountSource": null,
"DunsNumber": null,
"Tradestyle": null,
"NaicsCode": null,
"NaicsDesc": null,
"YearStarted": null,
"SicDesc": null,
"DandbCompanyId": null,
"OperatingHoursId": null,
"CustomerPriority__c": "Medium",
"SLA__c": "Silver",
"Active__c": "Yes",
"NumberofLocations__c": 2,
"UpsellOpportunity__c": "Maybe",
"SLASerialNumber__c": "2657",
"SLAExpirationDate__c": "2025-04-23"
}
}
],
"No Operation, do nothing4": [
{
"json": {
"errors": [],
"success": true
}
}
],
"No Operation, do nothing5": [
{
"json": {
"id": "id1",
"success": true,
"errors": [],
"created": false
}
}
],
"No Operation, do nothing6": [
{
"json": {
"success": true,
"errors": []
}
}
]
},
"connections": {
"When clicking Execute workflow": {
"main": [
[
{
"node": "Salesforce",
"type": "main",
"index": 0
},
{
"node": "Salesforce1",
"type": "main",
"index": 0
},
{
"node": "Salesforce2",
"type": "main",
"index": 0
},
{
"node": "Salesforce3",
"type": "main",
"index": 0
},
{
"node": "Salesforce4",
"type": "main",
"index": 0
},
{
"node": "Salesforce5",
"type": "main",
"index": 0
},
{
"node": "Salesforce6",
"type": "main",
"index": 0
}
]
]
},
"Salesforce": {
"main": [
[
{
"node": "No Operation, do nothing",
"type": "main",
"index": 0
}
]
]
},
"Salesforce1": {
"main": [
[
{
"node": "No Operation, do nothing1",
"type": "main",
"index": 0
}
]
]
},
"Salesforce2": {
"main": [
[
{
"node": "No Operation, do nothing2",
"type": "main",
"index": 0
}
]
]
},
"Salesforce3": {
"main": [
[
{
"node": "No Operation, do nothing3",
"type": "main",
"index": 0
}
]
]
},
"Salesforce4": {
"main": [
[
{
"node": "No Operation, do nothing4",
"type": "main",
"index": 0
}
]
]
},
"Salesforce5": {
"main": [
[
{
"node": "No Operation, do nothing5",
"type": "main",
"index": 0
}
]
]
},
"Salesforce6": {
"main": [
[
{
"node": "No Operation, do nothing6",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "52a99175-c048-41ef-a650-cb0df49afc9e",
"meta": {
"instanceId": "e115be144a6a5547dbfca93e774dfffa178aa94a181854c13e2ce5e14d195b2e"
},
"id": "I1K0IcGLrqCdREVg",
"tags": []
}
@@ -0,0 +1,98 @@
{
"attributes": {
"type": "Account",
"url": "/services/data/v59.0/sobjects/Account/id1"
},
"Id": "id1",
"IsDeleted": false,
"MasterRecordId": null,
"Name": "Account 1",
"Type": "Customer - Direct",
"ParentId": null,
"BillingStreet": "Foo",
"BillingCity": "Bar",
"BillingState": "TX",
"BillingPostalCode": null,
"BillingCountry": "United States",
"BillingStateCode": "TX",
"BillingCountryCode": "US",
"BillingLatitude": null,
"BillingLongitude": null,
"BillingGeocodeAccuracy": null,
"BillingAddress": {
"city": "Bar",
"country": "United States",
"countryCode": "US",
"geocodeAccuracy": null,
"latitude": null,
"longitude": null,
"postalCode": null,
"state": "TX",
"stateCode": "TX",
"street": "Foo"
},
"ShippingStreet": "Foo Bar",
"ShippingCity": null,
"ShippingState": null,
"ShippingPostalCode": null,
"ShippingCountry": null,
"ShippingStateCode": null,
"ShippingCountryCode": null,
"ShippingLatitude": null,
"ShippingLongitude": null,
"ShippingGeocodeAccuracy": null,
"ShippingAddress": {
"city": null,
"country": null,
"countryCode": null,
"geocodeAccuracy": null,
"latitude": null,
"longitude": null,
"postalCode": null,
"state": null,
"stateCode": null,
"street": "Foo"
},
"Phone": "(123) 123-4567",
"Fax": "(123) 123-4567",
"AccountNumber": "AB12345",
"Website": "http://foo.com",
"PhotoUrl": "/services/images/photo/id1",
"Sic": "6576",
"Industry": "Electronics",
"AnnualRevenue": 139000000,
"NumberOfEmployees": 1000,
"Ownership": "Public",
"TickerSymbol": "FOO",
"Description": "Lorem ipsum",
"Rating": "Hot",
"Site": null,
"OwnerId": "005gL000001GYd4QAG",
"CreatedDate": "2025-03-30T17:57:34.000+0000",
"CreatedById": "005gL000001GYd4QAG",
"LastModifiedDate": "2025-03-30T17:57:34.000+0000",
"LastModifiedById": "005gL000001GYd4QAG",
"SystemModstamp": "2025-03-30T17:57:34.000+0000",
"LastActivityDate": null,
"LastViewedDate": "2025-04-11T12:22:06.000+0000",
"LastReferencedDate": "2025-04-11T12:22:06.000+0000",
"Jigsaw": null,
"JigsawCompanyId": null,
"CleanStatus": "Pending",
"AccountSource": null,
"DunsNumber": null,
"Tradestyle": null,
"NaicsCode": null,
"NaicsDesc": null,
"YearStarted": null,
"SicDesc": null,
"DandbCompanyId": null,
"OperatingHoursId": null,
"CustomerPriority__c": "Medium",
"SLA__c": "Silver",
"Active__c": "Yes",
"NumberofLocations__c": 2,
"UpsellOpportunity__c": "Maybe",
"SLASerialNumber__c": "2657",
"SLAExpirationDate__c": "2025-04-23"
}
@@ -0,0 +1,20 @@
[
{
"attributes": {
"type": "Account",
"url": "/services/data/v59.0/sobjects/Account/id1"
},
"Id": "id1",
"Name": "Account 1",
"Type": "Customer - Direct"
},
{
"attributes": {
"type": "Account",
"url": "/services/data/v59.0/sobjects/Account/id2"
},
"Id": "id2",
"Name": "Account 2",
"Type": "Customer - Direct"
}
]
@@ -0,0 +1,59 @@
{
"objectDescribe": {
"activateable": false,
"associateEntityType": null,
"associateParentEntity": null,
"createable": true,
"custom": false,
"customSetting": false,
"deepCloneable": true,
"deletable": true,
"deprecatedAndHidden": false,
"feedEnabled": true,
"hasSubtypes": false,
"isInterface": false,
"isSubtype": false,
"keyPrefix": "006",
"label": "Opportunity",
"labelPlural": "Opportunities",
"layoutable": true,
"mergeable": false,
"mruEnabled": true,
"name": "Opportunity",
"queryable": true,
"replicateable": true,
"retrieveable": true,
"searchable": true,
"triggerable": true,
"undeletable": true,
"updateable": true,
"urls": {
"compactLayouts": "/services/data/v59.0/sobjects/Opportunity/describe/compactLayouts",
"rowTemplate": "/services/data/v59.0/sobjects/Opportunity/{ID}",
"approvalLayouts": "/services/data/v59.0/sobjects/Opportunity/describe/approvalLayouts",
"listviews": "/services/data/v59.0/sobjects/Opportunity/listviews",
"describe": "/services/data/v59.0/sobjects/Opportunity/describe",
"quickActions": "/services/data/v59.0/sobjects/Opportunity/quickActions",
"layouts": "/services/data/v59.0/sobjects/Opportunity/describe/layouts",
"sobject": "/services/data/v59.0/sobjects/Opportunity"
}
},
"recentItems": [
{
"attributes": {
"type": "Opportunity",
"url": "/services/data/v59.0/sobjects/Opportunity/id1"
},
"Id": "id1",
"Name": "Opportunity 1"
},
{
"attributes": {
"type": "Opportunity",
"url": "/services/data/v59.0/sobjects/Opportunity/id2"
},
"Id": "id2",
"Name": "Opportunity 2"
}
]
}
@@ -0,0 +1,24 @@
[
{
"attributes": {
"type": "Opportunity",
"url": "/services/data/v59.0/sobjects/Opportunity/id1"
},
"Id": "id1",
"AccountId": "accountid1",
"Amount": 15000,
"Probability": 10,
"Type": "New Customer"
},
{
"attributes": {
"type": "Opportunity",
"url": "/services/data/v59.0/sobjects/Opportunity/id2"
},
"Id": "id2",
"AccountId": "accountid2",
"Amount": 125000,
"Probability": 90,
"Type": "Existing Customer - Upgrade"
}
]
@@ -0,0 +1,52 @@
{
"attributes": {
"type": "Opportunity",
"url": "/services/data/v59.0/sobjects/Opportunity/id1"
},
"Id": "id1",
"IsDeleted": false,
"AccountId": "accountid1",
"IsPrivate": false,
"Name": "Opportunity 1",
"Description": null,
"StageName": "Qualification",
"Amount": 15000,
"Probability": 10,
"ExpectedRevenue": 1500,
"TotalOpportunityQuantity": null,
"CloseDate": "2025-02-05",
"Type": "New Customer",
"NextStep": null,
"LeadSource": "Purchased List",
"IsClosed": false,
"IsWon": false,
"ForecastCategory": "Pipeline",
"ForecastCategoryName": "Pipeline",
"CampaignId": null,
"HasOpportunityLineItem": false,
"Pricebook2Id": null,
"OwnerId": "id1",
"CreatedDate": "2025-03-30T17:57:34.000+0000",
"CreatedById": "id1",
"LastModifiedDate": "2025-03-30T17:57:34.000+0000",
"LastModifiedById": "id1",
"SystemModstamp": "2025-03-30T17:57:34.000+0000",
"LastActivityDate": null,
"PushCount": 0,
"LastStageChangeDate": null,
"FiscalQuarter": 1,
"FiscalYear": 2015,
"Fiscal": "2015 1",
"ContactId": null,
"LastViewedDate": null,
"LastReferencedDate": null,
"HasOpenActivity": false,
"HasOverdueTask": false,
"LastAmountChangedHistoryId": null,
"LastCloseDateChangedHistoryId": null,
"DeliveryInstallationStatus__c": null,
"TrackingNumber__c": null,
"OrderNumber__c": null,
"CurrentGenerators__c": null,
"MainCompetitors__c": "Honda"
}
@@ -0,0 +1,45 @@
{
"attributes": {
"type": "Task",
"url": "/services/data/v59.0/sobjects/Task/id1"
},
"Id": "id1",
"WhoId": null,
"WhatId": null,
"Subject": "Email",
"ActivityDate": null,
"Status": "In Progress",
"Priority": "Normal",
"IsHighPriority": false,
"OwnerId": "005gL000001QBDlQAO",
"Description": "Sample description",
"IsDeleted": false,
"AccountId": null,
"IsClosed": false,
"CreatedDate": "2025-04-11T11:27:22.000+0000",
"CreatedById": "005gL000001QBDlQAO",
"LastModifiedDate": "2025-04-11T11:27:22.000+0000",
"LastModifiedById": "005gL000001QBDlQAO",
"SystemModstamp": "2025-04-11T11:27:22.000+0000",
"IsArchived": false,
"CallDurationInSeconds": null,
"CallType": null,
"CallDisposition": null,
"CallObject": null,
"ReminderDateTime": null,
"IsReminderSet": false,
"RecurrenceActivityId": null,
"IsRecurrence": false,
"RecurrenceStartDateOnly": null,
"RecurrenceEndDateOnly": null,
"RecurrenceTimeZoneSidKey": null,
"RecurrenceType": null,
"RecurrenceInterval": null,
"RecurrenceDayOfWeekMask": null,
"RecurrenceDayOfMonth": null,
"RecurrenceInstance": null,
"RecurrenceMonthOfYear": null,
"RecurrenceRegeneratedType": null,
"TaskSubtype": "Task",
"CompletedDateTime": null
}
@@ -0,0 +1,57 @@
{
"objectDescribe": {
"activateable": false,
"associateEntityType": null,
"associateParentEntity": null,
"createable": true,
"custom": false,
"customSetting": false,
"deepCloneable": false,
"deletable": true,
"deprecatedAndHidden": false,
"feedEnabled": false,
"hasSubtypes": false,
"isInterface": false,
"isSubtype": false,
"keyPrefix": "00T",
"label": "Task",
"labelPlural": "Tasks",
"layoutable": true,
"mergeable": false,
"mruEnabled": true,
"name": "Task",
"queryable": true,
"replicateable": true,
"retrieveable": true,
"searchable": true,
"triggerable": true,
"undeletable": true,
"updateable": true,
"urls": {
"compactLayouts": "/services/data/v59.0/sobjects/Task/describe/compactLayouts",
"rowTemplate": "/services/data/v59.0/sobjects/Task/{ID}",
"describe": "/services/data/v59.0/sobjects/Task/describe",
"quickActions": "/services/data/v59.0/sobjects/Task/quickActions",
"layouts": "/services/data/v59.0/sobjects/Task/describe/layouts",
"sobject": "/services/data/v59.0/sobjects/Task"
}
},
"recentItems": [
{
"attributes": {
"type": "Task",
"url": "/services/data/v59.0/sobjects/Task/00TgL000000BbYyUAK"
},
"Id": "00TgL000000BbYyUAK",
"Subject": "Email"
},
{
"attributes": {
"type": "Task",
"url": "/services/data/v59.0/sobjects/Task/00TgL000000BfCjUAK"
},
"Id": "00TgL000000BfCjUAK",
"Subject": "Email"
}
]
}
@@ -0,0 +1,22 @@
[
{
"attributes": {
"type": "Task",
"url": "/services/data/v59.0/sobjects/Task/id1"
},
"Id": "id1",
"Subject": "Email",
"Status": "In Progress",
"Priority": "Normal"
},
{
"attributes": {
"type": "Task",
"url": "/services/data/v59.0/sobjects/Task/id2"
},
"Id": "id2",
"Subject": "Email",
"Status": "In Progress",
"Priority": "Normal"
}
]
@@ -0,0 +1,189 @@
{
"attributes": {
"type": "User",
"url": "/services/data/v59.0/sobjects/User/id1"
},
"Id": "id1",
"Username": "user1@test.com",
"LastName": "Doe",
"FirstName": "John",
"Name": "John Doe",
"CompanyName": "Acme",
"Division": null,
"Department": null,
"Title": null,
"Street": null,
"City": null,
"State": null,
"PostalCode": null,
"Country": null,
"StateCode": null,
"CountryCode": null,
"Latitude": null,
"Longitude": null,
"GeocodeAccuracy": null,
"Address": null,
"Email": "user1@test.com",
"EmailPreferencesAutoBcc": true,
"EmailPreferencesAutoBccStayInTouch": false,
"EmailPreferencesStayInTouchReminder": true,
"SenderEmail": null,
"SenderName": null,
"Signature": null,
"StayInTouchSubject": null,
"StayInTouchSignature": null,
"StayInTouchNote": null,
"Phone": null,
"Fax": null,
"MobilePhone": null,
"Alias": "fit",
"CommunityNickname": "User17441049456622889395",
"BadgeText": "",
"IsActive": true,
"TimeZoneSidKey": "America/Los_Angeles",
"UserRoleId": null,
"LocaleSidKey": "en_US",
"ReceivesInfoEmails": false,
"ReceivesAdminInfoEmails": false,
"EmailEncodingKey": "UTF-8",
"ProfileId": "id1",
"UserType": "Standard",
"StartDay": "6",
"EndDay": "23",
"LanguageLocaleKey": "en_US",
"EmployeeNumber": null,
"DelegatedApproverId": null,
"ManagerId": null,
"LastLoginDate": "2025-04-11T11:01:57.000+0000",
"LastPasswordChangeDate": "2025-04-08T09:36:33.000+0000",
"CreatedDate": "2025-04-08T09:35:45.000+0000",
"CreatedById": "id0",
"LastModifiedDate": "2025-04-08T09:35:51.000+0000",
"LastModifiedById": "id0",
"SystemModstamp": "2025-04-09T03:00:35.000+0000",
"PasswordExpirationDate": "2025-07-07T09:36:33.000+0000",
"NumberOfFailedLogins": 0,
"SuAccessExpirationDate": null,
"OfflineTrialExpirationDate": null,
"OfflinePdaTrialExpirationDate": null,
"UserPermissionsMarketingUser": false,
"UserPermissionsOfflineUser": false,
"UserPermissionsCallCenterAutoLogin": false,
"UserPermissionsSFContentUser": true,
"UserPermissionsKnowledgeUser": true,
"UserPermissionsInteractionUser": false,
"UserPermissionsSupportUser": false,
"UserPermissionsJigsawProspectingUser": false,
"UserPermissionsSiteforceContributorUser": false,
"UserPermissionsSiteforcePublisherUser": false,
"UserPermissionsWorkDotComUserFeature": false,
"ForecastEnabled": false,
"UserPreferencesActivityRemindersPopup": true,
"UserPreferencesEventRemindersCheckboxDefault": true,
"UserPreferencesTaskRemindersCheckboxDefault": true,
"UserPreferencesReminderSoundOff": false,
"UserPreferencesDisableAllFeedsEmail": false,
"UserPreferencesDisableFollowersEmail": false,
"UserPreferencesDisableProfilePostEmail": false,
"UserPreferencesDisableChangeCommentEmail": false,
"UserPreferencesDisableLaterCommentEmail": false,
"UserPreferencesDisProfPostCommentEmail": false,
"UserPreferencesContentNoEmail": false,
"UserPreferencesContentEmailAsAndWhen": false,
"UserPreferencesApexPagesDeveloperMode": false,
"UserPreferencesReceiveNoNotificationsAsApprover": false,
"UserPreferencesReceiveNotificationsAsDelegatedApprover": false,
"UserPreferencesHideCSNGetChatterMobileTask": false,
"UserPreferencesDisableMentionsPostEmail": false,
"UserPreferencesDisMentionsCommentEmail": false,
"UserPreferencesHideCSNDesktopTask": false,
"UserPreferencesHideChatterOnboardingSplash": false,
"UserPreferencesHideSecondChatterOnboardingSplash": false,
"UserPreferencesDisCommentAfterLikeEmail": false,
"UserPreferencesDisableLikeEmail": true,
"UserPreferencesSortFeedByComment": true,
"UserPreferencesDisableMessageEmail": false,
"UserPreferencesJigsawListUser": false,
"UserPreferencesDisableBookmarkEmail": false,
"UserPreferencesDisableSharePostEmail": false,
"UserPreferencesEnableAutoSubForFeeds": false,
"UserPreferencesDisableFileShareNotificationsForApi": false,
"UserPreferencesShowTitleToExternalUsers": true,
"UserPreferencesShowManagerToExternalUsers": false,
"UserPreferencesShowEmailToExternalUsers": false,
"UserPreferencesShowWorkPhoneToExternalUsers": false,
"UserPreferencesShowMobilePhoneToExternalUsers": false,
"UserPreferencesShowFaxToExternalUsers": false,
"UserPreferencesShowStreetAddressToExternalUsers": false,
"UserPreferencesShowCityToExternalUsers": false,
"UserPreferencesShowStateToExternalUsers": false,
"UserPreferencesShowPostalCodeToExternalUsers": false,
"UserPreferencesShowCountryToExternalUsers": false,
"UserPreferencesShowProfilePicToGuestUsers": false,
"UserPreferencesShowTitleToGuestUsers": false,
"UserPreferencesShowCityToGuestUsers": false,
"UserPreferencesShowStateToGuestUsers": false,
"UserPreferencesShowPostalCodeToGuestUsers": false,
"UserPreferencesShowCountryToGuestUsers": false,
"UserPreferencesShowForecastingChangeSignals": false,
"UserPreferencesLiveAgentMiawSetupDeflection": false,
"UserPreferencesHideS1BrowserUI": false,
"UserPreferencesDisableEndorsementEmail": false,
"UserPreferencesPathAssistantCollapsed": false,
"UserPreferencesCacheDiagnostics": false,
"UserPreferencesShowEmailToGuestUsers": false,
"UserPreferencesShowManagerToGuestUsers": false,
"UserPreferencesShowWorkPhoneToGuestUsers": false,
"UserPreferencesShowMobilePhoneToGuestUsers": false,
"UserPreferencesShowFaxToGuestUsers": false,
"UserPreferencesShowStreetAddressToGuestUsers": false,
"UserPreferencesLightningExperiencePreferred": true,
"UserPreferencesPreviewLightning": false,
"UserPreferencesHideEndUserOnboardingAssistantModal": false,
"UserPreferencesHideLightningMigrationModal": false,
"UserPreferencesHideSfxWelcomeMat": true,
"UserPreferencesHideBiggerPhotoCallout": false,
"UserPreferencesGlobalNavBarWTShown": false,
"UserPreferencesGlobalNavGridMenuWTShown": false,
"UserPreferencesCreateLEXAppsWTShown": false,
"UserPreferencesFavoritesWTShown": false,
"UserPreferencesRecordHomeSectionCollapseWTShown": false,
"UserPreferencesRecordHomeReservedWTShown": false,
"UserPreferencesFavoritesShowTopFavorites": false,
"UserPreferencesExcludeMailAppAttachments": false,
"UserPreferencesSuppressTaskSFXReminders": false,
"UserPreferencesSuppressEventSFXReminders": false,
"UserPreferencesPreviewCustomTheme": false,
"UserPreferencesHasCelebrationBadge": false,
"UserPreferencesUserDebugModePref": false,
"UserPreferencesSRHOverrideActivities": false,
"UserPreferencesNewLightningReportRunPageEnabled": false,
"UserPreferencesReverseOpenActivitiesView": false,
"UserPreferencesShowTerritoryTimeZoneShifts": false,
"UserPreferencesHasSentWarningEmail": false,
"UserPreferencesHasSentWarningEmail238": false,
"UserPreferencesHasSentWarningEmail240": false,
"UserPreferencesNativeEmailClient": false,
"UserPreferencesShowForecastingRoundedAmounts": false,
"ContactId": null,
"AccountId": null,
"CallCenterId": null,
"Extension": null,
"FederationIdentifier": null,
"AboutMe": null,
"FullPhotoUrl": "https://salesforce.com/profilephoto/005/F",
"SmallPhotoUrl": "https://salesforce.com/profilephoto/005/T",
"IsExtIndicatorVisible": false,
"OutOfOfficeMessage": "",
"MediumPhotoUrl": "https://salesforce.com/profilephoto/005/M",
"DigestFrequency": "D",
"DefaultGroupNotificationFrequency": "N",
"JigsawImportLimitOverride": null,
"LastViewedDate": "2025-04-11T07:34:58.000+0000",
"LastReferencedDate": "2025-04-11T07:34:58.000+0000",
"BannerPhotoUrl": "/profilephoto/005/B",
"SmallBannerPhotoUrl": "/profilephoto/005/D",
"MediumBannerPhotoUrl": "/profilephoto/005/E",
"IsProfilePhotoActive": false,
"IndividualId": null
}
@@ -0,0 +1,29 @@
[
{
"attributes": {
"type": "User",
"url": "/services/data/v59.0/sobjects/User/id1"
},
"Id": "id1",
"Name": "User 1",
"Email": "test1@test.com"
},
{
"attributes": {
"type": "User",
"url": "/services/data/v59.0/sobjects/User/id2"
},
"Id": "id2",
"Name": "User 2",
"Email": "test2@test.com"
},
{
"attributes": {
"type": "User",
"url": "/services/data/v59.0/sobjects/User/id3"
},
"Id": "id3",
"Name": "User 3",
"Email": "test3@test.com"
}
]
@@ -0,0 +1,574 @@
{
"name": "opportunities.workflow",
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [-240, 800],
"id": "bfbc5552-b033-4a9b-88cc-2c489313b0c5",
"name": "When clicking Execute workflow"
},
{
"parameters": {
"resource": "opportunity",
"name": "New Opportunity",
"closeDate": "2025-01-01T00:00:00",
"stageName": "Prospecting",
"additionalFields": {
"amount": 1000
}
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [-20, 100],
"id": "bb9d487c-64ad-4e31-b54f-81282543f9b3",
"name": "Salesforce",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [200, 100],
"id": "cd4f2393-8fa7-4bf0-8a4e-81a519c2ca98",
"name": "No Operation, do nothing"
},
{
"parameters": {
"resource": "opportunity",
"operation": "getAll",
"returnAll": true,
"options": {}
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [-20, 300],
"id": "fec8e003-be8d-4f54-9d97-6b59c2390c44",
"name": "Salesforce1",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [200, 300],
"id": "15d62e5f-6576-4045-bc71-c75383e1dad4",
"name": "No Operation, do nothing1"
},
{
"parameters": {
"resource": "opportunity",
"operation": "upsert",
"externalId": "Name",
"externalIdValue": "SomeName",
"name": "New Opportunity",
"closeDate": "2025-01-01T00:00:00",
"stageName": "Prospecting",
"additionalFields": {}
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [-20, 500],
"id": "119704da-9b17-4f0f-8e2f-93b97e0cc897",
"name": "Salesforce2",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [200, 500],
"id": "487f0b0a-10c4-4015-a3d8-3007c62c57c7",
"name": "No Operation, do nothing2"
},
{
"parameters": {
"resource": "opportunity",
"operation": "addNote",
"opportunityId": "id1",
"title": "New Note",
"options": {}
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [-20, 700],
"id": "a6298428-c2b4-4f4a-a1ef-5241d4bf3cb7",
"name": "Salesforce3",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [200, 700],
"id": "b9dfe31c-7d30-47d5-833e-8d0043c4f156",
"name": "No Operation, do nothing3"
},
{
"parameters": {
"resource": "opportunity",
"operation": "get",
"opportunityId": "id1"
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [-20, 900],
"id": "27a29694-ebfb-4f22-a44d-33539b18609d",
"name": "Salesforce4",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [200, 900],
"id": "4da4ce39-99ba-430a-9dcb-f98dfd618988",
"name": "No Operation, do nothing4"
},
{
"parameters": {
"resource": "opportunity",
"operation": "getSummary"
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [-20, 1100],
"id": "671c9b0f-5c90-4789-967d-015112a31d69",
"name": "Salesforce5",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [200, 1100],
"id": "428e9f87-406c-4363-bf6c-c557eeabbdf4",
"name": "No Operation, do nothing5"
},
{
"parameters": {
"resource": "opportunity",
"operation": "delete",
"opportunityId": "id1"
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [-20, 1300],
"id": "c0069703-112e-4180-8f83-df1c993dd202",
"name": "Salesforce6",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [200, 1300],
"id": "83ef4a9a-cdcc-4052-9f8a-cd63207bf2bd",
"name": "No Operation, do nothing6"
},
{
"parameters": {
"resource": "opportunity",
"operation": "update",
"opportunityId": "id1",
"updateFields": {
"amount": 123
}
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [-20, 1500],
"id": "870e9a87-f1c7-4c28-9e44-86deaf9112e6",
"name": "Salesforce7",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [200, 1500],
"id": "7a98ba79-98b1-4a91-8bd0-b4432773b094",
"name": "No Operation, do nothing7"
}
],
"pinData": {
"No Operation, do nothing": [
{
"json": {
"id": "id1",
"success": true,
"errors": []
}
}
],
"No Operation, do nothing1": [
{
"json": {
"attributes": {
"type": "Opportunity",
"url": "/services/data/v59.0/sobjects/Opportunity/id1"
},
"Id": "id1",
"AccountId": "accountid1",
"Amount": 15000,
"Probability": 10,
"Type": "New Customer"
}
},
{
"json": {
"attributes": {
"type": "Opportunity",
"url": "/services/data/v59.0/sobjects/Opportunity/id2"
},
"Id": "id2",
"AccountId": "accountid2",
"Amount": 125000,
"Probability": 90,
"Type": "Existing Customer - Upgrade"
}
}
],
"No Operation, do nothing3": [
{
"json": {
"id": "noteid1",
"success": true,
"errors": []
}
}
],
"No Operation, do nothing4": [
{
"json": {
"attributes": {
"type": "Opportunity",
"url": "/services/data/v59.0/sobjects/Opportunity/id1"
},
"Id": "id1",
"IsDeleted": false,
"AccountId": "accountid1",
"IsPrivate": false,
"Name": "Opportunity 1",
"Description": null,
"StageName": "Qualification",
"Amount": 15000,
"Probability": 10,
"ExpectedRevenue": 1500,
"TotalOpportunityQuantity": null,
"CloseDate": "2025-02-05",
"Type": "New Customer",
"NextStep": null,
"LeadSource": "Purchased List",
"IsClosed": false,
"IsWon": false,
"ForecastCategory": "Pipeline",
"ForecastCategoryName": "Pipeline",
"CampaignId": null,
"HasOpportunityLineItem": false,
"Pricebook2Id": null,
"OwnerId": "id1",
"CreatedDate": "2025-03-30T17:57:34.000+0000",
"CreatedById": "id1",
"LastModifiedDate": "2025-03-30T17:57:34.000+0000",
"LastModifiedById": "id1",
"SystemModstamp": "2025-03-30T17:57:34.000+0000",
"LastActivityDate": null,
"PushCount": 0,
"LastStageChangeDate": null,
"FiscalQuarter": 1,
"FiscalYear": 2015,
"Fiscal": "2015 1",
"ContactId": null,
"LastViewedDate": null,
"LastReferencedDate": null,
"HasOpenActivity": false,
"HasOverdueTask": false,
"LastAmountChangedHistoryId": null,
"LastCloseDateChangedHistoryId": null,
"DeliveryInstallationStatus__c": null,
"TrackingNumber__c": null,
"OrderNumber__c": null,
"CurrentGenerators__c": null,
"MainCompetitors__c": "Honda"
}
}
],
"No Operation, do nothing5": [
{
"json": {
"objectDescribe": {
"activateable": false,
"associateEntityType": null,
"associateParentEntity": null,
"createable": true,
"custom": false,
"customSetting": false,
"deepCloneable": true,
"deletable": true,
"deprecatedAndHidden": false,
"feedEnabled": true,
"hasSubtypes": false,
"isInterface": false,
"isSubtype": false,
"keyPrefix": "006",
"label": "Opportunity",
"labelPlural": "Opportunities",
"layoutable": true,
"mergeable": false,
"mruEnabled": true,
"name": "Opportunity",
"queryable": true,
"replicateable": true,
"retrieveable": true,
"searchable": true,
"triggerable": true,
"undeletable": true,
"updateable": true,
"urls": {
"compactLayouts": "/services/data/v59.0/sobjects/Opportunity/describe/compactLayouts",
"rowTemplate": "/services/data/v59.0/sobjects/Opportunity/{ID}",
"approvalLayouts": "/services/data/v59.0/sobjects/Opportunity/describe/approvalLayouts",
"listviews": "/services/data/v59.0/sobjects/Opportunity/listviews",
"describe": "/services/data/v59.0/sobjects/Opportunity/describe",
"quickActions": "/services/data/v59.0/sobjects/Opportunity/quickActions",
"layouts": "/services/data/v59.0/sobjects/Opportunity/describe/layouts",
"sobject": "/services/data/v59.0/sobjects/Opportunity"
}
},
"recentItems": [
{
"attributes": {
"type": "Opportunity",
"url": "/services/data/v59.0/sobjects/Opportunity/id1"
},
"Id": "id1",
"Name": "Opportunity 1"
},
{
"attributes": {
"type": "Opportunity",
"url": "/services/data/v59.0/sobjects/Opportunity/id2"
},
"Id": "id2",
"Name": "Opportunity 2"
}
]
}
}
],
"No Operation, do nothing6": [
{
"json": {
"errors": [],
"success": true
}
}
],
"No Operation, do nothing2": [
{
"json": {
"id": "id1",
"success": true,
"errors": [],
"created": false
}
}
],
"No Operation, do nothing7": [
{
"json": {
"errors": [],
"success": true
}
}
]
},
"connections": {
"When clicking Execute workflow": {
"main": [
[
{
"node": "Salesforce",
"type": "main",
"index": 0
},
{
"node": "Salesforce1",
"type": "main",
"index": 0
},
{
"node": "Salesforce2",
"type": "main",
"index": 0
},
{
"node": "Salesforce3",
"type": "main",
"index": 0
},
{
"node": "Salesforce4",
"type": "main",
"index": 0
},
{
"node": "Salesforce5",
"type": "main",
"index": 0
},
{
"node": "Salesforce6",
"type": "main",
"index": 0
},
{
"node": "Salesforce7",
"type": "main",
"index": 0
}
]
]
},
"Salesforce": {
"main": [
[
{
"node": "No Operation, do nothing",
"type": "main",
"index": 0
}
]
]
},
"Salesforce1": {
"main": [
[
{
"node": "No Operation, do nothing1",
"type": "main",
"index": 0
}
]
]
},
"Salesforce2": {
"main": [
[
{
"node": "No Operation, do nothing2",
"type": "main",
"index": 0
}
]
]
},
"Salesforce3": {
"main": [
[
{
"node": "No Operation, do nothing3",
"type": "main",
"index": 0
}
]
]
},
"Salesforce4": {
"main": [
[
{
"node": "No Operation, do nothing4",
"type": "main",
"index": 0
}
]
]
},
"Salesforce5": {
"main": [
[
{
"node": "No Operation, do nothing5",
"type": "main",
"index": 0
}
]
]
},
"Salesforce6": {
"main": [
[
{
"node": "No Operation, do nothing6",
"type": "main",
"index": 0
}
]
]
},
"Salesforce7": {
"main": [
[
{
"node": "No Operation, do nothing7",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "8c7902c9-6bf2-44b1-85c8-13384bfd5ad1",
"meta": {
"instanceId": "e115be144a6a5547dbfca93e774dfffa178aa94a181854c13e2ce5e14d195b2e"
},
"id": "5uU0KQPrh1moPgAs",
"tags": []
}
@@ -0,0 +1,98 @@
{
"name": "search.workflow",
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [-240, 100],
"id": "e3a94904-ddfa-4fe8-bac2-e2c796c677a3",
"name": "When clicking Execute workflow"
},
{
"parameters": {
"resource": "search",
"query": "SELECT id, name, type FROM Account"
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [-20, 100],
"id": "0fcf65ac-c9a3-48b5-9c39-599216208b62",
"name": "Salesforce",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [200, 100],
"id": "8319a07f-465f-4456-b6b0-012aa36ce08b",
"name": "No Operation, do nothing"
}
],
"pinData": {
"No Operation, do nothing": [
{
"json": {
"attributes": {
"type": "Account",
"url": "/services/data/v59.0/sobjects/Account/id1"
},
"Id": "id1",
"Name": "Account 1",
"Type": "Customer - Direct"
}
},
{
"json": {
"attributes": {
"type": "Account",
"url": "/services/data/v59.0/sobjects/Account/id2"
},
"Id": "id2",
"Name": "Account 2",
"Type": "Customer - Direct"
}
}
]
},
"connections": {
"When clicking Execute workflow": {
"main": [
[
{
"node": "Salesforce",
"type": "main",
"index": 0
}
]
]
},
"Salesforce": {
"main": [
[
{
"node": "No Operation, do nothing",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "ca0a5b64-d016-4412-8ea9-0cefc269f2c1",
"meta": {
"instanceId": "e115be144a6a5547dbfca93e774dfffa178aa94a181854c13e2ce5e14d195b2e"
},
"id": "ODC4s7m6nIRLkeE9",
"tags": []
}
@@ -0,0 +1,452 @@
{
"name": "tasks.workflow",
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [-220, 600],
"id": "56d46635-cd55-4ca2-aa50-cc7f9767abc7",
"name": "When clicking Execute workflow"
},
{
"parameters": {
"resource": "task",
"status": "In Progress",
"additionalFields": {
"description": "Sample description",
"subject": "Email"
}
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [0, 100],
"id": "4797a994-e082-445e-a2ca-65e30503e055",
"name": "Salesforce",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [220, 100],
"id": "8639e379-1a63-4d2e-9032-28a9d2191a82",
"name": "No Operation, do nothing"
},
{
"parameters": {
"resource": "task",
"operation": "getAll",
"returnAll": true,
"options": {}
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [0, 300],
"id": "a4a735c1-02d5-42e7-8dbb-873ca7ed2a79",
"name": "Salesforce1",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [220, 300],
"id": "4b9bc48b-695c-40d5-be6b-d8d117e2ffc0",
"name": "No Operation, do nothing1"
},
{
"parameters": {
"resource": "task",
"operation": "get",
"taskId": "id1"
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [0, 500],
"id": "910ee66b-8a1b-46b9-a156-dd7ebb932dac",
"name": "Salesforce2",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [220, 500],
"id": "570eb762-96e4-4639-9ad7-c6c67a6b2f43",
"name": "No Operation, do nothing2"
},
{
"parameters": {
"resource": "task",
"operation": "getSummary"
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [0, 700],
"id": "7a27cca0-ac81-4b15-b64e-149b94dc1dd2",
"name": "Salesforce3",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [220, 700],
"id": "fc825f3a-24eb-4d4c-b7df-d872e9bae2c3",
"name": "No Operation, do nothing3"
},
{
"parameters": {
"resource": "task",
"operation": "update",
"taskId": "id1",
"updateFields": {
"description": "New description"
}
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [0, 900],
"id": "e3bb44cd-fcd8-407f-b700-63fe7cd94650",
"name": "Salesforce4",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [220, 900],
"id": "e7e8325c-4b2e-4aff-b077-e87b93cb5849",
"name": "No Operation, do nothing4"
},
{
"parameters": {
"resource": "task",
"operation": "delete",
"taskId": "id1"
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [0, 1100],
"id": "aa00475d-ea3e-4bf6-838c-34ec986b891f",
"name": "Salesforce5",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [220, 1100],
"id": "3199b901-5b75-4ee1-8e47-39e49c9160ba",
"name": "No Operation, do nothing5"
}
],
"pinData": {
"No Operation, do nothing": [
{
"json": {
"id": "id1",
"success": true,
"errors": []
}
}
],
"No Operation, do nothing1": [
{
"json": {
"attributes": {
"type": "Task",
"url": "/services/data/v59.0/sobjects/Task/id1"
},
"Id": "id1",
"Subject": "Email",
"Status": "In Progress",
"Priority": "Normal"
}
},
{
"json": {
"attributes": {
"type": "Task",
"url": "/services/data/v59.0/sobjects/Task/id2"
},
"Id": "id2",
"Subject": "Email",
"Status": "In Progress",
"Priority": "Normal"
}
}
],
"No Operation, do nothing2": [
{
"json": {
"attributes": {
"type": "Task",
"url": "/services/data/v59.0/sobjects/Task/id1"
},
"Id": "id1",
"WhoId": null,
"WhatId": null,
"Subject": "Email",
"ActivityDate": null,
"Status": "In Progress",
"Priority": "Normal",
"IsHighPriority": false,
"OwnerId": "005gL000001QBDlQAO",
"Description": "Sample description",
"IsDeleted": false,
"AccountId": null,
"IsClosed": false,
"CreatedDate": "2025-04-11T11:27:22.000+0000",
"CreatedById": "005gL000001QBDlQAO",
"LastModifiedDate": "2025-04-11T11:27:22.000+0000",
"LastModifiedById": "005gL000001QBDlQAO",
"SystemModstamp": "2025-04-11T11:27:22.000+0000",
"IsArchived": false,
"CallDurationInSeconds": null,
"CallType": null,
"CallDisposition": null,
"CallObject": null,
"ReminderDateTime": null,
"IsReminderSet": false,
"RecurrenceActivityId": null,
"IsRecurrence": false,
"RecurrenceStartDateOnly": null,
"RecurrenceEndDateOnly": null,
"RecurrenceTimeZoneSidKey": null,
"RecurrenceType": null,
"RecurrenceInterval": null,
"RecurrenceDayOfWeekMask": null,
"RecurrenceDayOfMonth": null,
"RecurrenceInstance": null,
"RecurrenceMonthOfYear": null,
"RecurrenceRegeneratedType": null,
"TaskSubtype": "Task",
"CompletedDateTime": null
}
}
],
"No Operation, do nothing3": [
{
"json": {
"objectDescribe": {
"activateable": false,
"associateEntityType": null,
"associateParentEntity": null,
"createable": true,
"custom": false,
"customSetting": false,
"deepCloneable": false,
"deletable": true,
"deprecatedAndHidden": false,
"feedEnabled": false,
"hasSubtypes": false,
"isInterface": false,
"isSubtype": false,
"keyPrefix": "00T",
"label": "Task",
"labelPlural": "Tasks",
"layoutable": true,
"mergeable": false,
"mruEnabled": true,
"name": "Task",
"queryable": true,
"replicateable": true,
"retrieveable": true,
"searchable": true,
"triggerable": true,
"undeletable": true,
"updateable": true,
"urls": {
"compactLayouts": "/services/data/v59.0/sobjects/Task/describe/compactLayouts",
"rowTemplate": "/services/data/v59.0/sobjects/Task/{ID}",
"describe": "/services/data/v59.0/sobjects/Task/describe",
"quickActions": "/services/data/v59.0/sobjects/Task/quickActions",
"layouts": "/services/data/v59.0/sobjects/Task/describe/layouts",
"sobject": "/services/data/v59.0/sobjects/Task"
}
},
"recentItems": [
{
"attributes": {
"type": "Task",
"url": "/services/data/v59.0/sobjects/Task/00TgL000000BbYyUAK"
},
"Id": "00TgL000000BbYyUAK",
"Subject": "Email"
},
{
"attributes": {
"type": "Task",
"url": "/services/data/v59.0/sobjects/Task/00TgL000000BfCjUAK"
},
"Id": "00TgL000000BfCjUAK",
"Subject": "Email"
}
]
}
}
],
"No Operation, do nothing4": [
{
"json": {
"errors": [],
"success": true
}
}
],
"No Operation, do nothing5": [
{
"json": {
"errors": [],
"success": true
}
}
]
},
"connections": {
"When clicking Execute workflow": {
"main": [
[
{
"node": "Salesforce",
"type": "main",
"index": 0
},
{
"node": "Salesforce1",
"type": "main",
"index": 0
},
{
"node": "Salesforce2",
"type": "main",
"index": 0
},
{
"node": "Salesforce3",
"type": "main",
"index": 0
},
{
"node": "Salesforce4",
"type": "main",
"index": 0
},
{
"node": "Salesforce5",
"type": "main",
"index": 0
}
]
]
},
"Salesforce": {
"main": [
[
{
"node": "No Operation, do nothing",
"type": "main",
"index": 0
}
]
]
},
"Salesforce1": {
"main": [
[
{
"node": "No Operation, do nothing1",
"type": "main",
"index": 0
}
]
]
},
"Salesforce2": {
"main": [
[
{
"node": "No Operation, do nothing2",
"type": "main",
"index": 0
}
]
]
},
"Salesforce3": {
"main": [
[
{
"node": "No Operation, do nothing3",
"type": "main",
"index": 0
}
]
]
},
"Salesforce4": {
"main": [
[
{
"node": "No Operation, do nothing4",
"type": "main",
"index": 0
}
]
]
},
"Salesforce5": {
"main": [
[
{
"node": "No Operation, do nothing5",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "9a2b3613-dfcb-40ff-8e85-431d73fb6a7c",
"meta": {
"instanceId": "e115be144a6a5547dbfca93e774dfffa178aa94a181854c13e2ce5e14d195b2e"
},
"id": "EY22EouBwE7vnSTT",
"tags": []
}
@@ -0,0 +1,349 @@
{
"name": "users.workflow",
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [0, 100],
"id": "c7971ec9-8bbf-42c1-b99b-dc24de009760",
"name": "When clicking Execute workflow"
},
{
"parameters": {
"resource": "user",
"operation": "getAll",
"returnAll": true,
"options": {}
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [220, 0],
"id": "b6762525-8485-4ac7-9cab-bad56c627187",
"name": "Salesforce",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [440, 0],
"id": "006a7764-e287-437a-9475-d8a78e972172",
"name": "No Operation, do nothing"
},
{
"parameters": {
"resource": "user",
"userId": "id1"
},
"type": "n8n-nodes-base.salesforce",
"typeVersion": 1,
"position": [220, 200],
"id": "19f7ad1c-0e32-4bf3-a12f-80dace398586",
"name": "Salesforce1",
"credentials": {
"salesforceOAuth2Api": {
"id": "gBXFg0tv79sQS6pc",
"name": "Salesforce account"
}
}
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [440, 200],
"id": "6c19df43-d32e-47ff-aecc-e045657c02c2",
"name": "No Operation, do nothing1"
}
],
"pinData": {
"No Operation, do nothing": [
{
"json": {
"attributes": {
"type": "User",
"url": "/services/data/v59.0/sobjects/User/id1"
},
"Id": "id1",
"Name": "User 1",
"Email": "test1@test.com"
}
},
{
"json": {
"attributes": {
"type": "User",
"url": "/services/data/v59.0/sobjects/User/id2"
},
"Id": "id2",
"Name": "User 2",
"Email": "test2@test.com"
}
},
{
"json": {
"attributes": {
"type": "User",
"url": "/services/data/v59.0/sobjects/User/id3"
},
"Id": "id3",
"Name": "User 3",
"Email": "test3@test.com"
}
}
],
"No Operation, do nothing1": [
{
"json": {
"attributes": {
"type": "User",
"url": "/services/data/v59.0/sobjects/User/id1"
},
"Id": "id1",
"Username": "user1@test.com",
"LastName": "Doe",
"FirstName": "John",
"Name": "John Doe",
"CompanyName": "Acme",
"Division": null,
"Department": null,
"Title": null,
"Street": null,
"City": null,
"State": null,
"PostalCode": null,
"Country": null,
"StateCode": null,
"CountryCode": null,
"Latitude": null,
"Longitude": null,
"GeocodeAccuracy": null,
"Address": null,
"Email": "user1@test.com",
"EmailPreferencesAutoBcc": true,
"EmailPreferencesAutoBccStayInTouch": false,
"EmailPreferencesStayInTouchReminder": true,
"SenderEmail": null,
"SenderName": null,
"Signature": null,
"StayInTouchSubject": null,
"StayInTouchSignature": null,
"StayInTouchNote": null,
"Phone": null,
"Fax": null,
"MobilePhone": null,
"Alias": "fit",
"CommunityNickname": "User17441049456622889395",
"BadgeText": "",
"IsActive": true,
"TimeZoneSidKey": "America/Los_Angeles",
"UserRoleId": null,
"LocaleSidKey": "en_US",
"ReceivesInfoEmails": false,
"ReceivesAdminInfoEmails": false,
"EmailEncodingKey": "UTF-8",
"ProfileId": "id1",
"UserType": "Standard",
"StartDay": "6",
"EndDay": "23",
"LanguageLocaleKey": "en_US",
"EmployeeNumber": null,
"DelegatedApproverId": null,
"ManagerId": null,
"LastLoginDate": "2025-04-11T11:01:57.000+0000",
"LastPasswordChangeDate": "2025-04-08T09:36:33.000+0000",
"CreatedDate": "2025-04-08T09:35:45.000+0000",
"CreatedById": "id0",
"LastModifiedDate": "2025-04-08T09:35:51.000+0000",
"LastModifiedById": "id0",
"SystemModstamp": "2025-04-09T03:00:35.000+0000",
"PasswordExpirationDate": "2025-07-07T09:36:33.000+0000",
"NumberOfFailedLogins": 0,
"SuAccessExpirationDate": null,
"OfflineTrialExpirationDate": null,
"OfflinePdaTrialExpirationDate": null,
"UserPermissionsMarketingUser": false,
"UserPermissionsOfflineUser": false,
"UserPermissionsCallCenterAutoLogin": false,
"UserPermissionsSFContentUser": true,
"UserPermissionsKnowledgeUser": true,
"UserPermissionsInteractionUser": false,
"UserPermissionsSupportUser": false,
"UserPermissionsJigsawProspectingUser": false,
"UserPermissionsSiteforceContributorUser": false,
"UserPermissionsSiteforcePublisherUser": false,
"UserPermissionsWorkDotComUserFeature": false,
"ForecastEnabled": false,
"UserPreferencesActivityRemindersPopup": true,
"UserPreferencesEventRemindersCheckboxDefault": true,
"UserPreferencesTaskRemindersCheckboxDefault": true,
"UserPreferencesReminderSoundOff": false,
"UserPreferencesDisableAllFeedsEmail": false,
"UserPreferencesDisableFollowersEmail": false,
"UserPreferencesDisableProfilePostEmail": false,
"UserPreferencesDisableChangeCommentEmail": false,
"UserPreferencesDisableLaterCommentEmail": false,
"UserPreferencesDisProfPostCommentEmail": false,
"UserPreferencesContentNoEmail": false,
"UserPreferencesContentEmailAsAndWhen": false,
"UserPreferencesApexPagesDeveloperMode": false,
"UserPreferencesReceiveNoNotificationsAsApprover": false,
"UserPreferencesReceiveNotificationsAsDelegatedApprover": false,
"UserPreferencesHideCSNGetChatterMobileTask": false,
"UserPreferencesDisableMentionsPostEmail": false,
"UserPreferencesDisMentionsCommentEmail": false,
"UserPreferencesHideCSNDesktopTask": false,
"UserPreferencesHideChatterOnboardingSplash": false,
"UserPreferencesHideSecondChatterOnboardingSplash": false,
"UserPreferencesDisCommentAfterLikeEmail": false,
"UserPreferencesDisableLikeEmail": true,
"UserPreferencesSortFeedByComment": true,
"UserPreferencesDisableMessageEmail": false,
"UserPreferencesJigsawListUser": false,
"UserPreferencesDisableBookmarkEmail": false,
"UserPreferencesDisableSharePostEmail": false,
"UserPreferencesEnableAutoSubForFeeds": false,
"UserPreferencesDisableFileShareNotificationsForApi": false,
"UserPreferencesShowTitleToExternalUsers": true,
"UserPreferencesShowManagerToExternalUsers": false,
"UserPreferencesShowEmailToExternalUsers": false,
"UserPreferencesShowWorkPhoneToExternalUsers": false,
"UserPreferencesShowMobilePhoneToExternalUsers": false,
"UserPreferencesShowFaxToExternalUsers": false,
"UserPreferencesShowStreetAddressToExternalUsers": false,
"UserPreferencesShowCityToExternalUsers": false,
"UserPreferencesShowStateToExternalUsers": false,
"UserPreferencesShowPostalCodeToExternalUsers": false,
"UserPreferencesShowCountryToExternalUsers": false,
"UserPreferencesShowProfilePicToGuestUsers": false,
"UserPreferencesShowTitleToGuestUsers": false,
"UserPreferencesShowCityToGuestUsers": false,
"UserPreferencesShowStateToGuestUsers": false,
"UserPreferencesShowPostalCodeToGuestUsers": false,
"UserPreferencesShowCountryToGuestUsers": false,
"UserPreferencesShowForecastingChangeSignals": false,
"UserPreferencesLiveAgentMiawSetupDeflection": false,
"UserPreferencesHideS1BrowserUI": false,
"UserPreferencesDisableEndorsementEmail": false,
"UserPreferencesPathAssistantCollapsed": false,
"UserPreferencesCacheDiagnostics": false,
"UserPreferencesShowEmailToGuestUsers": false,
"UserPreferencesShowManagerToGuestUsers": false,
"UserPreferencesShowWorkPhoneToGuestUsers": false,
"UserPreferencesShowMobilePhoneToGuestUsers": false,
"UserPreferencesShowFaxToGuestUsers": false,
"UserPreferencesShowStreetAddressToGuestUsers": false,
"UserPreferencesLightningExperiencePreferred": true,
"UserPreferencesPreviewLightning": false,
"UserPreferencesHideEndUserOnboardingAssistantModal": false,
"UserPreferencesHideLightningMigrationModal": false,
"UserPreferencesHideSfxWelcomeMat": true,
"UserPreferencesHideBiggerPhotoCallout": false,
"UserPreferencesGlobalNavBarWTShown": false,
"UserPreferencesGlobalNavGridMenuWTShown": false,
"UserPreferencesCreateLEXAppsWTShown": false,
"UserPreferencesFavoritesWTShown": false,
"UserPreferencesRecordHomeSectionCollapseWTShown": false,
"UserPreferencesRecordHomeReservedWTShown": false,
"UserPreferencesFavoritesShowTopFavorites": false,
"UserPreferencesExcludeMailAppAttachments": false,
"UserPreferencesSuppressTaskSFXReminders": false,
"UserPreferencesSuppressEventSFXReminders": false,
"UserPreferencesPreviewCustomTheme": false,
"UserPreferencesHasCelebrationBadge": false,
"UserPreferencesUserDebugModePref": false,
"UserPreferencesSRHOverrideActivities": false,
"UserPreferencesNewLightningReportRunPageEnabled": false,
"UserPreferencesReverseOpenActivitiesView": false,
"UserPreferencesShowTerritoryTimeZoneShifts": false,
"UserPreferencesHasSentWarningEmail": false,
"UserPreferencesHasSentWarningEmail238": false,
"UserPreferencesHasSentWarningEmail240": false,
"UserPreferencesNativeEmailClient": false,
"UserPreferencesShowForecastingRoundedAmounts": false,
"ContactId": null,
"AccountId": null,
"CallCenterId": null,
"Extension": null,
"FederationIdentifier": null,
"AboutMe": null,
"FullPhotoUrl": "https://salesforce.com/profilephoto/005/F",
"SmallPhotoUrl": "https://salesforce.com/profilephoto/005/T",
"IsExtIndicatorVisible": false,
"OutOfOfficeMessage": "",
"MediumPhotoUrl": "https://salesforce.com/profilephoto/005/M",
"DigestFrequency": "D",
"DefaultGroupNotificationFrequency": "N",
"JigsawImportLimitOverride": null,
"LastViewedDate": "2025-04-11T07:34:58.000+0000",
"LastReferencedDate": "2025-04-11T07:34:58.000+0000",
"BannerPhotoUrl": "/profilephoto/005/B",
"SmallBannerPhotoUrl": "/profilephoto/005/D",
"MediumBannerPhotoUrl": "/profilephoto/005/E",
"IsProfilePhotoActive": false,
"IndividualId": null
}
}
]
},
"connections": {
"When clicking Execute workflow": {
"main": [
[
{
"node": "Salesforce",
"type": "main",
"index": 0
},
{
"node": "Salesforce1",
"type": "main",
"index": 0
}
]
]
},
"Salesforce": {
"main": [
[
{
"node": "No Operation, do nothing",
"type": "main",
"index": 0
}
]
]
},
"Salesforce1": {
"main": [
[
{
"node": "No Operation, do nothing1",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1",
"saveExecutionProgress": true,
"callerPolicy": "workflowsFromSameOwner",
"executionTimeout": -1
},
"versionId": "75388b74-b236-4166-8b91-1f12b869b6e1",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "e115be144a6a5547dbfca93e774dfffa178aa94a181854c13e2ce5e14d195b2e"
},
"id": "3YnSQQ6F97w4Pzyj",
"tags": []
}
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.1 KiB