From 1cbbcf4a7726f38936a8fceeee8d635a212b0764 Mon Sep 17 00:00:00 2001 From: RomanDavydchuk Date: Wed, 23 Apr 2025 14:08:18 +0300 Subject: [PATCH] fix(Jira Software Node): 403 when getting a list of items from Jira Cloud (#14782) --- .../nodes-base/nodes/Jira/GenericFunctions.ts | 10 +++- packages/nodes-base/nodes/Jira/Jira.node.ts | 8 +-- .../nodes/Jira/test/GenericFunctions.test.ts | 57 +++++++++++++++++++ 3 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 packages/nodes-base/nodes/Jira/test/GenericFunctions.test.ts diff --git a/packages/nodes-base/nodes/Jira/GenericFunctions.ts b/packages/nodes-base/nodes/Jira/GenericFunctions.ts index e75041bbcb..1dd16a8393 100644 --- a/packages/nodes-base/nodes/Jira/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Jira/GenericFunctions.ts @@ -88,15 +88,19 @@ export async function jiraSoftwareCloudApiRequestAllItems( let responseData; query.startAt = 0; - body.startAt = 0; query.maxResults = 100; - body.maxResults = 100; + if (method !== 'GET') { + body.startAt = 0; + body.maxResults = 100; + } do { responseData = await jiraSoftwareCloudApiRequest.call(this, endpoint, method, body, query); returnData.push.apply(returnData, responseData[propertyName] as IDataObject[]); query.startAt = (responseData.startAt as number) + (responseData.maxResults as number); - body.startAt = (responseData.startAt as number) + (responseData.maxResults as number); + if (method !== 'GET') { + body.startAt = (responseData.startAt as number) + (responseData.maxResults as number); + } } while ( (responseData.startAt as number) + (responseData.maxResults as number) < responseData.total diff --git a/packages/nodes-base/nodes/Jira/Jira.node.ts b/packages/nodes-base/nodes/Jira/Jira.node.ts index ad269f4fd2..8a1b3f076f 100644 --- a/packages/nodes-base/nodes/Jira/Jira.node.ts +++ b/packages/nodes-base/nodes/Jira/Jira.node.ts @@ -566,7 +566,7 @@ export class Jira implements INodeType { this, '/api/2/issuetype', 'GET', - body, + {}, qs, ); const subtaskIssues = []; @@ -690,7 +690,6 @@ export class Jira implements INodeType { this, '/api/2/issuetype', 'GET', - body, ); const subtaskIssues = []; for (const issueType of issueTypes) { @@ -1298,7 +1297,6 @@ export class Jira implements INodeType { const issueKey = this.getNodeParameter('issueKey', i) as string; const returnAll = this.getNodeParameter('returnAll', i); const options = this.getNodeParameter('options', i); - const body: IDataObject = {}; Object.assign(qs, options); if (returnAll) { responseData = await jiraSoftwareCloudApiRequestAllItems.call( @@ -1306,7 +1304,7 @@ export class Jira implements INodeType { 'comments', `/api/${apiVersion}/issue/${issueKey}/comment`, 'GET', - body, + {}, qs, ); } else { @@ -1316,7 +1314,7 @@ export class Jira implements INodeType { this, `/api/${apiVersion}/issue/${issueKey}/comment`, 'GET', - body, + {}, qs, ); responseData = responseData.comments; diff --git a/packages/nodes-base/nodes/Jira/test/GenericFunctions.test.ts b/packages/nodes-base/nodes/Jira/test/GenericFunctions.test.ts new file mode 100644 index 0000000000..4a6a9153f5 --- /dev/null +++ b/packages/nodes-base/nodes/Jira/test/GenericFunctions.test.ts @@ -0,0 +1,57 @@ +import { type DeepMockProxy, mockDeep } from 'jest-mock-extended'; +import type { IExecuteFunctions } from 'n8n-workflow'; + +import { jiraSoftwareCloudApiRequestAllItems } from '../GenericFunctions'; + +describe('Jira -> GenericFunctions', () => { + describe('jiraSoftwareCloudApiRequestAllItems', () => { + let mockExecuteFunctions: DeepMockProxy; + + beforeEach(() => { + mockExecuteFunctions = mockDeep(); + mockExecuteFunctions.getNodeParameter.mockReturnValue('server'); + mockExecuteFunctions.getCredentials.mockResolvedValue({ domain: 'jira.domain.com' }); + mockExecuteFunctions.helpers.requestWithAuthentication.mockImplementation( + async function (_, options) { + if (!options.qs?.startAt) { + return { + issues: [{ id: 1000 }, { id: 1001 }], + startAt: 0, + maxResults: 2, + total: 3, + }; + } + + return { + issues: [{ id: 1002 }], + startAt: 2, + maxResults: 2, + total: 3, + }; + }, + ); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should get all items and not pass the body when the method is GET', async () => { + const result = await jiraSoftwareCloudApiRequestAllItems.call( + mockExecuteFunctions, + 'issues', + '/api/2/search', + 'GET', + ); + + expect(result).toEqual([{ id: 1000 }, { id: 1001 }, { id: 1002 }]); + expect(mockExecuteFunctions.helpers.requestWithAuthentication).toBeCalledTimes(2); + expect(mockExecuteFunctions.helpers.requestWithAuthentication).toHaveBeenCalledWith( + 'jiraSoftwareServerApi', + expect.not.objectContaining({ + body: expect.anything(), + }), + ); + }); + }); +});