🔀 Merge branch 'master' into Fix-AMQP-Nodes

This commit is contained in:
Jan Oberhauser 2020-08-06 12:58:40 +02:00
commit 3e67617130
36 changed files with 2147 additions and 46 deletions

View file

@ -1,6 +1,6 @@
{
"name": "n8n",
"version": "0.75.0",
"version": "0.76.0",
"description": "n8n Workflow Automation Tool",
"license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io",
@ -100,10 +100,10 @@
"lodash.get": "^4.4.2",
"mongodb": "^3.5.5",
"mysql2": "^2.0.1",
"n8n-core": "~0.40.0",
"n8n-editor-ui": "~0.51.0",
"n8n-nodes-base": "~0.70.0",
"n8n-workflow": "~0.36.0",
"n8n-core": "~0.41.0",
"n8n-editor-ui": "~0.52.0",
"n8n-nodes-base": "~0.71.0",
"n8n-workflow": "~0.37.0",
"oauth-1.0a": "^2.2.6",
"open": "^7.0.0",
"pg": "^8.3.0",

View file

@ -1162,6 +1162,13 @@ class App {
const authQueryParameters = _.get(oauthCredentials, 'authQueryParameters', '') as string;
let returnUri = oAuthObj.code.getUri();
// if scope uses comma, change it as the library always return then with spaces
if ((_.get(oauthCredentials, 'scope') as string).includes(',')) {
const data = querystring.parse(returnUri.split('?')[1] as string);
data.scope = _.get(oauthCredentials, 'scope') as string;
returnUri = `${_.get(oauthCredentials, 'authUrl', '')}?${querystring.stringify(data)}`;
}
if (authQueryParameters) {
returnUri += '&' + authQueryParameters;
}

View file

@ -1,6 +1,6 @@
{
"name": "n8n-core",
"version": "0.40.0",
"version": "0.41.0",
"description": "Core functionality of n8n",
"license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io",
@ -46,7 +46,7 @@
"file-type": "^14.6.2",
"lodash.get": "^4.4.2",
"mime-types": "^2.1.27",
"n8n-workflow": "~0.36.0",
"n8n-workflow": "~0.37.0",
"p-cancelable": "^2.0.0",
"request": "^2.88.2",
"request-promise-native": "^1.0.7"

View file

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

View file

@ -0,0 +1,19 @@
import {
ICredentialType,
NodePropertyTypes,
} from 'n8n-workflow';
export class CustomerIoApi implements ICredentialType {
name = 'customerIoApi';
displayName = 'Customer.io API';
properties = [
{
displayName: 'App API Key',
name: 'apiKey',
type: 'string' as NodePropertyTypes,
default: '',
},
];
}

View file

@ -0,0 +1,17 @@
import {
ICredentialType,
NodePropertyTypes,
} from 'n8n-workflow';
export class MediumApi implements ICredentialType {
name = 'mediumApi';
displayName = 'Medium API';
properties = [
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string' as NodePropertyTypes,
default: '',
},
];
}

View file

@ -0,0 +1,60 @@
import {
ICredentialType,
NodePropertyTypes,
} from 'n8n-workflow';
export class MediumOAuth2Api implements ICredentialType {
name = 'mediumOAuth2Api';
extends = [
'oAuth2Api',
];
displayName = 'Medium OAuth2 API';
properties = [
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden' as NodePropertyTypes,
default: 'https://medium.com/m/oauth/authorize',
required: true,
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden' as NodePropertyTypes,
default: 'https://medium.com/v1/tokens',
required: true,
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden' as NodePropertyTypes,
default: 'basicProfile,publishPost,listPublications',
},
{
displayName: 'Client ID',
name: 'clientId',
type: 'string' as NodePropertyTypes,
default: '',
required: true,
},
{
displayName: 'Client Secret',
name: 'clientSecret',
type: 'string' as NodePropertyTypes,
default: '',
required: true,
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden' as NodePropertyTypes,
default: '',
},
{
displayName: 'Authentication',
name: 'authentication',
type: 'hidden' as NodePropertyTypes,
default: 'body',
},
];
}

View file

@ -0,0 +1,17 @@
import {
ICredentialType,
NodePropertyTypes,
} from 'n8n-workflow';
export class TravisCiApi implements ICredentialType {
name = 'travisCiApi';
displayName = 'Travis API';
properties = [
{
displayName: 'API Token',
name: 'apiToken',
type: 'string' as NodePropertyTypes,
default: '',
},
];
}

View file

@ -0,0 +1,17 @@
import {
ICredentialType,
NodePropertyTypes,
} from 'n8n-workflow';
export class TwakeCloudApi implements ICredentialType {
name = 'twakeCloudApi';
displayName = 'Twake API';
properties = [
{
displayName: 'Workspace Key',
name: 'workspaceKey',
type: 'string' as NodePropertyTypes,
default: '',
},
];
}

View file

@ -0,0 +1,29 @@
import {
ICredentialType,
NodePropertyTypes,
} from 'n8n-workflow';
export class TwakeServerApi implements ICredentialType {
name = 'twakeServerApi';
displayName = 'Twake API';
properties = [
{
displayName: 'Host URL',
name: 'hostUrl',
type: 'string' as NodePropertyTypes,
default: '',
},
{
displayName: 'Public ID',
name: 'publicId',
type: 'string' as NodePropertyTypes,
default: '',
},
{
displayName: 'Private API Key',
name: 'privateApiKey',
type: 'string' as NodePropertyTypes,
default: '',
},
];
}

View file

@ -19,8 +19,8 @@ export async function awsApiRequest(this: IHookFunctions | IExecuteFunctions | I
const endpoint = `${service}.${credentials.region}.amazonaws.com`;
// Sign AWS API request with the user credentials
const signOpts = {headers: headers || {}, host: endpoint, method, path, body};
sign(signOpts, {accessKeyId: `${credentials.accessKeyId}`, secretAccessKey: `${credentials.secretAccessKey}`});
const signOpts = { headers: headers || {}, host: endpoint, method, path, body };
sign(signOpts, { accessKeyId: `${credentials.accessKeyId}`, secretAccessKey: `${credentials.secretAccessKey}` });
const options: OptionsWithUri = {
headers: signOpts.headers,

View file

@ -0,0 +1,329 @@
import {
IHookFunctions,
IWebhookFunctions,
} from 'n8n-core';
import {
INodeTypeDescription,
INodeType,
IDataObject,
IWebhookResponseData,
} from 'n8n-workflow';
import {
apiRequest,
eventExists,
} from './GenericFunctions';
interface IEvent {
customer?: IDataObject;
email?: IDataObject;
push?: IDataObject;
slack?: IDataObject;
sms?: IDataObject;
webhook?: IDataObject;
}
export class CustomerIoTrigger implements INodeType {
description: INodeTypeDescription = {
displayName: 'Customer.io Trigger',
name: 'customerIoTrigger',
group: ['trigger'],
icon: 'file:customerio.png',
version: 1,
description: 'Starts the workflow on a Customer.io update. (Beta)',
defaults: {
name: 'Customer.io Trigger',
color: '#7131ff',
},
inputs: [],
outputs: ['main'],
credentials: [
{
name: 'customerIoApi',
required: true,
},
],
webhooks: [
{
name: 'default',
httpMethod: 'POST',
responseMode: 'onReceived',
path: 'webhook',
},
],
properties: [
{
displayName: 'Events',
name: 'events',
type: 'multiOptions',
required: true,
default: [],
description: 'The events that can trigger the webhook and whether they are enabled.',
options: [
{
name: 'Customer Subscribed',
value: 'customer.subscribed',
description: 'Whether the webhook is triggered when a list subscriber is added.',
},
{
name: 'Customer Unsubscribe',
value: 'customer.unsubscribed',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Email Attempted',
value: 'email.attempted',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Email Bounced',
value: 'email.bounced',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Email clicked',
value: 'email.clicked',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Email converted',
value: 'email.converted',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Email delivered',
value: 'email.delivered',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Email drafted',
value: 'email.drafted',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Email failed',
value: 'email.failed',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Email opened',
value: 'email.opened',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Email sent',
value: 'email.sent',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Email spammed',
value: 'email.spammed',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Push attempted',
value: 'push.attempted',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Push bounced',
value: 'push.bounced',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Push clicked',
value: 'push.clicked',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Push delivered',
value: 'push.delivered',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Push drafted',
value: 'push.drafted',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Push failed',
value: 'push.failed',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Push opened',
value: 'push.opened',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Push sent',
value: 'push.sent',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Slack attempted',
value: 'slack.attempted',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Slack clicked',
value: 'slack.clicked',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Slack drafted',
value: 'slack.drafted',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Slack failed',
value: 'slack.failed',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'Slack sent',
value: 'slack.sent',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'SMS attempted',
value: 'sms.attempted',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'SMS bounced',
value: 'sms.bounced',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'SMS clicked',
value: 'sms.clicked',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'SMS delivered',
value: 'sms.delivered',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'SMS drafted',
value: 'sms.drafted',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'SMS failed',
value: 'sms.failed',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
{
name: 'SMS sent',
value: 'sms.sent',
description: 'Whether the webhook is triggered when a list member unsubscribes.',
},
],
},
],
};
// @ts-ignore (because of request)
webhookMethods = {
default: {
async checkExists(this: IHookFunctions): Promise<boolean> {
const webhookUrl = this.getNodeWebhookUrl('default');
const webhookData = this.getWorkflowStaticData('node');
const currentEvents = this.getNodeParameter('events', []) as string[];
const endpoint = '/reporting_webhooks';
let { reporting_webhooks: webhooks } = await apiRequest.call(this, 'GET', endpoint, {});
if (webhooks === null) {
webhooks = [];
}
for (const webhook of webhooks) {
if (webhook.endpoint === webhookUrl &&
eventExists(currentEvents, webhook.events)) {
webhookData.webhookId = webhook.id;
return true;
}
}
return false;
},
async create(this: IHookFunctions): Promise<boolean> {
let webhook;
const webhookUrl = this.getNodeWebhookUrl('default');
const events = this.getNodeParameter('events', []) as string[];
const endpoint = '/reporting_webhooks';
const data: IEvent = {
customer: {},
email: {},
push: {},
slack: {},
sms: {},
webhook: {},
};
for (const event of events) {
const option = event.split('.')[1];
if (event.startsWith('customer')) {
data.customer![option] = true;
}
if (event.startsWith('email')) {
data.email![option] = true;
}
if (event.startsWith('push')) {
data.push![option] = true;
}
if (event.startsWith('slack')) {
data.slack![option] = true;
}
if (event.startsWith('sms')) {
data.sms![option] = true;
}
if (event.startsWith('webhook')) {
data.webhook![option] = true;
}
}
const body = {
endpoint: webhookUrl,
events: data,
};
webhook = await apiRequest.call(this, 'POST', endpoint, body);
const webhookData = this.getWorkflowStaticData('node');
webhookData.webhookId = webhook.id as string;
return true;
},
async delete(this: IHookFunctions): Promise<boolean> {
const webhookData = this.getWorkflowStaticData('node');
if (webhookData.webhookId !== undefined) {
const endpoint = `/reporting_webhooks/${webhookData.webhookId}`;
try {
await apiRequest.call(this, 'DELETE', endpoint, {});
} catch (e) {
return false;
}
delete webhookData.webhookId;
}
return true;
},
}
};
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
const bodyData = this.getBodyData();
return {
workflowData: [
this.helpers.returnJsonArray(bodyData)
],
};
}
}

View file

@ -0,0 +1,65 @@
import {
IExecuteFunctions,
IHookFunctions,
ILoadOptionsFunctions,
} from 'n8n-core';
import {
OptionsWithUri,
} from 'request';
import {
IDataObject,
} from 'n8n-workflow';
import {
get,
} from 'lodash';
export async function apiRequest(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: object, query?: IDataObject): Promise<any> { // tslint:disable-line:no-any
const credentials = this.getCredentials('customerIoApi');
if (credentials === undefined) {
throw new Error('No credentials got returned!');
}
query = query || {};
const options: OptionsWithUri = {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${credentials.apiKey}`,
},
method,
body,
qs: query,
uri: `https://beta-api.customer.io/v1/api${endpoint}`,
json: true,
};
try {
return await this.helpers.request!(options);
} catch (error) {
if (error.statusCode === 401) {
// Return a clear error
throw new Error('The Customer.io credentials are not valid!');
}
if (error.response && error.response.body && error.response.body.error_code) {
// Try to return the error prettier
const errorBody = error.response.body;
throw new Error(`Customer.io error response [${errorBody.error_code}]: ${errorBody.description}`);
}
// Expected error data did not get returned so throw the actual error
throw error;
}
}
export function eventExists(currentEvents: string[], webhookEvents: IDataObject) {
for (const currentEvent of currentEvents) {
if (get(webhookEvents, `${currentEvent.split('.')[0]}.${currentEvent.split('.')[1]}`) !== true) {
return false;
}
}
return true;
}

View file

@ -133,7 +133,7 @@ export class FacebookGraphApi implements INodeType {
name: 'edge',
type: 'string',
default: '',
description: 'Edge of the node on which to operate. Edges represent collections of objects wich are attached to the node.',
description: 'Edge of the node on which to operate. Edges represent collections of objects which are attached to the node.',
placeholder: 'videos',
required: false,
},

View file

@ -99,7 +99,7 @@ export class FlowTrigger implements INodeType {
]
}
},
description: `Taks ids separated by ,`,
description: `Task ids separated by ,`,
},
],

View file

@ -181,7 +181,7 @@ export class GoogleSheet {
}
/**
* Returns the given sheet data in a strucutred way
* Returns the given sheet data in a structured way
*/
structureData(inputData: string[][], startRow: number, keys: string[], addEmpty?: boolean): IDataObject[] {
const returnData = [];
@ -208,7 +208,7 @@ export class GoogleSheet {
/**
* Returns the given sheet data in a strucutred way using
* Returns the given sheet data in a structured way using
* the startRow as the one with the name of the key
*/
structureArrayDataByColumn(inputData: string[][], keyRow: number, dataStartRow: number): IDataObject[] {
@ -216,7 +216,7 @@ export class GoogleSheet {
const keys: string[] = [];
if (keyRow < 0 || dataStartRow < keyRow || keyRow >= inputData.length) {
// The key row does not exist so it is not possible to strucutre data
// The key row does not exist so it is not possible to structure data
return [];
}

View file

@ -151,7 +151,7 @@ export class GoogleSheets implements INodeType {
displayName: 'To Delete',
name: 'toDelete',
placeholder: 'Add Columns/Rows to delete',
description: 'Deletes colums and rows from a sheet.',
description: 'Deletes columns and rows from a sheet.',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
@ -363,7 +363,7 @@ export class GoogleSheets implements INodeType {
},
},
default: 0,
description: 'Index of the row which contains the keys. Starts at 0.<br />The incoming node data is matched to the keys for assignment. The matching is case sensitve.',
description: 'Index of the row which contains the keys. Starts at 0.<br />The incoming node data is matched to the keys for assignment. The matching is case sensitive.',
},

View file

@ -1,19 +1,19 @@
import {
IHookFunctions,
IWebhookFunctions,
} from 'n8n-core';
} from 'n8n-core';
import {
import {
IDataObject,
INodeTypeDescription,
INodeType,
IWebhookResponseData,
ILoadOptionsFunctions,
INodePropertyOptions,
} from 'n8n-workflow';
import {
} from 'n8n-workflow';
import {
mailchimpApiRequest,
} from './GenericFunctions';
} from './GenericFunctions';
export class MailchimpTrigger implements INodeType {
description: INodeTypeDescription = {
@ -24,8 +24,8 @@ export class MailchimpTrigger implements INodeType {
version: 1,
description: 'Handle Mailchimp events via webhooks',
defaults: {
name: 'Mailchimp Trigger',
color: '#32325d',
name: 'Mailchimp Trigger',
color: '#32325d',
},
inputs: [],
outputs: ['main'],
@ -285,8 +285,8 @@ export class MailchimpTrigger implements INodeType {
}
// @ts-ignore
if (!webhookData.events.includes(req.body.type)
// @ts-ignore
&& !webhookData.sources.includes(req.body.type)) {
// @ts-ignore
&& !webhookData.sources.includes(req.body.type)) {
return {};
}
return {

View file

@ -0,0 +1,54 @@
import {
OptionsWithUri,
} from 'request';
import {
IExecuteFunctions,
IExecuteSingleFunctions,
IHookFunctions,
ILoadOptionsFunctions,
} from 'n8n-core';
import {
IDataObject,
} from 'n8n-workflow';
export async function mediumApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, query: IDataObject = {}, uri?: string): Promise<any> { // tslint:disable-line:no-any
const authenticationMethod = this.getNodeParameter('authentication', 0);
const options: OptionsWithUri = {
method,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Accept-Charset': 'utf-8',
},
qs: query,
uri: uri || `https://api.medium.com/v1${endpoint}`,
body,
json: true,
};
try {
if (authenticationMethod === 'accessToken') {
const credentials = this.getCredentials('mediumApi');
if (credentials === undefined) {
throw new Error('No credentials got returned!');
}
options.headers!['Authorization'] = `Bearer ${credentials.accessToken}`;
return await this.helpers.request!(options);
}
else {
return await this.helpers.requestOAuth2!.call(this, 'mediumOAuth2Api', options);
}
} catch (error) {
if (error.statusCode === 401) {
throw new Error('The Medium credentials are not valid!');
}
throw error;
}
}

View file

@ -0,0 +1,577 @@
import {
IExecuteFunctions,
} from 'n8n-core';
import {
IDataObject,
ILoadOptionsFunctions,
INodeExecutionData,
INodeType,
INodePropertyOptions,
INodeTypeDescription,
} from 'n8n-workflow';
import {
mediumApiRequest,
} from './GenericFunctions';
export class Medium implements INodeType {
description: INodeTypeDescription = {
displayName: 'Medium',
name: 'medium',
group: ['output'],
icon: 'file:medium.png',
version: 1,
description: 'Consume Medium API',
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
defaults: {
name: 'Medium',
color: '#000000',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'mediumApi',
required: true,
displayOptions: {
show: {
authentication: [
'accessToken',
],
},
},
},
{
name: 'mediumOAuth2Api',
required: true,
displayOptions: {
show: {
authentication: [
'oAuth2',
],
},
},
},
],
properties: [
{
displayName: 'Authentication',
name: 'authentication',
type: 'options',
options: [
{
name: 'Access Token',
value: 'accessToken',
},
{
name: 'OAuth2',
value: 'oAuth2',
},
],
default: 'accessToken',
description: 'The method of authentication.',
},
{
displayName: 'Resource',
name: 'resource',
type: 'options',
options: [
{
name: 'Post',
value: 'post',
},
{
name: 'Publication',
value: 'publication',
},
],
default: 'post',
description: 'Resource to operate on.',
},
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'post',
],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a post',
},
],
default: 'create',
description: 'The operation to perform.',
},
// ----------------------------------
// post:create
// ----------------------------------
{
displayName: 'Publication',
name: 'publication',
type: 'boolean',
displayOptions: {
show: {
resource: [
'post',
],
operation: [
'create',
],
},
},
default: false,
description: 'Are you posting for a publication?'
},
{
displayName: 'Publication ID',
name: 'publicationId',
type: 'options',
displayOptions: {
show: {
resource: [
'post',
],
operation: [
'create',
],
publication: [
true,
],
},
},
typeOptions: {
loadOptionsMethod: 'getPublications',
},
default: '',
description: 'Publication ids',
},
{
displayName: 'Title',
name: 'title',
type: 'string',
default: '',
placeholder: 'My Open Source Contribution',
required: true,
displayOptions: {
show: {
operation: [
'create',
],
resource: [
'post',
],
},
},
description: 'Title of the post. Max Length : 100 characters',
},
{
displayName: 'Content Format',
name: 'contentFormat',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'create',
],
resource: [
'post',
],
},
},
type: 'options',
options: [
{
name: 'HTML',
value: 'html',
},
{
name: 'Markdown',
value: 'markdown',
},
],
description: 'The format of the content to be posted.',
},
{
displayName: 'Content',
name: 'content',
type: 'string',
default: '',
placeholder: 'My open source contribution',
required: true,
typeOptions: {
alwaysOpenEditWindow: true,
},
displayOptions: {
show: {
operation: [
'create',
],
resource: [
'post',
],
},
},
description: 'The body of the post, in a valid semantic HTML fragment, or Markdown.',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
displayOptions: {
show: {
operation: [
'create',
],
resource: [
'post',
],
},
},
default: {},
options: [
{
displayName: 'Canonical Url',
name: 'canonicalUrl',
type: 'string',
default: '',
description: 'The original home of this content, if it was originally published elsewhere.',
},
{
displayName: 'License',
name: 'license',
type: 'options',
default: 'all-rights-reserved',
options: [
{
name: 'all-rights-reserved',
value: 'all-rights-reserved',
},
{
name: 'cc-40-by',
value: 'cc-40-by',
},
{
name: 'cc-40-by-nc',
value: 'cc-40-by-nc',
},
{
name: 'cc-40-by-nc-nd',
value: 'cc-40-by-nc-nd',
},
{
name: 'cc-40-by-nc-sa',
value: 'cc-40-by-nc-sa',
},
{
name: 'cc-40-by-nd',
value: 'cc-40-by-nd',
},
{
name: 'cc-40-by-sa',
value: 'cc-40-by-sa',
},
{
name: 'cc-40-zero',
value: 'cc-40-zero',
},
{
name: 'public-domain',
value: 'public-domain',
},
],
description: 'License of the post.',
},
{
displayName: 'Notify Followers',
name: 'notifyFollowers',
type: 'boolean',
default: false,
description: 'Whether to notify followers that the user has published.',
},
{
displayName: 'Publish Status',
name: 'publishStatus',
default: 'public',
type: 'options',
options: [
{
name: 'Public',
value: 'public',
},
{
name: 'Draft',
value: 'draft',
},
{
name: 'Unlisted',
value: 'unlisted',
},
],
description: 'The status of the post.',
},
{
displayName: 'Tags',
name: 'tags',
type: 'string',
default: '',
placeholder: 'open-source,mlh,fellowship',
description: 'Comma-separated strings to be used as tags for post classification. Max allowed tags: 5. Max tag length: 25 characters.',
},
],
},
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'publication',
],
},
},
options: [
{
name: 'Get All',
value: 'getAll',
description: 'Get all publications',
},
],
default: 'publication',
description: 'The operation to perform.',
},
// ----------------------------------
// publication:getAll
// ----------------------------------
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'publication',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit.',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'publication',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 200,
},
default: 100,
description: 'How many results to return.',
},
],
};
methods = {
loadOptions: {
// Get all the available publications to display them to user so that he can
// select them easily
async getPublications(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
//Get the User Id
const user = await mediumApiRequest.call(
this,
'GET',
`/me`,
);
const userId = user.data.id;
//Get all publications of that user
const publications = await mediumApiRequest.call(
this,
'GET',
`/users/${userId}/publications`,
);
const publicationsList = publications.data;
for (const publication of publicationsList) {
const publicationName = publication.name;
const publicationId = publication.id;
returnData.push({
name: publicationName,
value: publicationId,
});
}
return returnData;
},
},
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: IDataObject[] = [];
let operation: string;
let resource: string;
// For POST
let bodyRequest: IDataObject;
// For Query string
let qs: IDataObject;
let responseData;
for (let i = 0; i < items.length; i++) {
qs = {};
resource = this.getNodeParameter('resource', i) as string;
operation = this.getNodeParameter('operation', i) as string;
if (resource === 'post') {
//https://github.com/Medium/medium-api-docs
if (operation === 'create') {
// ----------------------------------
// post:create
// ----------------------------------
const title = this.getNodeParameter('title', i) as string;
const contentFormat = this.getNodeParameter('contentFormat', i) as string;
const content = this.getNodeParameter('content', i) as string;
bodyRequest = {
tags: [],
title,
contentFormat,
content,
};
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
if (additionalFields.tags) {
const tags = additionalFields.tags as string;
bodyRequest.tags = tags.split(',').map(name => {
const returnValue = name.trim();
if (returnValue.length > 25) {
throw new Error(`The tag "${returnValue}" is to long. Maximum lenght of a tag is 25 characters.`);
}
return returnValue;
});
if ((bodyRequest.tags as string[]).length > 5) {
throw new Error('To many tags got used. Maximum 5 can be set.');
}
}
if (additionalFields.canonicalUrl) {
bodyRequest.canonicalUrl = additionalFields.canonicalUrl as string;
}
if (additionalFields.publishStatus) {
bodyRequest.publishStatus = additionalFields.publishStatus as string;
}
if (additionalFields.license) {
bodyRequest.license = additionalFields.license as string;
}
if (additionalFields.notifyFollowers) {
bodyRequest.notifyFollowers = additionalFields.notifyFollowers as string;
}
const underPublication = this.getNodeParameter('publication', i) as boolean;
// if user wants to publish it under a specific publication
if (underPublication) {
const publicationId = this.getNodeParameter('publicationId', i) as number;
responseData = await mediumApiRequest.call(
this,
'POST',
`/publications/${publicationId}/posts`,
bodyRequest,
qs
);
}
else {
const responseAuthorId = await mediumApiRequest.call(
this,
'GET',
'/me',
{},
qs
);
const authorId = responseAuthorId.data.id;
responseData = await mediumApiRequest.call(
this,
'POST',
`/users/${authorId}/posts`,
bodyRequest,
qs
);
responseData = responseData.data;
}
}
}
if (resource === 'publication') {
//https://github.com/Medium/medium-api-docs#32-publications
if (operation === 'getAll') {
// ----------------------------------
// publication:getAll
// ----------------------------------
const returnAll = this.getNodeParameter('returnAll', i) as string;
const user = await mediumApiRequest.call(
this,
'GET',
`/me`,
);
const userId = user.data.id;
//Get all publications of that user
responseData = await mediumApiRequest.call(
this,
'GET',
`/users/${userId}/publications`,
);
responseData = responseData.data;
if (!returnAll) {
const limit = this.getNodeParameter('limit', i) as number;
responseData = responseData.splice(0, limit);
}
}
}
if (Array.isArray(responseData)) {
returnData.push.apply(returnData, responseData as IDataObject[]);
} else {
returnData.push(responseData as IDataObject);
}
}
return [this.helpers.returnJsonArray(returnData)];
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

@ -18,12 +18,12 @@ export const lightOperations = [
{
name: 'Delete',
value: 'delete',
description: 'Delete an light',
description: 'Delete a light',
},
{
name: 'Get',
value: 'get',
description: 'Retrieve an light',
description: 'Retrieve a light',
},
{
name: 'Get All',
@ -194,9 +194,9 @@ export const lightFields = [
type: 'options',
options: [
{
name: 'none',
name: 'None',
value: 'none',
description: 'the light is not performing an alert effect',
description: 'The light is not performing an alert effect',
},
{
name: 'Select',
@ -275,7 +275,7 @@ export const lightFields = [
type: 'options',
options: [
{
name: 'none',
name: 'None',
value: 'none',
},
{

View file

@ -36,7 +36,7 @@ export const opportunityOperations = [
{
name: 'Get All',
value: 'getAll',
description: 'Get all opportunitys',
description: 'Get all opportunities',
},
{
name: 'Get Summary',

View file

@ -119,7 +119,7 @@ export const taskFields = [
type: 'number',
default: '',
description: `Duration of the call in seconds. Not subject to field-level security,<br/>
available for any user in an organization with Salesforce CRM Call Cente`,
available for any user in an organization with Salesforce CRM Call Center`,
},
{
displayName: 'Call Object',
@ -434,7 +434,7 @@ export const taskFields = [
type: 'number',
default: '',
description: `Duration of the call in seconds. Not subject to field-level security,<br/>
available for any user in an organization with Salesforce CRM Call Cente`,
available for any user in an organization with Salesforce CRM Call Center`,
},
{
displayName: 'Call Object',

View file

@ -0,0 +1,356 @@
import {
INodeProperties,
} from 'n8n-workflow';
export const buildOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'build',
],
},
},
options: [
{
name: 'Cancel',
value: 'cancel',
description: 'Cancel a build',
},
{
name: 'Get',
value: 'get',
description: 'Get a build',
},
{
name: 'Get All',
value: 'getAll',
description: 'Get all builds',
},
{
name: 'Restart',
value: 'restart',
description: 'Restart a build',
},
{
name: 'Trigger',
value: 'trigger',
description: 'Trigger a build',
},
],
default: 'cancel',
description: 'The operation to perform.',
},
] as INodeProperties[];
export const buildFields = [
/* -------------------------------------------------------------------------- */
/* build:cancel */
/* -------------------------------------------------------------------------- */
{
displayName: 'Build ID',
name: 'buildId',
type: 'string',
displayOptions: {
show: {
operation: [
'cancel',
],
resource: [
'build',
],
},
},
default: '',
description: 'Value uniquely identifying the build.',
},
/* -------------------------------------------------------------------------- */
/* build:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Build ID',
name: 'buildId',
type: 'string',
displayOptions: {
show: {
operation: [
'get',
],
resource: [
'build',
],
},
},
default: '',
description: 'Value uniquely identifying the build.',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'build',
],
operation: [
'get',
],
},
},
options: [
{
displayName: 'Include',
name: 'include',
type: 'string',
default: '',
placeholder: 'build.commit',
description: 'List of attributes to eager load.',
},
],
},
/* -------------------------------------------------------------------------- */
/* build:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'build',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit.',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'build',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 500,
},
default: 100,
description: 'How many results to return.',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'build',
],
operation: [
'getAll',
],
},
},
options: [
{
displayName: 'Include',
name: 'include',
type: 'string',
default: '',
placeholder: 'build.commit',
description: 'List of attributes to eager load.',
},
{
displayName: 'Order',
name: 'order',
type: 'options',
options: [
{
name: 'ASC',
value: 'asc',
},
{
name: 'DESC',
value: 'desc',
}
],
default: 'asc',
description: 'You may specify order to sort your response.',
},
{
displayName: 'Sort By',
name: 'sortBy',
type: 'options',
options: [
{
name: 'ID',
value: 'id',
},
{
name: 'Created At',
value: 'created_at',
},
{
name: 'Started At',
value: 'started_at',
},
{
name: 'Finished At',
value: 'finished_at',
},
{
name: 'Finished At',
value: 'finished_at',
},
{
name: 'Number',
value: 'number',
},
],
default: 'number',
},
],
},
/* -------------------------------------------------------------------------- */
/* build:restart */
/* -------------------------------------------------------------------------- */
{
displayName: 'Build ID',
name: 'buildId',
type: 'string',
displayOptions: {
show: {
operation: [
'restart',
],
resource: [
'build',
],
},
},
default: '',
description: 'Value uniquely identifying the build.',
},
/* -------------------------------------------------------------------------- */
/* build:trigger */
/* -------------------------------------------------------------------------- */
{
displayName: 'Slug',
name: 'slug',
type: 'string',
displayOptions: {
show: {
operation: [
'trigger',
],
resource: [
'build',
],
},
},
placeholder: 'n8n-io/n8n',
default: '',
description: 'Same as {ownerName}/{repositoryName}',
},
{
displayName: 'Branch',
name: 'branch',
type: 'string',
displayOptions: {
show: {
operation: [
'trigger',
],
resource: [
'build',
],
},
},
default: '',
placeholder: 'master',
description: 'Branch requested to be built.',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'build',
],
operation: [
'trigger',
],
},
},
options: [
{
displayName: 'Message',
name: 'message',
type: 'string',
default: '',
description: 'Travis-ci status message attached to the request.',
},
{
displayName: 'Merge Mode',
name: 'mergeMode',
type: 'options',
options: [
{
name: 'Deep Merge Append',
value: 'deep_merge_append',
},
{
name: 'Deep Merge Prepend',
value: 'deep_merge_prepend',
},
{
name: 'Deep Merge',
value: 'deep_merge',
},
{
name: 'Merge',
value: 'merge',
},
{
name: 'Replace',
value: 'replace',
},
],
default: '',
description: '',
},
],
},
] as INodeProperties[];

View file

@ -0,0 +1,78 @@
import {
OptionsWithUri,
} from 'request';
import {
IExecuteFunctions,
IExecuteSingleFunctions,
IHookFunctions,
ILoadOptionsFunctions,
} from 'n8n-core';
import {
IDataObject,
} from 'n8n-workflow';
import {
get,
} from 'lodash';
import * as querystring from 'querystring';
export async function travisciApiRequest(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('travisCiApi');
if (credentials === undefined) {
throw new Error('No credentials got returned!');
}
let options: OptionsWithUri = {
headers: {
'Travis-API-Version': '3',
'Accept': 'application/json',
'Content-Type': 'application.json',
'Authorization': `token ${credentials.apiToken}`,
},
method,
qs,
body,
uri: uri || `https://api.travis-ci.com${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) {
if (err.response && err.response.body && err.response.body.error_message) {
// Try to return the error prettier
throw new Error(`TravisCI error response [${err.statusCode}]: ${err.response.body.error_message}`);
}
// If that data does not exist for some reason return the actual error
throw err;
}
}
/**
* Make an API request to paginated TravisCI endpoint
* and return all results
*/
export async function travisciApiRequestAllItems(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 travisciApiRequest.call(this, method, resource, body, query);
const path = get(responseData, '@pagination.next.@href');
if (path !== undefined) {
query = querystring.parse(path);
}
returnData.push.apply(returnData, responseData[propertyName]);
} while (
responseData['@pagination']['is_last'] !== true
);
return returnData;
}

View file

@ -0,0 +1,151 @@
import {
IExecuteFunctions,
} from 'n8n-core';
import {
IDataObject,
INodeTypeDescription,
INodeExecutionData,
INodeType,
} from 'n8n-workflow';
import {
buildFields,
buildOperations,
} from './BuildDescription';
import {
travisciApiRequest,
travisciApiRequestAllItems,
} from './GenericFunctions';
export class TravisCi implements INodeType {
description: INodeTypeDescription = {
displayName: 'TravisCI',
name: 'travisCi',
icon: 'file:travisci.png',
group: ['output'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Consume TravisCI API',
defaults: {
name: 'TravisCI',
color: '#666666',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'travisCiApi',
required: true,
}
],
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
options: [
{
name: ' Build',
value: 'build',
},
],
default: 'build',
description: 'Resource to consume.',
},
...buildOperations,
...buildFields,
],
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: IDataObject[] = [];
const length = items.length as unknown as number;
const 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 < length; i++) {
if (resource === 'build') {
//https://developer.travis-ci.com/resource/build#find
if (operation === 'get') {
const buildId = this.getNodeParameter('buildId', i) as string;
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
if (additionalFields.include) {
qs.include = additionalFields.include as string;
}
responseData = await travisciApiRequest.call(this, 'GET', `/build/${buildId}`, {}, qs);
}
//https://developer.travis-ci.com/resource/builds#for_current_user
if (operation === 'getAll') {
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
if (additionalFields.sortBy) {
qs.sort_by = additionalFields.sortBy;
}
if (additionalFields.sortBy && additionalFields.order) {
qs.sort_by = `${additionalFields.sortBy}:${additionalFields.order}`;
}
if (additionalFields.include) {
qs.include = additionalFields.include;
}
if (returnAll === true) {
responseData = await travisciApiRequestAllItems.call(this, 'builds', 'GET', '/builds', {}, qs);
} else {
qs.limit = this.getNodeParameter('limit', i) as number;
responseData = await travisciApiRequest.call(this, 'GET', '/builds', {}, qs);
responseData = responseData.builds;
}
}
//https://developer.travis-ci.com/resource/build#cancel
if (operation === 'cancel') {
const buildId = this.getNodeParameter('buildId', i) as string;
responseData = await travisciApiRequest.call(this, 'POST', `/build/${buildId}/cancel`, {}, qs);
}
//https://developer.travis-ci.com/resource/build#restart
if (operation === 'restart') {
const buildId = this.getNodeParameter('buildId', i) as string;
responseData = await travisciApiRequest.call(this, 'POST', `/build/${buildId}/restart`, {}, qs);
}
//https://developer.travis-ci.com/resource/requests#create
if (operation === 'trigger') {
let slug = this.getNodeParameter('slug', i) as string;
const branch = this.getNodeParameter('branch', i) as string;
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
slug = slug.replace(new RegExp(/\//g), '%2F');
const request: IDataObject = {
branch,
};
if (additionalFields.message) {
request.message = additionalFields.message as string;
}
if (additionalFields.mergeMode) {
request.merge_mode = additionalFields.mergeMode as string;
}
responseData = await travisciApiRequest.call(this, 'POST', `/repo/${slug}/requests`, JSON.stringify({ request }));
}
}
if (Array.isArray(responseData)) {
returnData.push.apply(returnData, responseData as IDataObject[]);
} else {
returnData.push(responseData as IDataObject);
}
}
return [this.helpers.returnJsonArray(returnData)];
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View file

@ -0,0 +1,68 @@
import {
IExecuteFunctions,
IHookFunctions,
ILoadOptionsFunctions,
} from 'n8n-core';
import {
OptionsWithUri,
} from 'request';
/**
* Make an API request to Twake
*
* @param {IHookFunctions} this
* @param {string} method
* @param {string} url
* @param {object} body
* @returns {Promise<any>}
*/
export async function twakeApiRequest(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, method: string, resource: string, body: object, query?: object, uri?: string): Promise<any> { // tslint:disable-line:no-any
const authenticationMethod = this.getNodeParameter('twakeVersion', 0, 'twakeCloudApi') as string;
const options: OptionsWithUri = {
headers: {},
method,
body,
qs: query,
uri: uri || `https://connectors.albatros.twakeapp.com/n8n${resource}`,
json: true,
};
// if (authenticationMethod === 'cloud') {
const credentials = this.getCredentials('twakeCloudApi');
options.headers!.Authorization = `Bearer ${credentials!.workspaceKey}`;
// } else {
// const credentials = this.getCredentials('twakeServerApi');
// options.auth = { user: credentials!.publicId as string, pass: credentials!.privateApiKey as string };
// options.uri = `${credentials!.hostUrl}/api/v1${resource}`;
// }
try {
return await this.helpers.request!(options);
} catch (error) {
if (error.error.code === 'ECONNREFUSED') {
throw new Error('Twake host is not accessible!');
}
if (error.statusCode === 401) {
// Return a clear error
throw new Error('The Twake credentials are not valid!');
}
if (error.response && error.response.body && error.response.body.errors) {
// Try to return the error prettier
const errorMessages = error.response.body.errors.map((errorData: { message: string }) => {
return errorData.message;
});
throw new Error(`Twake error response [${error.statusCode}]: ${errorMessages.join(' | ')}`);
}
// If that data does not exist for some reason return the actual error
throw error;
}
}

View file

@ -0,0 +1,247 @@
import {
IExecuteFunctions,
} from 'n8n-core';
import {
INodeExecutionData,
IDataObject,
INodeType,
INodeTypeDescription,
ILoadOptionsFunctions,
INodePropertyOptions,
} from 'n8n-workflow';
import {
twakeApiRequest,
} from './GenericFunctions';
export class Twake implements INodeType {
description: INodeTypeDescription = {
displayName: 'Twake',
name: 'twake',
group: ['transform'],
version: 1,
icon: 'file:twake.png',
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Consume Twake API',
defaults: {
name: 'Twake',
color: '#7168ee',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'twakeCloudApi',
required: true,
// displayOptions: {
// show: {
// twakeVersion: [
// 'cloud',
// ],
// },
// },
},
// {
// name: 'twakeServerApi',
// required: true,
// displayOptions: {
// show: {
// twakeVersion: [
// 'server',
// ],
// },
// },
// },
],
properties: [
// {
// displayName: 'Twake Version',
// name: 'twakeVersion',
// type: 'options',
// options: [
// {
// name: 'Cloud',
// value: 'cloud',
// },
// {
// name: 'Server (Self Hosted)',
// value: 'server',
// },
// ],
// default: 'cloud',
// },
{
displayName: 'Resource',
name: 'resource',
type: 'options',
options: [
{
name: 'Message',
value: 'message',
description: 'Send data to the message app',
},
],
default: 'message',
description: 'The operation to perform.',
},
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'message',
],
},
},
options: [
{
name: 'Send',
value: 'send',
description: 'Send a message',
},
],
default: 'send',
description: 'The operation to perform.',
},
{
displayName: 'Channel ID',
name: 'channelId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getChannels',
},
displayOptions: {
show: {
operation: [
'send',
],
},
},
default: '',
description: `Channel's ID`,
},
{
displayName: 'Content',
name: 'content',
type: 'string',
required: true,
displayOptions: {
show: {
operation: [
'send',
],
},
},
default: '',
description: 'Message content',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
displayOptions: {
show: {
operation: [
'send',
],
},
},
default: {},
options: [
{
displayName: 'Sender Icon',
name: 'senderIcon',
type: 'string',
default: '',
description: 'URL of the image/icon',
},
{
displayName: 'Sender Name',
name: 'senderName',
type: 'string',
default: '',
description: 'Sender name',
},
],
},
],
};
methods = {
loadOptions: {
async getChannels(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const responseData = await twakeApiRequest.call(this, 'POST', '/channel', {});
if (responseData === undefined) {
throw new Error('No data got returned');
}
const returnData: INodePropertyOptions[] = [];
for (const channel of responseData) {
returnData.push({
name: channel.name,
value: channel.id,
});
}
return returnData;
},
},
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: IDataObject[] = [];
const length = (items.length as unknown) as number;
const 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 < length; i++) {
if (resource === 'message') {
if (operation === 'send') {
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
const message: IDataObject = {
channel_id: this.getNodeParameter('channelId', i),
content: {
formatted: this.getNodeParameter('content', i) as string,
},
hidden_data: {
allow_delete: 'everyone',
},
};
if (additionalFields.senderName) {
//@ts-ignore
message.hidden_data!.custom_title = additionalFields.senderName as string;
}
if (additionalFields.senderIcon) {
//@ts-ignore
message.hidden_data!.custom_icon = additionalFields.senderIcon as string;
}
const body = {
object: message,
};
const endpoint = '/actions/message/save';
responseData = await twakeApiRequest.call(this, 'POST', endpoint, body);
responseData = responseData.object;
}
}
}
if (Array.isArray(responseData)) {
returnData.push.apply(returnData, responseData as IDataObject[]);
} else if (responseData !== undefined) {
returnData.push(responseData as IDataObject);
}
return [this.helpers.returnJsonArray(returnData)];
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View file

@ -228,7 +228,7 @@ export const meetingFields = [
type: 'options',
options: [
{
name: 'Attendees register once and can attend any of the occurences',
name: 'Attendees register once and can attend any of the occurrences',
value: 1,
},
{
@ -479,7 +479,7 @@ export const meetingFields = [
},
options: [
{
displayName: 'Occurence ID',
displayName: 'Occurrence ID',
name: 'occurrenceId',
type: 'string',
default: '',
@ -672,7 +672,7 @@ export const meetingFields = [
type: 'options',
options: [
{
name: 'Attendees register once and can attend any of the occurences',
name: 'Attendees register once and can attend any of the occurrences',
value: 1,
},
{

View file

@ -205,7 +205,7 @@ export const webinarFields = [
type: 'options',
options: [
{
name: 'Attendees register once and can attend any of the occurences',
name: 'Attendees register once and can attend any of the occurrences',
value: 1,
},
{
@ -308,8 +308,8 @@ export const webinarFields = [
},
options: [
{
displayName: 'Occurence ID',
name: 'occurenceId',
displayName: 'Occurrence ID',
name: 'occurrenceId',
type: 'string',
default: '',
description: 'To view webinar details of a particular occurrence of the recurring webinar.',

View file

@ -544,7 +544,7 @@ export class Zoom implements INodeType {
// 'additionalFields',
// i
// ) as IDataObject;
// if (additionalFields.occurenceId) {
// if (additionalFields.occurrenceId) {
// qs.occurrence_id = additionalFields.occurrenceId as string;
// }
// if (additionalFields.action) {

View file

@ -1,6 +1,6 @@
{
"name": "n8n-nodes-base",
"version": "0.70.1",
"version": "0.71.0",
"description": "Base nodes of n8n",
"license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io",
@ -48,6 +48,7 @@
"dist/credentials/CodaApi.credentials.js",
"dist/credentials/CopperApi.credentials.js",
"dist/credentials/CalendlyApi.credentials.js",
"dist/credentials/CustomerIoApi.credentials.js",
"dist/credentials/CrateDb.credentials.js",
"dist/credentials/DisqusApi.credentials.js",
"dist/credentials/DriftApi.credentials.js",
@ -98,6 +99,8 @@
"dist/credentials/MattermostApi.credentials.js",
"dist/credentials/MauticApi.credentials.js",
"dist/credentials/MauticOAuth2Api.credentials.js",
"dist/credentials/MediumApi.credentials.js",
"dist/credentials/MediumOAuth2Api.credentials.js",
"dist/credentials/MessageBirdApi.credentials.js",
"dist/credentials/MicrosoftExcelOAuth2Api.credentials.js",
"dist/credentials/MicrosoftOAuth2Api.credentials.js",
@ -141,12 +144,15 @@
"dist/credentials/SurveyMonkeyOAuth2Api.credentials.js",
"dist/credentials/TelegramApi.credentials.js",
"dist/credentials/TodoistApi.credentials.js",
"dist/credentials/TravisCiApi.credentials.js",
"dist/credentials/TrelloApi.credentials.js",
"dist/credentials/TwilioApi.credentials.js",
"dist/credentials/TwitterOAuth1Api.credentials.js",
"dist/credentials/TypeformApi.credentials.js",
"dist/credentials/TypeformOAuth2Api.credentials.js",
"dist/credentials/TogglApi.credentials.js",
"dist/credentials/TwakeCloudApi.credentials.js",
"dist/credentials/TwakeServerApi.credentials.js",
"dist/credentials/UpleadApi.credentials.js",
"dist/credentials/VeroApi.credentials.js",
"dist/credentials/WebflowApi.credentials.js",
@ -197,6 +203,7 @@
"dist/nodes/CrateDb/CrateDb.node.js",
"dist/nodes/Cron.node.js",
"dist/nodes/Crypto.node.js",
"dist/nodes/CustomerIo/CustomerIoTrigger.node.js",
"dist/nodes/DateTime.node.js",
"dist/nodes/Discord/Discord.node.js",
"dist/nodes/Disqus/Disqus.node.js",
@ -256,6 +263,7 @@
"dist/nodes/Mattermost/Mattermost.node.js",
"dist/nodes/Mautic/Mautic.node.js",
"dist/nodes/Mautic/MauticTrigger.node.js",
"dist/nodes/Medium/Medium.node.js",
"dist/nodes/Merge.node.js",
"dist/nodes/MessageBird/MessageBird.node.js",
"dist/nodes/Microsoft/Excel/MicrosoftExcel.node.js",
@ -309,11 +317,13 @@
"dist/nodes/Telegram/TelegramTrigger.node.js",
"dist/nodes/Todoist/Todoist.node.js",
"dist/nodes/Toggl/TogglTrigger.node.js",
"dist/nodes/TravisCi/TravisCi.node.js",
"dist/nodes/Trello/Trello.node.js",
"dist/nodes/Trello/TrelloTrigger.node.js",
"dist/nodes/Twilio/Twilio.node.js",
"dist/nodes/Twitter/Twitter.node.js",
"dist/nodes/Typeform/TypeformTrigger.node.js",
"dist/nodes/Twake/Twake.node.js",
"dist/nodes/Uplead/Uplead.node.js",
"dist/nodes/Vero/Vero.node.js",
"dist/nodes/Webflow/WebflowTrigger.node.js",
@ -355,7 +365,7 @@
"@types/xml2js": "^0.4.3",
"gulp": "^4.0.0",
"jest": "^24.9.0",
"n8n-workflow": "~0.36.0",
"n8n-workflow": "~0.37.0",
"ts-jest": "^24.0.2",
"tslint": "^5.17.0",
"typescript": "~3.7.4"
@ -382,7 +392,7 @@
"mongodb": "^3.5.5",
"mssql": "^6.2.0",
"mysql2": "^2.0.1",
"n8n-core": "~0.40.0",
"n8n-core": "~0.41.0",
"nodemailer": "^6.4.6",
"pdf-parse": "^1.1.1",
"pg": "^8.3.0",

View file

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