fix(Microsoft Excel 365 Node): Better error and description on unsupported range in upsert, update, getRange operations (#8452)

This commit is contained in:
Michael Kret 2024-01-26 13:33:29 +00:00 committed by GitHub
parent c70fa66e76
commit 8a595d1527
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 51 additions and 7 deletions

View file

@ -3,6 +3,7 @@ import { mock } from 'jest-mock-extended';
import type { IDataObject, IExecuteFunctions, IGetNodeParameterOptions, INode } from 'n8n-workflow'; import type { IDataObject, IExecuteFunctions, IGetNodeParameterOptions, INode } from 'n8n-workflow';
import { constructExecutionMetaData } from 'n8n-core'; import { constructExecutionMetaData } from 'n8n-core';
import { import {
checkRange,
prepareOutput, prepareOutput,
updateByAutoMaping, updateByAutoMaping,
updateByDefinedValues, updateByDefinedValues,
@ -491,7 +492,7 @@ describe('Test MicrosoftExcelV2, updateByAutoMaping', () => {
expect(updateSummary.updatedData[4][1]).toEqual('Ismael'); // updated value expect(updateSummary.updatedData[4][1]).toEqual('Ismael'); // updated value
}); });
it('should update all occurances', () => { it('should update all occurrences', () => {
const items = [ const items = [
{ {
json: { json: {
@ -556,3 +557,19 @@ describe('Test MicrosoftExcelV2, updateByAutoMaping', () => {
expect(updateSummary.appendData[1]).toEqual({ id: 5, name: 'Victor', age: 67, data: 'data 5' }); expect(updateSummary.appendData[1]).toEqual({ id: 5, name: 'Victor', age: 67, data: 'data 5' });
}); });
}); });
describe('Test MicrosoftExcelV2, checkRange', () => {
it('should not throw error', () => {
const range = 'A1:D4';
expect(() => {
checkRange(node, range);
}).not.toThrow();
});
it('should throw error', () => {
const range = 'A:D';
expect(() => {
checkRange(node, range);
}).toThrow();
});
});

View file

@ -5,7 +5,7 @@ import type {
INodeProperties, INodeProperties,
} from 'n8n-workflow'; } from 'n8n-workflow';
import type { ExcelResponse } from '../../helpers/interfaces'; import type { ExcelResponse } from '../../helpers/interfaces';
import { prepareOutput } from '../../helpers/utils'; import { checkRange, prepareOutput } from '../../helpers/utils';
import { microsoftApiRequest } from '../../transport'; import { microsoftApiRequest } from '../../transport';
import { workbookRLC, worksheetRLC } from '../common.descriptions'; import { workbookRLC, worksheetRLC } from '../common.descriptions';
import { updateDisplayOptions } from '@utils/utilities'; import { updateDisplayOptions } from '@utils/utilities';
@ -25,7 +25,8 @@ const properties: INodeProperties[] = [
type: 'string', type: 'string',
placeholder: 'e.g. A1:B2', placeholder: 'e.g. A1:B2',
default: '', default: '',
description: 'The sheet range to read the data from specified using a A1-style notation', description:
'The sheet range to read the data from specified using a A1-style notation, has to be specific e.g A1:B5, generic ranges like A:B are not supported',
hint: 'Leave blank to return entire sheet', hint: 'Leave blank to return entire sheet',
displayOptions: { displayOptions: {
show: { show: {
@ -140,6 +141,7 @@ export async function execute(
const options = this.getNodeParameter('options', i, {}); const options = this.getNodeParameter('options', i, {});
const range = this.getNodeParameter('range', i, '') as string; const range = this.getNodeParameter('range', i, '') as string;
checkRange(this.getNode(), range);
const rawData = (options.rawData as boolean) || false; const rawData = (options.rawData as boolean) || false;

View file

@ -6,7 +6,12 @@ import type {
} from 'n8n-workflow'; } from 'n8n-workflow';
import { NodeOperationError } from 'n8n-workflow'; import { NodeOperationError } from 'n8n-workflow';
import type { ExcelResponse, UpdateSummary } from '../../helpers/interfaces'; import type { ExcelResponse, UpdateSummary } from '../../helpers/interfaces';
import { prepareOutput, updateByAutoMaping, updateByDefinedValues } from '../../helpers/utils'; import {
checkRange,
prepareOutput,
updateByAutoMaping,
updateByDefinedValues,
} from '../../helpers/utils';
import { microsoftApiRequest } from '../../transport'; import { microsoftApiRequest } from '../../transport';
import { workbookRLC, worksheetRLC } from '../common.descriptions'; import { workbookRLC, worksheetRLC } from '../common.descriptions';
import { generatePairedItemData, processJsonInput, updateDisplayOptions } from '@utils/utilities'; import { generatePairedItemData, processJsonInput, updateDisplayOptions } from '@utils/utilities';
@ -33,7 +38,7 @@ const properties: INodeProperties[] = [
placeholder: 'e.g. A1:B2', placeholder: 'e.g. A1:B2',
default: '', default: '',
description: description:
'The sheet range to read the data from specified using a A1-style notation. Leave blank to use whole used range in the sheet.', 'The sheet range to read the data from specified using a A1-style notation, has to be specific e.g A1:B5, generic ranges like A:B are not supported. Leave blank to use whole used range in the sheet.',
hint: 'First row must contain column names', hint: 'First row must contain column names',
}, },
{ {
@ -254,6 +259,8 @@ export async function execute(
}) as string; }) as string;
let range = this.getNodeParameter('range', 0, '') as string; let range = this.getNodeParameter('range', 0, '') as string;
checkRange(this.getNode(), range);
const dataMode = this.getNodeParameter('dataMode', 0) as string; const dataMode = this.getNodeParameter('dataMode', 0) as string;
let worksheetData: IDataObject = {}; let worksheetData: IDataObject = {};

View file

@ -6,7 +6,12 @@ import type {
} from 'n8n-workflow'; } from 'n8n-workflow';
import { NodeOperationError } from 'n8n-workflow'; import { NodeOperationError } from 'n8n-workflow';
import type { ExcelResponse, UpdateSummary } from '../../helpers/interfaces'; import type { ExcelResponse, UpdateSummary } from '../../helpers/interfaces';
import { prepareOutput, updateByAutoMaping, updateByDefinedValues } from '../../helpers/utils'; import {
checkRange,
prepareOutput,
updateByAutoMaping,
updateByDefinedValues,
} from '../../helpers/utils';
import { microsoftApiRequest } from '../../transport'; import { microsoftApiRequest } from '../../transport';
import { workbookRLC, worksheetRLC } from '../common.descriptions'; import { workbookRLC, worksheetRLC } from '../common.descriptions';
import { generatePairedItemData, processJsonInput, updateDisplayOptions } from '@utils/utilities'; import { generatePairedItemData, processJsonInput, updateDisplayOptions } from '@utils/utilities';
@ -33,7 +38,7 @@ const properties: INodeProperties[] = [
placeholder: 'e.g. A1:B2', placeholder: 'e.g. A1:B2',
default: '', default: '',
description: description:
'The sheet range to read the data from specified using a A1-style notation. Leave blank to use whole used range in the sheet.', 'The sheet range to read the data from specified using a A1-style notation, has to be specific e.g A1:B5, generic ranges like A:B are not supported. Leave blank to use whole used range in the sheet.',
hint: 'First row must contain column names', hint: 'First row must contain column names',
}, },
{ {
@ -191,6 +196,8 @@ export async function execute(
}) as string; }) as string;
let range = this.getNodeParameter('range', 0, '') as string; let range = this.getNodeParameter('range', 0, '') as string;
checkRange(this.getNode(), range);
const dataMode = this.getNodeParameter('dataMode', 0) as string; const dataMode = this.getNodeParameter('dataMode', 0) as string;
let worksheetData: IDataObject = {}; let worksheetData: IDataObject = {};

View file

@ -206,3 +206,14 @@ export function updateByAutoMaping(
return summary; return summary;
} }
export const checkRange = (node: INode, range: string) => {
const rangeRegex = /^[A-Z]+:[A-Z]+$/i;
if (rangeRegex.test(range)) {
throw new NodeOperationError(
node,
`Specify the range more precisely e.g. A1:B5, generic ranges like ${range} are not supported`,
);
}
};