fix(Google Calendar Node): Mode to add or replace attendees in event update (#11132)

This commit is contained in:
Michael Kret 2024-10-10 17:03:51 +03:00 committed by GitHub
parent eaf933049e
commit 6c6a8efdea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 218 additions and 1 deletions

View file

@ -839,6 +839,58 @@ export const eventFields: INodeProperties[] = [
default: 'no',
description: 'Whether the event is all day or not',
},
{
displayName: 'Attendees',
name: 'attendeesUi',
type: 'fixedCollection',
placeholder: 'Add Attendees',
default: {
values: {
mode: 'add',
attendees: [],
},
},
options: [
{
displayName: 'Values',
name: 'values',
values: [
{
displayName: 'Mode',
name: 'mode',
type: 'options',
default: 'add',
options: [
{
name: 'Add Attendees Below [Default]',
value: 'add',
},
{
name: 'Replace Attendees with Those Below',
value: 'replace',
},
],
},
{
displayName: 'Attendees',
name: 'attendees',
type: 'string',
typeOptions: {
multipleValues: true,
multipleValueButtonText: 'Add Attendee',
},
default: '',
description: 'The attendees of the event. Multiple ones can be separated by comma.',
},
],
},
],
displayOptions: {
show: {
'@version': [{ _cnd: { gte: 1.2 } }],
},
},
},
{
displayName: 'Attendees',
name: 'attendees',
@ -849,6 +901,11 @@ export const eventFields: INodeProperties[] = [
},
default: '',
description: 'The attendees of the event. Multiple ones can be separated by comma.',
displayOptions: {
show: {
'@version': [1, 1.1],
},
},
},
{
displayName: 'Color Name or ID',

View file

@ -34,7 +34,7 @@ export class GoogleCalendar implements INodeType {
name: 'googleCalendar',
icon: 'file:googleCalendar.svg',
group: ['input'],
version: [1, 1.1],
version: [1, 1.1, 1.2],
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Consume Google Calendar API',
defaults: {
@ -502,6 +502,7 @@ export class GoogleCalendar implements INodeType {
timeZone: updateTimezone,
};
}
// nodeVersion < 1.2
if (updateFields.attendees) {
body.attendees = [];
(updateFields.attendees as string[]).forEach((attendee) => {
@ -514,6 +515,37 @@ export class GoogleCalendar implements INodeType {
);
});
}
// nodeVersion >= 1.2
if (updateFields.attendeesUi) {
const { mode, attendees } = (
updateFields.attendeesUi as {
values: {
mode: string;
attendees: string[];
};
}
).values;
body.attendees = [];
if (mode === 'add') {
const event = await googleApiRequest.call(
this,
'GET',
`/calendar/v3/calendars/${calendarId}/events/${eventId}`,
);
((event?.attendees as IDataObject[]) || []).forEach((attendee) => {
body.attendees?.push(attendee);
});
}
(attendees as string[]).forEach((attendee) => {
body.attendees!.push.apply(
body.attendees,
attendee
.split(',')
.map((a) => a.trim())
.map((email) => ({ email })),
);
});
}
if (updateFields.color) {
body.colorId = updateFields.color as string;
}

View file

@ -0,0 +1,128 @@
import type { MockProxy } from 'jest-mock-extended';
import { mock } from 'jest-mock-extended';
import type { INode, IExecuteFunctions } from 'n8n-workflow';
import { GoogleCalendar } from '../../GoogleCalendar.node';
import * as genericFunctions from '../../GenericFunctions';
jest.mock('../../GenericFunctions', () => ({
getTimezones: jest.fn(),
googleApiRequest: jest.fn(),
googleApiRequestAllItems: jest.fn(),
addTimezoneToDate: jest.fn(),
addNextOccurrence: jest.fn(),
encodeURIComponentOnce: jest.fn(),
}));
describe('RespondToWebhook Node', () => {
let googleCalendar: GoogleCalendar;
let mockExecuteFunctions: MockProxy<IExecuteFunctions>;
beforeEach(() => {
googleCalendar = new GoogleCalendar();
mockExecuteFunctions = mock<IExecuteFunctions>({
getInputData: jest.fn(),
getNode: jest.fn(),
getNodeParameter: jest.fn(),
getTimezone: jest.fn(),
helpers: {
constructExecutionMetaData: jest.fn().mockReturnValue([]),
},
});
});
afterEach(() => {
jest.clearAllMocks();
});
describe('Google Calendar > Event > Update', () => {
it('should update replace attendees in version 1.1', async () => {
mockExecuteFunctions.getInputData.mockReturnValue([{ json: {} }]);
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('event');
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('update');
mockExecuteFunctions.getTimezone.mockReturnValueOnce('Europe/Berlin');
mockExecuteFunctions.getNode.mockReturnValue(mock<INode>({ typeVersion: 1.1 }));
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('myCalendar');
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('myEvent');
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce(true);
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce({
attendees: ['email1@mail.com'],
});
await googleCalendar.execute.call(mockExecuteFunctions);
expect(genericFunctions.googleApiRequest).toHaveBeenCalledWith(
'PATCH',
'/calendar/v3/calendars/undefined/events/myEvent',
{ attendees: [{ email: 'email1@mail.com' }] },
{},
);
});
it('should update replace attendees', async () => {
mockExecuteFunctions.getInputData.mockReturnValue([{ json: {} }]);
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('event');
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('update');
mockExecuteFunctions.getTimezone.mockReturnValueOnce('Europe/Berlin');
mockExecuteFunctions.getNode.mockReturnValue(mock<INode>({ typeVersion: 1.2 }));
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('myCalendar');
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('myEvent');
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce(true);
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce({
attendeesUi: {
values: {
mode: 'replace',
attendees: ['email1@mail.com'],
},
},
});
await googleCalendar.execute.call(mockExecuteFunctions);
expect(genericFunctions.googleApiRequest).toHaveBeenCalledWith(
'PATCH',
'/calendar/v3/calendars/undefined/events/myEvent',
{ attendees: [{ email: 'email1@mail.com' }] },
{},
);
});
it('should update add attendees', async () => {
mockExecuteFunctions.getInputData.mockReturnValue([{ json: {} }]);
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('event');
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('update');
mockExecuteFunctions.getTimezone.mockReturnValueOnce('Europe/Berlin');
mockExecuteFunctions.getNode.mockReturnValue(mock<INode>({ typeVersion: 1.2 }));
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('myCalendar');
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('myEvent');
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce(true);
mockExecuteFunctions.getNodeParameter.mockReturnValueOnce({
attendeesUi: {
values: {
mode: 'add',
attendees: ['email1@mail.com'],
},
},
});
(genericFunctions.googleApiRequest as jest.Mock).mockResolvedValueOnce({
attendees: [{ email: 'email2@mail.com' }],
});
await googleCalendar.execute.call(mockExecuteFunctions);
expect(genericFunctions.googleApiRequest).toHaveBeenCalledTimes(2);
expect(genericFunctions.googleApiRequest).toHaveBeenCalledWith(
'GET',
'/calendar/v3/calendars/undefined/events/myEvent',
);
expect(genericFunctions.googleApiRequest).toHaveBeenCalledWith(
'PATCH',
'/calendar/v3/calendars/undefined/events/myEvent',
{ attendees: [{ email: 'email2@mail.com' }, { email: 'email1@mail.com' }] },
{},
);
});
});
});