misc: addressed review comments

This commit is contained in:
Sheen Capadngan
2024-12-16 22:20:59 +08:00
parent 40919ccf59
commit f8f5000bad
8 changed files with 66 additions and 78 deletions

View File

@@ -1,4 +1,3 @@
/* eslint-disable no-await-in-loop */
import { ForbiddenError } from "@casl/ability"; import { ForbiddenError } from "@casl/ability";
import { createAppAuth } from "@octokit/auth-app"; import { createAppAuth } from "@octokit/auth-app";
import { Octokit } from "@octokit/rest"; import { Octokit } from "@octokit/rest";
@@ -12,6 +11,7 @@ import { getConfig } from "@app/lib/config/env";
import { request } from "@app/lib/config/request"; import { request } from "@app/lib/config/request";
import { decryptSymmetric128BitHexKeyUTF8, encryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto"; import { decryptSymmetric128BitHexKeyUTF8, encryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
import { groupBy } from "@app/lib/fn";
import { logger } from "@app/lib/logger"; import { logger } from "@app/lib/logger";
import { TGenericPermission, TProjectPermission } from "@app/lib/types"; import { TGenericPermission, TProjectPermission } from "@app/lib/types";
@@ -1634,58 +1634,40 @@ export const integrationAuthServiceFactory = ({
logger.error(error); logger.error(error);
} }
const projectsByOrg = projects.reduce<Record<string, { name: string; id: string }[]>>((accum, project) => { const projectsByOrg = groupBy(
if (!accum[project.orgName]) { projects.map((p) => ({
return { orgName: p.orgName,
...accum, name: p.projectName,
[project.orgName]: [ id: p.projectId
{ })),
name: project.projectName, (p) => p.orgName
id: project.projectId as string );
}
]
};
}
return {
...accum,
[project.orgName]: [
...accum[project.orgName],
{
name: project.projectName,
id: project.projectId as string
}
]
};
}, {});
const getOrgContexts = async (orgSlug: string) => { const getOrgContexts = async (orgSlug: string) => {
type NextPageToken = string | null | undefined; type NextPageToken = string | null | undefined;
type CircleCIContextResponse = {
items: TCircleCIContext[];
next_page_token: NextPageToken;
};
try { try {
const contexts: TCircleCIContext[] = []; const contexts: TCircleCIContext[] = [];
let nextPageToken: NextPageToken; let nextPageToken: NextPageToken;
while (nextPageToken !== null) { while (nextPageToken !== null) {
const res = ( // eslint-disable-next-line no-await-in-loop
await request.get<CircleCIContextResponse>(`${IntegrationUrls.CIRCLECI_API_URL}/v2/context`, { const { data } = await request.get<{
headers: { items: TCircleCIContext[];
"Circle-Token": accessToken, next_page_token: NextPageToken;
"Accept-Encoding": "application/json" }>(`${IntegrationUrls.CIRCLECI_API_URL}/v2/context`, {
}, headers: {
params: new URLSearchParams({ "Circle-Token": accessToken,
"owner-slug": orgSlug, "Accept-Encoding": "application/json"
...(nextPageToken ? { "page-token": nextPageToken } : {}) },
}) params: new URLSearchParams({
"owner-slug": orgSlug,
...(nextPageToken ? { "page-token": nextPageToken } : {})
}) })
).data; });
contexts.push(...res.items); contexts.push(...data.items);
nextPageToken = res.next_page_token; nextPageToken = data.next_page_token;
} }
return contexts?.map((context) => ({ return contexts?.map((context) => ({

View File

@@ -227,6 +227,11 @@ export enum OctopusDeployScope {
// add tenant, variable set, etc. // add tenant, variable set, etc.
} }
export enum CircleCiScope {
Project = "project",
Context = "context"
}
export type TOctopusDeployVariableSet = { export type TOctopusDeployVariableSet = {
Id: string; Id: string;
OwnerId: string; OwnerId: string;

View File

@@ -76,8 +76,6 @@ export enum IntegrationUrls {
RAILWAY_API_URL = "https://backboard.railway.app/graphql/v2", RAILWAY_API_URL = "https://backboard.railway.app/graphql/v2",
FLYIO_API_URL = "https://api.fly.io/graphql", FLYIO_API_URL = "https://api.fly.io/graphql",
CIRCLECI_API_URL = "https://circleci.com/api", 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", TRAVISCI_API_URL = "https://api.travis-ci.com",
SUPABASE_API_URL = "https://api.supabase.com", SUPABASE_API_URL = "https://api.supabase.com",
LARAVELFORGE_API_URL = "https://forge.laravel.com", LARAVELFORGE_API_URL = "https://forge.laravel.com",

View File

@@ -39,7 +39,12 @@ import { TCreateManySecretsRawFn, TUpdateManySecretsRawFn } from "@app/services/
import { TIntegrationDALFactory } from "../integration/integration-dal"; import { TIntegrationDALFactory } from "../integration/integration-dal";
import { IntegrationMetadataSchema } from "../integration/integration-schema"; import { IntegrationMetadataSchema } from "../integration/integration-schema";
import { IntegrationAuthMetadataSchema } from "./integration-auth-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 { import {
IntegrationInitialSyncBehavior, IntegrationInitialSyncBehavior,
IntegrationMappingBehavior, IntegrationMappingBehavior,
@@ -2245,11 +2250,6 @@ const syncSecretsCircleCI = async ({
secrets: Record<string, { value: string; comment?: string }>; secrets: Record<string, { value: string; comment?: string }>;
accessToken: string; accessToken: string;
}) => { }) => {
enum CircleCiScope {
Project = "project",
Context = "context"
}
if (integration.scope === CircleCiScope.Context) { if (integration.scope === CircleCiScope.Context) {
// sync secrets to CircleCI // sync secrets to CircleCI
await Promise.all( await Promise.all(
@@ -2278,29 +2278,24 @@ const syncSecretsCircleCI = async ({
context_id: string; context_id: string;
}; };
type ResponseSchema = {
items: EnvVars[];
next_page_token: string | null;
};
let nextPageToken: string | null | undefined; let nextPageToken: string | null | undefined;
const envVars: EnvVars[] = []; const envVars: EnvVars[] = [];
while (nextPageToken !== null) { while (nextPageToken !== null) {
const res = await request.get<ResponseSchema>( const res = await request.get<{
`${IntegrationUrls.CIRCLECI_API_URL}/v2/context/${integration.appId}/environment-variable`, items: EnvVars[];
{ next_page_token: string | null;
headers: { }>(`${IntegrationUrls.CIRCLECI_API_URL}/v2/context/${integration.appId}/environment-variable`, {
"Circle-Token": accessToken, headers: {
"Accept-Encoding": "application/json" "Circle-Token": accessToken,
}, "Accept-Encoding": "application/json"
params: nextPageToken },
? new URLSearchParams({ params: nextPageToken
"page-token": nextPageToken ? new URLSearchParams({
}) "page-token": nextPageToken
: undefined })
} : undefined
); });
envVars.push(...res.data.items); envVars.push(...res.data.items);
nextPageToken = res.data.next_page_token; nextPageToken = res.data.next_page_token;

View File

@@ -138,3 +138,8 @@ export type TOctopusDeployVariableSetScopeValues = {
Name: string; Name: string;
}[]; }[];
}; };
export enum CircleCiScope {
Context = "context",
Project = "project"
}

View File

@@ -22,17 +22,18 @@ import { SecretPathInput } from "@app/components/v2/SecretPathInput";
import { useWorkspace } from "@app/context"; import { useWorkspace } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { useGetIntegrationAuthCircleCIOrganizations } from "@app/hooks/api/integrationAuth"; import { useGetIntegrationAuthCircleCIOrganizations } from "@app/hooks/api/integrationAuth";
import { CircleCiScope } from "@app/hooks/api/integrationAuth/types";
const formSchema = z.discriminatedUnion("scope", [ const formSchema = z.discriminatedUnion("scope", [
z.object({ z.object({
scope: z.literal("context"), scope: z.literal(CircleCiScope.Context),
secretPath: z.string().default("/"), secretPath: z.string().default("/"),
sourceEnvironment: z.object({ name: z.string(), slug: z.string() }), sourceEnvironment: z.object({ name: z.string(), slug: z.string() }),
targetOrg: z.object({ name: z.string().min(1), slug: z.string().min(1) }), 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) }) targetContext: z.object({ name: z.string().min(1), id: z.string().min(1) })
}), }),
z.object({ z.object({
scope: z.literal("project"), scope: z.literal(CircleCiScope.Project),
secretPath: z.string().default("/"), secretPath: z.string().default("/"),
sourceEnvironment: z.object({ name: z.string(), slug: z.string() }), sourceEnvironment: z.object({ name: z.string(), slug: z.string() }),
targetOrg: z.object({ name: z.string().min(1), slug: z.string().min(1) }), 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) => { const onSubmit = async (data: TFormData) => {
try { try {
if (data.scope === "context") { if (data.scope === CircleCiScope.Context) {
await mutateAsync({ await mutateAsync({
scope: data.scope, scope: data.scope,
integrationAuthId, integrationAuthId,
@@ -231,13 +232,13 @@ export default function CircleCICreateIntegrationPage() {
}} }}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
<SelectItem value="project">Project</SelectItem> <SelectItem value={CircleCiScope.Project}>Project</SelectItem>
<SelectItem value="context">Context</SelectItem> <SelectItem value={CircleCiScope.Context}>Context</SelectItem>
</Select> </Select>
</FormControl> </FormControl>
)} )}
/> />
{selectedScope === "context" && selectedOrganizationEntry && ( {selectedScope === CircleCiScope.Context && selectedOrganizationEntry && (
<Controller <Controller
control={control} control={control}
name="targetContext" name="targetContext"
@@ -264,7 +265,7 @@ export default function CircleCICreateIntegrationPage() {
)} )}
/> />
)} )}
{selectedScope === "project" && selectedOrganizationEntry && ( {selectedScope === CircleCiScope.Project && selectedOrganizationEntry && (
<Controller <Controller
control={control} control={control}
name="targetProject" name="targetProject"

View File

@@ -1,6 +1,7 @@
import { integrationSlugNameMapping } from "public/data/frequentConstants"; import { integrationSlugNameMapping } from "public/data/frequentConstants";
import { FormLabel } from "@app/components/v2"; import { FormLabel } from "@app/components/v2";
import { CircleCiScope } from "@app/hooks/api/integrationAuth/types";
import { IntegrationMappingBehavior, TIntegrationWithEnv } from "@app/hooks/api/integrations/types"; import { IntegrationMappingBehavior, TIntegrationWithEnv } from "@app/hooks/api/integrations/types";
type Props = { type Props = {
@@ -46,7 +47,7 @@ export const IntegrationConnectionSection = ({ integration }: Props) => {
case "qovery": case "qovery":
return integration.scope; return integration.scope;
case "circleci": case "circleci":
if (integration.scope === "context") { if (integration.scope === CircleCiScope.Context) {
return "Context"; return "Context";
} }

View File

@@ -1,4 +1,5 @@
import { FormLabel } from "@app/components/v2"; import { FormLabel } from "@app/components/v2";
import { CircleCiScope } from "@app/hooks/api/integrationAuth/types";
import { IntegrationMappingBehavior, TIntegration } from "@app/hooks/api/integrations/types"; import { IntegrationMappingBehavior, TIntegration } from "@app/hooks/api/integrations/types";
type Props = { type Props = {
@@ -53,7 +54,7 @@ export const IntegrationDetails = ({ integration }: Props) => {
label={ label={
(integration.integration === "qovery" && integration?.scope) || (integration.integration === "qovery" && integration?.scope) ||
(integration.integration === "circleci" && (integration.integration === "circleci" &&
(integration.scope === "context" ? "Context" : "Project")) || (integration.scope === CircleCiScope.Context ? "Context" : "Project")) ||
(integration.integration === "bitbucket" && "Repository") || (integration.integration === "bitbucket" && "Repository") ||
(integration.integration === "octopus-deploy" && "Project") || (integration.integration === "octopus-deploy" && "Project") ||
(integration.integration === "aws-secret-manager" && "Secret") || (integration.integration === "aws-secret-manager" && "Secret") ||