mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 20:24:05 -08:00
fix(Slack Node): Do not try to parse block if it's already object (#9643)
Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
This commit is contained in:
parent
1a3f72b751
commit
8f94dcc0e9
|
@ -100,6 +100,7 @@ import type {
|
||||||
WorkflowExecuteMode,
|
WorkflowExecuteMode,
|
||||||
CallbackManager,
|
CallbackManager,
|
||||||
INodeParameters,
|
INodeParameters,
|
||||||
|
EnsureTypeOptions,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import {
|
import {
|
||||||
ExpressionError,
|
ExpressionError,
|
||||||
|
@ -2329,6 +2330,99 @@ export const validateValueAgainstSchema = (
|
||||||
return validationResult.newValue;
|
return validationResult.newValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function ensureType(
|
||||||
|
toType: EnsureTypeOptions,
|
||||||
|
parameterValue: any,
|
||||||
|
parameterName: string,
|
||||||
|
errorOptions?: { itemIndex?: number; runIndex?: number; nodeCause?: string },
|
||||||
|
): string | number | boolean | object {
|
||||||
|
let returnData = parameterValue;
|
||||||
|
|
||||||
|
if (returnData === null) {
|
||||||
|
throw new ExpressionError(`Parameter '${parameterName}' must not be null`, errorOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returnData === undefined) {
|
||||||
|
throw new ExpressionError(
|
||||||
|
`Parameter '${parameterName}' could not be 'undefined'`,
|
||||||
|
errorOptions,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['object', 'array', 'json'].includes(toType)) {
|
||||||
|
if (typeof returnData !== 'object') {
|
||||||
|
// if value is not an object and is string try to parse it, else throw an error
|
||||||
|
if (typeof returnData === 'string' && returnData.length) {
|
||||||
|
try {
|
||||||
|
const parsedValue = JSON.parse(returnData);
|
||||||
|
returnData = parsedValue;
|
||||||
|
} catch (error) {
|
||||||
|
throw new ExpressionError(`Parameter '${parameterName}' could not be parsed`, {
|
||||||
|
...errorOptions,
|
||||||
|
description: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new ExpressionError(
|
||||||
|
`Parameter '${parameterName}' must be an ${toType}, but we got '${String(parameterValue)}'`,
|
||||||
|
errorOptions,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (toType === 'json') {
|
||||||
|
// value is an object, make sure it is valid JSON
|
||||||
|
try {
|
||||||
|
JSON.stringify(returnData);
|
||||||
|
} catch (error) {
|
||||||
|
throw new ExpressionError(`Parameter '${parameterName}' is not valid JSON`, {
|
||||||
|
...errorOptions,
|
||||||
|
description: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toType === 'array' && !Array.isArray(returnData)) {
|
||||||
|
// value is not an array, but has to be
|
||||||
|
throw new ExpressionError(
|
||||||
|
`Parameter '${parameterName}' must be an array, but we got object`,
|
||||||
|
errorOptions,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (toType === 'string') {
|
||||||
|
if (typeof returnData === 'object') {
|
||||||
|
returnData = JSON.stringify(returnData);
|
||||||
|
} else {
|
||||||
|
returnData = String(returnData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toType === 'number') {
|
||||||
|
returnData = Number(returnData);
|
||||||
|
if (Number.isNaN(returnData)) {
|
||||||
|
throw new ExpressionError(
|
||||||
|
`Parameter '${parameterName}' must be a number, but we got '${parameterValue}'`,
|
||||||
|
errorOptions,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toType === 'boolean') {
|
||||||
|
returnData = Boolean(returnData);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ExpressionError) throw error;
|
||||||
|
|
||||||
|
throw new ExpressionError(`Parameter '${parameterName}' could not be converted to ${toType}`, {
|
||||||
|
...errorOptions,
|
||||||
|
description: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnData;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the requested resolved (all expressions replaced) node parameters.
|
* Returns the requested resolved (all expressions replaced) node parameters.
|
||||||
*
|
*
|
||||||
|
@ -2399,6 +2493,15 @@ export function getNodeParameter(
|
||||||
returnData = extractValue(returnData, parameterName, node, nodeType, itemIndex);
|
returnData = extractValue(returnData, parameterName, node, nodeType, itemIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure parameter value is the type specified in the ensureType option, if needed convert it
|
||||||
|
if (options?.ensureType) {
|
||||||
|
returnData = ensureType(options.ensureType, returnData, parameterName, {
|
||||||
|
itemIndex,
|
||||||
|
runIndex,
|
||||||
|
nodeCause: node.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Validate parameter value if it has a schema defined(RMC) or validateType defined
|
// Validate parameter value if it has a schema defined(RMC) or validateType defined
|
||||||
returnData = validateValueAgainstSchema(
|
returnData = validateValueAgainstSchema(
|
||||||
node,
|
node,
|
||||||
|
|
|
@ -2,6 +2,7 @@ import type { SecureContextOptions } from 'tls';
|
||||||
import {
|
import {
|
||||||
cleanupParameterData,
|
cleanupParameterData,
|
||||||
copyInputItems,
|
copyInputItems,
|
||||||
|
ensureType,
|
||||||
getBinaryDataBuffer,
|
getBinaryDataBuffer,
|
||||||
parseIncomingMessage,
|
parseIncomingMessage,
|
||||||
parseRequestObject,
|
parseRequestObject,
|
||||||
|
@ -25,6 +26,7 @@ import type {
|
||||||
Workflow,
|
Workflow,
|
||||||
WorkflowHooks,
|
WorkflowHooks,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
import { ExpressionError } from 'n8n-workflow';
|
||||||
import { BinaryDataService } from '@/BinaryData/BinaryData.service';
|
import { BinaryDataService } from '@/BinaryData/BinaryData.service';
|
||||||
import nock from 'nock';
|
import nock from 'nock';
|
||||||
import { tmpdir } from 'os';
|
import { tmpdir } from 'os';
|
||||||
|
@ -583,4 +585,81 @@ describe('NodeExecuteFunctions', () => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('ensureType', () => {
|
||||||
|
it('throws error for null value', () => {
|
||||||
|
expect(() => ensureType('string', null, 'myParam')).toThrowError(
|
||||||
|
new ExpressionError("Parameter 'myParam' must not be null"),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws error for undefined value', () => {
|
||||||
|
expect(() => ensureType('string', undefined, 'myParam')).toThrowError(
|
||||||
|
new ExpressionError("Parameter 'myParam' could not be 'undefined'"),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns string value without modification', () => {
|
||||||
|
const value = 'hello';
|
||||||
|
const expectedValue = value;
|
||||||
|
const result = ensureType('string', value, 'myParam');
|
||||||
|
expect(result).toBe(expectedValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns number value without modification', () => {
|
||||||
|
const value = 42;
|
||||||
|
const expectedValue = value;
|
||||||
|
const result = ensureType('number', value, 'myParam');
|
||||||
|
expect(result).toBe(expectedValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns boolean value without modification', () => {
|
||||||
|
const value = true;
|
||||||
|
const expectedValue = value;
|
||||||
|
const result = ensureType('boolean', value, 'myParam');
|
||||||
|
expect(result).toBe(expectedValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('converts object to string if toType is string', () => {
|
||||||
|
const value = { name: 'John' };
|
||||||
|
const expectedValue = JSON.stringify(value);
|
||||||
|
const result = ensureType('string', value, 'myParam');
|
||||||
|
expect(result).toBe(expectedValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('converts string to number if toType is number', () => {
|
||||||
|
const value = '10';
|
||||||
|
const expectedValue = 10;
|
||||||
|
const result = ensureType('number', value, 'myParam');
|
||||||
|
expect(result).toBe(expectedValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws error for invalid conversion to number', () => {
|
||||||
|
const value = 'invalid';
|
||||||
|
expect(() => ensureType('number', value, 'myParam')).toThrowError(
|
||||||
|
new ExpressionError("Parameter 'myParam' must be a number, but we got 'invalid'"),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses valid JSON string to object if toType is object', () => {
|
||||||
|
const value = '{"name": "Alice"}';
|
||||||
|
const expectedValue = JSON.parse(value);
|
||||||
|
const result = ensureType('object', value, 'myParam');
|
||||||
|
expect(result).toEqual(expectedValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws error for invalid JSON string to object conversion', () => {
|
||||||
|
const value = 'invalid_json';
|
||||||
|
expect(() => ensureType('object', value, 'myParam')).toThrowError(
|
||||||
|
new ExpressionError("Parameter 'myParam' could not be parsed"),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws error for non-array value if toType is array', () => {
|
||||||
|
const value = { name: 'Alice' };
|
||||||
|
expect(() => ensureType('array', value, 'myParam')).toThrowError(
|
||||||
|
new ExpressionError("Parameter 'myParam' must be an array, but we got object"),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,7 +8,7 @@ import type {
|
||||||
IWebhookFunctions,
|
IWebhookFunctions,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import { NodeOperationError, jsonParse } from 'n8n-workflow';
|
import { NodeOperationError } from 'n8n-workflow';
|
||||||
|
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ export function getMessageContent(
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case 'block':
|
case 'block':
|
||||||
content = jsonParse(this.getNodeParameter('blocksUi', i) as string);
|
content = this.getNodeParameter('blocksUi', i, {}, { ensureType: 'object' }) as IDataObject;
|
||||||
|
|
||||||
if (includeLinkToWorkflow && Array.isArray(content.blocks)) {
|
if (includeLinkToWorkflow && Array.isArray(content.blocks)) {
|
||||||
content.blocks.push({
|
content.blocks.push({
|
||||||
|
|
|
@ -622,8 +622,11 @@ export interface IN8nRequestOperationPaginationOffset extends IN8nRequestOperati
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type EnsureTypeOptions = 'string' | 'number' | 'boolean' | 'object' | 'array' | 'json';
|
||||||
export interface IGetNodeParameterOptions {
|
export interface IGetNodeParameterOptions {
|
||||||
contextNode?: INode;
|
contextNode?: INode;
|
||||||
|
// make sure that returned value would be of specified type, converts it if needed
|
||||||
|
ensureType?: EnsureTypeOptions;
|
||||||
// extract value from regex, works only when parameter type is resourceLocator
|
// extract value from regex, works only when parameter type is resourceLocator
|
||||||
extractValue?: boolean;
|
extractValue?: boolean;
|
||||||
// get raw value of parameter with unresolved expressions
|
// get raw value of parameter with unresolved expressions
|
||||||
|
|
Loading…
Reference in a new issue