diff --git a/apps/server/src/pullRequest/BitbucketPullRequestApi.test.ts b/apps/server/src/pullRequest/BitbucketPullRequestApi.test.ts index 8945ecc5e1e2..d4c03cefb907 100644 --- a/apps/server/src/pullRequest/BitbucketPullRequestApi.test.ts +++ b/apps/server/src/pullRequest/BitbucketPullRequestApi.test.ts @@ -894,6 +894,25 @@ layer("BitbucketPullRequestApi.layer", (it) => { }), ); + it.effect( + "reads a removed permissions endpoint that answers 404 as granted rather than failing the merge", + () => + Effect.gen(function* () { + mockedRequest.mockReturnValue( + Effect.fail( + new BitbucketApi.BitbucketResponseError({ + operation: "request", + status: 404, + responseBodyLength: 0, + }), + ), + ); + const api = yield* BitbucketPullRequestApi.BitbucketPullRequestApi; + + assert.isTrue(yield* api.getRepositoryPermission({ repository: "acme/web" })); + }), + ); + it.effect("still fails the permission read on a failure that is not the removed endpoint", () => Effect.gen(function* () { mockedRequest.mockReturnValue( diff --git a/apps/server/src/pullRequest/BitbucketPullRequestApi.ts b/apps/server/src/pullRequest/BitbucketPullRequestApi.ts index 5b3149b0d75c..5aac56199833 100644 --- a/apps/server/src/pullRequest/BitbucketPullRequestApi.ts +++ b/apps/server/src/pullRequest/BitbucketPullRequestApi.ts @@ -111,11 +111,12 @@ export type BitbucketPullRequestApiError = /** * `/user/permissions/repositories` answering CHANGE-2770's removal notice rather than a * permission — Bitbucket sends this for every account now, not only ones it would have refused. + * Cloud answers 404. The original CHANGE-2770 notice was 410. */ function isRepositoryPermissionRemovedError( error: BitbucketPullRequestApiError, ): error is BitbucketApi.BitbucketResponseError { - return error._tag === "BitbucketResponseError" && error.status === 410; + return error._tag === "BitbucketResponseError" && (error.status === 410 || error.status === 404); } /** @@ -578,12 +579,13 @@ export const make = Effect.gen(function* () { // may do, so this endpoint is the one request Bitbucket makes unavoidable. It is asked // alongside the reads the detail was already making, so it costs no round trip of its own. // - // Bitbucket permanently removed this endpoint (CHANGE-2770): every account now gets HTTP 410 - // in place of an answer, whatever it may do. That is the deprecated-endpoint signal, not a - // permission being refused, so it is read the same way an unreachable read already is - // elsewhere — as a permission that could not be learned, which grants rather than blocks, and - // leaves the actual merge or write to say why if the account may not do it. Any other failure - // (a bad token, a network fault, an unreadable body) still fails as it did before. + // Bitbucket permanently removed this endpoint (CHANGE-2770): every account now gets HTTP 404 + // (or 410, from the original removal notice) in place of an answer, whatever it may do. That + // is the deprecated-endpoint signal, not a permission being refused, so it is read the same + // way an unreachable read already is elsewhere — as a permission that could not be learned, + // which grants rather than blocks, and leaves the actual merge or write to say why if the + // account may not do it. Any other failure (a bad token, a network fault, an unreadable body) + // still fails as it did before. getRepositoryPermission: (input) => withRepository(input.repository, () => readPage({