This commit is contained in:
Elias Meire 2025-03-05 11:22:59 +01:00 committed by GitHub
commit b38b024b57
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 3263 additions and 62 deletions

View file

@ -9,6 +9,7 @@ import type {
ILoadOptionsFunctions, ILoadOptionsFunctions,
INode, INode,
INodeExecutionData, INodeExecutionData,
INodePropertyOptions,
IPollFunctions, IPollFunctions,
IRequestOptions, IRequestOptions,
JsonObject, JsonObject,
@ -637,3 +638,34 @@ export async function simplifyOutput(
return item; return item;
}); });
} }
/**
* Get all the labels to display them to user so that they can select them easily
*/
export async function getLabels(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
const labels = await googleApiRequestAllItems.call(
this,
'labels',
'GET',
'/gmail/v1/users/me/labels',
);
for (const label of labels) {
returnData.push({
name: label.name,
value: label.id,
});
}
return returnData.sort((a, b) => {
if (a.name < b.name) {
return -1;
}
if (a.name > b.name) {
return 1;
}
return 0;
});
}

View file

@ -0,0 +1,347 @@
/* eslint-disable n8n-nodes-base/node-param-display-name-miscased */
import { mock, mockDeep } from 'jest-mock-extended';
import { jsonParse, type ILoadOptionsFunctions, type INode } from 'n8n-workflow';
import nock from 'nock';
import { testWorkflows } from '@test/nodes/Helpers';
import { getLabels } from '../../v1/loadOptions';
import labels from '../fixtures/labels.json';
import messages from '../fixtures/messages.json';
describe('Test Gmail Node v1', () => {
beforeAll(() => {
jest
.useFakeTimers({ doNotFake: ['setImmediate', 'nextTick'] })
.setSystemTime(new Date('2024-12-16 12:34:56.789Z'));
nock.disableNetConnect();
});
afterAll(() => {
nock.restore();
jest.resetAllMocks();
});
describe('Messages', () => {
const gmailNock = nock('https://www.googleapis.com/gmail');
beforeAll(() => {
gmailNock.get('/v1/users/me/messages').query({ maxResults: 2 }).reply(200, {
messages,
});
gmailNock
.get('/v1/users/me/messages')
.query({
includeSpamTrash: 'true',
labelIds: 'CHAT',
q: 'test from:Test Sender after:1734393600 before:1735171200',
readStatus: 'both',
dataPropertyAttachmentsPrefixName: 'attachment_',
downloadAttachments: 'true',
maxResults: '2',
})
.reply(200, { messages });
gmailNock
.get('/v1/users/me/messages/a1b2c3d4e5f6g7h8')
.query({
maxResults: '2',
format: 'metadata',
metadataHeaders: ['From', 'To', 'Cc', 'Bcc', 'Subject'],
})
.reply(200, messages[0]);
gmailNock
.get('/v1/users/me/messages/a1b2c3d4e5f6g7h8')
.query({
includeSpamTrash: 'true',
labelIds: 'CHAT',
q: 'test from:Test Sender after:1734393600 before:1735171200',
readStatus: 'both',
dataPropertyAttachmentsPrefixName: 'attachment_',
downloadAttachments: 'true',
maxResults: '2',
format: 'raw',
})
.reply(200, {
...messages[0],
raw: 'TUlNRS1WZXJzaW9uOiAxLjANCkRhdGU6IEZyaSwgMTMgRGVjIDIwMjQgMTE6MTU6MDEgKzAxMDANCk1lc3NhZ2UtSUQ6IDxDQUVHQVByb3d1ZEduS1h4cXJoTWpPdXhhbVRoN3lBcmp3UDdPRDlVQnEtSnBrYjBYOXdAbWFpbC5nbWFpbC5jb20-DQpTdWJqZWN0OiBUZXN0IGRyYWZ0DQpGcm9tOiBub2RlIHFhIDxub2RlOHFhQGdtYWlsLmNvbT4NClRvOiB0ZXN0QGdtYWlsLmNvbQ0KQ29udGVudC1UeXBlOiBtdWx0aXBhcnQvYWx0ZXJuYXRpdmU7IGJvdW5kYXJ5PSIwMDAwMDAwMDAwMDA5ZDU4YjYwNjI5MjQxYTIyIg0KDQotLTAwMDAwMDAwMDAwMDlkNThiNjA2MjkyNDFhMjINCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgY2hhcnNldD0iVVRGLTgiDQoNCmRyYWZ0IGJvZHkNCg0KLS0wMDAwMDAwMDAwMDA5ZDU4YjYwNjI5MjQxYTIyDQpDb250ZW50LVR5cGU6IHRleHQvaHRtbDsgY2hhcnNldD0iVVRGLTgiDQoNCjxkaXYgZGlyPSJsdHIiPmRyYWZ0IGJvZHk8YnI-PC9kaXY-DQoNCi0tMDAwMDAwMDAwMDAwOWQ1OGI2MDYyOTI0MWEyMi0t',
});
gmailNock
.get('/v1/users/me/messages/z9y8x7w6v5u4t3s2')
.query({
includeSpamTrash: 'true',
labelIds: 'CHAT',
q: 'test from:Test Sender after:1734393600 before:1735171200',
readStatus: 'both',
dataPropertyAttachmentsPrefixName: 'attachment_',
downloadAttachments: 'true',
maxResults: '2',
format: 'raw',
})
.reply(200, {
...messages[1],
raw: 'TUlNRS1WZXJzaW9uOiAxLjANCkRhdGU6IEZyaSwgMTMgRGVjIDIwMjQgMTE6MTU6MDEgKzAxMDANCk1lc3NhZ2UtSUQ6IDxDQUVHQVByb3d1ZEduS1h4cXJoTWpPdXhhbVRoN3lBcmp3UDdPRDlVQnEtSnBrYjBYOXdAbWFpbC5nbWFpbC5jb20-DQpTdWJqZWN0OiBUZXN0IGRyYWZ0DQpGcm9tOiBub2RlIHFhIDxub2RlOHFhQGdtYWlsLmNvbT4NClRvOiB0ZXN0QGdtYWlsLmNvbQ0KQ29udGVudC1UeXBlOiBtdWx0aXBhcnQvYWx0ZXJuYXRpdmU7IGJvdW5kYXJ5PSIwMDAwMDAwMDAwMDA5ZDU4YjYwNjI5MjQxYTIyIg0KDQotLTAwMDAwMDAwMDAwMDlkNThiNjA2MjkyNDFhMjINCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgY2hhcnNldD0iVVRGLTgiDQoNCmRyYWZ0IGJvZHkNCg0KLS0wMDAwMDAwMDAwMDA5ZDU4YjYwNjI5MjQxYTIyDQpDb250ZW50LVR5cGU6IHRleHQvaHRtbDsgY2hhcnNldD0iVVRGLTgiDQoNCjxkaXYgZGlyPSJsdHIiPmRyYWZ0IGJvZHk8YnI-PC9kaXY-DQoNCi0tMDAwMDAwMDAwMDAwOWQ1OGI2MDYyOTI0MWEyMi0t',
});
gmailNock
.get('/v1/users/me/messages/z9y8x7w6v5u4t3s2')
.query({
maxResults: '2',
format: 'metadata',
metadataHeaders: ['From', 'To', 'Cc', 'Bcc', 'Subject'],
})
.reply(200, messages[0]);
gmailNock.get('/v1/users/me/labels').reply(200, {
labels,
});
gmailNock.get('/v1/users/me/profile').times(2).reply(200, { emailAddress: 'test@n8n.io' });
gmailNock
.post('/v1/users/me/messages/send')
.query({ format: 'metadata' })
.reply(200, messages[0]);
gmailNock.post('/v1/users/me/messages/send').reply(200, messages[0]);
gmailNock
.post('/v1/users/me/messages/send')
.query({ userId: 'me', uploadType: 'media' })
.reply(200, messages[0]);
gmailNock
.post('/v1/users/me/messages/test/modify', (body) => 'addLabelIds' in body)
.reply(200, messages[0]);
gmailNock
.post('/v1/users/me/messages/test/modify', (body) => 'removeLabelIds' in body)
.reply(200, messages[0]);
gmailNock.delete('/v1/users/me/messages/test').reply(200, messages[0]);
gmailNock
.get('/v1/users/me/messages/test')
.query({
format: 'metadata',
metadataHeaders: ['From', 'To', 'Cc', 'Bcc', 'Subject'],
})
.reply(200, messages[0]);
gmailNock.get('/v1/users/me/labels').reply(200, { labels });
gmailNock
.get('/v1/users/me/messages/test')
.query({ format: 'raw' })
.reply(200, { raw: 'test email content' });
gmailNock
.post('/v1/users/me/messages/test/modify', { removeLabelIds: ['UNREAD'] })
.reply(200, messages[0]);
gmailNock
.post('/v1/users/me/messages/test/modify', { addLabelIds: ['UNREAD'] })
.reply(200, messages[0]);
gmailNock
.get('/v1/users/me/messages/test')
.query({
format: 'metadata',
})
.reply(200, messages[0]);
});
testWorkflows(['nodes/Google/Gmail/test/v2/messages.workflow.json']);
it('should make the correct network calls', () => {
gmailNock.done();
});
});
describe('Labels', () => {
const gmailNock = nock('https://www.googleapis.com/gmail');
beforeAll(() => {
gmailNock
.post('/v1/users/me/labels', {
labelListVisibility: 'labelShow',
messageListVisibility: 'show',
name: 'Test Label Name',
})
.reply(200, labels[0]);
gmailNock.delete('/v1/users/me/labels/test-label-id').reply(200, labels[0]);
gmailNock.get('/v1/users/me/labels/test-label-id').reply(200, labels[0]);
gmailNock.get('/v1/users/me/labels').reply(200, {
labels,
});
});
testWorkflows(['nodes/Google/Gmail/test/v2/labels.workflow.json']);
it('should make the correct network calls', () => {
gmailNock.done();
});
});
describe('Drafts', () => {
const gmailNock = nock('https://www.googleapis.com/gmail');
beforeAll(() => {
gmailNock
.filteringRequestBody((body) => {
try {
const parsedBody = jsonParse<{ message: { raw: string; threadId: string } }>(body);
const mail = Buffer.from(parsedBody.message.raw, 'base64').toString('utf-8');
// Remove dynamic fields from mail
parsedBody.message.raw = Buffer.from(
mail
.replace(/boundary=".*"/g, 'boundary="--test-boundary"')
.replace(/----.*/g, '----test-boundary')
.replace(/Message-ID:.*/g, 'Message-ID: test-message-id'),
'utf-8',
).toString('base64');
return JSON.stringify(parsedBody);
} catch (error) {
return body;
}
})
.post('/v1/users/me/drafts', {
message: {
raw: 'Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PSItLXRlc3QtYm91bmRhcnkiDQpGcm9tOiB0ZXN0LWFsaWFzQG44bi5pbw0KVG86IHRlc3QtdG9AbjhuLmlvDQpDYzogdGVzdC1jY0BuOG4uaW8NCkJjYzogdGVzdC1iY2NAbjhuLmlvDQpSZXBseS1UbzogdGVzdC1yZXBseUBuOG4uaW8NClN1YmplY3Q6IFRlc3QgRHJhZnQgU3ViamVjdA0KTWVzc2FnZS1JRDogdGVzdC1tZXNzYWdlLWlkDQpEYXRlOiBNb24sIDE2IERlYyAyMDI0IDEyOjM0OjU2ICswMDAwDQpNSU1FLVZlcnNpb246IDEuMA0KDQotLS0tdGVzdC1ib3VuZGFyeQ0KQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04DQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiA3Yml0DQoNClRlc3QgRHJhZnQgTWVzc2FnZQ0KLS0tLXRlc3QtYm91bmRhcnkNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vanNvbjsgbmFtZT1maWxlLmpzb24NCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJhc2U2NA0KQ29udGVudC1EaXNwb3NpdGlvbjogYXR0YWNobWVudDsgZmlsZW5hbWU9ZmlsZS5qc29uDQoNClczc2lZbWx1WVhKNUlqcDBjblZsZlYwPQ0KLS0tLXRlc3QtYm91bmRhcnkNCg==',
threadId: 'test-thread-id',
},
})
.query({ userId: 'me', uploadType: 'media' })
.reply(200, messages[0]);
gmailNock.delete('/v1/users/me/drafts/test-draft-id').reply(200, messages[0]);
gmailNock
.get('/v1/users/me/drafts/test-draft-id')
.query({ format: 'raw' })
.reply(200, {
message: {
...messages[0],
raw: 'TUlNRS1WZXJzaW9uOiAxLjANCkRhdGU6IEZyaSwgMTMgRGVjIDIwMjQgMTE6MTU6MDEgKzAxMDANCk1lc3NhZ2UtSUQ6IDxDQUVHQVByb3d1ZEduS1h4cXJoTWpPdXhhbVRoN3lBcmp3UDdPRDlVQnEtSnBrYjBYOXdAbWFpbC5nbWFpbC5jb20-DQpTdWJqZWN0OiBUZXN0IGRyYWZ0DQpGcm9tOiBub2RlIHFhIDxub2RlOHFhQGdtYWlsLmNvbT4NClRvOiB0ZXN0QGdtYWlsLmNvbQ0KQ29udGVudC1UeXBlOiBtdWx0aXBhcnQvYWx0ZXJuYXRpdmU7IGJvdW5kYXJ5PSIwMDAwMDAwMDAwMDA5ZDU4YjYwNjI5MjQxYTIyIg0KDQotLTAwMDAwMDAwMDAwMDlkNThiNjA2MjkyNDFhMjINCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgY2hhcnNldD0iVVRGLTgiDQoNCmRyYWZ0IGJvZHkNCg0KLS0wMDAwMDAwMDAwMDA5ZDU4YjYwNjI5MjQxYTIyDQpDb250ZW50LVR5cGU6IHRleHQvaHRtbDsgY2hhcnNldD0iVVRGLTgiDQoNCjxkaXYgZGlyPSJsdHIiPmRyYWZ0IGJvZHk8YnI-PC9kaXY-DQoNCi0tMDAwMDAwMDAwMDAwOWQ1OGI2MDYyOTI0MWEyMi0t',
},
});
gmailNock
.get('/v1/users/me/drafts')
.query({
dataPropertyAttachmentsPrefixName: 'attachment_',
downloadAttachments: true,
includeSpamTrash: true,
maxResults: 100,
})
.reply(200, { drafts: messages });
gmailNock
.get('/v1/users/me/drafts/a1b2c3d4e5f6g7h8')
.query({
dataPropertyAttachmentsPrefixName: 'attachment_',
downloadAttachments: true,
includeSpamTrash: true,
maxResults: 100,
format: 'raw',
})
.reply(200, {
message: {
...messages[0],
raw: 'TUlNRS1WZXJzaW9uOiAxLjANCkRhdGU6IEZyaSwgMTMgRGVjIDIwMjQgMTE6MTU6MDEgKzAxMDANCk1lc3NhZ2UtSUQ6IDxDQUVHQVByb3d1ZEduS1h4cXJoTWpPdXhhbVRoN3lBcmp3UDdPRDlVQnEtSnBrYjBYOXdAbWFpbC5nbWFpbC5jb20-DQpTdWJqZWN0OiBUZXN0IGRyYWZ0DQpGcm9tOiBub2RlIHFhIDxub2RlOHFhQGdtYWlsLmNvbT4NClRvOiB0ZXN0QGdtYWlsLmNvbQ0KQ29udGVudC1UeXBlOiBtdWx0aXBhcnQvYWx0ZXJuYXRpdmU7IGJvdW5kYXJ5PSIwMDAwMDAwMDAwMDA5ZDU4YjYwNjI5MjQxYTIyIg0KDQotLTAwMDAwMDAwMDAwMDlkNThiNjA2MjkyNDFhMjINCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgY2hhcnNldD0iVVRGLTgiDQoNCmRyYWZ0IGJvZHkNCg0KLS0wMDAwMDAwMDAwMDA5ZDU4YjYwNjI5MjQxYTIyDQpDb250ZW50LVR5cGU6IHRleHQvaHRtbDsgY2hhcnNldD0iVVRGLTgiDQoNCjxkaXYgZGlyPSJsdHIiPmRyYWZ0IGJvZHk8YnI-PC9kaXY-DQoNCi0tMDAwMDAwMDAwMDAwOWQ1OGI2MDYyOTI0MWEyMi0t',
},
});
gmailNock
.get('/v1/users/me/drafts/z9y8x7w6v5u4t3s2')
.query({
dataPropertyAttachmentsPrefixName: 'attachment_',
downloadAttachments: true,
includeSpamTrash: true,
maxResults: 100,
format: 'raw',
})
.reply(200, {
message: {
...messages[1],
raw: 'TUlNRS1WZXJzaW9uOiAxLjANCkRhdGU6IEZyaSwgMTMgRGVjIDIwMjQgMTE6MTU6MDEgKzAxMDANCk1lc3NhZ2UtSUQ6IDxDQUVHQVByb3d1ZEduS1h4cXJoTWpPdXhhbVRoN3lBcmp3UDdPRDlVQnEtSnBrYjBYOXdAbWFpbC5nbWFpbC5jb20-DQpTdWJqZWN0OiBUZXN0IGRyYWZ0DQpGcm9tOiBub2RlIHFhIDxub2RlOHFhQGdtYWlsLmNvbT4NClRvOiB0ZXN0QGdtYWlsLmNvbQ0KQ29udGVudC1UeXBlOiBtdWx0aXBhcnQvYWx0ZXJuYXRpdmU7IGJvdW5kYXJ5PSIwMDAwMDAwMDAwMDA5ZDU4YjYwNjI5MjQxYTIyIg0KDQotLTAwMDAwMDAwMDAwMDlkNThiNjA2MjkyNDFhMjINCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgY2hhcnNldD0iVVRGLTgiDQoNCmRyYWZ0IGJvZHkNCg0KLS0wMDAwMDAwMDAwMDA5ZDU4YjYwNjI5MjQxYTIyDQpDb250ZW50LVR5cGU6IHRleHQvaHRtbDsgY2hhcnNldD0iVVRGLTgiDQoNCjxkaXYgZGlyPSJsdHIiPmRyYWZ0IGJvZHk8YnI-PC9kaXY-DQoNCi0tMDAwMDAwMDAwMDAwOWQ1OGI2MDYyOTI0MWEyMi0t',
},
});
});
testWorkflows(['nodes/Google/Gmail/test/v2/drafts.workflow.json']);
it('should make the correct network calls', () => {
gmailNock.done();
});
});
describe('Threads', () => {
const gmailNock = nock('https://www.googleapis.com/gmail');
beforeAll(() => {
gmailNock.get('/v1/users/me/threads').query({ maxResults: 2 }).reply(200, {
threads: messages,
});
gmailNock
.get('/v1/users/me/threads')
.query((query) => {
return (
query.includeSpamTrash === 'true' &&
query.labelIds === 'CHAT' &&
!!query.q &&
query.q.includes('has:attachment') &&
query.q.includes('before:') &&
query.q.includes('after:')
);
})
.reply(200, { threads: messages });
gmailNock
.post('/v1/users/me/threads/test-thread-id/modify', { addLabelIds: ['CHAT'] })
.reply(200, messages[0]);
gmailNock
.post('/v1/users/me/threads/test-thread-id/modify', { removeLabelIds: ['CHAT'] })
.reply(200, messages[0]);
gmailNock.delete('/v1/users/me/threads/test-thread-id').reply(200, messages[0]);
gmailNock
.get('/v1/users/me/threads/test-thread-id')
.query({
format: 'metadata',
metadataHeaders: ['From', 'To', 'Cc', 'Bcc', 'Subject'],
})
.reply(200, messages[0]);
gmailNock.get('/v1/users/me/labels').times(2).reply(200, { labels });
gmailNock
.get('/v1/users/me/threads/test-thread-id')
.query({ format: 'full' })
.reply(200, messages[0]);
gmailNock.post('/v1/users/me/threads/test-thread-id/trash').reply(200, messages[0]);
gmailNock.post('/v1/users/me/threads/test-thread-id/untrash').reply(200, messages[0]);
gmailNock
.get('/v1/users/me/messages/test%20snippet')
.query({
userId: 'me',
uploadType: 'media',
format: 'metadata',
})
.reply(200, messages[0]);
gmailNock.get('/v1/users/me/profile').reply(200, { emailAddress: 'test@n8n.io' });
gmailNock
.post('/v1/users/me/messages/send')
.query({ userId: 'me', uploadType: 'media', format: 'metadata' })
.reply(200, messages[0]);
});
testWorkflows(['nodes/Google/Gmail/test/v2/threads.workflow.json']);
it('should make the correct network calls', () => {
gmailNock.done();
});
});
describe('loadOptions', () => {
describe('getLabels', () => {
it('should return a list of Gmail labels', async () => {
const loadOptionsFunctions = mockDeep<ILoadOptionsFunctions>({
getNode: jest.fn(() => mock<INode>()),
helpers: mock<ILoadOptionsFunctions['helpers']>({
requestWithAuthentication: jest
.fn()
// 2 pages of labels
.mockImplementationOnce(async () => ({ labels, nextPageToken: 'nextPageToken' }))
.mockImplementationOnce(async () => ({ labels })),
}),
});
expect(await getLabels.call(loadOptionsFunctions)).toEqual([
{ name: 'CHAT', value: 'CHAT' },
{ name: 'CHAT', value: 'CHAT' },
{ name: 'SENT', value: 'SENT' },
{ name: 'SENT', value: 'SENT' },
]);
});
});
});
});

View file

@ -0,0 +1,386 @@
{
"name": "My workflow 130",
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [-60, 100],
"id": "636b40bc-2c98-4b9a-8ce2-9d1322294518",
"name": "When clicking Test workflow"
},
{
"parameters": {
"resource": "draft",
"operation": "get",
"messageId": "test-draft-id",
"options": {}
},
"type": "n8n-nodes-base.gmail",
"typeVersion": 1,
"position": [440, 200],
"id": "8802fdb5-2741-407b-82a4-ccedc4055076",
"name": "Gmail - Drafts - Get",
"webhookId": "3b8b38e0-2f4b-40bc-8b67-37e7ea95cb60",
"credentials": {
"gmailOAuth2": {
"id": "22",
"name": "Gmail 0auth"
}
}
},
{
"parameters": {
"resource": "draft",
"operation": "delete",
"messageId": "test-draft-id"
},
"type": "n8n-nodes-base.gmail",
"typeVersion": 1,
"position": [440, 20],
"id": "ed979c3a-b2ea-413e-be63-0392cc1714a5",
"name": "Gmail - Drafts - Delete",
"webhookId": "3b8b38e0-2f4b-40bc-8b67-37e7ea95cb60",
"credentials": {
"gmailOAuth2": {
"id": "22",
"name": "Gmail 0auth"
}
}
},
{
"parameters": {
"resource": "draft",
"subject": "Test Draft Subject",
"message": "Test Draft Message",
"options": {
"attachmentsUi": {
"attachmentsBinary": [
{
"property": "data"
}
]
},
"bccList": "test-bcc@n8n.io",
"ccList": "test-cc@n8n.io",
"fromAlias": "=test-alias@n8n.io",
"replyTo": "test-reply@n8n.io",
"threadId": "test-thread-id",
"sendTo": "test-to@n8n.io"
}
},
"type": "n8n-nodes-base.gmail",
"typeVersion": 1,
"position": [840, -180],
"id": "45758452-3b5b-478d-aece-001e117ce69d",
"name": "Gmail - Drafts - Create",
"webhookId": "3b8b38e0-2f4b-40bc-8b67-37e7ea95cb60",
"credentials": {
"gmailOAuth2": {
"id": "22",
"name": "Gmail 0auth"
}
}
},
{
"parameters": {
"resource": "draft",
"operation": "getAll",
"returnAll": true,
"options": {
"dataPropertyAttachmentsPrefixName": "attachment_",
"downloadAttachments": true,
"includeSpamTrash": true
}
},
"type": "n8n-nodes-base.gmail",
"typeVersion": 1,
"position": [440, 400],
"id": "bae81586-7641-4fdc-81a4-0006b289bf9d",
"name": "Gmail - Drafts - Get Many",
"webhookId": "3b8b38e0-2f4b-40bc-8b67-37e7ea95cb60",
"credentials": {
"gmailOAuth2": {
"id": "22",
"name": "Gmail 0auth"
}
}
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "491590a8-27a6-4d14-b342-493947775d16",
"name": "binary",
"value": true,
"type": "boolean"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [420, -180],
"id": "d630c018-1d7b-4779-a280-9f4a21c6a764",
"name": "Edit Fields"
},
{
"parameters": {
"operation": "toJson",
"options": {}
},
"type": "n8n-nodes-base.convertToFile",
"typeVersion": 1.1,
"position": [640, -180],
"id": "fc3bdb76-c278-44f6-9aac-153b79c8177b",
"name": "Convert to File"
}
],
"pinData": {
"Gmail - Drafts - Create": [
{
"json": {
"id": "a1b2c3d4e5f6g7h8",
"threadId": "a1b2c3d4e5f6g7h8",
"labelIds": ["UNREAD", "CATEGORY_PROMOTIONS", "INBOX"],
"snippet": "Don't miss our exclusive holiday discounts on all items! Act now before the sale ends.",
"payload": {
"partId": "",
"mimeType": "multipart/alternative",
"filename": "",
"headers": [
{ "name": "Delivered-To", "value": "exampleuser@gmail.com" },
{
"name": "Received",
"value": "by 2001:db8::abcd with SMTP id xyz123abc456; Thu, 5 Dec 2024 08:30:00 -0800 (PST)"
},
{ "name": "X-Google-Smtp-Source", "value": "ABC12345+EXAMPLE123456789" },
{
"name": "X-Received",
"value": "by 192.0.2.1 with SMTP id 12345abc67890; Thu, 5 Dec 2024 08:30:00 -0800 (PST)"
},
{
"name": "ARC-Seal",
"value": "i=1; a=rsa-sha256; t=1733405400; cv=none; d=example.com; s=arc-20241205; b=ABCDEFG123456="
},
{
"name": "ARC-Message-Signature",
"value": "i=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; s=arc-20241205; bh=EXAMPLEHASH12345="
},
{
"name": "ARC-Authentication-Results",
"value": "i=1; mx.example.com; dkim=pass header.i=@promotion.example.com; spf=pass smtp.mailfrom=promo@promotion.example.com; dmarc=pass header.from=example.com"
},
{ "name": "Return-Path", "value": "<promo@promotion.example.com>" },
{ "name": "Date", "value": "Thu, 5 Dec 2024 08:30:00 -0800" },
{ "name": "From", "value": "Holiday Deals <promo@promotion.example.com>" },
{ "name": "To", "value": "exampleuser@gmail.com" },
{ "name": "Message-ID", "value": "<12345abc67890@promotion.example.com>" },
{ "name": "Subject", "value": "Exclusive Holiday Discounts!" },
{ "name": "MIME-Version", "value": "1.0" },
{
"name": "Content-Type",
"value": "multipart/alternative; boundary=\"----=_Part_12345_67890.1733405400000\""
}
],
"body": { "size": 0 },
"parts": [
{
"partId": "0",
"mimeType": "text/plain",
"filename": "",
"headers": [
{ "name": "Content-Type", "value": "text/plain; charset=utf-8" },
{ "name": "Content-Transfer-Encoding", "value": "quoted-printable" }
],
"body": {
"size": 1234,
"data": "VGhpcyBpcyBhbiBleGFtcGxlIG1lc3NhZ2UuIFRoYW5rIHlvdSBmb3Igc2hvcHBpbmcgd2l0aCB1cy4="
}
},
{
"partId": "1",
"mimeType": "text/html",
"filename": "",
"headers": [
{ "name": "Content-Type", "value": "text/html; charset=\"utf-8\"" },
{ "name": "Content-Transfer-Encoding", "value": "quoted-printable" }
],
"body": {
"size": 5678,
"data": "PGRpdiBzdHlsZT0nZm9udC1mYW1pbHk6IEFyaWFsLCBzYW5zLXNlcmlmOyc+VGhpcyBpcyBhbiBleGFtcGxlIGh0bWwgbWVzc2FnZS4gPGI+VGhhbmsgeW91IGZvciBzaG9wcGluZyB3aXRoIHVzLjwvYj48L2Rpdj4="
}
}
]
},
"sizeEstimate": 67890,
"historyId": "54321",
"internalDate": "1733405400000"
}
}
],
"Gmail - Drafts - Delete": [{ "json": { "success": true } }],
"Gmail - Drafts - Get": [
{
"json": {
"threadId": "a1b2c3d4e5f6g7h8",
"labelIds": ["UNREAD", "CATEGORY_PROMOTIONS", "INBOX"],
"sizeEstimate": 67890,
"headers": {
"mime-version": "MIME-Version: 1.0",
"date": "Date: Fri, 13 Dec 2024 11:15:01 +0100",
"message-id": "Message-ID: <CAEGAProwudGnKXxqrhMjOuxamTh7yArjwP7OD9UBq-Jpkb0X9w@mail.gmail.com>",
"subject": "Subject: Test draft",
"from": "From: node qa <node8qa@gmail.com>",
"to": "To: test@gmail.com",
"content-type": "Content-Type: multipart/alternative; boundary=\"0000000000009d58b60629241a22\""
},
"html": "<div dir=\"ltr\">draft body<br></div>\n",
"text": "draft body\n",
"textAsHtml": "<p>draft body</p>",
"subject": "Test draft",
"date": "2024-12-13T10:15:01.000Z",
"to": {
"value": [{ "address": "test@gmail.com", "name": "" }],
"html": "<span class=\"mp_address_group\"><a href=\"mailto:test@gmail.com\" class=\"mp_address_email\">test@gmail.com</a></span>",
"text": "test@gmail.com"
},
"from": {
"value": [{ "address": "node8qa@gmail.com", "name": "node qa" }],
"html": "<span class=\"mp_address_group\"><span class=\"mp_address_name\">node qa</span> &lt;<a href=\"mailto:node8qa@gmail.com\" class=\"mp_address_email\">node8qa@gmail.com</a>&gt;</span>",
"text": "\"node qa\" <node8qa@gmail.com>"
},
"messageId": "a1b2c3d4e5f6g7h8"
}
}
],
"Gmail - Drafts - Get Many": [
{
"json": {
"threadId": "a1b2c3d4e5f6g7h8",
"labelIds": ["UNREAD", "CATEGORY_PROMOTIONS", "INBOX"],
"sizeEstimate": 67890,
"headers": {
"mime-version": "MIME-Version: 1.0",
"date": "Date: Fri, 13 Dec 2024 11:15:01 +0100",
"message-id": "Message-ID: <CAEGAProwudGnKXxqrhMjOuxamTh7yArjwP7OD9UBq-Jpkb0X9w@mail.gmail.com>",
"subject": "Subject: Test draft",
"from": "From: node qa <node8qa@gmail.com>",
"to": "To: test@gmail.com",
"content-type": "Content-Type: multipart/alternative; boundary=\"0000000000009d58b60629241a22\""
},
"html": "<div dir=\"ltr\">draft body<br></div>\n",
"text": "draft body\n",
"textAsHtml": "<p>draft body</p>",
"subject": "Test draft",
"date": "2024-12-13T10:15:01.000Z",
"to": {
"value": [{ "address": "test@gmail.com", "name": "" }],
"html": "<span class=\"mp_address_group\"><a href=\"mailto:test@gmail.com\" class=\"mp_address_email\">test@gmail.com</a></span>",
"text": "test@gmail.com"
},
"from": {
"value": [{ "address": "node8qa@gmail.com", "name": "node qa" }],
"html": "<span class=\"mp_address_group\"><span class=\"mp_address_name\">node qa</span> &lt;<a href=\"mailto:node8qa@gmail.com\" class=\"mp_address_email\">node8qa@gmail.com</a>&gt;</span>",
"text": "\"node qa\" <node8qa@gmail.com>"
},
"messageId": "a1b2c3d4e5f6g7h8"
}
},
{
"json": {
"threadId": "z9y8x7w6v5u4t3s2",
"labelIds": ["UNREAD", "CATEGORY_SOCIAL", "INBOX"],
"sizeEstimate": 54321,
"headers": {
"mime-version": "MIME-Version: 1.0",
"date": "Date: Fri, 13 Dec 2024 11:15:01 +0100",
"message-id": "Message-ID: <CAEGAProwudGnKXxqrhMjOuxamTh7yArjwP7OD9UBq-Jpkb0X9w@mail.gmail.com>",
"subject": "Subject: Test draft",
"from": "From: node qa <node8qa@gmail.com>",
"to": "To: test@gmail.com",
"content-type": "Content-Type: multipart/alternative; boundary=\"0000000000009d58b60629241a22\""
},
"html": "<div dir=\"ltr\">draft body<br></div>\n",
"text": "draft body\n",
"textAsHtml": "<p>draft body</p>",
"subject": "Test draft",
"date": "2024-12-13T10:15:01.000Z",
"to": {
"value": [{ "address": "test@gmail.com", "name": "" }],
"html": "<span class=\"mp_address_group\"><a href=\"mailto:test@gmail.com\" class=\"mp_address_email\">test@gmail.com</a></span>",
"text": "test@gmail.com"
},
"from": {
"value": [{ "address": "node8qa@gmail.com", "name": "node qa" }],
"html": "<span class=\"mp_address_group\"><span class=\"mp_address_name\">node qa</span> &lt;<a href=\"mailto:node8qa@gmail.com\" class=\"mp_address_email\">node8qa@gmail.com</a>&gt;</span>",
"text": "\"node qa\" <node8qa@gmail.com>"
},
"messageId": "z9y8x7w6v5u4t3s2"
}
}
]
},
"connections": {
"When clicking Test workflow": {
"main": [
[
{
"node": "Gmail - Drafts - Get Many",
"type": "main",
"index": 0
},
{
"node": "Gmail - Drafts - Get",
"type": "main",
"index": 0
},
{
"node": "Gmail - Drafts - Delete",
"type": "main",
"index": 0
},
{
"node": "Edit Fields",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields": {
"main": [
[
{
"node": "Convert to File",
"type": "main",
"index": 0
}
]
]
},
"Convert to File": {
"main": [
[
{
"node": "Gmail - Drafts - Create",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "84fb11a4-4166-45bd-bd9f-60fa378d9e68",
"meta": {
"instanceId": "27cc9b56542ad45b38725555722c50a1c3fee1670bbb67980558314ee08517c4"
},
"id": "09KDcfGmfDrLInDE",
"tags": []
}

View file

@ -0,0 +1,163 @@
{
"name": "My workflow 130",
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [-60, 100],
"id": "636b40bc-2c98-4b9a-8ce2-9d1322294518",
"name": "When clicking Test workflow"
},
{
"parameters": {
"resource": "label",
"operation": "create",
"name": "Test Label Name",
"options": {}
},
"type": "n8n-nodes-base.gmail",
"typeVersion": 1,
"position": [220, -180],
"id": "45758452-3b5b-478d-aece-001e117ce69d",
"name": "Gmail - Labels - Create",
"webhookId": "3b8b38e0-2f4b-40bc-8b67-37e7ea95cb60",
"credentials": {
"gmailOAuth2": {
"id": "22",
"name": "Gmail 0auth"
}
}
},
{
"parameters": {
"resource": "label",
"operation": "delete",
"labelId": "test-label-id"
},
"type": "n8n-nodes-base.gmail",
"typeVersion": 1,
"position": [220, 20],
"id": "ed979c3a-b2ea-413e-be63-0392cc1714a5",
"name": "Gmail - Labels - Delete",
"webhookId": "3b8b38e0-2f4b-40bc-8b67-37e7ea95cb60",
"credentials": {
"gmailOAuth2": {
"id": "22",
"name": "Gmail 0auth"
}
}
},
{
"parameters": {
"resource": "label",
"operation": "get",
"labelId": "test-label-id"
},
"type": "n8n-nodes-base.gmail",
"typeVersion": 1,
"position": [220, 200],
"id": "8802fdb5-2741-407b-82a4-ccedc4055076",
"name": "Gmail - Labels - Get",
"webhookId": "3b8b38e0-2f4b-40bc-8b67-37e7ea95cb60",
"credentials": {
"gmailOAuth2": {
"id": "22",
"name": "Gmail 0auth"
}
}
},
{
"parameters": {
"resource": "label",
"limit": 2
},
"type": "n8n-nodes-base.gmail",
"typeVersion": 1,
"position": [220, 400],
"id": "bae81586-7641-4fdc-81a4-0006b289bf9d",
"name": "Gmail - Labels - Get Many",
"webhookId": "3b8b38e0-2f4b-40bc-8b67-37e7ea95cb60",
"credentials": {
"gmailOAuth2": {
"id": "22",
"name": "Gmail 0auth"
}
}
}
],
"pinData": {
"Gmail - Labels - Create": [
{
"json": {
"id": "CHAT",
"name": "CHAT",
"messageListVisibility": "hide",
"labelListVisibility": "labelHide",
"type": "system"
}
}
],
"Gmail - Labels - Delete": [{ "json": { "success": true } }],
"Gmail - Labels - Get": [
{
"json": {
"id": "CHAT",
"name": "CHAT",
"messageListVisibility": "hide",
"labelListVisibility": "labelHide",
"type": "system"
}
}
],
"Gmail - Labels - Get Many": [
{
"json": {
"id": "CHAT",
"name": "CHAT",
"messageListVisibility": "hide",
"labelListVisibility": "labelHide",
"type": "system"
}
},
{ "json": { "id": "SENT", "name": "SENT", "type": "system" } }
]
},
"connections": {
"When clicking Test workflow": {
"main": [
[
{
"node": "Gmail - Labels - Create",
"type": "main",
"index": 0
},
{
"node": "Gmail - Labels - Delete",
"type": "main",
"index": 0
},
{
"node": "Gmail - Labels - Get",
"type": "main",
"index": 0
},
{
"node": "Gmail - Labels - Get Many",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "",
"meta": {
"instanceId": "27cc9b56542ad45b38725555722c50a1c3fee1670bbb67980558314ee08517c4"
},
"tags": []
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -5,9 +5,9 @@ import nock from 'nock';
import { testWorkflows } from '@test/nodes/Helpers'; import { testWorkflows } from '@test/nodes/Helpers';
import labels from './fixtures/labels.json';
import messages from './fixtures/messages.json';
import { getGmailAliases, getLabels, getThreadMessages } from '../../v2/loadOptions'; import { getGmailAliases, getLabels, getThreadMessages } from '../../v2/loadOptions';
import labels from '../fixtures/labels.json';
import messages from '../fixtures/messages.json';
describe('Test Gmail Node v2', () => { describe('Test Gmail Node v2', () => {
beforeAll(() => { beforeAll(() => {

View file

@ -5,9 +5,7 @@ import {
type IDataObject, type IDataObject,
type IExecuteFunctions, type IExecuteFunctions,
type IHttpRequestMethods, type IHttpRequestMethods,
type ILoadOptionsFunctions,
type INodeExecutionData, type INodeExecutionData,
type INodePropertyOptions,
type INodeType, type INodeType,
type INodeTypeBaseDescription, type INodeTypeBaseDescription,
type INodeTypeDescription, type INodeTypeDescription,
@ -17,6 +15,7 @@ import { oldVersionNotice } from '@utils/descriptions';
import { draftFields, draftOperations } from './DraftDescription'; import { draftFields, draftOperations } from './DraftDescription';
import { labelFields, labelOperations } from './LabelDescription'; import { labelFields, labelOperations } from './LabelDescription';
import { getLabels } from './loadOptions';
import { messageFields, messageOperations } from './MessageDescription'; import { messageFields, messageOperations } from './MessageDescription';
import { messageLabelFields, messageLabelOperations } from './MessageLabelDescription'; import { messageLabelFields, messageLabelOperations } from './MessageLabelDescription';
import type { IEmail } from '../../../../utils/sendAndWait/interfaces'; import type { IEmail } from '../../../../utils/sendAndWait/interfaces';
@ -141,32 +140,7 @@ export class GmailV1 implements INodeType {
methods = { methods = {
loadOptions: { loadOptions: {
// Get all the labels to display them to user so that they can getLabels,
// select them easily
async getLabels(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
const labels = await googleApiRequestAllItems.call(
this,
'labels',
'GET',
'/gmail/v1/users/me/labels',
);
for (const label of labels) {
returnData.push({
name: label.name,
value: label.id,
});
}
return returnData.sort((a, b) => {
if (a.name < b.name) {
return -1;
}
if (a.name > b.name) {
return 1;
}
return 0;
});
},
}, },
}; };

View file

@ -0,0 +1,3 @@
import { getLabels } from '../GenericFunctions';
export { getLabels };

View file

@ -1,37 +1,6 @@
import type { ILoadOptionsFunctions, INodePropertyOptions } from 'n8n-workflow'; import type { ILoadOptionsFunctions, INodePropertyOptions } from 'n8n-workflow';
import { googleApiRequest, googleApiRequestAllItems } from '../GenericFunctions'; import { googleApiRequest, getLabels } from '../GenericFunctions';
/**
* Get all the labels to display them to user so that they can select them easily
*/
export async function getLabels(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
const labels = await googleApiRequestAllItems.call(
this,
'labels',
'GET',
'/gmail/v1/users/me/labels',
);
for (const label of labels) {
returnData.push({
name: label.name,
value: label.id,
});
}
return returnData.sort((a, b) => {
if (a.name < b.name) {
return -1;
}
if (a.name > b.name) {
return 1;
}
return 0;
});
}
export async function getThreadMessages( export async function getThreadMessages(
this: ILoadOptionsFunctions, this: ILoadOptionsFunctions,
@ -73,3 +42,5 @@ export async function getGmailAliases(
return returnData; return returnData;
} }
export { getLabels };