diff --git a/packages/nodes-base/credentials/MicrosoftOutlookOAuth2Api.credentials.ts b/packages/nodes-base/credentials/MicrosoftOutlookOAuth2Api.credentials.ts
new file mode 100644
index 0000000000..1c8eafb074
--- /dev/null
+++ b/packages/nodes-base/credentials/MicrosoftOutlookOAuth2Api.credentials.ts
@@ -0,0 +1,22 @@
+import {
+ ICredentialType,
+ NodePropertyTypes,
+} from 'n8n-workflow';
+
+export class MicrosoftOutlookOAuth2Api implements ICredentialType {
+ name = 'microsoftOutlookOAuth2Api';
+ extends = [
+ 'microsoftOAuth2Api',
+ ];
+ displayName = 'Microsoft Outlook OAuth2 API';
+ documentationUrl = 'microsoft';
+ properties = [
+ //https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent
+ {
+ displayName: 'Scope',
+ name: 'scope',
+ type: 'hidden' as NodePropertyTypes,
+ default: 'openid offline_access Mail.ReadWrite Mail.Send MailboxSettings.Read',
+ },
+ ];
+}
diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/DraftDescription.ts b/packages/nodes-base/nodes/Microsoft/Outlook/DraftDescription.ts
new file mode 100644
index 0000000000..96a5adcff3
--- /dev/null
+++ b/packages/nodes-base/nodes/Microsoft/Outlook/DraftDescription.ts
@@ -0,0 +1,304 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const draftOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'draft',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create a new email draft',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ description: 'Delete a draft',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Get a single draft',
+ },
+ {
+ name: 'Send',
+ value: 'send',
+ description: 'Send an existing draft message',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ description: 'Update a draft',
+ },
+ ],
+ default: 'create',
+ description: 'The operation to perform.',
+ },
+] as INodeProperties[];
+
+export const draftFields = [
+ {
+ displayName: 'Message ID',
+ name: 'messageId',
+ description: 'Message ID',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'draft',
+ ],
+ operation: [
+ 'delete',
+ 'get',
+ 'send',
+ 'update',
+ ],
+ },
+ },
+ },
+
+ // draft:create
+ {
+ displayName: 'Subject',
+ name: 'subject',
+ description: 'The subject of the message.',
+ displayOptions: {
+ show: {
+ resource: [
+ 'draft',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Body Content',
+ name: 'bodyContent',
+ description: 'Message body content.',
+ type: 'string',
+ displayOptions: {
+ show: {
+ resource: [
+ 'draft',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ default: '',
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'draft',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Attachments',
+ name: 'attachments',
+ type: 'fixedCollection',
+ placeholder: 'Add Attachment',
+ default: {},
+ typeOptions: {
+ multipleValues: true,
+ },
+ options: [
+ {
+ name: 'attachments',
+ displayName: 'Attachment',
+ values: [
+ {
+ displayName: 'Binary Property Name',
+ name: 'binaryPropertyName',
+ type: 'string',
+ default: '',
+ description: 'Name of the binary property containing the data to be added to the email as an attachment',
+ },
+ ],
+ },
+ ],
+
+ },
+ {
+ displayName: 'BCC Recipients',
+ name: 'bccRecipients',
+ description: 'Email addresses of BCC recipients.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Body Content Type',
+ name: 'bodyContentType',
+ description: 'Message body content type.',
+ type: 'options',
+ options: [
+ {
+ name: 'HTML',
+ value: 'html',
+ },
+ {
+ name: 'Text',
+ value: 'Text',
+ },
+ ],
+ default: 'html',
+ },
+ {
+ displayName: 'Categories',
+ name: 'categories',
+ type: 'multiOptions',
+ typeOptions: {
+ loadOptionsMethod: 'getCategories',
+ },
+ default: [],
+ },
+ {
+ displayName: 'CC Recipients',
+ name: 'ccRecipients',
+ description: 'Email addresses of CC recipients.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Custom Headers',
+ name: 'internetMessageHeaders',
+ placeholder: 'Add Header',
+ type: 'fixedCollection',
+ typeOptions: {
+ multipleValues: true,
+ },
+ default: {},
+ options: [
+ {
+ name: 'headers',
+ displayName: 'Header',
+ values: [
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ default: '',
+ description: 'Name of the header.',
+ },
+ {
+ displayName: 'Value',
+ name: 'value',
+ type: 'string',
+ default: '',
+ description: 'Value to set for the header.',
+ },
+ ],
+ },
+ ],
+ },
+ {
+ displayName: 'From',
+ name: 'from',
+ description: 'The owner of the mailbox which the message is sent.
Must correspond to the actual mailbox used.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Importance',
+ name: 'importance',
+ description: 'The importance of the message.',
+ type: 'options',
+ options: [
+ {
+ name: 'Low',
+ value: 'Low',
+ },
+ {
+ name: 'Normal',
+ value: 'Normal',
+ },
+ {
+ name: 'High',
+ value: 'High',
+ },
+ ],
+ default: 'Low',
+ },
+ {
+ displayName: 'Read Receipt Requested',
+ name: 'isReadReceiptRequested',
+ description: 'Indicates whether a read receipt is requested for the message.',
+ type: 'boolean',
+ default: false,
+ },
+ {
+ displayName: 'Recipients',
+ name: 'toRecipients',
+ description: 'Email addresses of recipients. Multiple can be added separated by comma.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Reply To',
+ name: 'replyTo',
+ description: 'Email addresses to use when replying.',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+
+ // draft:send
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'draft',
+ ],
+ operation: [
+ 'send',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Recipients',
+ name: 'recipients',
+ description: 'Email addresses of recipients. Mutiple can be set separated by comma.',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+
+
+
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/DraftMessageSharedDescription.ts b/packages/nodes-base/nodes/Microsoft/Outlook/DraftMessageSharedDescription.ts
new file mode 100644
index 0000000000..faa72220c8
--- /dev/null
+++ b/packages/nodes-base/nodes/Microsoft/Outlook/DraftMessageSharedDescription.ts
@@ -0,0 +1,209 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const draftMessageSharedFields = [
+
+ // Get & Get All operations
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'draft',
+ 'message',
+ ],
+ operation: [
+ 'get',
+ 'getAll',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Attachments Prefix',
+ name: 'dataPropertyAttachmentsPrefixName',
+ type: 'string',
+ default: 'attachment_',
+ description: 'Prefix for name of the binary property to which to
write the attachments. An index starting with 0 will be added.
So if name is "attachment_" the first attachment is saved to "attachment_0"',
+ },
+ {
+ displayName: 'Fields',
+ name: 'fields',
+ type: 'string',
+ default: '',
+ description: 'Fields the response will contain. Multiple can be added separated by comma.',
+ },
+ {
+ displayName: 'Filter',
+ name: 'filter',
+ type: 'string',
+ default: '',
+ placeholder: 'isRead eq false',
+ description: 'Microsoft Graph API OData $filter query. Information about the syntax can be found here.',
+ },
+ ],
+ },
+
+ // Update operation
+ {
+ displayName: 'Update Fields',
+ name: 'updateFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'draft',
+ 'message',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'BCC Recipients',
+ name: 'bccRecipients',
+ description: 'Email addresses of BCC recipients.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Body Content',
+ name: 'bodyContent',
+ description: 'Message body content.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Body Content Type',
+ name: 'bodyContentType',
+ description: 'Message body content type.',
+ type: 'options',
+ options: [
+ {
+ name: 'HTML',
+ value: 'html',
+ },
+ {
+ name: 'Text',
+ value: 'Text',
+ },
+ ],
+ default: 'html',
+ },
+ {
+ displayName: 'Categories',
+ name: 'categories',
+ type: 'multiOptions',
+ typeOptions: {
+ loadOptionsMethod: 'getCategories',
+ },
+ default: [],
+ },
+ {
+ displayName: 'CC Recipients',
+ name: 'ccRecipients',
+ description: 'Email addresses of CC recipients.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Custom Headers',
+ name: 'internetMessageHeaders',
+ placeholder: 'Add Header',
+ type: 'fixedCollection',
+ typeOptions: {
+ multipleValues: true,
+ },
+ default: {},
+ options: [
+ {
+ name: 'headers',
+ displayName: 'Header',
+ values: [
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ default: '',
+ description: 'Name of the header.',
+ },
+ {
+ displayName: 'Value',
+ name: 'value',
+ type: 'string',
+ default: '',
+ description: 'Value to set for the header.',
+ },
+ ],
+ },
+ ],
+ },
+ {
+ displayName: 'From',
+ name: 'from',
+ description: 'The owner of the mailbox which the message is sent.
Must correspond to the actual mailbox used.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Importance',
+ name: 'importance',
+ description: 'The importance of the message.',
+ type: 'options',
+ options: [
+ {
+ name: 'Low',
+ value: 'Low',
+ },
+ {
+ name: 'Normal',
+ value: 'Normal',
+ },
+ {
+ name: 'High',
+ value: 'High',
+ },
+ ],
+ default: 'Low',
+ },
+ {
+ displayName: 'Read Receipt Requested',
+ name: 'isReadReceiptRequested',
+ description: 'Indicates whether a read receipt is requested for the message.',
+ type: 'boolean',
+ default: false,
+ },
+ {
+ displayName: 'Recipients',
+ name: 'toRecipients',
+ description: 'Email addresses of recipients. Multiple can be added separated by comma.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Reply To',
+ name: 'replyTo',
+ description: 'Email addresses to use when replying.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Subject',
+ name: 'subject',
+ description: 'The subject of the message.',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/FolderDescription.ts b/packages/nodes-base/nodes/Microsoft/Outlook/FolderDescription.ts
new file mode 100644
index 0000000000..61991adff0
--- /dev/null
+++ b/packages/nodes-base/nodes/Microsoft/Outlook/FolderDescription.ts
@@ -0,0 +1,311 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const folderOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'folder',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create a new mail folder in the root folder of the user\'s mailbox',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ description: 'Delete a folder',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Get a single folder details',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Get all folders under the root folder of the signed-in user',
+ },
+ {
+ name: 'Get Children',
+ value: 'getChildren',
+ description: 'Lists all child folders under the folder',
+ },
+ ],
+ default: 'create',
+ description: 'The operation to perform.',
+ },
+] as INodeProperties[];
+
+export const folderFields = [
+ {
+ displayName: 'Folder ID',
+ name: 'folderId',
+ description: 'Folder ID',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'folder',
+ ],
+ operation: [
+ 'delete',
+ 'get',
+ 'getChildren',
+ 'update',
+ ],
+ },
+ },
+ },
+ // folder:list, getChildren, listMessages
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ displayOptions: {
+ show: {
+ resource: [
+ 'folder',
+ ],
+ operation: [
+ 'getAll',
+ 'getChildren',
+ ],
+ },
+ },
+ default: false,
+ description: 'If all results should be returned or only up to a given limit.',
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ displayOptions: {
+ show: {
+ resource: [
+ 'folder',
+ ],
+ operation: [
+ 'getAll',
+ 'getChildren',
+ ],
+ returnAll: [false],
+ },
+ },
+ typeOptions: {
+ minValue: 1,
+ maxValue: 500,
+ },
+ default: 100,
+ description: 'How many results to return.',
+ },
+ // folder:create
+ {
+ displayName: 'Type',
+ name: 'folderType',
+ description: 'Folder Type',
+ type: 'options',
+ options: [
+ {
+ name: 'Folder',
+ value: 'folder',
+ },
+ {
+ name: 'Search Folder',
+ value: 'searchFolder',
+ },
+ ],
+ displayOptions: {
+ show: {
+ resource: [
+ 'folder',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ default: 'folder',
+ },
+ {
+ displayName: 'Display Name',
+ name: 'displayName',
+ description: 'Name of the folder.',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'folder',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Include Nested Folders',
+ name: 'includeNestedFolders',
+ description: 'Include child folders in the search.',
+ type: 'boolean',
+ default: false,
+ displayOptions: {
+ show: {
+ resource: [
+ 'folder',
+ ],
+ operation: [
+ 'create',
+ ],
+ folderType: [
+ 'searchFolder',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Source Folder IDs',
+ name: 'sourceFolderIds',
+ description: 'The mailbox folders that should be mined.',
+ type: 'string',
+ typeOptions: {
+ multipleValues: true,
+ },
+ default: [],
+ displayOptions: {
+ show: {
+ resource: [
+ 'folder',
+ ],
+ operation: [
+ 'create',
+ ],
+ folderType: [
+ 'searchFolder',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Filter Query',
+ name: 'filterQuery',
+ description: 'The OData query to filter the messages.',
+ type: 'string',
+ default: '',
+ required: true,
+ displayOptions: {
+ show: {
+ resource: [
+ 'folder',
+ ],
+ operation: [
+ 'create',
+ ],
+ folderType: [
+ 'searchFolder',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'folder',
+ ],
+ operation: [
+ 'get',
+ 'getAll',
+ 'getChildren',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Fields',
+ name: 'fields',
+ type: 'string',
+ default: '',
+ description: 'Fields the response will contain. Multiple can be added separated by ,.',
+ },
+ {
+ displayName: 'Filter',
+ name: 'filter',
+ type: 'string',
+ default: '',
+ description: 'Microsoft Graph API OData $filter query.',
+ },
+ ],
+ },
+
+ // folder:update
+ {
+ displayName: 'Update Fields',
+ name: 'updateFields',
+ description: 'Fields to update.',
+ type: 'collection',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'folder',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Display Name',
+ name: 'displayName',
+ description: 'Name of the folder.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Filter Query',
+ name: 'filterQuery',
+ description: 'The OData query to filter the messages. Only for search folders.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Include Nested Folders',
+ name: 'includeNestedFolders',
+ description: 'Include child folders in the search. Only for search folders.',
+ type: 'boolean',
+ default: false,
+ },
+ {
+ displayName: 'Source Folder IDs',
+ name: 'sourceFolderIds',
+ description: 'The mailbox folders that should be mined. Only for search folders.',
+ type: 'string',
+ typeOptions: {
+ multipleValues: true,
+ },
+ default: [],
+ },
+ ],
+ },
+
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/FolderMessageDecription.ts b/packages/nodes-base/nodes/Microsoft/Outlook/FolderMessageDecription.ts
new file mode 100644
index 0000000000..3fdf7fd146
--- /dev/null
+++ b/packages/nodes-base/nodes/Microsoft/Outlook/FolderMessageDecription.ts
@@ -0,0 +1,122 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const folderMessageOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'folderMessage',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Get all the messages in a folder',
+ },
+ ],
+ default: 'create',
+ description: 'The operation to perform.',
+ },
+] as INodeProperties[];
+
+export const folderMessageFields = [
+ {
+ displayName: 'Folder ID',
+ name: 'folderId',
+ description: 'Folder ID',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'folderMessage',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ displayOptions: {
+ show: {
+ resource: [
+ 'folderMessage',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ default: false,
+ description: 'If all results should be returned or only up to a given limit.',
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ displayOptions: {
+ show: {
+ resource: [
+ 'folderMessage',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ 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: [
+ 'folderMessage',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Fields',
+ name: 'fields',
+ type: 'string',
+ default: '',
+ description: 'Fields the response will contain. Multiple can be added separated by ,.',
+ },
+ {
+ displayName: 'Filter',
+ name: 'filter',
+ type: 'string',
+ default: '',
+ description: 'Microsoft Graph API OData $filter query.',
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/GenericFunctions.ts b/packages/nodes-base/nodes/Microsoft/Outlook/GenericFunctions.ts
new file mode 100644
index 0000000000..fa190a8afd
--- /dev/null
+++ b/packages/nodes-base/nodes/Microsoft/Outlook/GenericFunctions.ts
@@ -0,0 +1,175 @@
+import {
+ OptionsWithUri,
+} from 'request';
+
+import {
+ IExecuteFunctions,
+ IExecuteSingleFunctions,
+ ILoadOptionsFunctions,
+} from 'n8n-core';
+
+import {
+ IDataObject,
+ INodeExecutionData,
+} from 'n8n-workflow';
+
+export async function microsoftApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, headers: IDataObject = {}, option: IDataObject = { json: true }): Promise { // tslint:disable-line:no-any
+ const options: OptionsWithUri = {
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ method,
+ body,
+ qs,
+ uri: uri || `https://graph.microsoft.com/v1.0/me${resource}`,
+ };
+ try {
+ Object.assign(options, option);
+
+ if (Object.keys(headers).length !== 0) {
+ options.headers = Object.assign({}, options.headers, headers);
+ }
+
+ if (Object.keys(body).length === 0) {
+ delete options.body;
+ }
+
+ //@ts-ignore
+ return await this.helpers.requestOAuth2.call(this, 'microsoftOutlookOAuth2Api', options);
+ } catch (error) {
+ if (error.response && error.response.body && error.response.body.error && error.response.body.error.message) {
+ // Try to return the error prettier
+ throw new Error(`Microsoft error response [${error.statusCode}]: ${error.response.body.error.message}`);
+ }
+ throw error;
+ }
+}
+
+export async function microsoftApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, method: string, endpoint: string, body: any = {}, query: IDataObject = {}, headers: IDataObject = {}): Promise { // tslint:disable-line:no-any
+
+ const returnData: IDataObject[] = [];
+
+ let responseData;
+ let uri: string | undefined;
+ query['$top'] = 100;
+
+ do {
+ responseData = await microsoftApiRequest.call(this, method, endpoint, body, query, uri, headers);
+ uri = responseData['@odata.nextLink'];
+ returnData.push.apply(returnData, responseData[propertyName]);
+ } while (
+ responseData['@odata.nextLink'] !== undefined
+ );
+
+ return returnData;
+}
+
+export async function microsoftApiRequestAllItemsSkip(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, method: string, endpoint: string, body: any = {}, query: IDataObject = {}, headers: IDataObject = {}): Promise { // tslint:disable-line:no-any
+
+ const returnData: IDataObject[] = [];
+
+ let responseData;
+ query['$top'] = 100;
+ query['$skip'] = 0;
+
+ do {
+ responseData = await microsoftApiRequest.call(this, method, endpoint, body, query, undefined, headers);
+ query['$skip'] += query['$top'];
+ returnData.push.apply(returnData, responseData[propertyName]);
+ } while (
+ responseData['value'].length !== 0
+ );
+
+ return returnData;
+}
+
+export function makeRecipient(email: string) {
+ return {
+ emailAddress: {
+ address: email,
+ },
+ };
+}
+
+export function createMessage(fields: IDataObject) {
+ const message: IDataObject = {};
+
+ // Create body object
+ if (fields.bodyContent || fields.bodyContentType) {
+ const bodyObject = {
+ content: fields.bodyContent,
+ contentType: fields.bodyContentType,
+ };
+
+ message['body'] = bodyObject;
+ delete fields['bodyContent'];
+ delete fields['bodyContentType'];
+ }
+
+ // Handle custom headers
+ if ('internetMessageHeaders' in fields && 'headers' in (fields.internetMessageHeaders as IDataObject)) {
+ fields.internetMessageHeaders = (fields.internetMessageHeaders as IDataObject).headers;
+ }
+
+ // Handle recipient fields
+ ['bccRecipients', 'ccRecipients', 'replyTo', 'sender', 'toRecipients'].forEach(key => {
+ if (Array.isArray(fields[key])) {
+ fields[key] = (fields[key] as string[]).map(email => makeRecipient(email));
+ } else if (fields[key] !== undefined) {
+ fields[key] = (fields[key] as string).split(',').map((recipient: string) => makeRecipient(recipient));
+ }
+ });
+
+ ['from', 'sender'].forEach(key => {
+ if (fields[key] !== undefined) {
+ fields[key] = makeRecipient(fields[key] as string);
+ }
+ });
+
+
+ Object.assign(message, fields);
+
+ return message;
+}
+
+export async function downloadAttachments(this: IExecuteFunctions, messages: IDataObject[] | IDataObject, prefix: string) {
+ const elements: INodeExecutionData[] = [];
+ if (!Array.isArray(messages)) {
+ messages = [messages];
+ }
+ for (const message of messages) {
+ const element: INodeExecutionData = {
+ json: message,
+ binary: {},
+ };
+ if (message.hasAttachments === true) {
+ const attachments = await microsoftApiRequestAllItems.call(
+ this,
+ 'value',
+ 'GET',
+ `/messages/${message.id}/attachments`,
+ {},
+ );
+ for (const [index, attachment] of attachments.entries()) {
+ const response = await microsoftApiRequest.call(
+ this,
+ 'GET',
+ `/messages/${message.id}/attachments/${attachment.id}/$value`,
+ undefined,
+ {},
+ undefined,
+ {},
+ { encoding: null, resolveWithFullResponse: true },
+ );
+
+ const data = Buffer.from(response.body as string, 'utf8');
+ element.binary![`${prefix}${index}`] = await this.helpers.prepareBinaryData(data as unknown as Buffer, attachment.name, attachment.contentType);
+ }
+ }
+ if (Object.keys(element.binary!).length === 0) {
+ delete element.binary;
+ }
+ elements.push(element);
+ }
+ return elements;
+}
diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/MessageAttachmentDescription.ts b/packages/nodes-base/nodes/Microsoft/Outlook/MessageAttachmentDescription.ts
new file mode 100644
index 0000000000..61b375f63f
--- /dev/null
+++ b/packages/nodes-base/nodes/Microsoft/Outlook/MessageAttachmentDescription.ts
@@ -0,0 +1,216 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const messageAttachmentOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'messageAttachment',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Add',
+ value: 'add',
+ description: 'Add an attachment to a message',
+ },
+ {
+ name: 'Download',
+ value: 'download',
+ description: 'Download attachment content',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Get an attachment from a message',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Get all the message\'s attachments',
+ },
+ ],
+ default: 'add',
+ description: 'The operation to perform.',
+ },
+] as INodeProperties[];
+
+export const messageAttachmentFields = [
+ {
+ displayName: 'Message ID',
+ name: 'messageId',
+ description: 'Message ID',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'messageAttachment',
+ ],
+ operation: [
+ 'add',
+ 'download',
+ 'get',
+ 'getAll',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Attachment ID',
+ name: 'attachmentId',
+ description: 'Attachment ID',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'messageAttachment',
+ ],
+ operation: [
+ 'download',
+ 'get',
+ ],
+ },
+ },
+ },
+
+ // messageAttachment:getAll, messageAttachment:listAttachments
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ displayOptions: {
+ show: {
+ resource: [
+ 'messageAttachment',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ default: false,
+ description: 'If all results should be returned or only up to a given limit.',
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ displayOptions: {
+ show: {
+ resource: [
+ 'messageAttachment',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ typeOptions: {
+ minValue: 1,
+ maxValue: 500,
+ },
+ default: 100,
+ description: 'How many results to return.',
+ },
+
+
+ // messageAttachment:create, messageAttachment:update, messageAttachment:send
+
+ // File operations
+ {
+ displayName: 'Binary Property',
+ name: 'binaryPropertyName',
+ description: 'Name of the binary property to which to
write the data of the read file.',
+ type: 'string',
+ required: true,
+ default: 'data',
+ displayOptions: {
+ show: {
+ resource: [
+ 'messageAttachment',
+ ],
+ operation: [
+ 'add',
+ 'download',
+ ],
+ },
+ },
+ },
+
+ // messageAttachment:add
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'messageAttachment',
+ ],
+ operation: [
+ 'add',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'File Name',
+ name: 'fileName',
+ description: 'Filename of the attachment. If not set will the file-name of the binary property be used, if it exists.',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+
+ // Get & Get All operations
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'messageAttachment',
+ ],
+ operation: [
+ 'get',
+ 'getAll',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Fields',
+ name: 'fields',
+ type: 'string',
+ default: '',
+ description: 'Fields the response will contain. Multiple can be added separated by ,.',
+ },
+ {
+ displayName: 'Filter',
+ name: 'filter',
+ type: 'string',
+ default: '',
+ description: 'Microsoft Graph API OData $filter query.',
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/MessageDescription.ts b/packages/nodes-base/nodes/Microsoft/Outlook/MessageDescription.ts
new file mode 100644
index 0000000000..d45f1bae17
--- /dev/null
+++ b/packages/nodes-base/nodes/Microsoft/Outlook/MessageDescription.ts
@@ -0,0 +1,633 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const messageOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'message',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Delete',
+ value: 'delete',
+ description: 'Delete a message',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Get a single message',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Get all messages in the signed-in user\'s mailbox',
+ },
+ {
+ name: 'Get MIME Content',
+ value: 'getMime',
+ description: 'Get MIME content of a message',
+ },
+ {
+ name: 'Reply',
+ value: 'reply',
+ description: 'Create reply to a message',
+ },
+ {
+ name: 'Send',
+ value: 'send',
+ description: 'Send a message',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ description: 'Update a message',
+ },
+ ],
+ default: 'send',
+ description: 'The operation to perform.',
+ },
+] as INodeProperties[];
+
+export const messageFields = [
+ {
+ displayName: 'Message ID',
+ name: 'messageId',
+ description: 'Message ID',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'message',
+ ],
+ operation: [
+ 'addAttachment',
+ 'delete',
+ 'get',
+ 'getAttachment',
+ 'getMime',
+ 'update',
+ 'reply',
+ ],
+ },
+ },
+ },
+
+ // message:reply
+ {
+ displayName: 'Reply Type',
+ name: 'replyType',
+ type: 'options',
+ options: [
+ {
+ name: 'Reply',
+ value: 'reply',
+ },
+ {
+ name: 'Reply All',
+ value: 'replyAll',
+ },
+ ],
+ default: 'reply',
+ required: true,
+ displayOptions: {
+ show: {
+ resource: [
+ 'message',
+ ],
+ operation: [
+ 'reply',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Comment',
+ name: 'comment',
+ description: 'A comment to include. Can be an empty string.',
+ displayOptions: {
+ show: {
+ resource: [
+ 'message',
+ ],
+ operation: [
+ 'reply',
+ ],
+ },
+ },
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Send',
+ name: 'send',
+ description: 'Send the reply message directly. If not set, it will be saved as draft.',
+ displayOptions: {
+ show: {
+ resource: [
+ 'message',
+ ],
+ operation: [
+ 'reply',
+ ],
+ },
+ },
+ type: 'boolean',
+ default: true,
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'message',
+ ],
+ operation: [
+ 'reply',
+ ],
+ replyType: [
+ 'reply',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Attachments',
+ name: 'attachments',
+ type: 'fixedCollection',
+ placeholder: 'Add Attachment',
+ default: {},
+ typeOptions: {
+ multipleValues: true,
+ },
+ options: [
+ {
+ name: 'attachments',
+ displayName: 'Attachment',
+ values: [
+ {
+ displayName: 'Binary Property Name',
+ name: 'binaryPropertyName',
+ type: 'string',
+ default: '',
+ description: 'Name of the binary property containing the data to be added to the email as an attachment',
+ },
+ ],
+ },
+ ],
+
+ },
+ {
+ displayName: 'BCC Recipients',
+ name: 'bccRecipients',
+ description: 'Email addresses of BCC recipients.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Body Content',
+ name: 'bodyContent',
+ description: 'Message body content.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Body Content Type',
+ name: 'bodyContentType',
+ description: 'Message body content type.',
+ type: 'options',
+ options: [
+ {
+ name: 'HTML',
+ value: 'html',
+ },
+ {
+ name: 'Text',
+ value: 'Text',
+ },
+ ],
+ default: 'html',
+ },
+ {
+ displayName: 'CC Recipients',
+ name: 'ccRecipients',
+ description: 'Email addresses of CC recipients.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Custom Headers',
+ name: 'internetMessageHeaders',
+ placeholder: 'Add Header',
+ type: 'fixedCollection',
+ typeOptions: {
+ multipleValues: true,
+ },
+ default: {},
+ options: [
+ {
+ name: 'headers',
+ displayName: 'Header',
+ values: [
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ default: '',
+ description: 'Name of the header.',
+ },
+ {
+ displayName: 'Value',
+ name: 'value',
+ type: 'string',
+ default: '',
+ description: 'Value to set for the header.',
+ },
+ ],
+ },
+ ],
+ },
+ {
+ displayName: 'From',
+ name: 'from',
+ description: 'The owner of the mailbox which the message is sent.
Must correspond to the actual mailbox used.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Importance',
+ name: 'importance',
+ description: 'The importance of the message.',
+ type: 'options',
+ options: [
+ {
+ name: 'Low',
+ value: 'Low',
+ },
+ {
+ name: 'Normal',
+ value: 'Normal',
+ },
+ {
+ name: 'High',
+ value: 'High',
+ },
+ ],
+ default: 'Low',
+ },
+ {
+ displayName: 'Read Receipt Requested',
+ name: 'isReadReceiptRequested',
+ description: 'Indicates whether a read receipt is requested for the message.',
+ type: 'boolean',
+ default: false,
+ },
+ {
+ displayName: 'Recipients',
+ name: 'toRecipients',
+ description: 'Email addresses of recipients. Multiple can be added separated by comma.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Reply To',
+ name: 'replyTo',
+ description: 'Email addresses to use when replying.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Subject',
+ name: 'subject',
+ description: 'The subject of the message.',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+
+ // message:getAll
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ displayOptions: {
+ show: {
+ resource: [
+ 'message',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ default: false,
+ description: 'If all results should be returned or only up to a given limit.',
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ displayOptions: {
+ show: {
+ resource: [
+ 'message',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ typeOptions: {
+ minValue: 1,
+ maxValue: 500,
+ },
+ default: 100,
+ description: 'How many results to return.',
+ },
+
+ // message:create, message:update, message:send
+ {
+ displayName: 'Subject',
+ name: 'subject',
+ description: 'The subject of the message.',
+ displayOptions: {
+ show: {
+ resource: [
+ 'message',
+ ],
+ operation: [
+ 'create',
+ 'send',
+ ],
+ },
+ },
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Body Content',
+ name: 'bodyContent',
+ description: 'Message body content.',
+ type: 'string',
+ displayOptions: {
+ show: {
+ resource: [
+ 'message',
+ ],
+ operation: [
+ 'create',
+ 'send',
+ ],
+ },
+ },
+ default: '',
+ },
+ {
+ displayName: 'Recipients',
+ name: 'toRecipients',
+ description: 'Email addresses of recipients. Multiple can be added separated by comma.',
+ type: 'string',
+ displayOptions: {
+ show: {
+ resource: [
+ 'message',
+ ],
+ operation: [
+ 'send',
+ ],
+ },
+ },
+ required: true,
+ default: '',
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'message',
+ ],
+ operation: [
+ 'send',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Attachments',
+ name: 'attachments',
+ type: 'fixedCollection',
+ placeholder: 'Add Attachment',
+ default: {},
+ typeOptions: {
+ multipleValues: true,
+ },
+ options: [
+ {
+ name: 'attachments',
+ displayName: 'Attachment',
+ values: [
+ {
+ displayName: 'Binary Property Name',
+ name: 'binaryPropertyName',
+ type: 'string',
+ default: '',
+ description: 'Name of the binary property containing the data to be added to the email as an attachment',
+ },
+ ],
+ },
+ ],
+
+ },
+ {
+ displayName: 'BCC Recipients',
+ name: 'bccRecipients',
+ description: 'Email addresses of BCC recipients.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Body Content Type',
+ name: 'bodyContentType',
+ description: 'Message body content type.',
+ type: 'options',
+ options: [
+ {
+ name: 'HTML',
+ value: 'html',
+ },
+ {
+ name: 'Text',
+ value: 'Text',
+ },
+ ],
+ default: 'html',
+ },
+ {
+ displayName: 'Categories',
+ name: 'categories',
+ type: 'multiOptions',
+ typeOptions: {
+ loadOptionsMethod: 'getCategories',
+ },
+ default: [],
+ },
+ {
+ displayName: 'CC Recipients',
+ name: 'ccRecipients',
+ description: 'Email addresses of CC recipients.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Custom Headers',
+ name: 'internetMessageHeaders',
+ placeholder: 'Add Header',
+ type: 'fixedCollection',
+ typeOptions: {
+ multipleValues: true,
+ },
+ default: {},
+ options: [
+ {
+ name: 'headers',
+ displayName: 'Header',
+ values: [
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ default: '',
+ description: 'Name of the header.',
+ },
+ {
+ displayName: 'Value',
+ name: 'value',
+ type: 'string',
+ default: '',
+ description: 'Value to set for the header.',
+ },
+ ],
+ },
+ ],
+ },
+ {
+ displayName: 'From',
+ name: 'from',
+ description: 'The owner of the mailbox which the message is sent.
Must correspond to the actual mailbox used.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Importance',
+ name: 'importance',
+ description: 'The importance of the message.',
+ type: 'options',
+ options: [
+ {
+ name: 'Low',
+ value: 'Low',
+ },
+ {
+ name: 'Normal',
+ value: 'Normal',
+ },
+ {
+ name: 'High',
+ value: 'High',
+ },
+ ],
+ default: 'Low',
+ },
+ {
+ displayName: 'Read Receipt Requested',
+ name: 'isReadReceiptRequested',
+ description: 'Indicates whether a read receipt is requested for the message.',
+ type: 'boolean',
+ default: false,
+ },
+ {
+ displayName: 'Recipients',
+ name: 'toRecipients',
+ description: 'Email addresses of recipients. Multiple can be added separated by comma.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Reply To',
+ name: 'replyTo',
+ description: 'Email addresses to use when replying.',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Save To Sent Items',
+ name: 'saveToSentItems',
+ description: 'Indicates whether to save the message in Sent Items.',
+ type: 'boolean',
+ default: true,
+ },
+ ],
+ },
+
+ // File operations
+ {
+ displayName: 'Binary Property',
+ name: 'binaryPropertyName',
+ description: 'Name of the binary property to which to
write the data of the read file.',
+ type: 'string',
+ required: true,
+ default: 'data',
+ displayOptions: {
+ show: {
+ resource: [
+ 'message',
+ ],
+ operation: [
+ 'getMime',
+ ],
+ },
+ },
+ },
+
+ // message:move
+ {
+ displayName: 'Folder ID',
+ name: 'folderId',
+ description: 'Folder ID',
+ type: 'string',
+ default: '',
+ required: true,
+ displayOptions: {
+ show: {
+ resource: [
+ 'message',
+ ],
+ operation: [
+ 'move',
+ ],
+ },
+ },
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/MicrosoftOutlook.node.ts b/packages/nodes-base/nodes/Microsoft/Outlook/MicrosoftOutlook.node.ts
new file mode 100644
index 0000000000..73e87bc22a
--- /dev/null
+++ b/packages/nodes-base/nodes/Microsoft/Outlook/MicrosoftOutlook.node.ts
@@ -0,0 +1,961 @@
+import {
+ IExecuteFunctions,
+} from 'n8n-core';
+
+import {
+ IBinaryKeyData,
+ IDataObject,
+ ILoadOptionsFunctions,
+ INodeExecutionData,
+ INodePropertyOptions,
+ INodeType,
+ INodeTypeDescription,
+} from 'n8n-workflow';
+
+import {
+ createMessage,
+ downloadAttachments,
+ makeRecipient,
+ microsoftApiRequest,
+ microsoftApiRequestAllItems
+} from './GenericFunctions';
+
+import {
+ draftFields,
+ draftOperations,
+} from './DraftDescription';
+
+import {
+ draftMessageSharedFields,
+} from './DraftMessageSharedDescription';
+
+import {
+ messageFields,
+ messageOperations,
+} from './MessageDescription';
+
+import {
+ messageAttachmentFields,
+ messageAttachmentOperations,
+} from './MessageAttachmentDescription';
+
+import {
+ folderFields,
+ folderOperations,
+} from './FolderDescription';
+
+import {
+ folderMessageFields,
+ folderMessageOperations,
+} from './FolderMessageDecription';
+
+export class MicrosoftOutlook implements INodeType {
+ description: INodeTypeDescription = {
+ displayName: 'Microsoft Outlook',
+ name: 'microsoftOutlook',
+ group: ['transform'],
+ icon: 'file:outlook.svg',
+ version: 1,
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
+ description: 'Consume Microsoft Outlook API',
+ defaults: {
+ name: 'Microsoft Outlook',
+ color: '#3a71b5',
+ },
+ inputs: ['main'],
+ outputs: ['main'],
+ credentials: [
+ {
+ name: 'microsoftOutlookOAuth2Api',
+ required: true,
+ },
+ ],
+ properties: [
+ {
+ displayName: 'Resource',
+ name: 'resource',
+ type: 'options',
+ default: 'message',
+ options: [
+ {
+ name: 'Draft',
+ value: 'draft',
+ },
+ {
+ name: 'Folder',
+ value: 'folder',
+ },
+ {
+ name: 'Folder Message',
+ value: 'folderMessage',
+ },
+ {
+ name: 'Message',
+ value: 'message',
+ },
+ {
+ name: 'Message Attachment',
+ value: 'messageAttachment',
+ },
+ ],
+ },
+ // Draft
+ ...draftOperations,
+ ...draftFields,
+ // Message
+ ...messageOperations,
+ ...messageFields,
+ // Message Attachment
+ ...messageAttachmentOperations,
+ ...messageAttachmentFields,
+ // Folder
+ ...folderOperations,
+ ...folderFields,
+ // Folder Message
+ ...folderMessageOperations,
+ ...folderMessageFields,
+
+ // Draft & Message
+ ...draftMessageSharedFields
+ ],
+ };
+
+ methods = {
+ loadOptions: {
+ // Get all the categories to display them to user so that he can
+ // select them easily
+ async getCategories(this: ILoadOptionsFunctions): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ const categories = await microsoftApiRequestAllItems.call(this, 'value', 'GET', '/outlook/masterCategories');
+ for (const category of categories) {
+ returnData.push({
+ name: category.displayName as string,
+ value: category.id as string,
+ });
+ }
+ return returnData;
+ },
+ },
+ };
+
+ async execute(this: IExecuteFunctions): Promise {
+ 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;
+
+ if (['draft', 'message'].includes(resource)) {
+ if (operation === 'delete') {
+ for (let i = 0; i < length; i++) {
+ const messageId = this.getNodeParameter('messageId', i) as string;
+ responseData = await microsoftApiRequest.call(
+ this,
+ 'DELETE',
+ `/messages/${messageId}`,
+ );
+
+ returnData.push({ success: true });
+ }
+ }
+
+ if (operation === 'get') {
+ for (let i = 0; i < length; i++) {
+ const messageId = this.getNodeParameter('messageId', i) as string;
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+
+ if (additionalFields.fields) {
+ qs['$select'] = additionalFields.fields;
+ }
+
+ if (additionalFields.filter) {
+ qs['$filter'] = additionalFields.filter;
+ }
+
+ responseData = await microsoftApiRequest.call(
+ this,
+ 'GET',
+ `/messages/${messageId}`,
+ undefined,
+ qs,
+ );
+
+ if (additionalFields.dataPropertyAttachmentsPrefixName) {
+ const prefix = additionalFields.dataPropertyAttachmentsPrefixName as string;
+ const data = await downloadAttachments.call(this, responseData, prefix);
+ returnData.push.apply(returnData, data as unknown as IDataObject[]);
+ } else {
+ returnData.push(responseData);
+ }
+
+ if (additionalFields.dataPropertyAttachmentsPrefixName) {
+ return [returnData as INodeExecutionData[]];
+ }
+ }
+ }
+
+ if (operation === 'update') {
+ for (let i = 0; i < length; i++) {
+ const messageId = this.getNodeParameter('messageId', i) as string;
+
+ const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
+
+ // Create message from optional fields
+ const body: IDataObject = createMessage(updateFields);
+
+ responseData = await microsoftApiRequest.call(
+ this,
+ 'PATCH',
+ `/messages/${messageId}`,
+ body,
+ {},
+ );
+ returnData.push(responseData);
+ }
+ }
+ }
+
+ if (resource === 'draft') {
+
+ if (operation === 'create') {
+ for (let i = 0; i < length; i++) {
+
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+
+ const subject = this.getNodeParameter('subject', i) as string;
+
+ const bodyContent = this.getNodeParameter('bodyContent', i, '') as string;
+
+ additionalFields.subject = subject;
+
+ additionalFields.bodyContent = bodyContent || ' ';
+
+ // Create message object from optional fields
+ const body: IDataObject = createMessage(additionalFields);
+
+ if (additionalFields.attachments) {
+ const attachments = (additionalFields.attachments as IDataObject).attachments as IDataObject[];
+
+ // // Handle attachments
+ body['attachments'] = attachments.map(attachment => {
+ const binaryPropertyName = attachment.binaryPropertyName as string;
+
+ if (items[i].binary === undefined) {
+ throw new Error('No binary data exists on item!');
+ }
+ //@ts-ignore
+ if (items[i].binary[binaryPropertyName] === undefined) {
+ throw new Error(`No binary data property "${binaryPropertyName}" does not exists on item!`);
+ }
+
+ const binaryData = (items[i].binary as IBinaryKeyData)[binaryPropertyName];
+ return {
+ '@odata.type': '#microsoft.graph.fileAttachment',
+ name: binaryData.fileName,
+ contentBytes: binaryData.data,
+ };
+ });
+ }
+
+ responseData = await microsoftApiRequest.call(
+ this,
+ 'POST',
+ `/messages`,
+ body,
+ {},
+ );
+
+ returnData.push(responseData);
+ }
+ }
+
+ if (operation === 'send') {
+ for (let i = 0; i < length; i++) {
+ const messageId = this.getNodeParameter('messageId', i);
+ const additionalFields = this.getNodeParameter('additionalFields', i, {}) as IDataObject;
+
+ if (additionalFields && additionalFields.recipients) {
+ const recipients = ((additionalFields.recipients as string).split(',') as string[]).filter(email => !!email);
+ if (recipients.length !== 0) {
+ await microsoftApiRequest.call(
+ this,
+ 'PATCH',
+ `/messages/${messageId}`,
+ { toRecipients: recipients.map((recipient: string) => makeRecipient(recipient)) },
+ );
+ }
+ }
+
+ responseData = await microsoftApiRequest.call(
+ this,
+ 'POST',
+ `/messages/${messageId}/send`,
+ );
+
+ returnData.push({ success: true });
+ }
+ }
+ }
+
+ if (resource === 'message') {
+
+ if (operation === 'reply') {
+ for (let i = 0; i < length; i++) {
+ const messageId = this.getNodeParameter('messageId', i) as string;
+ const replyType = this.getNodeParameter('replyType', i) as string;
+ const comment = this.getNodeParameter('comment', i) as string;
+ const send = this.getNodeParameter('send', i, false) as boolean;
+ const additionalFields = this.getNodeParameter('additionalFields', i, {}) as IDataObject;
+
+ const body: IDataObject = {};
+
+ let action = 'createReply';
+ if (replyType === 'replyAll') {
+ body.comment = comment;
+ action = 'createReplyAll';
+ } else {
+ body.comment = comment;
+ body.message = {};
+ Object.assign(body.message, createMessage(additionalFields));
+ //@ts-ignore
+ delete body.message.attachments;
+ }
+
+ responseData = await microsoftApiRequest.call(
+ this,
+ 'POST',
+ `/messages/${messageId}/${action}`,
+ body,
+ );
+
+ if (additionalFields.attachments) {
+ const attachments = (additionalFields.attachments as IDataObject).attachments as IDataObject[];
+ // // Handle attachments
+ const data = attachments.map(attachment => {
+ const binaryPropertyName = attachment.binaryPropertyName as string;
+
+ if (items[i].binary === undefined) {
+ throw new Error('No binary data exists on item!');
+ }
+ //@ts-ignore
+ if (items[i].binary[binaryPropertyName] === undefined) {
+ throw new Error(`No binary data property "${binaryPropertyName}" does not exists on item!`);
+ }
+
+ const binaryData = (items[i].binary as IBinaryKeyData)[binaryPropertyName];
+ return {
+ '@odata.type': '#microsoft.graph.fileAttachment',
+ name: binaryData.fileName,
+ contentBytes: binaryData.data,
+ };
+ });
+
+ for (const attachment of data) {
+ await microsoftApiRequest.call(
+ this,
+ 'POST',
+ `/messages/${responseData.id}/attachments`,
+ attachment,
+ {},
+ );
+ }
+ }
+
+ if (send === true) {
+ await microsoftApiRequest.call(
+ this,
+ 'POST',
+ `/messages/${responseData.id}/send`,
+ );
+ }
+
+ returnData.push(responseData);
+ }
+ }
+
+ if (operation === 'getMime') {
+ for (let i = 0; i < length; i++) {
+ const messageId = this.getNodeParameter('messageId', i) as string;
+ const dataPropertyNameDownload = this.getNodeParameter('binaryPropertyName', i) as string;
+ const response = await microsoftApiRequest.call(
+ this,
+ 'GET',
+ `/messages/${messageId}/$value`,
+ undefined,
+ {},
+ undefined,
+ {},
+ { encoding: null, resolveWithFullResponse: true },
+ );
+
+ let mimeType: string | undefined;
+ if (response.headers['content-type']) {
+ mimeType = response.headers['content-type'];
+ }
+
+ const newItem: INodeExecutionData = {
+ json: items[i].json,
+ binary: {},
+ };
+
+ if (items[i].binary !== undefined) {
+ // Create a shallow copy of the binary data so that the old
+ // data references which do not get changed still stay behind
+ // but the incoming data does not get changed.
+ Object.assign(newItem.binary, items[i].binary);
+ }
+
+ items[i] = newItem;
+
+
+ const fileName = `${messageId}.eml`;
+ const data = Buffer.from(response.body as string, 'utf8');
+ items[i].binary![dataPropertyNameDownload] = await this.helpers.prepareBinaryData(data as unknown as Buffer, fileName, mimeType);
+ }
+ }
+
+ if (operation === 'getAll') {
+ let additionalFields: IDataObject = {};
+ for (let i = 0; i < length; i++) {
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+
+ if (additionalFields.fields) {
+ qs['$select'] = additionalFields.fields;
+ }
+
+ if (additionalFields.filter) {
+ qs['$filter'] = additionalFields.filter;
+ }
+
+ const endpoint = '/messages';
+
+ if (returnAll === true) {
+ responseData = await microsoftApiRequestAllItems.call(
+ this,
+ 'value',
+ 'GET',
+ endpoint,
+ undefined,
+ qs,
+ );
+ } else {
+ qs['$top'] = this.getNodeParameter('limit', i) as number;
+ responseData = await microsoftApiRequest.call(
+ this,
+ 'GET',
+ endpoint,
+ undefined,
+ qs,
+ );
+ responseData = responseData.value;
+ }
+
+ if (additionalFields.dataPropertyAttachmentsPrefixName) {
+ const prefix = additionalFields.dataPropertyAttachmentsPrefixName as string;
+ const data = await downloadAttachments.call(this, responseData, prefix);
+ returnData.push.apply(returnData, data as unknown as IDataObject[]);
+ } else {
+ returnData.push.apply(returnData, responseData);
+ }
+ }
+
+ if (additionalFields.dataPropertyAttachmentsPrefixName) {
+ return [returnData as INodeExecutionData[]];
+ }
+ }
+
+ if (operation === 'move') {
+ for (let i = 0; i < length; i++) {
+ const messageId = this.getNodeParameter('messageId', i) as string;
+ const destinationId = this.getNodeParameter('folderId', i) as string;
+ const body: IDataObject = {
+ destinationId,
+ };
+
+ responseData = await microsoftApiRequest.call(
+ this,
+ 'POST',
+ `/messages/${messageId}/move`,
+ body,
+ );
+ returnData.push({ success: true });
+ }
+ }
+
+ if (operation === 'send') {
+ for (let i = 0; i < length; i++) {
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+
+ const toRecipients = this.getNodeParameter('toRecipients', i) as string;
+
+ const subject = this.getNodeParameter('subject', i) as string;
+
+ const bodyContent = this.getNodeParameter('bodyContent', i, '') as string;
+
+ additionalFields.subject = subject;
+
+ additionalFields.bodyContent = bodyContent || ' ';
+
+ additionalFields.toRecipients = toRecipients;
+
+ const saveToSentItems = additionalFields.saveToSentItems === undefined ? true : additionalFields.saveToSentItems;
+ delete additionalFields.saveToSentItems;
+
+ // Create message object from optional fields
+ const message: IDataObject = createMessage(additionalFields);
+
+ if (additionalFields.attachments) {
+ const attachments = (additionalFields.attachments as IDataObject).attachments as IDataObject[];
+
+ // // Handle attachments
+ message['attachments'] = attachments.map(attachment => {
+ const binaryPropertyName = attachment.binaryPropertyName as string;
+
+ if (items[i].binary === undefined) {
+ throw new Error('No binary data exists on item!');
+ }
+ //@ts-ignore
+ if (items[i].binary[binaryPropertyName] === undefined) {
+ throw new Error(`No binary data property "${binaryPropertyName}" does not exists on item!`);
+ }
+
+ const binaryData = (items[i].binary as IBinaryKeyData)[binaryPropertyName];
+ return {
+ '@odata.type': '#microsoft.graph.fileAttachment',
+ name: binaryData.fileName,
+ contentBytes: binaryData.data,
+ };
+ });
+ }
+
+ const body: IDataObject = {
+ message,
+ saveToSentItems,
+ };
+
+ responseData = await microsoftApiRequest.call(
+ this,
+ 'POST',
+ `/sendMail`,
+ body,
+ {},
+ );
+ returnData.push({ success: true });
+ }
+ }
+
+ }
+
+ if (resource === 'messageAttachment') {
+ if (operation === 'add') {
+ for (let i = 0; i < length; i++) {
+ const messageId = this.getNodeParameter('messageId', i) as string;
+ const binaryPropertyName = this.getNodeParameter('binaryPropertyName', 0) as string;
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+
+ if (items[i].binary === undefined) {
+ throw new Error('No binary data exists on item!');
+ }
+ //@ts-ignore
+ if (items[i].binary[binaryPropertyName] === undefined) {
+ throw new Error(`No binary data property "${binaryPropertyName}" does not exists on item!`);
+ }
+
+ const binaryData = (items[i].binary as IBinaryKeyData)[binaryPropertyName];
+ const dataBuffer = Buffer.from(binaryData.data, 'base64');
+
+ const fileName = additionalFields.fileName === undefined ? binaryData.fileName : additionalFields.fileName;
+
+ if (!fileName) {
+ throw new Error('File name is not set. It has either to be set via "Additional Fields" or has to be set on the binary property!');
+ }
+
+ // Check if the file is over 3MB big
+ if (dataBuffer.length > 3e6) {
+ // Maximum chunk size is 4MB
+ const chunkSize = 4e6;
+ const body: IDataObject = {
+ AttachmentItem: {
+ attachmentType: 'file',
+ name: fileName,
+ size: dataBuffer.length,
+ },
+ };
+
+ // Create upload session
+ responseData = await microsoftApiRequest.call(
+ this,
+ 'POST',
+ `/messages/${messageId}/attachments/createUploadSession`,
+ body,
+ );
+ const uploadUrl = responseData.uploadUrl;
+
+ if (uploadUrl === undefined) {
+ throw new Error('Failed to get upload session');
+ }
+
+ for (let bytesUploaded = 0; bytesUploaded < dataBuffer.length; bytesUploaded += chunkSize) {
+ // Upload the file chunk by chunk
+ const nextChunk = Math.min(bytesUploaded + chunkSize, dataBuffer.length);
+ const contentRange = `bytes ${bytesUploaded}-${nextChunk - 1}/${dataBuffer.length}`;
+
+ const data = dataBuffer.subarray(bytesUploaded, nextChunk);
+
+ responseData = await this.helpers.request(
+ uploadUrl,
+ {
+ method: 'PUT',
+ headers: {
+ 'Content-Type': 'application/octet-stream',
+ 'Content-Length': data.length,
+ 'Content-Range': contentRange,
+ },
+ body: data,
+ });
+ }
+ } else {
+ const body: IDataObject = {
+ '@odata.type': '#microsoft.graph.fileAttachment',
+ name: fileName,
+ contentBytes: binaryData.data,
+ };
+
+ responseData = await microsoftApiRequest.call(
+ this,
+ 'POST',
+ `/messages/${messageId}/attachments`,
+ body,
+ {},
+ );
+ }
+ returnData.push({ success: true });
+ }
+ }
+
+ if (operation === 'download') {
+ for (let i = 0; i < length; i++) {
+ const messageId = this.getNodeParameter('messageId', i) as string;
+ const attachmentId = this.getNodeParameter('attachmentId', i) as string;
+ const dataPropertyNameDownload = this.getNodeParameter('binaryPropertyName', i) as string;
+
+ // Get attachment details first
+ const attachmentDetails = await microsoftApiRequest.call(
+ this,
+ 'GET',
+ `/messages/${messageId}/attachments/${attachmentId}`,
+ undefined,
+ { '$select': 'id,name,contentType' },
+ );
+
+ let mimeType: string | undefined;
+ if (attachmentDetails.contentType) {
+ mimeType = attachmentDetails.contentType;
+ }
+ const fileName = attachmentDetails.name;
+
+ const response = await microsoftApiRequest.call(
+ this,
+ 'GET',
+ `/messages/${messageId}/attachments/${attachmentId}/$value`,
+ undefined,
+ {},
+ undefined,
+ {},
+ { encoding: null, resolveWithFullResponse: true },
+ );
+
+ const newItem: INodeExecutionData = {
+ json: items[i].json,
+ binary: {},
+ };
+
+ if (items[i].binary !== undefined) {
+ // Create a shallow copy of the binary data so that the old
+ // data references which do not get changed still stay behind
+ // but the incoming data does not get changed.
+ Object.assign(newItem.binary, items[i].binary);
+ }
+
+ items[i] = newItem;
+ const data = Buffer.from(response.body as string, 'utf8');
+ items[i].binary![dataPropertyNameDownload] = await this.helpers.prepareBinaryData(data as unknown as Buffer, fileName, mimeType);
+ }
+ }
+
+ if (operation === 'get') {
+ for (let i = 0; i < length; i++) {
+ const messageId = this.getNodeParameter('messageId', i) as string;
+ const attachmentId = this.getNodeParameter('attachmentId', i) as string;
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+
+ // Have sane defaults so we don't fetch attachment data in this operation
+ qs['$select'] = 'id,lastModifiedDateTime,name,contentType,size,isInline';
+ if (additionalFields.fields) {
+ qs['$select'] = additionalFields.fields;
+ }
+
+ responseData = await microsoftApiRequest.call(
+ this,
+ 'GET',
+ `/messages/${messageId}/attachments/${attachmentId}`,
+ undefined,
+ qs,
+ );
+ returnData.push(responseData);
+ }
+ }
+
+ if (operation === 'getAll') {
+ for (let i = 0; i < length; i++) {
+ const messageId = this.getNodeParameter('messageId', i) as string;
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+
+ // Have sane defaults so we don't fetch attachment data in this operation
+ qs['$select'] = 'id,lastModifiedDateTime,name,contentType,size,isInline';
+ if (additionalFields.fields) {
+ qs['$select'] = additionalFields.fields;
+ }
+
+ if (additionalFields.filter) {
+ qs['$filter'] = additionalFields.filter;
+ }
+
+ const endpoint = `/messages/${messageId}/attachments`;
+ if (returnAll === true) {
+ responseData = await microsoftApiRequestAllItems.call(
+ this,
+ 'value',
+ 'GET',
+ endpoint,
+ undefined,
+ qs,
+ );
+ } else {
+ qs['$top'] = this.getNodeParameter('limit', i) as number;
+ responseData = await microsoftApiRequest.call(
+ this,
+ 'GET',
+ endpoint,
+ undefined,
+ qs,
+ );
+ responseData = responseData.value;
+ }
+ returnData.push.apply(returnData, responseData as IDataObject[]);
+ }
+ }
+ }
+
+ if (resource === 'folder') {
+ if (operation === 'create') {
+ for (let i = 0; i < length; i++) {
+ const displayName = this.getNodeParameter('displayName', i) as string;
+ const folderType = this.getNodeParameter('folderType', i) as string;
+ const body: IDataObject = {
+ displayName,
+ };
+
+ let endpoint = '/mailFolders';
+
+ if (folderType === 'searchFolder') {
+ endpoint = '/mailFolders/searchfolders/childFolders';
+ const includeNestedFolders = this.getNodeParameter('includeNestedFolders', i);
+ const sourceFolderIds = this.getNodeParameter('sourceFolderIds', i);
+ const filterQuery = this.getNodeParameter('filterQuery', i);
+ Object.assign(body, {
+ '@odata.type': 'microsoft.graph.mailSearchFolder',
+ includeNestedFolders,
+ sourceFolderIds,
+ filterQuery,
+ });
+ }
+
+ responseData = await microsoftApiRequest.call(
+ this,
+ 'POST',
+ endpoint,
+ body,
+ );
+ returnData.push(responseData);
+ }
+ }
+
+ if (operation === 'delete') {
+ for (let i = 0; i < length; i++) {
+ const folderId = this.getNodeParameter('folderId', i) as string;
+ responseData = await microsoftApiRequest.call(
+ this,
+ 'DELETE',
+ `/mailFolders/${folderId}`,
+ );
+ returnData.push({ success: true });
+ }
+ }
+
+ if (operation === 'get') {
+ for (let i = 0; i < length; i++) {
+ const folderId = this.getNodeParameter('folderId', i) as string;
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+
+ if (additionalFields.fields) {
+ qs['$select'] = additionalFields.fields;
+ }
+
+ if (additionalFields.filter) {
+ qs['$filter'] = additionalFields.filter;
+ }
+ responseData = await microsoftApiRequest.call(
+ this,
+ 'GET',
+ `/mailFolders/${folderId}`,
+ {},
+ qs,
+ );
+ returnData.push(responseData);
+ }
+ }
+
+ if (operation === 'getAll') {
+ for (let i = 0; i < length; i++) {
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+
+ if (additionalFields.fields) {
+ qs['$select'] = additionalFields.fields;
+ }
+
+ if (additionalFields.filter) {
+ qs['$filter'] = additionalFields.filter;
+ }
+
+ if (returnAll === true) {
+ responseData = await microsoftApiRequestAllItems.call(
+ this,
+ 'value',
+ 'GET',
+ '/mailFolders',
+ {},
+ qs,
+ );
+ } else {
+ qs['$top'] = this.getNodeParameter('limit', i) as number;
+ responseData = await microsoftApiRequest.call(
+ this,
+ 'GET',
+ '/mailFolders',
+ {},
+ qs,
+ );
+ responseData = responseData.value;
+ }
+ returnData.push.apply(returnData, responseData as IDataObject[]);
+ }
+ }
+
+ if (operation === 'getChildren') {
+ for (let i = 0; i < length; i++) {
+ const folderId = this.getNodeParameter('folderId', i) as string;
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+
+ if (additionalFields.fields) {
+ qs['$select'] = additionalFields.fields;
+ }
+
+ if (additionalFields.filter) {
+ qs['$filter'] = additionalFields.filter;
+ }
+
+ if (returnAll) {
+ responseData = await microsoftApiRequestAllItems.call(
+ this,
+ 'value',
+ 'GET',
+ `/mailFolders/${folderId}/childFolders`,
+ qs,
+ );
+ } else {
+ qs['$top'] = this.getNodeParameter('limit', i) as number;
+ responseData = await microsoftApiRequest.call(
+ this,
+ 'GET',
+ `/mailFolders/${folderId}/childFolders`,
+ undefined,
+ qs,
+ );
+ responseData = responseData.value;
+ }
+ returnData.push.apply(returnData, responseData as IDataObject[]);
+ }
+ }
+
+ if (operation === 'update') {
+ for (let i = 0; i < length; i++) {
+ const folderId = this.getNodeParameter('folderId', i) as string;
+ const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
+
+ const body: IDataObject = {
+ ...updateFields,
+ };
+
+ responseData = await microsoftApiRequest.call(
+ this,
+ 'PATCH',
+ `/mailFolders/${folderId}`,
+ body,
+ );
+ returnData.push(responseData);
+ }
+ }
+ }
+
+ if (resource === 'folderMessage') {
+ for (let i = 0; i < length; i++) {
+ if (operation === 'getAll') {
+ const folderId = this.getNodeParameter('folderId', i) as string;
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+
+ if (additionalFields.fields) {
+ qs['$select'] = additionalFields.fields;
+ }
+
+ const endpoint = `/mailFolders/${folderId}/messages`;
+ if (returnAll) {
+ responseData = await microsoftApiRequestAllItems.call(
+ this,
+ 'value',
+ 'GET',
+ endpoint,
+ qs,
+ );
+ }
+ else {
+ qs['$top'] = this.getNodeParameter('limit', i) as number;
+ responseData = await microsoftApiRequest.call(
+ this,
+ 'GET',
+ endpoint,
+ undefined,
+ qs,
+ );
+ responseData = responseData.value;
+ }
+ returnData.push.apply(returnData, responseData as IDataObject[]);
+ }
+ }
+ }
+
+ if ((resource === 'message' && operation === 'getMime') || (resource === 'messageAttachment' && operation === 'download')) {
+ return this.prepareOutputData(items);
+ } else {
+ return [this.helpers.returnJsonArray(returnData)];
+ }
+ }
+}
diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/outlook.svg b/packages/nodes-base/nodes/Microsoft/Outlook/outlook.svg
new file mode 100644
index 0000000000..a4eae7d657
--- /dev/null
+++ b/packages/nodes-base/nodes/Microsoft/Outlook/outlook.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json
index c7eb05ec81..bf767c3ba3 100644
--- a/packages/nodes-base/package.json
+++ b/packages/nodes-base/package.json
@@ -139,6 +139,7 @@
"dist/credentials/MicrosoftExcelOAuth2Api.credentials.js",
"dist/credentials/MicrosoftOAuth2Api.credentials.js",
"dist/credentials/MicrosoftOneDriveOAuth2Api.credentials.js",
+ "dist/credentials/MicrosoftOutlookOAuth2Api.credentials.js",
"dist/credentials/MicrosoftSql.credentials.js",
"dist/credentials/MicrosoftTeamsOAuth2Api.credentials.js",
"dist/credentials/MindeeReceiptApi.credentials.js",
@@ -373,6 +374,7 @@
"dist/nodes/MessageBird/MessageBird.node.js",
"dist/nodes/Microsoft/Excel/MicrosoftExcel.node.js",
"dist/nodes/Microsoft/OneDrive/MicrosoftOneDrive.node.js",
+ "dist/nodes/Microsoft/Outlook/MicrosoftOutlook.node.js",
"dist/nodes/Microsoft/Sql/MicrosoftSql.node.js",
"dist/nodes/Microsoft/Teams/MicrosoftTeams.node.js",
"dist/nodes/Mindee/Mindee.node.js",