Merge pull request #22246 from secustor/fix/rate-limit-message

fix(Github): use `x-ratelimit-remaining` and 429 status code for rate limit detection
This commit is contained in:
Fredrik Adelöw
2024-01-26 13:43:24 +01:00
committed by GitHub
9 changed files with 70 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
@@ -333,10 +333,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.parseRateLimitInfo(response).isRateLimited) {
message += ' (rate limit exceeded)';
}
+9
View File
@@ -4,6 +4,7 @@
```ts
import { Config } from '@backstage/config';
import { ConsumedResponse } from '@backstage/errors';
import { RestEndpointMethodTypes } from '@octokit/rest';
// @public
@@ -567,6 +568,8 @@ export class GithubIntegration implements ScmIntegration {
// (undocumented)
static factory: ScmIntegrationsFactory<GithubIntegration>;
// (undocumented)
parseRateLimitInfo(response: ConsumedResponse): RateLimitInfo;
// (undocumented)
resolveEditUrl(url: string): string;
// (undocumented)
resolveUrl(options: {
@@ -694,6 +697,12 @@ export type PersonalAccessTokenCredential = AzureCredentialBase & {
personalAccessToken: string;
};
// @public
export interface RateLimitInfo {
// (undocumented)
isRateLimited: boolean;
}
// @public
export function readAwsS3IntegrationConfig(
config: Config,
+1
View File
@@ -35,6 +35,7 @@
"dependencies": {
"@azure/identity": "^4.0.0",
"@backstage/config": "workspace:^",
"@backstage/errors": "workspace:^",
"@octokit/auth-app": "^4.0.0",
"@octokit/rest": "^19.0.3",
"cross-fetch": "^4.0.0",
@@ -78,6 +78,33 @@ 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.parseRateLimitInfo({
status,
headers,
} as Response);
expect(result).toMatchObject({
isRateLimited: expected,
});
},
);
});
});
describe('replaceGithubUrlType', () => {
@@ -15,11 +15,16 @@
*/
import { basicIntegrations, defaultScmResolveUrl } from '../helpers';
import { ScmIntegration, ScmIntegrationsFactory } from '../types';
import {
RateLimitInfo,
ScmIntegration,
ScmIntegrationsFactory,
} from '../types';
import {
GithubIntegrationConfig,
readGithubIntegrationConfigs,
} from './config';
import { ConsumedResponse } from '@backstage/errors';
/**
* A GitHub based integration.
@@ -65,6 +70,15 @@ export class GithubIntegration implements ScmIntegration {
resolveEditUrl(url: string): string {
return replaceGithubUrlType(url, 'edit');
}
parseRateLimitInfo(response: ConsumedResponse): RateLimitInfo {
return {
isRateLimited:
response.status === 429 ||
(response.status === 403 &&
response.headers.get('x-ratelimit-remaining') === '0'),
};
}
}
/**
+1
View File
@@ -37,5 +37,6 @@ export type {
ScmIntegration,
ScmIntegrationsFactory,
ScmIntegrationsGroup,
RateLimitInfo,
} from './types';
export type { ScmIntegrationRegistry } from './registry';
+9
View File
@@ -108,3 +108,12 @@ export interface ScmIntegrationsGroup<T extends ScmIntegration> {
export type ScmIntegrationsFactory<T extends ScmIntegration> = (options: {
config: Config;
}) => ScmIntegrationsGroup<T>;
/**
* Encapsulates information about the RateLimit state
*
* @public
*/
export interface RateLimitInfo {
isRateLimited: boolean;
}
+1
View File
@@ -4147,6 +4147,7 @@ __metadata:
"@backstage/cli": "workspace:^"
"@backstage/config": "workspace:^"
"@backstage/config-loader": "workspace:^"
"@backstage/errors": "workspace:^"
"@octokit/auth-app": ^4.0.0
"@octokit/rest": ^19.0.3
"@types/luxon": ^3.0.0