diff --git a/packages/nodes-base/nodes/Google/Sheet/test/v2/utils/utils.test.ts b/packages/nodes-base/nodes/Google/Sheet/test/v2/utils/utils.test.ts index 2988ade4579..60b49a87d25 100644 --- a/packages/nodes-base/nodes/Google/Sheet/test/v2/utils/utils.test.ts +++ b/packages/nodes-base/nodes/Google/Sheet/test/v2/utils/utils.test.ts @@ -473,18 +473,18 @@ 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', - }, - }; + const node: INode = { + id: '1', + name: 'Google Sheets', + typeVersion: 4.4, + type: 'n8n-nodes-base.googleSheets', + position: [60, 760], + parameters: { + operation: 'append', + }, + }; + it('should not throw when columns match exactly', () => { expect(() => checkForSchemaChanges(node, ['id', 'name', 'data'], [ { id: 'id' }, @@ -493,18 +493,8 @@ describe('Test Google Sheets, checkForSchemaChanges', () => { ] 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', - }, - }; + it('should throw when a schema column is missing from the sheet', () => { expect(() => checkForSchemaChanges(node, ['id', 'name', 'data'], [ { id: 'id' }, @@ -514,18 +504,7 @@ describe('Test Google Sheets, checkForSchemaChanges', () => { ).toThrow("Column names were updated after the node's setup"); }); - it('should filter out empty columns without throwing an error', async () => { - const node: INode = { - id: '1', - name: 'Google Sheets', - typeVersion: 4.4, - type: 'n8n-nodes-base.googleSheets', - position: [60, 760], - parameters: { - operation: 'append', - }, - }; - + it('should filter out empty columns without throwing', () => { expect(() => checkForSchemaChanges(node, ['', '', 'id', 'name', 'data'], [ { id: 'id' }, @@ -534,6 +513,42 @@ describe('Test Google Sheets, checkForSchemaChanges', () => { ] as ResourceMapperField[]), ).not.toThrow(); }); + + it('should not throw when columns are reordered', () => { + expect(() => + checkForSchemaChanges(node, ['data', 'id', 'name'], [ + { id: 'id' }, + { id: 'name' }, + { id: 'data' }, + ] as ResourceMapperField[]), + ).not.toThrow(); + }); + + it('should not throw when new columns are inserted', () => { + expect(() => + checkForSchemaChanges(node, ['id', 'owner_email', 'name', 'data'], [ + { id: 'id' }, + { id: 'name' }, + { id: 'data' }, + ] as ResourceMapperField[]), + ).not.toThrow(); + }); + + it('should throw and list only the missing columns', () => { + try { + checkForSchemaChanges(node, ['id', 'name'], [ + { id: 'id' }, + { id: 'name' }, + { id: 'data' }, + ] as ResourceMapperField[]); + fail('Expected checkForSchemaChanges to throw'); + } catch (error) { + expect(error.message).toBe("Column names were updated after the node's setup"); + expect(error.description).toBe( + "Refresh the columns list in the 'Column to Match On' parameter. Missing columns: data", + ); + } + }); }); describe('Test Google Sheets, getSpreadsheetId', () => { diff --git a/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheets.utils.ts b/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheets.utils.ts index 8ba6d03956d..7435fec736a 100644 --- a/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheets.utils.ts +++ b/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheets.utils.ts @@ -360,26 +360,19 @@ export function checkForSchemaChanges( columnNames: string[], schema: ResourceMapperField[], ) { - const updatedColumnNames: Array<{ oldName: string; newName: string }> = []; // RMC filters out empty columns so do the same here - columnNames = columnNames.filter((col) => col !== ''); + const liveColumns = new Set(columnNames.filter((col) => col !== '')); // if sheet does not contain ROW_NUMBER ignore it as data come from read rows operation - const schemaColumns = columnNames.includes(ROW_NUMBER) + const schemaColumns = liveColumns.has(ROW_NUMBER) ? schema.map((s) => s.id) : schema.filter((s) => s.id !== ROW_NUMBER).map((s) => s.id); - for (const [columnIndex, columnName] of columnNames.entries()) { - const schemaEntry = schemaColumns[columnIndex]; - if (schemaEntry === undefined) break; - if (columnName !== schemaEntry) { - updatedColumnNames.push({ oldName: schemaEntry, newName: columnName }); - } - } + const missingColumns = schemaColumns.filter((col) => !liveColumns.has(col)); - if (updatedColumnNames.length) { + if (missingColumns.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(', ')}`, + description: `Refresh the columns list in the 'Column to Match On' parameter. Missing columns: ${missingColumns.join(', ')}`, }); } }