Merge branch 'master' into feature/zendesk-trigger

This commit is contained in:
Ricardo Espinoza 2020-01-06 19:31:37 -05:00
commit a69a32e1b4
30 changed files with 886 additions and 49 deletions

View file

@ -17,7 +17,7 @@ received or lost a star.
## Available integrations
n8n has 50+ different nodes to automate workflows. The list can be found on: [https://n8n.io/nodes](https://n8n.io/nodes)
n8n has 80+ different nodes to automate workflows. The list can be found on: [https://n8n.io/nodes](https://n8n.io/nodes)
## Documentation

View file

@ -5,7 +5,7 @@ ARG N8N_VERSION
RUN if [ -z "$N8N_VERSION" ] ; then echo "The N8N_VERSION argument is missing!" ; exit 1; fi
# Update everything and install needed dependencies
RUN apk add --update graphicsmagick tzdata
RUN apk add --update graphicsmagick tzdata git
# # Set a custom user to not have n8n run as root
USER root

View file

@ -2,9 +2,8 @@
![n8n.io - Workflow Automation](https://raw.githubusercontent.com/n8n-io/n8n/master/docs/images/n8n-logo.png)
n8n is a free node-based "Open Source" (with Commons Clause)
Workflow Automation Tool. It can be self-hosted, easily extended, and
so also used with internal tools.
n8n is a free and open node based Workflow Automation Tool. It can be
self-hosted, easily extended, and so also used with internal tools.
<a href="https://raw.githubusercontent.com/n8n-io/n8n/master/docs/images/n8n-screenshot.png"><img src="https://raw.githubusercontent.com/n8n-io/n8n/master/docs/images/n8n-screenshot.png" width="550" alt="n8n.io - Screenshot"></a>
@ -33,7 +32,7 @@ Slack notification every time a Github repository received or lost a star.
## Available integrations
n8n has 50+ different nodes to automate workflows. The list can be found on: [https://n8n.io/nodes](https://n8n.io/nodes)
n8n has 80+ different nodes to automate workflows. The list can be found on: [https://n8n.io/nodes](https://n8n.io/nodes)
## Documentation

View file

@ -1,6 +1,6 @@
{
"name": "n8n",
"version": "0.43.0",
"version": "0.44.0",
"description": "n8n Workflow Automation Tool",
"license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io",
@ -92,10 +92,10 @@
"localtunnel": "^2.0.0",
"lodash.get": "^4.4.2",
"mongodb": "^3.2.3",
"n8n-core": "~0.19.0",
"n8n-editor-ui": "~0.30.0",
"n8n-nodes-base": "~0.38.0",
"n8n-workflow": "~0.19.0",
"n8n-core": "~0.20.0",
"n8n-editor-ui": "~0.31.0",
"n8n-nodes-base": "~0.39.0",
"n8n-workflow": "~0.20.0",
"open": "^7.0.0",
"pg": "^7.11.0",
"request-promise-native": "^1.0.7",

View file

@ -1,6 +1,6 @@
{
"name": "n8n-core",
"version": "0.19.0",
"version": "0.20.0",
"description": "Core functionality of n8n",
"license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io",
@ -43,7 +43,7 @@
"crypto-js": "^3.1.9-1",
"lodash.get": "^4.4.2",
"mmmagic": "^0.5.2",
"n8n-workflow": "~0.19.0",
"n8n-workflow": "~0.20.0",
"request-promise-native": "^1.0.7"
},
"jest": {

View file

@ -1,6 +1,6 @@
{
"name": "n8n-editor-ui",
"version": "0.30.2",
"version": "0.31.0",
"description": "Workflow Editor UI for n8n",
"license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io",
@ -63,7 +63,7 @@
"lodash.debounce": "^4.0.8",
"lodash.get": "^4.4.2",
"lodash.set": "^4.3.2",
"n8n-workflow": "~0.19.0",
"n8n-workflow": "~0.20.0",
"node-sass": "^4.12.0",
"prismjs": "^1.17.1",
"quill": "^2.0.0-dev.3",

View file

@ -3,7 +3,7 @@
<div class="header-side-menu">
<span v-if="node">
<display-with-change :key-name="'name'" @valueChanged="valueChanged"></display-with-change>
<a :href="'http://n8n.io/nodes/' + nodeType.name" target="_blank" class="node-info">
<a v-if="nodeType" :href="'http://n8n.io/nodes/' + nodeType.name" target="_blank" class="node-info">
<el-tooltip class="clickable" placement="top" effect="light">
<div slot="content" v-html="'<strong>Node Description:</strong><br />' + nodeTypeDescription + '<br /><br /><strong>For more information and usage examples click!</strong>'"></div>
<font-awesome-icon icon="question-circle" />
@ -89,8 +89,8 @@ export default mixins(
return null;
},
nodeTypeDescription (): string {
if (this.nodeType!.description) {
return this.nodeType!.description;
if (this.nodeType && this.nodeType.description) {
return this.nodeType.description;
} else {
return 'No description found';
}

View file

@ -116,6 +116,7 @@
<script lang="ts">
import Vue from 'vue';
import { get } from 'lodash';
import {
INodeUi,
@ -206,11 +207,34 @@ export default mixins(
};
},
watch: {
dependentParametersValues () {
// Reload the remote parameters whenever a parameter
// on which the current field depends on changes
this.loadRemoteParameterOptions();
},
value () {
this.tempValue = this.displayValue as string;
},
},
computed: {
dependentParametersValues (): string | null {
const loadOptionsDependsOn = this.getArgument('loadOptionsDependsOn') as string[] | undefined;
if (loadOptionsDependsOn === undefined) {
return null;
}
// Get the resolved parameter values of the current node
const currentNodeParameters = this.$store.getters.activeNode.parameters;
const resolvedNodeParameters = this.getResolveNodeParameters(currentNodeParameters);
const returnValues: string[] = [];
for (const parameterPath of loadOptionsDependsOn) {
returnValues.push(get(resolvedNodeParameters, parameterPath) as string);
}
return returnValues.join('|');
},
node (): INodeUi | null {
if (this.isCredential === true) {
return null;

View file

@ -124,7 +124,7 @@ export default mixins(
getArgument (
argumentName: string,
parameter: INodeProperties,
): string | number | boolean | undefined {
): string | string[] | number | boolean | undefined{
if (parameter.typeOptions === undefined) {
return undefined;
}

View file

@ -79,8 +79,8 @@ export const nodeBase = mixins(nodeIndex).extend({
maxConnections: -1,
endpoint: 'Rectangle',
endpointStyle: {
width: nodeTypeData.outputs.length > 2 ? 9 : 10,
height: nodeTypeData.outputs.length > 2 ? 18 : 24,
width: nodeTypeData && nodeTypeData.outputs.length > 2 ? 9 : 10,
height: nodeTypeData && nodeTypeData.outputs.length > 2 ? 18 : 24,
fill: '#777',
stroke: '#777',
lineWidth: 0,
@ -92,7 +92,7 @@ export const nodeBase = mixins(nodeIndex).extend({
maxConnections: -1,
endpoint: 'Dot',
endpointStyle: {
radius: nodeTypeData.outputs.length > 2 ? 7 : 11,
radius: nodeTypeData && nodeTypeData.outputs.length > 2 ? 7 : 11,
fill: '#555',
outlineStroke: 'none',
},

View file

@ -0,0 +1,23 @@
import {
ICredentialType,
NodePropertyTypes,
} from 'n8n-workflow';
export class BitbucketApi implements ICredentialType {
name = 'bitbucketApi';
displayName = 'Bitbucket API';
properties = [
{
displayName: 'Username',
name: 'username',
type: 'string' as NodePropertyTypes,
default: '',
},
{
displayName: 'App Password',
name: 'appPassword',
type: 'string' as NodePropertyTypes,
default: '',
},
];
}

View file

@ -0,0 +1,17 @@
import {
ICredentialType,
NodePropertyTypes,
} from 'n8n-workflow';
export class EventbriteApi implements ICredentialType {
name = 'eventbriteApi';
displayName = 'Eventbrite API';
properties = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string' as NodePropertyTypes,
default: '',
},
];
}

View file

@ -0,0 +1,358 @@
import {
IHookFunctions,
IWebhookFunctions,
} from 'n8n-core';
import {
IDataObject,
ILoadOptionsFunctions,
INodeType,
INodeTypeDescription,
INodePropertyOptions,
IWebhookResponseData,
} from 'n8n-workflow';
import {
bitbucketApiRequest,
bitbucketApiRequestAllItems,
} from './GenericFunctions';
export class BitbucketTrigger implements INodeType {
description: INodeTypeDescription = {
displayName: 'Bitbucket Trigger',
name: 'bitbucket',
icon: 'file:bitbucket.png',
group: ['trigger'],
version: 1,
description: 'Handle Bitbucket events via webhooks',
defaults: {
name: 'Bitbucket Trigger',
color: '#0052cc',
},
inputs: [],
outputs: ['main'],
credentials: [
{
name: 'bitbucketApi',
required: true,
}
],
webhooks: [
{
name: 'default',
httpMethod: 'POST',
responseMode: 'onReceived',
path: 'webhook',
},
],
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
required: true,
options: [
{
name: 'User',
value: 'user',
},
{
name: 'Team',
value: 'team',
},
{
name: 'Repository',
value: 'repository',
},
],
default: 'user',
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',
type: 'options',
displayOptions: {
show: {
resource: [
'team'
]
}
},
typeOptions: {
loadOptionsMethod: 'getTeams',
},
required: true,
default: '',
description: 'The team of which to listen to the events.',
},
{
displayName: 'Events',
name: 'events',
type: 'multiOptions',
displayOptions: {
show: {
resource: [
'team'
]
}
},
typeOptions: {
loadOptionsMethod: 'getTeamEvents',
},
options: [],
required: true,
default: [],
description: 'The events to listen to.',
},
{
displayName: 'Repository',
name: 'repository',
type: 'options',
displayOptions: {
show: {
resource: [
'repository'
]
}
},
typeOptions: {
loadOptionsMethod: 'getRepositories',
},
required: true,
default: '',
description: 'The repository of which to listen to the events.',
},
{
displayName: 'Events',
name: 'events',
type: 'multiOptions',
displayOptions: {
show: {
resource: [
'repository'
]
}
},
typeOptions: {
loadOptionsMethod: 'getRepositoriesEvents',
},
options: [],
required: true,
default: [],
description: 'The events to listen to.',
},
],
};
methods = {
loadOptions: {
// Get all the events to display them to user so that he can
// select them easily
async getUsersEvents(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
const events = await bitbucketApiRequestAllItems.call(this, 'values', 'GET', '/hook_events/user');
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 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,
});
}
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 = this.getCredentials('bitbucketApi');
const returnData: INodePropertyOptions[] = [];
const repositories = await bitbucketApiRequestAllItems.call(this, 'values', 'GET', `/repositories/${credentials!.username}`);
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,
});
}
return returnData;
},
// Get all the teams to display them to user so that he can
// select them easily
async getTeams(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;
returnData.push({
name: teamName,
value: teamId,
});
}
return returnData;
},
},
};
// @ts-ignore
webhookMethods = {
default: {
async checkExists(this: IHookFunctions): Promise<boolean> {
let endpoint = '';
const credentials = this.getCredentials('bitbucketApi');
const resource = this.getNodeParameter('resource', 0) as string;
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 === 'repository') {
const repository = this.getNodeParameter('repository', 0) as string;
endpoint = `/repositories/${credentials!.username}/${repository}/hooks/${webhookData.webhookId}`;
}
try {
await bitbucketApiRequest.call(this, 'GET', endpoint);
} catch (e) {
return false;
}
return true;
},
async create(this: IHookFunctions): Promise<boolean> {
let responseData;
let endpoint = '';
const webhookUrl = this.getNodeWebhookUrl('default');
const webhookData = this.getWorkflowStaticData('node');
const events = this.getNodeParameter('events') as string[];
const resource = this.getNodeParameter('resource', 0) as string;
const credentials = this.getCredentials('bitbucketApi');
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 === 'repository') {
const repository = this.getNodeParameter('repository', 0) as string;
endpoint = `/repositories/${credentials!.username}/${repository}/hooks`;
}
const body: IDataObject = {
description: 'n8n webhook',
url: webhookUrl,
active: true,
events,
};
responseData = await bitbucketApiRequest.call(this, 'POST', endpoint, body);
webhookData.webhookId = responseData.uuid.replace('{', '').replace('}', '');
return true;
},
async delete(this: IHookFunctions): Promise<boolean> {
let endpoint = '';
const webhookData = this.getWorkflowStaticData('node');
const credentials = this.getCredentials('bitbucketApi');
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 === 'repository') {
const repository = this.getNodeParameter('repository', 0) as string;
endpoint = `/repositories/${credentials!.username}/${repository}/hooks/${webhookData.webhookId}`;
}
try {
await bitbucketApiRequest.call(this, 'DELETE', endpoint);
} catch(error) {
return false;
}
delete webhookData.webhookId;
return true;
},
},
};
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
const req = this.getRequestObject();
const headerData = this.getHeaderData() as IDataObject;
const webhookData = this.getWorkflowStaticData('node');
if (headerData['x-hook-uuid'] !== webhookData.webhookId) {
return {};
}
return {
workflowData: [
this.helpers.returnJsonArray(req.body)
],
};
}
}

View file

@ -0,0 +1,59 @@
import { OptionsWithUri } from 'request';
import {
IExecuteFunctions,
IExecuteSingleFunctions,
IHookFunctions,
ILoadOptionsFunctions,
} from 'n8n-core';
import { IDataObject } from 'n8n-workflow';
export async function bitbucketApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
const credentials = this.getCredentials('bitbucketApi');
if (credentials === undefined) {
throw new Error('No credentials got returned!');
}
let options: OptionsWithUri = {
method,
auth: {
user: credentials.username as string,
password: credentials.appPassword as string,
},
qs,
body,
uri: uri ||`https://api.bitbucket.org/2.0${resource}`,
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 (err) {
throw new Error('Bitbucket Error: ' + err.message);
}
}
/**
* Make an API request to paginated flow endpoint
* and return all results
*/
export async function bitbucketApiRequestAllItems(this: IHookFunctions | IExecuteFunctions| ILoadOptionsFunctions, propertyName: string, method: string, resource: string, body: any = {}, query: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
const returnData: IDataObject[] = [];
let responseData;
let uri: string | undefined;
do {
responseData = await bitbucketApiRequest.call(this, method, resource, body, query, uri);
uri = responseData.next;
returnData.push.apply(returnData, responseData[propertyName]);
} while (
responseData.next !== undefined
);
return returnData;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

View file

@ -70,10 +70,13 @@ export const tableFields = [
name: 'tableId',
type: 'options',
typeOptions: {
loadOptionsDependsOn: [
'docId',
],
loadOptionsMethod: 'getTables',
},
required: true,
default: [],
default: '',
displayOptions: {
show: {
resource: [
@ -150,10 +153,13 @@ export const tableFields = [
name: 'tableId',
type: 'options',
typeOptions: {
loadOptionsDependsOn: [
'docId',
],
loadOptionsMethod: 'getTables',
},
required: true,
default: [],
default: '',
displayOptions: {
show: {
resource: [
@ -223,7 +229,7 @@ export const tableFields = [
displayName: 'ValueFormat',
name: 'valueFormat',
type: 'options',
default: [],
default: '',
options: [
{
name: 'Simple',
@ -271,10 +277,13 @@ export const tableFields = [
name: 'tableId',
type: 'options',
typeOptions: {
loadOptionsDependsOn: [
'docId',
],
loadOptionsMethod: 'getTables',
},
required: true,
default: [],
default: '',
displayOptions: {
show: {
resource: [
@ -358,7 +367,7 @@ export const tableFields = [
displayName: 'ValueFormat',
name: 'valueFormat',
type: 'options',
default: [],
default: '',
options: [
{
name: 'Simple',
@ -386,7 +395,7 @@ export const tableFields = [
displayName: 'Sort By',
name: 'sortBy',
type: 'options',
default: [],
default: '',
options: [
{
name: 'Created At',
@ -438,10 +447,13 @@ export const tableFields = [
name: 'tableId',
type: 'options',
typeOptions: {
loadOptionsDependsOn: [
'docId',
],
loadOptionsMethod: 'getTables',
},
required: true,
default: [],
default: '',
displayOptions: {
show: {
resource: [

View file

@ -0,0 +1,277 @@
import {
IHookFunctions,
IWebhookFunctions,
} from 'n8n-core';
import {
IDataObject,
ILoadOptionsFunctions,
INodePropertyOptions,
INodeTypeDescription,
INodeType,
IWebhookResponseData,
} from 'n8n-workflow';
import {
eventbriteApiRequest,
eventbriteApiRequestAllItems,
} from './GenericFunctions';
export class EventbriteTrigger implements INodeType {
description: INodeTypeDescription = {
displayName: 'Eventbrite Trigger',
name: 'eventbrite',
icon: 'file:eventbrite.png',
group: ['trigger'],
version: 1,
description: 'Handle Eventbrite events via webhooks',
defaults: {
name: 'Eventbrite Trigger',
color: '#dc5237',
},
inputs: [],
outputs: ['main'],
credentials: [
{
name: 'eventbriteApi',
required: true,
}
],
webhooks: [
{
name: 'default',
httpMethod: 'POST',
responseMode: 'onReceived',
path: 'webhook',
},
],
properties: [
{
displayName: 'Organization',
name: 'organization',
type: 'options',
required: true,
typeOptions: {
loadOptionsMethod: 'getOrganizations'
},
default: '',
description: '',
},
{
displayName: 'Event',
name: 'event',
type: 'options',
required: true,
typeOptions: {
loadOptionsDependsOn: [
'organization',
],
loadOptionsMethod: 'getEvents'
},
default: '',
description: '',
},
{
displayName: 'Actions',
name: 'actions',
type: 'multiOptions',
options: [
{
name: 'attendee.updated',
value: 'attendee.updated'
},
{
name: 'attendee.checked_in',
value: 'attendee.checked_in'
},
{
name: 'attendee.checked_out',
value: 'attendee.checked_out'
},
{
name: 'event.created',
value: 'event.created'
},
{
name: 'event.published',
value: 'event.published'
},
{
name: 'event.unpublished',
value: 'event.unpublished'
},
{
name: 'event.updated',
value: 'event.updated'
},
{
name: 'order.placed',
value: 'order.placed'
},
{
name: 'order.refunded',
value: 'order.refunded'
},
{
name: 'order.updated',
value: 'order.updated'
},
{
name: 'organizer.updated',
value: 'organizer.updated'
},
{
name: 'ticket_class.created',
value: 'ticket_class.created'
},
{
name: 'ticket_class.deleted',
value: 'ticket_class.deleted'
},
{
name: 'ticket_class.updated',
value: 'ticket_class.updated'
},
{
name: 'venue.updated',
value: 'venue.updated'
},
],
required: true,
default: [],
description: '',
},
{
displayName: 'Resolve Data',
name: 'resolveData',
type: 'boolean',
default: true,
description: 'By default does the webhook-data only contain the URL to receive<br />the object data manually. If this option gets activated it<br />will resolve the data automatically.',
},
],
};
methods = {
loadOptions: {
// Get all the available organizations to display them to user so that he can
// select them easily
async getOrganizations(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
const organizations = await eventbriteApiRequestAllItems.call(this, 'organizations', 'GET', '/users/me/organizations');
for (const organization of organizations) {
const organizationName = organization.name;
const organizationId = organization.id;
returnData.push({
name: organizationName,
value: organizationId,
});
}
return returnData;
},
// Get all the available events to display them to user so that he can
// select them easily
async getEvents(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
const organization = this.getCurrentNodeParameter('organization');
const events = await eventbriteApiRequestAllItems.call(this, 'events', 'GET', `/organizations/${organization}/events`);
for (const event of events) {
const eventName = event.name.text;
const eventId = event.id;
returnData.push({
name: eventName,
value: eventId,
});
}
return returnData;
},
},
};
// @ts-ignore
webhookMethods = {
default: {
async checkExists(this: IHookFunctions): Promise<boolean> {
const webhookData = this.getWorkflowStaticData('node');
if (webhookData.webhookId === undefined) {
return false;
}
const endpoint = `/webhooks/${webhookData.webhookId}/`;
try {
await eventbriteApiRequest.call(this, 'GET', endpoint);
} catch (e) {
return false;
}
return true;
},
async create(this: IHookFunctions): Promise<boolean> {
const webhookUrl = this.getNodeWebhookUrl('default');
const webhookData = this.getWorkflowStaticData('node');
const event = this.getNodeParameter('event') as string;
const actions = this.getNodeParameter('actions') as string[];
const endpoint = `/webhooks/`;
const body: IDataObject = {
endpoint_url: webhookUrl,
actions: actions.join(','),
event_id: event,
};
const responseData = await eventbriteApiRequest.call(this, 'POST', endpoint, body);
webhookData.webhookId = responseData.id;
return true;
},
async delete(this: IHookFunctions): Promise<boolean> {
let responseData;
const webhookData = this.getWorkflowStaticData('node');
const endpoint = `/webhooks/${webhookData.webhookId}/`;
try {
responseData = await eventbriteApiRequest.call(this, 'DELETE', endpoint);
} catch(error) {
return false;
}
if (!responseData.success) {
return false;
}
delete webhookData.webhookId;
return true;
},
},
};
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
const req = this.getRequestObject();
if (req.body.api_url === undefined) {
throw new Error('The received data does not contain required "api_url" property!');
}
const resolveData = this.getNodeParameter('resolveData', false) as boolean;
if (resolveData === false) {
// Return the data as it got received
return {
workflowData: [
this.helpers.returnJsonArray(req.body),
],
};
}
if (req.body.api_url.includes('api-endpoint-to-fetch-object-details')) {
return {
workflowData: [
this.helpers.returnJsonArray({
placeholder: 'Test received. To display actual data of object get the webhook triggered by performing the action which triggers it.',
})
],
};
}
const responseData = await eventbriteApiRequest.call(this, 'GET', '', {}, undefined, req.body.api_url);
return {
workflowData: [
this.helpers.returnJsonArray(responseData),
],
};
}
}

View file

@ -0,0 +1,63 @@
import { OptionsWithUri } from 'request';
import {
IExecuteFunctions,
IExecuteSingleFunctions,
IHookFunctions,
ILoadOptionsFunctions,
IWebhookFunctions,
} from 'n8n-core';
import { IDataObject } from 'n8n-workflow';
export async function eventbriteApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IWebhookFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
const credentials = this.getCredentials('eventbriteApi');
if (credentials === undefined) {
throw new Error('No credentials got returned!');
}
let options: OptionsWithUri = {
headers: { 'Authorization': `Bearer ${credentials.apiKey}`},
method,
qs,
body,
uri: uri ||`https://www.eventbriteapi.com/v3${resource}`,
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) {
let errorMessage = error.message;
if (error.response.body && error.response.body.error_description) {
errorMessage = error.response.body.error_description;
}
throw new Error('Eventbrite Error: ' + errorMessage);
}
}
/**
* Make an API request to paginated flow endpoint
* and return all results
*/
export async function eventbriteApiRequestAllItems(this: IHookFunctions | IExecuteFunctions| ILoadOptionsFunctions, propertyName: string, method: string, resource: string, body: any = {}, query: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
const returnData: IDataObject[] = [];
let responseData;
do {
responseData = await eventbriteApiRequest.call(this, method, resource, body, query);
query.continuation = responseData.pagination.continuation;
returnData.push.apply(returnData, responseData[propertyName]);
} while (
responseData.pagination !== undefined &&
responseData.pagination.has_more_items !== undefined &&
responseData.pagination.has_more_items !== false
);
return returnData;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -66,7 +66,7 @@ export class FlowTrigger implements INodeType {
name: 'listIds',
type: 'string',
required: true,
default: [],
default: '',
displayOptions: {
show: {
resource:[
@ -86,7 +86,7 @@ export class FlowTrigger implements INodeType {
name: 'taskIds',
type: 'string',
required: true,
default: [],
default: '',
displayOptions: {
show: {
resource:[

View file

@ -107,7 +107,7 @@ export const taskFields = [
displayName: 'List ID',
name: 'listID',
type: 'string',
default: [],
default: '',
required : false,
description: 'Put the new task in a list ("project"). Omit this param to have the task be private.',
},

View file

@ -776,7 +776,7 @@ export class GoogleDrive implements INodeType {
},
],
required: true,
default: [],
default: '',
description: 'The corpora to operate on.',
},
{

View file

@ -135,7 +135,7 @@ export const dealFields = [
typeOptions: {
loadOptionsMethod: 'getDealTypes',
},
default: [],
default: '',
},
{
displayName: 'Associated Company',
@ -209,7 +209,7 @@ export const dealFields = [
typeOptions: {
loadOptionsMethod: 'getDealStages'
},
default: [],
default: '',
description: 'The dealstage is required when creating a deal. See the CRM Pipelines API for details on managing pipelines and stages.',
},
{
@ -243,7 +243,7 @@ export const dealFields = [
typeOptions: {
loadOptionsMethod: 'getDealTypes',
},
default: [],
default: '',
},
]
},

View file

@ -160,7 +160,7 @@ export const issueFields = [
typeOptions: {
loadOptionsMethod: 'getPriorities',
},
default: [],
default: '',
required : false,
description: 'Priority',
},
@ -171,7 +171,7 @@ export const issueFields = [
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: [],
default: '',
required : false,
description: 'Assignee',
},
@ -278,7 +278,7 @@ export const issueFields = [
typeOptions: {
loadOptionsMethod: 'getPriorities',
},
default: [],
default: '',
required : false,
description: 'Priority',
},
@ -289,7 +289,7 @@ export const issueFields = [
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: [],
default: '',
required : false,
description: 'Assignee',
},

View file

@ -55,7 +55,7 @@ export class MailchimpTrigger implements INodeType {
name: 'list',
type: 'options',
required: true,
default: [],
default: '',
description: 'The list that is gonna fire the event.',
typeOptions: {
loadOptionsMethod: 'getLists'

View file

@ -104,7 +104,7 @@ export class Todoist implements INodeType {
]
},
},
default: [],
default: '',
description: 'The project you want to add the task to.',
},
{
@ -145,7 +145,7 @@ export class Todoist implements INodeType {
]
},
},
default: [],
default: '',
required: true,
description: 'Task content',
},

View file

@ -11,7 +11,7 @@ import { togglApiRequest } from './GenericFunctions';
export class TogglTrigger implements INodeType {
description: INodeTypeDescription = {
displayName: 'Toggl',
displayName: 'Toggl Trigger',
name: 'toggl',
icon: 'file:toggl.png',
group: ['trigger'],

View file

@ -1,6 +1,6 @@
{
"name": "n8n-nodes-base",
"version": "0.38.0",
"version": "0.39.0",
"description": "Base nodes of n8n",
"license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io",
@ -31,9 +31,11 @@
"dist/credentials/Amqp.credentials.js",
"dist/credentials/AsanaApi.credentials.js",
"dist/credentials/Aws.credentials.js",
"dist/credentials/BitbucketApi.credentials.js",
"dist/credentials/ChargebeeApi.credentials.js",
"dist/credentials/CodaApi.credentials.js",
"dist/credentials/DropboxApi.credentials.js",
"dist/credentials/EventbriteApi.credentials.js",
"dist/credentials/FreshdeskApi.credentials.js",
"dist/credentials/FileMaker.credentials.js",
"dist/credentials/FlowApi.credentials.js",
@ -87,6 +89,7 @@
"dist/nodes/Asana/AsanaTrigger.node.js",
"dist/nodes/Aws/AwsLambda.node.js",
"dist/nodes/Aws/AwsSns.node.js",
"dist/nodes/Bitbucket/BitbucketTrigger.node.js",
"dist/nodes/Chargebee/Chargebee.node.js",
"dist/nodes/Chargebee/ChargebeeTrigger.node.js",
"dist/nodes/Cron.node.js",
@ -99,6 +102,7 @@
"dist/nodes/ErrorTrigger.node.js",
"dist/nodes/ExecuteCommand.node.js",
"dist/nodes/ExecuteWorkflow.node.js",
"dist/nodes/Eventbrite/EventbriteTrigger.node.js",
"dist/nodes/FileMaker/FileMaker.node.js",
"dist/nodes/Freshdesk/Freshdesk.node.js",
"dist/nodes/Flow/Flow.node.js",
@ -189,7 +193,7 @@
"@types/xml2js": "^0.4.3",
"gulp": "^4.0.0",
"jest": "^24.9.0",
"n8n-workflow": "~0.19.0",
"n8n-workflow": "~0.20.0",
"ts-jest": "^24.0.2",
"tslint": "^5.17.0",
"typescript": "~3.7.4"
@ -209,7 +213,7 @@
"lodash.unset": "^4.5.2",
"mongodb": "^3.3.2",
"mysql2": "^2.0.1",
"n8n-core": "~0.19.0",
"n8n-core": "~0.20.0",
"nodemailer": "^5.1.1",
"pdf-parse": "^1.1.1",
"pg-promise": "^9.0.3",

View file

@ -1,6 +1,6 @@
{
"name": "n8n-workflow",
"version": "0.19.0",
"version": "0.20.0",
"description": "Workflow base code of n8n",
"license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io",

View file

@ -332,6 +332,7 @@ export type EditorTypes = 'code';
export interface INodePropertyTypeOptions {
alwaysOpenEditWindow?: boolean; // Supported by: string
editor?: EditorTypes; // Supported by: string
loadOptionsDependsOn?: string[]; // Supported by: options
loadOptionsMethod?: string; // Supported by: options
maxValue?: number; // Supported by: number
minValue?: number; // Supported by: number
@ -341,7 +342,7 @@ export interface INodePropertyTypeOptions {
numberStepSize?: number; // Supported by: number
password?: boolean; // Supported by: string
rows?: number; // Supported by: string
[key: string]: boolean | number | string | EditorTypes | undefined;
[key: string]: boolean | number | string | EditorTypes | undefined | string[];
}
export interface IDisplayOptions {