From f8f5000bad0cebc7f9566d0e3891ef543140493e Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Mon, 16 Dec 2024 22:20:59 +0800 Subject: [PATCH] misc: addressed review comments --- .../integration-auth-service.ts | 66 +++++++------------ .../integration-auth-types.ts | 5 ++ .../integration-auth/integration-list.ts | 2 - .../integration-sync-secret.ts | 45 ++++++------- .../src/hooks/api/integrationAuth/types.ts | 5 ++ .../pages/integrations/circleci/create.tsx | 15 +++-- .../IntegrationConnectionSection.tsx | 3 +- .../components/IntegrationDetails.tsx | 3 +- 8 files changed, 66 insertions(+), 78 deletions(-) diff --git a/backend/src/services/integration-auth/integration-auth-service.ts b/backend/src/services/integration-auth/integration-auth-service.ts index 30be70d11..7c68a2640 100644 --- a/backend/src/services/integration-auth/integration-auth-service.ts +++ b/backend/src/services/integration-auth/integration-auth-service.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-await-in-loop */ import { ForbiddenError } from "@casl/ability"; import { createAppAuth } from "@octokit/auth-app"; import { Octokit } from "@octokit/rest"; @@ -12,6 +11,7 @@ import { getConfig } from "@app/lib/config/env"; import { request } from "@app/lib/config/request"; import { decryptSymmetric128BitHexKeyUTF8, encryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto"; import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors"; +import { groupBy } from "@app/lib/fn"; import { logger } from "@app/lib/logger"; import { TGenericPermission, TProjectPermission } from "@app/lib/types"; @@ -1634,58 +1634,40 @@ export const integrationAuthServiceFactory = ({ logger.error(error); } - const projectsByOrg = projects.reduce>((accum, project) => { - if (!accum[project.orgName]) { - return { - ...accum, - [project.orgName]: [ - { - name: project.projectName, - id: project.projectId as string - } - ] - }; - } - return { - ...accum, - [project.orgName]: [ - ...accum[project.orgName], - { - name: project.projectName, - id: project.projectId as string - } - ] - }; - }, {}); + const projectsByOrg = groupBy( + projects.map((p) => ({ + orgName: p.orgName, + name: p.projectName, + id: p.projectId + })), + (p) => p.orgName + ); const getOrgContexts = async (orgSlug: string) => { type NextPageToken = string | null | undefined; - type CircleCIContextResponse = { - items: TCircleCIContext[]; - next_page_token: NextPageToken; - }; - try { const contexts: TCircleCIContext[] = []; let nextPageToken: NextPageToken; while (nextPageToken !== null) { - const res = ( - await request.get(`${IntegrationUrls.CIRCLECI_API_URL}/v2/context`, { - headers: { - "Circle-Token": accessToken, - "Accept-Encoding": "application/json" - }, - params: new URLSearchParams({ - "owner-slug": orgSlug, - ...(nextPageToken ? { "page-token": nextPageToken } : {}) - }) + // eslint-disable-next-line no-await-in-loop + const { data } = await request.get<{ + items: TCircleCIContext[]; + next_page_token: NextPageToken; + }>(`${IntegrationUrls.CIRCLECI_API_URL}/v2/context`, { + headers: { + "Circle-Token": accessToken, + "Accept-Encoding": "application/json" + }, + params: new URLSearchParams({ + "owner-slug": orgSlug, + ...(nextPageToken ? { "page-token": nextPageToken } : {}) }) - ).data; + }); - contexts.push(...res.items); - nextPageToken = res.next_page_token; + contexts.push(...data.items); + nextPageToken = data.next_page_token; } return contexts?.map((context) => ({ diff --git a/backend/src/services/integration-auth/integration-auth-types.ts b/backend/src/services/integration-auth/integration-auth-types.ts index 751af0e79..68d7bf5b9 100644 --- a/backend/src/services/integration-auth/integration-auth-types.ts +++ b/backend/src/services/integration-auth/integration-auth-types.ts @@ -227,6 +227,11 @@ export enum OctopusDeployScope { // add tenant, variable set, etc. } +export enum CircleCiScope { + Project = "project", + Context = "context" +} + export type TOctopusDeployVariableSet = { Id: string; OwnerId: string; diff --git a/backend/src/services/integration-auth/integration-list.ts b/backend/src/services/integration-auth/integration-list.ts index b18c07e7b..d6da2194d 100644 --- a/backend/src/services/integration-auth/integration-list.ts +++ b/backend/src/services/integration-auth/integration-list.ts @@ -76,8 +76,6 @@ export enum IntegrationUrls { RAILWAY_API_URL = "https://backboard.railway.app/graphql/v2", FLYIO_API_URL = "https://api.fly.io/graphql", CIRCLECI_API_URL = "https://circleci.com/api", - // eslint-disable-next-line - DATABRICKS_API_URL = "https:/xxxx.com/api", TRAVISCI_API_URL = "https://api.travis-ci.com", SUPABASE_API_URL = "https://api.supabase.com", LARAVELFORGE_API_URL = "https://forge.laravel.com", diff --git a/backend/src/services/integration-auth/integration-sync-secret.ts b/backend/src/services/integration-auth/integration-sync-secret.ts index a16b04be6..cd8b8baea 100644 --- a/backend/src/services/integration-auth/integration-sync-secret.ts +++ b/backend/src/services/integration-auth/integration-sync-secret.ts @@ -39,7 +39,12 @@ import { TCreateManySecretsRawFn, TUpdateManySecretsRawFn } from "@app/services/ import { TIntegrationDALFactory } from "../integration/integration-dal"; import { IntegrationMetadataSchema } from "../integration/integration-schema"; import { IntegrationAuthMetadataSchema } from "./integration-auth-schema"; -import { OctopusDeployScope, TIntegrationsWithEnvironment, TOctopusDeployVariableSet } from "./integration-auth-types"; +import { + CircleCiScope, + OctopusDeployScope, + TIntegrationsWithEnvironment, + TOctopusDeployVariableSet +} from "./integration-auth-types"; import { IntegrationInitialSyncBehavior, IntegrationMappingBehavior, @@ -2245,11 +2250,6 @@ const syncSecretsCircleCI = async ({ secrets: Record; accessToken: string; }) => { - enum CircleCiScope { - Project = "project", - Context = "context" - } - if (integration.scope === CircleCiScope.Context) { // sync secrets to CircleCI await Promise.all( @@ -2278,29 +2278,24 @@ const syncSecretsCircleCI = async ({ context_id: string; }; - type ResponseSchema = { - items: EnvVars[]; - next_page_token: string | null; - }; - let nextPageToken: string | null | undefined; const envVars: EnvVars[] = []; while (nextPageToken !== null) { - const res = await request.get( - `${IntegrationUrls.CIRCLECI_API_URL}/v2/context/${integration.appId}/environment-variable`, - { - headers: { - "Circle-Token": accessToken, - "Accept-Encoding": "application/json" - }, - params: nextPageToken - ? new URLSearchParams({ - "page-token": nextPageToken - }) - : undefined - } - ); + const res = await request.get<{ + items: EnvVars[]; + next_page_token: string | null; + }>(`${IntegrationUrls.CIRCLECI_API_URL}/v2/context/${integration.appId}/environment-variable`, { + headers: { + "Circle-Token": accessToken, + "Accept-Encoding": "application/json" + }, + params: nextPageToken + ? new URLSearchParams({ + "page-token": nextPageToken + }) + : undefined + }); envVars.push(...res.data.items); nextPageToken = res.data.next_page_token; diff --git a/frontend/src/hooks/api/integrationAuth/types.ts b/frontend/src/hooks/api/integrationAuth/types.ts index 6b20989fe..e2dee6067 100644 --- a/frontend/src/hooks/api/integrationAuth/types.ts +++ b/frontend/src/hooks/api/integrationAuth/types.ts @@ -138,3 +138,8 @@ export type TOctopusDeployVariableSetScopeValues = { Name: string; }[]; }; + +export enum CircleCiScope { + Context = "context", + Project = "project" +} diff --git a/frontend/src/pages/integrations/circleci/create.tsx b/frontend/src/pages/integrations/circleci/create.tsx index 37a26da6c..c48036368 100644 --- a/frontend/src/pages/integrations/circleci/create.tsx +++ b/frontend/src/pages/integrations/circleci/create.tsx @@ -22,17 +22,18 @@ import { SecretPathInput } from "@app/components/v2/SecretPathInput"; import { useWorkspace } from "@app/context"; import { useCreateIntegration } from "@app/hooks/api"; import { useGetIntegrationAuthCircleCIOrganizations } from "@app/hooks/api/integrationAuth"; +import { CircleCiScope } from "@app/hooks/api/integrationAuth/types"; const formSchema = z.discriminatedUnion("scope", [ z.object({ - scope: z.literal("context"), + scope: z.literal(CircleCiScope.Context), secretPath: z.string().default("/"), sourceEnvironment: z.object({ name: z.string(), slug: z.string() }), targetOrg: z.object({ name: z.string().min(1), slug: z.string().min(1) }), targetContext: z.object({ name: z.string().min(1), id: z.string().min(1) }) }), z.object({ - scope: z.literal("project"), + scope: z.literal(CircleCiScope.Project), secretPath: z.string().default("/"), sourceEnvironment: z.object({ name: z.string(), slug: z.string() }), targetOrg: z.object({ name: z.string().min(1), slug: z.string().min(1) }), @@ -69,7 +70,7 @@ export default function CircleCICreateIntegrationPage() { const onSubmit = async (data: TFormData) => { try { - if (data.scope === "context") { + if (data.scope === CircleCiScope.Context) { await mutateAsync({ scope: data.scope, integrationAuthId, @@ -231,13 +232,13 @@ export default function CircleCICreateIntegrationPage() { }} className="w-full border border-mineshaft-500" > - Project - Context + Project + Context )} /> - {selectedScope === "context" && selectedOrganizationEntry && ( + {selectedScope === CircleCiScope.Context && selectedOrganizationEntry && ( )} - {selectedScope === "project" && selectedOrganizationEntry && ( + {selectedScope === CircleCiScope.Project && selectedOrganizationEntry && ( { case "qovery": return integration.scope; case "circleci": - if (integration.scope === "context") { + if (integration.scope === CircleCiScope.Context) { return "Context"; } diff --git a/frontend/src/views/IntegrationsPage/components/IntegrationsSection/components/IntegrationDetails.tsx b/frontend/src/views/IntegrationsPage/components/IntegrationsSection/components/IntegrationDetails.tsx index fee7f9100..4e31cdedb 100644 --- a/frontend/src/views/IntegrationsPage/components/IntegrationsSection/components/IntegrationDetails.tsx +++ b/frontend/src/views/IntegrationsPage/components/IntegrationsSection/components/IntegrationDetails.tsx @@ -1,4 +1,5 @@ import { FormLabel } from "@app/components/v2"; +import { CircleCiScope } from "@app/hooks/api/integrationAuth/types"; import { IntegrationMappingBehavior, TIntegration } from "@app/hooks/api/integrations/types"; type Props = { @@ -53,7 +54,7 @@ export const IntegrationDetails = ({ integration }: Props) => { label={ (integration.integration === "qovery" && integration?.scope) || (integration.integration === "circleci" && - (integration.scope === "context" ? "Context" : "Project")) || + (integration.scope === CircleCiScope.Context ? "Context" : "Project")) || (integration.integration === "bitbucket" && "Repository") || (integration.integration === "octopus-deploy" && "Project") || (integration.integration === "aws-secret-manager" && "Secret") ||