add meeting functionality

This commit is contained in:
shraddha shaligram 2020-06-22 11:51:15 -07:00
parent 37c80f3d8b
commit b95e3464a4
15 changed files with 1561 additions and 175 deletions

View file

@ -38,7 +38,7 @@ The most important directories:
execution, active webhooks and
workflows
- [/packages/editor-ui](/packages/editor-ui) - Vue frontend workflow editor
- [/packages/node-dev](/packages/node-dev) - Simple CLI to create new n8n-nodes
- [/packages/node-dev](/packages/node-dev) - CLI to create new n8n-nodes
- [/packages/nodes-base](/packages/nodes-base) - Base n8n nodes
- [/packages/workflow](/packages/workflow) - Workflow code with interfaces which
get used by front- & backend
@ -159,7 +159,7 @@ tests of all packages.
## Create Custom Nodes
It is very easy to create own nodes for n8n. More information about that can
It is very straightforward to create your own nodes for n8n. More information about that can
be found in the documentation of "n8n-node-dev" which is a small CLI which
helps with n8n-node-development.
@ -177,9 +177,9 @@ If you want to create a node which should be added to n8n follow these steps:
1. Create a new folder for the new node. For a service named "Example" the folder would be called: `/packages/nodes-base/nodes/Example`
1. If there is already a similar node simply copy the existing one in the new folder and rename it. If none exists yet, create a boilerplate node with [n8n-node-dev](https://github.com/n8n-io/n8n/tree/master/packages/node-dev) and copy that one in the folder.
1. If there is already a similar node, copy the existing one in the new folder and rename it. If none exists yet, create a boilerplate node with [n8n-node-dev](https://github.com/n8n-io/n8n/tree/master/packages/node-dev) and copy that one in the folder.
1. If the node needs credentials because it has to authenticate with an API or similar create new ones. Existing ones can be found in folder `/packages/nodes-base/credentials`. Also there it is the easiest to simply copy existing similar ones.
1. If the node needs credentials because it has to authenticate with an API or similar create new ones. Existing ones can be found in folder `/packages/nodes-base/credentials`. Also there it is the easiest to copy existing similar ones.
1. Add the path to the new node (and optionally credentials) to package.json of `nodes-base`. It already contains a property `n8n` with its own keys `credentials` and `nodes`.
@ -236,6 +236,6 @@ docsify serve ./docs
That we do not have any potential problems later it is sadly necessary to sign a [Contributor License Agreement](CONTRIBUTOR_LICENSE_AGREEMENT.md). That can be done literally with the push of a button.
We used the most simple one that exists. It is from [Indie Open Source](https://indieopensource.com/forms/cla) which uses plain English and is literally just a few lines long.
We used the most simple one that exists. It is from [Indie Open Source](https://indieopensource.com/forms/cla) which uses plain English and is literally only a few lines long.
A bot will automatically comment on the pull request once it got opened asking for the agreement to be signed. Before it did not get signed it is sadly not possible to merge it in.

View file

@ -215,7 +215,7 @@ Licensor: n8n GmbH
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Copyright 2020 n8n GmbH
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View file

@ -215,7 +215,7 @@ Licensor: n8n GmbH
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Copyright 2020 n8n GmbH
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View file

@ -215,7 +215,7 @@ Licensor: n8n GmbH
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Copyright 2020 n8n GmbH
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View file

@ -215,7 +215,7 @@ Licensor: n8n GmbH
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Copyright 2020 n8n GmbH
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View file

@ -215,7 +215,7 @@ Licensor: n8n GmbH
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Copyright 2020 n8n GmbH
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View file

@ -215,7 +215,7 @@ Licensor: n8n GmbH
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Copyright 2020 n8n GmbH
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View file

@ -30,7 +30,7 @@ export async function salesforceApiRequest(this: IExecuteFunctions | IExecuteSin
}
}
export async function salesforceApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string ,method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
export async function salesforceApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
const returnData: IDataObject[] = [];

View file

@ -289,7 +289,7 @@ export class Slack implements INodeType {
if (operation === 'get') {
const channel = this.getNodeParameter('channelId', i) as string;
qs.channel = channel,
responseData = await slackApiRequest.call(this, 'POST', '/conversations.info', {}, qs);
responseData = await slackApiRequest.call(this, 'POST', '/conversations.info', {}, qs);
responseData = responseData.channel;
}
//https://api.slack.com/methods/conversations.list
@ -456,7 +456,7 @@ export class Slack implements INodeType {
if (!jsonParameters) {
const attachments = this.getNodeParameter('attachments', i, []) as unknown as Attachment[];
const blocksUi = (this.getNodeParameter('blocksUi', i, []) as IDataObject).blocksValues as IDataObject[];
const blocksUi = (this.getNodeParameter('blocksUi', i, []) as IDataObject).blocksValues as IDataObject[];
// The node does save the fields data differently than the API
// expects so fix the data befre we send the request
@ -482,7 +482,7 @@ export class Slack implements INodeType {
block.block_id = blockUi.blockId as string;
block.type = blockUi.type as string;
if (block.type === 'actions') {
const elementsUi = (blockUi.elementsUi as IDataObject).elementsValues as IDataObject[];
const elementsUi = (blockUi.elementsUi as IDataObject).elementsValues as IDataObject[];
if (elementsUi) {
for (const elementUi of elementsUi) {
const element: Element = {};
@ -498,7 +498,7 @@ export class Slack implements INodeType {
text: elementUi.text as string,
type: 'plain_text',
emoji: elementUi.emoji as boolean,
};
};
if (elementUi.url) {
element.url = elementUi.url as string;
}
@ -508,13 +508,13 @@ export class Slack implements INodeType {
if (elementUi.style !== 'default') {
element.style = elementUi.style as string;
}
const confirmUi = (elementUi.confirmUi as IDataObject).confirmValue as IDataObject;
if (confirmUi) {
const confirmUi = (elementUi.confirmUi as IDataObject).confirmValue as IDataObject;
if (confirmUi) {
const confirm: Confirm = {};
const titleUi = (confirmUi.titleUi as IDataObject).titleValue as IDataObject;
const textUi = (confirmUi.textUi as IDataObject).textValue as IDataObject;
const confirmTextUi = (confirmUi.confirmTextUi as IDataObject).confirmValue as IDataObject;
const denyUi = (confirmUi.denyUi as IDataObject).denyValue as IDataObject;
const titleUi = (confirmUi.titleUi as IDataObject).titleValue as IDataObject;
const textUi = (confirmUi.textUi as IDataObject).textValue as IDataObject;
const confirmTextUi = (confirmUi.confirmTextUi as IDataObject).confirmValue as IDataObject;
const denyUi = (confirmUi.denyUi as IDataObject).denyValue as IDataObject;
const style = confirmUi.style as string;
if (titleUi) {
confirm.title = {
@ -548,13 +548,13 @@ export class Slack implements INodeType {
confirm.style = style as string;
}
element.confirm = confirm;
}
elements.push(element);
}
elements.push(element);
}
block.elements = elements;
}
} else if (block.type === 'section') {
const textUi = (blockUi.textUi as IDataObject).textValue as IDataObject;
const textUi = (blockUi.textUi as IDataObject).textValue as IDataObject;
if (textUi) {
const text: Text = {};
if (textUi.type === 'plainText') {
@ -569,7 +569,7 @@ export class Slack implements INodeType {
} else {
throw new Error('Property text must be defined');
}
const fieldsUi = (blockUi.fieldsUi as IDataObject).fieldsValues as IDataObject[];
const fieldsUi = (blockUi.fieldsUi as IDataObject).fieldsValues as IDataObject[];
if (fieldsUi) {
const fields: Text[] = [];
for (const fieldUi of fieldsUi) {
@ -589,7 +589,7 @@ export class Slack implements INodeType {
block.fields = fields;
}
}
const accessoryUi = (blockUi.accessoryUi as IDataObject).accessoriesValues as IDataObject;
const accessoryUi = (blockUi.accessoryUi as IDataObject).accessoriesValues as IDataObject;
if (accessoryUi) {
const accessory: Element = {};
if (accessoryUi.type === 'button') {
@ -608,46 +608,46 @@ export class Slack implements INodeType {
if (accessoryUi.style !== 'default') {
accessory.style = accessoryUi.style as string;
}
const confirmUi = (accessoryUi.confirmUi as IDataObject).confirmValue as IDataObject;
const confirmUi = (accessoryUi.confirmUi as IDataObject).confirmValue as IDataObject;
if (confirmUi) {
const confirm: Confirm = {};
const titleUi = (confirmUi.titleUi as IDataObject).titleValue as IDataObject;
const textUi = (confirmUi.textUi as IDataObject).textValue as IDataObject;
const confirmTextUi = (confirmUi.confirmTextUi as IDataObject).confirmValue as IDataObject;
const denyUi = (confirmUi.denyUi as IDataObject).denyValue as IDataObject;
const style = confirmUi.style as string;
if (titleUi) {
confirm.title = {
type: 'plain_text',
text: titleUi.text as string,
emoji: titleUi.emoji as boolean,
};
}
if (textUi) {
confirm.text = {
type: 'plain_text',
text: textUi.text as string,
emoji: textUi.emoji as boolean,
};
}
if (confirmTextUi) {
confirm.confirm = {
type: 'plain_text',
text: confirmTextUi.text as string,
emoji: confirmTextUi.emoji as boolean,
};
}
if (denyUi) {
confirm.deny = {
type: 'plain_text',
text: denyUi.text as string,
emoji: denyUi.emoji as boolean,
};
}
if (style !== 'default') {
confirm.style = style as string;
}
accessory.confirm = confirm;
const confirm: Confirm = {};
const titleUi = (confirmUi.titleUi as IDataObject).titleValue as IDataObject;
const textUi = (confirmUi.textUi as IDataObject).textValue as IDataObject;
const confirmTextUi = (confirmUi.confirmTextUi as IDataObject).confirmValue as IDataObject;
const denyUi = (confirmUi.denyUi as IDataObject).denyValue as IDataObject;
const style = confirmUi.style as string;
if (titleUi) {
confirm.title = {
type: 'plain_text',
text: titleUi.text as string,
emoji: titleUi.emoji as boolean,
};
}
if (textUi) {
confirm.text = {
type: 'plain_text',
text: textUi.text as string,
emoji: textUi.emoji as boolean,
};
}
if (confirmTextUi) {
confirm.confirm = {
type: 'plain_text',
text: confirmTextUi.text as string,
emoji: confirmTextUi.emoji as boolean,
};
}
if (denyUi) {
confirm.deny = {
type: 'plain_text',
text: denyUi.text as string,
emoji: denyUi.emoji as boolean,
};
}
if (style !== 'default') {
confirm.style = style as string;
}
accessory.confirm = confirm;
}
}
block.accessory = accessory;
@ -790,8 +790,8 @@ export class Slack implements INodeType {
if (binaryData) {
const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i) as string;
if (items[i].binary === undefined
//@ts-ignore
|| items[i].binary[binaryPropertyName] === undefined) {
//@ts-ignore
|| items[i].binary[binaryPropertyName] === undefined) {
throw new Error(`No binary data property "${binaryPropertyName}" does not exists on item!`);
}
body.file = {
@ -804,7 +804,7 @@ export class Slack implements INodeType {
contentType: items[i].binary[binaryPropertyName].mimeType,
}
};
responseData = await slackApiRequest.call(this, 'POST', '/files.upload', {}, qs, { 'Content-Type': 'multipart/form-data' }, { formData: body });
responseData = await slackApiRequest.call(this, 'POST', '/files.upload', {}, qs, { 'Content-Type': 'multipart/form-data' }, { formData: body });
responseData = responseData.file;
} else {
const fileContent = this.getNodeParameter('fileContent', i) as string;

View file

@ -40,21 +40,11 @@ export async function zoomApiRequest(this: IExecuteFunctions | IExecuteSingleFun
throw new Error('No credentials got returned!');
}
options.headers!.Authorization = `Bearer ${credentials.accessToken}`;
console.log("options if");
console.log(options);
//@ts-ignore
return await this.helpers.request(options);
} else {
console.log("options else");
console.log(options);
let credentials = this.getCredentials('zoomOAuth2Api');
// let oauthtoken1 = credentials!.oauthTokenData;
console.log(credentials);
console.log("credss");
//@ts-ignore
return await this.helpers.requestOAuth2.call(this, 'zoomOAuth2Api', options);

View file

@ -0,0 +1,751 @@
import {
INodeProperties,
} from 'n8n-workflow';
export const meetingOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'meeting',
],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a meeting',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a meeting',
},
{
name: 'Get',
value: 'get',
description: 'Retrieve a meeting',
},
{
name: 'Get All',
value: 'getAll',
description: 'Retrieve all meetings',
},
{
name: 'Update',
value: 'update',
description: 'Update a meeting',
}
],
default: 'create',
description: 'The operation to perform.',
}
] as INodeProperties[];
export const meetingFields = [
/* -------------------------------------------------------------------------- */
/* meeting:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'Id',
name: 'userId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'create',
],
resource: [
'meeting',
],
},
},
description: 'User ID.',
},
{
displayName: 'Additional settings',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
operation: [
'create',
],
resource: [
'meeting',
],
}
},
options: [
{
displayName: 'Meeting topic',
name: 'topic',
type: 'string',
default: '',
description: `Meeting topic.`,
},
{
displayName: 'Meeting type',
name: 'type',
type: 'options',
options: [
{
name: 'Instant Meeting',
value: 1,
},
{
name: 'Scheduled Meeting',
value: 2,
},
{
name: 'Recurring meeting with no fixed time',
value: 3,
},
{
name: 'Recurring meeting with no fixed time',
value: 8,
},
],
default: 2,
description: 'Meeting type.'
},
{
displayName: 'Start time',
name: 'startTime',
type: 'dateTime',
default: '',
description: 'Start time should be used only for scheduled or recurring meetings with fixed time',
},
{
displayName: 'Duration',
name: 'duration',
type: 'number',
default: '',
description: 'Duration.',
},
{
displayName: 'Timezone',
name: 'timeZone',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getTimezones',
},
default: '',
description: `Time zone used in the response. The default is the time zone of the calendar.`,
},
{
displayName: 'Schedule for',
name: 'scheduleFor',
type: 'string',
default: '',
description: 'Schedule meeting for someone else from your account, provide their email id.',
},
{
displayName: 'Password',
name: 'password',
type: 'string',
default: '',
description: 'Password to join the meeting with maximum 10 characters.',
},
{
displayName: 'Agenda',
name: 'agenda',
type: 'string',
default: '',
description: 'Meeting agenda.',
},
{
displayName: 'Host Meeting in China',
name: 'cn_meeting',
type: 'boolean',
default: false,
description: 'Host Meeting in China.',
},
{
displayName: 'Host Meeting in India',
name: 'in_meeting',
type: 'boolean',
default: false,
description: 'Host Meeting in India.',
},
{
displayName: 'Host Video',
name: 'host_video',
type: 'boolean',
default: false,
description: 'Start video when host joins the meeting.',
},
{
displayName: 'Participant Video',
name: 'participant_video',
type: 'boolean',
default: false,
description: 'Start video when participant joins the meeting.',
},
{
displayName: 'Join before Host',
name: 'join_before_host',
type: 'boolean',
default: false,
description: 'Allow participants to join the meeting before host starts it.',
},
{
displayName: 'Muting before entry',
name: 'mute_upon_entry',
type: 'boolean',
default: false,
description: 'Mute participants upon entry.',
},
{
displayName: 'Watermark',
name: 'watermark',
type: 'boolean',
default: false,
description: 'Adds watermark when viewing a shared screen.',
},
{
displayName: 'Alternative Hosts',
name: 'alternative_hosts',
type: 'string',
default: '',
description: 'Alternative hosts email ids.',
},
{
displayName: 'Auto recording',
name: 'auto_recording',
type: 'options',
options: [
{
name: 'Record on local',
value: 'local',
},
{
name: 'Record on cloud',
value: 'cloud',
},
{
name: 'Disabled',
value: 'none',
},
],
default: 'none',
description: 'Auto recording.',
},
{
displayName: 'Audio',
name: 'auto_recording',
type: 'options',
options: [
{
name: 'Both Telephony and VoiP',
value: 'both',
},
{
name: 'Telephony',
value: 'telephony',
},
{
name: 'VOIP',
value: 'voip',
},
],
default: 'both',
description: 'Determine how participants can join audio portion of the meeting.',
},
{
displayName: 'Registration type',
name: 'registration_type',
type: 'options',
options: [
{
name: 'Attendees register once and can attend any of the occurences',
value: 1,
},
{
name: 'Attendees need to register for every occurence',
value: 2,
},
{
name: 'Attendees register once and can choose one or more occurences to attend',
value: 3,
},
],
default: 1,
description: 'Registration type. Used for recurring meetings with fixed time only',
},
],
},
/* -------------------------------------------------------------------------- */
/* meeting:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Id',
name: 'userId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'get',
],
resource: [
'meeting',
],
},
},
description: 'User ID.',
},
/* -------------------------------------------------------------------------- */
/* meeting:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Id',
name: 'userId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'meeting',
],
},
},
description: 'User ID.',
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'meeting',
],
},
},
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: [
'meeting',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 300
},
default: 30,
description: 'How many results to return.',
},
{
displayName: 'Additional settings',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'meeting',
],
}
},
options: [
{
displayName: 'Type',
name: 'type',
type: 'options',
options: [
{
name: 'Scheduled',
value: 'scheduled',
},
{
name: 'Live',
value: 'live',
},
{
name: 'Upcoming',
value: 'upcoming',
},
],
default: 'live',
description: `Meeting type.`,
},
]
},
/* -------------------------------------------------------------------------- */
/* meeting:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'Meeting Id',
name: 'meetingId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'delete'
],
resource: [
'meeting',
],
},
},
description: 'Meeting ID.',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
operation: [
'delete',
],
resource: [
'meeting',
],
},
},
options: [
{
displayName: 'Occurence Id',
name: 'occurenceId',
type: 'string',
default: '',
description: 'Meeting occurence Id.',
},
{
displayName: 'Schedule a reminder',
name: 'scheduleForReminder',
type: 'boolean',
default: false,
description: 'Schedule a reminder via email',
},
],
},
/* -------------------------------------------------------------------------- */
/* meeting:update */
/* -------------------------------------------------------------------------- */
{
displayName: 'Meeting Id',
name: 'meetingId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'update',
],
resource: [
'meeting',
],
},
},
description: 'Meeting ID.',
},
{
displayName: 'Occurence Id',
name: 'occurenceId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'update',
],
resource: [
'meeting',
],
},
},
description: 'Occurence ID.',
},
{
displayName: 'Additional settings',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
operation: [
'update',
],
resource: [
'meeting',
],
}
},
options: [
{
displayName: 'Meeting topic',
name: 'topic',
type: 'string',
default: '',
description: `Meeting topic.`,
},
{
displayName: 'Meeting type',
name: 'type',
type: 'options',
options: [
{
name: 'Instant Meeting',
value: 1,
},
{
name: 'Scheduled Meeting',
value: 2,
},
{
name: 'Recurring meeting with no fixed time',
value: 3,
},
{
name: 'Recurring meeting with no fixed time',
value: 8,
},
],
default: 2,
description: 'Meeting type.'
},
{
displayName: 'Start time',
name: 'startTime',
type: 'dateTime',
default: '',
description: 'Start time should be used only for scheduled or recurring meetings with fixed time',
},
{
displayName: 'Duration',
name: 'duration',
type: 'number',
default: '',
description: 'Duration.',
},
{
displayName: 'Timezone',
name: 'timeZone',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getTimezones',
},
default: '',
description: `Time zone used in the response. The default is the time zone of the calendar.`,
},
{
displayName: 'Schedule for',
name: 'scheduleFor',
type: 'string',
default: '',
description: 'Schedule meeting for someone else from your account, provide their email id.',
},
{
displayName: 'Password',
name: 'password',
type: 'string',
default: '',
description: 'Password to join the meeting with maximum 10 characters.',
},
{
displayName: 'Agenda',
name: 'agenda',
type: 'string',
default: '',
description: 'Meeting agenda.',
},
{
displayName: 'Host Meeting in China',
name: 'cn_meeting',
type: 'boolean',
default: false,
description: 'Host Meeting in China.',
},
{
displayName: 'Host Meeting in India',
name: 'in_meeting',
type: 'boolean',
default: false,
description: 'Host Meeting in India.',
},
{
displayName: 'Host Video',
name: 'host_video',
type: 'boolean',
default: false,
description: 'Start video when host joins the meeting.',
},
{
displayName: 'Participant Video',
name: 'participant_video',
type: 'boolean',
default: false,
description: 'Start video when participant joins the meeting.',
},
{
displayName: 'Join before Host',
name: 'join_before_host',
type: 'boolean',
default: false,
description: 'Allow participants to join the meeting before host starts it.',
},
{
displayName: 'Muting before entry',
name: 'mute_upon_entry',
type: 'boolean',
default: false,
description: 'Mute participants upon entry.',
},
{
displayName: 'Watermark',
name: 'watermark',
type: 'boolean',
default: false,
description: 'Adds watermark when viewing a shared screen.',
},
{
displayName: 'Alternative Hosts',
name: 'alternative_hosts',
type: 'string',
default: '',
description: 'Alternative hosts email ids.',
},
{
displayName: 'Auto recording',
name: 'auto_recording',
type: 'options',
options: [
{
name: 'Record on local',
value: 'local',
},
{
name: 'Record on cloud',
value: 'cloud',
},
{
name: 'Disabled',
value: 'none',
},
],
default: 'none',
description: 'Auto recording.',
},
{
displayName: 'Audio',
name: 'auto_recording',
type: 'options',
options: [
{
name: 'Both Telephony and VoiP',
value: 'both',
},
{
name: 'Telephony',
value: 'telephony',
},
{
name: 'VOIP',
value: 'voip',
},
],
default: 'both',
description: 'Determine how participants can join audio portion of the meeting.',
},
{
displayName: 'Registration type',
name: 'registration_type',
type: 'options',
options: [
{
name: 'Attendees register once and can attend any of the occurences',
value: 1,
},
{
name: 'Attendees need to register for every occurence',
value: 2,
},
{
name: 'Attendees register once and can choose one or more occurences to attend',
value: 3,
},
],
default: 1,
description: 'Registration type. Used for recurring meetings with fixed time only',
},
],
},
] as INodeProperties[];

View file

@ -0,0 +1,407 @@
import {
INodeProperties,
} from 'n8n-workflow';
export const meetingRegistrantOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'meetingRegistrants',
],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create Meeting Registrants',
},
{
name: 'Update',
value: 'update',
description: 'Update Meeting Registrant status',
},
{
name: 'Get All',
value: 'getAll',
description: 'Retrieve all meeting registrants',
},
],
default: 'create',
description: 'The operation to perform.',
}
] as INodeProperties[];
export const meetingRegistrantFields = [
/* -------------------------------------------------------------------------- */
/* meetingRegistrants:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'Meeting Id',
name: 'meetingId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'create',
],
resource: [
'meetingRegistrants',
],
},
},
description: 'Meeting ID.',
},
{
displayName: 'Occurence Id',
name: 'occurenceId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'create',
],
resource: [
'meetingRegistrants',
],
},
},
description: 'Occurence ID.',
},
{
displayName: 'Email',
name: 'email',
type: 'string',
required: true,
default: '',
description: 'Valid email-id of registrant.',
},
{
displayName: 'First name',
name: 'firstName',
required: true,
type: 'string',
default: '',
description: 'First Name.',
},
{
displayName: 'Additional settings',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
operation: [
'get',
],
resource: [
'meetingRegistrants',
],
}
},
options: [
{
displayName: 'Last Name',
name: 'lastName',
type: 'string',
default: '',
description: 'Last Name.',
},
{
displayName: 'Address',
name: 'address',
type: 'string',
default: '',
description: 'Valid address of registrant.',
},
{
displayName: 'City',
name: 'city',
type: 'string',
default: '',
description: 'Valid city of registrant.',
},
{
displayName: 'State',
name: 'state',
type: 'string',
default: '',
description: 'Valid state of registrant.',
},
{
displayName: 'Country',
name: 'country',
type: 'string',
default: '',
description: 'Valid country of registrant.',
},
{
displayName: 'Zip code',
name: 'zip',
type: 'string',
default: '',
description: 'Valid zip-code of registrant.',
},
{
displayName: 'Phone Number',
name: 'phone',
type: 'string',
default: '',
description: 'Valid phone number of registrant.',
},
{
displayName: 'Comments',
name: 'comments',
type: 'string',
default: '',
description: 'Allows registrants to provide any questions they have.',
},
{
displayName: 'Organization',
name: 'org',
type: 'string',
default: '',
description: 'Organization of registrant.',
},
{
displayName: 'Job title',
name: 'job_title',
type: 'string',
default: '',
description: 'Job title of registrant.',
},
{
displayName: 'Purchasing time frame',
name: 'purchasing_time_frame',
type: 'options',
options: [
{
name: 'Within a month',
value: 'Within a month',
},
{
name: '1-3 months',
value: '1-3 months',
},
{
name: '4-6 months',
value: '4-6 months',
},
{
name: 'More than 6 months',
value: 'More than 6 months',
},
{
name: 'No timeframe',
value: 'No timeframe',
},
],
default: '',
description: 'Meeting type.'
},
{
displayName: 'Role in purchase process',
name: 'role_in_purchase_process',
type: 'options',
options: [
{
name: 'Decision Maker',
value: 'Decision Maker',
},
{
name: 'Evaluator/Recommender',
value: 'Evaluator/Recommender',
},
{
name: 'Influener',
value: 'Influener',
},
{
name: 'Not Involved',
value: 'Not Involved',
},
],
default: '',
description: 'Role in purchase process.'
},
],
},
/* -------------------------------------------------------------------------- */
/* meetingRegistrants:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Meeting Id',
name: 'meetingId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'meetingRegistrants',
],
},
},
description: 'Meeting ID.',
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
operation: [
'getAll',
],
resource: [
'meetingRegistrants',
],
},
},
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: [
'meetingRegistrants',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 300
},
default: 30,
description: 'How many results to return.',
},
{
displayName: 'Additional settings',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
operation: [
'get',
],
resource: [
'meetingRegistrants',
],
}
},
options: [
{
displayName: 'Occurence Id',
name: 'occurence_id',
type: 'string',
default: '',
description: `Occurence Id.`,
},
{
displayName: 'Status',
name: 'status',
type: 'options',
options: [
{
name: 'Pending',
value: 'pending',
},
{
name: 'Approved',
value: 'approved',
},
{
name: 'Denied',
value: 'denied',
},
],
default: '',
description: `Registrant Status.`,
},
]
},
/* -------------------------------------------------------------------------- */
/* meetingRegistrants:update */
/* -------------------------------------------------------------------------- */
{
displayName: 'Meeting Id',
name: 'meetingId',
type: 'number',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'update',
],
resource: [
'meetingRegistrants',
],
},
},
description: 'Meeting ID.',
},
{
displayName: 'Occurence Id',
name: 'occurenceId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'update',
],
resource: [
'meetingRegistrants',
],
},
},
description: 'Occurence ID.',
},
] as INodeProperties[];

View file

@ -3,7 +3,9 @@ import {
IDataObject,
INodeExecutionData,
INodeType,
ILoadOptionsFunctions,
INodeTypeDescription,
INodePropertyOptions,
} from 'n8n-workflow';
import {
zoomApiRequest,
@ -14,7 +16,30 @@ import {
import {
meetingOperations,
meetingFields,
} from './ZoomOperations';
} from './MeetingDescription';
import {
meetingRegistrantOperations,
meetingRegistrantFields,
} from './MeetingRegistrantDescription';
import * as moment from 'moment-timezone';
interface Settings {
host_video?: boolean;
participant_video?: boolean;
cn_meeting?: boolean;
in_meeting?: boolean;
join_before_host?: boolean;
mute_upon_entry?: boolean;
watermark?: boolean;
audio?: string;
alternative_hosts?: string;
auto_recording?: string;
registration_type?: number;
}
export class Zoom implements INodeType {
description: INodeTypeDescription = {
displayName: 'Zoom',
@ -25,31 +50,11 @@ export class Zoom implements INodeType {
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
defaults: {
name: 'Zoom',
color: '#772244'
color: '#0B6CF9'
},
icon: 'file:zoom.png',
inputs: ['main'],
outputs: ['main'],
// credentials: [
// {
// name: 'zoomApi',
// required: true,
// displayOptions: {
// show: {
// authentication: ['accessToken']
// }
// }
// },
// {
// name: 'zoomOAuth2Api',
// required: true,
// displayOptions: {
// show: {
// authentication: ['oAuth2']
// }
// }
// }
// ],
credentials: [
{
name: 'zoomApi',
@ -100,16 +105,41 @@ export class Zoom implements INodeType {
{
name: 'Meeting',
value: 'meeting'
},
{
name: 'Meeting Registrants',
value: 'meetingRegistrants'
}
],
default: 'meeting',
description: 'The resource to operate on.'
},
...meetingOperations,
...meetingFields
...meetingFields,
...meetingRegistrantOperations,
...meetingRegistrantFields,
]
};
methods = {
loadOptions: {
// Get all the timezones to display them to user so that he can select them easily
async getTimezones(
this: ILoadOptionsFunctions
): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
for (const timezone of moment.tz.names()) {
const timezoneName = timezone;
const timezoneId = timezone;
returnData.push({
name: timezoneName,
value: timezoneId
});
}
return returnData;
}
}
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
@ -120,11 +150,13 @@ export class Zoom implements INodeType {
const resource = this.getNodeParameter('resource', 0) as string;
const operation = this.getNodeParameter('operation', 0) as string;
console.log(this.getCredentials('zoomOAuth2Api'));
let body: IDataObject = {};
for (let i = 0; i < length; i++) {
qs = {};
if (resource === 'meeting') {
if (operation === 'get') {
//https://marketplace.zoom.us/docs/api-reference/zoom-api/meetings/meeting
const userId = this.getNodeParameter('userId', i) as string;
responseData = await zoomApiRequest.call(
@ -135,6 +167,283 @@ export class Zoom implements INodeType {
qs
);
}
if (operation === 'getAll') {
//https://marketplace.zoom.us/docs/api-reference/zoom-api/meetings/meetings
const userId = this.getNodeParameter('userId', i) as string;
responseData = await zoomApiRequest.call(
this,
'GET',
`/users/${userId}/meetings`,
{},
qs
);
}
if (operation === 'delete') {
//https://marketplace.zoom.us/docs/api-reference/zoom-api/meetings/meetingdelete
const meetingId = this.getNodeParameter('meetingId', i) as string;
responseData = await zoomApiRequest.call(
this,
'DELETE',
`/meetings/${meetingId}`,
{},
qs
);
responseData = { success: true };
}
if (operation === 'create') {
//https://marketplace.zoom.us/docs/api-reference/zoom-api/meetings/meetingcreate
const userId = this.getNodeParameter('userId', i) as string;
const additionalFields = this.getNodeParameter(
'additionalFields',
i
) as IDataObject;
const settings: Settings = {};
if (additionalFields.cn_meeting) {
settings.cn_meeting = additionalFields.cn_meeting as boolean;
}
if (additionalFields.in_meeting) {
settings.in_meeting = additionalFields.in_meeting as boolean;
}
if (additionalFields.join_before_host) {
settings.join_before_host = additionalFields.join_before_host as boolean;
}
if (additionalFields.mute_upon_entry) {
settings.mute_upon_entry = additionalFields.mute_upon_entry as boolean;
}
if (additionalFields.watermark) {
settings.watermark = additionalFields.watermark as boolean;
}
if (additionalFields.audio) {
settings.audio = additionalFields.audio as string;
}
if (additionalFields.alternative_hosts) {
settings.alternative_hosts = additionalFields.alternative_hosts as string;
}
if (additionalFields.participant_video) {
settings.participant_video = additionalFields.participant_video as boolean;
}
if (additionalFields.host_video) {
settings.host_video = additionalFields.host_video as boolean;
}
if (additionalFields.auto_recording) {
settings.auto_recording = additionalFields.auto_recording as string;
}
if (additionalFields.registration_type) {
settings.registration_type = additionalFields.registration_type as number;
}
body = {
settings,
};
if (additionalFields.topic) {
body.topic = additionalFields.topic as string;
}
if (additionalFields.type) {
body.type = additionalFields.type as string;
}
if (additionalFields.startTime) {
body.start_time = additionalFields.startTime as string;
}
if (additionalFields.duration) {
body.duration = additionalFields.duration as number;
}
if (additionalFields.scheduleFor) {
body.schedule_for = additionalFields.scheduleFor as string;
}
if (additionalFields.timeZone) {
body.timezone = additionalFields.timeZone as string;
}
if (additionalFields.password) {
body.password = additionalFields.password as string;
}
if (additionalFields.agenda) {
body.agenda = additionalFields.agenda as string;
}
responseData = await zoomApiRequest.call(
this,
'POST',
`/users/${userId}/meetings`,
body,
qs
);
}
if (operation === 'update') {
//https://marketplace.zoom.us/docs/api-reference/zoom-api/meetings/meetingupdate
const meetingId = this.getNodeParameter('meetingId', i) as string;
qs.occurence_id = this.getNodeParameter('occurenceId', i) as string;
const additionalFields = this.getNodeParameter(
'additionalFields',
i
) as IDataObject;
const settings: Settings = {};
if (additionalFields.cn_meeting) {
settings.cn_meeting = additionalFields.cn_meeting as boolean;
}
if (additionalFields.in_meeting) {
settings.in_meeting = additionalFields.in_meeting as boolean;
}
if (additionalFields.join_before_host) {
settings.join_before_host = additionalFields.join_before_host as boolean;
}
if (additionalFields.mute_upon_entry) {
settings.mute_upon_entry = additionalFields.mute_upon_entry as boolean;
}
if (additionalFields.watermark) {
settings.watermark = additionalFields.watermark as boolean;
}
if (additionalFields.audio) {
settings.audio = additionalFields.audio as string;
}
if (additionalFields.alternative_hosts) {
settings.alternative_hosts = additionalFields.alternative_hosts as string;
}
if (additionalFields.participant_video) {
settings.participant_video = additionalFields.participant_video as boolean;
}
if (additionalFields.host_video) {
settings.host_video = additionalFields.host_video as boolean;
}
if (additionalFields.auto_recording) {
settings.auto_recording = additionalFields.auto_recording as string;
}
if (additionalFields.registration_type) {
settings.registration_type = additionalFields.registration_type as number;
}
body = {
settings,
};
if (additionalFields.topic) {
body.topic = additionalFields.topic as string;
}
if (additionalFields.type) {
body.type = additionalFields.type as string;
}
if (additionalFields.startTime) {
body.start_time = additionalFields.startTime as string;
}
if (additionalFields.duration) {
body.duration = additionalFields.duration as number;
}
if (additionalFields.scheduleFor) {
body.schedule_for = additionalFields.scheduleFor as string;
}
if (additionalFields.timeZone) {
body.timezone = additionalFields.timeZone as string;
}
if (additionalFields.password) {
body.password = additionalFields.password as string;
}
if (additionalFields.agenda) {
body.agenda = additionalFields.agenda as string;
}
responseData = await zoomApiRequest.call(
this,
'PATCH',
`/meetings/${meetingId}`,
body,
qs
);
}
}
if (resource === 'meetingRegistrant') {
if (operation === 'create') {
//https://marketplace.zoom.us/docs/api-reference/zoom-api/meetings/meetingregistrantcreate
const meetingId = this.getNodeParameter('meetingId', i) as string;
qs.occurence_id = this.getNodeParameter('occurenceId', i) as string;
responseData = await zoomApiRequest.call(
this,
'PATCH',
`/meetings/${meetingId}/registrants`,
body,
qs
);
}
if (operation === 'getAll') {
//https://marketplace.zoom.us/docs/api-reference/zoom-api/meetings/meetingregistrants
}
if (operation === 'update') {
//https://marketplace.zoom.us/docs/api-reference/zoom-api/meetings/meetingregistrantstatus
}
}
}
if (Array.isArray(responseData)) {

View file

@ -1,71 +0,0 @@
import {
INodeProperties,
} from 'n8n-workflow';
export const meetingOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'meeting',
],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a meeting',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a meeting',
},
{
name: 'Get',
value: 'get',
description: 'Retrieve a meeting',
},
{
name: 'Get All',
value: 'getAll',
description: 'Retrieve all meetings',
},
{
name: 'Update',
value: 'update',
description: 'Update a meeting',
}
],
default: 'create',
description: 'The operation to perform.',
}
] as INodeProperties[];
export const meetingFields = [
/* -------------------------------------------------------------------------- */
/* meeting:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'User Id',
name: 'userId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: [
'get',
],
resource: [
'meeting',
],
},
},
description: 'User ID.',
},
] as INodeProperties[];

View file

@ -215,7 +215,7 @@ Licensor: n8n GmbH
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Copyright 2020 n8n GmbH
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.