From 43d32fd28ff9d3bdabd2b234249d3be5d9ffc6ab Mon Sep 17 00:00:00 2001 From: Hammad Khan Date: Wed, 3 Jun 2026 20:06:28 +0500 Subject: [PATCH] fix(TheHiveProject Node): Normalize analyzers when expression returns a string (#31580) --- .../observable/executeAnalyzer.operation.ts | 9 +--- .../nodes/TheHiveProject/helpers/utils.ts | 12 +++++ .../nodes/TheHiveProject/test/utils.test.ts | 51 ++++++++++++++++++- 3 files changed, 64 insertions(+), 8 deletions(-) diff --git a/packages/nodes-base/nodes/TheHiveProject/actions/observable/executeAnalyzer.operation.ts b/packages/nodes-base/nodes/TheHiveProject/actions/observable/executeAnalyzer.operation.ts index f547b67a702..26316b747fd 100644 --- a/packages/nodes-base/nodes/TheHiveProject/actions/observable/executeAnalyzer.operation.ts +++ b/packages/nodes-base/nodes/TheHiveProject/actions/observable/executeAnalyzer.operation.ts @@ -8,6 +8,7 @@ import type { import { updateDisplayOptions, wrapData } from '@utils/utilities'; import { observableRLC, observableTypeOptions } from '../../descriptions'; +import { parseAnalyzers } from '../../helpers/utils'; import { theHiveApiRequest } from '../../transport'; const properties: INodeProperties[] = [ @@ -49,13 +50,7 @@ export async function execute(this: IExecuteFunctions, i: number): Promise { - const parts = analyzer.split('::'); - return { - analyzerId: parts[0], - cortexId: parts[1], - }; - }); + const analyzers = parseAnalyzers(this.getNodeParameter('analyzers', i) as string | string[]); let response: any; let body: IDataObject; diff --git a/packages/nodes-base/nodes/TheHiveProject/helpers/utils.ts b/packages/nodes-base/nodes/TheHiveProject/helpers/utils.ts index b4768123a2c..17188f20721 100644 --- a/packages/nodes-base/nodes/TheHiveProject/helpers/utils.ts +++ b/packages/nodes-base/nodes/TheHiveProject/helpers/utils.ts @@ -12,6 +12,18 @@ export function splitAndTrim(str: string | string[]) { return str; } +// The "Analyzers" field is a multiOptions parameter, so it normally resolves to +// an array of "analyzerId::cortexId" entries. When its value comes from an +// expression wrapped in surrounding text/whitespace, n8n switches to string +// interpolation and the array is coerced to a comma-joined string. Normalize +// both shapes so the operation does not throw "(...).map is not a function". +export function parseAnalyzers(value: string | string[]) { + return splitAndTrim(value).map((analyzer) => { + const [analyzerId, cortexId] = analyzer.split('::'); + return { analyzerId, cortexId }; + }); +} + export function fixFieldType(fields: IDataObject) { const returnData: IDataObject = {}; diff --git a/packages/nodes-base/nodes/TheHiveProject/test/utils.test.ts b/packages/nodes-base/nodes/TheHiveProject/test/utils.test.ts index af2e5855ff3..a0c690b9d25 100644 --- a/packages/nodes-base/nodes/TheHiveProject/test/utils.test.ts +++ b/packages/nodes-base/nodes/TheHiveProject/test/utils.test.ts @@ -1,4 +1,10 @@ -import { splitAndTrim, fixFieldType, prepareInputItem, constructFilter } from '../helpers/utils'; +import { + splitAndTrim, + parseAnalyzers, + fixFieldType, + prepareInputItem, + constructFilter, +} from '../helpers/utils'; describe('Test TheHiveProject, splitAndTrim', () => { it('should split and trim string, removing empty entries', () => { @@ -18,6 +24,49 @@ describe('Test TheHiveProject, splitAndTrim', () => { }); }); +describe('Test TheHiveProject, parseAnalyzers', () => { + it('should map an array of "analyzerId::cortexId" entries', () => { + const data = ['analyzer-1::cortex-1', 'analyzer-2::cortex-2']; + + const result = parseAnalyzers(data); + + expect(result).toEqual([ + { analyzerId: 'analyzer-1', cortexId: 'cortex-1' }, + { analyzerId: 'analyzer-2', cortexId: 'cortex-2' }, + ]); + }); + + it('should map a comma-joined string (expression coercion case)', () => { + const data = 'analyzer-1::cortex-1, analyzer-2::cortex-2'; + + const result = parseAnalyzers(data); + + expect(result).toEqual([ + { analyzerId: 'analyzer-1', cortexId: 'cortex-1' }, + { analyzerId: 'analyzer-2', cortexId: 'cortex-2' }, + ]); + }); + + it('should handle a single analyzer provided as a string', () => { + const result = parseAnalyzers('analyzer-1::cortex-1'); + + expect(result).toEqual([{ analyzerId: 'analyzer-1', cortexId: 'cortex-1' }]); + }); + + it('should drop empty entries from a comma-joined string', () => { + const result = parseAnalyzers('analyzer-1::cortex-1,, analyzer-2::cortex-2,'); + + expect(result).toEqual([ + { analyzerId: 'analyzer-1', cortexId: 'cortex-1' }, + { analyzerId: 'analyzer-2', cortexId: 'cortex-2' }, + ]); + }); + + it('should return an empty array for an empty string', () => { + expect(parseAnalyzers('')).toEqual([]); + }); +}); + describe('Test TheHiveProject, fixFieldType', () => { it('should split and trim tags', () => { const data = {