diff --git a/backend/package-lock.json b/backend/package-lock.json index ffe9cfcb0..71d994f2e 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -25,6 +25,7 @@ "@fastify/swagger": "^8.14.0", "@fastify/swagger-ui": "^2.1.0", "@node-saml/passport-saml": "^4.0.4", + "@octokit/plugin-retry": "^5.0.5", "@octokit/rest": "^20.0.2", "@octokit/webhooks-types": "^7.3.1", "@peculiar/asn1-schema": "^2.3.8", @@ -7812,19 +7813,45 @@ } }, "node_modules/@octokit/plugin-retry": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.0.1.tgz", - "integrity": "sha512-SKs+Tz9oj0g4p28qkZwl/topGcb0k0qPNX/i7vBKmDsjoeqnVfFUquqrE/O9oJY7+oLzdCtkiWSXLpLjvl6uog==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-5.0.5.tgz", + "integrity": "sha512-sB1RWMhSrre02Atv95K6bhESlJ/sPdZkK/wE/w1IdSCe0yM6FxSjksLa6T7aAvxvxlLKzQEC4KIiqpqyov1Tbg==", "dependencies": { - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^12.0.0", + "@octokit/request-error": "^4.0.1", + "@octokit/types": "^10.0.0", "bottleneck": "^2.15.3" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "@octokit/core": ">=5" + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-retry/node_modules/@octokit/openapi-types": { + "version": "18.1.1", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.1.1.tgz", + "integrity": "sha512-VRaeH8nCDtF5aXWnjPuEMIYf1itK/s3JYyJcWFJT8X9pSNnBtriDf7wlEWsGuhPLl4QIH4xM8fqTXDwJ3Mu6sw==" + }, + "node_modules/@octokit/plugin-retry/node_modules/@octokit/request-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-4.0.2.tgz", + "integrity": "sha512-uqwUEmZw3x4I9DGYq9fODVAAvcLsPQv97NRycP6syEFu5916M189VnNBW2zANNwqg3OiligNcAey7P0SET843w==", + "dependencies": { + "@octokit/types": "^10.0.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/plugin-retry/node_modules/@octokit/types": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-10.0.0.tgz", + "integrity": "sha512-Vm8IddVmhCgU1fxC1eyinpwqzXPEYu0NrYzD3YZjlGjyftdLBTeqNblRC0jmJmgxbJIsQlyogVeGnrNaaMVzIg==", + "dependencies": { + "@octokit/openapi-types": "^18.0.0" } }, "node_modules/@octokit/plugin-throttling": { @@ -17396,6 +17423,22 @@ "node": ">=18" } }, + "node_modules/probot/node_modules/@octokit/plugin-retry": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.0.1.tgz", + "integrity": "sha512-SKs+Tz9oj0g4p28qkZwl/topGcb0k0qPNX/i7vBKmDsjoeqnVfFUquqrE/O9oJY7+oLzdCtkiWSXLpLjvl6uog==", + "dependencies": { + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=5" + } + }, "node_modules/probot/node_modules/commander": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", diff --git a/backend/package.json b/backend/package.json index 1328db4dd..4ea217808 100644 --- a/backend/package.json +++ b/backend/package.json @@ -121,6 +121,7 @@ "@fastify/swagger": "^8.14.0", "@fastify/swagger-ui": "^2.1.0", "@node-saml/passport-saml": "^4.0.4", + "@octokit/plugin-retry": "^5.0.5", "@octokit/rest": "^20.0.2", "@octokit/webhooks-types": "^7.3.1", "@peculiar/asn1-schema": "^2.3.8", diff --git a/backend/src/services/integration-auth/integration-delete-secret.ts b/backend/src/services/integration-auth/integration-delete-secret.ts index 2a33677dd..5da48fb6e 100644 --- a/backend/src/services/integration-auth/integration-delete-secret.ts +++ b/backend/src/services/integration-auth/integration-delete-secret.ts @@ -1,3 +1,4 @@ +import { retry } from "@octokit/plugin-retry"; import { Octokit } from "@octokit/rest"; import { TIntegrationAuths, TIntegrations } from "@app/db/schemas"; @@ -79,7 +80,7 @@ const getIntegrationSecretsV2 = async ( * Return the secrets in a given [folderId] including secrets from * nested imported folders recursively. */ -const getIntegrationSecrets = async ( +const getIntegrationSecretsV1 = async ( dto: { projectId: string; environment: string; @@ -129,7 +130,7 @@ const getIntegrationSecrets = async ( if (folder) { // get secrets contained in each imported folder by recursively calling // this function against the imported folder - const importedSecrets = await getIntegrationSecrets( + const importedSecrets = await getIntegrationSecretsV1( { environment: dto.environment, projectId: dto.projectId, @@ -167,7 +168,8 @@ export const deleteGithubSecrets = async ({ selected_repositories_url?: string | undefined; } - const octokit = new Octokit({ + const OctokitWithRetry = Octokit.plugin(retry); + const octokit = new OctokitWithRetry({ auth: accessToken }); @@ -177,11 +179,11 @@ export const deleteGithubSecrets = async ({ Env = "github-env" } - let encryptedSecrets: GitHubSecret[]; + let encryptedGithubSecrets: GitHubSecret[]; switch (integration.scope) { case GithubScope.Org: { - encryptedSecrets = ( + encryptedGithubSecrets = ( await octokit.request("GET /orgs/{org}/actions/secrets", { org: integration.owner as string }) @@ -189,7 +191,7 @@ export const deleteGithubSecrets = async ({ break; } case GithubScope.Env: { - encryptedSecrets = ( + encryptedGithubSecrets = ( await octokit.request("GET /repositories/{repository_id}/environments/{environment_name}/secrets", { repository_id: Number(integration.appId), environment_name: integration.targetEnvironmentId as string @@ -198,7 +200,7 @@ export const deleteGithubSecrets = async ({ break; } default: { - encryptedSecrets = ( + encryptedGithubSecrets = ( await octokit.request("GET /repos/{owner}/{repo}/actions/secrets", { owner: integration.owner as string, repo: integration.app as string @@ -208,7 +210,7 @@ export const deleteGithubSecrets = async ({ } } - for await (const encryptedSecret of encryptedSecrets) { + for await (const encryptedSecret of encryptedGithubSecrets) { if (encryptedSecret.name in secrets) { switch (integration.scope) { case GithubScope.Org: { @@ -238,6 +240,11 @@ export const deleteGithubSecrets = async ({ break; } } + + // small delay to prevent hitting API rate limits + await new Promise((resolve) => { + setTimeout(resolve, 50); + }); } } }; @@ -308,7 +315,7 @@ export const deleteIntegrationSecrets = async ({ folderDAL, secretImportDAL ) - : await getIntegrationSecrets( + : await getIntegrationSecretsV1( { environment: integration.environment.id, projectId: integration.projectId,