fix(Jira Software Node): 403 when getting a list of items from Jira Cloud (#14782)

This commit is contained in:
RomanDavydchuk
2025-04-23 14:08:18 +03:00
committed by GitHub
parent 9f60270430
commit 1cbbcf4a77
3 changed files with 67 additions and 8 deletions

View File

@@ -88,15 +88,19 @@ export async function jiraSoftwareCloudApiRequestAllItems(
let responseData; let responseData;
query.startAt = 0; query.startAt = 0;
body.startAt = 0;
query.maxResults = 100; query.maxResults = 100;
body.maxResults = 100; if (method !== 'GET') {
body.startAt = 0;
body.maxResults = 100;
}
do { do {
responseData = await jiraSoftwareCloudApiRequest.call(this, endpoint, method, body, query); responseData = await jiraSoftwareCloudApiRequest.call(this, endpoint, method, body, query);
returnData.push.apply(returnData, responseData[propertyName] as IDataObject[]); returnData.push.apply(returnData, responseData[propertyName] as IDataObject[]);
query.startAt = (responseData.startAt as number) + (responseData.maxResults as number); 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 ( } while (
(responseData.startAt as number) + (responseData.maxResults as number) < (responseData.startAt as number) + (responseData.maxResults as number) <
responseData.total responseData.total

View File

@@ -566,7 +566,7 @@ export class Jira implements INodeType {
this, this,
'/api/2/issuetype', '/api/2/issuetype',
'GET', 'GET',
body, {},
qs, qs,
); );
const subtaskIssues = []; const subtaskIssues = [];
@@ -690,7 +690,6 @@ export class Jira implements INodeType {
this, this,
'/api/2/issuetype', '/api/2/issuetype',
'GET', 'GET',
body,
); );
const subtaskIssues = []; const subtaskIssues = [];
for (const issueType of issueTypes) { for (const issueType of issueTypes) {
@@ -1298,7 +1297,6 @@ export class Jira implements INodeType {
const issueKey = this.getNodeParameter('issueKey', i) as string; const issueKey = this.getNodeParameter('issueKey', i) as string;
const returnAll = this.getNodeParameter('returnAll', i); const returnAll = this.getNodeParameter('returnAll', i);
const options = this.getNodeParameter('options', i); const options = this.getNodeParameter('options', i);
const body: IDataObject = {};
Object.assign(qs, options); Object.assign(qs, options);
if (returnAll) { if (returnAll) {
responseData = await jiraSoftwareCloudApiRequestAllItems.call( responseData = await jiraSoftwareCloudApiRequestAllItems.call(
@@ -1306,7 +1304,7 @@ export class Jira implements INodeType {
'comments', 'comments',
`/api/${apiVersion}/issue/${issueKey}/comment`, `/api/${apiVersion}/issue/${issueKey}/comment`,
'GET', 'GET',
body, {},
qs, qs,
); );
} else { } else {
@@ -1316,7 +1314,7 @@ export class Jira implements INodeType {
this, this,
`/api/${apiVersion}/issue/${issueKey}/comment`, `/api/${apiVersion}/issue/${issueKey}/comment`,
'GET', 'GET',
body, {},
qs, qs,
); );
responseData = responseData.comments; responseData = responseData.comments;

View File

@@ -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<IExecuteFunctions>;
beforeEach(() => {
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
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(),
}),
);
});
});
});