first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
import type { IDataObject, IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
description: 'The name of the calendar to create',
|
||||
placeholder: 'e.g. My Calendar',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options
|
||||
displayName: 'Calendar Group',
|
||||
name: 'calendarGroup',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getCalendarGroups',
|
||||
},
|
||||
default: [],
|
||||
description:
|
||||
'If set, the calendar will be created in the specified calendar group. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Color',
|
||||
name: 'color',
|
||||
type: 'options',
|
||||
default: 'lightBlue',
|
||||
options: [
|
||||
{
|
||||
name: 'Light Blue',
|
||||
value: 'lightBlue',
|
||||
},
|
||||
{
|
||||
name: 'Light Brown',
|
||||
value: 'lightBrown',
|
||||
},
|
||||
{
|
||||
name: 'Light Gray',
|
||||
value: 'lightGray',
|
||||
},
|
||||
{
|
||||
name: 'Light Green',
|
||||
value: 'lightGreen',
|
||||
},
|
||||
{
|
||||
name: 'Light Orange',
|
||||
value: 'lightOrange',
|
||||
},
|
||||
{
|
||||
name: 'Light Pink',
|
||||
value: 'lightPink',
|
||||
},
|
||||
{
|
||||
name: 'Light Red',
|
||||
value: 'lightRed',
|
||||
},
|
||||
{
|
||||
name: 'Light Teal',
|
||||
value: 'lightTeal',
|
||||
},
|
||||
{
|
||||
name: 'Light Yellow',
|
||||
value: 'lightYellow',
|
||||
},
|
||||
],
|
||||
description: 'Specify the color to distinguish the calendar from the others',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['calendar'],
|
||||
operation: ['create'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
const additionalFields = this.getNodeParameter('additionalFields', index);
|
||||
const name = this.getNodeParameter('name', index) as string;
|
||||
|
||||
let endpoint = '/calendars';
|
||||
|
||||
if (additionalFields.calendarGroup) {
|
||||
endpoint = `/calendarGroups/${additionalFields.calendarGroup}/calendars`;
|
||||
delete additionalFields.calendarGroup;
|
||||
}
|
||||
|
||||
const body: IDataObject = {
|
||||
name,
|
||||
...additionalFields,
|
||||
};
|
||||
|
||||
const responseData = await microsoftApiRequest.call(this, 'POST', endpoint, body);
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { calendarRLC } from '../../descriptions';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [calendarRLC];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['calendar'],
|
||||
operation: ['delete'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
const calendarId = this.getNodeParameter('calendarId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
await microsoftApiRequest.call(this, 'DELETE', `/calendars/${calendarId}`);
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray({ success: true }),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import type { IDataObject, IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { calendarRLC } from '../../descriptions';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [calendarRLC];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['calendar'],
|
||||
operation: ['get'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
const qs: IDataObject = {};
|
||||
|
||||
const calendarId = this.getNodeParameter('calendarId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
const responseData = await microsoftApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
`/calendars/${calendarId}`,
|
||||
undefined,
|
||||
qs,
|
||||
);
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import type { IDataObject, IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { returnAllOrLimit } from '../../descriptions';
|
||||
import { microsoftApiRequest, microsoftApiRequestAllItems } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
...returnAllOrLimit,
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Filter',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Filter Query',
|
||||
name: 'custom',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'e.g. canShare eq true',
|
||||
hint: 'Search query to filter calendars. <a href="https://learn.microsoft.com/en-us/graph/filter-query-parameter">More info</a>.',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['calendar'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
let responseData;
|
||||
const qs = {} as IDataObject;
|
||||
|
||||
const returnAll = this.getNodeParameter('returnAll', index);
|
||||
const filters = this.getNodeParameter('filters', index, {});
|
||||
|
||||
if (Object.keys(filters).length) {
|
||||
const filterString: string[] = [];
|
||||
|
||||
if (filters.custom) {
|
||||
filterString.push(filters.custom as string);
|
||||
}
|
||||
|
||||
if (filterString.length) {
|
||||
qs.$filter = filterString.join(' and ');
|
||||
}
|
||||
}
|
||||
|
||||
const endpoint = '/calendars';
|
||||
|
||||
if (returnAll) {
|
||||
responseData = await microsoftApiRequestAllItems.call(
|
||||
this,
|
||||
'value',
|
||||
'GET',
|
||||
endpoint,
|
||||
undefined,
|
||||
qs,
|
||||
);
|
||||
} else {
|
||||
qs.$top = this.getNodeParameter('limit', index);
|
||||
responseData = await microsoftApiRequest.call(this, 'GET', endpoint, undefined, qs);
|
||||
responseData = responseData.value;
|
||||
}
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import * as create from './create.operation';
|
||||
import * as del from './delete.operation';
|
||||
import * as get from './get.operation';
|
||||
import * as getAll from './getAll.operation';
|
||||
import * as update from './update.operation';
|
||||
|
||||
export { create, del as delete, get, getAll, update };
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['calendar'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a new calendar',
|
||||
action: 'Create a calendar',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete a calendar',
|
||||
action: 'Delete a calendar',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Retrieve a calendar',
|
||||
action: 'Get a calendar',
|
||||
},
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
description: 'List and search calendars',
|
||||
action: 'Get many calendars',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update a calendar',
|
||||
action: 'Update a calendar',
|
||||
},
|
||||
],
|
||||
default: 'getAll',
|
||||
},
|
||||
|
||||
...create.description,
|
||||
...del.description,
|
||||
...get.description,
|
||||
...getAll.description,
|
||||
...update.description,
|
||||
];
|
||||
@@ -0,0 +1,110 @@
|
||||
import type { IDataObject, IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { calendarRLC } from '../../descriptions';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
calendarRLC,
|
||||
{
|
||||
displayName: 'Update Fields',
|
||||
name: 'updateFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Color',
|
||||
name: 'color',
|
||||
type: 'options',
|
||||
default: 'lightBlue',
|
||||
options: [
|
||||
{
|
||||
name: 'Light Blue',
|
||||
value: 'lightBlue',
|
||||
},
|
||||
{
|
||||
name: 'Light Brown',
|
||||
value: 'lightBrown',
|
||||
},
|
||||
{
|
||||
name: 'Light Gray',
|
||||
value: 'lightGray',
|
||||
},
|
||||
{
|
||||
name: 'Light Green',
|
||||
value: 'lightGreen',
|
||||
},
|
||||
{
|
||||
name: 'Light Orange',
|
||||
value: 'lightOrange',
|
||||
},
|
||||
{
|
||||
name: 'Light Pink',
|
||||
value: 'lightPink',
|
||||
},
|
||||
{
|
||||
name: 'Light Red',
|
||||
value: 'lightRed',
|
||||
},
|
||||
{
|
||||
name: 'Light Teal',
|
||||
value: 'lightTeal',
|
||||
},
|
||||
{
|
||||
name: 'Light Yellow',
|
||||
value: 'lightYellow',
|
||||
},
|
||||
],
|
||||
description: 'Specify the color to distinguish the calendar from the others',
|
||||
},
|
||||
{
|
||||
displayName: 'Default Calendar',
|
||||
name: 'isDefaultCalendar',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'e.g. My Calendar',
|
||||
description: 'The name of the calendar',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['calendar'],
|
||||
operation: ['update'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
const updateFields = this.getNodeParameter('updateFields', index);
|
||||
|
||||
const calendarId = this.getNodeParameter('calendarId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
const endpoint = `/calendars/${calendarId}`;
|
||||
|
||||
const body: IDataObject = {
|
||||
...updateFields,
|
||||
};
|
||||
|
||||
const responseData = await microsoftApiRequest.call(this, 'PATCH', endpoint, body);
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import type { IDataObject, IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { contactFields } from '../../descriptions';
|
||||
import { prepareContactFields } from '../../helpers/utils';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'First Name',
|
||||
name: 'givenName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Last Name',
|
||||
name: 'surname',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
options: contactFields,
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['contact'],
|
||||
operation: ['create'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
const additionalFields = this.getNodeParameter('additionalFields', index);
|
||||
const givenName = this.getNodeParameter('givenName', index) as string;
|
||||
const surname = this.getNodeParameter('surname', index) as string;
|
||||
|
||||
const body: IDataObject = {
|
||||
givenName,
|
||||
...prepareContactFields(additionalFields),
|
||||
};
|
||||
|
||||
if (surname) {
|
||||
body.surname = surname;
|
||||
}
|
||||
|
||||
const responseData = await microsoftApiRequest.call(this, 'POST', '/contacts', body);
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import type { IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { contactRLC } from '../../descriptions';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [contactRLC];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['contact'],
|
||||
operation: ['delete'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
const contactId = this.getNodeParameter('contactId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
await microsoftApiRequest.call(this, 'DELETE', `/contacts/${contactId}`);
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray({ success: true }),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import type { IDataObject, IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { contactRLC } from '../../descriptions';
|
||||
import { contactFields } from '../../helpers/utils';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
contactRLC,
|
||||
{
|
||||
displayName: 'Output',
|
||||
name: 'output',
|
||||
type: 'options',
|
||||
default: 'simple',
|
||||
options: [
|
||||
{
|
||||
name: 'Simplified',
|
||||
value: 'simple',
|
||||
},
|
||||
{
|
||||
name: 'Raw',
|
||||
value: 'raw',
|
||||
},
|
||||
{
|
||||
name: 'Select Included Fields',
|
||||
value: 'fields',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Fields',
|
||||
name: 'fields',
|
||||
type: 'multiOptions',
|
||||
description: 'The fields to add to the output',
|
||||
displayOptions: {
|
||||
show: {
|
||||
output: ['fields'],
|
||||
},
|
||||
},
|
||||
options: contactFields,
|
||||
default: [],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['contact'],
|
||||
operation: ['get'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
const qs: IDataObject = {};
|
||||
|
||||
const contactId = this.getNodeParameter('contactId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
const output = this.getNodeParameter('output', index) as string;
|
||||
|
||||
if (output === 'fields') {
|
||||
const fields = this.getNodeParameter('fields', index) as string[];
|
||||
qs.$select = fields.join(',');
|
||||
}
|
||||
|
||||
if (output === 'simple') {
|
||||
qs.$select = 'id,displayName,emailAddresses,businessPhones,mobilePhone';
|
||||
}
|
||||
|
||||
const responseData = await microsoftApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
`/contacts/${contactId}`,
|
||||
undefined,
|
||||
qs,
|
||||
);
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
import type { IDataObject, IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { returnAllOrLimit } from '../../descriptions';
|
||||
import { contactFields } from '../../helpers/utils';
|
||||
import { microsoftApiRequest, microsoftApiRequestAllItems } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
...returnAllOrLimit,
|
||||
{
|
||||
displayName: 'Output',
|
||||
name: 'output',
|
||||
type: 'options',
|
||||
default: 'simple',
|
||||
options: [
|
||||
{
|
||||
name: 'Simplified',
|
||||
value: 'simple',
|
||||
},
|
||||
{
|
||||
name: 'Raw',
|
||||
value: 'raw',
|
||||
},
|
||||
{
|
||||
name: 'Select Included Fields',
|
||||
value: 'fields',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Fields',
|
||||
name: 'fields',
|
||||
type: 'multiOptions',
|
||||
description: 'The fields to add to the output',
|
||||
displayOptions: {
|
||||
show: {
|
||||
output: ['fields'],
|
||||
},
|
||||
},
|
||||
options: contactFields,
|
||||
default: [],
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Filter',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Filter Query',
|
||||
name: 'custom',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: "e.g. displayName eq 'John Doe'",
|
||||
hint: 'Search query to filter contacts. <a href="https://learn.microsoft.com/en-us/graph/filter-query-parameter">More info</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Email Address',
|
||||
name: 'emailAddress',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description:
|
||||
'If contacts that you want to retrieve have multiple email addresses, you can enter them separated by commas',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['contact'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
let responseData;
|
||||
const qs = {} as IDataObject;
|
||||
|
||||
const returnAll = this.getNodeParameter('returnAll', index);
|
||||
const filters = this.getNodeParameter('filters', index, {});
|
||||
const output = this.getNodeParameter('output', index) as string;
|
||||
|
||||
if (output === 'fields') {
|
||||
const fields = this.getNodeParameter('fields', index) as string[];
|
||||
qs.$select = fields.join(',');
|
||||
}
|
||||
|
||||
if (output === 'simple') {
|
||||
qs.$select = 'id,displayName,emailAddresses,businessPhones,mobilePhone';
|
||||
}
|
||||
|
||||
if (Object.keys(filters).length) {
|
||||
const filterString: string[] = [];
|
||||
|
||||
if (filters.emailAddress) {
|
||||
const emails = (filters.emailAddress as string)
|
||||
.split(',')
|
||||
.map((email) => `emailAddresses/any(a:a/address eq '${email.trim()}')`);
|
||||
filterString.push(emails.join(' and '));
|
||||
}
|
||||
|
||||
if (filters.custom) {
|
||||
filterString.push(filters.custom as string);
|
||||
}
|
||||
|
||||
if (filterString.length) {
|
||||
qs.$filter = filterString.join(' and ');
|
||||
}
|
||||
}
|
||||
|
||||
const endpoint = '/contacts';
|
||||
|
||||
if (returnAll) {
|
||||
responseData = await microsoftApiRequestAllItems.call(
|
||||
this,
|
||||
'value',
|
||||
'GET',
|
||||
endpoint,
|
||||
undefined,
|
||||
qs,
|
||||
);
|
||||
} else {
|
||||
qs.$top = this.getNodeParameter('limit', index);
|
||||
responseData = await microsoftApiRequest.call(this, 'GET', endpoint, undefined, qs);
|
||||
responseData = responseData.value;
|
||||
}
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject[]),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import * as create from './create.operation';
|
||||
import * as del from './delete.operation';
|
||||
import * as get from './get.operation';
|
||||
import * as getAll from './getAll.operation';
|
||||
import * as update from './update.operation';
|
||||
|
||||
export { create, del as delete, get, getAll, update };
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['contact'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a new contact',
|
||||
action: 'Create a contact',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete a contact',
|
||||
action: 'Delete a contact',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Retrieve a contact',
|
||||
action: 'Get a contact',
|
||||
},
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
description: 'List and search contacts',
|
||||
action: 'Get many contacts',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update a contact',
|
||||
action: 'Update a contact',
|
||||
},
|
||||
],
|
||||
default: 'getAll',
|
||||
},
|
||||
...create.description,
|
||||
...del.description,
|
||||
...get.description,
|
||||
...getAll.description,
|
||||
...update.description,
|
||||
];
|
||||
@@ -0,0 +1,51 @@
|
||||
import type { IDataObject, IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { contactFields, contactRLC } from '../../descriptions';
|
||||
import { prepareContactFields } from '../../helpers/utils';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
contactRLC,
|
||||
{
|
||||
displayName: 'Update Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
options: contactFields,
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['contact'],
|
||||
operation: ['update'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
const additionalFields = this.getNodeParameter('additionalFields', index);
|
||||
const contactId = this.getNodeParameter('contactId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
const body: IDataObject = prepareContactFields(additionalFields);
|
||||
|
||||
const responseData = await microsoftApiRequest.call(
|
||||
this,
|
||||
'PATCH',
|
||||
`/contacts/${contactId}`,
|
||||
body,
|
||||
);
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
import type {
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { createMessage } from '../../helpers/utils';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Subject',
|
||||
name: 'subject',
|
||||
description: 'The subject of the message',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Message',
|
||||
name: 'bodyContent',
|
||||
description: 'Message body content',
|
||||
type: 'string',
|
||||
typeOptions: {
|
||||
rows: 2,
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Attachments',
|
||||
name: 'attachments',
|
||||
type: 'fixedCollection',
|
||||
placeholder: 'Add Attachment',
|
||||
default: {},
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'attachments',
|
||||
displayName: 'Attachment',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Input Data Field Name',
|
||||
name: 'binaryPropertyName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'e.g. data',
|
||||
hint: 'The name of the input field containing the binary file data to be attached',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'BCC Recipients',
|
||||
name: 'bccRecipients',
|
||||
description: 'Comma-separated list of email addresses of BCC recipients',
|
||||
type: 'string',
|
||||
placeholder: 'e.g. john@example.com',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Category Names or IDs',
|
||||
name: 'categories',
|
||||
type: 'multiOptions',
|
||||
description:
|
||||
'Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getCategoriesNames',
|
||||
},
|
||||
default: [],
|
||||
},
|
||||
{
|
||||
displayName: 'CC Recipients',
|
||||
name: 'ccRecipients',
|
||||
description: 'Comma-separated list of email addresses of CC recipients',
|
||||
type: 'string',
|
||||
placeholder: 'e.g. john@example.com',
|
||||
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 from which the message is sent. Must correspond to the actual mailbox used.',
|
||||
type: 'string',
|
||||
placeholder: 'e.g. john@example.com',
|
||||
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: 'Normal',
|
||||
},
|
||||
{
|
||||
displayName: 'Message Type',
|
||||
name: 'bodyContentType',
|
||||
description: 'Message body content type',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'HTML',
|
||||
value: 'html',
|
||||
},
|
||||
{
|
||||
name: 'Text',
|
||||
value: 'Text',
|
||||
},
|
||||
],
|
||||
default: 'html',
|
||||
},
|
||||
{
|
||||
displayName: 'Read Receipt Requested',
|
||||
name: 'isReadReceiptRequested',
|
||||
description: 'Whether a read receipt is requested for the message',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
displayName: 'Reply To',
|
||||
name: 'replyTo',
|
||||
description: 'Email address to use when replying',
|
||||
type: 'string',
|
||||
placeholder: 'e.g. replyto@example.com',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'To',
|
||||
name: 'toRecipients',
|
||||
description: 'Comma-separated list of email addresses of recipients',
|
||||
type: 'string',
|
||||
placeholder: 'e.g. john@example.com',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['draft'],
|
||||
operation: ['create'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number, _: INodeExecutionData[]) {
|
||||
const additionalFields = this.getNodeParameter('additionalFields', index);
|
||||
const subject = this.getNodeParameter('subject', index) as string;
|
||||
const bodyContent = this.getNodeParameter('bodyContent', index, '') 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 = await Promise.all(
|
||||
attachments.map(async (attachment) => {
|
||||
const binaryPropertyName = attachment.binaryPropertyName as string;
|
||||
|
||||
const binaryData = this.helpers.assertBinaryData(index, binaryPropertyName);
|
||||
|
||||
let fileBase64;
|
||||
if (binaryData.id) {
|
||||
const chunkSize = 256 * 1024;
|
||||
const stream = await this.helpers.getBinaryStream(binaryData.id, chunkSize);
|
||||
const buffer = await this.helpers.binaryToBuffer(stream);
|
||||
fileBase64 = buffer.toString('base64');
|
||||
} else {
|
||||
fileBase64 = binaryData.data;
|
||||
}
|
||||
|
||||
return {
|
||||
'@odata.type': '#microsoft.graph.fileAttachment',
|
||||
name: binaryData.fileName,
|
||||
contentBytes: fileBase64,
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const responseData = await microsoftApiRequest.call(this, 'POST', '/messages', body, {});
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import type { IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { draftRLC } from '../../descriptions';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [draftRLC];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['draft'],
|
||||
operation: ['delete'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
const draftId = this.getNodeParameter('draftId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
await microsoftApiRequest.call(this, 'DELETE', `/messages/${draftId}`);
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray({ success: true }),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
import type {
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { draftRLC } from '../../descriptions';
|
||||
import { messageFields, simplifyOutputMessages } from '../../helpers/utils';
|
||||
import { downloadAttachments, microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
draftRLC,
|
||||
{
|
||||
displayName: 'Output',
|
||||
name: 'output',
|
||||
type: 'options',
|
||||
default: 'simple',
|
||||
options: [
|
||||
{
|
||||
name: 'Simplified',
|
||||
value: 'simple',
|
||||
},
|
||||
{
|
||||
name: 'Raw',
|
||||
value: 'raw',
|
||||
},
|
||||
{
|
||||
name: 'Select Included Fields',
|
||||
value: 'fields',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Fields',
|
||||
name: 'fields',
|
||||
type: 'multiOptions',
|
||||
description: 'The fields to add to the output',
|
||||
displayOptions: {
|
||||
show: {
|
||||
output: ['fields'],
|
||||
},
|
||||
},
|
||||
options: messageFields,
|
||||
default: [],
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add option',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Attachments Prefix',
|
||||
name: 'attachmentsPrefix',
|
||||
type: 'string',
|
||||
default: 'attachment_',
|
||||
description:
|
||||
'Prefix for name of the output fields to put the binary files data in. An index starting from 0 will be added. So if name is "attachment_" the first attachment is saved to "attachment_0".',
|
||||
},
|
||||
{
|
||||
displayName: 'Download Attachments',
|
||||
name: 'downloadAttachments',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description:
|
||||
"Whether the message's attachments will be downloaded and included in the output",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['draft'],
|
||||
operation: ['get'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
let responseData;
|
||||
const qs: IDataObject = {};
|
||||
|
||||
const draftId = this.getNodeParameter('draftId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
const options = this.getNodeParameter('options', index, {});
|
||||
const output = this.getNodeParameter('output', index) as string;
|
||||
|
||||
if (output === 'fields') {
|
||||
const fields = this.getNodeParameter('fields', index) as string[];
|
||||
|
||||
if (options.downloadAttachments) {
|
||||
fields.push('hasAttachments');
|
||||
}
|
||||
|
||||
qs.$select = fields.join(',');
|
||||
}
|
||||
|
||||
if (output === 'simple') {
|
||||
qs.$select =
|
||||
'id,conversationId,subject,bodyPreview,from,toRecipients,categories,hasAttachments';
|
||||
}
|
||||
|
||||
responseData = await microsoftApiRequest.call(this, 'GET', `/messages/${draftId}`, undefined, qs);
|
||||
|
||||
if (output === 'simple') {
|
||||
responseData = simplifyOutputMessages([responseData as IDataObject]);
|
||||
}
|
||||
|
||||
let executionData: INodeExecutionData[] = [];
|
||||
|
||||
if (options.downloadAttachments) {
|
||||
const prefix = (options.attachmentsPrefix as string) || 'attachment_';
|
||||
executionData = await downloadAttachments.call(this, responseData as IDataObject, prefix);
|
||||
} else {
|
||||
executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
}
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import * as create from './create.operation';
|
||||
import * as del from './delete.operation';
|
||||
import * as get from './get.operation';
|
||||
import * as send from './send.operation';
|
||||
import * as update from './update.operation';
|
||||
|
||||
export { create, del as delete, get, send, update };
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['draft'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a new email draft',
|
||||
action: 'Create a draft',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete an email draft',
|
||||
action: 'Delete a draft',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Retrieve an email draft',
|
||||
action: 'Get a draft',
|
||||
},
|
||||
{
|
||||
name: 'Send',
|
||||
value: 'send',
|
||||
description: 'Send an existing email draft',
|
||||
action: 'Send a draft',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update an email draft',
|
||||
action: 'Update a draft',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
},
|
||||
|
||||
...create.description,
|
||||
...del.description,
|
||||
...get.description,
|
||||
...send.description,
|
||||
...update.description,
|
||||
];
|
||||
@@ -0,0 +1,54 @@
|
||||
import type { IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { draftRLC } from '../../descriptions';
|
||||
import { makeRecipient } from '../../helpers/utils';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
draftRLC,
|
||||
{
|
||||
displayName: 'To',
|
||||
name: 'to',
|
||||
description: 'Comma-separated list of email addresses of recipients',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['draft'],
|
||||
operation: ['send'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
const draftId = this.getNodeParameter('draftId', index, undefined, { extractValue: true });
|
||||
const to = this.getNodeParameter('to', index) as string;
|
||||
|
||||
if (to) {
|
||||
const recipients = to
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter((email) => email);
|
||||
|
||||
if (recipients.length !== 0) {
|
||||
await microsoftApiRequest.call(this, 'PATCH', `/messages/${draftId}`, {
|
||||
toRecipients: recipients.map((recipient: string) => makeRecipient(recipient)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await microsoftApiRequest.call(this, 'POST', `/messages/${draftId}/send`);
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray({ success: true }),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
import type { IDataObject, IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { draftRLC } from '../../descriptions';
|
||||
import { createMessage } from '../../helpers/utils';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
draftRLC,
|
||||
{
|
||||
displayName: 'Update Fields',
|
||||
name: 'updateFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'BCC Recipients',
|
||||
name: 'bccRecipients',
|
||||
description: 'Comma-separated list of email addresses of BCC recipients',
|
||||
type: 'string',
|
||||
placeholder: 'e.g. john@example.com',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Category Names or IDs',
|
||||
name: 'categories',
|
||||
type: 'multiOptions',
|
||||
description:
|
||||
'Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getCategoriesNames',
|
||||
},
|
||||
default: [],
|
||||
},
|
||||
{
|
||||
displayName: 'CC Recipients',
|
||||
name: 'ccRecipients',
|
||||
description: 'Comma-separated list of email addresses of CC recipients',
|
||||
type: 'string',
|
||||
placeholder: 'e.g. john@example.com',
|
||||
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 from which the message is sent. Must correspond to the actual mailbox used.',
|
||||
type: 'string',
|
||||
placeholder: 'e.g. john@example.com',
|
||||
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: 'Normal',
|
||||
},
|
||||
{
|
||||
displayName: 'Is Read',
|
||||
name: 'isRead',
|
||||
description: 'Whether the message must be marked as read',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
displayName: 'Message',
|
||||
name: 'bodyContent',
|
||||
description: 'Message body content',
|
||||
type: 'string',
|
||||
typeOptions: {
|
||||
rows: 2,
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Message Type',
|
||||
name: 'bodyContentType',
|
||||
description: 'Message body content type',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'HTML',
|
||||
value: 'html',
|
||||
},
|
||||
{
|
||||
name: 'Text',
|
||||
value: 'Text',
|
||||
},
|
||||
],
|
||||
default: 'html',
|
||||
},
|
||||
{
|
||||
displayName: 'Read Receipt Requested',
|
||||
name: 'isReadReceiptRequested',
|
||||
description: 'Whether a read receipt is requested for the message',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
displayName: 'Reply To',
|
||||
name: 'replyTo',
|
||||
description: 'Email address to use when replying',
|
||||
type: 'string',
|
||||
placeholder: 'e.g. replyto@example.com',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Subject',
|
||||
name: 'subject',
|
||||
description: 'The subject of the message',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'To',
|
||||
name: 'toRecipients',
|
||||
description: 'Comma-separated list of email addresses of recipients',
|
||||
type: 'string',
|
||||
placeholder: 'e.g. john@example.com',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['draft'],
|
||||
operation: ['update'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
const draftId = this.getNodeParameter('draftId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
const updateFields = this.getNodeParameter('updateFields', index);
|
||||
|
||||
// Create message from optional fields
|
||||
const body: IDataObject = createMessage(updateFields);
|
||||
|
||||
const responseData = await microsoftApiRequest.call(
|
||||
this,
|
||||
'PATCH',
|
||||
`/messages/${draftId}`,
|
||||
body,
|
||||
{},
|
||||
);
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
import { DateTime } from 'luxon';
|
||||
import moment from 'moment-timezone';
|
||||
import type { IDataObject, IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { calendarRLC } from '../../descriptions';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
calendarRLC,
|
||||
{
|
||||
displayName: 'Title',
|
||||
name: 'subject',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Start',
|
||||
name: 'startDateTime',
|
||||
type: 'dateTime',
|
||||
default: DateTime.now().toISO(),
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
displayName: 'End',
|
||||
name: 'endDateTime',
|
||||
type: 'dateTime',
|
||||
required: true,
|
||||
default: DateTime.now().plus({ minutes: 30 }).toISO(),
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-multi-options
|
||||
displayName: 'Categories',
|
||||
name: 'categories',
|
||||
type: 'multiOptions',
|
||||
description:
|
||||
'Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getCategoriesNames',
|
||||
},
|
||||
default: [],
|
||||
},
|
||||
{
|
||||
displayName: 'Description',
|
||||
name: 'body',
|
||||
type: 'string',
|
||||
typeOptions: {
|
||||
rows: 2,
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Description Preview',
|
||||
name: 'bodyPreview',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Hide Attendees',
|
||||
name: 'hideAttendees',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description:
|
||||
'Whether to allow each attendee to only see themselves in the meeting request and meeting tracking list',
|
||||
},
|
||||
{
|
||||
displayName: 'Importance',
|
||||
name: 'importance',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Low',
|
||||
value: 'low',
|
||||
},
|
||||
{
|
||||
name: 'Normal',
|
||||
value: 'normal',
|
||||
},
|
||||
{
|
||||
name: 'High',
|
||||
value: 'high',
|
||||
},
|
||||
],
|
||||
default: 'normal',
|
||||
},
|
||||
{
|
||||
displayName: 'Is All Day',
|
||||
name: 'isAllDay',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
displayName: 'Is Cancelled',
|
||||
name: 'isCancelled',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
displayName: 'Is Draft',
|
||||
name: 'isDraft',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
displayName: 'Is Online Meeting',
|
||||
name: 'isOnlineMeeting',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
displayName: 'Sensitivity',
|
||||
name: 'sensitivity',
|
||||
type: 'options',
|
||||
default: 'normal',
|
||||
options: [
|
||||
{
|
||||
name: 'Normal',
|
||||
value: 'normal',
|
||||
},
|
||||
{
|
||||
name: 'Personal',
|
||||
value: 'personal',
|
||||
},
|
||||
{
|
||||
name: 'Private',
|
||||
value: 'private',
|
||||
},
|
||||
{
|
||||
name: 'Confidential',
|
||||
value: 'confidential',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Show As',
|
||||
name: 'showAs',
|
||||
type: 'options',
|
||||
default: 'free',
|
||||
options: [
|
||||
{
|
||||
name: 'Busy',
|
||||
value: 'busy',
|
||||
},
|
||||
{
|
||||
name: 'Free',
|
||||
value: 'free',
|
||||
},
|
||||
{
|
||||
name: 'Oof',
|
||||
value: 'oof',
|
||||
},
|
||||
{
|
||||
name: 'Tentative',
|
||||
value: 'tentative',
|
||||
},
|
||||
{
|
||||
name: 'Working Elsewhere',
|
||||
value: 'workingElsewhere',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Timezone',
|
||||
name: 'timeZone',
|
||||
type: 'options',
|
||||
default: 'UTC',
|
||||
options: moment.tz.names().map((name) => ({
|
||||
name,
|
||||
value: name,
|
||||
})),
|
||||
},
|
||||
{
|
||||
displayName: 'Type',
|
||||
name: 'type',
|
||||
type: 'options',
|
||||
default: 'singleInstance',
|
||||
options: [
|
||||
{
|
||||
name: 'Single Instance',
|
||||
value: 'singleInstance',
|
||||
},
|
||||
{
|
||||
name: 'Occurrence',
|
||||
value: 'occurrence',
|
||||
},
|
||||
{
|
||||
name: 'Exception',
|
||||
value: 'exception',
|
||||
},
|
||||
{
|
||||
name: 'Series Master',
|
||||
value: 'seriesMaster',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['event'],
|
||||
operation: ['create'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
let additionalFields = this.getNodeParameter('additionalFields', index);
|
||||
|
||||
additionalFields = Object.keys(additionalFields).reduce((acc: IDataObject, key: string) => {
|
||||
if (additionalFields[key] !== '' || additionalFields[key] !== undefined) {
|
||||
acc[key] = additionalFields[key];
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const calendarId = this.getNodeParameter('calendarId', index, '', {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
if (calendarId === '') {
|
||||
throw new NodeOperationError(this.getNode(), 'Calendar ID is required');
|
||||
}
|
||||
const subject = this.getNodeParameter('subject', index) as string;
|
||||
|
||||
const endpoint = `/calendars/${calendarId}/events`;
|
||||
|
||||
let timeZone = 'UTC';
|
||||
|
||||
if (additionalFields.timeZone) {
|
||||
timeZone = additionalFields.timeZone as string;
|
||||
delete additionalFields.timeZone;
|
||||
}
|
||||
|
||||
if (additionalFields.body) {
|
||||
additionalFields.body = {
|
||||
content: additionalFields.body,
|
||||
contentType: 'html',
|
||||
};
|
||||
}
|
||||
|
||||
let startDateTime = this.getNodeParameter('startDateTime', index) as string;
|
||||
let endDateTime = this.getNodeParameter('endDateTime', index) as string;
|
||||
|
||||
if (additionalFields.isAllDay) {
|
||||
startDateTime = DateTime.fromISO(startDateTime, { zone: timeZone }).toFormat('yyyy-MM-dd');
|
||||
endDateTime = DateTime.fromISO(endDateTime, { zone: timeZone }).toFormat('yyyy-MM-dd');
|
||||
|
||||
const minimalWholeDayDuration = 24;
|
||||
const duration = DateTime.fromISO(startDateTime, { zone: timeZone }).diff(
|
||||
DateTime.fromISO(endDateTime, { zone: timeZone }),
|
||||
).hours;
|
||||
|
||||
if (duration < minimalWholeDayDuration) {
|
||||
endDateTime = DateTime.fromISO(startDateTime, { zone: timeZone }).plus({ hours: 24 }).toISO();
|
||||
}
|
||||
}
|
||||
|
||||
const body: IDataObject = {
|
||||
subject,
|
||||
start: {
|
||||
dateTime: startDateTime,
|
||||
timeZone,
|
||||
},
|
||||
end: {
|
||||
dateTime: endDateTime,
|
||||
timeZone,
|
||||
},
|
||||
...additionalFields,
|
||||
};
|
||||
|
||||
const responseData = await microsoftApiRequest.call(this, 'POST', endpoint, body);
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import type { IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { calendarRLC, eventRLC } from '../../descriptions';
|
||||
import { decodeOutlookId } from '../../helpers/utils';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [calendarRLC, eventRLC];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['event'],
|
||||
operation: ['delete'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
const eventId = decodeOutlookId(
|
||||
this.getNodeParameter('eventId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string,
|
||||
);
|
||||
|
||||
await microsoftApiRequest.call(this, 'DELETE', `/calendar/events/${eventId}`);
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray({ success: true }),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import type { IDataObject, IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { calendarRLC, eventRLC } from '../../descriptions';
|
||||
import { decodeOutlookId, eventfields } from '../../helpers/utils';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
calendarRLC,
|
||||
eventRLC,
|
||||
{
|
||||
displayName: 'Output',
|
||||
name: 'output',
|
||||
type: 'options',
|
||||
default: 'simple',
|
||||
options: [
|
||||
{
|
||||
name: 'Simplified',
|
||||
value: 'simple',
|
||||
},
|
||||
{
|
||||
name: 'Raw',
|
||||
value: 'raw',
|
||||
},
|
||||
{
|
||||
name: 'Select Included Fields',
|
||||
value: 'fields',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Fields',
|
||||
name: 'fields',
|
||||
type: 'multiOptions',
|
||||
description: 'The fields to add to the output',
|
||||
displayOptions: {
|
||||
show: {
|
||||
output: ['fields'],
|
||||
},
|
||||
},
|
||||
options: eventfields,
|
||||
default: [],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['event'],
|
||||
operation: ['get'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
const qs = {} as IDataObject;
|
||||
|
||||
const eventId = decodeOutlookId(
|
||||
this.getNodeParameter('eventId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string,
|
||||
);
|
||||
|
||||
const output = this.getNodeParameter('output', index) as string;
|
||||
|
||||
if (output === 'fields') {
|
||||
const fields = this.getNodeParameter('fields', index) as string[];
|
||||
qs.$select = fields.join(',');
|
||||
}
|
||||
|
||||
if (output === 'simple') {
|
||||
qs.$select = 'id,subject,bodyPreview,start,end,organizer,attendees,webLink';
|
||||
}
|
||||
|
||||
const endpoint = `/calendar/events/${eventId}`;
|
||||
|
||||
const responseData = await microsoftApiRequest.call(this, 'GET', endpoint, undefined, qs);
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject[]),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
import type { IDataObject, IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { calendarRLC, returnAllOrLimit } from '../../descriptions';
|
||||
import { eventfields } from '../../helpers/utils';
|
||||
import { microsoftApiRequest, microsoftApiRequestAllItems } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'From All Calendars',
|
||||
name: 'fromAllCalendars',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
...calendarRLC,
|
||||
displayOptions: {
|
||||
show: {
|
||||
fromAllCalendars: [false],
|
||||
},
|
||||
},
|
||||
},
|
||||
...returnAllOrLimit,
|
||||
{
|
||||
displayName: 'Output',
|
||||
name: 'output',
|
||||
type: 'options',
|
||||
default: 'simple',
|
||||
options: [
|
||||
{
|
||||
name: 'Simplified',
|
||||
value: 'simple',
|
||||
},
|
||||
{
|
||||
name: 'Raw',
|
||||
value: 'raw',
|
||||
},
|
||||
{
|
||||
name: 'Select Included Fields',
|
||||
value: 'fields',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Fields',
|
||||
name: 'fields',
|
||||
type: 'multiOptions',
|
||||
description: 'The fields to add to the output',
|
||||
displayOptions: {
|
||||
show: {
|
||||
output: ['fields'],
|
||||
},
|
||||
},
|
||||
options: eventfields,
|
||||
default: [],
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Filter',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Filter Query',
|
||||
name: 'custom',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: "e.g. contains(subject,'Hello')",
|
||||
hint: 'Search query to filter events. <a href="https://learn.microsoft.com/en-us/graph/filter-query-parameter">More info</a>.',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['event'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
const responseData: IDataObject[] = [];
|
||||
const qs = {} as IDataObject;
|
||||
|
||||
const returnAll = this.getNodeParameter('returnAll', index);
|
||||
const filters = this.getNodeParameter('filters', index, {});
|
||||
const output = this.getNodeParameter('output', index) as string;
|
||||
|
||||
if (output === 'fields') {
|
||||
const fields = this.getNodeParameter('fields', index) as string[];
|
||||
qs.$select = fields.join(',');
|
||||
}
|
||||
|
||||
if (output === 'simple') {
|
||||
qs.$select = 'id,subject,bodyPreview,start,end,organizer,attendees,webLink';
|
||||
}
|
||||
|
||||
if (Object.keys(filters).length) {
|
||||
const filterString: string[] = [];
|
||||
|
||||
if (filters.custom) {
|
||||
filterString.push(filters.custom as string);
|
||||
}
|
||||
|
||||
if (filterString.length) {
|
||||
qs.$filter = filterString.join(' and ');
|
||||
}
|
||||
}
|
||||
|
||||
const calendars: string[] = [];
|
||||
|
||||
const fromAllCalendars = this.getNodeParameter('fromAllCalendars', index) as boolean;
|
||||
|
||||
if (fromAllCalendars) {
|
||||
const response = await microsoftApiRequest.call(this, 'GET', '/calendars', undefined, {
|
||||
$select: 'id',
|
||||
});
|
||||
for (const calendar of response.value) {
|
||||
calendars.push(calendar.id as string);
|
||||
}
|
||||
} else {
|
||||
const calendarId = this.getNodeParameter('calendarId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
calendars.push(calendarId);
|
||||
}
|
||||
const limit = this.getNodeParameter('limit', index, 0);
|
||||
|
||||
for (const calendarId of calendars) {
|
||||
const endpoint = `/calendars/${calendarId}/events`;
|
||||
|
||||
if (returnAll) {
|
||||
const response = await microsoftApiRequestAllItems.call(
|
||||
this,
|
||||
'value',
|
||||
'GET',
|
||||
endpoint,
|
||||
undefined,
|
||||
qs,
|
||||
);
|
||||
responseData.push(...response);
|
||||
} else {
|
||||
qs.$top = limit - responseData.length;
|
||||
|
||||
if (qs.$top <= 0) break;
|
||||
|
||||
const response = await microsoftApiRequest.call(this, 'GET', endpoint, undefined, qs);
|
||||
responseData.push(...response.value);
|
||||
}
|
||||
}
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import * as create from './create.operation';
|
||||
import * as del from './delete.operation';
|
||||
import * as get from './get.operation';
|
||||
import * as getAll from './getAll.operation';
|
||||
import * as update from './update.operation';
|
||||
|
||||
export { create, del as delete, get, getAll, update };
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['event'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a new event',
|
||||
action: 'Create an event',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete an event',
|
||||
action: 'Delete an event',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Retrieve an event',
|
||||
action: 'Get an event',
|
||||
},
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
description: 'List and search events',
|
||||
action: 'Get many events',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update an event',
|
||||
action: 'Update an event',
|
||||
},
|
||||
],
|
||||
default: 'getAll',
|
||||
},
|
||||
|
||||
...create.description,
|
||||
...del.description,
|
||||
...get.description,
|
||||
...getAll.description,
|
||||
...update.description,
|
||||
];
|
||||
@@ -0,0 +1,284 @@
|
||||
import { DateTime } from 'luxon';
|
||||
import type { IDataObject, IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { calendarRLC, eventRLC } from '../../descriptions';
|
||||
import { decodeOutlookId } from '../../helpers/utils';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
calendarRLC,
|
||||
eventRLC,
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-multi-options
|
||||
displayName: 'Categories',
|
||||
name: 'categories',
|
||||
type: 'multiOptions',
|
||||
description:
|
||||
'Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getCategoriesNames',
|
||||
},
|
||||
default: [],
|
||||
},
|
||||
{
|
||||
displayName: 'Description',
|
||||
name: 'body',
|
||||
type: 'string',
|
||||
typeOptions: {
|
||||
rows: 2,
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Description Preview',
|
||||
name: 'bodyPreview',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'End',
|
||||
name: 'end',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Hide Attendees',
|
||||
name: 'hideAttendees',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description:
|
||||
'Whether to allow each attendee to only see themselves in the meeting request and meeting tracking list',
|
||||
},
|
||||
{
|
||||
displayName: 'Importance',
|
||||
name: 'importance',
|
||||
type: 'options',
|
||||
default: 'low',
|
||||
options: [
|
||||
{
|
||||
name: 'Low',
|
||||
value: 'low',
|
||||
},
|
||||
{
|
||||
name: 'Normal',
|
||||
value: 'normal',
|
||||
},
|
||||
{
|
||||
name: 'High',
|
||||
value: 'high',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Is All Day',
|
||||
name: 'isAllDay',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
displayName: 'Is Cancelled',
|
||||
name: 'isCancelled',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
displayName: 'Is Draft',
|
||||
name: 'isDraft',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
displayName: 'Is Online Meeting',
|
||||
name: 'isOnlineMeeting',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Sensitivity',
|
||||
name: 'sensitivity',
|
||||
type: 'options',
|
||||
default: 'normal',
|
||||
options: [
|
||||
{
|
||||
name: 'Normal',
|
||||
value: 'normal',
|
||||
},
|
||||
{
|
||||
name: 'Personal',
|
||||
value: 'personal',
|
||||
},
|
||||
{
|
||||
name: 'Private',
|
||||
value: 'private',
|
||||
},
|
||||
{
|
||||
name: 'Confidential',
|
||||
value: 'confidential',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Show As',
|
||||
name: 'showAs',
|
||||
type: 'options',
|
||||
default: 'free',
|
||||
options: [
|
||||
{
|
||||
name: 'Busy',
|
||||
value: 'busy',
|
||||
},
|
||||
{
|
||||
name: 'Free',
|
||||
value: 'free',
|
||||
},
|
||||
{
|
||||
name: 'Oof',
|
||||
value: 'oof',
|
||||
},
|
||||
{
|
||||
name: 'Tentative',
|
||||
value: 'tentative',
|
||||
},
|
||||
{
|
||||
name: 'Working Elsewhere',
|
||||
value: 'workingElsewhere',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Start',
|
||||
name: 'start',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Timezone',
|
||||
name: 'timeZone',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Title',
|
||||
name: 'subject',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Type',
|
||||
name: 'type',
|
||||
type: 'options',
|
||||
default: 'singleInstance',
|
||||
options: [
|
||||
{
|
||||
name: 'Single Instance',
|
||||
value: 'singleInstance',
|
||||
},
|
||||
{
|
||||
name: 'Occurrence',
|
||||
value: 'occurrence',
|
||||
},
|
||||
{
|
||||
name: 'Exception',
|
||||
value: 'exception',
|
||||
},
|
||||
{
|
||||
name: 'Series Master',
|
||||
value: 'seriesMaster',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['event'],
|
||||
operation: ['update'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
const additionalFields = this.getNodeParameter('additionalFields', index);
|
||||
|
||||
const eventId = decodeOutlookId(
|
||||
this.getNodeParameter('eventId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string,
|
||||
);
|
||||
|
||||
let timeZone = 'UTC';
|
||||
|
||||
if (additionalFields.timeZone) {
|
||||
timeZone = additionalFields.timeZone as string;
|
||||
delete additionalFields.timeZone;
|
||||
}
|
||||
|
||||
if (additionalFields.body) {
|
||||
additionalFields.body = {
|
||||
content: additionalFields.body,
|
||||
contentType: 'html',
|
||||
};
|
||||
}
|
||||
|
||||
let startDateTime = additionalFields.start as string;
|
||||
let endDateTime = additionalFields.end as string;
|
||||
|
||||
if (additionalFields.isAllDay) {
|
||||
startDateTime =
|
||||
DateTime.fromISO(startDateTime, { zone: timeZone }).toFormat('yyyy-MM-dd') ||
|
||||
DateTime.utc().toFormat('yyyy-MM-dd');
|
||||
endDateTime =
|
||||
DateTime.fromISO(endDateTime, { zone: timeZone }).toFormat('yyyy-MM-dd') ||
|
||||
DateTime.utc().toFormat('yyyy-MM-dd');
|
||||
|
||||
const minimalWholeDayDuration = 24;
|
||||
const duration = DateTime.fromISO(startDateTime, { zone: timeZone }).diff(
|
||||
DateTime.fromISO(endDateTime, { zone: timeZone }),
|
||||
).hours;
|
||||
|
||||
if (duration < minimalWholeDayDuration) {
|
||||
endDateTime = DateTime.fromISO(startDateTime, { zone: timeZone }).plus({ hours: 24 }).toISO();
|
||||
}
|
||||
}
|
||||
|
||||
const body: IDataObject = {
|
||||
...additionalFields,
|
||||
};
|
||||
|
||||
if (startDateTime) {
|
||||
body.start = {
|
||||
dateTime: startDateTime,
|
||||
timeZone,
|
||||
};
|
||||
}
|
||||
|
||||
if (endDateTime) {
|
||||
body.end = {
|
||||
dateTime: endDateTime,
|
||||
timeZone,
|
||||
};
|
||||
}
|
||||
|
||||
const endpoint = `/calendar/events/${eventId}`;
|
||||
|
||||
const responseData = await microsoftApiRequest.call(this, 'PATCH', endpoint, body);
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import type { IDataObject, IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { folderRLC } from '../../descriptions';
|
||||
import { decodeOutlookId } from '../../helpers/utils';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'displayName',
|
||||
description: 'Name of the folder',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
placeholder: 'e.g. My Folder',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add option',
|
||||
default: {},
|
||||
options: [{ ...folderRLC, displayName: 'Parent Folder', required: false }],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['folder'],
|
||||
operation: ['create'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
const displayName = this.getNodeParameter('displayName', index) as string;
|
||||
|
||||
const folderId = decodeOutlookId(
|
||||
this.getNodeParameter('options.folderId', index, '', {
|
||||
extractValue: true,
|
||||
}) as string,
|
||||
);
|
||||
|
||||
const body: IDataObject = {
|
||||
displayName,
|
||||
};
|
||||
|
||||
let endpoint;
|
||||
|
||||
if (folderId) {
|
||||
endpoint = `/mailFolders/${folderId}/childFolders`;
|
||||
} else {
|
||||
endpoint = '/mailFolders';
|
||||
}
|
||||
|
||||
const responseData = await microsoftApiRequest.call(this, 'POST', endpoint, body);
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject[]),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import type { IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { folderRLC } from '../../descriptions';
|
||||
import { decodeOutlookId } from '../../helpers/utils';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [folderRLC];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['folder'],
|
||||
operation: ['delete'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
const folderId = decodeOutlookId(
|
||||
this.getNodeParameter('folderId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string,
|
||||
);
|
||||
|
||||
await microsoftApiRequest.call(this, 'DELETE', `/mailFolders/${folderId}`);
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray({ success: true }),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import type { IDataObject, IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { folderFields, folderRLC } from '../../descriptions';
|
||||
import { decodeOutlookId } from '../../helpers/utils';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
folderRLC,
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add option',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Fields',
|
||||
name: 'fields',
|
||||
type: 'multiOptions',
|
||||
description: 'The fields to add to the output',
|
||||
options: folderFields,
|
||||
default: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['folder'],
|
||||
operation: ['get'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
const qs: IDataObject = {};
|
||||
|
||||
const folderId = decodeOutlookId(
|
||||
this.getNodeParameter('folderId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string,
|
||||
);
|
||||
|
||||
const options = this.getNodeParameter('options', index);
|
||||
|
||||
if (options.fields) {
|
||||
qs.$select = (options.fields as string[]).join(',');
|
||||
}
|
||||
|
||||
if (options.filter) {
|
||||
qs.$filter = options.filter;
|
||||
}
|
||||
const responseData = await microsoftApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
`/mailFolders/${folderId}`,
|
||||
{},
|
||||
qs,
|
||||
);
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
import type { IDataObject, IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { folderFields, folderRLC, returnAllOrLimit } from '../../descriptions';
|
||||
import { getSubfolders, microsoftApiRequest, microsoftApiRequestAllItems } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
...returnAllOrLimit,
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Filter',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Filter Query',
|
||||
name: 'filter',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: "e.g. displayName eq 'My Folder'",
|
||||
hint: 'Search query to filter folders. <a href="https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter">More info</a>.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Fields',
|
||||
name: 'fields',
|
||||
type: 'multiOptions',
|
||||
description: 'The fields to add to the output',
|
||||
options: folderFields,
|
||||
default: [],
|
||||
},
|
||||
{
|
||||
displayName: 'Include Child Folders',
|
||||
name: 'includeChildFolders',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Whether to include child folders in the response',
|
||||
},
|
||||
{
|
||||
...folderRLC,
|
||||
displayName: 'Parent Folder',
|
||||
required: false,
|
||||
description: 'The folder you want to search in',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['folder'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
let responseData;
|
||||
const qs: IDataObject = {};
|
||||
|
||||
const returnAll = this.getNodeParameter('returnAll', index);
|
||||
const options = this.getNodeParameter('options', index);
|
||||
const filter = this.getNodeParameter('filters.filter', index, '') as string;
|
||||
|
||||
const parentFolderId = this.getNodeParameter('options.folderId', index, '', {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
if (options.fields) {
|
||||
qs.$select = (options.fields as string[]).join(',');
|
||||
}
|
||||
|
||||
if (filter) {
|
||||
qs.$filter = filter;
|
||||
}
|
||||
|
||||
let endpoint;
|
||||
if (parentFolderId) {
|
||||
endpoint = `/mailFolders/${parentFolderId}/childFolders`;
|
||||
} else {
|
||||
endpoint = '/mailFolders';
|
||||
}
|
||||
|
||||
if (returnAll) {
|
||||
responseData = await microsoftApiRequestAllItems.call(this, 'value', 'GET', endpoint, {}, qs);
|
||||
} else {
|
||||
qs.$top = this.getNodeParameter('limit', index);
|
||||
responseData = await microsoftApiRequest.call(this, 'GET', endpoint, {}, qs);
|
||||
responseData = responseData.value;
|
||||
}
|
||||
|
||||
if (options.includeChildFolders) {
|
||||
responseData = await getSubfolders.call(this, responseData as IDataObject[]);
|
||||
}
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject[]),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import * as create from './create.operation';
|
||||
import * as del from './delete.operation';
|
||||
import * as get from './get.operation';
|
||||
import * as getAll from './getAll.operation';
|
||||
import * as update from './update.operation';
|
||||
|
||||
export { create, del as delete, get, getAll, update };
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['folder'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: "Create a mail folder in the root folder of the user's mailbox",
|
||||
action: 'Create a folder',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete a folder',
|
||||
action: 'Delete a folder',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Retrieve a folder',
|
||||
action: 'Get a folder',
|
||||
},
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
description: 'Get many folders',
|
||||
action: 'Get many folders',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update a folder',
|
||||
action: 'Update a folder',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
},
|
||||
...create.description,
|
||||
...del.description,
|
||||
...get.description,
|
||||
...getAll.description,
|
||||
...update.description,
|
||||
];
|
||||
@@ -0,0 +1,48 @@
|
||||
import type { IDataObject, IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { folderRLC } from '../../descriptions';
|
||||
import { decodeOutlookId } from '../../helpers/utils';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
folderRLC,
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'displayName',
|
||||
description: 'Name of the folder',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['folder'],
|
||||
operation: ['update'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
const folderId = decodeOutlookId(
|
||||
this.getNodeParameter('folderId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string,
|
||||
);
|
||||
const displayName = this.getNodeParameter('displayName', index, undefined) as string;
|
||||
|
||||
const responseData = await microsoftApiRequest.call(this, 'PATCH', `/mailFolders/${folderId}`, {
|
||||
displayName,
|
||||
});
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
+303
@@ -0,0 +1,303 @@
|
||||
import type {
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { folderRLC, returnAllOrLimit } from '../../descriptions';
|
||||
import {
|
||||
decodeOutlookId,
|
||||
messageFields,
|
||||
prepareFilterString,
|
||||
simplifyOutputMessages,
|
||||
} from '../../helpers/utils';
|
||||
import {
|
||||
downloadAttachments,
|
||||
microsoftApiRequest,
|
||||
microsoftApiRequestAllItems,
|
||||
} from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
folderRLC,
|
||||
...returnAllOrLimit,
|
||||
{
|
||||
displayName: 'Output',
|
||||
name: 'output',
|
||||
type: 'options',
|
||||
default: 'simple',
|
||||
options: [
|
||||
{
|
||||
name: 'Simplified',
|
||||
value: 'simple',
|
||||
},
|
||||
{
|
||||
name: 'Raw',
|
||||
value: 'raw',
|
||||
},
|
||||
{
|
||||
name: 'Select Included Fields',
|
||||
value: 'fields',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Fields',
|
||||
name: 'fields',
|
||||
type: 'multiOptions',
|
||||
description: 'The fields to add to the output',
|
||||
displayOptions: {
|
||||
show: {
|
||||
output: ['fields'],
|
||||
},
|
||||
},
|
||||
options: messageFields,
|
||||
default: [],
|
||||
},
|
||||
{
|
||||
displayName:
|
||||
'Fetching a lot of messages may take a long time. Consider using filters to speed things up',
|
||||
name: 'filtersNotice',
|
||||
type: 'notice',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
returnAll: [true],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filtersUI',
|
||||
type: 'fixedCollection',
|
||||
placeholder: 'Add Filters',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Values',
|
||||
name: 'values',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Filter By',
|
||||
name: 'filterBy',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Filters',
|
||||
value: 'filters',
|
||||
},
|
||||
{
|
||||
name: 'Search',
|
||||
value: 'search',
|
||||
},
|
||||
],
|
||||
default: 'filters',
|
||||
},
|
||||
{
|
||||
displayName: 'Search',
|
||||
name: 'search',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'e.g. automation',
|
||||
description:
|
||||
'Only return messages that contains search term. Without specific message properties, the search is carried out on the default search properties of from, subject, and body. <a href="https://docs.microsoft.com/en-us/graph/query-parameters#search-parameter target="_blank">More info</a>.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
filterBy: ['search'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Filter',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
filterBy: ['filters'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Filter Query',
|
||||
name: 'custom',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'e.g. isRead eq false',
|
||||
hint: 'Search query to filter messages. <a href="https://learn.microsoft.com/en-us/graph/filter-query-parameter">More info</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Has Attachments',
|
||||
name: 'hasAttachments',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
displayName: 'Read Status',
|
||||
name: 'readStatus',
|
||||
type: 'options',
|
||||
default: 'unread',
|
||||
hint: 'Filter messages by whether they have been read or not',
|
||||
options: [
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
|
||||
name: 'Unread and read messages',
|
||||
value: 'both',
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
|
||||
name: 'Unread messages only',
|
||||
value: 'unread',
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
|
||||
name: 'Read messages only',
|
||||
value: 'read',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Received After',
|
||||
name: 'receivedAfter',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description:
|
||||
'Get all messages received after the specified date. In an expression you can set date using string in ISO format or a timestamp in miliseconds.',
|
||||
},
|
||||
{
|
||||
displayName: 'Received Before',
|
||||
name: 'receivedBefore',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description:
|
||||
'Get all messages received before the specified date. In an expression you can set date using string in ISO format or a timestamp in miliseconds.',
|
||||
},
|
||||
{
|
||||
displayName: 'Sender',
|
||||
name: 'sender',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Sender name or email to filter by',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add option',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Attachments Prefix',
|
||||
name: 'attachmentsPrefix',
|
||||
type: 'string',
|
||||
default: 'attachment_',
|
||||
description:
|
||||
'Prefix for name of the output fields to put the binary files data in. An index starting from 0 will be added. So if name is "attachment_" the first attachment is saved to "attachment_0".',
|
||||
},
|
||||
{
|
||||
displayName: 'Download Attachments',
|
||||
name: 'downloadAttachments',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description:
|
||||
"Whether the message's attachments will be downloaded and included in the output",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['folderMessage'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
let responseData;
|
||||
const qs: IDataObject = {};
|
||||
|
||||
const folderId = decodeOutlookId(
|
||||
this.getNodeParameter('folderId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string,
|
||||
);
|
||||
|
||||
const returnAll = this.getNodeParameter('returnAll', index);
|
||||
const filters = this.getNodeParameter('filtersUI.values', index, {}) as IDataObject;
|
||||
const options = this.getNodeParameter('options', index, {});
|
||||
const output = this.getNodeParameter('output', index) as string;
|
||||
|
||||
if (output === 'fields') {
|
||||
const fields = this.getNodeParameter('fields', index) as string[];
|
||||
|
||||
if (options.downloadAttachments) {
|
||||
fields.push('hasAttachments');
|
||||
}
|
||||
|
||||
qs.$select = fields.join(',');
|
||||
}
|
||||
|
||||
if (output === 'simple') {
|
||||
qs.$select =
|
||||
'id,conversationId,subject,bodyPreview,from,toRecipients,categories,hasAttachments';
|
||||
}
|
||||
|
||||
if (filters.filterBy === 'search' && filters.search !== '') {
|
||||
qs.$search = `"${filters.search}"`;
|
||||
}
|
||||
|
||||
if (filters.filterBy === 'filters') {
|
||||
const filterString = prepareFilterString(filters);
|
||||
|
||||
if (filterString) {
|
||||
qs.$filter = filterString;
|
||||
}
|
||||
}
|
||||
|
||||
const endpoint = `/mailFolders/${folderId}/messages`;
|
||||
|
||||
if (returnAll) {
|
||||
responseData = await microsoftApiRequestAllItems.call(
|
||||
this,
|
||||
'value',
|
||||
'GET',
|
||||
endpoint,
|
||||
undefined,
|
||||
qs,
|
||||
);
|
||||
} else {
|
||||
qs.$top = this.getNodeParameter('limit', index);
|
||||
responseData = await microsoftApiRequest.call(this, 'GET', endpoint, undefined, qs);
|
||||
responseData = responseData.value;
|
||||
}
|
||||
|
||||
if (output === 'simple') {
|
||||
responseData = simplifyOutputMessages(responseData as IDataObject[]);
|
||||
}
|
||||
|
||||
let executionData: INodeExecutionData[] = [];
|
||||
|
||||
if (options.downloadAttachments) {
|
||||
const prefix = (options.attachmentsPrefix as string) || 'attachment_';
|
||||
executionData = await downloadAttachments.call(this, responseData as IDataObject, prefix);
|
||||
} else {
|
||||
executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject[]),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
}
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import * as getAll from './getAll.operation';
|
||||
|
||||
export { getAll };
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['folderMessage'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
description: 'Retrieves the messages in a folder',
|
||||
action: 'Get many folder messages',
|
||||
},
|
||||
],
|
||||
default: 'getAll',
|
||||
},
|
||||
|
||||
...getAll.description,
|
||||
];
|
||||
@@ -0,0 +1,31 @@
|
||||
import type { IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { messageRLC } from '../../descriptions';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [messageRLC];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['message'],
|
||||
operation: ['delete'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
const messageId = this.getNodeParameter('messageId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
await microsoftApiRequest.call(this, 'DELETE', `/messages/${messageId}`);
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray({ success: true }),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
import type {
|
||||
IBinaryKeyData,
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { messageRLC } from '../../descriptions';
|
||||
import { messageFields, simplifyOutputMessages } from '../../helpers/utils';
|
||||
import { downloadAttachments, getMimeContent, microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
messageRLC,
|
||||
{
|
||||
displayName: 'Output',
|
||||
name: 'output',
|
||||
type: 'options',
|
||||
default: 'simple',
|
||||
options: [
|
||||
{
|
||||
name: 'Simplified',
|
||||
value: 'simple',
|
||||
},
|
||||
{
|
||||
name: 'Raw',
|
||||
value: 'raw',
|
||||
},
|
||||
{
|
||||
name: 'Select Included Fields',
|
||||
value: 'fields',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Fields',
|
||||
name: 'fields',
|
||||
type: 'multiOptions',
|
||||
description: 'The fields to add to the output',
|
||||
displayOptions: {
|
||||
show: {
|
||||
output: ['fields'],
|
||||
},
|
||||
},
|
||||
options: messageFields,
|
||||
default: [],
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add option',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Attachments Prefix',
|
||||
name: 'attachmentsPrefix',
|
||||
type: 'string',
|
||||
default: 'attachment_',
|
||||
description:
|
||||
'Prefix for name of the output fields to put the binary files data in. An index starting from 0 will be added. So if name is "attachment_" the first attachment is saved to "attachment_0".',
|
||||
},
|
||||
{
|
||||
displayName: 'Download Attachments',
|
||||
name: 'downloadAttachments',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description:
|
||||
"Whether the message's attachments will be downloaded and included in the output",
|
||||
},
|
||||
{
|
||||
displayName: 'Get MIME Content',
|
||||
name: 'getMimeContent',
|
||||
type: 'fixedCollection',
|
||||
default: { values: { binaryPropertyName: 'data' } },
|
||||
options: [
|
||||
{
|
||||
displayName: 'Values',
|
||||
name: 'values',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Put Output in Field',
|
||||
name: 'binaryPropertyName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
hint: 'The name of the output field to put the binary file data in',
|
||||
},
|
||||
{
|
||||
displayName: 'File Name',
|
||||
name: 'outputFileName',
|
||||
type: 'string',
|
||||
placeholder: 'message',
|
||||
default: '',
|
||||
description: 'Optional name of the output file, if not set message ID is used',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['message'],
|
||||
operation: ['get'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
let responseData;
|
||||
const qs: IDataObject = {};
|
||||
|
||||
const messageId = this.getNodeParameter('messageId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
const options = this.getNodeParameter('options', index, {});
|
||||
const output = this.getNodeParameter('output', index) as string;
|
||||
|
||||
if (output === 'fields') {
|
||||
const fields = this.getNodeParameter('fields', index) as string[];
|
||||
|
||||
if (options.downloadAttachments) {
|
||||
fields.push('hasAttachments');
|
||||
}
|
||||
|
||||
qs.$select = fields.join(',');
|
||||
}
|
||||
|
||||
if (output === 'simple') {
|
||||
qs.$select =
|
||||
'id,conversationId,subject,bodyPreview,from,toRecipients,categories,hasAttachments';
|
||||
}
|
||||
|
||||
responseData = await microsoftApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
`/messages/${messageId}`,
|
||||
undefined,
|
||||
qs,
|
||||
);
|
||||
|
||||
if (output === 'simple') {
|
||||
responseData = simplifyOutputMessages([responseData as IDataObject]);
|
||||
}
|
||||
|
||||
let executionData: INodeExecutionData[] = [];
|
||||
|
||||
if (options.downloadAttachments) {
|
||||
const prefix = (options.attachmentsPrefix as string) || 'attachment_';
|
||||
executionData = await downloadAttachments.call(this, responseData as IDataObject, prefix);
|
||||
} else {
|
||||
executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
}
|
||||
|
||||
if (options.getMimeContent) {
|
||||
const { binaryPropertyName, outputFileName } = (options.getMimeContent as IDataObject)
|
||||
.values as IDataObject;
|
||||
|
||||
const binary = await getMimeContent.call(
|
||||
this,
|
||||
messageId,
|
||||
binaryPropertyName as string,
|
||||
outputFileName as string,
|
||||
);
|
||||
|
||||
executionData[0].binary = {
|
||||
...(executionData[0].binary || {}),
|
||||
...(binary as IBinaryKeyData),
|
||||
};
|
||||
}
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
import type {
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { returnAllOrLimit } from '../../descriptions';
|
||||
import { messageFields, prepareFilterString, simplifyOutputMessages } from '../../helpers/utils';
|
||||
import {
|
||||
downloadAttachments,
|
||||
microsoftApiRequest,
|
||||
microsoftApiRequestAllItems,
|
||||
} from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
...returnAllOrLimit,
|
||||
{
|
||||
displayName: 'Output',
|
||||
name: 'output',
|
||||
type: 'options',
|
||||
default: 'simple',
|
||||
options: [
|
||||
{
|
||||
name: 'Simplified',
|
||||
value: 'simple',
|
||||
},
|
||||
{
|
||||
name: 'Raw',
|
||||
value: 'raw',
|
||||
},
|
||||
{
|
||||
name: 'Select Included Fields',
|
||||
value: 'fields',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Fields',
|
||||
name: 'fields',
|
||||
type: 'multiOptions',
|
||||
description: 'The fields to add to the output',
|
||||
displayOptions: {
|
||||
show: {
|
||||
output: ['fields'],
|
||||
},
|
||||
},
|
||||
options: messageFields,
|
||||
default: [],
|
||||
},
|
||||
{
|
||||
displayName:
|
||||
'Fetching a lot of messages may take a long time. Consider using filters to speed things up',
|
||||
name: 'filtersNotice',
|
||||
type: 'notice',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
returnAll: [true],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filtersUI',
|
||||
type: 'fixedCollection',
|
||||
placeholder: 'Add Filters',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Values',
|
||||
name: 'values',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Filter By',
|
||||
name: 'filterBy',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Filters',
|
||||
value: 'filters',
|
||||
},
|
||||
{
|
||||
name: 'Search',
|
||||
value: 'search',
|
||||
},
|
||||
],
|
||||
default: 'filters',
|
||||
},
|
||||
{
|
||||
displayName: 'Search',
|
||||
name: 'search',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'e.g. automation',
|
||||
description:
|
||||
'Only return messages that contains search term. Without specific message properties, the search is carried out on the default search properties of from, subject, and body. <a href="https://docs.microsoft.com/en-us/graph/query-parameters#search-parameter target="_blank">More info</a>.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
filterBy: ['search'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Filter',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
filterBy: ['filters'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Filter Query',
|
||||
name: 'custom',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'e.g. isRead eq false',
|
||||
hint: 'Search query to filter messages. <a href="https://learn.microsoft.com/en-us/graph/filter-query-parameter">More info</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Has Attachments',
|
||||
name: 'hasAttachments',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
displayName: 'Folders to Exclude',
|
||||
name: 'foldersToExclude',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getFolders',
|
||||
},
|
||||
default: [],
|
||||
description:
|
||||
'Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
|
||||
},
|
||||
{
|
||||
displayName: 'Folders to Include',
|
||||
name: 'foldersToInclude',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getFolders',
|
||||
},
|
||||
default: [],
|
||||
description:
|
||||
'Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
|
||||
},
|
||||
{
|
||||
displayName: 'Read Status',
|
||||
name: 'readStatus',
|
||||
type: 'options',
|
||||
default: 'unread',
|
||||
hint: 'Filter messages by whether they have been read or not',
|
||||
options: [
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
|
||||
name: 'Unread and read messages',
|
||||
value: 'both',
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
|
||||
name: 'Unread messages only',
|
||||
value: 'unread',
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
|
||||
name: 'Read messages only',
|
||||
value: 'read',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Received After',
|
||||
name: 'receivedAfter',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description:
|
||||
'Get all messages received after the specified date. In an expression you can set date using string in ISO format or a timestamp in miliseconds.',
|
||||
},
|
||||
{
|
||||
displayName: 'Received Before',
|
||||
name: 'receivedBefore',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description:
|
||||
'Get all messages received before the specified date. In an expression you can set date using string in ISO format or a timestamp in miliseconds.',
|
||||
},
|
||||
{
|
||||
displayName: 'Sender',
|
||||
name: 'sender',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Sender name or email to filter by',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add option',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Attachments Prefix',
|
||||
name: 'attachmentsPrefix',
|
||||
type: 'string',
|
||||
default: 'attachment_',
|
||||
description:
|
||||
'Prefix for name of the output fields to put the binary files data in. An index starting from 0 will be added. So if name is "attachment_" the first attachment is saved to "attachment_0".',
|
||||
},
|
||||
{
|
||||
displayName: 'Download Attachments',
|
||||
name: 'downloadAttachments',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description:
|
||||
"Whether the message's attachments will be downloaded and included in the output",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['message'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
let responseData;
|
||||
const qs = {} as IDataObject;
|
||||
|
||||
const returnAll = this.getNodeParameter('returnAll', index);
|
||||
const filters = this.getNodeParameter('filtersUI.values', index, {}) as IDataObject;
|
||||
const options = this.getNodeParameter('options', index, {});
|
||||
const output = this.getNodeParameter('output', index) as string;
|
||||
|
||||
if (output === 'fields') {
|
||||
const fields = this.getNodeParameter('fields', index) as string[];
|
||||
|
||||
if (options.downloadAttachments) {
|
||||
fields.push('hasAttachments');
|
||||
}
|
||||
|
||||
qs.$select = fields.join(',');
|
||||
}
|
||||
|
||||
if (output === 'simple') {
|
||||
qs.$select =
|
||||
'id,conversationId,subject,bodyPreview,from,toRecipients,categories,hasAttachments';
|
||||
}
|
||||
|
||||
if (filters.filterBy === 'search' && filters.search !== '') {
|
||||
qs.$search = `"${filters.search}"`;
|
||||
}
|
||||
|
||||
if (filters.filterBy === 'filters') {
|
||||
const filterString = prepareFilterString(filters);
|
||||
|
||||
if (filterString) {
|
||||
qs.$filter = filterString;
|
||||
}
|
||||
}
|
||||
|
||||
const endpoint = '/messages';
|
||||
|
||||
if (returnAll) {
|
||||
responseData = await microsoftApiRequestAllItems.call(
|
||||
this,
|
||||
'value',
|
||||
'GET',
|
||||
endpoint,
|
||||
undefined,
|
||||
qs,
|
||||
);
|
||||
} else {
|
||||
qs.$top = this.getNodeParameter('limit', index);
|
||||
responseData = await microsoftApiRequest.call(this, 'GET', endpoint, undefined, qs);
|
||||
responseData = responseData.value;
|
||||
}
|
||||
|
||||
if (output === 'simple') {
|
||||
responseData = simplifyOutputMessages(responseData as IDataObject[]);
|
||||
}
|
||||
|
||||
let executionData: INodeExecutionData[] = [];
|
||||
|
||||
if (options.downloadAttachments) {
|
||||
const prefix = (options.attachmentsPrefix as string) || 'attachment_';
|
||||
executionData = await downloadAttachments.call(this, responseData as IDataObject, prefix);
|
||||
} else {
|
||||
executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject[]),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
}
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import { SEND_AND_WAIT_OPERATION, type INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import * as del from './delete.operation';
|
||||
import * as get from './get.operation';
|
||||
import * as getAll from './getAll.operation';
|
||||
import * as move from './move.operation';
|
||||
import * as reply from './reply.operation';
|
||||
import * as send from './send.operation';
|
||||
import * as sendAndWait from './sendAndWait.operation';
|
||||
import * as update from './update.operation';
|
||||
|
||||
export { del as delete, get, getAll, move, reply, send, sendAndWait, update };
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['message'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete a message',
|
||||
action: 'Delete a message',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Retrieve a single message',
|
||||
action: 'Get a message',
|
||||
},
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
description: 'List and search messages',
|
||||
action: 'Get many messages',
|
||||
},
|
||||
{
|
||||
name: 'Move',
|
||||
value: 'move',
|
||||
description: 'Move a message to a folder',
|
||||
action: 'Move a message',
|
||||
},
|
||||
{
|
||||
name: 'Reply',
|
||||
value: 'reply',
|
||||
description: 'Create a reply to a message',
|
||||
action: 'Reply to a message',
|
||||
},
|
||||
{
|
||||
name: 'Send',
|
||||
value: 'send',
|
||||
description: 'Send a message',
|
||||
action: 'Send a message',
|
||||
},
|
||||
{
|
||||
name: 'Send and Wait for Response',
|
||||
value: SEND_AND_WAIT_OPERATION,
|
||||
description: 'Send a message and wait for response',
|
||||
action: 'Send message and wait for response',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update a message',
|
||||
action: 'Update a message',
|
||||
},
|
||||
],
|
||||
default: 'send',
|
||||
},
|
||||
|
||||
...del.description,
|
||||
...get.description,
|
||||
...getAll.description,
|
||||
...move.description,
|
||||
...reply.description,
|
||||
...send.description,
|
||||
...sendAndWait.description,
|
||||
...update.description,
|
||||
];
|
||||
@@ -0,0 +1,43 @@
|
||||
import type { IDataObject, IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { folderRLC, messageRLC } from '../../descriptions';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
messageRLC,
|
||||
{ ...folderRLC, displayName: 'Parent Folder' },
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['message'],
|
||||
operation: ['move'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
const messageId = this.getNodeParameter('messageId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
const destinationId = this.getNodeParameter('folderId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
const body: IDataObject = {
|
||||
destinationId,
|
||||
};
|
||||
|
||||
await microsoftApiRequest.call(this, 'POST', `/messages/${messageId}/move`, body);
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray({ success: true }),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
import type {
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { messageRLC } from '../../descriptions';
|
||||
import { createMessage } from '../../helpers/utils';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
messageRLC,
|
||||
{
|
||||
displayName: 'Reply to Sender Only',
|
||||
name: 'replyToSenderOnly',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Whether to reply to the sender only or to the entire list of recipients',
|
||||
},
|
||||
{
|
||||
displayName: 'Message',
|
||||
name: 'message',
|
||||
// name: 'bodyContent',
|
||||
description: 'Message body content',
|
||||
type: 'string',
|
||||
typeOptions: {
|
||||
rows: 2,
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
replyToSenderOnly: [true],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Attachments',
|
||||
name: 'attachments',
|
||||
type: 'fixedCollection',
|
||||
placeholder: 'Add Attachment',
|
||||
default: {},
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'attachments',
|
||||
displayName: 'Attachment',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Input Data Field Name',
|
||||
name: 'binaryPropertyName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'e.g. data',
|
||||
hint: 'The name of the input field containing the binary file data to be attached',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'BCC Recipients',
|
||||
name: 'bccRecipients',
|
||||
description: 'Comma-separated list of email addresses of BCC recipients',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'CC Recipients',
|
||||
name: 'ccRecipients',
|
||||
description: 'Comma-separated list of 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 from 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: 'Normal',
|
||||
},
|
||||
{
|
||||
displayName: 'Message Type',
|
||||
name: 'bodyContentType',
|
||||
description: 'Message body content type',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'HTML',
|
||||
value: 'html',
|
||||
},
|
||||
{
|
||||
name: 'Text',
|
||||
value: 'Text',
|
||||
},
|
||||
],
|
||||
default: 'html',
|
||||
},
|
||||
{
|
||||
displayName: 'Read Receipt Requested',
|
||||
name: 'isReadReceiptRequested',
|
||||
description: 'Whether a read receipt is requested for the message',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
displayName: 'To',
|
||||
name: 'toRecipients',
|
||||
description: 'Comma-separated list of email addresses of recipients',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Reply To',
|
||||
name: 'replyTo',
|
||||
description: 'Email address to use when replying',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Subject',
|
||||
name: 'subject',
|
||||
description: 'The subject of the message',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Save as Draft',
|
||||
name: 'saveAsDraft',
|
||||
description:
|
||||
'Whether to save the message as a draft. If false, the message is sent immediately.',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['message'],
|
||||
operation: ['reply'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number, _: INodeExecutionData[]) {
|
||||
const messageId = this.getNodeParameter('messageId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
const replyToSenderOnly = this.getNodeParameter('replyToSenderOnly', index, false) as string;
|
||||
const message = this.getNodeParameter('message', index) as string;
|
||||
const saveAsDraft = this.getNodeParameter('options.saveAsDraft', index, false) as boolean;
|
||||
const additionalFields = this.getNodeParameter('additionalFields', index, {});
|
||||
|
||||
const body: IDataObject = {};
|
||||
|
||||
let action = 'createReply';
|
||||
|
||||
if (!replyToSenderOnly) {
|
||||
body.comment = message;
|
||||
action = 'createReplyAll';
|
||||
} else {
|
||||
// body.comment = comment;
|
||||
body.message = {} as IDataObject;
|
||||
additionalFields.bodyContent = message;
|
||||
Object.assign(body.message, createMessage(additionalFields));
|
||||
|
||||
delete (body.message as IDataObject).attachments;
|
||||
}
|
||||
|
||||
const 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: IDataObject[] = [];
|
||||
|
||||
for (const attachment of attachments) {
|
||||
const binaryPropertyName = attachment.binaryPropertyName as string;
|
||||
|
||||
const binaryData = this.helpers.assertBinaryData(index, binaryPropertyName);
|
||||
|
||||
let fileBase64;
|
||||
if (binaryData.id) {
|
||||
const chunkSize = 256 * 1024;
|
||||
const stream = await this.helpers.getBinaryStream(binaryData.id, chunkSize);
|
||||
const buffer = await this.helpers.binaryToBuffer(stream);
|
||||
fileBase64 = buffer.toString('base64');
|
||||
} else {
|
||||
fileBase64 = binaryData.data;
|
||||
}
|
||||
|
||||
data.push({
|
||||
'@odata.type': '#microsoft.graph.fileAttachment',
|
||||
name: binaryData.fileName,
|
||||
contentBytes: fileBase64,
|
||||
});
|
||||
}
|
||||
|
||||
for (const attachment of data) {
|
||||
await microsoftApiRequest.call(
|
||||
this,
|
||||
'POST',
|
||||
`/messages/${responseData.id}/attachments`,
|
||||
attachment,
|
||||
{},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!saveAsDraft) {
|
||||
await microsoftApiRequest.call(this, 'POST', `/messages/${responseData.id}/send`);
|
||||
}
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
import type {
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { createMessage } from '../../helpers/utils';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'To',
|
||||
name: 'toRecipients',
|
||||
description: 'Comma-separated list of email addresses of recipients',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Subject',
|
||||
name: 'subject',
|
||||
description: 'The subject of the message',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Message',
|
||||
name: 'bodyContent',
|
||||
description: 'Message body content',
|
||||
type: 'string',
|
||||
typeOptions: {
|
||||
rows: 2,
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Attachments',
|
||||
name: 'attachments',
|
||||
type: 'fixedCollection',
|
||||
placeholder: 'Add Attachment',
|
||||
default: {},
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'attachments',
|
||||
displayName: 'Attachment',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Input Data Field Name',
|
||||
name: 'binaryPropertyName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'e.g. data',
|
||||
hint: 'The name of the input field containing the binary file data to be attached',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'BCC Recipients',
|
||||
name: 'bccRecipients',
|
||||
description: 'Comma-separated list of email addresses of BCC recipients',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Category Names or IDs',
|
||||
name: 'categories',
|
||||
type: 'multiOptions',
|
||||
description:
|
||||
'Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getCategoriesNames',
|
||||
},
|
||||
default: [],
|
||||
},
|
||||
{
|
||||
displayName: 'CC Recipients',
|
||||
name: 'ccRecipients',
|
||||
description: 'Comma-separated list of 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 from 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: 'Normal',
|
||||
},
|
||||
{
|
||||
displayName: 'Message Type',
|
||||
name: 'bodyContentType',
|
||||
description: 'Message body content type',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'HTML',
|
||||
value: 'html',
|
||||
},
|
||||
{
|
||||
name: 'Text',
|
||||
value: 'Text',
|
||||
},
|
||||
],
|
||||
default: 'html',
|
||||
},
|
||||
{
|
||||
displayName: 'Read Receipt Requested',
|
||||
name: 'isReadReceiptRequested',
|
||||
description: 'Whether a read receipt is requested for the message',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
displayName: 'Reply To',
|
||||
name: 'replyTo',
|
||||
description: 'Email address to use when replying',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Save To Sent Items',
|
||||
name: 'saveToSentItems',
|
||||
description: 'Whether to save the message in Sent Items',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['message'],
|
||||
operation: ['send'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number, _: INodeExecutionData[]) {
|
||||
const additionalFields = this.getNodeParameter('additionalFields', index);
|
||||
const toRecipients = this.getNodeParameter('toRecipients', index) as string;
|
||||
const subject = this.getNodeParameter('subject', index) as string;
|
||||
const bodyContent = this.getNodeParameter('bodyContent', index, '') 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[];
|
||||
|
||||
const messageAttachments: IDataObject[] = [];
|
||||
|
||||
for (const attachment of attachments) {
|
||||
const binaryPropertyName = attachment.binaryPropertyName as string;
|
||||
|
||||
const binaryData = this.helpers.assertBinaryData(index, binaryPropertyName);
|
||||
|
||||
let fileBase64;
|
||||
if (binaryData.id) {
|
||||
const chunkSize = 256 * 1024;
|
||||
const stream = await this.helpers.getBinaryStream(binaryData.id, chunkSize);
|
||||
const buffer = await this.helpers.binaryToBuffer(stream);
|
||||
fileBase64 = buffer.toString('base64');
|
||||
} else {
|
||||
fileBase64 = binaryData.data;
|
||||
}
|
||||
|
||||
messageAttachments.push({
|
||||
'@odata.type': '#microsoft.graph.fileAttachment',
|
||||
name: binaryData.fileName,
|
||||
contentBytes: fileBase64,
|
||||
});
|
||||
}
|
||||
|
||||
message.attachments = messageAttachments;
|
||||
}
|
||||
|
||||
const body: IDataObject = {
|
||||
message,
|
||||
saveToSentItems,
|
||||
};
|
||||
|
||||
await microsoftApiRequest.call(this, 'POST', '/sendMail', body, {});
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray({ success: true }),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import type {
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
createEmailBodyWithN8nAttribution,
|
||||
createEmailBodyWithoutN8nAttribution,
|
||||
} from '../../../../../../utils/sendAndWait/email-templates';
|
||||
import {
|
||||
getSendAndWaitConfig,
|
||||
getSendAndWaitProperties,
|
||||
createButton,
|
||||
} from '../../../../../../utils/sendAndWait/utils';
|
||||
import { createMessage } from '../../helpers/utils';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const description: INodeProperties[] = getSendAndWaitProperties([
|
||||
{
|
||||
displayName: 'To',
|
||||
name: 'toRecipients',
|
||||
description: 'Comma-separated list of email addresses of recipients',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
},
|
||||
]);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number, items: INodeExecutionData[]) {
|
||||
const toRecipients = this.getNodeParameter('toRecipients', index) as string;
|
||||
|
||||
const config = getSendAndWaitConfig(this);
|
||||
const buttons: string[] = [];
|
||||
for (const option of config.options) {
|
||||
buttons.push(createButton(option.url, option.label, option.style));
|
||||
}
|
||||
|
||||
let bodyContent: string;
|
||||
if (config.appendAttribution !== false) {
|
||||
const instanceId = this.getInstanceId();
|
||||
bodyContent = createEmailBodyWithN8nAttribution(config.message, buttons.join('\n'), instanceId);
|
||||
} else {
|
||||
bodyContent = createEmailBodyWithoutN8nAttribution(config.message, buttons.join('\n'));
|
||||
}
|
||||
|
||||
const fields: IDataObject = {
|
||||
subject: config.title,
|
||||
bodyContent,
|
||||
toRecipients,
|
||||
bodyContentType: 'html',
|
||||
};
|
||||
|
||||
const message: IDataObject = createMessage(fields);
|
||||
|
||||
const body: IDataObject = { message };
|
||||
|
||||
await microsoftApiRequest.call(this, 'POST', '/sendMail', body);
|
||||
|
||||
return items;
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
import type { IDataObject, IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { folderRLC, messageRLC } from '../../descriptions';
|
||||
import { createMessage, decodeOutlookId } from '../../helpers/utils';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
messageRLC,
|
||||
{
|
||||
displayName: 'Update Fields',
|
||||
name: 'updateFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'BCC Recipients',
|
||||
name: 'bccRecipients',
|
||||
description: 'Comma-separated list of email addresses of BCC recipients',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Category Names or IDs',
|
||||
name: 'categories',
|
||||
type: 'multiOptions',
|
||||
description:
|
||||
'Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getCategoriesNames',
|
||||
},
|
||||
default: [],
|
||||
},
|
||||
{
|
||||
displayName: 'CC Recipients',
|
||||
name: 'ccRecipients',
|
||||
description: 'Comma-separated list of 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',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{ ...folderRLC, required: false },
|
||||
{
|
||||
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: 'Normal',
|
||||
},
|
||||
{
|
||||
displayName: 'Is Read',
|
||||
name: 'isRead',
|
||||
description: 'Whether the message must be marked as read',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
displayName: 'Message',
|
||||
name: 'bodyContent',
|
||||
description: 'Message body content',
|
||||
type: 'string',
|
||||
typeOptions: {
|
||||
rows: 2,
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Message Type',
|
||||
name: 'bodyContentType',
|
||||
description: 'Message body content type',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'HTML',
|
||||
value: 'html',
|
||||
},
|
||||
{
|
||||
name: 'Text',
|
||||
value: 'Text',
|
||||
},
|
||||
],
|
||||
default: 'html',
|
||||
},
|
||||
{
|
||||
displayName: 'Read Receipt Requested',
|
||||
name: 'isReadReceiptRequested',
|
||||
description: 'Whether a read receipt is requested for the message',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
displayName: 'To',
|
||||
name: 'toRecipients',
|
||||
description: 'Comma-separated list of email addresses of recipients',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Reply To',
|
||||
name: 'replyTo',
|
||||
description: 'Email address to use when replying',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Subject',
|
||||
name: 'subject',
|
||||
description: 'The subject of the message',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['message'],
|
||||
operation: ['update'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
let responseData;
|
||||
|
||||
const messageId = this.getNodeParameter('messageId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
const updateFields = this.getNodeParameter('updateFields', index);
|
||||
|
||||
const folderId = decodeOutlookId(
|
||||
this.getNodeParameter('updateFields.folderId', index, '', {
|
||||
extractValue: true,
|
||||
}) as string,
|
||||
);
|
||||
|
||||
if (folderId) {
|
||||
const body: IDataObject = {
|
||||
destinationId: folderId,
|
||||
};
|
||||
|
||||
responseData = await microsoftApiRequest.call(
|
||||
this,
|
||||
'POST',
|
||||
`/messages/${messageId}/move`,
|
||||
body,
|
||||
);
|
||||
|
||||
delete updateFields.folderId;
|
||||
|
||||
if (!Object.keys(updateFields).length) {
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
}
|
||||
|
||||
const body: IDataObject = createMessage(updateFields);
|
||||
|
||||
if (!Object.keys(body).length) {
|
||||
throw new NodeOperationError(this.getNode(), 'No fields to update got specified');
|
||||
}
|
||||
|
||||
responseData = await microsoftApiRequest.call(this, 'PATCH', `/messages/${messageId}`, body, {});
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
import type {
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
JsonObject,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeApiError, NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { messageRLC } from '../../descriptions';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
messageRLC,
|
||||
{
|
||||
displayName: 'Input Data Field Name',
|
||||
name: 'binaryPropertyName',
|
||||
hint: 'The name of the input field containing the binary file data to be attached',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: 'data',
|
||||
placeholder: 'e.g. data',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add option',
|
||||
default: {},
|
||||
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: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['messageAttachment'],
|
||||
operation: ['add'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number, _: INodeExecutionData[]) {
|
||||
let responseData;
|
||||
|
||||
const messageId = this.getNodeParameter('messageId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
const binaryPropertyName = this.getNodeParameter('binaryPropertyName', 0);
|
||||
const options = this.getNodeParameter('options', index);
|
||||
|
||||
const binaryData = this.helpers.assertBinaryData(index, binaryPropertyName);
|
||||
const dataBuffer = await this.helpers.getBinaryDataBuffer(index, binaryPropertyName);
|
||||
|
||||
const fileName = options.fileName === undefined ? binaryData.fileName : options.fileName;
|
||||
|
||||
if (!fileName) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
'File name is not set. It has either to be set via "Additional Fields" or has to be set on the binary property!',
|
||||
{ itemIndex: index },
|
||||
);
|
||||
}
|
||||
|
||||
// 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 NodeApiError(this.getNode(), responseData as JsonObject, {
|
||||
message: '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,
|
||||
{},
|
||||
);
|
||||
}
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray({ success: true }),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
import type { IExecuteFunctions, INodeExecutionData, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { attachmentRLC, messageRLC } from '../../descriptions';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
messageRLC,
|
||||
attachmentRLC,
|
||||
{
|
||||
displayName: 'Put Output in Field',
|
||||
name: 'binaryPropertyName',
|
||||
hint: 'The name of the output field to put the binary file data in',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: 'data',
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['messageAttachment'],
|
||||
operation: ['download'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number, items: INodeExecutionData[]) {
|
||||
const messageId = this.getNodeParameter('messageId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
const attachmentId = this.getNodeParameter('attachmentId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
const dataPropertyNameDownload = this.getNodeParameter('binaryPropertyName', index);
|
||||
|
||||
// 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 as string;
|
||||
|
||||
const response = await microsoftApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
`/messages/${messageId}/attachments/${attachmentId}/$value`,
|
||||
undefined,
|
||||
{},
|
||||
undefined,
|
||||
{},
|
||||
{ encoding: null, resolveWithFullResponse: true },
|
||||
);
|
||||
|
||||
const newItem: INodeExecutionData = {
|
||||
json: items[index].json,
|
||||
binary: {},
|
||||
};
|
||||
|
||||
if (items[index].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[index].binary);
|
||||
}
|
||||
|
||||
const data = Buffer.from(response.body as string, 'utf8');
|
||||
newItem.binary![dataPropertyNameDownload] = await this.helpers.prepareBinaryData(
|
||||
data,
|
||||
fileName,
|
||||
mimeType,
|
||||
);
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(newItem),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
import type { IDataObject, IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { attachmentRLC, messageRLC } from '../../descriptions';
|
||||
import { microsoftApiRequest } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
messageRLC,
|
||||
attachmentRLC,
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Fields',
|
||||
name: 'fields',
|
||||
type: 'multiOptions',
|
||||
description: 'The fields to add to the output',
|
||||
default: [],
|
||||
options: [
|
||||
{
|
||||
name: 'contentType',
|
||||
value: 'contentType',
|
||||
},
|
||||
{
|
||||
name: 'isInline',
|
||||
value: 'isInline',
|
||||
},
|
||||
{
|
||||
name: 'lastModifiedDateTime',
|
||||
value: 'lastModifiedDateTime',
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
|
||||
name: 'name',
|
||||
value: 'name',
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
|
||||
name: 'size',
|
||||
value: 'size',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['messageAttachment'],
|
||||
operation: ['get'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
const qs: IDataObject = {};
|
||||
|
||||
const messageId = this.getNodeParameter('messageId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
const attachmentId = this.getNodeParameter('attachmentId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
const options = this.getNodeParameter('options', index);
|
||||
|
||||
// Have sane defaults so we don't fetch attachment data in this operation
|
||||
qs.$select = 'id,lastModifiedDateTime,name,contentType,size,isInline';
|
||||
|
||||
if (options.fields && (options.fields as string[]).length) {
|
||||
qs.$select = (options.fields as string[]).map((field) => field.trim()).join(',');
|
||||
}
|
||||
|
||||
const responseData = await microsoftApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
`/messages/${messageId}/attachments/${attachmentId}`,
|
||||
undefined,
|
||||
qs,
|
||||
);
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
import type { IDataObject, IExecuteFunctions, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { messageRLC, returnAllOrLimit } from '../../descriptions';
|
||||
import { microsoftApiRequest, microsoftApiRequestAllItems } from '../../transport';
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
messageRLC,
|
||||
...returnAllOrLimit,
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add option',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Fields',
|
||||
name: 'fields',
|
||||
type: 'multiOptions',
|
||||
description: 'The fields to add to the output',
|
||||
default: [],
|
||||
options: [
|
||||
{
|
||||
name: 'contentType',
|
||||
value: 'contentType',
|
||||
},
|
||||
{
|
||||
name: 'isInline',
|
||||
value: 'isInline',
|
||||
},
|
||||
{
|
||||
name: 'lastModifiedDateTime',
|
||||
value: 'lastModifiedDateTime',
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
|
||||
name: 'name',
|
||||
value: 'name',
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
|
||||
name: 'size',
|
||||
value: 'size',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['messageAttachment'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(this: IExecuteFunctions, index: number) {
|
||||
let responseData;
|
||||
const qs = {} as IDataObject;
|
||||
|
||||
const messageId = this.getNodeParameter('messageId', index, undefined, {
|
||||
extractValue: true,
|
||||
}) as string;
|
||||
|
||||
const returnAll = this.getNodeParameter('returnAll', index);
|
||||
const options = this.getNodeParameter('options', index);
|
||||
|
||||
// Have sane defaults so we don't fetch attachment data in this operation
|
||||
qs.$select = 'id,lastModifiedDateTime,name,contentType,size,isInline';
|
||||
|
||||
if (options.fields && (options.fields as string[]).length) {
|
||||
qs.$select = (options.fields as string[]).map((field) => field.trim()).join(',');
|
||||
}
|
||||
|
||||
const endpoint = `/messages/${messageId}/attachments`;
|
||||
if (returnAll) {
|
||||
responseData = await microsoftApiRequestAllItems.call(
|
||||
this,
|
||||
'value',
|
||||
'GET',
|
||||
endpoint,
|
||||
undefined,
|
||||
qs,
|
||||
);
|
||||
} else {
|
||||
qs.$top = this.getNodeParameter('limit', index);
|
||||
responseData = await microsoftApiRequest.call(this, 'GET', endpoint, undefined, qs);
|
||||
responseData = responseData.value;
|
||||
}
|
||||
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject),
|
||||
{ itemData: { item: index } },
|
||||
);
|
||||
|
||||
return executionData;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import * as add from './add.operation';
|
||||
import * as download from './download.operation';
|
||||
import * as get from './get.operation';
|
||||
import * as getAll from './getAll.operation';
|
||||
|
||||
export { add, download, get, getAll };
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['messageAttachment'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Add',
|
||||
value: 'add',
|
||||
description: 'Add an attachment to a message',
|
||||
action: 'Add an attachment',
|
||||
},
|
||||
{
|
||||
name: 'Download',
|
||||
value: 'download',
|
||||
description: 'Download an attachment from a message',
|
||||
action: 'Download an attachment',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Retrieve information about an attachment of a message',
|
||||
action: 'Get an attachment',
|
||||
},
|
||||
{
|
||||
name: 'Get Many',
|
||||
value: 'getAll',
|
||||
description: 'Retrieve information about the attachments of a message',
|
||||
action: 'Get many attachments',
|
||||
},
|
||||
],
|
||||
default: 'add',
|
||||
},
|
||||
...add.description,
|
||||
...download.description,
|
||||
...get.description,
|
||||
...getAll.description,
|
||||
];
|
||||
@@ -0,0 +1,88 @@
|
||||
/* eslint-disable n8n-nodes-base/node-filename-against-convention */
|
||||
import { NodeConnectionTypes, type INodeTypeDescription } from 'n8n-workflow';
|
||||
|
||||
import * as calendar from './calendar';
|
||||
import * as contact from './contact';
|
||||
import * as draft from './draft';
|
||||
import * as event from './event';
|
||||
import * as folder from './folder';
|
||||
import * as folderMessage from './folderMessage';
|
||||
import * as message from './message';
|
||||
import * as messageAttachment from './messageAttachment';
|
||||
import { sendAndWaitWebhooksDescription } from '../../../../../utils/sendAndWait/descriptions';
|
||||
import { SEND_AND_WAIT_WAITING_TOOLTIP } from '../../../../../utils/sendAndWait/utils';
|
||||
|
||||
export const description: INodeTypeDescription = {
|
||||
displayName: 'Microsoft Outlook',
|
||||
name: 'microsoftOutlook',
|
||||
group: ['transform'],
|
||||
icon: 'file:outlook.svg',
|
||||
version: 2,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Consume Microsoft Outlook API',
|
||||
defaults: {
|
||||
name: 'Microsoft Outlook',
|
||||
},
|
||||
inputs: [NodeConnectionTypes.Main],
|
||||
outputs: [NodeConnectionTypes.Main],
|
||||
usableAsTool: true,
|
||||
credentials: [
|
||||
{
|
||||
name: 'microsoftOutlookOAuth2Api',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
waitingNodeTooltip: SEND_AND_WAIT_WAITING_TOOLTIP,
|
||||
webhooks: sendAndWaitWebhooksDescription,
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
default: 'message',
|
||||
options: [
|
||||
{
|
||||
name: 'Calendar',
|
||||
value: 'calendar',
|
||||
},
|
||||
{
|
||||
name: 'Contact',
|
||||
value: 'contact',
|
||||
},
|
||||
{
|
||||
name: 'Draft',
|
||||
value: 'draft',
|
||||
},
|
||||
{
|
||||
name: 'Event',
|
||||
value: 'event',
|
||||
},
|
||||
{
|
||||
name: 'Folder',
|
||||
value: 'folder',
|
||||
},
|
||||
{
|
||||
name: 'Folder Message',
|
||||
value: 'folderMessage',
|
||||
},
|
||||
{
|
||||
name: 'Message',
|
||||
value: 'message',
|
||||
},
|
||||
{
|
||||
name: 'Message Attachment',
|
||||
value: 'messageAttachment',
|
||||
},
|
||||
],
|
||||
},
|
||||
...calendar.description,
|
||||
...contact.description,
|
||||
...draft.description,
|
||||
...event.description,
|
||||
...folder.description,
|
||||
...folderMessage.description,
|
||||
...message.description,
|
||||
...messageAttachment.description,
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { AllEntities } from 'n8n-workflow';
|
||||
|
||||
type NodeMap = {
|
||||
calendar: 'create' | 'delete' | 'get' | 'getAll' | 'update';
|
||||
contact: 'create' | 'delete' | 'get' | 'getAll' | 'update';
|
||||
draft: 'create' | 'delete' | 'get' | 'send' | 'update';
|
||||
event: 'create' | 'delete' | 'get' | 'getAll' | 'update';
|
||||
folder: 'create' | 'delete' | 'get' | 'getAll' | 'update';
|
||||
folderMessage: 'getAll';
|
||||
message: 'delete' | 'get' | 'getAll' | 'move' | 'update' | 'send' | 'reply' | 'sendAndWait';
|
||||
messageAttachment: 'add' | 'download' | 'getAll' | 'get';
|
||||
};
|
||||
|
||||
export type MicrosoftOutlook = AllEntities<NodeMap>;
|
||||
@@ -0,0 +1,97 @@
|
||||
import type { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
|
||||
import { NodeApiError, NodeOperationError, SEND_AND_WAIT_OPERATION } from 'n8n-workflow';
|
||||
|
||||
import * as calendar from './calendar';
|
||||
import * as contact from './contact';
|
||||
import * as draft from './draft';
|
||||
import * as event from './event';
|
||||
import * as folder from './folder';
|
||||
import * as folderMessage from './folderMessage';
|
||||
import * as message from './message';
|
||||
import * as messageAttachment from './messageAttachment';
|
||||
import type { MicrosoftOutlook } from './node.type';
|
||||
import { configureWaitTillDate } from '../../../../../utils/sendAndWait/configureWaitTillDate.util';
|
||||
|
||||
export async function router(this: IExecuteFunctions) {
|
||||
const items = this.getInputData();
|
||||
const returnData: INodeExecutionData[] = [];
|
||||
|
||||
const resource = this.getNodeParameter<MicrosoftOutlook>('resource', 0) as string;
|
||||
const operation = this.getNodeParameter('operation', 0);
|
||||
|
||||
let responseData;
|
||||
|
||||
const microsoftOutlook = {
|
||||
resource,
|
||||
operation,
|
||||
} as MicrosoftOutlook;
|
||||
|
||||
if (
|
||||
microsoftOutlook.resource === 'message' &&
|
||||
microsoftOutlook.operation === SEND_AND_WAIT_OPERATION
|
||||
) {
|
||||
await message[microsoftOutlook.operation].execute.call(this, 0, items);
|
||||
|
||||
const waitTill = configureWaitTillDate(this);
|
||||
|
||||
await this.putExecutionToWait(waitTill);
|
||||
return [items];
|
||||
}
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
try {
|
||||
switch (microsoftOutlook.resource) {
|
||||
case 'calendar':
|
||||
responseData = await calendar[microsoftOutlook.operation].execute.call(this, i);
|
||||
break;
|
||||
case 'contact':
|
||||
responseData = await contact[microsoftOutlook.operation].execute.call(this, i);
|
||||
break;
|
||||
case 'draft':
|
||||
responseData = await draft[microsoftOutlook.operation].execute.call(this, i, items);
|
||||
break;
|
||||
case 'event':
|
||||
responseData = await event[microsoftOutlook.operation].execute.call(this, i);
|
||||
break;
|
||||
case 'folder':
|
||||
responseData = await folder[microsoftOutlook.operation].execute.call(this, i);
|
||||
break;
|
||||
case 'folderMessage':
|
||||
responseData = await folderMessage[microsoftOutlook.operation].execute.call(this, i);
|
||||
break;
|
||||
case 'message':
|
||||
responseData = await message[microsoftOutlook.operation].execute.call(this, i, items);
|
||||
break;
|
||||
case 'messageAttachment':
|
||||
responseData = await messageAttachment[microsoftOutlook.operation].execute.call(
|
||||
this,
|
||||
i,
|
||||
items,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new NodeOperationError(this.getNode(), `The resource "${resource}" is not known`);
|
||||
}
|
||||
|
||||
returnData.push(...responseData);
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
const executionErrorData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray({ error: error.message }),
|
||||
{ itemData: { item: i } },
|
||||
);
|
||||
returnData.push(...executionErrorData);
|
||||
continue;
|
||||
}
|
||||
//NodeApiError will be missing the itemIndex, add it
|
||||
if (error instanceof NodeApiError && error?.context?.itemIndex === undefined) {
|
||||
if (error.context === undefined) {
|
||||
error.context = {};
|
||||
}
|
||||
error.context.itemIndex = i;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return [returnData];
|
||||
}
|
||||
Reference in New Issue
Block a user