mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-20 03:12:15 +00:00
Merge branch 'n8n-io:master' into Add-schema-registry-into-kafka
This commit is contained in:
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"node": "n8n-nodes-base.activationTrigger",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": [
|
||||
"Core Nodes"
|
||||
],
|
||||
"resources": {
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.activationTrigger/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,7 @@ export class AcuitySchedulingTrigger implements INodeType {
|
||||
name: 'resolveData',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'By default does the webhook-data only contain the ID of the object.<br />If this option gets activated it will resolve the data automatically.',
|
||||
description: 'By default does the webhook-data only contain the ID of the object. If this option gets activated, it will resolve the data automatically.',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -107,8 +107,7 @@ export const listEntryFields = [
|
||||
name: 'creator_id',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `The id of a Person resource who should be recorded as adding the entry to the list. <br/>
|
||||
Must be a person who can access Affinity. If not provided the creator defaults to the owner of the API key.`,
|
||||
description: `The id of a Person resource who should be recorded as adding the entry to the list. Must be a person who can access Affinity. If not provided the creator defaults to the owner of the API key.`,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { IExecuteFunctions } from 'n8n-core';
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
@@ -22,16 +25,34 @@ import {
|
||||
dealOperations
|
||||
} from './DealDescription';
|
||||
|
||||
import { IContact, IContactUpdate } from './ContactInterface';
|
||||
import { agileCrmApiRequest, agileCrmApiRequestUpdate, validateJSON } from './GenericFunctions';
|
||||
import { IDeal } from './DealInterface';
|
||||
import {
|
||||
IContact,
|
||||
IContactUpdate,
|
||||
} from './ContactInterface';
|
||||
|
||||
import {
|
||||
agileCrmApiRequest, agileCrmApiRequestAllItems,
|
||||
agileCrmApiRequestUpdate,
|
||||
getFilterRules,
|
||||
simplifyResponse,
|
||||
validateJSON,
|
||||
} from './GenericFunctions';
|
||||
|
||||
import {
|
||||
IDeal,
|
||||
} from './DealInterface';
|
||||
|
||||
import {
|
||||
IFilter,
|
||||
ISearchConditions,
|
||||
} from './FilterInterface';
|
||||
|
||||
export class AgileCrm implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Agile CRM',
|
||||
name: 'agileCrm',
|
||||
icon: 'file:agilecrm.png',
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
group: ['transform'],
|
||||
version: 1,
|
||||
description: 'Consume Agile CRM API',
|
||||
@@ -86,7 +107,6 @@ export class AgileCrm implements INodeType {
|
||||
|
||||
};
|
||||
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
|
||||
const items = this.getInputData();
|
||||
@@ -113,26 +133,59 @@ export class AgileCrm implements INodeType {
|
||||
responseData = await agileCrmApiRequest.call(this, 'DELETE', endpoint, {});
|
||||
|
||||
} else if (operation === 'getAll') {
|
||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
const simple = this.getNodeParameter('simple', 0) as boolean;
|
||||
const returnAll = this.getNodeParameter('returnAll', 0) as boolean;
|
||||
const filterType = this.getNodeParameter('filterType', i) as string;
|
||||
const sort = this.getNodeParameter('options.sort.sort', i, {}) as { direction: string, field: string };
|
||||
const body: IDataObject = {};
|
||||
const filterJson: IFilter = {};
|
||||
|
||||
let contactType = '';
|
||||
if (resource === 'contact') {
|
||||
if (returnAll) {
|
||||
const endpoint = 'api/contacts';
|
||||
responseData = await agileCrmApiRequest.call(this, 'GET', endpoint, {});
|
||||
} else {
|
||||
const limit = this.getNodeParameter('limit', i) as number;
|
||||
const endpoint = `api/contacts?page_size=${limit}`;
|
||||
responseData = await agileCrmApiRequest.call(this, 'GET', endpoint, {});
|
||||
}
|
||||
contactType = 'PERSON';
|
||||
} else {
|
||||
if (returnAll) {
|
||||
const endpoint = 'api/contacts/companies/list';
|
||||
responseData = await agileCrmApiRequest.call(this, 'POST', endpoint, {});
|
||||
contactType = 'COMPANY';
|
||||
}
|
||||
filterJson.contact_type = contactType;
|
||||
|
||||
if (filterType === 'manual') {
|
||||
const conditions = this.getNodeParameter('filters.conditions', i, []) as ISearchConditions[];
|
||||
const matchType = this.getNodeParameter('matchType', i) as string;
|
||||
let rules;
|
||||
if (conditions.length !== 0) {
|
||||
rules = getFilterRules(conditions, matchType);
|
||||
Object.assign(filterJson, rules);
|
||||
} else {
|
||||
const limit = this.getNodeParameter('limit', i) as number;
|
||||
const endpoint = `api/contacts/companies/list?page_size=${limit}`;
|
||||
responseData = await agileCrmApiRequest.call(this, 'POST', endpoint, {});
|
||||
throw new NodeOperationError(this.getNode(), 'At least one condition must be added.');
|
||||
}
|
||||
} else if (filterType === 'json') {
|
||||
const filterJsonRules = this.getNodeParameter('filterJson', i) as string;
|
||||
if (validateJSON(filterJsonRules) !== undefined) {
|
||||
Object.assign(filterJson, JSON.parse(filterJsonRules) as IFilter);
|
||||
} else {
|
||||
throw new NodeOperationError(this.getNode(), 'Filter (JSON) must be a valid json');
|
||||
}
|
||||
}
|
||||
body.filterJson = JSON.stringify(filterJson);
|
||||
|
||||
if (sort) {
|
||||
if (sort.direction === 'ASC') {
|
||||
body.global_sort_key = sort.field;
|
||||
} else if (sort.direction === 'DESC') {
|
||||
body.global_sort_key = `-${sort.field}`;
|
||||
}
|
||||
}
|
||||
|
||||
if (returnAll) {
|
||||
body.page_size = 100;
|
||||
responseData = await agileCrmApiRequestAllItems.call(this, 'POST', `api/filters/filter/dynamic-filter`, body, undefined, undefined, true);
|
||||
} else {
|
||||
body.page_size = this.getNodeParameter('limit', 0) as number;
|
||||
responseData = await agileCrmApiRequest.call(this, 'POST', `api/filters/filter/dynamic-filter`, body, undefined, undefined, true);
|
||||
}
|
||||
|
||||
if (simple) {
|
||||
responseData = simplifyResponse(responseData);
|
||||
}
|
||||
|
||||
} else if (operation === 'create') {
|
||||
@@ -461,15 +514,15 @@ export class AgileCrm implements INodeType {
|
||||
responseData = await agileCrmApiRequest.call(this, 'DELETE', endpoint, {});
|
||||
|
||||
} else if (operation === 'getAll') {
|
||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
const returnAll = this.getNodeParameter('returnAll', 0) as boolean;
|
||||
const endpoint = 'api/opportunity';
|
||||
|
||||
if (returnAll) {
|
||||
const endpoint = 'api/opportunity';
|
||||
responseData = await agileCrmApiRequest.call(this, 'GET', endpoint, {});
|
||||
const limit = 100;
|
||||
responseData = await agileCrmApiRequestAllItems.call(this, 'GET', endpoint, undefined, { page_size: limit });
|
||||
} else {
|
||||
const limit = this.getNodeParameter('limit', i) as number;
|
||||
const endpoint = `api/opportunity?page_size=${limit}`;
|
||||
responseData = await agileCrmApiRequest.call(this, 'GET', endpoint, {});
|
||||
const limit = this.getNodeParameter('limit', 0) as number;
|
||||
responseData = await agileCrmApiRequest.call(this, 'GET', endpoint, undefined, { page_size: limit });
|
||||
}
|
||||
|
||||
} else if (operation === 'create') {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const companyOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
@@ -44,6 +45,7 @@ export const companyOperations = [
|
||||
description: 'The operation to perform.',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const companyFields = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* company:get */
|
||||
@@ -91,7 +93,6 @@ export const companyFields = [
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 20,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
@@ -105,7 +106,280 @@ export const companyFields = [
|
||||
],
|
||||
},
|
||||
},
|
||||
default: 20,
|
||||
description: 'Number of results to fetch.',
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Filter',
|
||||
name: 'filterType',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'None',
|
||||
value: 'none',
|
||||
},
|
||||
{
|
||||
name: 'Build Manually',
|
||||
value: 'manual',
|
||||
},
|
||||
{
|
||||
name: 'JSON',
|
||||
value: 'json',
|
||||
},
|
||||
],
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'company',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: 'none',
|
||||
},
|
||||
{
|
||||
displayName: 'Must Match',
|
||||
name: 'matchType',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Any filter',
|
||||
value: 'anyFilter',
|
||||
},
|
||||
{
|
||||
name: 'All Filters',
|
||||
value: 'allFilters',
|
||||
},
|
||||
],
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'company',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
filterType: [
|
||||
'manual',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: 'anyFilter',
|
||||
},
|
||||
{
|
||||
displayName: 'Simplify Response',
|
||||
name: 'simple',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'company',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Return a simplified version of the response instead of the raw data.',
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'company',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
filterType: [
|
||||
'manual',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
placeholder: 'Add Condition',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Conditions',
|
||||
name: 'conditions',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'field',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Any searchable field.',
|
||||
},
|
||||
{
|
||||
displayName: 'Condition Type',
|
||||
name: 'condition_type',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Equals',
|
||||
value: 'EQUALS',
|
||||
},
|
||||
{
|
||||
name: 'Not Equal',
|
||||
value: 'NOTEQUALS',
|
||||
},
|
||||
{
|
||||
name: 'Last',
|
||||
value: 'LAST',
|
||||
},
|
||||
{
|
||||
name: 'Between',
|
||||
value: 'BETWEEN',
|
||||
},
|
||||
{
|
||||
name: 'On',
|
||||
value: 'ON',
|
||||
},
|
||||
{
|
||||
name: 'Before',
|
||||
value: 'BEFORE',
|
||||
},
|
||||
{
|
||||
name: 'After',
|
||||
value: 'AFTER',
|
||||
},
|
||||
],
|
||||
default: 'EQUALS',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Value 2',
|
||||
name: 'value2',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
condition_type: [
|
||||
'BETWEEN',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'See <a href="https://github.com/agilecrm/rest-api#121-get-contacts-by-dynamic-filter" target="_blank">Agile CRM guide</a> to creating filters',
|
||||
name: 'jsonNotice',
|
||||
type: 'notice',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'company',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
filterType: [
|
||||
'json',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Filters (JSON)',
|
||||
name: 'filterJson',
|
||||
type: 'string',
|
||||
typeOptions: {
|
||||
alwaysOpenEditWindow: true,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'company',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
filterType: [
|
||||
'json',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Option',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'company',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Sort',
|
||||
name: 'sort',
|
||||
type: 'fixedCollection',
|
||||
placeholder: 'Add Sort',
|
||||
default: [],
|
||||
options: [
|
||||
{
|
||||
displayName: 'Sort',
|
||||
name: 'sort',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Direction',
|
||||
name: 'direction',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Ascending',
|
||||
value: 'ASC',
|
||||
},
|
||||
{
|
||||
name: 'Descending',
|
||||
value: 'DESC',
|
||||
},
|
||||
],
|
||||
default: 'ASC',
|
||||
description: 'The sorting direction',
|
||||
},
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'field',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `The sorting field`,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* company:create */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
@@ -657,4 +931,5 @@ export const companyFields = [
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
] as INodeProperties[];
|
||||
|
||||
@@ -71,25 +71,7 @@ export const contactFields = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* contact:get all */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 20,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
@@ -107,6 +89,296 @@ export const contactFields = [
|
||||
default: false,
|
||||
description: 'If all results should be returned or only up to a given limit.',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
default: 20,
|
||||
description: 'Number of results to fetch.',
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Filter',
|
||||
name: 'filterType',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'None',
|
||||
value: 'none',
|
||||
},
|
||||
{
|
||||
name: 'Build Manually',
|
||||
value: 'manual',
|
||||
},
|
||||
{
|
||||
name: 'JSON',
|
||||
value: 'json',
|
||||
},
|
||||
],
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: 'none',
|
||||
},
|
||||
{
|
||||
displayName: 'Must Match',
|
||||
name: 'matchType',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Any filter',
|
||||
value: 'anyFilter',
|
||||
},
|
||||
{
|
||||
name: 'All Filters',
|
||||
value: 'allFilters',
|
||||
},
|
||||
],
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
filterType: [
|
||||
'manual',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: 'anyFilter',
|
||||
},
|
||||
{
|
||||
displayName: 'Simplify Response',
|
||||
name: 'simple',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Return a simplified version of the response instead of the raw data.',
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
filterType: [
|
||||
'manual',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
placeholder: 'Add Condition',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Conditions',
|
||||
name: 'conditions',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'field',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Any searchable field.',
|
||||
},
|
||||
{
|
||||
displayName: 'Condition Type',
|
||||
name: 'condition_type',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Equals',
|
||||
value: 'EQUALS',
|
||||
},
|
||||
{
|
||||
name: 'Not Equal',
|
||||
value: 'NOTEQUALS',
|
||||
},
|
||||
{
|
||||
name: 'Last',
|
||||
value: 'LAST',
|
||||
},
|
||||
{
|
||||
name: 'Between',
|
||||
value: 'BETWEEN',
|
||||
},
|
||||
{
|
||||
name: 'On',
|
||||
value: 'ON',
|
||||
},
|
||||
{
|
||||
name: 'Before',
|
||||
value: 'BEFORE',
|
||||
},
|
||||
{
|
||||
name: 'After',
|
||||
value: 'AFTER',
|
||||
},
|
||||
],
|
||||
default: 'EQUALS',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Value 2',
|
||||
name: 'value2',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
condition_type: [
|
||||
'BETWEEN',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'See <a href="https://github.com/agilecrm/rest-api#121-get-contacts-by-dynamic-filter" target="_blank">Agile CRM guide</a> to creating filters',
|
||||
name: 'jsonNotice',
|
||||
type: 'notice',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
filterType: [
|
||||
'json',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Filters (JSON)',
|
||||
name: 'filterJson',
|
||||
type: 'string',
|
||||
typeOptions: {
|
||||
alwaysOpenEditWindow: true,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
filterType: [
|
||||
'json',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Option',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Sort',
|
||||
name: 'sort',
|
||||
type: 'fixedCollection',
|
||||
placeholder: 'Add Sort',
|
||||
default: [],
|
||||
options: [
|
||||
{
|
||||
displayName: 'Sort',
|
||||
name: 'sort',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Direction',
|
||||
name: 'direction',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Ascending',
|
||||
value: 'ASC',
|
||||
},
|
||||
{
|
||||
name: 'Descending',
|
||||
value: 'DESC',
|
||||
},
|
||||
],
|
||||
default: 'ASC',
|
||||
description: 'The sorting direction',
|
||||
},
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'field',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `The sorting field`,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* contact:create */
|
||||
@@ -988,4 +1260,5 @@ export const contactFields = [
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
] as INodeProperties[];
|
||||
|
||||
@@ -110,8 +110,6 @@ export const dealFields = [
|
||||
default: false,
|
||||
description: 'If all results should be returned or only up to a given limit.',
|
||||
},
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* deal:create */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
19
packages/nodes-base/nodes/AgileCrm/FilterInterface.ts
Normal file
19
packages/nodes-base/nodes/AgileCrm/FilterInterface.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export interface ISearchConditions {
|
||||
field?: string;
|
||||
condition_type?: string;
|
||||
value?: string;
|
||||
value2?: string;
|
||||
}
|
||||
|
||||
export interface IFilterRules {
|
||||
LHS?: string;
|
||||
CONDITION?: string;
|
||||
RHS?: string;
|
||||
RHS_NEW?: string;
|
||||
}
|
||||
|
||||
export interface IFilter {
|
||||
or_rules?: IFilterRules;
|
||||
rules?: IFilterRules;
|
||||
contact_type?: string;
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
import {
|
||||
OptionsWithUri
|
||||
} from 'request';
|
||||
import { OptionsWithUri } from 'request';
|
||||
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
@@ -10,12 +8,21 @@ import {
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject, NodeApiError,
|
||||
IDataObject,
|
||||
NodeApiError,
|
||||
} from 'n8n-workflow';
|
||||
import { IContactUpdate } from './ContactInterface';
|
||||
|
||||
import {
|
||||
IContactUpdate,
|
||||
} from './ContactInterface';
|
||||
|
||||
export async function agileCrmApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, query: IDataObject = {}, uri?: string): Promise<any> { // tslint:disable-line:no-any
|
||||
import {
|
||||
IFilterRules,
|
||||
ISearchConditions,
|
||||
} from './FilterInterface';
|
||||
|
||||
export async function agileCrmApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
|
||||
method: string, endpoint: string, body: any = {}, query: IDataObject = {}, uri?: string, sendAsForm?: boolean): Promise<any> { // tslint:disable-line:no-any
|
||||
|
||||
const credentials = await this.getCredentials('agileCrmApi');
|
||||
const options: OptionsWithUri = {
|
||||
@@ -27,12 +34,18 @@ export async function agileCrmApiRequest(this: IHookFunctions | IExecuteFunction
|
||||
username: credentials!.email as string,
|
||||
password: credentials!.apiKey as string,
|
||||
},
|
||||
qs: query,
|
||||
uri: uri || `https://${credentials!.subdomain}.agilecrm.com/dev/${endpoint}`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
// To send the request as 'content-type': 'application/x-www-form-urlencoded' add form to options instead of body
|
||||
if(sendAsForm) {
|
||||
options.form = body;
|
||||
}
|
||||
// Only add Body property if method not GET or DELETE to avoid 400 response
|
||||
if (method !== 'GET' && method !== 'DELETE') {
|
||||
// And when not sending a form
|
||||
else if (method !== 'GET' && method !== 'DELETE') {
|
||||
options.body = body;
|
||||
}
|
||||
|
||||
@@ -41,7 +54,30 @@ export async function agileCrmApiRequest(this: IHookFunctions | IExecuteFunction
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function agileCrmApiRequestAllItems(this: IHookFunctions | ILoadOptionsFunctions | IExecuteFunctions,
|
||||
method: string, resource: string, body: any = {}, query: IDataObject = {}, uri?: string, sendAsForm?: boolean): Promise<any> { // tslint:disable-line:no-any
|
||||
// https://github.com/agilecrm/rest-api#11-listing-contacts-
|
||||
|
||||
const returnData: IDataObject[] = [];
|
||||
let responseData;
|
||||
do {
|
||||
responseData = await agileCrmApiRequest.call(this, method, resource, body, query, uri, sendAsForm);
|
||||
if (responseData.length !== 0) {
|
||||
returnData.push.apply(returnData, responseData);
|
||||
if (sendAsForm) {
|
||||
body.cursor = responseData[responseData.length-1].cursor;
|
||||
} else {
|
||||
query.cursor = responseData[responseData.length-1].cursor;
|
||||
}
|
||||
}
|
||||
} while (
|
||||
responseData.length !== 0 &&
|
||||
responseData[responseData.length-1].hasOwnProperty('cursor')
|
||||
);
|
||||
|
||||
return returnData;
|
||||
}
|
||||
|
||||
export async function agileCrmApiRequestUpdate(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method = 'PUT', endpoint?: string, body: any = {}, query: IDataObject = {}, uri?: string): Promise<any> { // tslint:disable-line:no-any
|
||||
@@ -131,3 +167,39 @@ export function validateJSON(json: string | undefined): any { // tslint:disable-
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getFilterRules(conditions: ISearchConditions[], matchType: string): IDataObject { // tslint:disable-line:no-any
|
||||
const rules = [];
|
||||
|
||||
for (const key in conditions) {
|
||||
if (conditions.hasOwnProperty(key)) {
|
||||
const searchConditions: ISearchConditions = conditions[key] as ISearchConditions;
|
||||
const rule: IFilterRules = {
|
||||
LHS: searchConditions.field,
|
||||
CONDITION: searchConditions.condition_type,
|
||||
RHS: searchConditions.value as string,
|
||||
RHS_NEW: searchConditions.value2 as string,
|
||||
};
|
||||
rules.push(rule);
|
||||
}
|
||||
}
|
||||
|
||||
if (matchType === 'anyFilter') {
|
||||
return {
|
||||
or_rules: rules,
|
||||
};
|
||||
}
|
||||
else {
|
||||
return {
|
||||
rules,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function simplifyResponse(records: [{ id: string, properties: [{ name: string, value: string }] } ]) {
|
||||
const results = [];
|
||||
for (const record of records) {
|
||||
results.push(record.properties.reduce((obj, value) => Object.assign(obj, { [`${value.name}`]: value.value }), { id: record.id }));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -256,7 +256,7 @@ export class Airtable implements INodeType {
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'NOT({Name} = \'\')',
|
||||
description: 'A formula used to filter records. The formula will be evaluated for each<br />record, and if the result is not 0, false, "", NaN, [], or #Error!<br />the record will be included in the response.',
|
||||
description: 'A formula used to filter records. The formula will be evaluated for each record, and if the result is not 0, false, "", NaN, [], or #Error! the record will be included in the response.',
|
||||
},
|
||||
{
|
||||
displayName: 'Sort',
|
||||
@@ -309,7 +309,7 @@ export class Airtable implements INodeType {
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'All Stories',
|
||||
description: 'The name or ID of a view in the Stories table. If set,<br />only the records in that view will be returned. The records<br />will be sorted according to the order of the view.',
|
||||
description: 'The name or ID of a view in the Stories table. If set, only the records in that view will be returned. The records will be sorted according to the order of the view.',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -741,4 +741,4 @@ export class Airtable implements INodeType {
|
||||
|
||||
return [this.helpers.returnJsonArray(returnData)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,9 +61,7 @@ export class AirtableTrigger implements INodeType {
|
||||
name: 'triggerField',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `A Created Time or Last Modified Time field that will be used to sort records. <br>
|
||||
If you do not have a Created Time or Last Modified Time field in your schema, please create one,<br>
|
||||
because without this field trigger will not work correctly.`,
|
||||
description: `A Created Time or Last Modified Time field that will be used to sort records. If you do not have a Created Time or Last Modified Time field in your schema, please create one, because without this field trigger will not work correctly.`,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
@@ -100,9 +98,7 @@ export class AirtableTrigger implements INodeType {
|
||||
name: 'fields',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `Fields to be included in the response.<br>
|
||||
Multiple ones can be set separated by comma. Example: name,id.<br>
|
||||
By default just the trigger field will be included.`,
|
||||
description: `Fields to be included in the response. Multiple ones can be set separated by comma. Example: <code>name, id</code>. By default just the trigger field will be included.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Formula',
|
||||
|
||||
@@ -180,7 +180,7 @@ export class ApiTemplateIo implements INodeType {
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Name of the binary property to which to<br />write the data of the read file.',
|
||||
description: 'Name of the binary property to which to write the data of the read file.',
|
||||
},
|
||||
{
|
||||
displayName: 'Binary Property',
|
||||
|
||||
@@ -599,7 +599,7 @@ export class Asana implements INodeType {
|
||||
loadOptionsMethod: 'getUsers',
|
||||
},
|
||||
default: '',
|
||||
description: 'The assignee to filter tasks on. Note: If you specify assignee, you must also specify the workspace to filter on.',
|
||||
description: 'The assignee to filter tasks on. Note: If you specify assignee, you must also specify the workspace to filter on.',
|
||||
},
|
||||
{
|
||||
displayName: 'Fields',
|
||||
@@ -650,7 +650,7 @@ export class Asana implements INodeType {
|
||||
loadOptionsMethod: 'getWorkspaces',
|
||||
},
|
||||
default: '',
|
||||
description: 'The workspace to filter tasks on. Note: If you specify workspace, you must also specify the assignee to filter on.',
|
||||
description: 'The workspace to filter tasks on. Note: If you specify workspace, you must also specify the assignee to filter on.',
|
||||
},
|
||||
{
|
||||
displayName: 'Completed Since',
|
||||
@@ -1438,7 +1438,7 @@ export class Asana implements INodeType {
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'An identifier for the user to get data of. Can be one of an<br />email address,the globally unique identifier for the user,<br />or the keyword me to indicate the current user making the request.',
|
||||
description: 'An identifier for the user to get data of. Can be one of an email address,the globally unique identifier for the user, or the keyword me to indicate the current user making the request.',
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
|
||||
@@ -217,7 +217,7 @@ export const contactFields = [
|
||||
name: 'notify',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: `By default Autopilot notifies registered REST hook endpoints for contact_added/contact_updated events when</br> a new contact is added or an existing contact is updated via API. Disable to skip notifications.`,
|
||||
description: `By default Autopilot notifies registered REST hook endpoints for contact_added/contact_updated events when a new contact is added or an existing contact is updated via API. Disable to skip notifications.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Number of Employees',
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
} from 'url';
|
||||
|
||||
import {
|
||||
Request,
|
||||
sign,
|
||||
} from 'aws4';
|
||||
|
||||
@@ -47,7 +48,7 @@ export async function awsApiRequest(this: IHookFunctions | IExecuteFunctions | I
|
||||
const endpoint = new URL(getEndpointForService(service, credentials) + path);
|
||||
|
||||
// Sign AWS API request with the user credentials
|
||||
const signOpts = { headers: headers || {}, host: endpoint.host, method, path, body };
|
||||
const signOpts = { headers: headers || {}, host: endpoint.host, method, path, body } as Request;
|
||||
sign(signOpts, { accessKeyId: `${credentials.accessKeyId}`.trim(), secretAccessKey: `${credentials.secretAccessKey}`.trim() });
|
||||
|
||||
|
||||
|
||||
@@ -312,7 +312,7 @@ export class AwsDynamoDB implements INodeType {
|
||||
if (scan === true) {
|
||||
body['FilterExpression'] = this.getNodeParameter('filterExpression', i) as string;
|
||||
} else {
|
||||
body['KeyConditionExpression'] = this.getNodeParameter('KeyConditionExpression', i) as string;
|
||||
body['KeyConditionExpression'] = this.getNodeParameter('keyConditionExpression', i) as string;
|
||||
}
|
||||
|
||||
const {
|
||||
|
||||
@@ -45,9 +45,10 @@ export async function awsApiRequest(this: IHookFunctions | IExecuteFunctions | I
|
||||
const endpoint = new URL(getEndpointForService(service, credentials) + path);
|
||||
|
||||
const options = sign({
|
||||
// @ts-ignore
|
||||
uri: endpoint,
|
||||
service,
|
||||
region: credentials.region,
|
||||
region: credentials.region as string,
|
||||
method,
|
||||
path: '/',
|
||||
headers: { ...headers },
|
||||
|
||||
@@ -172,7 +172,7 @@ export const itemFields = [
|
||||
{
|
||||
displayName: 'Expression Attribute Values',
|
||||
name: 'eavUi',
|
||||
description: 'Substitution tokens for attribute names in an expression.<br>Only needed when the parameter "condition expression" is set',
|
||||
description: 'Substitution tokens for attribute names in an expression. Only needed when the parameter "condition expression" is set',
|
||||
placeholder: 'Add Attribute Value',
|
||||
type: 'fixedCollection',
|
||||
default: '',
|
||||
@@ -350,7 +350,7 @@ export const itemFields = [
|
||||
],
|
||||
},
|
||||
],
|
||||
description: 'Item\'s primary key. For example, with a simple primary key, you only need to provide a value for the partition key.<br>For a composite primary key, you must provide values for both the partition key and the sort key',
|
||||
description: 'Item\'s primary key. For example, with a simple primary key, you only need to provide a value for the partition key. For a composite primary key, you must provide values for both the partition key and the sort key',
|
||||
},
|
||||
{
|
||||
displayName: 'Simple',
|
||||
@@ -430,7 +430,7 @@ export const itemFields = [
|
||||
{
|
||||
displayName: 'Expression Attribute Values',
|
||||
name: 'expressionAttributeUi',
|
||||
description: 'Substitution tokens for attribute names in an expression.<br>Only needed when the parameter "condition expression" is set',
|
||||
description: 'Substitution tokens for attribute names in an expression. Only needed when the parameter "condition expression" is set',
|
||||
placeholder: 'Add Attribute Value',
|
||||
type: 'fixedCollection',
|
||||
default: '',
|
||||
@@ -593,7 +593,7 @@ export const itemFields = [
|
||||
],
|
||||
},
|
||||
],
|
||||
description: 'Item\'s primary key. For example, with a simple primary key, you only need to provide a value for the partition key.<br>For a composite primary key, you must provide values for both the partition key and the sort key',
|
||||
description: 'Item\'s primary key. For example, with a simple primary key, you only need to provide a value for the partition key. For a composite primary key, you must provide values for both the partition key and the sort key',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
@@ -708,7 +708,7 @@ export const itemFields = [
|
||||
{
|
||||
displayName: 'Key Condition Expression',
|
||||
name: 'keyConditionExpression',
|
||||
description: 'Condition to determine the items to be retrieved. The condition must perform an equality test<br>on a single partition key value, in this format: <code>partitionKeyName = :partitionkeyval</code>',
|
||||
description: 'Condition to determine the items to be retrieved. The condition must perform an equality test on a single partition key value, in this format: <code>partitionKeyName = :partitionkeyval</code>',
|
||||
placeholder: 'id = :id',
|
||||
default: '',
|
||||
type: 'string',
|
||||
@@ -901,7 +901,7 @@ export const itemFields = [
|
||||
{
|
||||
displayName: 'Index Name',
|
||||
name: 'indexName',
|
||||
description: 'Name of the index to query. It can be any <br>secondary local or global index on the table.',
|
||||
description: 'Name of the index to query. It can be any secondary local or global index on the table.',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
@@ -910,7 +910,7 @@ export const itemFields = [
|
||||
name: 'projectionExpression',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Text that identifies one or more attributes to retrieve from the table.<br>These attributes can include scalars, sets, or elements of a JSON document. The attributes<br>in the expression must be separated by commas',
|
||||
description: 'Text that identifies one or more attributes to retrieve from the table. These attributes can include scalars, sets, or elements of a JSON document. The attributes in the expression must be separated by commas',
|
||||
},
|
||||
{
|
||||
displayName: 'Filter Expression',
|
||||
@@ -924,7 +924,7 @@ export const itemFields = [
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'Text that contains conditions that DynamoDB applies after the Query operation,<br>but before the data is returned. Items that do not satisfy the FilterExpression criteria</br>are not returned',
|
||||
description: 'Text that contains conditions that DynamoDB applies after the Query operation, but before the data is returned. Items that do not satisfy the FilterExpression criteria are not returned',
|
||||
},
|
||||
{
|
||||
displayName: 'Expression Attribute Names',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { URL } from 'url';
|
||||
import { sign } from 'aws4';
|
||||
import { Request, sign } from 'aws4';
|
||||
import { OptionsWithUri } from 'request';
|
||||
import { parseString as parseXml } from 'xml2js';
|
||||
|
||||
@@ -38,7 +38,7 @@ export async function awsApiRequest(this: IHookFunctions | IExecuteFunctions | I
|
||||
const endpoint = new URL(getEndpointForService(service, credentials) + path);
|
||||
|
||||
// Sign AWS API request with the user credentials
|
||||
const signOpts = { headers: headers || {}, host: endpoint.host, method, path, body };
|
||||
const signOpts = { headers: headers || {}, host: endpoint.host, method, path, body } as Request;
|
||||
sign(signOpts, { accessKeyId: `${credentials.accessKeyId}`.trim(), secretAccessKey: `${credentials.secretAccessKey}`.trim() });
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
} from 'url';
|
||||
|
||||
import {
|
||||
Request,
|
||||
sign,
|
||||
} from 'aws4';
|
||||
|
||||
@@ -44,7 +45,7 @@ export async function awsApiRequest(this: IHookFunctions | IExecuteFunctions | I
|
||||
const endpoint = new URL(((credentials.rekognitionEndpoint as string || '').replace('{region}', credentials.region as string) || `https://${service}.${credentials.region}.amazonaws.com`) + path);
|
||||
|
||||
// Sign AWS API request with the user credentials
|
||||
const signOpts = {headers: headers || {}, host: endpoint.host, method, path, body};
|
||||
const signOpts = {headers: headers || {}, host: endpoint.host, method, path, body} as Request;
|
||||
|
||||
sign(signOpts, { accessKeyId: `${credentials.accessKeyId}`.trim(), secretAccessKey: `${credentials.secretAccessKey}`.trim()});
|
||||
|
||||
|
||||
@@ -450,7 +450,7 @@ export const fileFields = [
|
||||
|
||||
},
|
||||
placeholder: '',
|
||||
description: 'Name of the binary property which contains<br />the data for the file to be uploaded.',
|
||||
description: 'Name of the binary property which contains the data for the file to be uploaded.',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
@@ -762,7 +762,7 @@ export const fileFields = [
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Name of the binary property to which to<br />write the data of the read file.',
|
||||
description: 'Name of the binary property to which to write the data of the read file.',
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* file:delete */
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
} from 'url';
|
||||
|
||||
import {
|
||||
Request,
|
||||
sign,
|
||||
} from 'aws4';
|
||||
|
||||
@@ -38,7 +39,7 @@ export async function awsApiRequest(this: IHookFunctions | IExecuteFunctions | I
|
||||
const endpoint = new URL(((credentials.s3Endpoint as string || '').replace('{region}', credentials.region as string) || `https://${service}.${credentials.region}.amazonaws.com`) + path);
|
||||
|
||||
// Sign AWS API request with the user credentials
|
||||
const signOpts = {headers: headers || {}, host: endpoint.host, method, path: `${endpoint.pathname}?${queryToString(query).replace(/\+/g, '%2B')}`, body};
|
||||
const signOpts = {headers: headers || {}, host: endpoint.host, method, path: `${endpoint.pathname}?${queryToString(query).replace(/\+/g, '%2B')}`, body} as Request;
|
||||
|
||||
|
||||
sign(signOpts, { accessKeyId: `${credentials.accessKeyId}`.trim(), secretAccessKey: `${credentials.secretAccessKey}`.trim()});
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
} from 'url';
|
||||
|
||||
import {
|
||||
Request,
|
||||
sign,
|
||||
} from 'aws4';
|
||||
|
||||
@@ -39,7 +40,7 @@ export async function awsApiRequest(this: IHookFunctions | IExecuteFunctions | I
|
||||
|
||||
// Sign AWS API request with the user credentials
|
||||
|
||||
const signOpts = { headers: headers || {}, host: endpoint.host, method, path, body };
|
||||
const signOpts = { headers: headers || {}, host: endpoint.host, method, path, body } as Request;
|
||||
sign(signOpts, { accessKeyId: `${credentials.accessKeyId}`.trim(), secretAccessKey: `${credentials.secretAccessKey}`.trim() });
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
|
||||
20
packages/nodes-base/nodes/Aws/Textract/AwsTextract.node.json
Normal file
20
packages/nodes-base/nodes/Aws/Textract/AwsTextract.node.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"node": "n8n-nodes-base.awsTextract",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": [
|
||||
"Utility"
|
||||
],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/credentials/aws"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.awsTextract/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
163
packages/nodes-base/nodes/Aws/Textract/AwsTextract.node.ts
Normal file
163
packages/nodes-base/nodes/Aws/Textract/AwsTextract.node.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IBinaryKeyData,
|
||||
ICredentialDataDecryptedObject,
|
||||
ICredentialsDecrypted,
|
||||
ICredentialTestFunctions,
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
NodeCredentialTestResult,
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
awsApiRequestREST,
|
||||
IExpenseDocument,
|
||||
simplify,
|
||||
validateCrendetials,
|
||||
} from './GenericFunctions';
|
||||
|
||||
export class AwsTextract implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'AWS Textract',
|
||||
name: 'awsTextract',
|
||||
icon: 'file:textract.svg',
|
||||
group: ['output'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"]}}',
|
||||
description: 'Sends data to Amazon Textract',
|
||||
defaults: {
|
||||
name: 'AWS Textract',
|
||||
color: '#5aa08d',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'aws',
|
||||
required: true,
|
||||
testedBy: 'awsTextractApiCredentialTest',
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Analyze Receipt or Invoice',
|
||||
value: 'analyzeExpense',
|
||||
},
|
||||
],
|
||||
default: 'analyzeExpense',
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Input Data Field Name',
|
||||
name: 'binaryPropertyName',
|
||||
type: 'string',
|
||||
default: 'data',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'analyzeExpense',
|
||||
],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'The name of the input field containing the binary file data to be uploaded. Supported file types: PNG, JPEG',
|
||||
},
|
||||
{
|
||||
displayName: 'Simplify Response',
|
||||
name: 'simple',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'analyzeExpense',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: true,
|
||||
description: 'Return a simplified version of the response instead of the raw data.',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
methods = {
|
||||
credentialTest: {
|
||||
async awsTextractApiCredentialTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise<NodeCredentialTestResult> {
|
||||
try {
|
||||
await validateCrendetials.call(this, credential.data as ICredentialDataDecryptedObject, 'sts');
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 'Error',
|
||||
message: 'The security token included in the request is invalid',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: 'OK',
|
||||
message: 'Connection successful!',
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
const returnData: IDataObject[] = [];
|
||||
let responseData;
|
||||
const operation = this.getNodeParameter('operation', 0) as string;
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
try {
|
||||
//https://docs.aws.amazon.com/textract/latest/dg/API_AnalyzeExpense.html
|
||||
if (operation === 'analyzeExpense') {
|
||||
const binaryProperty = this.getNodeParameter('binaryPropertyName', i) as string;
|
||||
const simple = this.getNodeParameter('simple', i) as boolean;
|
||||
|
||||
if (items[i].binary === undefined) {
|
||||
throw new NodeOperationError(this.getNode(), 'No binary data exists on item!');
|
||||
}
|
||||
|
||||
if ((items[i].binary as IBinaryKeyData)[binaryProperty] === undefined) {
|
||||
throw new NodeOperationError(this.getNode(), `No binary data property "${binaryProperty}" does not exists on item!`);
|
||||
}
|
||||
|
||||
const binaryPropertyData = (items[i].binary as IBinaryKeyData)[binaryProperty];
|
||||
|
||||
const body: IDataObject = {
|
||||
Document: {
|
||||
Bytes: binaryPropertyData.data,
|
||||
},
|
||||
};
|
||||
|
||||
const action = 'Textract.AnalyzeExpense';
|
||||
responseData = await awsApiRequestREST.call(this, 'textract', 'POST', '', JSON.stringify(body), { 'x-amz-target': action, 'Content-Type': 'application/x-amz-json-1.1' }) as IExpenseDocument;
|
||||
if (simple) {
|
||||
responseData = simplify(responseData);
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(responseData)) {
|
||||
returnData.push.apply(returnData, responseData as IDataObject[]);
|
||||
} else {
|
||||
returnData.push(responseData as unknown as IDataObject);
|
||||
}
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ error: error.message });
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return [this.helpers.returnJsonArray(returnData)];
|
||||
}
|
||||
}
|
||||
156
packages/nodes-base/nodes/Aws/Textract/GenericFunctions.ts
Normal file
156
packages/nodes-base/nodes/Aws/Textract/GenericFunctions.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
import {
|
||||
URL,
|
||||
} from 'url';
|
||||
|
||||
import {
|
||||
Request,
|
||||
sign,
|
||||
} from 'aws4';
|
||||
|
||||
import {
|
||||
OptionsWithUri,
|
||||
} from 'request';
|
||||
|
||||
import {
|
||||
parseString,
|
||||
} from 'xml2js';
|
||||
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
IHookFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
IWebhookFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
ICredentialDataDecryptedObject,
|
||||
ICredentialTestFunctions,
|
||||
NodeApiError,
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
function getEndpointForService(service: string, credentials: ICredentialDataDecryptedObject): string {
|
||||
let endpoint;
|
||||
if (service === 'lambda' && credentials.lambdaEndpoint) {
|
||||
endpoint = credentials.lambdaEndpoint;
|
||||
} else if (service === 'sns' && credentials.snsEndpoint) {
|
||||
endpoint = credentials.snsEndpoint;
|
||||
} else {
|
||||
endpoint = `https://${service}.${credentials.region}.amazonaws.com`;
|
||||
}
|
||||
return (endpoint as string).replace('{region}', credentials.region as string);
|
||||
}
|
||||
|
||||
export async function awsApiRequest(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions, service: string, method: string, path: string, body?: string, headers?: object): Promise<any> { // tslint:disable-line:no-any
|
||||
const credentials = await this.getCredentials('aws');
|
||||
if (credentials === undefined) {
|
||||
throw new NodeOperationError(this.getNode(), 'No credentials got returned!');
|
||||
}
|
||||
|
||||
// Concatenate path and instantiate URL object so it parses correctly query strings
|
||||
const endpoint = new URL(getEndpointForService(service, credentials) + path);
|
||||
|
||||
// Sign AWS API request with the user credentials
|
||||
const signOpts = { headers: headers || {}, host: endpoint.host, method, path, body } as Request;
|
||||
sign(signOpts, { accessKeyId: `${credentials.accessKeyId}`.trim(), secretAccessKey: `${credentials.secretAccessKey}`.trim() });
|
||||
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
headers: signOpts.headers,
|
||||
method,
|
||||
uri: endpoint.href,
|
||||
body: signOpts.body,
|
||||
};
|
||||
|
||||
try {
|
||||
return await this.helpers.request!(options);
|
||||
} catch (error) {
|
||||
if (error?.response?.data || error?.response?.body) {
|
||||
const errorMessage = error?.response?.data || error?.response?.body;
|
||||
if (errorMessage.includes('AccessDeniedException')) {
|
||||
const user = JSON.parse(errorMessage).Message.split(' ')[1];
|
||||
throw new NodeApiError(this.getNode(), error, {
|
||||
message: 'Unauthorized — please check your AWS policy configuration',
|
||||
description: `Make sure an identity-based policy allows user ${user} to perform textract:AnalyzeExpense` });
|
||||
}
|
||||
}
|
||||
|
||||
throw new NodeApiError(this.getNode(), error); // no XML parsing needed
|
||||
}
|
||||
}
|
||||
|
||||
export async function awsApiRequestREST(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, service: string, method: string, path: string, body?: string, headers?: object): Promise<any> { // tslint:disable-line:no-any
|
||||
const response = await awsApiRequest.call(this, service, method, path, body, headers);
|
||||
try {
|
||||
return JSON.parse(response);
|
||||
} catch (error) {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
export async function awsApiRequestSOAP(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions, service: string, method: string, path: string, body?: string, headers?: object): Promise<any> { // tslint:disable-line:no-any
|
||||
const response = await awsApiRequest.call(this, service, method, path, body, headers);
|
||||
try {
|
||||
return await new Promise((resolve, reject) => {
|
||||
parseString(response, { explicitArray: false }, (err, data) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
export function simplify(data: IExpenseDocument) {
|
||||
const result: { [key: string]: string } = {};
|
||||
for (const document of data.ExpenseDocuments) {
|
||||
for (const field of document.SummaryFields) {
|
||||
result[field?.Type?.Text || field?.LabelDetection?.Text] = field.ValueDetection.Text;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export interface IExpenseDocument {
|
||||
ExpenseDocuments: [
|
||||
{
|
||||
SummaryFields: [
|
||||
{
|
||||
LabelDetection: { Text: string },
|
||||
ValueDetection: { Text: string },
|
||||
Type: { Text: string }
|
||||
}]
|
||||
}];
|
||||
}
|
||||
|
||||
export async function validateCrendetials(this: ICredentialTestFunctions, decryptedCredentials: ICredentialDataDecryptedObject, service: string): Promise<any> { // tslint:disable-line:no-any
|
||||
const credentials = decryptedCredentials;
|
||||
|
||||
// Concatenate path and instantiate URL object so it parses correctly query strings
|
||||
const endpoint = new URL(getEndpointForService(service, credentials) + `?Action=GetCallerIdentity&Version=2011-06-15`);
|
||||
|
||||
// Sign AWS API request with the user credentials
|
||||
const signOpts = { host: endpoint.host, method: 'POST', path: '?Action=GetCallerIdentity&Version=2011-06-15' } as Request;
|
||||
sign(signOpts, { accessKeyId: `${credentials.accessKeyId}`.trim(), secretAccessKey: `${credentials.secretAccessKey}`.trim() });
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
headers: signOpts.headers,
|
||||
method: 'POST',
|
||||
uri: endpoint.href,
|
||||
body: signOpts.body,
|
||||
};
|
||||
|
||||
const response = await this.helpers.request!(options);
|
||||
|
||||
return await new Promise((resolve, reject) => {
|
||||
parseString(response, { explicitArray: false }, (err, data) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
18
packages/nodes-base/nodes/Aws/Textract/textract.svg
Normal file
18
packages/nodes-base/nodes/Aws/Textract/textract.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="80px" height="80px" viewBox="0 0 80 80" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 64 (93537) - https://sketch.com -->
|
||||
<title>Icon-Architecture/64/Arch_AWS-Textract_64</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<linearGradient x1="0%" y1="100%" x2="100%" y2="0%" id="linearGradient-1">
|
||||
<stop stop-color="#055F4E" offset="0%"></stop>
|
||||
<stop stop-color="#56C0A7" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="Icon-Architecture/64/Arch_AWS-Textract_64" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Icon-Architecture-BG/64/Machine-Learning" fill="url(#linearGradient-1)">
|
||||
<rect id="Rectangle" x="0" y="0" width="80" height="80"></rect>
|
||||
</g>
|
||||
<path d="M22.0624102,50 C24.3763895,53.603 28.4103535,56 33.0003125,56 C40.1672485,56 45.9991964,50.168 45.9991964,43 C45.9991964,35.832 40.1672485,30 33.0003125,30 C27.6033607,30 22.9664021,33.307 21.0024196,38 L23.2143999,38 C25.0393836,34.444 28.7363506,32 33.0003125,32 C39.0652583,32 43.9992143,36.935 43.9992143,43 C43.9992143,49.065 39.0652583,54 33.0003125,54 C29.5913429,54 26.5413702,52.441 24.5213882,50 L22.0624102,50 Z M37.0002768,45 L37.0002768,43 L41.9992321,43 C41.9992321,38.038 37.9622682,34 33.0003125,34 C28.0373568,34 23.9993929,38.038 23.9993929,43 L28.9993482,43 L28.9993482,45 L24.2313908,45 C25.1443826,49.002 28.7253507,52 33.0003125,52 C35.1362934,52 37.0992759,51.249 38.6442621,50 L34.0003036,50 L34.0003036,48 L40.4782457,48 C41.0812403,47.102 41.5202364,46.087 41.7682342,45 L37.0002768,45 Z M21.0024196,48 L23.2143999,48 C22.4434068,46.498 22.0004107,44.801 22.0004107,43 C22.0004107,41.959 22.1554093,40.955 22.4264069,40 L20.3634253,40 C20.1344274,40.965 19.9994286,41.966 19.9994286,43 C19.9994286,44.771 20.3584254,46.46 21.0024196,48 L21.0024196,48 Z M19.7434309,50 L17.0004554,50 L17.0004554,48 L18.8744386,48 C18.5344417,47.04 18.2894438,46.038 18.1494451,45 L15.4144695,45 L16.707458,46.293 L15.2924706,47.707 L12.2924974,44.707 C11.9025009,44.316 11.9025009,43.684 12.2924974,43.293 L15.2924706,40.293 L16.707458,41.707 L15.4144695,43 L18.0004464,43 C18.0004464,41.973 18.1044455,40.97 18.3024437,40 L17.0004554,40 L17.0004554,38 L18.8744386,38 C20.9404202,32.184 26.4833707,28 33.0003125,28 C37.427273,28 41.4002375,29.939 44.148213,33 L59.0000804,33 L59.0000804,35 L45.6661994,35 C47.1351863,37.318 47.9991786,40.058 47.9991786,43 L59.0000804,43 L59.0000804,45 L47.8501799,45 C46.8681887,52.327 40.5912447,58 33.0003125,58 C27.2563638,58 22.2624084,54.752 19.7434309,50 L19.7434309,50 Z M37.0002768,39 C37.0002768,38.448 36.5522808,38 36.0002857,38 L29.9993482,38 C29.4473442,38 28.9993482,38.448 28.9993482,39 L28.9993482,41 L31.0003304,41 L31.0003304,40 L32.0003214,40 L32.0003214,43 L31.0003304,43 L31.0003304,45 L35.0002946,45 L35.0002946,43 L34.0003036,43 L34.0003036,40 L35.0002946,40 L35.0002946,41 L37.0002768,41 L37.0002768,39 Z M49.0001696,40 L59.0000804,40 L59.0000804,38 L49.0001696,38 L49.0001696,40 Z M49.0001696,50 L59.0000804,50 L59.0000804,48 L49.0001696,48 L49.0001696,50 Z M57.0000982,27 L60.5850662,27 L57.0000982,23.414 L57.0000982,27 Z M63.7070383,27.293 C63.8940367,27.48 64.0000357,27.735 64.0000357,28 L64.0000357,63 C64.0000357,63.552 63.5520397,64 63.0000446,64 L32.0003304,64 C31.4473264,64 31.0003304,63.552 31.0003304,63 L31.0003304,59 L33.0003125,59 L33.0003125,62 L62.0000536,62 L62.0000536,29 L56.0001071,29 C55.4471121,29 55.0001161,28.552 55.0001161,28 L55.0001161,22 L33.0003125,22 L33.0003125,27 L31.0003304,27 L31.0003304,21 C31.0003304,20.448 31.4473264,20 32.0003304,20 L56.0001071,20 C56.2651048,20 56.5191025,20.105 56.7071008,20.293 L63.7070383,27.293 Z M68,24.166 L68,61 C68,61.552 67.552004,62 67.0000089,62 L65.0000268,62 L65.0000268,60 L66.0000179,60 L66.0000179,24.612 L58.6170838,18 L36.0002857,18 L36.0002857,19 L34.0003036,19 L34.0003036,17 C34.0003036,16.448 34.4472996,16 35.0003036,16 L59.0000804,16 C59.2460782,16 59.483076,16.091 59.6660744,16.255 L67.666003,23.42 C67.8780011,23.61 68,23.881 68,24.166 L68,24.166 Z" id="Amazon-Textract_Icon_64_Squid" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -203,10 +203,7 @@ export class AwsTranscribe implements INodeType {
|
||||
name: 'channelIdentification',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: `Instructs Amazon Transcribe to process each audiochannel separately</br>
|
||||
and then merge the transcription output of each channel into a single transcription.
|
||||
You can't set both Max Speaker Labels and Channel Identification in the same request.
|
||||
If you set both, your request returns a BadRequestException.`,
|
||||
description: `Instructs Amazon Transcribe to process each audiochannel separately and then merge the transcription output of each channel into a single transcription. You can't set both Max Speaker Labels and Channel Identification in the same request. If you set both, your request returns a BadRequestException.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Max Alternatives',
|
||||
@@ -228,9 +225,7 @@ export class AwsTranscribe implements INodeType {
|
||||
minValue: 2,
|
||||
maxValue: 10,
|
||||
},
|
||||
description: `The maximum number of speakers to identify in the input audio.</br>
|
||||
If there are more speakers in the audio than this number, multiple speakers are</br>
|
||||
identified as a single speaker.`,
|
||||
description: `The maximum number of speakers to identify in the input audio. If there are more speakers in the audio than this number, multiple speakers are identified as a single speaker.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Vocabulary Name',
|
||||
@@ -244,8 +239,7 @@ export class AwsTranscribe implements INodeType {
|
||||
name: 'vocabularyFilterName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `The name of the vocabulary filter to use when transcribing the audio.</br>
|
||||
The filter that you specify must have the same language code as the transcription job.`,
|
||||
description: `The name of the vocabulary filter to use when transcribing the audio. The filter that you specify must have the same language code as the transcription job.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Vocabulary Filter Method',
|
||||
@@ -267,9 +261,7 @@ export class AwsTranscribe implements INodeType {
|
||||
|
||||
],
|
||||
default: 'remove',
|
||||
description: `Set to mask to remove filtered text from the transcript and replace it with three asterisks ("***") as placeholder text.</br>
|
||||
Set to remove to remove filtered text from the transcript without using placeholder text. Set to tag to mark the word in the transcription</br>
|
||||
output that matches the vocabulary filter. When you set the filter method to tag, the words matching your vocabulary filter are not masked or removed.`,
|
||||
description: `<p>Set to mask to remove filtered text from the transcript and replace it with three asterisks ("***") as placeholder text.</p><p>Set to remove to remove filtered text from the transcript without using placeholder text. Set to tag to mark the word in the transcription output that matches the vocabulary filter. When you set the filter method to tag, the words matching your vocabulary filter are not masked or removed.</p>`,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -288,7 +280,7 @@ export class AwsTranscribe implements INodeType {
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'By default, the response only contains metadata about the transcript.<br>Enable this option to retrieve the transcript instead.',
|
||||
description: 'By default, the response only contains metadata about the transcript. Enable this option to retrieve the transcript instead.',
|
||||
},
|
||||
{
|
||||
displayName: 'Simple',
|
||||
@@ -444,7 +436,7 @@ export class AwsTranscribe implements INodeType {
|
||||
ShowAlternatives: true,
|
||||
MaxAlternatives: options.maxAlternatives,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (options.maxSpeakerLabels) {
|
||||
Object.assign(body.Settings, {
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
} from 'url';
|
||||
|
||||
import {
|
||||
Request,
|
||||
sign,
|
||||
} from 'aws4';
|
||||
|
||||
@@ -50,7 +51,7 @@ export async function awsApiRequest(this: IHookFunctions | IExecuteFunctions | I
|
||||
const endpoint = new URL(getEndpointForService(service, credentials) + path);
|
||||
|
||||
// Sign AWS API request with the user credentials
|
||||
const signOpts = { headers: headers || {}, host: endpoint.host, method, path, body };
|
||||
const signOpts = { headers: headers || {}, host: endpoint.host, method, path, body } as Request;
|
||||
sign(signOpts, { accessKeyId: `${credentials.accessKeyId}`.trim(), secretAccessKey: `${credentials.secretAccessKey}`.trim() });
|
||||
|
||||
|
||||
@@ -104,4 +105,3 @@ export async function awsApiRequestRESTAllItems(this: IHookFunctions | IExecuteF
|
||||
|
||||
return returnData;
|
||||
}
|
||||
|
||||
|
||||
@@ -86,9 +86,7 @@ export const imageFields = [
|
||||
name: 'waitForImage',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: `Wait for the image to be proccesed before returning.<br />
|
||||
If after three tries the images is not ready, an error will be thrown.<br />
|
||||
Number of tries can be increased by setting "Wait Max Tries".`,
|
||||
description: `Wait for the image to be proccesed before returning. If after three tries the images is not ready, an error will be thrown. Number of tries can be increased by setting "Wait Max Tries".`,
|
||||
},
|
||||
{
|
||||
displayName: 'Wait Max Tries',
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
import { OptionsWithUri } from 'request';
|
||||
|
||||
import {
|
||||
IHookFunctions,
|
||||
IWebhookFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
ICredentialsDecrypted,
|
||||
ICredentialTestFunctions,
|
||||
IDataObject,
|
||||
ILoadOptionsFunctions,
|
||||
INodePropertyOptions,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
IWebhookResponseData,
|
||||
NodeCredentialTestResult,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
@@ -35,6 +40,7 @@ export class BitbucketTrigger implements INodeType {
|
||||
{
|
||||
name: 'bitbucketApi',
|
||||
required: true,
|
||||
testedBy: 'bitbucketApiTest',
|
||||
},
|
||||
],
|
||||
webhooks: [
|
||||
@@ -52,58 +58,36 @@ export class BitbucketTrigger implements INodeType {
|
||||
type: 'options',
|
||||
required: true,
|
||||
options: [
|
||||
{
|
||||
name: 'User',
|
||||
value: 'user',
|
||||
},
|
||||
{
|
||||
name: 'Team',
|
||||
value: 'team',
|
||||
},
|
||||
{
|
||||
name: 'Repository',
|
||||
value: 'repository',
|
||||
},
|
||||
{
|
||||
name: 'Workspace',
|
||||
value: 'workspace',
|
||||
},
|
||||
],
|
||||
default: 'user',
|
||||
default: 'workspace',
|
||||
description: 'The resource to operate on.',
|
||||
},
|
||||
{
|
||||
displayName: 'Events',
|
||||
name: 'events',
|
||||
type: 'multiOptions',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'user',
|
||||
],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getUsersEvents',
|
||||
},
|
||||
options: [],
|
||||
required: true,
|
||||
default: [],
|
||||
description: 'The events to listen to.',
|
||||
},
|
||||
{
|
||||
displayName: 'Team',
|
||||
name: 'team',
|
||||
displayName: 'Workspace',
|
||||
name: 'workspace',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'team',
|
||||
'workspace',
|
||||
'repository',
|
||||
],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getTeams',
|
||||
loadOptionsMethod: 'getWorkspaces',
|
||||
},
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'The team of which to listen to the events.',
|
||||
description: 'The repository of which to listen to the events.',
|
||||
},
|
||||
{
|
||||
displayName: 'Events',
|
||||
@@ -112,12 +96,12 @@ export class BitbucketTrigger implements INodeType {
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'team',
|
||||
'workspace',
|
||||
],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getTeamEvents',
|
||||
loadOptionsMethod: 'getWorkspaceEvents',
|
||||
},
|
||||
options: [],
|
||||
required: true,
|
||||
@@ -137,6 +121,9 @@ export class BitbucketTrigger implements INodeType {
|
||||
},
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getRepositories',
|
||||
loadOptionsDependsOn: [
|
||||
'workspace',
|
||||
],
|
||||
},
|
||||
required: true,
|
||||
default: '',
|
||||
@@ -166,90 +153,86 @@ export class BitbucketTrigger implements INodeType {
|
||||
};
|
||||
|
||||
methods = {
|
||||
credentialTest: {
|
||||
async bitbucketApiTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise<NodeCredentialTestResult> {
|
||||
const credentials = credential.data;
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
method: 'GET',
|
||||
auth: {
|
||||
user: credentials!.username as string,
|
||||
password: credentials!.appPassword as string,
|
||||
},
|
||||
uri: 'https://api.bitbucket.org/2.0/user',
|
||||
json: true,
|
||||
timeout: 5000,
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await this.helpers.request(options);
|
||||
if (!response.username) {
|
||||
return {
|
||||
status: 'Error',
|
||||
message: `Token is not valid: ${response.error}`,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 'Error',
|
||||
message: `Settings are not valid: ${error}`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: 'OK',
|
||||
message: 'Authentication successful!',
|
||||
};
|
||||
},
|
||||
},
|
||||
loadOptions: {
|
||||
// Get all the events to display them to user so that he can
|
||||
// select them easily
|
||||
async getUsersEvents(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
async getWorkspaceEvents(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const events = await bitbucketApiRequestAllItems.call(this, 'values', 'GET', '/hook_events/user');
|
||||
const events = await bitbucketApiRequestAllItems.call(this, 'values', 'GET', '/hook_events/workspace');
|
||||
for (const event of events) {
|
||||
const eventName = event.event;
|
||||
const eventId = event.event;
|
||||
const eventDescription = event.description;
|
||||
returnData.push({
|
||||
name: eventName,
|
||||
value: eventId,
|
||||
description: eventDescription,
|
||||
name: event.event,
|
||||
value: event.event,
|
||||
description: event.description,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
// Get all the events to display them to user so that he can
|
||||
// select them easily
|
||||
async getTeamEvents(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const events = await bitbucketApiRequestAllItems.call(this, 'values', 'GET', '/hook_events/team');
|
||||
for (const event of events) {
|
||||
const eventName = event.event;
|
||||
const eventId = event.event;
|
||||
const eventDescription = event.description;
|
||||
returnData.push({
|
||||
name: eventName,
|
||||
value: eventId,
|
||||
description: eventDescription,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
// Get all the events to display them to user so that he can
|
||||
// select them easily
|
||||
async getRepositoriesEvents(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const events = await bitbucketApiRequestAllItems.call(this, 'values', 'GET', '/hook_events/repository');
|
||||
for (const event of events) {
|
||||
const eventName = event.event;
|
||||
const eventId = event.event;
|
||||
const eventDescription = event.description;
|
||||
returnData.push({
|
||||
name: eventName,
|
||||
value: eventId,
|
||||
description: eventDescription,
|
||||
name: event.event,
|
||||
value: event.event,
|
||||
description: event.description,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
// Get all the repositories to display them to user so that he can
|
||||
// select them easily
|
||||
async getRepositories(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const credentials = await this.getCredentials('bitbucketApi');
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const repositories = await bitbucketApiRequestAllItems.call(this, 'values', 'GET', `/repositories/${credentials!.username}`);
|
||||
const workspace = this.getCurrentNodeParameter('workspace') as string;
|
||||
const repositories = await bitbucketApiRequestAllItems.call(this, 'values', 'GET', `/repositories/${workspace}`);
|
||||
for (const repository of repositories) {
|
||||
const repositoryName = repository.slug;
|
||||
const repositoryId = repository.slug;
|
||||
const repositoryDescription = repository.description;
|
||||
returnData.push({
|
||||
name: repositoryName,
|
||||
value: repositoryId,
|
||||
description: repositoryDescription,
|
||||
name: repository.slug,
|
||||
value: repository.slug,
|
||||
description: repository.description,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
// Get all the teams to display them to user so that he can
|
||||
// select them easily
|
||||
async getTeams(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
async getWorkspaces(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const qs: IDataObject = {
|
||||
role: 'member',
|
||||
};
|
||||
const teams = await bitbucketApiRequestAllItems.call(this, 'values', 'GET', '/teams', {}, qs);
|
||||
for (const team of teams) {
|
||||
const teamName = team.display_name;
|
||||
const teamId = team.username;
|
||||
const workspaces = await bitbucketApiRequestAllItems.call(this, 'values', 'GET', `/workspaces`);
|
||||
for (const workspace of workspaces) {
|
||||
returnData.push({
|
||||
name: teamName,
|
||||
value: teamId,
|
||||
name: workspace.name,
|
||||
value: workspace.slug,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
@@ -261,29 +244,25 @@ export class BitbucketTrigger implements INodeType {
|
||||
default: {
|
||||
async checkExists(this: IHookFunctions): Promise<boolean> {
|
||||
let endpoint = '';
|
||||
const credentials = await this.getCredentials('bitbucketApi');
|
||||
const resource = this.getNodeParameter('resource', 0) as string;
|
||||
const workspace = this.getNodeParameter('workspace', 0) as string;
|
||||
const webhookUrl = this.getNodeWebhookUrl('default');
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
if (webhookData.webhookId === undefined) {
|
||||
return false;
|
||||
}
|
||||
if (resource === 'user') {
|
||||
endpoint = `/users/${credentials!.username}/hooks/${webhookData.webhookId}`;
|
||||
}
|
||||
if (resource === 'team') {
|
||||
const team = this.getNodeParameter('team', 0) as string;
|
||||
endpoint = `/teams/${team}/hooks/${webhookData.webhookId}`;
|
||||
if (resource === 'workspace') {
|
||||
endpoint = `/workspaces/${workspace}/hooks`;
|
||||
}
|
||||
if (resource === 'repository') {
|
||||
const repository = this.getNodeParameter('repository', 0) as string;
|
||||
endpoint = `/repositories/${credentials!.username}/${repository}/hooks/${webhookData.webhookId}`;
|
||||
endpoint = `/repositories/${workspace}/${repository}/hooks`;
|
||||
}
|
||||
try {
|
||||
await bitbucketApiRequest.call(this, 'GET', endpoint);
|
||||
} catch (error) {
|
||||
return false;
|
||||
const { values: hooks } = await bitbucketApiRequest.call(this, 'GET', endpoint);
|
||||
for (const hook of hooks) {
|
||||
if (webhookUrl === hook.url && hook.active === true) {
|
||||
webhookData.webhookId = hook.uuid.replace('{', '').replace('}', '');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
async create(this: IHookFunctions): Promise<boolean> {
|
||||
let responseData;
|
||||
@@ -292,18 +271,14 @@ export class BitbucketTrigger implements INodeType {
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
const events = this.getNodeParameter('events') as string[];
|
||||
const resource = this.getNodeParameter('resource', 0) as string;
|
||||
const credentials = await this.getCredentials('bitbucketApi');
|
||||
const workspace = this.getNodeParameter('workspace', 0) as string;
|
||||
|
||||
if (resource === 'user') {
|
||||
endpoint = `/users/${credentials!.username}/hooks`;
|
||||
}
|
||||
if (resource === 'team') {
|
||||
const team = this.getNodeParameter('team', 0) as string;
|
||||
endpoint = `/teams/${team}/hooks`;
|
||||
if (resource === 'workspace') {
|
||||
endpoint = `/workspaces/${workspace}/hooks`;
|
||||
}
|
||||
if (resource === 'repository') {
|
||||
const repository = this.getNodeParameter('repository', 0) as string;
|
||||
endpoint = `/repositories/${credentials!.username}/${repository}/hooks`;
|
||||
endpoint = `/repositories/${workspace}/${repository}/hooks`;
|
||||
}
|
||||
const body: IDataObject = {
|
||||
description: 'n8n webhook',
|
||||
@@ -318,22 +293,18 @@ export class BitbucketTrigger implements INodeType {
|
||||
async delete(this: IHookFunctions): Promise<boolean> {
|
||||
let endpoint = '';
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
const credentials = await this.getCredentials('bitbucketApi');
|
||||
const workspace = this.getNodeParameter('workspace', 0) as string;
|
||||
const resource = this.getNodeParameter('resource', 0) as string;
|
||||
if (resource === 'user') {
|
||||
endpoint = `/users/${credentials!.username}/hooks/${webhookData.webhookId}`;
|
||||
}
|
||||
if (resource === 'team') {
|
||||
const team = this.getNodeParameter('team', 0) as string;
|
||||
endpoint = `/teams/${team}/hooks/${webhookData.webhookId}`;
|
||||
if (resource === 'workspace') {
|
||||
endpoint = `/workspaces/${workspace}/hooks/${webhookData.webhookId}`;
|
||||
}
|
||||
if (resource === 'repository') {
|
||||
const repository = this.getNodeParameter('repository', 0) as string;
|
||||
endpoint = `/repositories/${credentials!.username}/${repository}/hooks/${webhookData.webhookId}`;
|
||||
endpoint = `/repositories/${workspace}/${repository}/hooks/${webhookData.webhookId}`;
|
||||
}
|
||||
try {
|
||||
await bitbucketApiRequest.call(this, 'DELETE', endpoint);
|
||||
} catch(error) {
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
delete webhookData.webhookId;
|
||||
|
||||
@@ -144,7 +144,7 @@ export const groupFields = [
|
||||
name: 'accessAll',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Allow this group to access all collections within the organization, instead of only its associated collections.<br>If set to true, this option overrides any collection assignments.',
|
||||
description: 'Allow this group to access all collections within the organization, instead of only its associated collections. If set to true, this option overrides any collection assignments.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
@@ -208,7 +208,7 @@ export const groupFields = [
|
||||
name: 'accessAll',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Allow this group to access all collections within the organization, instead of only its associated collections.<br>If set to true, this option overrides any collection assignments.',
|
||||
description: 'Allow this group to access all collections within the organization, instead of only its associated collections. If set to true, this option overrides any collection assignments.',
|
||||
},
|
||||
{
|
||||
displayName: 'Collections',
|
||||
|
||||
@@ -194,7 +194,7 @@ export const fileFields = [
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Name of the binary property to which to<br />write the data of the read file.',
|
||||
description: 'Name of the binary property to which to write the data of the read file.',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
@@ -327,8 +327,7 @@ export const fileFields = [
|
||||
name: 'contet_types',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `Limits search results to items with the given content types.</br>
|
||||
Content types are defined as a comma separated lists of Box recognized content types.`,
|
||||
description: `Limits search results to items with the given content types. Content types are defined as a comma separated lists of Box recognized content types.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Created At Range',
|
||||
@@ -397,8 +396,7 @@ export const fileFields = [
|
||||
name: 'ancestor_folder_ids',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `Limits search results to items within the given list of folders.</br>
|
||||
Folders are defined as a comma separated lists of folder IDs.`,
|
||||
description: `Limits search results to items within the given list of folders. Folders are defined as a comma separated lists of folder IDs.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Scope',
|
||||
@@ -423,8 +421,7 @@ export const fileFields = [
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: '1000000,5000000',
|
||||
description: `Limits search results to items within a given file size range.</br>
|
||||
File size ranges are defined as comma separated byte sizes.`,
|
||||
description: `Limits search results to items within a given file size range. File size ranges are defined as comma separated byte sizes.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Sort',
|
||||
@@ -495,8 +492,7 @@ export const fileFields = [
|
||||
name: 'owner_user_ids',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `Limits search results to items owned by the given list of owners..</br>
|
||||
Owners are defined as a comma separated list of user IDs.`,
|
||||
description: `Limits search results to items owned by the given list of owners. Owners are defined as a comma separated list of user IDs.`,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -710,8 +706,7 @@ export const fileFields = [
|
||||
name: 'can_view_path',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: `Whether the invited users can see the entire parent path to the associated folder.</br>
|
||||
The user will not gain privileges in any parent folder and therefore cannot see content the user is not collaborated on.`,
|
||||
description: `Whether the invited users can see the entire parent path to the associated folder. The user will not gain privileges in any parent folder and therefore cannot see content the user is not collaborated on.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Expires At',
|
||||
@@ -818,7 +813,7 @@ export const fileFields = [
|
||||
},
|
||||
|
||||
},
|
||||
description: 'Name of the binary property which contains<br />the data for the file.',
|
||||
description: 'Name of the binary property which contains the data for the file.',
|
||||
},
|
||||
{
|
||||
displayName: 'Parent ID',
|
||||
|
||||
@@ -279,8 +279,7 @@ export const folderFields = [
|
||||
name: 'contet_types',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `Limits search results to items with the given content types.</br>
|
||||
Content types are defined as a comma separated lists of Box recognized content types.`,
|
||||
description: `Limits search results to items with the given content types. Content types are defined as a comma separated lists of Box recognized content types.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Created At Range',
|
||||
@@ -349,8 +348,7 @@ export const folderFields = [
|
||||
name: 'ancestor_folder_ids',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `Limits search results to items within the given list of folders.</br>
|
||||
Folders are defined as a comma separated lists of folder IDs.`,
|
||||
description: `Limits search results to items within the given list of folders. Folders are defined as a comma separated lists of folder IDs.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Scope',
|
||||
@@ -375,8 +373,7 @@ export const folderFields = [
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: '1000000,5000000',
|
||||
description: `Limits search results to items within a given file size range.</br>
|
||||
File size ranges are defined as comma separated byte sizes.`,
|
||||
description: `Limits search results to items within a given file size range. File size ranges are defined as comma separated byte sizes.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Sort',
|
||||
@@ -447,8 +444,7 @@ export const folderFields = [
|
||||
name: 'owner_user_ids',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `Limits search results to items owned by the given list of owners..</br>
|
||||
Owners are defined as a comma separated list of user IDs.`,
|
||||
description: `Limits search results to items owned by the given list of owners. Owners are defined as a comma separated list of user IDs.`,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -662,8 +658,7 @@ export const folderFields = [
|
||||
name: 'can_view_path',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: `Whether the invited users can see the entire parent path to the associated folder.</br>
|
||||
The user will not gain privileges in any parent folder and therefore cannot see content the user is not collaborated on.`,
|
||||
description: `Whether the invited users can see the entire parent path to the associated folder. The user will not gain privileges in any parent folder and therefore cannot see content the user is not collaborated on.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Expires At',
|
||||
|
||||
@@ -95,7 +95,7 @@ export class Brandfetch implements INodeType {
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Name of the binary property to which to<br />write the data of the read file.',
|
||||
description: 'Name of the binary property to which to write the data of the read file.',
|
||||
},
|
||||
{
|
||||
displayName: 'Image Type',
|
||||
|
||||
@@ -492,8 +492,7 @@ export const objectFields = [
|
||||
name: 'sort_field',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `Specify the field to use for sorting. Either use a fielddefined for</br>
|
||||
the current typeor use _random_sorting to get the entries in a random order`,
|
||||
description: `Specify the field to use for sorting. Either use a fielddefined for the current typeor use <code>_random_sorting</code> to get the entries in a random order`,
|
||||
},
|
||||
{
|
||||
displayName: 'Descending',
|
||||
|
||||
@@ -211,18 +211,14 @@ export const pipelineFields = [
|
||||
name: 'branch',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `The branch where the pipeline ran.<br/>
|
||||
The HEAD commit on this branch was used for the pipeline.<br/>
|
||||
Note that branch and tag are mutually exclusive.`,
|
||||
description: `The branch where the pipeline ran. The HEAD commit on this branch was used for the pipeline. Note that branch and tag are mutually exclusive.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Tag',
|
||||
name: 'tag',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `The tag used by the pipeline.<br/>
|
||||
The commit that this tag points to was used for the pipeline.<br/>
|
||||
Note that branch and tag are mutually exclusive`,
|
||||
description: `The tag used by the pipeline. The commit that this tag points to was used for the pipeline. Note that branch and tag are mutually exclusive`,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -107,7 +107,7 @@ export class CiscoWebexTrigger implements INodeType {
|
||||
},
|
||||
},
|
||||
default: true,
|
||||
description: 'By default the response only contain a reference to the data the user inputed<br />If this option gets activated it will resolve the data automatically.',
|
||||
description: 'By default the response only contain a reference to the data the user inputed. If this option gets activated, it will resolve the data automatically.',
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
|
||||
@@ -848,8 +848,7 @@ export const meetingFields = [
|
||||
name: 'password',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `Meeting password. Must conform to the site's password complexity settings.</br>
|
||||
If not specified, a random password conforming to the site's password rules will be generated automatically`,
|
||||
description: `Meeting password. Must conform to the site's password complexity settings. If not specified, a random password conforming to the site's password rules will be generated automatically`,
|
||||
},
|
||||
{
|
||||
displayName: 'Public Meeting',
|
||||
|
||||
@@ -58,7 +58,7 @@ export class Clearbit implements INodeType {
|
||||
{
|
||||
name: 'Person',
|
||||
value: 'person',
|
||||
description: `The Person API lets you retrieve social information associated with an email address,<br/>
|
||||
description: `The Person API lets you retrieve social information associated with an email address,
|
||||
such as a person’s name, location and Twitter handle.`,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -254,7 +254,7 @@ export const taskFields = [
|
||||
alwaysOpenEditWindow: true,
|
||||
},
|
||||
default: '',
|
||||
description: 'Custom fields to set as JSON in the format:<br />[{"id": "", "value": ""}]',
|
||||
description: 'Custom fields to set as JSON in the format: <code>[ {"id": "", "value": ""} ]</code>',
|
||||
},
|
||||
{
|
||||
displayName: 'Content',
|
||||
@@ -1063,8 +1063,8 @@ export const taskFields = [
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: `The value is JSON and will be parsed as such. Is needed<br />
|
||||
if for example needed for labels which expects the value<br />
|
||||
description: `The value is JSON and will be parsed as such. Is needed
|
||||
if for example needed for labels which expects the value
|
||||
to be an array.`,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -133,8 +133,8 @@ export const tableFields = [
|
||||
name: 'keyColumns',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `Optional column IDs, URLs, or names (fragile and discouraged)<br />,
|
||||
specifying columns to be used as upsert keys. If more than one separate by ,`,
|
||||
description: `Optional column IDs, URLs, or names (fragile and discouraged),
|
||||
specifying columns to be used as upsert keys. If more than one separate by a comma (,)`,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -202,10 +202,10 @@ export const tableFields = [
|
||||
],
|
||||
},
|
||||
},
|
||||
description: `ID or name of the row. Names are discouraged because<br />
|
||||
they're easily prone to being changed by users. If you're<br />
|
||||
using a name, be sure to URI-encode it. If there are<br />
|
||||
multiple rows with the same value in the identifying column,<br />
|
||||
description: `ID or name of the row. Names are discouraged because
|
||||
they're easily prone to being changed by users. If you're
|
||||
using a name, be sure to URI-encode it. If there are
|
||||
multiple rows with the same value in the identifying column,
|
||||
an arbitrary one will be selected`,
|
||||
},
|
||||
{
|
||||
@@ -237,9 +237,7 @@ export const tableFields = [
|
||||
name: 'useColumnNames',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: `Use column names instead of column IDs in the returned output.</br>
|
||||
This is generally discouraged as it is fragile. If columns are renamed,</br>
|
||||
code using original names may throw errors.`,
|
||||
description: `Use column names instead of column IDs in the returned output. This is generally discouraged as it is fragile. If columns are renamed, code using original names may throw errors.`,
|
||||
},
|
||||
{
|
||||
displayName: 'ValueFormat',
|
||||
@@ -378,9 +376,7 @@ export const tableFields = [
|
||||
alwaysOpenEditWindow: true,
|
||||
},
|
||||
default: '',
|
||||
description: `Query used to filter returned rows, specified as <column_id_or_name>:<value>. <br/>
|
||||
If you'd like to use a column name instead of an ID, you must quote it (e.g., "My Column":123).<br/>
|
||||
Also note that value is a JSON value; if you'd like to use a string, you must surround it in quotes (e.g., "groceries").`,
|
||||
description: `Query used to filter returned rows, specified as <column_id_or_name>:<value>. If you'd like to use a column name instead of an ID, you must quote it (e.g., "My Column":123). Also note that value is a JSON value; if you'd like to use a string, you must surround it in quotes (e.g., "groceries").`,
|
||||
},
|
||||
{
|
||||
displayName: 'RAW Data',
|
||||
@@ -404,17 +400,14 @@ export const tableFields = [
|
||||
value: 'natural',
|
||||
},
|
||||
],
|
||||
description: `Specifies the sort order of the rows returned.<br />
|
||||
If left unspecified, rows are returned by creation time ascending.`,
|
||||
description: `Specifies the sort order of the rows returned. If left unspecified, rows are returned by creation time ascending.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Use Column Names',
|
||||
name: 'useColumnNames',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: `Use column names instead of column IDs in the returned output.</br>
|
||||
This is generally discouraged as it is fragile. If columns<br />
|
||||
are renamed, code using original names may throw errors.`,
|
||||
description: `Use column names instead of column IDs in the returned output. This is generally discouraged as it is fragile. If columns are renamed, code using original names may throw errors.`,
|
||||
},
|
||||
{
|
||||
displayName: 'ValueFormat',
|
||||
@@ -576,10 +569,10 @@ export const tableFields = [
|
||||
],
|
||||
},
|
||||
},
|
||||
description: `ID or name of the row. Names are discouraged because<br />
|
||||
they're easily prone to being changed by users. If you're<br />
|
||||
using a name, be sure to URI-encode it. If there are multiple<br />
|
||||
rows with the same value in the identifying column, an arbitrary<br />
|
||||
description: `ID or name of the row. Names are discouraged because
|
||||
they're easily prone to being changed by users. If you're
|
||||
using a name, be sure to URI-encode it. If there are multiple
|
||||
rows with the same value in the identifying column, an arbitrary
|
||||
one will be selected`,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -277,18 +277,14 @@ export const viewFields = [
|
||||
alwaysOpenEditWindow: true,
|
||||
},
|
||||
default: '',
|
||||
description: `Query used to filter returned rows, specified as <column_id_or_name>:<value>. <br/>
|
||||
If you'd like to use a column name instead of an ID, you must quote it (e.g., "My Column":123).<br/>
|
||||
Also note that value is a JSON value; if you'd like to use a string, you must surround it in quotes (e.g., "groceries").`,
|
||||
description: `Query used to filter returned rows, specified as <column_id_or_name>:<value>. If you'd like to use a column name instead of an ID, you must quote it (e.g., "My Column":123). Also note that value is a JSON value; if you'd like to use a string, you must surround it in quotes (e.g., "groceries").`,
|
||||
},
|
||||
{
|
||||
displayName: 'Use Column Names',
|
||||
name: 'useColumnNames',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: `Use column names instead of column IDs in the returned output.</br>
|
||||
This is generally discouraged as it is fragile. If columns are renamed,</br>
|
||||
code using original names may throw errors.`,
|
||||
description: `Use column names instead of column IDs in the returned output. This is generally discouraged as it is fragile. If columns are renamed, code using original names may throw errors.`,
|
||||
},
|
||||
{
|
||||
displayName: 'ValueFormat',
|
||||
@@ -333,8 +329,7 @@ export const viewFields = [
|
||||
value: 'natural',
|
||||
},
|
||||
],
|
||||
description: `Specifies the sort order of the rows returned.<br />
|
||||
If left unspecified, rows are returned by creation time ascending.`,
|
||||
description: `Specifies the sort order of the rows returned. If left unspecified, rows are returned by creation time ascending.`,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -96,7 +96,7 @@ export class Compression implements INodeType {
|
||||
|
||||
},
|
||||
placeholder: '',
|
||||
description: 'Name of the binary property which contains<br />the data for the file(s) to be compress/decompress. Multiple can be used separated by ,',
|
||||
description: 'Name of the binary property which contains the data for the file(s) to be compress/decompress. Multiple can be used separated by a comma (,)',
|
||||
},
|
||||
{
|
||||
displayName: 'Output Format',
|
||||
@@ -159,7 +159,7 @@ export class Compression implements INodeType {
|
||||
},
|
||||
},
|
||||
placeholder: '',
|
||||
description: 'Name of the binary property to which to<br />write the data of the compressed files.',
|
||||
description: 'Name of the binary property to which to write the data of the compressed files.',
|
||||
},
|
||||
{
|
||||
displayName: 'Output Prefix',
|
||||
@@ -129,7 +129,7 @@ export const analyzerFields: INodeProperties[] = [
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Name of the binary property to which to<br />write the data of the read file.',
|
||||
description: 'Name of the binary property to which to write the data of the read file.',
|
||||
},
|
||||
{
|
||||
displayName: 'TLP',
|
||||
|
||||
@@ -229,11 +229,7 @@ export class CrateDb implements INodeType {
|
||||
},
|
||||
],
|
||||
default: 'multiple',
|
||||
description: [
|
||||
'The way queries should be sent to database.',
|
||||
'Can be used in conjunction with <b>Continue on Fail</b>.',
|
||||
'See the docs for more examples',
|
||||
].join('<br>'),
|
||||
description: 'The way queries should be sent to database. Can be used in conjunction with <b>Continue on Fail</b>. See <a href="https://docs.n8n.io/nodes/n8n-nodes-base.crateDb/">the docs</a> for more examples.',
|
||||
},
|
||||
{
|
||||
displayName: 'Query Parameters',
|
||||
|
||||
@@ -25,6 +25,7 @@ export class Cron implements INodeType {
|
||||
group: ['trigger'],
|
||||
version: 1,
|
||||
description: 'Triggers the workflow at a specific time',
|
||||
eventTriggerDescription: '',
|
||||
defaults: {
|
||||
name: 'Cron',
|
||||
color: '#00FF00',
|
||||
@@ -496,7 +496,7 @@ export class DateTime implements INodeType {
|
||||
newItem.binary = item.binary;
|
||||
}
|
||||
|
||||
set(newItem, `json.${dataPropertyName}`, newDate);
|
||||
set(newItem, `json.${dataPropertyName}`, newDate.toISOString());
|
||||
|
||||
returnData.push(newItem);
|
||||
}
|
||||
@@ -279,7 +279,7 @@ export const eventFields = [
|
||||
name: 'fieldId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Each custom field\'s unique identifier<br /> can be found within the Event\'s Registration block in the Customize tab.',
|
||||
description: 'Each custom field\'s unique identifier can be found within the Event\'s Registration block in the Customize tab.',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
@@ -297,7 +297,7 @@ export const eventFields = [
|
||||
name: 'ref_url',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Event Registration page URL. It can be useful when you<br /> do not know Event ID, but have Event link.',
|
||||
description: 'Event Registration page URL. It can be useful when you do not know Event ID, but have Event link.',
|
||||
},
|
||||
{
|
||||
displayName: 'GDPR',
|
||||
|
||||
162
packages/nodes-base/nodes/Dhl/Dhl.node.ts
Normal file
162
packages/nodes-base/nodes/Dhl/Dhl.node.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
ICredentialDataDecryptedObject,
|
||||
ICredentialsDecrypted,
|
||||
ICredentialTestFunctions,
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
NodeCredentialTestResult,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
dhlApiRequest,
|
||||
validateCrendetials,
|
||||
} from './GenericFunctions';
|
||||
|
||||
export class Dhl implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'DHL',
|
||||
name: 'dhl',
|
||||
icon: 'file:dhl.svg',
|
||||
group: ['input'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Consume DHL API',
|
||||
defaults: {
|
||||
name: 'DHL',
|
||||
color: '#fecc00',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'dhlApi',
|
||||
required: true,
|
||||
testedBy: 'dhlApiCredentialTest',
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
noDataExpression: true,
|
||||
type: 'hidden',
|
||||
options: [
|
||||
{
|
||||
name: 'Shipment',
|
||||
value: 'shipment',
|
||||
},
|
||||
],
|
||||
default: 'shipment',
|
||||
},
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'shipment',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Get Tracking Details',
|
||||
value: 'get',
|
||||
},
|
||||
],
|
||||
default: 'get',
|
||||
},
|
||||
{
|
||||
displayName: 'Tracking Number',
|
||||
name: 'trackingNumber',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Option',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: `Recipient's Postal Code`,
|
||||
name: 'recipientPostalCode',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `DHL will return more detailed information on the shipment when you provide the Recipient's Postal Code - it acts as a verification step`,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
methods = {
|
||||
credentialTest: {
|
||||
async dhlApiCredentialTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise<NodeCredentialTestResult> {
|
||||
try {
|
||||
await validateCrendetials.call(this, credential.data as ICredentialDataDecryptedObject);
|
||||
} catch (error) {
|
||||
if (error.statusCode === 401) {
|
||||
return {
|
||||
status: 'Error',
|
||||
message: 'The API Key included in the request is invalid',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
status: 'OK',
|
||||
message: 'Connection successful!',
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
const returnData: IDataObject[] = [];
|
||||
let qs: IDataObject = {};
|
||||
let responseData;
|
||||
const resource = this.getNodeParameter('resource', 0) as string;
|
||||
const operation = this.getNodeParameter('operation', 0) as string;
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
try {
|
||||
if (resource === 'shipment') {
|
||||
if (operation === 'get') {
|
||||
|
||||
const trackingNumber = this.getNodeParameter('trackingNumber', i) as string;
|
||||
const options = this.getNodeParameter('options', i) as IDataObject;
|
||||
|
||||
qs = {
|
||||
trackingNumber,
|
||||
};
|
||||
|
||||
Object.assign(qs, options);
|
||||
|
||||
responseData = await dhlApiRequest.call(this, 'GET', `/track/shipments`, {}, qs);
|
||||
|
||||
returnData.push(...responseData.shipments);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ error: error.description });
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return [this.helpers.returnJsonArray(returnData)];
|
||||
}
|
||||
}
|
||||
65
packages/nodes-base/nodes/Dhl/GenericFunctions.ts
Normal file
65
packages/nodes-base/nodes/Dhl/GenericFunctions.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import {
|
||||
OptionsWithUri,
|
||||
} from 'request';
|
||||
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
IExecuteSingleFunctions,
|
||||
IHookFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
ICredentialDataDecryptedObject,
|
||||
ICredentialTestFunctions,
|
||||
IDataObject,
|
||||
NodeApiError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export async function dhlApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, path: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||
|
||||
const credentials = await this.getCredentials('dhlApi') as { apiKey: string };
|
||||
|
||||
let options: OptionsWithUri = {
|
||||
headers: {
|
||||
'DHL-API-Key': credentials.apiKey,
|
||||
},
|
||||
method,
|
||||
qs,
|
||||
body,
|
||||
uri: uri || `https://api-eu.dhl.com${path}`,
|
||||
json: true,
|
||||
};
|
||||
options = Object.assign({}, options, option);
|
||||
if (Object.keys(options.body).length === 0) {
|
||||
delete options.body;
|
||||
}
|
||||
|
||||
try {
|
||||
return await this.helpers.request!(options);
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function validateCrendetials(this: ICredentialTestFunctions, decryptedCredentials: ICredentialDataDecryptedObject): Promise<any> { // tslint:disable-line:no-any
|
||||
const credentials = decryptedCredentials;
|
||||
|
||||
const { apiKey } = credentials as {
|
||||
apiKey: string,
|
||||
};
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'DHL-API-Key': apiKey,
|
||||
},
|
||||
qs: {
|
||||
trackingNumber: 123,
|
||||
},
|
||||
method: 'GET',
|
||||
uri: `https://api-eu.dhl.com/track/shipments`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
return this.helpers.request!(options);
|
||||
}
|
||||
21
packages/nodes-base/nodes/Dhl/dhl.svg
Normal file
21
packages/nodes-base/nodes/Dhl/dhl.svg
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 60 60" style="enable-background:new 0 0 60 60;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFCC00;}
|
||||
.st1{fill:#D40511;}
|
||||
</style>
|
||||
<path class="st0" d="M56,60H4c-2.2,0-4-1.8-4-4V4c0-2.2,1.8-4,4-4h52c2.2,0,4,1.8,4,4v52C60,58.2,58.2,60,56,60z"/>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st1" d="M16.2,32.3H6v2h8.7L16.2,32.3z M18.9,28.6H6v2h11.4L18.9,28.6z M36.1,27.9c-0.5,0.7-1.3,1.8-1.8,2.5
|
||||
c-0.3,0.3-0.7,1,0.8,1h7.8c0,0,1.3-1.8,2.4-3.2c1.5-2,0.1-6.2-5.2-6.2H19.2l-3.6,4.9h20C36.6,26.9,36.6,27.3,36.1,27.9z M6,38h6
|
||||
l1.5-2H6V38z M40.2,38H54v-2H41.6L40.2,38z M47.1,28.6l-1.5,2H54v-2H47.1z M41.6,33.1h-8.9l-3.4,0c-1.5,0-1.1-0.6-0.8-1
|
||||
c0.5-0.7,1.4-1.8,1.8-2.5c0.5-0.7,0.8-1-0.2-1h-9L14.2,38h17.7C37.1,38,40.3,34.8,41.6,33.1z M42.9,34.3H54v-2h-9.6L42.9,34.3z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -387,7 +387,6 @@ export class Discourse implements INodeType {
|
||||
// if (simple === true) {
|
||||
// const response = [];
|
||||
// for (const key of Object.keys(responseData)) {
|
||||
// console.log(key)
|
||||
// for (const data of responseData[key]) {
|
||||
// response.push(Object.assign(data, { __type: key }));
|
||||
// }
|
||||
|
||||
@@ -624,7 +624,7 @@ export class Disqus implements INodeType {
|
||||
name: 'threadId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Looks up a thread by ID. You may pass us the "ident"<br />query type instead of an ID by including "forum". You may<br />pass us the "link" query type to filter by URL. You must pass<br />the "forum" if you do not have the Pro API Access addon.',
|
||||
description: 'Looks up a thread by ID. You may pass us the "ident" query type instead of an ID by including "forum". You may pass us the "link" query type to filter by URL. You must pass the "forum" if you do not have the Pro API Access addon.',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -361,7 +361,7 @@ export class Dropbox implements INodeType {
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Name of the binary property to which to<br />write the data of the read file.',
|
||||
description: 'Name of the binary property to which to write the data of the read file.',
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
@@ -446,7 +446,7 @@ export class Dropbox implements INodeType {
|
||||
|
||||
},
|
||||
placeholder: '',
|
||||
description: 'Name of the binary property which contains<br />the data for the file to be uploaded.',
|
||||
description: 'Name of the binary property which contains the data for the file to be uploaded.',
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
@@ -621,7 +621,7 @@ export class Dropbox implements INodeType {
|
||||
name: 'file_extensions',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Multiple can be set separated by comma. Example: jpg,pdf',
|
||||
description: 'Multiple file extensions can be set separated by comma. Example: jpg,pdf',
|
||||
},
|
||||
{
|
||||
displayName: 'Folder',
|
||||
|
||||
20
packages/nodes-base/nodes/Dropcontact/Dropcontact.node.json
Normal file
20
packages/nodes-base/nodes/Dropcontact/Dropcontact.node.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"node": "n8n-nodes-base.dropcontact",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": [
|
||||
"Sales"
|
||||
],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/credentials/dropcontact"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.dropcontact/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
369
packages/nodes-base/nodes/Dropcontact/Dropcontact.node.ts
Normal file
369
packages/nodes-base/nodes/Dropcontact/Dropcontact.node.ts
Normal file
@@ -0,0 +1,369 @@
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
ICredentialDataDecryptedObject,
|
||||
ICredentialsDecrypted,
|
||||
ICredentialTestFunctions,
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
NodeApiError,
|
||||
NodeCredentialTestResult,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
dropcontactApiRequest,
|
||||
validateCrendetials,
|
||||
} from './GenericFunction';
|
||||
|
||||
export class Dropcontact implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Dropcontact',
|
||||
name: 'dropcontact',
|
||||
icon: 'file:dropcontact.svg',
|
||||
group: ['transform'],
|
||||
version: 1,
|
||||
description: 'Find B2B emails and enrich contacts',
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
defaults: {
|
||||
name: 'Dropcontact',
|
||||
color: '#0ABA9F',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'dropcontactApi',
|
||||
required: true,
|
||||
testedBy: 'dropcontactApiCredentialTest',
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Resource',
|
||||
noDataExpression: true,
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Contact',
|
||||
value: 'contact',
|
||||
},
|
||||
],
|
||||
default: 'contact',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Operation',
|
||||
noDataExpression: true,
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Enrich',
|
||||
value: 'enrich',
|
||||
description: 'Find B2B emails and enrich your contact from his name and his website',
|
||||
},
|
||||
{
|
||||
name: 'Fetch Request',
|
||||
value: 'fetchRequest',
|
||||
},
|
||||
],
|
||||
default: 'enrich',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Request ID',
|
||||
name: 'requestId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'fetchRequest',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'enrich',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Simplify Output (Faster)',
|
||||
name: 'simplify',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'enrich',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'When off, waits for the contact data before completing. Waiting time can be adjusted with Extend Wait Time option. When on, returns a request_id that can be used later in the Fetch Request operation.',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'enrich',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Company SIREN Number',
|
||||
name: 'num_siren',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Company SIRET Code',
|
||||
name: 'siret',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Company Name',
|
||||
name: 'company',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Country',
|
||||
name: 'country',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'First Name',
|
||||
name: 'first_name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Full Name',
|
||||
name: 'full_name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Name',
|
||||
name: 'last_name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'LinkedIn Profile',
|
||||
name: 'linkedin',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Phone Number',
|
||||
name: 'phone',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Website',
|
||||
name: 'website',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'enrich',
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Add Option',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Data Fetch Wait Time',
|
||||
name: 'waitTime',
|
||||
type: 'number',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
'/simplify': [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
default: 45,
|
||||
description: 'When not simplifying the response, data will be fetched in two steps. This parameter controls how long to wait (in seconds) before trying the second step',
|
||||
},
|
||||
{
|
||||
displayName: 'French Company Enrich',
|
||||
name: 'siren',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: `Whether you want the <a href="https://en.wikipedia.org/wiki/SIREN_code" target="_blank">SIREN number</a>, NAF code, TVA number, company address and informations about the company leader. Only applies to french companies`,
|
||||
},
|
||||
{
|
||||
displayName: 'Language',
|
||||
name: 'language',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'English',
|
||||
value: 'en',
|
||||
},
|
||||
{
|
||||
name: 'French',
|
||||
value: 'fr',
|
||||
},
|
||||
],
|
||||
default: 'en',
|
||||
description: 'Whether the response is in English or French',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
methods = {
|
||||
credentialTest: {
|
||||
async dropcontactApiCredentialTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise<NodeCredentialTestResult> {
|
||||
try {
|
||||
await validateCrendetials.call(this, credential.data as ICredentialDataDecryptedObject);
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 'Error',
|
||||
message: 'The API Key included in the request is invalid',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: 'OK',
|
||||
message: 'Connection successful!',
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const entryData = this.getInputData();
|
||||
const resource = this.getNodeParameter('resource', 0) as string;
|
||||
const operation = this.getNodeParameter('operation', 0) as string;
|
||||
// tslint:disable-next-line: no-any
|
||||
let responseData: any;
|
||||
const returnData: IDataObject[] = [];
|
||||
|
||||
if (resource === 'contact') {
|
||||
if (operation === 'enrich') {
|
||||
const options = this.getNodeParameter('options', 0) as IDataObject;
|
||||
const data = [];
|
||||
const simplify = this.getNodeParameter('simplify', 0) as boolean;
|
||||
|
||||
const siren = options.siren === true ? true : false;
|
||||
const language = options.language ? options.language : 'en';
|
||||
|
||||
for (let i = 0; i < entryData.length; i++) {
|
||||
const email = this.getNodeParameter('email', i) as string;
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i);
|
||||
const body: IDataObject = {};
|
||||
if (email !== '') {
|
||||
body.email = email;
|
||||
}
|
||||
Object.assign(body, additionalFields);
|
||||
data.push(body);
|
||||
}
|
||||
|
||||
responseData = await dropcontactApiRequest.call(this, 'POST', '/batch', { data, siren, language }, {}) as { request_id: string, error: string, success: boolean };
|
||||
|
||||
if (!responseData.success) {
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ error: responseData.reason || 'invalid request' });
|
||||
} else {
|
||||
throw new NodeApiError(this.getNode(), { error: responseData.reason || 'invalid request' });
|
||||
}
|
||||
}
|
||||
|
||||
if (simplify === false) {
|
||||
const waitTime = this.getNodeParameter('options.waitTime', 0, 45) as number;
|
||||
// tslint:disable-next-line: no-any
|
||||
const delay = (ms: any) => new Promise(res => setTimeout(res, ms * 1000));
|
||||
await delay(waitTime);
|
||||
responseData = await dropcontactApiRequest.call(this, 'GET', `/batch/${responseData.request_id}`, {}, {});
|
||||
if (!responseData.success) {
|
||||
if (this.continueOnFail()) {
|
||||
responseData.push({ error: responseData.reason });
|
||||
} else {
|
||||
throw new NodeApiError(this.getNode(), {
|
||||
error: responseData.reason,
|
||||
description: 'Hint: Increase the Wait Time to avoid this error',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
returnData.push(...responseData.data);
|
||||
}
|
||||
} else {
|
||||
returnData.push(responseData);
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'fetchRequest') {
|
||||
for (let i = 0; i < entryData.length; i++) {
|
||||
const requestId = this.getNodeParameter('requestId', i) as string;
|
||||
responseData = await dropcontactApiRequest.call(this, 'GET', `/batch/${requestId}`, {}, {}) as { request_id: string, error: string, success: boolean };
|
||||
if (!responseData.success) {
|
||||
if (this.continueOnFail()) {
|
||||
responseData.push({ error: responseData.reason || 'invalid request' });
|
||||
} else {
|
||||
throw new NodeApiError(this.getNode(), { error: responseData.reason || 'invalid request' });
|
||||
}
|
||||
}
|
||||
returnData.push(...responseData.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [this.helpers.returnJsonArray(returnData)];
|
||||
}
|
||||
}
|
||||
82
packages/nodes-base/nodes/Dropcontact/GenericFunction.ts
Normal file
82
packages/nodes-base/nodes/Dropcontact/GenericFunction.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
IHookFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
ICredentialDataDecryptedObject,
|
||||
ICredentialTestFunctions,
|
||||
IDataObject,
|
||||
ILoadOptionsFunctions,
|
||||
NodeApiError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
OptionsWithUri,
|
||||
} from 'request';
|
||||
|
||||
/**
|
||||
* Make an authenticated API request to Bubble.
|
||||
*/
|
||||
export async function dropcontactApiRequest(
|
||||
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions,
|
||||
method: string,
|
||||
endpoint: string,
|
||||
body: IDataObject,
|
||||
qs: IDataObject,
|
||||
) {
|
||||
|
||||
const { apiKey } = await this.getCredentials('dropcontactApi') as {
|
||||
apiKey: string,
|
||||
};
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'user-agent': 'n8n',
|
||||
'X-Access-Token': apiKey,
|
||||
},
|
||||
method,
|
||||
uri: `https://api.dropcontact.io${endpoint}`,
|
||||
qs,
|
||||
body,
|
||||
json: true,
|
||||
};
|
||||
|
||||
if (!Object.keys(body).length) {
|
||||
delete options.body;
|
||||
}
|
||||
|
||||
if (!Object.keys(qs).length) {
|
||||
delete options.qs;
|
||||
}
|
||||
|
||||
try {
|
||||
return await this.helpers.request!(options);
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function validateCrendetials(this: ICredentialTestFunctions, decryptedCredentials: ICredentialDataDecryptedObject): Promise<any> { // tslint:disable-line:no-any
|
||||
const credentials = decryptedCredentials;
|
||||
|
||||
const { apiKey } = credentials as {
|
||||
apiKey: string,
|
||||
};
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'user-agent': 'n8n',
|
||||
'X-Access-Token': apiKey,
|
||||
},
|
||||
method: 'POST',
|
||||
body: {
|
||||
data: [{ email: '' }],
|
||||
},
|
||||
uri: `https://api.dropcontact.io/batch`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
return this.helpers.request!(options);
|
||||
}
|
||||
|
||||
3
packages/nodes-base/nodes/Dropcontact/dropcontact.svg
Normal file
3
packages/nodes-base/nodes/Dropcontact/dropcontact.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M29.5692 16.219C28.1469 15.5371 26.5535 15.1552 24.8709 15.1552C18.8615 15.1552 13.9899 20.0268 13.9899 26.0362C13.9899 32.0456 18.8615 36.9172 24.8709 36.9172C28.9621 36.9172 32.526 34.6592 34.3843 31.3215L31.7614 29.8643C30.4154 32.2818 27.8341 33.9172 24.8709 33.9172C20.5183 33.9172 16.9899 30.3888 16.9899 26.0362C16.9899 21.6836 20.5183 18.1552 24.8709 18.1552C26.0896 18.1552 27.2437 18.4318 28.2738 18.9257L29.5692 16.219ZM34.7779 1.98444V27.7461H31.669V0.899363C29.5459 0.313171 27.3095 0 25 0C11.1929 0 0 11.1929 0 25C0 38.8071 11.1929 50 25 50C38.8071 50 50 38.8071 50 25C50 14.6626 43.7259 5.7907 34.7779 1.98444Z" fill="#0ABA9F"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 773 B |
@@ -152,6 +152,10 @@ const nodeOperationOptions: INodeProperties[] = [
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Circle',
|
||||
value: 'circle',
|
||||
},
|
||||
{
|
||||
name: 'Line',
|
||||
value: 'line',
|
||||
@@ -192,6 +196,7 @@ const nodeOperationOptions: INodeProperties[] = [
|
||||
'draw',
|
||||
],
|
||||
primitive: [
|
||||
'circle',
|
||||
'line',
|
||||
'rectangle',
|
||||
],
|
||||
@@ -210,6 +215,7 @@ const nodeOperationOptions: INodeProperties[] = [
|
||||
'draw',
|
||||
],
|
||||
primitive: [
|
||||
'circle',
|
||||
'line',
|
||||
'rectangle',
|
||||
],
|
||||
@@ -228,6 +234,7 @@ const nodeOperationOptions: INodeProperties[] = [
|
||||
'draw',
|
||||
],
|
||||
primitive: [
|
||||
'circle',
|
||||
'line',
|
||||
'rectangle',
|
||||
],
|
||||
@@ -246,6 +253,7 @@ const nodeOperationOptions: INodeProperties[] = [
|
||||
'draw',
|
||||
],
|
||||
primitive: [
|
||||
'circle',
|
||||
'line',
|
||||
'rectangle',
|
||||
],
|
||||
@@ -363,7 +371,7 @@ const nodeOperationOptions: INodeProperties[] = [
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Max amount of characters in a line before a<br />line-break should get added.',
|
||||
description: 'Max amount of characters in a line before a line-break should get added.',
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
@@ -470,7 +478,111 @@ const nodeOperationOptions: INodeProperties[] = [
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The name of the binary property which contains the data of the image to<br />composite on top of image which is found in Property Name.',
|
||||
description: 'The name of the binary property which contains the data of the image to composite on top of image which is found in Property Name.',
|
||||
},
|
||||
{
|
||||
displayName: 'Operator',
|
||||
name: 'operator',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'composite',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Add',
|
||||
value: 'Add',
|
||||
},
|
||||
{
|
||||
name: 'Atop',
|
||||
value: 'Atop',
|
||||
},
|
||||
{
|
||||
name: 'Bumpmap',
|
||||
value: 'Bumpmap',
|
||||
},
|
||||
{
|
||||
name: 'Copy',
|
||||
value: 'Copy',
|
||||
},
|
||||
{
|
||||
name: 'Copy Black',
|
||||
value: 'CopyBlack',
|
||||
},
|
||||
{
|
||||
name: 'Copy Blue',
|
||||
value: 'CopyBlue',
|
||||
},
|
||||
{
|
||||
name: 'Copy Cyan',
|
||||
value: 'CopyCyan',
|
||||
},
|
||||
{
|
||||
name: 'Copy Green',
|
||||
value: 'CopyGreen',
|
||||
},
|
||||
{
|
||||
name: 'Copy Magenta',
|
||||
value: 'CopyMagenta',
|
||||
},
|
||||
{
|
||||
name: 'Copy Opacity',
|
||||
value: 'CopyOpacity',
|
||||
},
|
||||
{
|
||||
name: 'Copy Red',
|
||||
value: 'CopyRed',
|
||||
},
|
||||
{
|
||||
name: 'Copy Yellow',
|
||||
value: 'CopyYellow',
|
||||
},
|
||||
{
|
||||
name: 'Difference',
|
||||
value: 'Difference',
|
||||
},
|
||||
{
|
||||
name: 'Divide',
|
||||
value: 'Divide',
|
||||
},
|
||||
{
|
||||
name: 'In',
|
||||
value: 'In',
|
||||
},
|
||||
{
|
||||
name: 'Minus',
|
||||
value: 'Minus',
|
||||
},
|
||||
{
|
||||
name: 'Multiply',
|
||||
value: 'Multiply',
|
||||
},
|
||||
{
|
||||
name: 'Out',
|
||||
value: 'Out',
|
||||
},
|
||||
{
|
||||
name: 'Over',
|
||||
value: 'Over',
|
||||
},
|
||||
{
|
||||
name: 'Plus',
|
||||
value: 'Plus',
|
||||
},
|
||||
{
|
||||
name: 'Subtract',
|
||||
value: 'Subtract',
|
||||
},
|
||||
{
|
||||
name: 'Xor',
|
||||
value: 'Xor',
|
||||
},
|
||||
],
|
||||
default: 'Over',
|
||||
description: 'The operator to use to combine the images.',
|
||||
},
|
||||
{
|
||||
displayName: 'Position X',
|
||||
@@ -997,6 +1109,7 @@ export class EditImage implements INodeType {
|
||||
],
|
||||
composite: [
|
||||
'dataPropertyNameComposite',
|
||||
'operator',
|
||||
'positionX',
|
||||
'positionY',
|
||||
],
|
||||
@@ -1095,6 +1208,7 @@ export class EditImage implements INodeType {
|
||||
} else if (operationData.operation === 'composite') {
|
||||
const positionX = operationData.positionX as number;
|
||||
const positionY = operationData.positionY as number;
|
||||
const operator = operationData.operator as string;
|
||||
|
||||
const geometryString = (positionX >= 0 ? '+' : '') + positionX + (positionY >= 0 ? '+' : '') + positionY;
|
||||
|
||||
@@ -1109,9 +1223,9 @@ export class EditImage implements INodeType {
|
||||
if (operations[0].operation === 'create') {
|
||||
// It seems like if the image gets created newly we have to create a new gm instance
|
||||
// else it fails for some reason
|
||||
gmInstance = gm(gmInstance!.stream('png')).geometry(geometryString).composite(path);
|
||||
gmInstance = gm(gmInstance!.stream('png')).compose(operator).geometry(geometryString).composite(path);
|
||||
} else {
|
||||
gmInstance = gmInstance!.geometry(geometryString).composite(path);
|
||||
gmInstance = gmInstance!.compose(operator).geometry(geometryString).composite(path);
|
||||
}
|
||||
|
||||
if (operations.length !== i + 1) {
|
||||
@@ -1131,6 +1245,8 @@ export class EditImage implements INodeType {
|
||||
|
||||
if (operationData.primitive === 'line') {
|
||||
gmInstance = gmInstance.drawLine(operationData.startPositionX as number, operationData.startPositionY as number, operationData.endPositionX as number, operationData.endPositionY as number);
|
||||
} else if (operationData.primitive === 'circle') {
|
||||
gmInstance = gmInstance.drawCircle(operationData.startPositionX as number, operationData.startPositionY as number, operationData.endPositionX as number, operationData.endPositionY as number);
|
||||
} else if (operationData.primitive === 'rectangle') {
|
||||
gmInstance = gmInstance.drawRectangle(operationData.startPositionX as number, operationData.startPositionY as number, operationData.endPositionX as number, operationData.endPositionY as number, operationData.cornerRadius as number || undefined);
|
||||
}
|
||||
@@ -154,7 +154,7 @@ export class Egoi implements INodeType {
|
||||
},
|
||||
},
|
||||
default: true,
|
||||
description: 'By default the response just includes the contact id. If this option gets activated it<br />will resolve the data automatically.',
|
||||
description: 'By default the response just includes the contact id. If this option gets activated, it will resolve the data automatically.',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"node": "n8n-nodes-base.elasticSecurity",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": [
|
||||
"Development"
|
||||
],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/credentials/elasticSecurity"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.elasticSecurity/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -291,7 +291,7 @@ export const documentFields = [
|
||||
{
|
||||
displayName: 'Allow Partial Search Results',
|
||||
name: 'allow_partial_search_results',
|
||||
description: 'If true, return partial results if there are shard request timeouts or shard failures.<br>If false, returns an error with no partial results. Defaults to true',
|
||||
description: '<p>If true, return partial results if there are shard request timeouts or shard failures.</p><p>If false, returns an error with no partial results. Defaults to true.</p>',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
},
|
||||
@@ -379,7 +379,7 @@ export const documentFields = [
|
||||
{
|
||||
displayName: 'Pre-Filter Shard Size',
|
||||
name: 'pre_filter_shard_size',
|
||||
description: 'Define a threshold that enforces a pre-filter roundtrip to prefilter search shards based on query rewriting.<br>Only used if the number of shards the search request expands to exceeds the threshold',
|
||||
description: 'Define a threshold that enforces a pre-filter roundtrip to prefilter search shards based on query rewriting. Only used if the number of shards the search request expands to exceeds the threshold',
|
||||
type: 'number',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
|
||||
@@ -110,7 +110,7 @@ export const indexFields = [
|
||||
{
|
||||
displayName: 'Master Timeout',
|
||||
name: 'master_timeout',
|
||||
description: 'Period to wait for a connection to the master node. If no response is received before the timeout expires,<br>the request fails and returns an error. Defaults to <code>1m</code>. See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#time-units">Elasticsearch time units reference</a>',
|
||||
description: 'Period to wait for a connection to the master node. If no response is received before the timeout expires, the request fails and returns an error. Defaults to <code>1m</code>. See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#time-units">Elasticsearch time units reference</a>',
|
||||
type: 'string',
|
||||
default: '1m',
|
||||
},
|
||||
@@ -128,14 +128,14 @@ export const indexFields = [
|
||||
{
|
||||
displayName: 'Timeout',
|
||||
name: 'timeout',
|
||||
description: 'Period to wait for a response. If no response is received before the timeout expires, the request<br>fails and returns an error. Defaults to <code>30s</code>. See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#time-units">Elasticsearch time units reference</a>',
|
||||
description: 'Period to wait for a response. If no response is received before the timeout expires, the request fails and returns an error. Defaults to <code>30s</code>. See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#time-units">Elasticsearch time units reference</a>',
|
||||
type: 'string',
|
||||
default: '30s',
|
||||
},
|
||||
{
|
||||
displayName: 'Wait for Active Shards',
|
||||
name: 'wait_for_active_shards',
|
||||
description: 'The number of shard copies that must be active before proceeding with the operation. Set to <code>all</code><br>or any positive integer up to the total number of shards in the index. Default: 1, the primary shard',
|
||||
description: 'The number of shard copies that must be active before proceeding with the operation. Set to <code>all</code> or any positive integer up to the total number of shards in the index. Default: 1, the primary shard',
|
||||
type: 'string',
|
||||
default: '1',
|
||||
},
|
||||
@@ -269,7 +269,7 @@ export const indexFields = [
|
||||
{
|
||||
displayName: 'Master Timeout',
|
||||
name: 'master_timeout',
|
||||
description: 'Period to wait for a connection to the master node. If no response is received before the timeout expires,<br>the request fails and returns an error. Defaults to <code>1m</code>. See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#time-units">Elasticsearch time units reference</a>',
|
||||
description: 'Period to wait for a connection to the master node. If no response is received before the timeout expires, the request fails and returns an error. Defaults to <code>1m</code>. See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#time-units">Elasticsearch time units reference</a>',
|
||||
type: 'string',
|
||||
default: '1m',
|
||||
},
|
||||
|
||||
@@ -36,6 +36,7 @@ export class EmailReadImap implements INodeType {
|
||||
group: ['trigger'],
|
||||
version: 1,
|
||||
description: 'Triggers the workflow when a new email is received',
|
||||
eventTriggerDescription: 'Waiting for you to receive an email',
|
||||
defaults: {
|
||||
name: 'IMAP Email',
|
||||
color: '#44AA22',
|
||||
@@ -70,7 +71,7 @@ export class EmailReadImap implements INodeType {
|
||||
},
|
||||
],
|
||||
default: 'read',
|
||||
description: 'What to do after the email has been received. If "nothing" gets<br />selected it will be processed multiple times.',
|
||||
description: 'What to do after the email has been received. If "nothing" gets selected it will be processed multiple times.',
|
||||
},
|
||||
{
|
||||
displayName: 'Download Attachments',
|
||||
@@ -84,7 +85,7 @@ export class EmailReadImap implements INodeType {
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'If attachments of emails should be downloaded.<br />Only set if needed as it increases processing.',
|
||||
description: 'If attachments of emails should be downloaded. Only set if needed as it increases processing.',
|
||||
},
|
||||
{
|
||||
displayName: 'Format',
|
||||
@@ -122,7 +123,7 @@ export class EmailReadImap implements INodeType {
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Prefix for name of the binary property to which to<br />write the attachments. An index starting with 0 will be added.<br />So if name is "attachment_" the first attachment is saved to "attachment_0"',
|
||||
description: 'Prefix for name of the binary property to which to write the attachments. An index starting with 0 will be added. So if name is "attachment_" the first attachment is saved to "attachment_0"',
|
||||
},
|
||||
{
|
||||
displayName: 'Property Prefix Name',
|
||||
@@ -139,7 +140,7 @@ export class EmailReadImap implements INodeType {
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Prefix for name of the binary property to which to<br />write the attachments. An index starting with 0 will be added.<br />So if name is "attachment_" the first attachment is saved to "attachment_0"',
|
||||
description: 'Prefix for name of the binary property to which to write the attachments. An index starting with 0 will be added. So if name is "attachment_" the first attachment is saved to "attachment_0"',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
@@ -399,7 +400,7 @@ export class EmailReadImap implements INodeType {
|
||||
}
|
||||
if (staticData.lastMessageUid !== undefined) {
|
||||
searchCriteria.push(['UID', `${staticData.lastMessageUid as number}:*`]);
|
||||
/**
|
||||
/**
|
||||
* A short explanation about UIDs and how they work
|
||||
* can be found here: https://dev.to/kehers/imap-new-messages-since-last-check-44gm
|
||||
* TL;DR:
|
||||
@@ -413,7 +414,7 @@ export class EmailReadImap implements INodeType {
|
||||
*/
|
||||
Logger.debug('Querying for new messages on node "EmailReadImap"', {searchCriteria});
|
||||
}
|
||||
|
||||
|
||||
const returnData = await getNewEmails(connection, searchCriteria);
|
||||
|
||||
if (returnData.length) {
|
||||
@@ -103,7 +103,7 @@ export class EmailSend implements INodeType {
|
||||
name: 'attachments',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Name of the binary properties which contain<br />data which should be added to email as attachment.<br />Multiple ones can be comma separated.',
|
||||
description: 'Name of the binary properties that contain data to add to email as attachment. Multiple ones can be comma separated.',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
@@ -288,7 +288,7 @@ export const campaignFields = [
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
description: 'The ID of the campaign to pause.<br>The campaign must be in RUNNING mode.',
|
||||
description: 'The ID of the campaign to pause. The campaign must be in RUNNING mode.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
@@ -310,7 +310,7 @@ export const campaignFields = [
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
description: 'The ID of the campaign to start.<br>Email provider and contacts must be set.',
|
||||
description: 'The ID of the campaign to start. Email provider and contacts must be set.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
||||
@@ -14,6 +14,7 @@ export class ErrorTrigger implements INodeType {
|
||||
group: ['trigger'],
|
||||
version: 1,
|
||||
description: 'Triggers the workflow when another workflow has an error',
|
||||
eventTriggerDescription: '',
|
||||
maxNodes: 1,
|
||||
defaults: {
|
||||
name: 'Error Trigger',
|
||||
@@ -182,7 +182,7 @@ export class EventbriteTrigger implements INodeType {
|
||||
name: 'resolveData',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'By default does the webhook-data only contain the URL to receive<br />the object data manually. If this option gets activated it<br />will resolve the data automatically.',
|
||||
description: 'By default does the webhook-data only contain the URL to receive the object data manually. If this option gets activated, it will resolve the data automatically.',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -86,6 +86,14 @@ export class FacebookGraphApi implements INodeType {
|
||||
name: 'Default',
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
name: 'v12.0',
|
||||
value: 'v12.0',
|
||||
},
|
||||
{
|
||||
name: 'v11.0',
|
||||
value: 'v11.0',
|
||||
},
|
||||
{
|
||||
name: 'v10.0',
|
||||
value: 'v10.0',
|
||||
@@ -196,9 +204,7 @@ export class FacebookGraphApi implements INodeType {
|
||||
],
|
||||
},
|
||||
},
|
||||
description: `Name of the binary property which contains the data for the file to be uploaded.<br />
|
||||
For Form-Data Multipart, multiple can be provided in the format:<br />
|
||||
"sendKey1:binaryProperty1,sendKey2:binaryProperty2`,
|
||||
description: `Name of the binary property which contains the data for the file to be uploaded. For Form-Data Multipart, they can be provided in the format: <code>"sendKey1:binaryProperty1,sendKey2:binaryProperty2</code>`,
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
|
||||
@@ -202,7 +202,7 @@ export class FileMaker implements INodeType {
|
||||
},
|
||||
},
|
||||
placeholder: 'Portals',
|
||||
description: 'The portal result set to return. Use the portal object name or portal<br />table name. If this parameter is omitted, the API will return all<br />portal objects and records in the layout. For best performance<br />, pass the portal object name or portal table name.',
|
||||
description: 'The portal result set to return. Use the portal object name or portal table name. If this parameter is omitted, the API will return all portal objects and records in the layout. For best performance, pass the portal object name or portal table name.',
|
||||
},
|
||||
// ----------------------------------
|
||||
// find/records
|
||||
|
||||
@@ -80,7 +80,7 @@ export class FlowTrigger implements INodeType {
|
||||
],
|
||||
},
|
||||
},
|
||||
description: `Lists ids, perhaps known better as "Projects" separated by ,`,
|
||||
description: `Lists ids, perhaps known better as "Projects" separated by a comma (,)`,
|
||||
},
|
||||
{
|
||||
displayName: 'Task ID',
|
||||
@@ -100,7 +100,7 @@ export class FlowTrigger implements INodeType {
|
||||
],
|
||||
},
|
||||
},
|
||||
description: `Task ids separated by ,`,
|
||||
description: `Task ids separated by a comma (,)`,
|
||||
},
|
||||
],
|
||||
|
||||
|
||||
@@ -221,7 +221,7 @@ export const taskFields = [
|
||||
type: 'string',
|
||||
default: '',
|
||||
required : false,
|
||||
description: `A list of tag names to apply to the new task separated by ,`,
|
||||
description: `A list of tag names to apply to the new task separated by a comma (,)`,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -408,7 +408,7 @@ export const taskFields = [
|
||||
name: 'tags',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `A list of tag names to apply to the new task separated by ,`,
|
||||
description: `A list of tag names to apply to the new task separated by a comma (,)`,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -86,8 +86,7 @@ export const contactFields = [
|
||||
],
|
||||
},
|
||||
},
|
||||
description: `Primary email address of the contact. If you want to associate<br>
|
||||
additional email(s) with this contact, use the other_emails attribute.`,
|
||||
description: `Primary email address of the contact. If you want to associate additional email(s) with this contact, use the other_emails attribute.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Contact ID',
|
||||
@@ -136,7 +135,7 @@ export const contactFields = [
|
||||
// name: 'avatar',
|
||||
// type: '',
|
||||
// default: '',
|
||||
// description: `Avatar image of the contact The maximum file size is 5MB<br>
|
||||
// description: `Avatar image of the contact The maximum file size is 5MB
|
||||
// and the supported file types are .jpg, .jpeg, .jpe, and .png.`,
|
||||
// },
|
||||
{
|
||||
@@ -154,8 +153,7 @@ export const contactFields = [
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
description: `Key value pairs containing the name and value of the custom field.<br>
|
||||
Only dates in the format YYYY-MM-DD are accepted as input for custom date fields.`,
|
||||
description: `Key value pairs containing the name and value of the custom field. Only dates in the format YYYY-MM-DD are accepted as input for custom date fields.`,
|
||||
default: [],
|
||||
options: [
|
||||
{
|
||||
@@ -199,8 +197,7 @@ export const contactFields = [
|
||||
],
|
||||
},
|
||||
},
|
||||
description: `Primary email address of the contact. If you want to associate<br>
|
||||
additional email(s) with this contact, use the other_emails attribute.`,
|
||||
description: `Primary email address of the contact. If you want to associate additional email(s) with this contact, use the other_emails attribute.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Job Title',
|
||||
@@ -214,9 +211,7 @@ export const contactFields = [
|
||||
name: 'language',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `Language of the contact. Default language is "en".<br>
|
||||
This attribute can only be set if the Multiple Language feature is<br>
|
||||
enabled (Garden plan and above).`,
|
||||
description: `Language of the contact. Default language is "en". This attribute can only be set if the Multiple Language feature is enabled (Garden plan and above).`,
|
||||
},
|
||||
{
|
||||
displayName: 'Mobile',
|
||||
@@ -248,9 +243,7 @@ export const contactFields = [
|
||||
multipleValues: true,
|
||||
},
|
||||
placeholder: 'Add Company',
|
||||
description: `Additional companies associated with the contact.<br>
|
||||
This attribute can only be set if the Multiple Companies feature<br>
|
||||
is enabled (Estate plan and above).`,
|
||||
description: `Additional companies associated with the contact. This attribute can only be set if the Multiple Companies feature is enabled (Estate plan and above).`,
|
||||
},
|
||||
{
|
||||
displayName: 'Other Emails',
|
||||
@@ -285,8 +278,7 @@ export const contactFields = [
|
||||
name: 'time_zone',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `Time zone of the contact. Default value is the time zone of the domain.<br>
|
||||
This attribute can only be set if the Multiple Time Zone feature is enabled (Garden plan and above)];`,
|
||||
description: `Time zone of the contact. Default value is the time zone of the domain. This attribute can only be set if the Multiple Time Zone feature is enabled (Garden plan and above).`,
|
||||
},
|
||||
{
|
||||
displayName: 'Twitter ID',
|
||||
@@ -307,8 +299,7 @@ export const contactFields = [
|
||||
name: 'view_all_tickets',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: `Set to true if the contact can see all the tickets<br>
|
||||
that are associated with the company to which he belong.`,
|
||||
description: `Set to true if the contact can see all the tickets that are associated with the company to which they belong.`,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -398,7 +398,7 @@ export class Freshdesk implements INodeType {
|
||||
name: 'ccEmails',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `Separated by , email addresses added in the 'cc' field of the incoming ticket email`,
|
||||
description: `Separated by a comma (,) email addresses added in the 'cc' field of the incoming ticket email`,
|
||||
},
|
||||
{
|
||||
displayName: 'Company',
|
||||
@@ -485,7 +485,7 @@ export class Freshdesk implements INodeType {
|
||||
name: 'tags',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `separated by , tags that have been associated with the ticket`,
|
||||
description: `separated by a comma (,) tags that have been associated with the ticket`,
|
||||
},
|
||||
{
|
||||
displayName: 'Type',
|
||||
@@ -642,7 +642,7 @@ export class Freshdesk implements INodeType {
|
||||
name: 'ccEmails',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `Separated by , email addresses added in the 'cc' field of the incoming ticket email`,
|
||||
description: `Separated by a comma (,) email addresses added in the 'cc' field of the incoming ticket email`,
|
||||
},
|
||||
{
|
||||
displayName: 'Company',
|
||||
@@ -847,7 +847,7 @@ export class Freshdesk implements INodeType {
|
||||
name: 'tags',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `separated by , tags that have been associated with the ticket`,
|
||||
description: `separated by a comma (,) tags that have been associated with the ticket`,
|
||||
},
|
||||
{
|
||||
displayName: 'Type',
|
||||
|
||||
@@ -573,7 +573,6 @@ export class Freshservice implements INodeType {
|
||||
const assetFields = this.getNodeParameter('assetFieldsUi.assetFieldValue', i, []) as IDataObject[];
|
||||
|
||||
Object.assign(body, { type_fields: assetFields.reduce((obj, value) => Object.assign(obj, { [`${value.name}`]: value.value }), {}) });
|
||||
console.log(body);
|
||||
responseData = await freshserviceApiRequest.call(this, 'POST', '/assets', body);
|
||||
|
||||
} else if (operation === 'delete') {
|
||||
|
||||
@@ -18,5 +18,8 @@
|
||||
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.ftp/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"alias": [
|
||||
"SFTP"
|
||||
]
|
||||
}
|
||||
@@ -386,9 +386,7 @@ export const contactFields = [
|
||||
name: 'exactMatch',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: `When set to true it will search for contacts with the exact value<br>
|
||||
of the email and name provided in the query string. Without this flag, matching is done via a standard 'like' comparison,<br>
|
||||
which may sometimes be slow.`,
|
||||
description: `When set to true it will search for contacts with the exact value of the email and name provided in the query string. Without this flag, matching is done via a standard 'like' comparison, which may sometimes be slow.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Fields',
|
||||
|
||||
@@ -37,8 +37,7 @@ export const commitFields = [
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: '/data/file1.json',
|
||||
description: `Comma separated list of paths (absolute or relative to Repository Path) of<br />
|
||||
files or folders to commit. If not set will all "added" files and folders be committed.`,
|
||||
description: `Comma separated list of paths (absolute or relative to Repository Path) of files or folders to commit. If not set will all "added" files and folders be committed.`,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import { OptionsWithUri } from 'request';
|
||||
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
ICredentialsDecrypted,
|
||||
ICredentialTestFunctions,
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
NodeCredentialTestResult,
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
@@ -39,6 +44,7 @@ export class Github implements INodeType {
|
||||
{
|
||||
name: 'githubApi',
|
||||
required: true,
|
||||
testedBy: 'githubApiTest',
|
||||
displayOptions: {
|
||||
show: {
|
||||
authentication: [
|
||||
@@ -481,7 +487,7 @@ export class Github implements INodeType {
|
||||
|
||||
},
|
||||
placeholder: '',
|
||||
description: 'Name of the binary property which contains<br />the data for the file.',
|
||||
description: 'Name of the binary property which contains the data for the file.',
|
||||
},
|
||||
{
|
||||
displayName: 'Commit Message',
|
||||
@@ -597,7 +603,7 @@ export class Github implements INodeType {
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'If set it will set the data of the file as binary property<br />instead of returning the raw API response.',
|
||||
description: 'If set it will set the data of the file as binary property instead of returning the raw API response.',
|
||||
},
|
||||
{
|
||||
displayName: 'Binary Property',
|
||||
@@ -620,7 +626,7 @@ export class Github implements INodeType {
|
||||
|
||||
},
|
||||
placeholder: '',
|
||||
description: 'Name of the binary property in which to save<br />the binary data of the received file.',
|
||||
description: 'Name of the binary property in which to save the binary data of the received file.',
|
||||
},
|
||||
|
||||
|
||||
@@ -1698,6 +1704,43 @@ export class Github implements INodeType {
|
||||
],
|
||||
};
|
||||
|
||||
methods = {
|
||||
credentialTest: {
|
||||
async githubApiTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise<NodeCredentialTestResult> {
|
||||
const credentials = credential.data;
|
||||
const baseUrl = credentials!.server as string || 'https://api.github.com/user';
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'User-Agent': 'n8n',
|
||||
Authorization: `token ${credentials!.accessToken}`,
|
||||
},
|
||||
uri: baseUrl,
|
||||
json: true,
|
||||
timeout: 5000,
|
||||
};
|
||||
try {
|
||||
const response = await this.helpers.request(options);
|
||||
if (!response.id) {
|
||||
return {
|
||||
status: 'Error',
|
||||
message: `Token is not valid: ${response.error}`,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 'Error',
|
||||
message: `Settings are not valid: ${error}`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: 'OK',
|
||||
message: 'Authentication successful!',
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
|
||||
@@ -173,7 +173,7 @@ export class GithubTrigger implements INodeType {
|
||||
{
|
||||
name: 'installation',
|
||||
value: 'installation',
|
||||
description: 'Triggered when someone installs (created) , uninstalls (deleted), or accepts new permissions (new_permissions_accepted) for a GitHub App. When a GitHub App owner requests new permissions, the person who installed the GitHub App must accept the new permissions request.',
|
||||
description: 'Triggered when someone installs (created), uninstalls (deleted), or accepts new permissions (new_permissions_accepted) for a GitHub App. When a GitHub App owner requests new permissions, the person who installed the GitHub App must accept the new permissions request.',
|
||||
},
|
||||
{
|
||||
name: 'installation_repositories',
|
||||
@@ -314,7 +314,7 @@ export class GithubTrigger implements INodeType {
|
||||
{
|
||||
name: 'team',
|
||||
value: 'team',
|
||||
description: 'Triggered when an organization\'s team is created,<br/>deleted, edited, added_to_repository, or removed_from_repository. Organization hooks only',
|
||||
description: 'Triggered when an organization\'s team is created, deleted, edited, added_to_repository, or removed_from_repository. Organization hooks only',
|
||||
},
|
||||
{
|
||||
name: 'team_add',
|
||||
|
||||
@@ -197,8 +197,7 @@ export const coorganizerFields = [
|
||||
],
|
||||
},
|
||||
},
|
||||
description: `By default only internal co-organizers (with a GoToWebinar account) can be deleted.</br>
|
||||
If you want to use this call for external co-organizers you have to set this parameter to 'true'.`,
|
||||
description: `By default only internal co-organizers (with a GoToWebinar account) can be deleted. If you want to use this call for external co-organizers you have to set this parameter to 'true'.`,
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
@@ -276,8 +275,7 @@ export const coorganizerFields = [
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: `By default only internal co-organizers (with a GoToWebinar account) can be deleted.</br>
|
||||
If you want to use this call for external co-organizers you have to set this parameter to 'true'.`,
|
||||
description: `By default only internal co-organizers (with a GoToWebinar account) can be deleted. If you want to use this call for external co-organizers you have to set this parameter to 'true'.`,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user