From 013b705f43d5679099f508f3252d77e6ec3a4811 Mon Sep 17 00:00:00 2001 From: Adam Setch <adam.setch@outlook.com> Date: Thu, 16 Feb 2023 01:36:28 -0500 Subject: [PATCH] feat: bitbucket sanitize reviewers (#20445) --- lib/modules/platform/bitbucket/index.spec.ts | 68 ++++++++++++++++---- lib/modules/platform/bitbucket/index.ts | 48 +++++++++----- 2 files changed, 90 insertions(+), 26 deletions(-) diff --git a/lib/modules/platform/bitbucket/index.spec.ts b/lib/modules/platform/bitbucket/index.spec.ts index fdce165e05..2e8cd56de5 100644 --- a/lib/modules/platform/bitbucket/index.spec.ts +++ b/lib/modules/platform/bitbucket/index.spec.ts @@ -748,7 +748,7 @@ describe('modules/platform/bitbucket/index', () => { expect(pr?.number).toBe(5); }); - it('removes inactive reviewers when updating pr', async () => { + it('removes inactive reviewers when creating pr', async () => { const inactiveReviewer = { user: { display_name: 'Bob Smith', @@ -756,7 +756,14 @@ describe('modules/platform/bitbucket/index', () => { account_id: '123', }, }; - const activeReviewer = { + const activeReviewerOutsideOfWorkspace = { + user: { + display_name: 'Alice Smith', + uuid: '{a10e0228-ad84-11ed-afa1-0242ac120002}', + account_id: '789', + }, + }; + const activeReviewerWithinWorkspace = { user: { display_name: 'Jane Smith', uuid: '{90b6646d-1724-4a64-9fd9-539515fe94e9}', @@ -767,7 +774,11 @@ describe('modules/platform/bitbucket/index', () => { scope .get('/2.0/repositories/some/repo/effective-default-reviewers') .reply(200, { - values: [activeReviewer, inactiveReviewer], + values: [ + activeReviewerWithinWorkspace, + activeReviewerOutsideOfWorkspace, + inactiveReviewer, + ], }) .post('/2.0/repositories/some/repo/pullrequests') .reply(400, { @@ -779,14 +790,26 @@ describe('modules/platform/bitbucket/index', () => { message: 'reviewers: Malformed reviewers list', }, }) - .get('/2.0/users/%7Bd2238482-2e9f-48b3-8630-de22ccb9e42f%7D') + .get('/2.0/users/%7B90b6646d-1724-4a64-9fd9-539515fe94e9%7D') .reply(200, { - account_status: 'inactive', + account_status: 'active', }) - .get('/2.0/users/%7B90b6646d-1724-4a64-9fd9-539515fe94e9%7D') + .get( + '/2.0/workspaces/some/members/%7B90b6646d-1724-4a64-9fd9-539515fe94e9%7D' + ) + .reply(200) + .get('/2.0/users/%7Ba10e0228-ad84-11ed-afa1-0242ac120002%7D') .reply(200, { account_status: 'active', }) + .get( + '/2.0/workspaces/some/members/%7Ba10e0228-ad84-11ed-afa1-0242ac120002%7D' + ) + .reply(404) + .get('/2.0/users/%7Bd2238482-2e9f-48b3-8630-de22ccb9e42f%7D') + .reply(200, { + account_status: 'inactive', + }) .post('/2.0/repositories/some/repo/pullrequests') .reply(200, { id: 5 }); const pr = await bitbucket.createPr({ @@ -1065,7 +1088,12 @@ describe('modules/platform/bitbucket/index', () => { uuid: '{d2238482-2e9f-48b3-8630-de22ccb9e42f}', account_id: '123', }; - const activeReviewer = { + const activeReviewerOutsideOfWorkspace = { + display_name: 'Alice Smith', + uuid: '{a10e0228-ad84-11ed-afa1-0242ac120002}', + account_id: '789', + }; + const activeReviewerWithinWorkspace = { display_name: 'Jane Smith', uuid: '{90b6646d-1724-4a64-9fd9-539515fe94e9}', account_id: '456', @@ -1073,7 +1101,13 @@ describe('modules/platform/bitbucket/index', () => { const scope = await initRepoMock(); scope .get('/2.0/repositories/some/repo/pullrequests/5') - .reply(200, { reviewers: [activeReviewer, inactiveReviewer] }) + .reply(200, { + reviewers: [ + activeReviewerWithinWorkspace, + activeReviewerOutsideOfWorkspace, + inactiveReviewer, + ], + }) .put('/2.0/repositories/some/repo/pullrequests/5') .reply(400, { type: 'error', @@ -1084,14 +1118,26 @@ describe('modules/platform/bitbucket/index', () => { message: 'reviewers: Malformed reviewers list', }, }) - .get('/2.0/users/%7Bd2238482-2e9f-48b3-8630-de22ccb9e42f%7D') + .get('/2.0/users/%7B90b6646d-1724-4a64-9fd9-539515fe94e9%7D') .reply(200, { - account_status: 'inactive', + account_status: 'active', }) - .get('/2.0/users/%7B90b6646d-1724-4a64-9fd9-539515fe94e9%7D') + .get( + '/2.0/workspaces/some/members/%7B90b6646d-1724-4a64-9fd9-539515fe94e9%7D' + ) + .reply(200) + .get('/2.0/users/%7Ba10e0228-ad84-11ed-afa1-0242ac120002%7D') .reply(200, { account_status: 'active', }) + .get( + '/2.0/workspaces/some/members/%7Ba10e0228-ad84-11ed-afa1-0242ac120002%7D' + ) + .reply(404) + .get('/2.0/users/%7Bd2238482-2e9f-48b3-8630-de22ccb9e42f%7D') + .reply(200, { + account_status: 'inactive', + }) .put('/2.0/repositories/some/repo/pullrequests/5') .reply(200); await expect( diff --git a/lib/modules/platform/bitbucket/index.ts b/lib/modules/platform/bitbucket/index.ts index e8b79da387..589557487c 100644 --- a/lib/modules/platform/bitbucket/index.ts +++ b/lib/modules/platform/bitbucket/index.ts @@ -675,7 +675,7 @@ async function sanitizeReviewers( if (msg === 'Malformed reviewers list') { logger.debug( { err }, - 'PR contains inactive reviewer accounts. Will try setting only active reviewers' + 'PR contains reviewers that may be either inactive or no longer a member of this workspace. Will try setting only active reviewers' ); // Validate that each previous PR reviewer account is still active @@ -685,10 +685,12 @@ async function sanitizeReviewers( ).body; if (reviewerUser.account_status === 'active') { - sanitizedReviewers.push(reviewer); + // There are cases where an active user may still not be a member of a workspace + if (await isAccountMemberOfWorkspace(reviewer, config.repository)) { + sanitizedReviewers.push(reviewer); + } } } - // Bitbucket returns a 400 if any of the PR reviewer accounts are no longer members of this workspace } else if ( msg.endsWith( @@ -700,21 +702,10 @@ async function sanitizeReviewers( 'PR contains reviewer accounts which are no longer member of this workspace. Will try setting only member reviewers' ); - const workspace = config.repository.split('/')[0]; - // Validate that each previous PR reviewer account is still a member of this workspace for (const reviewer of reviewers) { - try { - await bitbucketHttp.get( - `/2.0/workspaces/${workspace}/members/${reviewer.uuid}` - ); - + if (await isAccountMemberOfWorkspace(reviewer, config.repository)) { sanitizedReviewers.push(reviewer); - } catch (err) { - // HTTP 404: User cannot be found, or the user is not a member of this workspace. - if (err.response?.statusCode !== 404) { - throw err; - } } } } else { @@ -728,6 +719,33 @@ async function sanitizeReviewers( return undefined; } +async function isAccountMemberOfWorkspace( + reviewer: Account, + repository: string +): Promise<boolean> { + const workspace = repository.split('/')[0]; + + try { + await bitbucketHttp.get( + `/2.0/workspaces/${workspace}/members/${reviewer.uuid}` + ); + + return true; + } catch (err) { + // HTTP 404: User cannot be found, or the user is not a member of this workspace. + if (err.statusCode === 404) { + logger.debug( + { err }, + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + `User ${reviewer.display_name} is not a member of the workspace ${workspace}. Will be removed from the PR` + ); + + return false; + } + throw err; + } +} + // Creates PR and returns PR number export async function createPr({ sourceBranch, -- GitLab