feat(catalog): add retries to octokit client

Signed-off-by: wpessers <warre.pessers@gmail.com>
This commit is contained in:
wpessers
2026-03-27 14:54:37 +01:00
parent b9fec1bdc8
commit cca9fc2bb6
7 changed files with 17 additions and 6 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend-module-github': patch
---
Added automatic retry on temporary errors (like 5XX) to the shared GitHub GraphQL client used by `GithubOrgEntityProvider` and replaced the GraphQL client in `GithubEntityProvider` by this one as well, improving resilience against intermittent GitHub API failures.
@@ -63,6 +63,7 @@
"@octokit/auth-callback": "^5.0.0",
"@octokit/core": "^5.2.0",
"@octokit/graphql": "^7.0.2",
"@octokit/plugin-retry": "^6.0.0",
"@octokit/plugin-throttling": "^8.1.3",
"@octokit/rest": "^19.0.3",
"@octokit/webhooks-types": "^7.6.1",
@@ -41,6 +41,7 @@ import {
} from './github';
import { Octokit } from '@octokit/core';
import { throttling } from '@octokit/plugin-throttling';
import { retry } from '@octokit/plugin-retry';
jest.mock('@octokit/core', () => ({
...jest.requireActual('@octokit/core'),
@@ -1011,7 +1012,7 @@ describe('github', () => {
});
it('should return a graphql client with throttling', async () => {
expect(client).toBeDefined();
expect(Octokit.plugin).toHaveBeenCalledWith(throttling);
expect(Octokit.plugin).toHaveBeenCalledWith(throttling, retry);
});
it('should return a graphql client with the correct options', async () => {
@@ -30,6 +30,7 @@ import { DeferredEntity } from '@backstage/plugin-catalog-node';
import { Octokit } from '@octokit/core';
import { LoggerService } from '@backstage/backend-plugin-api';
import { throttling } from '@octokit/plugin-throttling';
import { retry } from '@octokit/plugin-retry';
/**
* Configuration for GitHub GraphQL API page sizes.
@@ -874,7 +875,7 @@ export const createReplaceEntitiesOperation =
};
/**
* Creates a GraphQL Client with Throttling
* Creates a GraphQL Client with Throttling and Retries
*/
export const createGraphqlClient = (args: {
headers:
@@ -886,7 +887,7 @@ export const createGraphqlClient = (args: {
logger: LoggerService;
}): typeof graphql => {
const { headers, baseUrl, logger } = args;
const ThrottledOctokit = Octokit.plugin(throttling);
const ThrottledOctokit = Octokit.plugin(throttling, retry);
const octokit = new ThrottledOctokit({
throttle: {
onRateLimit: (retryAfter, rateLimitData, _, retryCount) => {
@@ -47,6 +47,7 @@ type PartialDeep<T> = T extends (...args: unknown[]) => unknown
jest.mock('../lib/github', () => {
return {
getOrganizationRepositories: jest.fn(),
createGraphqlClient: jest.fn().mockReturnValue(jest.fn()),
};
});
class PersistingTaskRunner implements SchedulerServiceTaskRunner {
@@ -32,13 +32,13 @@ import {
import { LocationSpec } from '@backstage/plugin-catalog-common';
import { graphql } from '@octokit/graphql';
import * as uuid from 'uuid';
import {
GithubEntityProviderConfig,
readProviderConfigs,
} from './GithubEntityProviderConfig';
import {
createGraphqlClient,
getOrganizationRepositories,
getOrganizationRepository,
RepositoryResponse,
@@ -249,9 +249,10 @@ export class GithubEntityProvider implements EntityProvider, EventSubscriber {
url: orgUrl,
});
return graphql.defaults({
baseUrl: this.integration.apiBaseUrl,
return createGraphqlClient({
headers,
baseUrl: this.integration.apiBaseUrl!,
logger: this.logger,
});
}
+1
View File
@@ -4916,6 +4916,7 @@ __metadata:
"@octokit/auth-callback": "npm:^5.0.0"
"@octokit/core": "npm:^5.2.0"
"@octokit/graphql": "npm:^7.0.2"
"@octokit/plugin-retry": "npm:^6.0.0"
"@octokit/plugin-throttling": "npm:^8.1.3"
"@octokit/rest": "npm:^19.0.3"
"@octokit/webhooks-types": "npm:^7.6.1"