Skip to content

Commit

Permalink
fix(Google Sheets Node): Check for column names changes before upsert…
Browse files Browse the repository at this point in the history
…, append, update (#9649)
  • Loading branch information
michael-radency committed Jun 20, 2024
1 parent cdc2f9e commit 223488f
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 8 deletions.
3 changes: 2 additions & 1 deletion packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class GoogleSheets extends VersionedNodeType {
name: 'googleSheets',
icon: 'file:googleSheets.svg',
group: ['input', 'output'],
defaultVersion: 4.3,
defaultVersion: 4.4,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Read, update and write data to Google Sheets',
};
Expand All @@ -24,6 +24,7 @@ export class GoogleSheets extends VersionedNodeType {
4.1: new GoogleSheetsV2(baseDescription),
4.2: new GoogleSheetsV2(baseDescription),
4.3: new GoogleSheetsV2(baseDescription),
4.4: new GoogleSheetsV2(baseDescription),
};

super(nodeVersions, baseDescription);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { IExecuteFunctions, INode } from 'n8n-workflow';
import type { IExecuteFunctions, INode, ResourceMapperField } from 'n8n-workflow';
import { GoogleSheet } from '../../../v2/helpers/GoogleSheet';
import {
addRowNumber,
autoMapInputData,
checkForSchemaChanges,
prepareSheetData,
removeEmptyColumns,
removeEmptyRows,
Expand Down Expand Up @@ -400,3 +401,46 @@ describe('Test Google Sheets, lookupValues', () => {
]);
});
});

describe('Test Google Sheets, checkForSchemaChanges', () => {
it('should not to throw error', async () => {
const node: INode = {
id: '1',
name: 'Google Sheets',
typeVersion: 4.4,
type: 'n8n-nodes-base.googleSheets',
position: [60, 760],
parameters: {
operation: 'append',
},
};

expect(() =>
checkForSchemaChanges(node, ['id', 'name', 'data'], [
{ id: 'id' },
{ id: 'name' },
{ id: 'data' },
] as ResourceMapperField[]),
).not.toThrow();
});
it('should throw error when columns were renamed', async () => {
const node: INode = {
id: '1',
name: 'Google Sheets',
typeVersion: 4.4,
type: 'n8n-nodes-base.googleSheets',
position: [60, 760],
parameters: {
operation: 'append',
},
};

expect(() =>
checkForSchemaChanges(node, ['id', 'name', 'data'], [
{ id: 'id' },
{ id: 'name' },
{ id: 'text' },
] as ResourceMapperField[]),
).toThrow("Column names were updated after the node's setup");
});
});
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import type { IExecuteFunctions, IDataObject, INodeExecutionData } from 'n8n-workflow';
import {
type IExecuteFunctions,
type IDataObject,
type INodeExecutionData,
NodeOperationError,
type ResourceMapperField,
} from 'n8n-workflow';
import type { SheetProperties, ValueInputOption } from '../../helpers/GoogleSheets.types';
import type { GoogleSheet } from '../../helpers/GoogleSheet';
import {
autoMapInputData,
cellFormatDefault,
checkForSchemaChanges,
mapFields,
untilSheetSelected,
} from '../../helpers/GoogleSheets.utils';
Expand Down Expand Up @@ -226,6 +233,21 @@ export async function execute(
headerRow = locationDefine.headerRow as number;
}

if (nodeVersion >= 4.4 && dataMode !== 'autoMapInputData') {
//not possible to refresh columns when mode is autoMapInputData
const sheetData = await sheet.getData(sheetName, 'FORMATTED_VALUE');

if (sheetData?.[headerRow - 1] === undefined) {
throw new NodeOperationError(
this.getNode(),
`Could not retrieve the column names from row ${headerRow}`,
);
}

const schema = this.getNodeParameter('columns.schema', 0) as ResourceMapperField[];
checkForSchemaChanges(this.getNode(), sheetData[headerRow - 1], schema);
}

let setData: IDataObject[] = [];

if (dataMode === 'autoMapInputData') {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import type { IExecuteFunctions, IDataObject, INodeExecutionData } from 'n8n-workflow';
import type {
IExecuteFunctions,
IDataObject,
INodeExecutionData,
ResourceMapperField,
} from 'n8n-workflow';
import { NodeOperationError } from 'n8n-workflow';
import type {
ISheetUpdateData,
Expand All @@ -7,7 +12,11 @@ import type {
ValueRenderOption,
} from '../../helpers/GoogleSheets.types';
import type { GoogleSheet } from '../../helpers/GoogleSheet';
import { cellFormatDefault, untilSheetSelected } from '../../helpers/GoogleSheets.utils';
import {
cellFormatDefault,
checkForSchemaChanges,
untilSheetSelected,
} from '../../helpers/GoogleSheets.utils';
import { cellFormat, handlingExtraData, locationDefine } from './commonDescription';

export const description: SheetProperties = [
Expand Down Expand Up @@ -267,6 +276,11 @@ export async function execute(

columnNames = sheetData[headerRow] ?? [];

if (nodeVersion >= 4.4) {
const schema = this.getNodeParameter('columns.schema', 0) as ResourceMapperField[];
checkForSchemaChanges(this.getNode(), columnNames, schema);
}

const newColumns = new Set<string>();

const columnsToMatchOn: string[] =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import type { IExecuteFunctions, IDataObject, INodeExecutionData } from 'n8n-workflow';
import type {
IExecuteFunctions,
IDataObject,
INodeExecutionData,
ResourceMapperField,
} from 'n8n-workflow';
import { NodeOperationError } from 'n8n-workflow';
import type {
ISheetUpdateData,
Expand All @@ -7,7 +12,11 @@ import type {
ValueRenderOption,
} from '../../helpers/GoogleSheets.types';
import type { GoogleSheet } from '../../helpers/GoogleSheet';
import { cellFormatDefault, untilSheetSelected } from '../../helpers/GoogleSheets.utils';
import {
cellFormatDefault,
checkForSchemaChanges,
untilSheetSelected,
} from '../../helpers/GoogleSheets.utils';
import { cellFormat, handlingExtraData, locationDefine } from './commonDescription';

export const description: SheetProperties = [
Expand Down Expand Up @@ -252,6 +261,12 @@ export async function execute(
}

columnNames = sheetData[headerRow];

if (nodeVersion >= 4.4) {
const schema = this.getNodeParameter('columns.schema', 0) as ResourceMapperField[];
checkForSchemaChanges(this.getNode(), columnNames, schema);
}

const newColumns = new Set<string>();

const columnsToMatchOn: string[] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const versionDescription: INodeTypeDescription = {
name: 'googleSheets',
icon: 'file:googleSheets.svg',
group: ['input', 'output'],
version: [3, 4, 4.1, 4.2, 4.3],
version: [3, 4, 4.1, 4.2, 4.3, 4.4],
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Read, update and write data to Google Sheets',
defaults: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
INodeListSearchItems,
INodePropertyOptions,
INode,
ResourceMapperField,
} from 'n8n-workflow';
import { NodeOperationError } from 'n8n-workflow';
import type { GoogleSheet } from './GoogleSheet';
Expand Down Expand Up @@ -334,3 +335,25 @@ export function cellFormatDefault(nodeVersion: number) {
}
return 'USER_ENTERED';
}

export function checkForSchemaChanges(
node: INode,
columnNames: string[],
schema: ResourceMapperField[],
) {
const updatedColumnNames: Array<{ oldName: string; newName: string }> = [];

for (const [columnIndex, columnName] of columnNames.entries()) {
const schemaEntry = schema[columnIndex];
if (schemaEntry === undefined) break;
if (columnName !== schema[columnIndex].id) {
updatedColumnNames.push({ oldName: schema[columnIndex].id, newName: columnName });
}
}

if (updatedColumnNames.length) {
throw new NodeOperationError(node, "Column names were updated after the node's setup", {
description: `Refresh the columns list in the 'Column to Match On' parameter. Updated columns: ${updatedColumnNames.map((c) => `${c.oldName} -> ${c.newName}`).join(', ')}`,
});
}
}

0 comments on commit 223488f

Please sign in to comment.