n8n-nodes-base
Version:
Base nodes of n8n
646 lines • 36.4 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GoogleCalendar = void 0;
const moment_timezone_1 = __importDefault(require("moment-timezone"));
const n8n_workflow_1 = require("n8n-workflow");
const uuid_1 = require("uuid");
const CalendarDescription_1 = require("./CalendarDescription");
const EventDescription_1 = require("./EventDescription");
const GenericFunctions_1 = require("./GenericFunctions");
const utilities_1 = require("../../../utils/utilities");
class GoogleCalendar {
description = {
displayName: 'Google Calendar',
name: 'googleCalendar',
icon: 'file:googleCalendar.svg',
group: ['input'],
version: [1, 1.1, 1.2, 1.3],
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Consume Google Calendar API',
schemaPath: 'Google/Calendar',
defaults: {
name: 'Google Calendar',
},
inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
builderHint: {
relatedNodes: [
{
nodeType: 'n8n-nodes-base.googleCalendarTool',
relationHint: 'Tool version for AI Agent use',
},
],
},
usableAsTool: true,
credentials: [
{
name: 'googleCalendarOAuth2Api',
required: true,
},
],
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Calendar',
value: 'calendar',
},
{
name: 'Event',
value: 'event',
},
],
default: 'event',
},
...CalendarDescription_1.calendarOperations,
...CalendarDescription_1.calendarFields,
...EventDescription_1.eventOperations,
...EventDescription_1.eventFields,
{
displayName: 'This node will use the time zone set in n8n’s settings, but you can override this in the workflow settings',
name: 'useN8nTimeZone',
type: 'notice',
default: '',
},
],
};
methods = {
listSearch: {
getCalendars: GenericFunctions_1.getCalendars,
getTimezones: GenericFunctions_1.getTimezones,
},
loadOptions: {
// Get all the calendars to display them to user so that they can
// select them easily
async getConferenceSolutions() {
const returnData = [];
const calendar = this.getCurrentNodeParameter('calendar', { extractValue: true });
const possibleSolutions = {
eventHangout: 'Google Hangout',
eventNamedHangout: 'Google Hangout Classic',
hangoutsMeet: 'Google Meet',
};
const { conferenceProperties: { allowedConferenceSolutionTypes }, } = await GenericFunctions_1.googleApiRequest.call(this, 'GET', `/calendar/v3/users/me/calendarList/${calendar}`);
for (const solution of allowedConferenceSolutionTypes) {
returnData.push({
name: possibleSolutions[solution],
value: solution,
});
}
return returnData;
},
// Get all the colors to display them to user so that they can
// select them easily
async getColors() {
const returnData = [];
const { event } = await GenericFunctions_1.googleApiRequest.call(this, 'GET', '/calendar/v3/colors');
for (const key of Object.keys(event)) {
const colorName = `Background: ${event[key].background} - Foreground: ${event[key].foreground}`;
const colorId = key;
returnData.push({
name: `${colorName}`,
value: colorId,
});
}
return returnData;
},
},
};
async execute() {
const items = this.getInputData();
const returnData = [];
const length = items.length;
const qs = {};
const hints = [];
let responseData;
const resource = this.getNodeParameter('resource', 0);
const operation = this.getNodeParameter('operation', 0);
const timezone = this.getTimezone();
const nodeVersion = this.getNode().typeVersion;
for (let i = 0; i < length; i++) {
try {
if (resource === 'calendar') {
//https://developers.google.com/calendar/v3/reference/freebusy/query
if (operation === 'availability') {
// we need to decode once because calendar used to be saved encoded
const calendarId = decodeURIComponent(this.getNodeParameter('calendar', i, '', { extractValue: true }));
const timeMin = (0, GenericFunctions_1.dateObjectToISO)(this.getNodeParameter('timeMin', i));
const timeMax = (0, GenericFunctions_1.dateObjectToISO)(this.getNodeParameter('timeMax', i));
const options = this.getNodeParameter('options', i);
const outputFormat = options.outputFormat || 'availability';
const tz = this.getNodeParameter('options.timezone', i, '', {
extractValue: true,
});
const body = {
timeMin: (0, moment_timezone_1.default)(timeMin).utc().format(),
timeMax: (0, moment_timezone_1.default)(timeMax).utc().format(),
items: [
{
id: calendarId,
},
],
timeZone: tz || timezone,
};
responseData = await GenericFunctions_1.googleApiRequest.call(this, 'POST', '/calendar/v3/freeBusy', body, {});
if (responseData.calendars[calendarId].errors) {
throw new n8n_workflow_1.NodeApiError(this.getNode(), responseData.calendars[calendarId], {
itemIndex: i,
});
}
if (outputFormat === 'availability') {
responseData = {
available: !responseData.calendars[calendarId].busy.length,
};
}
else if (outputFormat === 'bookedSlots') {
responseData = responseData.calendars[calendarId].busy;
}
}
}
if (resource === 'event') {
//https://developers.google.com/calendar/v3/reference/events/insert
if (operation === 'create') {
const calendarId = (0, GenericFunctions_1.encodeURIComponentOnce)(this.getNodeParameter('calendar', i, '', { extractValue: true }));
const start = (0, GenericFunctions_1.dateObjectToISO)(this.getNodeParameter('start', i));
const end = (0, GenericFunctions_1.dateObjectToISO)(this.getNodeParameter('end', i));
const useDefaultReminders = this.getNodeParameter('useDefaultReminders', i);
const additionalFields = this.getNodeParameter('additionalFields', i);
if (additionalFields.maxAttendees) {
qs.maxAttendees = additionalFields.maxAttendees;
}
if (additionalFields.sendNotifications) {
qs.sendNotifications = additionalFields.sendNotifications;
}
if (additionalFields.sendUpdates) {
qs.sendUpdates = additionalFields.sendUpdates;
}
const body = {
start: {
dateTime: moment_timezone_1.default.tz(start, timezone).utc().format(),
timeZone: timezone,
},
end: {
dateTime: moment_timezone_1.default.tz(end, timezone).utc().format(),
timeZone: timezone,
},
};
if (additionalFields.attendees) {
body.attendees = [];
additionalFields.attendees.forEach((attendee) => {
body.attendees.push.apply(body.attendees, attendee
.split(',')
.map((a) => a.trim())
.map((email) => ({ email })));
});
}
if (additionalFields.color) {
body.colorId = additionalFields.color;
}
if (additionalFields.description) {
body.description = additionalFields.description;
}
if (additionalFields.guestsCanInviteOthers) {
body.guestsCanInviteOthers = additionalFields.guestsCanInviteOthers;
}
if (additionalFields.guestsCanModify) {
body.guestsCanModify = additionalFields.guestsCanModify;
}
if (additionalFields.guestsCanSeeOtherGuests) {
body.guestsCanSeeOtherGuests = additionalFields.guestsCanSeeOtherGuests;
}
if (additionalFields.id) {
body.id = additionalFields.id;
}
if (additionalFields.location) {
body.location = additionalFields.location;
}
if (additionalFields.summary) {
body.summary = additionalFields.summary;
}
if (additionalFields.showMeAs) {
body.transparency = additionalFields.showMeAs;
}
if (additionalFields.visibility) {
body.visibility = additionalFields.visibility;
}
if (!useDefaultReminders) {
const reminders = this.getNodeParameter('remindersUi', i)
.remindersValues;
body.reminders = {
useDefault: false,
};
if (reminders) {
body.reminders.overrides = reminders;
}
}
if (additionalFields.allday === 'yes') {
body.start = {
date: timezone
? moment_timezone_1.default.tz(start, timezone).utc(true).format('YYYY-MM-DD')
: moment_timezone_1.default.tz(start, moment_timezone_1.default.tz.guess()).utc(true).format('YYYY-MM-DD'),
};
body.end = {
date: timezone
? moment_timezone_1.default.tz(end, timezone).utc(true).format('YYYY-MM-DD')
: moment_timezone_1.default.tz(end, moment_timezone_1.default.tz.guess()).utc(true).format('YYYY-MM-DD'),
};
}
//exampel: RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=10;UNTIL=20110701T170000Z
//https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html
body.recurrence = [];
if (additionalFields.rrule) {
body.recurrence = [`RRULE:${additionalFields.rrule}`];
}
else {
if (additionalFields.repeatHowManyTimes && additionalFields.repeatUntil) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), "You can set either 'Repeat How Many Times' or 'Repeat Until' but not both", { itemIndex: i });
}
if (additionalFields.repeatFrecuency) {
body.recurrence?.push(`FREQ=${additionalFields.repeatFrecuency.toUpperCase()};`);
}
if (additionalFields.repeatHowManyTimes) {
body.recurrence?.push(`COUNT=${additionalFields.repeatHowManyTimes};`);
}
if (additionalFields.repeatUntil) {
const repeatUntil = (0, moment_timezone_1.default)(additionalFields.repeatUntil)
.utc()
.format('YYYYMMDDTHHmmss');
body.recurrence?.push(`UNTIL=${repeatUntil}Z`);
}
if (body.recurrence.length !== 0) {
body.recurrence = [`RRULE:${body.recurrence.join('')}`];
}
}
if (additionalFields.conferenceDataUi) {
const conferenceData = additionalFields.conferenceDataUi
.conferenceDataValues;
if (conferenceData) {
qs.conferenceDataVersion = 1;
body.conferenceData = {
createRequest: {
requestId: (0, uuid_1.v4)(),
conferenceSolution: {
type: conferenceData.conferenceSolution,
},
},
};
}
}
responseData = await GenericFunctions_1.googleApiRequest.call(this, 'POST', `/calendar/v3/calendars/${calendarId}/events`, body, qs);
}
//https://developers.google.com/calendar/v3/reference/events/delete
if (operation === 'delete') {
const calendarId = (0, GenericFunctions_1.encodeURIComponentOnce)(this.getNodeParameter('calendar', i, '', { extractValue: true }));
const eventId = this.getNodeParameter('eventId', i);
const options = this.getNodeParameter('options', i);
if (options.sendUpdates) {
qs.sendUpdates = options.sendUpdates;
}
responseData = await GenericFunctions_1.googleApiRequest.call(this, 'DELETE', `/calendar/v3/calendars/${calendarId}/events/${eventId}`, {});
responseData = { success: true };
}
//https://developers.google.com/calendar/v3/reference/events/get
if (operation === 'get') {
const calendarId = (0, GenericFunctions_1.encodeURIComponentOnce)(this.getNodeParameter('calendar', i, '', { extractValue: true }));
const eventId = this.getNodeParameter('eventId', i);
const options = this.getNodeParameter('options', i);
const tz = this.getNodeParameter('options.timeZone', i, '', {
extractValue: true,
});
if (options.maxAttendees) {
qs.maxAttendees = options.maxAttendees;
}
if (tz) {
qs.timeZone = tz;
}
responseData = (await GenericFunctions_1.googleApiRequest.call(this, 'GET', `/calendar/v3/calendars/${calendarId}/events/${eventId}`, {}, qs));
if (responseData) {
if (nodeVersion >= 1.3 && options.returnNextInstance && responseData.recurrence) {
const eventInstances = (await GenericFunctions_1.googleApiRequest.call(this, 'GET', `/calendar/v3/calendars/${calendarId}/events/${responseData.id}/instances`, {}, {
timeMin: new Date().toISOString(),
maxResults: 1,
})).items || [];
responseData = eventInstances[0] ? [eventInstances[0]] : [responseData];
}
else {
responseData = (0, GenericFunctions_1.addNextOccurrence)([responseData]);
}
}
}
//https://developers.google.com/calendar/v3/reference/events/list
if (operation === 'getAll') {
const returnAll = this.getNodeParameter('returnAll', i);
const calendarId = (0, GenericFunctions_1.encodeURIComponentOnce)(this.getNodeParameter('calendar', i, '', { extractValue: true }));
const options = this.getNodeParameter('options', i);
const tz = this.getNodeParameter('options.timeZone', i, '', {
extractValue: true,
});
if (nodeVersion >= 1.3) {
const timeMin = (0, GenericFunctions_1.dateObjectToISO)(this.getNodeParameter('timeMin', i));
const timeMax = (0, GenericFunctions_1.dateObjectToISO)(this.getNodeParameter('timeMax', i));
if (timeMin) {
qs.timeMin = (0, GenericFunctions_1.addTimezoneToDate)(timeMin, tz || timezone);
}
if (timeMax) {
qs.timeMax = (0, GenericFunctions_1.addTimezoneToDate)(timeMax, tz || timezone);
}
if (!options.recurringEventHandling || options.recurringEventHandling === 'expand') {
qs.singleEvents = true;
}
}
if (options.iCalUID) {
qs.iCalUID = options.iCalUID;
}
if (options.maxAttendees) {
qs.maxAttendees = options.maxAttendees;
}
if (options.orderBy) {
qs.orderBy = options.orderBy;
}
if (options.query) {
qs.q = options.query;
}
if (options.showDeleted) {
qs.showDeleted = options.showDeleted;
}
if (options.showHiddenInvitations) {
qs.showHiddenInvitations = options.showHiddenInvitations;
}
if (options.singleEvents) {
qs.singleEvents = options.singleEvents;
}
if (options.timeMax) {
qs.timeMax = (0, GenericFunctions_1.addTimezoneToDate)((0, GenericFunctions_1.dateObjectToISO)(options.timeMax), tz || timezone);
}
if (options.timeMin) {
qs.timeMin = (0, GenericFunctions_1.addTimezoneToDate)((0, GenericFunctions_1.dateObjectToISO)(options.timeMin), tz || timezone);
}
if (tz) {
qs.timeZone = tz;
}
if (options.updatedMin) {
qs.updatedMin = (0, GenericFunctions_1.addTimezoneToDate)((0, GenericFunctions_1.dateObjectToISO)(options.updatedMin), tz || timezone);
}
if (options.fields) {
qs.fields = options.fields;
}
if (returnAll) {
responseData = await GenericFunctions_1.googleApiRequestAllItems.call(this, 'items', 'GET', `/calendar/v3/calendars/${calendarId}/events`, {}, qs);
}
else {
qs.maxResults = this.getNodeParameter('limit', i);
responseData = await GenericFunctions_1.googleApiRequest.call(this, 'GET', `/calendar/v3/calendars/${calendarId}/events`, {}, qs);
responseData = responseData.items;
}
if (responseData) {
if (nodeVersion >= 1.3 && options.recurringEventHandling === 'next') {
const updatedEvents = [];
for (const event of responseData) {
if (event.recurrence) {
const eventInstances = (await (0, GenericFunctions_1.googleApiRequestWithRetries)({
context: this,
method: 'GET',
resource: `/calendar/v3/calendars/${calendarId}/events/${event.id}/instances`,
qs: {
timeMin: new Date().toISOString(),
maxResults: 1,
},
itemIndex: i,
})).items || [];
updatedEvents.push(eventInstances[0] || event);
continue;
}
updatedEvents.push(event);
}
responseData = updatedEvents;
}
else if (nodeVersion >= 1.3 && options.recurringEventHandling === 'first') {
responseData = responseData.filter((event) => {
if (qs.timeMin &&
event.recurrence &&
event.created &&
event.created < qs.timeMin) {
return false;
}
if (qs.timeMax &&
event.recurrence &&
event.created &&
event.created > qs.timeMax) {
return false;
}
return true;
});
}
else if (nodeVersion < 1.3) {
// in node version above or equal to 1.3, this would correspond to the 'expand' option,
// so no need to add the next occurrence as event instances returned by the API
responseData = (0, GenericFunctions_1.addNextOccurrence)(responseData);
}
if (!qs.timeMax &&
(!options.recurringEventHandling || options.recurringEventHandling === 'expand')) {
const suggestTrim = (0, GenericFunctions_1.eventExtendYearIntoFuture)(responseData, timezone);
if (suggestTrim) {
hints.push({
message: "Some events repeat far into the future. To return less of them, add a 'Before' date or change the 'Recurring Event Handling' option.",
location: 'outputPane',
});
}
}
}
}
//https://developers.google.com/calendar/v3/reference/events/patch
if (operation === 'update') {
const calendarId = (0, GenericFunctions_1.encodeURIComponentOnce)(this.getNodeParameter('calendar', i, '', { extractValue: true }));
let eventId = this.getNodeParameter('eventId', i);
if (nodeVersion >= 1.3) {
const modifyTarget = this.getNodeParameter('modifyTarget', i, 'instance');
if (modifyTarget === 'event') {
const instance = (await GenericFunctions_1.googleApiRequest.call(this, 'GET', `/calendar/v3/calendars/${calendarId}/events/${eventId}`, {}, qs));
eventId = instance.recurringEventId;
}
}
const useDefaultReminders = this.getNodeParameter('useDefaultReminders', i);
const updateFields = this.getNodeParameter('updateFields', i);
let updateTimezone = updateFields.timezone;
if (nodeVersion > 1 && updateTimezone === undefined) {
updateTimezone = timezone;
}
if (updateFields.maxAttendees) {
qs.maxAttendees = updateFields.maxAttendees;
}
if (updateFields.sendNotifications) {
qs.sendNotifications = updateFields.sendNotifications;
}
if (updateFields.sendUpdates) {
qs.sendUpdates = updateFields.sendUpdates;
}
const body = {};
if (updateFields.start) {
body.start = {
dateTime: moment_timezone_1.default.tz(updateFields.start, updateTimezone).utc().format(),
timeZone: updateTimezone,
};
}
if (updateFields.end) {
body.end = {
dateTime: moment_timezone_1.default.tz(updateFields.end, updateTimezone).utc().format(),
timeZone: updateTimezone,
};
}
// nodeVersion < 1.2
if (updateFields.attendees) {
body.attendees = [];
updateFields.attendees.forEach((attendee) => {
body.attendees.push.apply(body.attendees, attendee
.split(',')
.map((a) => a.trim())
.map((email) => ({ email })));
});
}
// nodeVersion >= 1.2
if (updateFields.attendeesUi) {
const { mode, attendees } = updateFields.attendeesUi.values;
body.attendees = [];
if (mode === 'add') {
const event = await GenericFunctions_1.googleApiRequest.call(this, 'GET', `/calendar/v3/calendars/${calendarId}/events/${eventId}`);
(event?.attendees || []).forEach((attendee) => {
body.attendees?.push(attendee);
});
}
attendees.forEach((attendee) => {
body.attendees.push.apply(body.attendees, attendee
.split(',')
.map((a) => a.trim())
.map((email) => ({ email })));
});
}
if (updateFields.color) {
body.colorId = updateFields.color;
}
if (updateFields.description) {
body.description = updateFields.description;
}
if (updateFields.guestsCanInviteOthers) {
body.guestsCanInviteOthers = updateFields.guestsCanInviteOthers;
}
if (updateFields.guestsCanModify) {
body.guestsCanModify = updateFields.guestsCanModify;
}
if (updateFields.guestsCanSeeOtherGuests) {
body.guestsCanSeeOtherGuests = updateFields.guestsCanSeeOtherGuests;
}
if (updateFields.id) {
body.id = updateFields.id;
}
if (updateFields.location) {
body.location = updateFields.location;
}
if (updateFields.summary) {
body.summary = updateFields.summary;
}
if (updateFields.showMeAs) {
body.transparency = updateFields.showMeAs;
}
if (updateFields.visibility) {
body.visibility = updateFields.visibility;
}
if (!useDefaultReminders) {
const reminders = this.getNodeParameter('remindersUi', i)
.remindersValues;
body.reminders = {
useDefault: false,
};
if (reminders) {
body.reminders.overrides = reminders;
}
}
if (updateFields.allday === 'yes' && updateFields.start && updateFields.end) {
body.start = {
date: updateTimezone
? moment_timezone_1.default.tz(updateFields.start, updateTimezone).utc(true).format('YYYY-MM-DD')
: moment_timezone_1.default.tz(updateFields.start, moment_timezone_1.default.tz.guess()).utc(true).format('YYYY-MM-DD'),
};
body.end = {
date: updateTimezone
? moment_timezone_1.default.tz(updateFields.end, updateTimezone).utc(true).format('YYYY-MM-DD')
: moment_timezone_1.default.tz(updateFields.end, moment_timezone_1.default.tz.guess()).utc(true).format('YYYY-MM-DD'),
};
}
//example: RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=10;UNTIL=20110701T170000Z
//https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html
body.recurrence = [];
if (updateFields.rrule) {
body.recurrence = [`RRULE:${updateFields.rrule}`];
}
else {
if (updateFields.repeatHowManyTimes && updateFields.repeatUntil) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), "You can set either 'Repeat How Many Times' or 'Repeat Until' but not both", { itemIndex: i });
}
if (updateFields.repeatFrecuency) {
body.recurrence?.push(`FREQ=${updateFields.repeatFrecuency.toUpperCase()};`);
}
if (updateFields.repeatHowManyTimes) {
body.recurrence?.push(`COUNT=${updateFields.repeatHowManyTimes};`);
}
if (updateFields.repeatUntil) {
const repeatUntil = (0, moment_timezone_1.default)(updateFields.repeatUntil)
.utc()
.format('YYYYMMDDTHHmmss');
body.recurrence?.push(`UNTIL=${repeatUntil}Z`);
}
if (body.recurrence.length !== 0) {
body.recurrence = [`RRULE:${body.recurrence.join('')}`];
}
else {
delete body.recurrence;
}
}
responseData = await GenericFunctions_1.googleApiRequest.call(this, 'PATCH', `/calendar/v3/calendars/${calendarId}/events/${eventId}`, body, qs);
}
}
const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(responseData), { itemData: { item: i } });
returnData.push(...executionData);
}
catch (error) {
if (!this.continueOnFail()) {
throw error;
}
else {
const executionErrorData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray({ error: error.message }), { itemData: { item: i } });
returnData.push(...executionErrorData);
continue;
}
}
}
const keysPriorityList = [
'id',
'summary',
'start',
'end',
'attendees',
'creator',
'organizer',
'description',
'location',
'created',
'updated',
];
let nodeExecutionData = returnData;
if (nodeVersion >= 1.3) {
nodeExecutionData = (0, utilities_1.sortItemKeysByPriorityList)(returnData, keysPriorityList);
}
if (hints.length) {
this.addExecutionHints(...hints);
}
return [nodeExecutionData];
}
}
exports.GoogleCalendar = GoogleCalendar;
//# sourceMappingURL=GoogleCalendar.node.js.map