From f899be15138442a3f043c683a189a987b201218a Mon Sep 17 00:00:00 2001 From: Roman Davydchuk Date: Fri, 17 Apr 2026 16:33:01 +0000 Subject: [PATCH 1/3] fix: Fetch more parameters from Jenkins API --- .../nodes-base/nodes/Jenkins/Jenkins.node.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/nodes-base/nodes/Jenkins/Jenkins.node.ts b/packages/nodes-base/nodes/Jenkins/Jenkins.node.ts index 9db534299ee8b..96534c3f4972d 100644 --- a/packages/nodes-base/nodes/Jenkins/Jenkins.node.ts +++ b/packages/nodes-base/nodes/Jenkins/Jenkins.node.ts @@ -452,15 +452,22 @@ export class Jenkins implements INodeType { async getJobParameters(this: ILoadOptionsFunctions): Promise { const job = this.getCurrentNodeParameter('job') as string; const returnData: INodePropertyOptions[] = []; - const endpoint = `/job/${job}/api/json?tree=actions[parameterDefinitions[*]]`; - const { actions } = await jenkinsApiRequest.call(this, 'GET', endpoint); - for (const { _class, parameterDefinitions } of actions) { - if (_class?.includes('ParametersDefinitionProperty')) { - for (const { name, type } of parameterDefinitions) { + const endpoint = `/job/${job}/api/json?tree=actions[parameterDefinitions[*]],property[parameterDefinitions[*]]`; + const result = await jenkinsApiRequest.call(this, 'GET', endpoint); + const allParameters = [...(result.actions ?? []), ...(result.property ?? [])]; + const seenParameterNames = new Set(); + for (const { _class, parameterDefinitions } of allParameters) { + if (!_class?.includes('ParametersDefinitionProperty')) { + continue; + } + + for (const { name, type } of parameterDefinitions) { + if (!seenParameterNames.has(name)) { returnData.push({ name: `${name} - (${type})`, value: name, }); + seenParameterNames.add(name); } } } From b53a5a701459c935d92708cc2da1559b4e275df3 Mon Sep 17 00:00:00 2001 From: Roman Davydchuk Date: Fri, 17 Apr 2026 16:40:58 +0000 Subject: [PATCH 2/3] test: Add tests --- .../nodes/Jenkins/test/Jenkins.node.test.ts | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 packages/nodes-base/nodes/Jenkins/test/Jenkins.node.test.ts diff --git a/packages/nodes-base/nodes/Jenkins/test/Jenkins.node.test.ts b/packages/nodes-base/nodes/Jenkins/test/Jenkins.node.test.ts new file mode 100644 index 0000000000000..295b4242cbe17 --- /dev/null +++ b/packages/nodes-base/nodes/Jenkins/test/Jenkins.node.test.ts @@ -0,0 +1,121 @@ +import { mockDeep } from 'jest-mock-extended'; +import type { ILoadOptionsFunctions, INodePropertyOptions } from 'n8n-workflow'; + +import * as GenericFunctions from '../GenericFunctions'; +import { Jenkins } from '../Jenkins.node'; + +describe('Jenkins node', () => { + let node: Jenkins; + let loadOptionsFunctions: jest.Mocked; + const jenkinsApiRequestSpy = jest.spyOn(GenericFunctions, 'jenkinsApiRequest'); + + beforeEach(() => { + node = new Jenkins(); + loadOptionsFunctions = mockDeep(); + loadOptionsFunctions.getCurrentNodeParameter.mockReturnValue('demo-job'); + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + describe('loadOptions.getJobParameters', () => { + it('loads parameters from actions', async () => { + jenkinsApiRequestSpy.mockResolvedValue({ + actions: [ + { + _class: 'hudson.model.ParametersDefinitionProperty', + parameterDefinitions: [ + { name: 'BRANCH', type: 'StringParameterDefinition' }, + { name: 'DRY_RUN', type: 'BooleanParameterDefinition' }, + ], + }, + ], + }); + + const result = await node.methods.loadOptions.getJobParameters.call(loadOptionsFunctions); + + expect(result).toEqual([ + { name: 'BRANCH - (StringParameterDefinition)', value: 'BRANCH' }, + { name: 'DRY_RUN - (BooleanParameterDefinition)', value: 'DRY_RUN' }, + ]); + expect(jenkinsApiRequestSpy).toHaveBeenCalledWith( + 'GET', + '/job/demo-job/api/json?tree=actions[parameterDefinitions[*]],property[parameterDefinitions[*]]', + ); + }); + + it('loads parameters from property', async () => { + jenkinsApiRequestSpy.mockResolvedValue({ + property: [ + { + _class: + 'org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty ParametersDefinitionProperty', + parameterDefinitions: [{ name: 'VERSION', type: 'StringParameterDefinition' }], + }, + ], + }); + + const result = await node.methods.loadOptions.getJobParameters.call(loadOptionsFunctions); + + expect(result).toEqual([ + { name: 'VERSION - (StringParameterDefinition)', value: 'VERSION' }, + ]); + }); + + it('merges actions and property results and deduplicates parameter names', async () => { + jenkinsApiRequestSpy.mockResolvedValue({ + actions: [ + { + _class: 'hudson.model.ParametersDefinitionProperty', + parameterDefinitions: [{ name: 'ENV', type: 'StringParameterDefinition' }], + }, + ], + property: [ + { + _class: 'hudson.model.ParametersDefinitionProperty', + parameterDefinitions: [ + { name: 'ENV', type: 'StringParameterDefinition' }, + { name: 'REGION', type: 'ChoiceParameterDefinition' }, + ], + }, + ], + }); + + const result = await node.methods.loadOptions.getJobParameters.call(loadOptionsFunctions); + + expect(result).toEqual([ + { name: 'ENV - (StringParameterDefinition)', value: 'ENV' }, + { name: 'REGION - (ChoiceParameterDefinition)', value: 'REGION' }, + ]); + }); + + it('filters non parameter classes and sorts by display name', async () => { + jenkinsApiRequestSpy.mockResolvedValue({ + actions: [ + { + _class: 'hudson.model.ScmProperty', + parameterDefinitions: [ + { name: 'SHOULD_NOT_APPEAR', type: 'StringParameterDefinition' }, + ], + }, + { + _class: 'hudson.model.ParametersDefinitionProperty', + parameterDefinitions: [ + { name: 'ZZZ', type: 'StringParameterDefinition' }, + { name: 'AAA', type: 'StringParameterDefinition' }, + ], + }, + ], + }); + + const result = await node.methods.loadOptions.getJobParameters.call(loadOptionsFunctions); + + expect(result).toEqual([ + { name: 'AAA - (StringParameterDefinition)', value: 'AAA' }, + { name: 'ZZZ - (StringParameterDefinition)', value: 'ZZZ' }, + ]); + }); + }); +}); From e3c5bb6e54ef110135fac6b8381c14cbdc86d36e Mon Sep 17 00:00:00 2001 From: Roman Davydchuk Date: Wed, 22 Apr 2026 20:21:51 +0000 Subject: [PATCH 3/3] Check if parameterDefinitions is an array --- packages/nodes-base/nodes/Jenkins/Jenkins.node.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Jenkins/Jenkins.node.ts b/packages/nodes-base/nodes/Jenkins/Jenkins.node.ts index 96534c3f4972d..33cc82e4c6da1 100644 --- a/packages/nodes-base/nodes/Jenkins/Jenkins.node.ts +++ b/packages/nodes-base/nodes/Jenkins/Jenkins.node.ts @@ -457,7 +457,10 @@ export class Jenkins implements INodeType { const allParameters = [...(result.actions ?? []), ...(result.property ?? [])]; const seenParameterNames = new Set(); for (const { _class, parameterDefinitions } of allParameters) { - if (!_class?.includes('ParametersDefinitionProperty')) { + if ( + !_class?.includes('ParametersDefinitionProperty') || + !Array.isArray(parameterDefinitions) + ) { continue; }