diff --git a/packages/nodes-base/credentials/GoogleTasksOAuth2Api.credentials.ts b/packages/nodes-base/credentials/GoogleTasksOAuth2Api.credentials.ts new file mode 100644 index 0000000000..db30799920 --- /dev/null +++ b/packages/nodes-base/credentials/GoogleTasksOAuth2Api.credentials.ts @@ -0,0 +1,17 @@ +import { ICredentialType, NodePropertyTypes } from 'n8n-workflow'; + +const scopes = ['https://www.googleapis.com/auth/tasks']; + +export class GoogleTasksOAuth2Api implements ICredentialType { + name = 'googleTasksOAuth2Api'; + extends = ['googleOAuth2Api']; + displayName = 'Google Tasks OAuth2 API'; + properties = [ + { + displayName: 'Scope', + name: 'scope', + type: 'hidden' as NodePropertyTypes, + default: scopes.join(' ') + } + ]; +} diff --git a/packages/nodes-base/nodes/Google/Calendar/EventDescription.ts b/packages/nodes-base/nodes/Google/Calendar/EventDescription.ts index e92582a847..a7800334e5 100644 --- a/packages/nodes-base/nodes/Google/Calendar/EventDescription.ts +++ b/packages/nodes-base/nodes/Google/Calendar/EventDescription.ts @@ -1,4 +1,4 @@ -import { INodeProperties } from "n8n-workflow"; +import { INodeProperties } from 'n8n-workflow'; export const eventOperations = [ { @@ -7,67 +7,60 @@ export const eventOperations = [ type: 'options', displayOptions: { show: { - resource: [ - 'event', - ], - }, + resource: ['event'] + } }, options: [ { name: 'Create', value: 'create', - description: 'Add a event to calendar', + description: 'Add a event to calendar' }, { name: 'Delete', value: 'delete', - description: 'Delete an event', + description: 'Delete an event' }, { name: 'Get', value: 'get', - description: 'Retrieve an event', + description: 'Retrieve an event' }, { name: 'Get All', value: 'getAll', - description: 'Retrieve all events from a calendar', + description: 'Retrieve all events from a calendar' }, { name: 'Update', value: 'update', - description: 'Update an event', - }, + description: 'Update an event' + } ], default: 'create', - description: 'The operation to perform.', - }, + description: 'The operation to perform.' + } ] as INodeProperties[]; export const eventFields = [ - -/* -------------------------------------------------------------------------- */ -/* event:create */ -/* -------------------------------------------------------------------------- */ + /* -------------------------------------------------------------------------- */ + /* event:create */ + /* -------------------------------------------------------------------------- */ { displayName: 'Calendar', name: 'calendar', type: 'options', typeOptions: { - loadOptionsMethod: 'getCalendars', + loadOptionsMethod: 'getCalendars' }, required: true, displayOptions: { show: { - operation: [ - 'create', - ], - resource: [ - 'event', - ], - }, + operation: ['create'], + resource: ['event'] + } }, - default: '', + default: '' }, { displayName: 'Start', @@ -76,16 +69,12 @@ export const eventFields = [ required: true, displayOptions: { show: { - operation: [ - 'create', - ], - resource: [ - 'event', - ], - }, + operation: ['create'], + resource: ['event'] + } }, default: '', - description: 'Start time of the event.', + description: 'Start time of the event.' }, { displayName: 'End', @@ -94,16 +83,12 @@ export const eventFields = [ required: true, displayOptions: { show: { - operation: [ - 'create', - ], - resource: [ - 'event', - ], - }, + operation: ['create'], + resource: ['event'] + } }, default: '', - description: 'End time of the event.', + description: 'End time of the event.' }, { displayName: 'Use Default Reminders', @@ -111,15 +96,11 @@ export const eventFields = [ type: 'boolean', displayOptions: { show: { - operation: [ - 'create', - ], - resource: [ - 'event', - ], - }, + operation: ['create'], + resource: ['event'] + } }, - default: true, + default: true }, { displayName: 'Additional Fields', @@ -129,13 +110,9 @@ export const eventFields = [ default: {}, displayOptions: { show: { - operation: [ - 'create', - ], - resource: [ - 'event', - ], - }, + operation: ['create'], + resource: ['event'] + } }, options: [ { @@ -145,15 +122,15 @@ export const eventFields = [ options: [ { name: 'Yes', - value: 'yes', + value: 'yes' }, { name: 'No', - value: 'no', - }, + value: 'no' + } ], default: 'no', - description: 'Wheater the event is all day or not', + description: 'Wheater the event is all day or not' }, { displayName: 'Attendees', @@ -161,55 +138,57 @@ export const eventFields = [ type: 'string', typeOptions: { multipleValues: true, - multipleValueButtonText: 'Add Attendee', + multipleValueButtonText: 'Add Attendee' }, default: '', - description: 'The attendees of the event', + description: 'The attendees of the event' }, { displayName: 'Color', name: 'color', type: 'options', typeOptions: { - loadOptionsMethod: 'getColors', + loadOptionsMethod: 'getColors' }, default: '', - description: 'The color of the event.', + description: 'The color of the event.' }, { displayName: 'Guests Can Invite Others', name: 'guestsCanInviteOthers', type: 'boolean', default: true, - description: 'Whether attendees other than the organizer can invite others to the event', + description: + 'Whether attendees other than the organizer can invite others to the event' }, { displayName: 'Guests Can Modify', name: 'guestsCanModify', type: 'boolean', default: false, - description: 'Whether attendees other than the organizer can modify the event', + description: + 'Whether attendees other than the organizer can modify the event' }, { displayName: 'Guests Can See Other Guests', name: 'guestsCanSeeOtherGuests', type: 'boolean', default: true, - description: `Whether attendees other than the organizer can see who the event's attendees are.`, + description: `Whether attendees other than the organizer can see who the event's attendees are.` }, { displayName: 'ID', name: 'id', type: 'string', default: '', - description: 'Opaque identifier of the event', + description: 'Opaque identifier of the event' }, { displayName: 'Location', name: 'location', type: 'string', default: '', - description: 'Geographic location of the event as free-form text.', + description: 'Geographic location of the event as free-form text.' }, { displayName: 'Max Attendees', @@ -217,7 +196,7 @@ export const eventFields = [ type: 'number', default: 0, description: `The maximum number of attendees to include in the response.
- If there are more than the specified number of attendees, only the participant is returned`, + If there are more than the specified number of attendees, only the participant is returned` }, { displayName: 'Repeat Frecuency', @@ -226,37 +205,37 @@ export const eventFields = [ options: [ { name: 'Daily', - value: 'Daily', + value: 'Daily' }, { name: 'Weekly', - value: 'weekly', + value: 'weekly' }, { name: 'Monthly', - value: 'monthly', + value: 'monthly' }, { name: 'Yearly', - value: 'yearly', - }, + value: 'yearly' + } ], - default: '', + default: '' }, { displayName: 'Repeat Until', name: 'repeatUntil', type: 'dateTime', - default: '', + default: '' }, { displayName: 'Repeat How Many Times?', name: 'repeatHowManyTimes', type: 'number', typeOptions: { - minValue: 1, + minValue: 1 }, - default: 1, + default: 1 }, { displayName: 'Send Updates', @@ -266,28 +245,31 @@ export const eventFields = [ { name: 'All', value: 'all', - description: ' Notifications are sent to all guests', + description: ' Notifications are sent to all guests' }, { name: 'External Only', value: 'externalOnly', - description: 'Notifications are sent to non-Google Calendar guests only', + description: + 'Notifications are sent to non-Google Calendar guests only' }, { name: 'None', value: 'none', - description: ' No notifications are sent. This value should only be used for migration use case', - }, + description: + ' No notifications are sent. This value should only be used for migration use case' + } ], - description: 'Whether to send notifications about the creation of the new event', - default: '', + description: + 'Whether to send notifications about the creation of the new event', + default: '' }, { displayName: 'Summary', name: 'summary', type: 'string', default: '', - description: 'Title of the event.', + description: 'Title of the event.' }, { displayName: 'Show Me As', @@ -297,26 +279,27 @@ export const eventFields = [ { name: 'Available', value: 'transparent', - description: 'The event does not block time on the calendar', + description: 'The event does not block time on the calendar' }, { name: 'Busy', value: 'opaque', - description: ' The event does block time on the calendar.', - }, + description: ' The event does block time on the calendar.' + } ], default: 'opaque', - description: 'Whether the event blocks time on the calendar', + description: 'Whether the event blocks time on the calendar' }, { displayName: 'Timezone', name: 'timezone', type: 'options', typeOptions: { - loadOptionsMethod: 'getTimezones', + loadOptionsMethod: 'getTimezones' }, default: '', - description: 'The timezone the event will have set. By default events are schedule on timezone set in n8n.' + description: + 'The timezone the event will have set. By default events are schedule on timezone set in n8n.' }, { displayName: 'Visibility', @@ -326,28 +309,32 @@ export const eventFields = [ { name: 'Confidential', value: 'confidential', - description: 'The event is private. This value is provided for compatibility reasons.', + description: + 'The event is private. This value is provided for compatibility reasons.' }, { name: 'Default', value: 'default', - description: ' Uses the default visibility for events on the calendar.', + description: + ' Uses the default visibility for events on the calendar.' }, { name: 'Private', value: 'private', - description: 'The event is private and only event attendees may view event details.', + description: + 'The event is private and only event attendees may view event details.' }, { name: 'Public', value: 'public', - description: 'The event is public and event details are visible to all readers of the calendar.', - }, + description: + 'The event is public and event details are visible to all readers of the calendar.' + } ], default: 'default', - description: 'Visibility of the event.', - }, - ], + description: 'Visibility of the event.' + } + ] }, { displayName: 'Reminders', @@ -356,21 +343,15 @@ export const eventFields = [ default: '', placeholder: 'Add Reminder', typeOptions: { - multipleValues: true, + multipleValues: true }, required: false, displayOptions: { show: { - resource: [ - 'event', - ], - operation: [ - 'create', - ], - useDefaultReminders: [ - false, - ], - }, + resource: ['event'], + operation: ['create'], + useDefaultReminders: [false] + } }, options: [ { @@ -384,14 +365,14 @@ export const eventFields = [ options: [ { name: 'Email', - value: 'email', + value: 'email' }, { name: 'Popup', - value: 'popup', - }, + value: 'popup' + } ], - default: '', + default: '' }, { displayName: 'Minutes Before', @@ -399,37 +380,33 @@ export const eventFields = [ type: 'number', typeOptions: { minValue: 0, - maxValue: 40320, + maxValue: 40320 }, - default: 0, - }, - ], + default: 0 + } + ] } ], - description: `If the event doesn't use the default reminders, this lists the reminders specific to the event`, + description: `If the event doesn't use the default reminders, this lists the reminders specific to the event` }, -/* -------------------------------------------------------------------------- */ -/* event:delete */ -/* -------------------------------------------------------------------------- */ + /* -------------------------------------------------------------------------- */ + /* event:delete */ + /* -------------------------------------------------------------------------- */ { displayName: 'Calendar', name: 'calendar', type: 'options', typeOptions: { - loadOptionsMethod: 'getCalendars', + loadOptionsMethod: 'getCalendars' }, required: true, displayOptions: { show: { - operation: [ - 'delete', - ], - resource: [ - 'event', - ], - }, + operation: ['delete'], + resource: ['event'] + } }, - default: '', + default: '' }, { displayName: 'Event ID', @@ -438,15 +415,11 @@ export const eventFields = [ required: true, displayOptions: { show: { - operation: [ - 'delete', - ], - resource: [ - 'event', - ], - }, + operation: ['delete'], + resource: ['event'] + } }, - default: '', + default: '' }, { displayName: 'Options', @@ -456,13 +429,9 @@ export const eventFields = [ default: {}, displayOptions: { show: { - operation: [ - 'delete', - ], - resource: [ - 'event', - ], - }, + operation: ['delete'], + resource: ['event'] + } }, options: [ { @@ -473,46 +442,45 @@ export const eventFields = [ { name: 'All', value: 'all', - description: ' Notifications are sent to all guests', + description: ' Notifications are sent to all guests' }, { name: 'External Only', value: 'externalOnly', - description: 'Notifications are sent to non-Google Calendar guests only', + description: + 'Notifications are sent to non-Google Calendar guests only' }, { name: 'None', value: 'none', - description: ' No notifications are sent. This value should only be used for migration use case', - }, + description: + ' No notifications are sent. This value should only be used for migration use case' + } ], - description: 'Whether to send notifications about the creation of the new event', - default: '', - }, - ], + description: + 'Whether to send notifications about the creation of the new event', + default: '' + } + ] }, -/* -------------------------------------------------------------------------- */ -/* event:get */ -/* -------------------------------------------------------------------------- */ + /* -------------------------------------------------------------------------- */ + /* event:get */ + /* -------------------------------------------------------------------------- */ { displayName: 'Calendar', name: 'calendar', type: 'options', typeOptions: { - loadOptionsMethod: 'getCalendars', + loadOptionsMethod: 'getCalendars' }, required: true, displayOptions: { show: { - operation: [ - 'get', - ], - resource: [ - 'event', - ], - }, + operation: ['get'], + resource: ['event'] + } }, - default: '', + default: '' }, { displayName: 'Event ID', @@ -521,15 +489,11 @@ export const eventFields = [ required: true, displayOptions: { show: { - operation: [ - 'get', - ], - resource: [ - 'event', - ], - }, + operation: ['get'], + resource: ['event'] + } }, - default: '', + default: '' }, { displayName: 'Options', @@ -539,13 +503,9 @@ export const eventFields = [ default: {}, displayOptions: { show: { - operation: [ - 'get', - ], - resource: [ - 'event', - ], - }, + operation: ['get'], + resource: ['event'] + } }, options: [ { @@ -554,42 +514,38 @@ export const eventFields = [ type: 'number', default: 0, description: `The maximum number of attendees to include in the response.
- If there are more than the specified number of attendees, only the participant is returned`, + If there are more than the specified number of attendees, only the participant is returned` }, { displayName: 'Timezone', name: 'timeZone', type: 'options', typeOptions: { - loadOptionsMethod: 'getTimezones', + loadOptionsMethod: 'getTimezones' }, default: '', - description: `Time zone used in the response. The default is the time zone of the calendar.`, - }, - ], + description: `Time zone used in the response. The default is the time zone of the calendar.` + } + ] }, -/* -------------------------------------------------------------------------- */ -/* event:getAll */ -/* -------------------------------------------------------------------------- */ + /* -------------------------------------------------------------------------- */ + /* event:getAll */ + /* -------------------------------------------------------------------------- */ { displayName: 'Calendar', name: 'calendar', type: 'options', typeOptions: { - loadOptionsMethod: 'getCalendars', + loadOptionsMethod: 'getCalendars' }, required: true, displayOptions: { show: { - operation: [ - 'getAll', - ], - resource: [ - 'event', - ], - }, + operation: ['getAll'], + resource: ['event'] + } }, - default: '', + default: '' }, { displayName: 'Return All', @@ -597,16 +553,13 @@ export const eventFields = [ type: 'boolean', displayOptions: { show: { - operation: [ - 'getAll', - ], - resource: [ - 'event', - ], - }, + operation: ['getAll'], + resource: ['event'] + } }, default: false, - description: 'If all results should be returned or only up to a given limit.', + description: + 'If all results should be returned or only up to a given limit.' }, { displayName: 'Limit', @@ -614,23 +567,17 @@ export const eventFields = [ type: 'number', displayOptions: { show: { - operation: [ - 'getAll', - ], - resource: [ - 'event', - ], - returnAll: [ - false, - ], - }, + operation: ['getAll'], + resource: ['event'], + returnAll: [false] + } }, typeOptions: { minValue: 1, - maxValue: 500, + maxValue: 500 }, default: 100, - description: 'How many results to return.', + description: 'How many results to return.' }, { displayName: 'Options', @@ -640,13 +587,9 @@ export const eventFields = [ default: {}, displayOptions: { show: { - operation: [ - 'getAll', - ], - resource: [ - 'event', - ], - }, + operation: ['getAll'], + resource: ['event'] + } }, options: [ { @@ -654,7 +597,8 @@ export const eventFields = [ name: 'iCalUID', type: 'string', default: '', - description: 'Specifies event ID in the iCalendar format to be included in the response', + description: + 'Specifies event ID in the iCalendar format to be included in the response' }, { displayName: 'Max Attendees', @@ -662,7 +606,7 @@ export const eventFields = [ type: 'number', default: 0, description: `The maximum number of attendees to include in the response.
- If there are more than the specified number of attendees, only the participant is returned`, + If there are more than the specified number of attendees, only the participant is returned` }, { displayName: 'Order By', @@ -672,37 +616,40 @@ export const eventFields = [ { name: 'Start Time', value: 'startTime', - description: 'Order by the start date/time (ascending). This is only available when querying single events (i.e. the parameter singleEvents is True)', + description: + 'Order by the start date/time (ascending). This is only available when querying single events (i.e. the parameter singleEvents is True)' }, { name: 'Updated', value: 'updated', - description: 'Order by last modification time (ascending).', - }, + description: 'Order by last modification time (ascending).' + } ], default: '', - description: 'The order of the events returned in the result.', + description: 'The order of the events returned in the result.' }, { displayName: 'Query', name: 'query', type: 'string', default: '', - description: 'Free text search terms to find events that match these terms in any field, except for extended properties.', + description: + 'Free text search terms to find events that match these terms in any field, except for extended properties.' }, { displayName: 'Show Deleted', name: 'showDeleted', type: 'boolean', default: false, - description: 'Whether to include deleted events (with status equals "cancelled") in the result.', + description: + 'Whether to include deleted events (with status equals "cancelled") in the result.' }, { displayName: 'Show Hidden Invitations', name: 'showHiddenInvitations', type: 'boolean', default: false, - description: 'Whether to include hidden invitations in the result.', + description: 'Whether to include hidden invitations in the result.' }, { displayName: 'Single Events', @@ -710,31 +657,31 @@ export const eventFields = [ type: 'boolean', default: false, description: `Whether to expand recurring events into instances and only return single one-off
- events and instances of recurring events, but not the underlying recurring events themselves.`, + events and instances of recurring events, but not the underlying recurring events themselves.` }, { displayName: 'Time Max', name: 'timeMax', type: 'dateTime', default: '', - description: `Upper bound (exclusive) for an event's start time to filter by`, + description: `Upper bound (exclusive) for an event's start time to filter by` }, { displayName: 'Time Min', name: 'timeMin', type: 'dateTime', default: '', - description: `Lower bound (exclusive) for an event's end time to filter by`, + description: `Lower bound (exclusive) for an event's end time to filter by` }, { displayName: 'Timezone', name: 'timeZone', type: 'options', typeOptions: { - loadOptionsMethod: 'getTimezones', + loadOptionsMethod: 'getTimezones' }, default: '', - description: `Time zone used in the response. The default is the time zone of the calendar.`, + description: `Time zone used in the response. The default is the time zone of the calendar.` }, { displayName: 'Updated Min', @@ -742,32 +689,28 @@ export const eventFields = [ type: 'dateTime', default: '', description: `Lower bound for an event's last modification time (as a RFC3339 timestamp) to filter by. - When specified, entries deleted since this time will always be included regardless of showDeleted`, - }, - ], + When specified, entries deleted since this time will always be included regardless of showDeleted` + } + ] }, -/* -------------------------------------------------------------------------- */ -/* event:update */ -/* -------------------------------------------------------------------------- */ + /* -------------------------------------------------------------------------- */ + /* event:update */ + /* -------------------------------------------------------------------------- */ { displayName: 'Calendar', name: 'calendar', type: 'options', typeOptions: { - loadOptionsMethod: 'getCalendars', + loadOptionsMethod: 'getCalendars' }, required: true, displayOptions: { show: { - operation: [ - 'update', - ], - resource: [ - 'event', - ], - }, + operation: ['update'], + resource: ['event'] + } }, - default: '', + default: '' }, { displayName: 'Event ID', @@ -776,15 +719,11 @@ export const eventFields = [ required: true, displayOptions: { show: { - operation: [ - 'update', - ], - resource: [ - 'event', - ], - }, + operation: ['update'], + resource: ['event'] + } }, - default: '', + default: '' }, { displayName: 'Use Default Reminders', @@ -792,15 +731,11 @@ export const eventFields = [ type: 'boolean', displayOptions: { show: { - operation: [ - 'update', - ], - resource: [ - 'event', - ], - }, + operation: ['update'], + resource: ['event'] + } }, - default: true, + default: true }, { displayName: 'Update Fields', @@ -810,13 +745,9 @@ export const eventFields = [ default: {}, displayOptions: { show: { - operation: [ - 'update', - ], - resource: [ - 'event', - ], - }, + operation: ['update'], + resource: ['event'] + } }, options: [ { @@ -826,15 +757,15 @@ export const eventFields = [ options: [ { name: 'Yes', - value: 'yes', + value: 'yes' }, { name: 'No', - value: 'no', - }, + value: 'no' + } ], default: 'no', - description: 'Wheater the event is all day or not', + description: 'Wheater the event is all day or not' }, { displayName: 'Attendees', @@ -842,62 +773,64 @@ export const eventFields = [ type: 'string', typeOptions: { multipleValues: true, - multipleValueButtonText: 'Add Attendee', + multipleValueButtonText: 'Add Attendee' }, default: '', - description: 'The attendees of the event', + description: 'The attendees of the event' }, { displayName: 'Color', name: 'color', type: 'options', typeOptions: { - loadOptionsMethod: 'getColors', + loadOptionsMethod: 'getColors' }, default: '', - description: 'The color of the event.', + description: 'The color of the event.' }, { displayName: 'End', name: 'end', type: 'dateTime', default: '', - description: 'End time of the event.', + description: 'End time of the event.' }, { displayName: 'Guests Can Invite Others', name: 'guestsCanInviteOthers', type: 'boolean', default: true, - description: 'Whether attendees other than the organizer can invite others to the event', + description: + 'Whether attendees other than the organizer can invite others to the event' }, { displayName: 'Guests Can Modify', name: 'guestsCanModify', type: 'boolean', default: false, - description: 'Whether attendees other than the organizer can modify the event', + description: + 'Whether attendees other than the organizer can modify the event' }, { displayName: 'Guests Can See Other Guests', name: 'guestsCanSeeOtherGuests', type: 'boolean', default: true, - description: `Whether attendees other than the organizer can see who the event's attendees are.`, + description: `Whether attendees other than the organizer can see who the event's attendees are.` }, { displayName: 'ID', name: 'id', type: 'string', default: '', - description: 'Opaque identifier of the event', + description: 'Opaque identifier of the event' }, { displayName: 'Location', name: 'location', type: 'string', default: '', - description: 'Geographic location of the event as free-form text.', + description: 'Geographic location of the event as free-form text.' }, { displayName: 'Max Attendees', @@ -905,7 +838,7 @@ export const eventFields = [ type: 'number', default: 0, description: `The maximum number of attendees to include in the response.
- If there are more than the specified number of attendees, only the participant is returned`, + If there are more than the specified number of attendees, only the participant is returned` }, { displayName: 'Repeat Frecuency', @@ -914,44 +847,44 @@ export const eventFields = [ options: [ { name: 'Daily', - value: 'Daily', + value: 'Daily' }, { name: 'Weekly', - value: 'weekly', + value: 'weekly' }, { name: 'Monthly', - value: 'monthly', + value: 'monthly' }, { name: 'Yearly', - value: 'yearly', - }, + value: 'yearly' + } ], - default: '', + default: '' }, { displayName: 'Repeat Until', name: 'repeatUntil', type: 'dateTime', - default: '', + default: '' }, { displayName: 'Repeat How Many Times?', name: 'repeatHowManyTimes', type: 'number', typeOptions: { - minValue: 1, + minValue: 1 }, - default: 1, + default: 1 }, { displayName: 'Start', name: 'start', type: 'dateTime', default: '', - description: 'Start time of the event.', + description: 'Start time of the event.' }, { displayName: 'Send Updates', @@ -961,28 +894,31 @@ export const eventFields = [ { name: 'All', value: 'all', - description: ' Notifications are sent to all guests', + description: ' Notifications are sent to all guests' }, { name: 'External Only', value: 'externalOnly', - description: 'Notifications are sent to non-Google Calendar guests only', + description: + 'Notifications are sent to non-Google Calendar guests only' }, { name: 'None', value: 'none', - description: ' No notifications are sent. This value should only be used for migration use case', - }, + description: + ' No notifications are sent. This value should only be used for migration use case' + } ], - description: 'Whether to send notifications about the creation of the new event', - default: '', + description: + 'Whether to send notifications about the creation of the new event', + default: '' }, { displayName: 'Summary', name: 'summary', type: 'string', default: '', - description: 'Title of the event.', + description: 'Title of the event.' }, { displayName: 'Show Me As', @@ -992,26 +928,27 @@ export const eventFields = [ { name: 'Available', value: 'transparent', - description: 'The event does not block time on the calendar', + description: 'The event does not block time on the calendar' }, { name: 'Busy', value: 'opaque', - description: ' The event does block time on the calendar.', - }, + description: ' The event does block time on the calendar.' + } ], default: 'opaque', - description: 'Whether the event blocks time on the calendar', + description: 'Whether the event blocks time on the calendar' }, { displayName: 'Timezone', name: 'timezone', type: 'options', typeOptions: { - loadOptionsMethod: 'getTimezones', + loadOptionsMethod: 'getTimezones' }, default: '', - description: 'The timezone the event will have set. By default events are schedule on n8n timezone ' + description: + 'The timezone the event will have set. By default events are schedule on n8n timezone ' }, { displayName: 'Visibility', @@ -1021,28 +958,32 @@ export const eventFields = [ { name: 'Confidential', value: 'confidential', - description: 'The event is private. This value is provided for compatibility reasons.', + description: + 'The event is private. This value is provided for compatibility reasons.' }, { name: 'Default', value: 'default', - description: ' Uses the default visibility for events on the calendar.', + description: + ' Uses the default visibility for events on the calendar.' }, { name: 'Public', value: 'public', - description: 'The event is public and event details are visible to all readers of the calendar.', + description: + 'The event is public and event details are visible to all readers of the calendar.' }, { name: 'Private', value: 'private', - description: 'The event is private and only event attendees may view event details.', - }, + description: + 'The event is private and only event attendees may view event details.' + } ], default: 'default', - description: 'Visibility of the event.', - }, - ], + description: 'Visibility of the event.' + } + ] }, { displayName: 'Reminders', @@ -1051,21 +992,15 @@ export const eventFields = [ default: '', placeholder: 'Add Reminder', typeOptions: { - multipleValues: true, + multipleValues: true }, required: false, displayOptions: { show: { - resource: [ - 'event', - ], - operation: [ - 'update', - ], - useDefaultReminders: [ - false, - ], - }, + resource: ['event'], + operation: ['update'], + useDefaultReminders: [false] + } }, options: [ { @@ -1079,14 +1014,14 @@ export const eventFields = [ options: [ { name: 'Email', - value: 'email', + value: 'email' }, { name: 'Popup', - value: 'popup', - }, + value: 'popup' + } ], - default: '', + default: '' }, { displayName: 'Minutes Before', @@ -1094,13 +1029,13 @@ export const eventFields = [ type: 'number', typeOptions: { minValue: 0, - maxValue: 40320, + maxValue: 40320 }, - default: 0, - }, - ], + default: 0 + } + ] } ], - description: `If the event doesn't use the default reminders, this lists the reminders specific to the event`, - }, + description: `If the event doesn't use the default reminders, this lists the reminders specific to the event` + } ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts b/packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts index 1f185935d7..53d3d9de83 100644 --- a/packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts +++ b/packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts @@ -1,6 +1,4 @@ -import { - IExecuteFunctions, -} from 'n8n-core'; +import { IExecuteFunctions } from 'n8n-core'; import { IDataObject, @@ -8,22 +6,14 @@ import { INodeTypeDescription, INodeType, ILoadOptionsFunctions, - INodePropertyOptions, + INodePropertyOptions } from 'n8n-workflow'; -import { - googleApiRequest, - googleApiRequestAllItems, -} from './GenericFunctions'; +import { googleApiRequest, googleApiRequestAllItems } from './GenericFunctions'; -import { - eventOperations, - eventFields, -} from './EventDescription'; +import { eventOperations, eventFields } from './EventDescription'; -import { - IEvent, -} from './EventInterface'; +import { IEvent } from './EventInterface'; import * as moment from 'moment-timezone'; @@ -38,15 +28,15 @@ export class GoogleCalendar implements INodeType { description: 'Consume Google Calendar API.', defaults: { name: 'Google Calendar', - color: '#3E87E4', + color: '#3E87E4' }, inputs: ['main'], outputs: ['main'], credentials: [ { name: 'googleCalendarOAuth2Api', - required: true, - }, + required: true + } ], properties: [ { @@ -56,70 +46,85 @@ export class GoogleCalendar implements INodeType { options: [ { name: 'Event', - value: 'event', - }, + value: 'event' + } ], default: 'event', - description: 'The resource to operate on.', + description: 'The resource to operate on.' }, ...eventOperations, - ...eventFields, - ], + ...eventFields + ] }; methods = { loadOptions: { // Get all the calendars to display them to user so that he can // select them easily - async getCalendars(this: ILoadOptionsFunctions): Promise { + async getCalendars( + this: ILoadOptionsFunctions + ): Promise { const returnData: INodePropertyOptions[] = []; - const calendars = await googleApiRequestAllItems.call(this, 'items', 'GET', '/calendar/v3/users/me/calendarList'); + const calendars = await googleApiRequestAllItems.call( + this, + 'items', + 'GET', + '/calendar/v3/users/me/calendarList' + ); for (const calendar of calendars) { const calendarName = calendar.summary; const calendarId = calendar.id; returnData.push({ name: calendarName, - value: calendarId, + value: calendarId }); } return returnData; }, // Get all the colors to display them to user so that he can // select them easily - async getColors(this: ILoadOptionsFunctions): Promise { + async getColors( + this: ILoadOptionsFunctions + ): Promise { const returnData: INodePropertyOptions[] = []; - const { calendar } = await googleApiRequest.call(this, 'GET', '/calendar/v3/colors'); - for (const key of Object.keys(calendar)) { + const { calendar } = await googleApiRequest.call( + this, + 'GET', + '/calendar/v3/colors' + ); + for (const key of Object.keys(calendar)) { const colorName = calendar[key].background; const colorId = key; returnData.push({ name: `${colorName} - ${colorId}`, - value: colorId, + value: colorId }); } return returnData; }, // Get all the timezones to display them to user so that he can // select them easily - async getTimezones(this: ILoadOptionsFunctions): Promise { + async getTimezones( + this: ILoadOptionsFunctions + ): Promise { const returnData: INodePropertyOptions[] = []; for (const timezone of moment.tz.names()) { const timezoneName = timezone; const timezoneId = timezone; returnData.push({ name: timezoneName, - value: timezoneId, + value: timezoneId }); } return returnData; - }, - }, + } + } }; async execute(this: IExecuteFunctions): Promise { const items = this.getInputData(); const returnData: IDataObject[] = []; - const length = items.length as unknown as number; + const length = (items.length as unknown) as number; const qs: IDataObject = {}; let responseData; const resource = this.getNodeParameter('resource', 0) as string; @@ -131,8 +136,14 @@ export class GoogleCalendar implements INodeType { const calendarId = this.getNodeParameter('calendar', i) as string; const start = this.getNodeParameter('start', i) as string; const end = this.getNodeParameter('end', i) as string; - const useDefaultReminders = this.getNodeParameter('useDefaultReminders', i) as boolean; - const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const useDefaultReminders = this.getNodeParameter( + 'useDefaultReminders', + i + ) as boolean; + const additionalFields = this.getNodeParameter( + 'additionalFields', + i + ) as IDataObject; if (additionalFields.maxAttendees) { qs.maxAttendees = additionalFields.maxAttendees as number; } @@ -145,17 +156,19 @@ export class GoogleCalendar implements INodeType { const body: IEvent = { start: { dateTime: start, - timeZone: additionalFields.timeZone || this.getTimezone(), + timeZone: additionalFields.timeZone || this.getTimezone() }, end: { dateTime: end, - timeZone: additionalFields.timeZone || this.getTimezone(), + timeZone: additionalFields.timeZone || this.getTimezone() } }; if (additionalFields.attendees) { - body.attendees = (additionalFields.attendees as string[]).map(attendee => { - return { email: attendee }; - }); + body.attendees = (additionalFields.attendees as string[]).map( + attendee => { + return { email: attendee }; + } + ); } if (additionalFields.color) { body.colorId = additionalFields.color as string; @@ -188,9 +201,12 @@ export class GoogleCalendar implements INodeType { body.visibility = additionalFields.visibility as string; } if (!useDefaultReminders) { - const reminders = (this.getNodeParameter('remindersUi', i) as IDataObject).remindersValues as IDataObject[]; + const reminders = (this.getNodeParameter( + 'remindersUi', + i + ) as IDataObject).remindersValues as IDataObject[]; body.reminders = { - useDefault: false, + useDefault: false }; if (reminders) { body.reminders.overrides = reminders; @@ -198,32 +214,54 @@ export class GoogleCalendar implements INodeType { } if (additionalFields.allday) { body.start = { - date: moment(start).utc().format('YYYY-MM-DD'), + date: moment(start) + .utc() + .format('YYYY-MM-DD') }; body.end = { - date: moment(end).utc().format('YYYY-MM-DD'), + date: moment(end) + .utc() + .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.repeatHowManyTimes - && additionalFields.repeatUntil) { - throw new Error(`You can set either 'Repeat How Many Times' or 'Repeat Until' but not both`); + if ( + additionalFields.repeatHowManyTimes && + additionalFields.repeatUntil + ) { + throw new Error( + `You can set either 'Repeat How Many Times' or 'Repeat Until' but not both` + ); } if (additionalFields.repeatFrecuency) { - body.recurrence?.push(`FREQ=${(additionalFields.repeatFrecuency as string).toUpperCase()};`); + body.recurrence?.push( + `FREQ=${(additionalFields.repeatFrecuency as string).toUpperCase()};` + ); } if (additionalFields.repeatHowManyTimes) { - body.recurrence?.push(`COUNT=${additionalFields.repeatHowManyTimes};`); + body.recurrence?.push( + `COUNT=${additionalFields.repeatHowManyTimes};` + ); } if (additionalFields.repeatUntil) { - body.recurrence?.push(`UNTIL=${moment(additionalFields.repeatUntil as string).utc().format('YYYYMMDDTHHmmss')}Z`); + body.recurrence?.push( + `UNTIL=${moment(additionalFields.repeatUntil as string) + .utc() + .format('YYYYMMDDTHHmmss')}Z` + ); } if (body.recurrence.length !== 0) { body.recurrence = [`RRULE:${body.recurrence.join('')}`]; } - responseData = await googleApiRequest.call(this, 'POST', `/calendar/v3/calendars/${calendarId}/events`, body, qs); + responseData = await googleApiRequest.call( + this, + 'POST', + `/calendar/v3/calendars/${calendarId}/events`, + body, + qs + ); } //https://developers.google.com/calendar/v3/reference/events/delete if (operation === 'delete') { @@ -233,8 +271,13 @@ export class GoogleCalendar implements INodeType { if (options.sendUpdates) { qs.sendUpdates = options.sendUpdates as number; } - responseData = await googleApiRequest.call(this, 'DELETE', `/calendar/v3/calendars/${calendarId}/events/${eventId}`, {}); - responseData = { success: true }; + responseData = await 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') { @@ -247,7 +290,13 @@ export class GoogleCalendar implements INodeType { if (options.timeZone) { qs.timeZone = options.timeZone as string; } - responseData = await googleApiRequest.call(this, 'GET', `/calendar/v3/calendars/${calendarId}/events/${eventId}`, {}, qs); + responseData = await googleApiRequest.call( + this, + 'GET', + `/calendar/v3/calendars/${calendarId}/events/${eventId}`, + {}, + qs + ); } //https://developers.google.com/calendar/v3/reference/events/list if (operation === 'getAll') { @@ -288,10 +337,23 @@ export class GoogleCalendar implements INodeType { qs.updatedMin = options.updatedMin as string; } if (returnAll) { - responseData = await googleApiRequestAllItems.call(this, 'items', 'GET', `/calendar/v3/calendars/${calendarId}/events`, {}, qs); + responseData = await googleApiRequestAllItems.call( + this, + 'items', + 'GET', + `/calendar/v3/calendars/${calendarId}/events`, + {}, + qs + ); } else { qs.maxResults = this.getNodeParameter('limit', i) as number; - responseData = await googleApiRequest.call(this, 'GET', `/calendar/v3/calendars/${calendarId}/events`, {}, qs); + responseData = await googleApiRequest.call( + this, + 'GET', + `/calendar/v3/calendars/${calendarId}/events`, + {}, + qs + ); responseData = responseData.items; } } @@ -299,8 +361,14 @@ export class GoogleCalendar implements INodeType { if (operation === 'update') { const calendarId = this.getNodeParameter('calendar', i) as string; const eventId = this.getNodeParameter('eventId', i) as string; - const useDefaultReminders = this.getNodeParameter('useDefaultReminders', i) as boolean; - const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + const useDefaultReminders = this.getNodeParameter( + 'useDefaultReminders', + i + ) as boolean; + const updateFields = this.getNodeParameter( + 'updateFields', + i + ) as IDataObject; if (updateFields.maxAttendees) { qs.maxAttendees = updateFields.maxAttendees as number; } @@ -314,19 +382,21 @@ export class GoogleCalendar implements INodeType { if (updateFields.start) { body.start = { dateTime: updateFields.start, - timeZone: updateFields.timeZone || this.getTimezone(), + timeZone: updateFields.timeZone || this.getTimezone() }; } if (updateFields.end) { body.end = { dateTime: updateFields.end, - timeZone: updateFields.timeZone || this.getTimezone(), + timeZone: updateFields.timeZone || this.getTimezone() }; } if (updateFields.attendees) { - body.attendees = (updateFields.attendees as string[]).map(attendee => { - return { email: attendee }; - }); + body.attendees = (updateFields.attendees as string[]).map( + attendee => { + return { email: attendee }; + } + ); } if (updateFields.color) { body.colorId = updateFields.color as string; @@ -359,46 +429,64 @@ export class GoogleCalendar implements INodeType { body.visibility = updateFields.visibility as string; } if (!useDefaultReminders) { - const reminders = (this.getNodeParameter('remindersUi', i) as IDataObject).remindersValues as IDataObject[]; + const reminders = (this.getNodeParameter( + 'remindersUi', + i + ) as IDataObject).remindersValues as IDataObject[]; body.reminders = { - useDefault: false, + useDefault: false }; if (reminders) { body.reminders.overrides = reminders; } } - if (updateFields.allday - && updateFields.start - && updateFields.end) { + if (updateFields.allday && updateFields.start && updateFields.end) { body.start = { - date: moment(updateFields.start as string).utc().format('YYYY-MM-DD'), + date: moment(updateFields.start as string) + .utc() + .format('YYYY-MM-DD') }; body.end = { - date: moment(updateFields.end as string).utc().format('YYYY-MM-DD'), + date: moment(updateFields.end as string) + .utc() + .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 (updateFields.repeatHowManyTimes - && updateFields.repeatUntil) { - throw new Error(`You can set either 'Repeat How Many Times' or 'Repeat Until' but not both`); + if (updateFields.repeatHowManyTimes && updateFields.repeatUntil) { + throw new Error( + `You can set either 'Repeat How Many Times' or 'Repeat Until' but not both` + ); } if (updateFields.repeatFrecuency) { - body.recurrence?.push(`FREQ=${(updateFields.repeatFrecuency as string).toUpperCase()};`); + body.recurrence?.push( + `FREQ=${(updateFields.repeatFrecuency as string).toUpperCase()};` + ); } if (updateFields.repeatHowManyTimes) { body.recurrence?.push(`COUNT=${updateFields.repeatHowManyTimes};`); } if (updateFields.repeatUntil) { - body.recurrence?.push(`UNTIL=${moment(updateFields.repeatUntil as string).utc().format('YYYYMMDDTHHmmss')}Z`); + body.recurrence?.push( + `UNTIL=${moment(updateFields.repeatUntil as string) + .utc() + .format('YYYYMMDDTHHmmss')}Z` + ); } if (body.recurrence.length !== 0) { body.recurrence = [`RRULE:${body.recurrence.join('')}`]; } else { delete body.recurrence; } - responseData = await googleApiRequest.call(this, 'PATCH', `/calendar/v3/calendars/${calendarId}/events/${eventId}`, body, qs); + responseData = await googleApiRequest.call( + this, + 'PATCH', + `/calendar/v3/calendars/${calendarId}/events/${eventId}`, + body, + qs + ); } } } diff --git a/packages/nodes-base/nodes/Google/Task/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Task/GenericFunctions.ts new file mode 100644 index 0000000000..19652d1d4a --- /dev/null +++ b/packages/nodes-base/nodes/Google/Task/GenericFunctions.ts @@ -0,0 +1,83 @@ +import { OptionsWithUri } from 'request'; + +import { + IExecuteFunctions, + IExecuteSingleFunctions, + ILoadOptionsFunctions +} from 'n8n-core'; + +import { IDataObject } from 'n8n-workflow'; + +export async function googleApiRequest( + this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, + method: string, + resource: string, + body: any = {}, + qs: IDataObject = {}, + uri?: string, + headers: IDataObject = {} +): Promise { + const options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/json' + }, + method, + body, + qs, + uri: uri || `https://www.googleapis.com${resource}`, + json: true + }; + try { + if (Object.keys(headers).length !== 0) { + options.headers = Object.assign({}, options.headers, headers); + } + if (Object.keys(body).length === 0) { + delete options.body; + } + //@ts-ignore + return await this.helpers.requestOAuth2.call( + this, + 'googleTasksOAuth2Api', + options + ); + } catch (error) { + if (error.response && error.response.body && error.response.body.message) { + // Try to return the error prettier + throw new Error( + `Google Tasks error response [${error.statusCode}]: ${error.response.body.message}` + ); + } + throw error; + } +} + +export async function googleApiRequestAllItems( + this: IExecuteFunctions | ILoadOptionsFunctions, + propertyName: string, + method: string, + endpoint: string, + body: any = {}, + query: IDataObject = {} +): Promise { + const returnData: IDataObject[] = []; + + let responseData; + query.maxResults = 100; + + do { + responseData = await googleApiRequest.call( + this, + method, + endpoint, + body, + query + ); + query.pageToken = responseData['nextPageToken']; + returnData.push.apply(returnData, responseData[propertyName]); + } while ( + responseData['nextPageToken'] !== undefined && + responseData['nextPageToken'] !== '' + ); + + return returnData; +} diff --git a/packages/nodes-base/nodes/Google/Task/GoogleTasks.node.ts b/packages/nodes-base/nodes/Google/Task/GoogleTasks.node.ts new file mode 100644 index 0000000000..450b389376 --- /dev/null +++ b/packages/nodes-base/nodes/Google/Task/GoogleTasks.node.ts @@ -0,0 +1,290 @@ +import { IExecuteFunctions } from 'n8n-core'; + +import { + IDataObject, + INodeExecutionData, + INodeTypeDescription, + INodeType, + ILoadOptionsFunctions, + INodePropertyOptions +} from 'n8n-workflow'; +import { taskOperations, taskFields } from './TaskDescription'; + +import { googleApiRequest, googleApiRequestAllItems } from './GenericFunctions'; + +export class GoogleTasks implements INodeType { + description: INodeTypeDescription = { + displayName: 'Google Tasks', + name: 'googleTasks', + icon: 'file:googleTasks.png', + group: ['input'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Consume Google Tasks API.', + defaults: { + name: 'Google Tasks', + color: '#3E87E4' + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'googleTasksOAuth2Api', + required: true + } + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Task', + value: 'task' + } + ], + default: 'task', + description: 'The resource to operate on.' + }, + ...taskOperations, + ...taskFields + ] + }; + methods = { + loadOptions: { + // Get all the tasklists to display them to user so that he can select them easily + + async getTasks( + this: ILoadOptionsFunctions + ): Promise { + const returnData: INodePropertyOptions[] = []; + const tasks = await googleApiRequestAllItems.call( + this, + 'items', + 'GET', + '/tasks/v1/users/@me/lists' + ); + for (const task of tasks) { + const taskName = task.title; + const taskId = task.id; + returnData.push({ + name: taskName, + value: taskId + }); + } + return returnData; + } + } + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: IDataObject[] = []; + const length = (items.length as unknown) as number; + const qs: IDataObject = {}; + let responseData; + const resource = this.getNodeParameter('resource', 0) as string; + const operation = this.getNodeParameter('operation', 0) as string; + let body: IDataObject = {}; + for (let i = 0; i < length; i++) { + if (resource === 'task') { + if (operation === 'create') { + body = {}; + //https://developers.google.com/tasks/v1/reference/tasks/insert + const taskId = this.getNodeParameter('task', i) as string; + const additionalFields = this.getNodeParameter( + 'additionalFields', + i + ) as IDataObject; + + if (additionalFields.parent) + qs.parent = additionalFields.parent as string; + + if (additionalFields.previous) + qs.previous = additionalFields.previous as string; + + if (additionalFields.links) { + body.links = (additionalFields.links as string[]).map(link => { + return { link: link }; + }); + } + if (additionalFields.status) + body.status = additionalFields.status as string; + + if (additionalFields.notes) + body.notes = additionalFields.notes as string; + + if (additionalFields.title) + body.title = additionalFields.title as string; + + if (additionalFields.dueDate) + body.dueDate = additionalFields.dueDate as string; + + if (additionalFields.completed) + body.completed = additionalFields.completed as string; + + if (additionalFields.deleted) + body.deleted = additionalFields.deleted as boolean; + + if (additionalFields.hidden) + body.hidden = additionalFields.hidden as boolean; + + if (additionalFields.position) + body.position = additionalFields.position as string; + + if (additionalFields.selfLink) + body.selfLink = additionalFields.selfLink as string; + + responseData = await googleApiRequest.call( + this, + 'POST', + `/tasks/v1/lists/${taskId}/tasks`, + body, + qs + ); + } + if (operation == 'delete') { + //https://developers.google.com/tasks/v1/reference/tasks/delete + const taskListId = this.getNodeParameter('task', i) as string; + const taskId = this.getNodeParameter('taskId', i) as string; + + responseData = await googleApiRequest.call( + this, + 'DELETE', + `/tasks/v1/lists/${taskListId}/tasks/${taskId}`, + {} + ); + responseData = { success: true }; + } + if (operation === 'get') { + //https://developers.google.com/tasks/v1/reference/tasks/get + const taskListId = this.getNodeParameter('task', i) as string; + const taskId = this.getNodeParameter('taskId', i) as string; + responseData = await googleApiRequest.call( + this, + 'GET', + `/tasks/v1/lists/${taskListId}/tasks/${taskId}`, + {}, + qs + ); + } + if (operation === 'getAll') { + //https://developers.google.com/tasks/v1/reference/tasks/list + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const taskListId = this.getNodeParameter('task', i) as string; + const options = this.getNodeParameter( + 'additionalFields', + i + ) as IDataObject; + if (options.completedMax) { + qs.completedMax = options.completedMax as string; + } + if (options.completedMin) { + qs.completedMin = options.completedMin as string; + } + if (options.dueMin) { + qs.dueMin = options.dueMin as string; + } + if (options.dueMax) { + qs.dueMax = options.dueMax as string; + } + if (options.pageToken) { + qs.pageToken = options.pageToken as string; + } + if (options.showCompleted) { + qs.showCompleted = options.showCompleted as boolean; + } + if (options.showDeleted) { + qs.showDeleted = options.showDeleted as boolean; + } + if (options.showHidden) { + qs.showHidden = options.showHidden as boolean; + } + + if (options.updatedMin) { + qs.updatedMin = options.updatedMin as string; + } + if (returnAll) { + responseData = await googleApiRequestAllItems.call( + this, + 'items', + 'GET', + `/tasks/v1/lists/${taskListId}/tasks`, + {}, + qs + ); + } else { + qs.maxResults = this.getNodeParameter('limit', i) as number; + responseData = await googleApiRequest.call( + this, + 'GET', + `/tasks/v1/lists/${taskListId}/tasks`, + {}, + qs + ); + responseData = responseData.items; + } + } + if (operation == 'update') { + body = {}; + //https://developers.google.com/tasks/v1/reference/tasks/patch + const taskListId = this.getNodeParameter('task', i) as string; + const taskId = this.getNodeParameter('taskId', i) as string; + const updateFields = this.getNodeParameter( + 'updateFields', + i + ) as IDataObject; + + if (updateFields.parent) qs.parent = updateFields.parent as string; + + if (updateFields.previous) + qs.previous = updateFields.previous as string; + + if (updateFields.links) { + body.links = (updateFields.links as string[]).map(link => { + return { link: link }; + }); + } + if (updateFields.status) body.status = updateFields.status as string; + + if (updateFields.notes) body.notes = updateFields.notes as string; + + if (updateFields.title) body.title = updateFields.title as string; + + if (updateFields.dueDate) + body.dueDate = updateFields.dueDate as string; + + if (updateFields.completed) + body.completed = updateFields.completed as string; + + if (updateFields.deleted) + body.deleted = updateFields.deleted as boolean; + + if (updateFields.hidden) body.hidden = updateFields.hidden as boolean; + + if (updateFields.position) + body.position = updateFields.position as string; + + if (updateFields.selfLink) + body.selfLink = updateFields.selfLink as string; + + responseData = await googleApiRequest.call( + this, + 'PATCH', + `/tasks/v1/lists/${taskListId}/tasks/${taskId}`, + body, + qs + ); + } + } + } + + if (Array.isArray(responseData)) { + returnData.push.apply(returnData, responseData as IDataObject[]); + } else if (responseData !== undefined) { + returnData.push(responseData as IDataObject); + } + return [this.helpers.returnJsonArray(returnData)]; + } +} diff --git a/packages/nodes-base/nodes/Google/Task/TaskDescription.ts b/packages/nodes-base/nodes/Google/Task/TaskDescription.ts new file mode 100644 index 0000000000..24c5b9018b --- /dev/null +++ b/packages/nodes-base/nodes/Google/Task/TaskDescription.ts @@ -0,0 +1,539 @@ +import { INodeProperties } from 'n8n-workflow'; + +export const taskOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: ['task'] + } + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Add a task to tasklist' + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a task' + }, + { + name: 'Get', + value: 'get', + description: 'Retrieve a task' + }, + { + name: 'Get All', + value: 'getAll', + description: 'Retrieve all tasks from a tasklist' + }, + { + name: 'Update', + value: 'update', + description: 'Update a task' + } + ], + default: 'create', + description: 'The operation to perform.' + } +] as INodeProperties[]; + +export const taskFields = [ + /* -------------------------------------------------------------------------- */ + /* task:create */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'TaskList', + name: 'task', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTasks' + }, + required: true, + displayOptions: { + show: { + operation: ['create'], + resource: ['task'] + } + }, + default: '' + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: ['create'], + resource: ['task'] + } + }, + options: [ + { + displayName: 'Status', + name: 'status', + type: 'options', + options: [ + { + name: 'needs Action', + value: 'needsAction' + }, + { + name: 'completed', + value: 'completed' + } + ], + default: '', + description: 'Current status of the task.' + }, + { + displayName: 'Links', + name: 'links', + type: 'string', + typeOptions: { + multipleValues: true, + multipleValueButtonText: 'Add Link' + }, + default: '', + description: 'The links to insert in the task.' + }, + + { + displayName: 'Notes', + name: 'notes', + type: 'string', + default: '', + description: 'Additional Notes.' + }, + { + displayName: 'Title', + name: 'title', + type: 'string', + default: '', + description: 'Title of the task.' + }, + { + displayName: 'Due Date', + name: 'dueDate', + type: 'dateTime', + default: '', + description: 'Due date of the task.' + }, + { + displayName: 'Completion date', + name: 'completed', + type: 'dateTime', + default: '', + description: `Completion date of the task (as a RFC 3339 timestamp). This field is omitted if the task has not been completed.` + }, + + { + displayName: 'Deleted status', + name: 'deleted', + type: 'boolean', + default: false, + description: 'Flag indicating whether the task has been deleted.' + }, + { + displayName: 'Hidden', + name: 'hidden', + type: 'boolean', + default: false, + description: 'Flag indicating whether the task is hidden.' + }, + { + displayName: 'Parent', + name: 'parent', + type: 'string', + default: '', + description: + 'Parent task identifier.This field is omitted if it is a top-level task.' + }, + { + displayName: 'Position', + name: 'position', + type: 'string', + default: '', + description: + 'Parent task identifier.This field is omitted if it is a top-level task.' + }, + { + displayName: 'Self Link', + name: 'selfLink', + type: 'string', + default: '', + description: + 'URL pointing to this task. Used to retrieve, update, or delete this task.' + }, + { + displayName: 'Previous', + name: 'previous', + type: 'string', + default: '', + description: + 'Previous sibling task identifier. If the task is created at the first position among its siblings, this parameter is omitted.' + } + ] + }, + /* -------------------------------------------------------------------------- */ + /* task:delete */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'TaskList', + name: 'task', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTasks' + }, + required: true, + displayOptions: { + show: { + operation: ['delete'], + resource: ['task'] + } + }, + default: '' + }, + { + displayName: 'Task ID', + name: 'taskId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: ['delete'], + resource: ['task'] + } + }, + default: '' + }, + /* -------------------------------------------------------------------------- */ + /* task:get */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'TaskList', + name: 'task', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTasks' + }, + required: true, + displayOptions: { + show: { + operation: ['get'], + resource: ['task'] + } + }, + default: '' + }, + { + displayName: 'Task ID', + name: 'taskId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: ['get'], + resource: ['task'] + } + }, + default: '' + }, + /* -------------------------------------------------------------------------- */ + /* task:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'TaskList', + name: 'task', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTasks' + }, + required: true, + displayOptions: { + show: { + operation: ['getAll'], + resource: ['task'] + } + }, + default: '' + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: ['getAll'], + resource: ['task'] + } + }, + default: false, + description: + 'If all results should be returned or only up to a given limit.' + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + operation: ['getAll'], + resource: ['task'], + returnAll: [false] + } + }, + typeOptions: { + minValue: 1, + maxValue: 100 + }, + default: 20, + description: 'How many results to return.' + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: ['getAll'], + resource: ['task'] + } + }, + + options: [ + { + displayName: 'Completed Max', + name: 'completedMax', + type: 'dateTime', + default: '', + description: + 'Upper bound for a task completion date (as a RFC 3339 timestamp) to filter by.' + }, + { + displayName: 'Completed Min', + name: 'completedMin', + type: 'dateTime', + default: '', + description: + 'Lower bound for a task completion date (as a RFC 3339 timestamp) to filter by.' + }, + { + displayName: 'Due Min', + name: 'dueMin', + type: 'dateTime', + default: '', + description: + 'Lower bound for a task due date (as a RFC 3339 timestamp) to filter by.' + }, + { + displayName: 'Due Max', + name: 'dueMax', + type: 'dateTime', + default: '', + description: + 'Upper bound for a task due date (as a RFC 3339 timestamp) to filter by.' + }, + + { + displayName: 'Page Token', + name: 'pageToken', + type: 'string', + default: '', + description: 'Token specifying the result page to return.' + }, + + { + displayName: 'Show Completed', + name: 'showCompleted', + type: 'boolean', + default: true, + description: + 'Flag indicating whether completed tasks are returned in the result' + }, + { + displayName: 'Show Deleted', + name: 'showDeleted', + type: 'boolean', + default: false, + description: + 'Flag indicating whether deleted tasks are returned in the result' + }, + { + displayName: 'Show Hidden', + name: 'showHidden', + type: 'boolean', + default: false, + description: + 'Flag indicating whether hidden tasks are returned in the result' + }, + { + displayName: 'updated Min', + name: 'updatedMin', + type: 'string', + + description: + 'Lower bound for a task last modification time (as a RFC 3339 timestamp) to filter by.' + } + ] + }, + /* -------------------------------------------------------------------------- */ + /* task:update */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'TaskList', + name: 'task', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getTasks' + }, + required: true, + displayOptions: { + show: { + operation: ['update'], + resource: ['task'] + } + }, + default: '' + }, + { + displayName: 'Task ID', + name: 'taskId', + type: 'string', + required: true, + displayOptions: { + show: { + operation: ['update'], + resource: ['task'] + } + }, + default: '' + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Update Field', + default: {}, + displayOptions: { + show: { + operation: ['update'], + resource: ['task'] + } + }, + options: [ + { + displayName: 'Status', + name: 'status', + type: 'options', + options: [ + { + name: 'needs Action', + value: 'needsAction' + }, + { + name: 'completed', + value: 'completed' + } + ], + default: '', + description: 'Current status of the task.' + }, + { + displayName: 'Links', + name: 'links', + type: 'string', + typeOptions: { + multipleValues: true, + multipleValueButtonText: 'Add Link' + }, + default: '', + description: 'The links to insert in the task.' + }, + + { + displayName: 'Notes', + name: 'notes', + type: 'string', + default: '', + description: 'Additional Notes.' + }, + { + displayName: 'Title', + name: 'title', + type: 'string', + default: '', + description: 'Title of the task.' + }, + { + displayName: 'Due Date', + name: 'dueDate', + type: 'dateTime', + default: '', + description: 'Due date of the task.' + }, + { + displayName: 'Completion date', + name: 'completed', + type: 'dateTime', + default: '', + description: `Completion date of the task (as a RFC 3339 timestamp). This field is omitted if the task has not been completed.` + }, + + { + displayName: 'Deleted status', + name: 'deleted', + type: 'boolean', + default: false, + description: 'Flag indicating whether the task has been deleted.' + }, + { + displayName: 'Hidden', + name: 'hidden', + type: 'boolean', + default: false, + description: 'Flag indicating whether the task is hidden.' + }, + { + displayName: 'Parent', + name: 'parent', + type: 'string', + default: '', + description: + 'Parent task identifier.This field is omitted if it is a top-level task.' + }, + { + displayName: 'Position', + name: 'position', + type: 'string', + default: '', + description: + 'Parent task identifier.This field is omitted if it is a top-level task.' + }, + { + displayName: 'Self Link', + name: 'selfLink', + type: 'string', + default: '', + description: + 'URL pointing to this task. Used to retrieve, update, or delete this task.' + }, + { + displayName: 'Previous', + name: 'previous', + type: 'string', + default: '', + description: + 'Previous sibling task identifier. If the task is created at the first position among its siblings, this parameter is omitted.' + } + ] + } +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Google/Task/googleTasks.png b/packages/nodes-base/nodes/Google/Task/googleTasks.png new file mode 100644 index 0000000000..f0339e5bfe Binary files /dev/null and b/packages/nodes-base/nodes/Google/Task/googleTasks.png differ diff --git a/packages/nodes-base/nodes/MessageBird/MessageBird.node.ts b/packages/nodes-base/nodes/MessageBird/MessageBird.node.ts index 9c74b6a3d9..5a7418a708 100644 --- a/packages/nodes-base/nodes/MessageBird/MessageBird.node.ts +++ b/packages/nodes-base/nodes/MessageBird/MessageBird.node.ts @@ -1,17 +1,13 @@ -import { - IExecuteFunctions, - } from 'n8n-core'; +import { IExecuteFunctions } from 'n8n-core'; import { IDataObject, INodeTypeDescription, INodeExecutionData, - INodeType, + INodeType } from 'n8n-workflow'; -import { - messageBirdApiRequest, - } from './GenericFunctions'; +import { messageBirdApiRequest } from './GenericFunctions'; export class MessageBird implements INodeType { description: INodeTypeDescription = { @@ -55,20 +51,18 @@ export class MessageBird implements INodeType { type: 'options', displayOptions: { show: { - resource: [ - 'sms', - ], - }, + resource: ['sms'] + } }, options: [ { name: 'Send', value: 'send', - description: 'Send text messages (SMS)', - }, + description: 'Send text messages (SMS)' + } ], default: 'send', - description: 'The operation to perform.', + description: 'The operation to perform.' }, // ---------------------------------- @@ -83,15 +77,11 @@ export class MessageBird implements INodeType { required: true, displayOptions: { show: { - operation: [ - 'send', - ], - resource: [ - 'sms', - ], - }, + operation: ['send'], + resource: ['sms'] + } }, - description: 'The number from which to send the message.', + description: 'The number from which to send the message.' }, { displayName: 'To', @@ -103,10 +93,10 @@ export class MessageBird implements INodeType { displayOptions: { show: { operation: ['send'], - resource: ['sms'], - }, + resource: ['sms'] + } }, - description: 'All recipients separated by commas.', + description: 'All recipients separated by commas.' }, { @@ -117,15 +107,11 @@ export class MessageBird implements INodeType { required: true, displayOptions: { show: { - operation: [ - 'send', - ], - resource: [ - 'sms', - ], - }, + operation: ['send'], + resource: ['sms'] + } }, - description: 'The message to be send.', + description: 'The message to be send.' }, { displayName: 'Additional Fields', @@ -139,7 +125,8 @@ export class MessageBird implements INodeType { name: 'createdDatetime', type: 'dateTime', default: '', - description: 'The date and time of the creation of the message in RFC3339 format (Y-m-dTH:i:sP).', + description: + 'The date and time of the creation of the message in RFC3339 format (Y-m-dTH:i:sP).' }, { displayName: 'Datacoding', @@ -148,26 +135,27 @@ export class MessageBird implements INodeType { options: [ { name: 'Auto', - value: 'auto', + value: 'auto' }, { name: 'Plain', - value: 'plain', + value: 'plain' }, { name: 'Unicode', - value: 'unicode', - }, + value: 'unicode' + } ], default: '', - description: 'Using unicode will limit the maximum number of characters to 70 instead of 160.', + description: + 'Using unicode will limit the maximum number of characters to 70 instead of 160.' }, { displayName: 'Gateway', name: 'gateway', type: 'number', default: '', - description: 'The SMS route that is used to send the message.', + description: 'The SMS route that is used to send the message.' }, { displayName: 'Group IDs', @@ -175,7 +163,8 @@ export class MessageBird implements INodeType { placeholder: '1,2', type: 'string', default: '', - description: 'Group IDs separated by commas, If provided recipients can be omitted.', + description: + 'Group IDs separated by commas, If provided recipients can be omitted.' }, { displayName: 'Message Type', @@ -185,36 +174,39 @@ export class MessageBird implements INodeType { options: [ { name: 'Flash', - value: 1, + value: 1 }, { name: 'Normal', - value: 0, - }, + value: 0 + } ], default: 1, - description: 'Indicated the message type. 1 is a normal message, 0 is a flash message.', + description: + 'Indicated the message type. 1 is a normal message, 0 is a flash message.' }, { displayName: 'Reference', name: 'reference', type: 'string', default: '', - description: 'A client reference.', + description: 'A client reference.' }, { displayName: 'Report Url', name: 'reportUrl', type: 'string', default: '', - description: 'The status report URL to be used on a per-message basis.
Reference is required for a status report webhook to be sent.', + description: + 'The status report URL to be used on a per-message basis.
Reference is required for a status report webhook to be sent.' }, { displayName: 'Scheduled Date-time', name: 'scheduledDatetime', type: 'dateTime', default: '', - description: 'The scheduled date and time of the message in RFC3339 format (Y-m-dTH:i:sP).', + description: + 'The scheduled date and time of the message in RFC3339 format (Y-m-dTH:i:sP).' }, { displayName: 'Type', @@ -232,17 +224,19 @@ export class MessageBird implements INodeType { { name: 'SMS', value: 'sms' - }, + } ], default: '', - description: 'The type of message.
Values can be: sms, binary, or flash.', + description: + 'The type of message.
Values can be: sms, binary, or flash.' }, { displayName: 'Type Details', name: 'typeDetails', type: 'string', default: '', - description: 'A hash with extra information.
Is only used when a binary message is sent.', + description: + 'A hash with extra information.
Is only used when a binary message is sent.' }, { displayName: 'Validity', @@ -250,13 +244,13 @@ export class MessageBird implements INodeType { type: 'number', default: 1, typeOptions: { - minValue: 1, + minValue: 1 }, - description: 'The amount of seconds that the message is valid.', - }, - ], - }, - ], + description: 'The amount of seconds that the message is valid.' + } + ] + } + ] }; async execute(this: IExecuteFunctions): Promise { diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 7566f70f06..dc4b084109 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -61,6 +61,7 @@ "dist/credentials/GoogleDriveOAuth2Api.credentials.js", "dist/credentials/GoogleOAuth2Api.credentials.js", "dist/credentials/GoogleSheetsOAuth2Api.credentials.js", + "dist/credentials/GoogleTasksOAuth2Api.credentials.js", "dist/credentials/GumroadApi.credentials.js", "dist/credentials/HarvestApi.credentials.js", "dist/credentials/HelpScoutOAuth2Api.credentials.js", @@ -194,6 +195,7 @@ "dist/nodes/Google/Calendar/GoogleCalendar.node.js", "dist/nodes/Google/Drive/GoogleDrive.node.js", "dist/nodes/Google/Sheet/GoogleSheets.node.js", + "dist/nodes/Google/Task/GoogleTasks.node.js", "dist/nodes/GraphQL/GraphQL.node.js", "dist/nodes/Gumroad/GumroadTrigger.node.js", "dist/nodes/Harvest/Harvest.node.js",