Skip to content

fix(Google Sheets Trigger Node): Filter by first data row on rowAdded event #14731

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

17 changes: 13 additions & 4 deletions packages/nodes-base/nodes/Google/Sheet/GoogleSheetsTrigger.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -534,13 +534,18 @@ export class GoogleSheetsTrigger implements INodeType {
);

if (Array.isArray(sheetData) && sheetData.length !== 0) {
const zeroBasedKeyRow = keyRow - 1;
sheetData.splice(zeroBasedKeyRow, 1); // Remove key row
sheetData.splice(0, 1); // Remove header row
}

let dataStartIndex = 0;
if (rangeDefinition === 'specifyRange' && keyRow < startIndex) {
dataStartIndex = startIndex - keyRow - 1;
}

if (this.getMode() === 'manual') {
if (Array.isArray(sheetData)) {
const returnData = arrayOfArraysToJson(sheetData, columns);
const sheetDataFromStartIndex = sheetData.slice(dataStartIndex);
const returnData = arrayOfArraysToJson(sheetDataFromStartIndex, columns);

if (Array.isArray(returnData) && returnData.length !== 0) {
return [this.helpers.returnJsonArray(returnData)];
Expand All @@ -554,7 +559,11 @@ export class GoogleSheetsTrigger implements INodeType {
return null;
}

const addedRows = sheetData?.slice(workflowStaticData.lastIndexChecked as number) || [];
const rowsStartIndex = Math.max(
workflowStaticData.lastIndexChecked as number,
dataStartIndex,
);
const addedRows = sheetData?.slice(rowsStartIndex) || [];
const returnData = arrayOfArraysToJson(addedRows, columns);

workflowStaticData.lastIndexChecked = sheetData.length;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,215 @@ describe('GoogleSheetsTrigger', () => {
[{ json: { count: 14, name: 'apple' } }, { json: { count: 12, name: 'banana' } }],
]);
});

it('should return rows starting from first data row', async () => {
const scope = nock(baseUrl);
scope
.get('/v4/spreadsheets/testDocumentId')
.query({ fields: 'sheets.properties' })
.reply(200, {
sheets: [{ properties: { sheetId: 1, title: 'testSheetName' } }],
});
scope
.get((uri) => uri.startsWith('/v4/spreadsheets/testDocumentId/values/testSheetName!A5:ZZZ'))
.times(2)
.reply(200, {
values: [
['name', 'count'],
['apple', 14],
['banana', 12],
['orange', 10],
],
});

const { response } = await testPollingTriggerNode(GoogleSheetsTrigger, {
credential: mockDeep(),
mode: 'manual',
node: {
parameters: {
documentId: 'testDocumentId',
sheetName: 1,
event: 'rowAdded',
options: {
dataLocationOnSheet: {
values: {
rangeDefinition: 'specifyRange',
headerRow: 5,
firstDataRow: 7,
},
},
},
},
},
});

scope.done();

expect(response).toEqual([
[{ json: { count: 12, name: 'banana' } }, { json: { count: 10, name: 'orange' } }],
]);
});

it('should return rows starting from header row when first data row is less than header row', async () => {
const scope = nock(baseUrl);
scope
.get('/v4/spreadsheets/testDocumentId')
.query({ fields: 'sheets.properties' })
.reply(200, {
sheets: [{ properties: { sheetId: 1, title: 'testSheetName' } }],
});
scope
.get((uri) => uri.startsWith('/v4/spreadsheets/testDocumentId/values/testSheetName!A5:ZZZ'))
.times(2)
.reply(200, {
values: [
['name', 'count'],
['apple', 14],
['banana', 12],
['orange', 10],
],
});

const { response } = await testPollingTriggerNode(GoogleSheetsTrigger, {
credential: mockDeep(),
mode: 'manual',
node: {
parameters: {
documentId: 'testDocumentId',
sheetName: 1,
event: 'rowAdded',
options: {
dataLocationOnSheet: {
values: {
rangeDefinition: 'specifyRange',
headerRow: 5,
firstDataRow: 4,
},
},
},
},
},
});

scope.done();

expect(response).toEqual([
[
{ json: { count: 14, name: 'apple' } },
{ json: { count: 12, name: 'banana' } },
{ json: { count: 10, name: 'orange' } },
],
]);
});

it('should return rows starting from first data row in trigger mode', async () => {
const scope = nock(baseUrl);
scope
.get('/v4/spreadsheets/testDocumentId')
.query({ fields: 'sheets.properties' })
.reply(200, {
sheets: [{ properties: { sheetId: 1, title: 'testSheetName' } }],
});
scope
.get((uri) => uri.startsWith('/v4/spreadsheets/testDocumentId/values/testSheetName!A5:ZZZ'))
.times(2)
.reply(200, {
values: [
['name', 'count'],
['apple', 14],
['banana', 12],
['orange', 10],
],
});

const { response } = await testPollingTriggerNode(GoogleSheetsTrigger, {
credential: mockDeep(),
mode: 'trigger',
workflowStaticData: {
documentId: 'testDocumentId',
sheetId: 1,
lastIndexChecked: 0,
},
node: {
parameters: {
documentId: 'testDocumentId',
sheetName: 1,
event: 'rowAdded',
options: {
dataLocationOnSheet: {
values: {
rangeDefinition: 'specifyRange',
headerRow: 5,
firstDataRow: 7,
},
},
},
},
},
});

scope.done();

expect(response).toEqual([
[{ json: { count: 12, name: 'banana' } }, { json: { count: 10, name: 'orange' } }],
]);
});

it('should return rows starting from header row when first data row is less than header row in trigger mode', async () => {
const scope = nock(baseUrl);
scope
.get('/v4/spreadsheets/testDocumentId')
.query({ fields: 'sheets.properties' })
.reply(200, {
sheets: [{ properties: { sheetId: 1, title: 'testSheetName' } }],
});
scope
.get((uri) => uri.startsWith('/v4/spreadsheets/testDocumentId/values/testSheetName!A5:ZZZ'))
.times(2)
.reply(200, {
values: [
['name', 'count'],
['apple', 14],
['banana', 12],
['orange', 10],
],
});

const { response } = await testPollingTriggerNode(GoogleSheetsTrigger, {
credential: mockDeep(),
mode: 'trigger',
workflowStaticData: {
documentId: 'testDocumentId',
sheetId: 1,
lastIndexChecked: 0,
},
node: {
parameters: {
documentId: 'testDocumentId',
sheetName: 1,
event: 'rowAdded',
options: {
dataLocationOnSheet: {
values: {
rangeDefinition: 'specifyRange',
headerRow: 5,
firstDataRow: 4,
},
},
},
},
},
});

scope.done();

expect(response).toEqual([
[
{ json: { count: 14, name: 'apple' } },
{ json: { count: 12, name: 'banana' } },
{ json: { count: 10, name: 'orange' } },
],
]);
});
});
});
Loading