Merge branch 'n8n-io:master' into Add-schema-registry-into-kafka

This commit is contained in:
Ricardo Georgel
2021-11-26 17:11:45 -03:00
committed by GitHub
593 changed files with 62033 additions and 4803 deletions

View File

@@ -1,17 +1,17 @@
import {
ICredentialType,
NodePropertyTypes,
INodeProperties,
} from 'n8n-workflow';
export class ActionNetworkApi implements ICredentialType {
name = 'actionNetworkApi';
displayName = 'Action Network API';
documentationUrl = 'actionNetwork';
properties = [
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string' as NodePropertyTypes,
type: 'string',
default: '',
},
];

View File

@@ -17,7 +17,7 @@ export class Aws implements ICredentialType {
default: 'us-east-1',
},
{
displayName: 'Access Key Id',
displayName: 'Access Key ID',
name: 'accessKeyId',
type: 'string',
default: '',

View File

@@ -1,6 +1,6 @@
import {
ICredentialType,
NodePropertyTypes,
INodeProperties,
} from 'n8n-workflow';
export class CiscoWebexOAuth2Api implements ICredentialType {
@@ -9,37 +9,37 @@ export class CiscoWebexOAuth2Api implements ICredentialType {
'oAuth2Api',
];
displayName = 'Cisco Webex OAuth2 API';
properties = [
properties: INodeProperties[] = [
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden' as NodePropertyTypes,
type: 'hidden',
default: 'https://webexapis.com/v1/authorize',
required: true,
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden' as NodePropertyTypes,
type: 'hidden',
default: 'https://webexapis.com/v1/access_token',
required: true,
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden' as NodePropertyTypes,
type: 'hidden',
default: 'spark:memberships_read meeting:recordings_read spark:kms meeting:schedules_read spark:rooms_read spark:messages_write spark:memberships_write meeting:recordings_write meeting:preferences_read spark:messages_read meeting:schedules_write',
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden' as NodePropertyTypes,
type: 'hidden',
default: '',
},
{
displayName: 'Authentication',
name: 'authentication',
type: 'hidden' as NodePropertyTypes,
type: 'hidden',
default: 'body',
},
];

View File

@@ -0,0 +1,18 @@
import {
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class DhlApi implements ICredentialType {
name = 'dhlApi';
displayName = 'DHL API';
documentationUrl = 'dhl';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
default: '',
},
];
}

View File

@@ -0,0 +1,18 @@
import {
ICredentialType,
NodePropertyTypes,
} from 'n8n-workflow';
export class DropcontactApi implements ICredentialType {
name = 'dropcontactApi';
displayName = 'Dropcontact API';
documentationUrl = 'dropcontact';
properties = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string' as NodePropertyTypes,
default: '',
},
];
}

View File

@@ -1,7 +1,6 @@
import {
ICredentialType,
INodeProperties,
NodePropertyTypes,
} from 'n8n-workflow';
export class FormIoApi implements ICredentialType {
@@ -42,13 +41,13 @@ export class FormIoApi implements ICredentialType {
{
displayName: 'Email',
name: 'email',
type: 'string' as NodePropertyTypes,
type: 'string',
default: '',
},
{
displayName: 'Password',
name: 'password',
type: 'string' as NodePropertyTypes,
type: 'string',
typeOptions: {
password: true,
},

View File

@@ -1,6 +1,6 @@
import {
ICredentialType,
NodePropertyTypes,
INodeProperties,
} from 'n8n-workflow';
@@ -8,11 +8,11 @@ export class FormstackApi implements ICredentialType {
name = 'formstackApi';
displayName = 'Formstack API';
documentationUrl = 'formstackTrigger';
properties = [
properties: INodeProperties[] = [
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string' as NodePropertyTypes,
type: 'string',
default: '',
},
];

View File

@@ -1,6 +1,6 @@
import {
ICredentialType,
NodePropertyTypes,
INodeProperties,
} from 'n8n-workflow';
const scopes: string[] = [];
@@ -12,37 +12,37 @@ export class FormstackOAuth2Api implements ICredentialType {
];
displayName = 'Formstack OAuth2 API';
documentationUrl = 'formstackTrigger';
properties = [
properties: INodeProperties[] = [
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden' as NodePropertyTypes,
type: 'hidden',
default: 'https://www.formstack.com/api/v2/oauth2/authorize',
required: true,
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden' as NodePropertyTypes,
type: 'hidden',
default: 'https://www.formstack.com/api/v2/oauth2/token',
required: true,
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden' as NodePropertyTypes,
type: 'hidden',
default: scopes.join(' '),
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden' as NodePropertyTypes,
type: 'hidden',
default: '',
},
{
displayName: 'Authentication',
name: 'authentication',
type: 'hidden' as NodePropertyTypes,
type: 'hidden',
default: 'header',
},
];

View File

@@ -1,6 +1,6 @@
import {
ICredentialType,
NodePropertyTypes,
INodeProperties,
} from 'n8n-workflow';
const scopes = [
@@ -16,11 +16,11 @@ export class GoogleDocsOAuth2Api implements ICredentialType {
];
displayName = 'Google Docs OAuth2 API';
documentationUrl = 'google';
properties = [
properties: INodeProperties[] = [
{
displayName: 'Scope',
name: 'scope',
type: 'hidden' as NodePropertyTypes,
type: 'hidden',
default: scopes.join(' '),
},
];

View File

@@ -1,6 +1,6 @@
import {
ICredentialType,
NodePropertyTypes,
INodeProperties,
} from 'n8n-workflow';
const scopes = [
@@ -14,11 +14,11 @@ export class GooglePerspectiveOAuth2Api implements ICredentialType {
];
displayName = 'Google Perspective OAuth2 API';
documentationUrl = 'google';
properties = [
properties: INodeProperties[] = [
{
displayName: 'Scope',
name: 'scope',
type: 'hidden' as NodePropertyTypes,
type: 'hidden',
default: scopes.join(' '),
},
];

View File

@@ -0,0 +1,28 @@
import {
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class GrafanaApi implements ICredentialType {
name = 'grafanaApi';
displayName = 'Grafana API';
documentationUrl = 'grafana';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
default: '',
required: true,
},
{
displayName: 'Base URL',
name: 'baseUrl',
type: 'string',
default: '',
description: 'Base URL of your Grafana instance',
placeholder: 'e.g. https://n8n.grafana.net/',
required: true,
},
];
}

View File

@@ -0,0 +1,50 @@
import {
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class GristApi implements ICredentialType {
name = 'gristApi';
displayName = 'Grist API';
documentationUrl = 'grist';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
default: '',
required: true,
},
{
displayName: 'Plan Type',
name: 'planType',
type: 'options',
default: 'free',
options: [
{
name: 'Free',
value: 'free',
},
{
name: 'Paid',
value: 'paid',
},
],
},
{
displayName: 'Custom Subdomain',
name: 'customSubdomain',
type: 'string',
default: '',
required: true,
description: 'Custom subdomain of your team',
displayOptions: {
show: {
planType: [
'paid',
],
},
},
},
];
}

View File

@@ -1,35 +1,35 @@
import {
ICredentialType,
NodePropertyTypes,
INodeProperties,
} from 'n8n-workflow';
export class HomeAssistantApi implements ICredentialType {
name = 'homeAssistantApi';
displayName = 'Home Assistant API';
documentationUrl = 'homeAssistant';
properties = [
properties: INodeProperties[] = [
{
displayName: 'Host',
name: 'host',
type: 'string' as NodePropertyTypes,
type: 'string',
default: '',
},
{
displayName: 'Port',
name: 'port',
type: 'number' as NodePropertyTypes,
type: 'number',
default: 8123,
},
{
displayName: 'SSL',
name: 'ssl',
type: 'boolean' as NodePropertyTypes,
type: 'boolean',
default: false,
},
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string' as NodePropertyTypes,
type: 'string',
default: '',
},
];

View File

@@ -1,23 +1,23 @@
import {
ICredentialType,
NodePropertyTypes,
INodeProperties,
} from 'n8n-workflow';
export class Magento2Api implements ICredentialType {
name = 'magento2Api';
displayName = 'Magento 2 API';
documentationUrl = 'magento2';
properties = [
properties: INodeProperties[] = [
{
displayName: 'Host',
name: 'host',
type: 'string' as NodePropertyTypes,
type: 'string',
default: '',
},
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string' as NodePropertyTypes,
type: 'string',
default: '',
},
];

View File

@@ -1,23 +1,23 @@
import {
ICredentialType,
NodePropertyTypes,
INodeProperties,
} from 'n8n-workflow';
export class MarketstackApi implements ICredentialType {
name = 'marketstackApi';
displayName = 'Marketstack API';
documentationUrl = 'marketstack';
properties = [
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string' as NodePropertyTypes,
type: 'string',
default: '',
},
{
displayName: 'Use HTTPS',
name: 'useHttps',
type: 'boolean' as NodePropertyTypes,
type: 'boolean',
default: false,
description: 'Use HTTPS (paid plans only).',
},

View File

@@ -0,0 +1,30 @@
import {
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class MicrosoftDynamicsOAuth2Api implements ICredentialType {
name = 'microsoftDynamicsOAuth2Api';
extends = [
'microsoftOAuth2Api',
];
displayName = 'Microsoft Dynamics OAuth2 API';
documentationUrl = 'microsoft';
properties: INodeProperties[] = [
//https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent
{
displayName: 'Subdomain',
name: 'subdomain',
type: 'string',
required: true,
placeholder: 'organization',
default: '',
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden',
default: '=openid offline_access https://{{$self.subdomain}}.crm.dynamics.com/.default',
},
];
}

View File

@@ -38,7 +38,7 @@ export class MongoDb implements ICredentialType {
default: '',
placeholder: 'mongodb://<USERNAME>:<PASSWORD>@localhost:27017/?authSource=admin&readPreference=primary&appname=n8n&ssl=false',
required: false,
description: `If provided, the value here will be used as a MongoDB connection string,<br />
description: `If provided, the value here will be used as a MongoDB connection string,
and the MongoDB credentials will be ignored`,
},
{

View File

@@ -0,0 +1,19 @@
import {
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class OneSimpleApi implements ICredentialType {
name = 'oneSimpleApi';
displayName = 'One Simple API';
documentationUrl = 'oneSimpleApi';
properties: INodeProperties[] = [
{
displayName: 'API Token',
name: 'apiToken',
type: 'string',
default: '',
},
];
}

View File

@@ -22,7 +22,7 @@ export class S3 implements ICredentialType {
default: 'us-east-1',
},
{
displayName: 'Access Key Id',
displayName: 'Access Key ID',
name: 'accessKeyId',
type: 'string',
default: '',

View File

@@ -48,7 +48,7 @@ export class SalesforceJwtApi implements ICredentialType {
},
default: '',
required: true,
description: 'Use the multiline editor. Make sure it is in standard PEM key format:<br />-----BEGIN PRIVATE KEY-----<br />KEY DATA GOES HERE<br />-----END PRIVATE KEY-----',
description: 'Use the multiline editor. Make sure it is in standard PEM key format:<br />-----BEGIN PRIVATE KEY-----<br />KEY DATA GOES HERE<br />-----END PRIVATE KEY-----',
},
];
}

View File

@@ -0,0 +1,48 @@
import {
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class SeaTableApi implements ICredentialType {
name = 'seaTableApi';
displayName = 'SeaTable API';
documentationUrl = 'seaTable';
properties: INodeProperties[] = [
{
displayName: 'Environment',
name: 'environment',
type: 'options',
default: 'cloudHosted',
options: [
{
name: 'Cloud-hosted',
value: 'cloudHosted',
},
{
name: 'Self-hosted',
value: 'selfHosted',
},
],
},
{
displayName: 'Self-hosted domain',
name: 'domain',
type: 'string',
default: '',
placeholder: 'https://www.mydomain.com',
displayOptions: {
show: {
environment: [
'selfHosted',
],
},
},
},
{
displayName: 'API Token (of a Base)',
name: 'token',
type: 'string',
default: '',
},
];
}

View File

@@ -1,6 +1,6 @@
import {
ICredentialType,
NodePropertyTypes,
INodeProperties,
} from 'n8n-workflow';
export class ServiceNowOAuth2Api implements ICredentialType {
@@ -10,11 +10,11 @@ export class ServiceNowOAuth2Api implements ICredentialType {
];
displayName = 'ServiceNow OAuth2 API';
documentationUrl = 'serviceNow';
properties = [
properties: INodeProperties[] = [
{
displayName: 'Subdomain',
name: 'subdomain',
type: 'string' as NodePropertyTypes,
type: 'string',
default: '',
placeholder: 'n8n',
description: 'The subdomain of your ServiceNow environment',
@@ -23,39 +23,39 @@ export class ServiceNowOAuth2Api implements ICredentialType {
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden' as NodePropertyTypes,
type: 'hidden',
default: '=https://{{$self["subdomain"]}}.service-now.com/oauth_auth.do',
required: true,
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden' as NodePropertyTypes,
type: 'hidden',
default: '=https://{{$self["subdomain"]}}.service-now.com/oauth_token.do',
required: true,
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden' as NodePropertyTypes,
type: 'hidden',
default: 'useraccount',
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden' as NodePropertyTypes,
type: 'hidden',
default: 'response_type=code',
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden' as NodePropertyTypes,
type: 'hidden',
default: 'grant_type=authorization_code',
},
{
displayName: 'Authentication',
name: 'authentication',
type: 'hidden' as NodePropertyTypes,
type: 'hidden',
default: 'header',
},
];

View File

@@ -15,6 +15,8 @@ const userScopes = [
'reactions:write',
'stars:read',
'stars:write',
'usergroups:write',
'usergroups:read',
'users.profile:read',
'users.profile:write',
];

View File

@@ -63,8 +63,7 @@ export class Snowflake implements ICredentialType {
name: 'clientSessionKeepAlive',
type: 'boolean',
default: false,
description: `By default, client connections typically time out approximately 3-4 hours after the most recent query was executed.<br>
If the parameter clientSessionKeepAlive is set to true, the clients connection to the server will be kept alive indefinitely, even if no queries are executed.`,
description: `By default, client connections typically time out approximately 3-4 hours after the most recent query was executed. If the parameter clientSessionKeepAlive is set to true, the clients connection to the server will be kept alive indefinitely, even if no queries are executed.`,
},
];
}

View File

@@ -0,0 +1,32 @@
import {
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class SplunkApi implements ICredentialType {
name = 'splunkApi';
displayName = 'Splunk API';
documentationUrl = 'splunk';
properties: INodeProperties[] = [
{
displayName: 'Auth Token',
name: 'authToken',
type: 'string',
default: '',
},
{
displayName: 'Base URL',
name: 'baseUrl',
type: 'string',
description: 'Protocol, domain and port',
placeholder: 'e.g. https://localhost:8089',
default: '',
},
{
displayName: 'Allow Self-Signed Certificates',
name: 'allowUnauthorizedCerts',
type: 'boolean',
default: false,
},
];
}

View File

@@ -13,13 +13,15 @@ export class SurveyMonkeyApi implements ICredentialType {
name: 'accessToken',
type: 'string',
default: '',
description: `The access token must have the following scopes:</br />
- Create/modify webhooks</br />
- View webhooks</br />
- View surveys</br />
- View collectors</br />
- View responses<br />
- View response details`,
description: `The access token must have the following scopes:
<ul>
<li>Create/modify webhooks</li>
<li>View webhooks</li>
<li>View surveys</li>
<li>View collectors</li>
<li>View responses</li>
<li>View response details</li>
</ul>`,
},
{
displayName: 'Client ID',

View File

@@ -0,0 +1,19 @@
import {
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class UrlScanIoApi implements ICredentialType {
name = 'urlScanIoApi';
displayName = 'urlscan.io API';
documentationUrl = 'urlScanIo';
properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
default: '',
required: true,
},
];
}

View File

@@ -32,9 +32,7 @@ export class WooCommerceApi implements ICredentialType {
name: 'includeCredentialsInQuery',
type: 'boolean',
default: false,
description: `Occasionally, some servers may not parse the Authorization header correctly</br>
(if you see a “Consumer key is missing” error when authenticating over SSL, you have a server issue).</br>
In this case, you may provide the consumer key/secret as query string parameters instead.`,
description: `Occasionally, some servers may not parse the Authorization header correctly (if you see a “Consumer key is missing” error when authenticating over SSL, you have a server issue). In this case, you may provide the consumer key/secret as query string parameters instead.`,
},
];
}

View File

@@ -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/"
}
]
}
}

View File

@@ -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.',
},
],
};

View File

@@ -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.`,
},
],
},

View File

@@ -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') {

View File

@@ -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[];

View File

@@ -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[];

View File

@@ -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 */
/* -------------------------------------------------------------------------- */

View 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;
}

View File

@@ -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;
}

View File

@@ -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)];
}
}
}

View File

@@ -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',

View File

@@ -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',

View File

@@ -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.',
},
// ----------------------------------

View File

@@ -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',

View File

@@ -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() });

View File

@@ -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 {

View File

@@ -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 },

View File

@@ -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',

View File

@@ -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() });

View File

@@ -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()});

View File

@@ -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 */

View File

@@ -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()});

View File

@@ -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 = {

View 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/"
}
]
}
}

View 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)];
}
}

View 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);
});
});
}

View 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

View File

@@ -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, {

View File

@@ -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;
}

View File

@@ -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',

View File

@@ -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;

View File

@@ -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',

View File

@@ -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',

View File

@@ -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',

View File

@@ -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',

View File

@@ -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',

View File

@@ -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`,
},
],
},

View File

@@ -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',

View File

@@ -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',

View File

@@ -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 persons name, location and Twitter handle.`,
},
],

View File

@@ -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.`,
},
{

View File

@@ -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 &lt;column_id_or_name&gt;:&lt;value&gt;. 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`,
},
{

View File

@@ -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 &lt;column_id_or_name&gt;:&lt;value&gt;. 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.`,
},
],
},

View File

@@ -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',

View File

@@ -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',

View File

@@ -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',

View File

@@ -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',

View File

@@ -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);
}

View File

@@ -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',

View 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)];
}
}

View 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);
}

View 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

View File

@@ -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 }));
// }

View File

@@ -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.',
},
],
},

View File

@@ -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',

View 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/"
}
]
}
}

View 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)];
}
}

View 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);
}

View 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

View File

@@ -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);
}

View File

@@ -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',

View File

@@ -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/"
}
]
}
}

View File

@@ -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,

View File

@@ -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',
},

View File

@@ -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) {

Some files were not shown because too many files have changed in this diff Show More