fix(Github): use x-ratelimit-remaining and 429 status code for rate limit detection

Signed-off-by: secustor <sebastian@poxhofer.at>
This commit is contained in:
secustor
2024-01-15 15:39:13 +01:00
parent 2d59a19fd3
commit e27b7f3207
4 changed files with 49 additions and 5 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/integration': minor
'@backstage/backend-common': patch
---
Fix rate limit detection by looking for HTTP status code 429 and updating the header `x-ratelimit-remaining` to look for in case of a 403 code is returned
@@ -149,10 +149,7 @@ export class GithubUrlReader implements UrlReader {
// GitHub returns a 403 response with a couple of headers indicating rate
// limit status. See more in the GitHub docs:
// https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting
if (
response.status === 403 &&
response.headers.get('X-RateLimit-Remaining') === '0'
) {
if (this.integration.isRateLimited(response)) {
message += ' (rate limit exceeded)';
}
@@ -350,10 +347,18 @@ export class GithubUrlReader implements UrlReader {
const response = await fetch(urlAsString, init);
if (!response.ok) {
const message = `Request failed for ${urlAsString}, ${response.status} ${response.statusText}`;
let message = `Request failed for ${urlAsString}, ${response.status} ${response.statusText}`;
if (response.status === 404) {
throw new NotFoundError(message);
}
// GitHub returns a 403 response with a couple of headers indicating rate
// limit status. See more in the GitHub docs:
// https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting
if (this.integration.isRateLimited(response)) {
message += ' (rate limit exceeded)';
}
throw new Error(message);
}
@@ -78,6 +78,31 @@ describe('GithubIntegration', () => {
),
).toBe('https://github.com/backstage/backstage/edit/master/README.md');
});
describe('isRateLimited', () => {
const integration = new GithubIntegration({ host: 'h.com' });
it.each`
status | ratelimitRemaining | expected
${404} | ${100} | ${false}
${429} | ${undefined} | ${true}
${429} | ${100} | ${true}
${403} | ${100} | ${false}
${403} | ${0} | ${true}
`(
'(statusCode: $status, header: $ratelimitRemaining) === $expected',
({ status, ratelimitRemaining, expected }) => {
const headers = new Headers({
'x-ratelimit-remaining': ratelimitRemaining,
});
const result = integration.isRateLimited({
status,
headers,
} as Response);
expect(expected).toBe(result);
},
);
});
});
describe('replaceGithubUrlType', () => {
@@ -65,6 +65,14 @@ export class GithubIntegration implements ScmIntegration {
resolveEditUrl(url: string): string {
return replaceGithubUrlType(url, 'edit');
}
isRateLimited(response: Response): boolean {
return (
response.status === 429 ||
(response.status === 403 &&
response.headers.get('x-ratelimit-remaining') === '0')
);
}
}
/**