diff --git a/packages/nodes-base/nodes/Google/Calendar/FreebusyDescription.ts b/packages/nodes-base/nodes/Google/Calendar/CalendarDescription.ts similarity index 50% rename from packages/nodes-base/nodes/Google/Calendar/FreebusyDescription.ts rename to packages/nodes-base/nodes/Google/Calendar/CalendarDescription.ts index 3afdbcc6d2..ab2f70a65b 100644 --- a/packages/nodes-base/nodes/Google/Calendar/FreebusyDescription.ts +++ b/packages/nodes-base/nodes/Google/Calendar/CalendarDescription.ts @@ -2,7 +2,7 @@ import { INodeProperties, } from 'n8n-workflow'; -export const freeBusyOperations = [ +export const calendarOperations = [ { displayName: 'Operation', name: 'operation', @@ -10,26 +10,29 @@ export const freeBusyOperations = [ displayOptions: { show: { resource: [ - 'freeBusy', + 'calendar', ], }, }, options: [ { - name: 'Get', - value: 'get', - description: 'Returns free/busy information for a calendar', + name: 'Availability', + value: 'availability', + description: 'If a time-slot is available in a calendar', }, ], - default: 'get', + default: 'availability', description: 'The operation to perform.', }, ] as INodeProperties[]; -export const freeBusyFields = [ +export const calendarFields = [ + /* -------------------------------------------------------------------------- */ + /* calendar:availability */ + /* -------------------------------------------------------------------------- */ { displayName: 'Calendar ID', - name: 'calendarId', + name: 'calendar', type: 'options', typeOptions: { loadOptionsMethod: 'getCalendars', @@ -37,28 +40,25 @@ export const freeBusyFields = [ required: true, displayOptions: { show: { - operation: [ - 'get', - ], resource: [ - 'freeBusy', + 'calendar', ], }, }, default: '', }, { - displayName: 'Time Min.', + displayName: 'Start Time', name: 'timeMin', type: 'dateTime', required: true, displayOptions: { show: { operation: [ - 'get', + 'availability', ], resource: [ - 'freeBusy', + 'calendar', ], }, }, @@ -66,17 +66,17 @@ export const freeBusyFields = [ description: 'Start of the interval', }, { - displayName: 'Time Max.', + displayName: 'End Time', name: 'timeMax', type: 'dateTime', required: true, displayOptions: { show: { operation: [ - 'get', + 'availability', ], resource: [ - 'freeBusy', + 'calendar', ], }, }, @@ -84,39 +84,46 @@ export const freeBusyFields = [ description: 'End of the interval', }, { - displayName: 'Simple', - name: 'simple', - type: 'boolean', - displayOptions: { - show: { - resource: [ - 'freeBusy', - ], - operation: [ - 'get', - ], - }, - }, - default: true, - description: 'When set to true a simplify version of the response will be used else the raw data.', - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', + displayName: 'Options', + name: 'options', type: 'collection', - placeholder: 'Add Field', + placeholder: 'Add Options', displayOptions: { show: { operation: [ - 'get', + 'availability', ], resource: [ - 'freeBusy', + 'calendar', ], }, }, default: {}, options: [ + { + displayName: 'Output Format', + name: 'outputFormat', + type: 'options', + options: [ + { + name: 'Availability', + value: 'availability', + description: 'Returns if there are any events in the given time or not.', + }, + { + name: 'Booked Slots', + value: 'bookedSlots', + description: 'Returns the booked slots.', + }, + { + name: 'RAW', + value: 'raw', + description: 'Returns the RAW data from the API.', + }, + ], + default: 'availability', + description: 'The format to return the data in.', + }, { displayName: 'Timezone', name: 'timezone', @@ -129,4 +136,6 @@ export const freeBusyFields = [ }, ], }, + + ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Google/Calendar/EventDescription.ts b/packages/nodes-base/nodes/Google/Calendar/EventDescription.ts index 67e1314ca5..3ed2846659 100644 --- a/packages/nodes-base/nodes/Google/Calendar/EventDescription.ts +++ b/packages/nodes-base/nodes/Google/Calendar/EventDescription.ts @@ -48,7 +48,7 @@ export const eventOperations = [ export const eventFields = [ /* -------------------------------------------------------------------------- */ - /* event:create */ + /* event:ALL */ /* -------------------------------------------------------------------------- */ { displayName: 'Calendar ID', @@ -60,9 +60,6 @@ export const eventFields = [ required: true, displayOptions: { show: { - operation: [ - 'create', - ], resource: [ 'event', ], @@ -70,6 +67,10 @@ export const eventFields = [ }, default: '', }, + + /* -------------------------------------------------------------------------- */ + /* event:create */ + /* -------------------------------------------------------------------------- */ { displayName: 'Start', name: 'start', @@ -449,29 +450,10 @@ export const eventFields = [ ], description: `If the event doesn't use the default reminders, this lists the reminders specific to the event`, }, + /* -------------------------------------------------------------------------- */ /* event:delete */ /* -------------------------------------------------------------------------- */ - { - displayName: 'Calendar ID', - name: 'calendar', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getCalendars', - }, - required: true, - displayOptions: { - show: { - operation: [ - 'delete', - ], - resource: [ - 'event', - ], - }, - }, - default: '', - }, { displayName: 'Event ID', name: 'eventId', @@ -535,26 +517,6 @@ export const eventFields = [ /* -------------------------------------------------------------------------- */ /* event:get */ /* -------------------------------------------------------------------------- */ - { - displayName: 'Calendar ID', - name: 'calendar', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getCalendars', - }, - required: true, - displayOptions: { - show: { - operation: [ - 'get', - ], - resource: [ - 'event', - ], - }, - }, - default: '', - }, { displayName: 'Event ID', name: 'eventId', @@ -609,29 +571,10 @@ export const eventFields = [ }, ], }, + /* -------------------------------------------------------------------------- */ /* event:getAll */ /* -------------------------------------------------------------------------- */ - { - displayName: 'Calendar ID', - name: 'calendar', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getCalendars', - }, - required: true, - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'event', - ], - }, - }, - default: '', - }, { displayName: 'Return All', name: 'returnAll', @@ -754,14 +697,14 @@ export const eventFields = [ events and instances of recurring events, but not the underlying recurring events themselves.`, }, { - displayName: 'Time Max', + displayName: 'End Time', name: 'timeMax', type: 'dateTime', default: '', description: `Upper bound (exclusive) for an event's start time to filter by`, }, { - displayName: 'Time Min', + displayName: 'Start Time', name: 'timeMin', type: 'dateTime', default: '', @@ -787,29 +730,10 @@ export const eventFields = [ }, ], }, + /* -------------------------------------------------------------------------- */ /* event:update */ /* -------------------------------------------------------------------------- */ - { - displayName: 'Calendar ID', - name: 'calendar', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getCalendars', - }, - required: true, - displayOptions: { - show: { - operation: [ - 'update', - ], - resource: [ - 'event', - ], - }, - }, - default: '', - }, { displayName: 'Event ID', name: 'eventId', diff --git a/packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts b/packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts index 42b3a6943a..597276d172 100644 --- a/packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts +++ b/packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts @@ -22,9 +22,9 @@ import { } from './EventDescription'; import { - freeBusyFields, - freeBusyOperations, -} from './freeBusyDescription'; + calendarFields, + calendarOperations, +} from './CalendarDescription'; import { IEvent, @@ -62,21 +62,21 @@ export class GoogleCalendar implements INodeType { type: 'options', options: [ { - name: 'Event', - value: 'event', + name: 'Calendar', + value: 'calendar', }, { - name: 'Freebusy', - value: 'freeBusy', + name: 'Event', + value: 'event', }, ], default: 'event', description: 'The resource to operate on.', }, + ...calendarOperations, + ...calendarFields, ...eventOperations, ...eventFields, - ...freeBusyOperations, - ...freeBusyFields, ], }; @@ -178,6 +178,53 @@ export class GoogleCalendar implements INodeType { const resource = this.getNodeParameter('resource', 0) as string; const operation = this.getNodeParameter('operation', 0) as string; for (let i = 0; i < length; i++) { + if (resource === 'calendar') { + //https://developers.google.com/calendar/v3/reference/freebusy/query + if (operation === 'availability') { + const timezone = this.getTimezone(); + const calendarId = this.getNodeParameter('calendar', i) as string; + const timeMin = this.getNodeParameter('timeMin', i) as string; + const timeMax = this.getNodeParameter('timeMax', i) as string; + const options = this.getNodeParameter('options', i) as IDataObject; + const outputFormat = options.outputFormat || 'availability'; + + const body: IDataObject = { + timeMin: moment.tz(timeMin, timezone).utc().format(), + timeMax: moment.tz(timeMax, timezone).utc().format(), + items: [ + { + id: calendarId, + }, + ], + timeZone: options.timezone || timezone, + }; + + responseData = await googleApiRequest.call( + this, + 'POST', + `/calendar/v3/freeBusy`, + body, + {}, + ); + + if (responseData.calendars[calendarId].errors) { + let errors = responseData.calendars[calendarId].errors; + errors = errors.map((e: IDataObject) => e.reason); + throw new Error( + `Google Calendar error response: ${errors.join('|')}`, + ); + } + + 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') { @@ -554,47 +601,7 @@ export class GoogleCalendar implements INodeType { ); } } - if (resource === 'freeBusy') { - //https://developers.google.com/calendar/v3/reference/freebusy/query - if (operation === 'get') { - const timezone = this.getTimezone(); - const calendarId = this.getNodeParameter('calendarId', i) as string; - const timeMin = this.getNodeParameter('timeMin', i) as string; - const timeMax = this.getNodeParameter('timeMax', i) as string; - const simple = this.getNodeParameter('simple', i) as boolean; - const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; - const body: IDataObject = { - timeMin: moment.tz(timeMin, timezone).utc().format(), - timeMax: moment.tz(timeMax, timezone).utc().format(), - items: [ - { - id: calendarId, - }, - ], - timeZone: additionalFields.timezone || timezone, - }; - responseData = await googleApiRequest.call( - this, - 'POST', - `/calendar/v3/freeBusy`, - body, - {}, - ); - - if (responseData.calendars[calendarId].errors) { - let errors = responseData.calendars[calendarId].errors; - errors = errors.map((e: IDataObject) => e.reason); - throw new Error( - `Google Calendar error response: ${errors.join('|')}`, - ); - } - - if (simple) { - responseData = responseData.calendars[calendarId].busy; - } - } - } if (Array.isArray(responseData)) { returnData.push.apply(returnData, responseData as IDataObject[]); } else if (responseData !== undefined) {