Requested changes

This commit is contained in:
Daniel Hougaard
2024-09-05 18:48:20 +04:00
parent 8f3d328b9a
commit a29fb613b9
6 changed files with 78 additions and 57 deletions
@@ -459,10 +459,9 @@ const getAppsFlyio = async ({ accessToken }: { accessToken: string }) => {
* Return list of projects for CircleCI integration * Return list of projects for CircleCI integration
*/ */
const getAppsCircleCI = async ({ accessToken }: { accessToken: string }) => { const getAppsCircleCI = async ({ accessToken }: { accessToken: string }) => {
// Fetch collaborations (v2 API) const res = (
const collaborations = ( await request.get<{ reponame: string; username: string; vcs_url: string }[]>(
await request.get<{ id: string; name: string; slug: string }[]>( `${IntegrationUrls.CIRCLECI_API_URL}/v1.1/projects`,
`${IntegrationUrls.CIRCLECI_API_URL}/v2/me/collaborations`,
{ {
headers: { headers: {
"Circle-Token": accessToken, "Circle-Token": accessToken,
@@ -472,9 +471,10 @@ const getAppsCircleCI = async ({ accessToken }: { accessToken: string }) => {
) )
).data; ).data;
const apps = collaborations.map((a) => ({ const apps = res.map((a) => ({
name: a.name, owner: a.username, // username maps to unique organization name in CircleCI
appId: a.id name: a.reponame, // reponame maps to project name within an organization in CircleCI
appId: a.vcs_url.split("/").pop() // vcs_url maps to the project id in CircleCI
})); }));
return apps; return apps;
@@ -143,12 +143,6 @@ export type TBitbucketWorkspace = {
updated_on: string; updated_on: string;
}; };
export enum CircleCiVcsType {
GitHub = "GitHub",
CircleCI = "CircleCI",
BitBucket = "BitBucket"
}
export type TNorthflankSecretGroup = { export type TNorthflankSecretGroup = {
id: string; id: string;
name: string; name: string;
@@ -35,7 +35,7 @@ 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 { CircleCiVcsType, TIntegrationsWithEnvironment } from "./integration-auth-types"; import { TIntegrationsWithEnvironment } from "./integration-auth-types";
import { import {
IntegrationInitialSyncBehavior, IntegrationInitialSyncBehavior,
IntegrationMappingBehavior, IntegrationMappingBehavior,
@@ -1929,8 +1929,9 @@ const syncSecretsCircleCI = async ({
secrets: Record<string, { value: string; comment?: string }>; secrets: Record<string, { value: string; comment?: string }>;
accessToken: string; accessToken: string;
}) => { }) => {
const circleciOrganizationDetail = ( let projectSlug: string | null = null;
await request.get<{ slug: string; name: string }[]>(`${IntegrationUrls.CIRCLECI_API_URL}/v2/me/collaborations`, { const projectDetails = (
await request.get<{ slug: string }>(`${IntegrationUrls.CIRCLECI_API_URL}/v2/project/${integration.appId}`, {
headers: { headers: {
"Circle-Token": accessToken, "Circle-Token": accessToken,
"Accept-Encoding": "application/json" "Accept-Encoding": "application/json"
@@ -1938,30 +1939,7 @@ const syncSecretsCircleCI = async ({
}) })
).data; ).data;
let projectSlug: string | null = null; projectSlug = `${projectDetails.slug}`;
if (!integration.owner) {
projectSlug = `${circleciOrganizationDetail[0].slug}/${integration.app}`;
} else {
const projectDetails = (
await request.get<{ vcs_info: { provider: CircleCiVcsType } }>(
`${IntegrationUrls.CIRCLECI_API_URL}/v2/project/${integration.app}`,
{
headers: {
"Circle-Token": accessToken,
"Accept-Encoding": "application/json"
}
}
)
).data;
const vcsProviderMap: Record<CircleCiVcsType, string> = {
[CircleCiVcsType.GitHub]: "gh",
[CircleCiVcsType.BitBucket]: "bb",
[CircleCiVcsType.CircleCI]: "circleci"
};
projectSlug = `${vcsProviderMap[projectDetails.vcs_info.provider]}/${integration.owner}/${integration.app}`;
}
// sync secrets to CircleCI // sync secrets to CircleCI
await Promise.all( await Promise.all(
@@ -69,7 +69,7 @@ export default function AWSParameterStoreAuthorizeIntegrationPage() {
subTitle="After adding the details below, you will be prompted to set up an integration for a particular Infisical project and environment." subTitle="After adding the details below, you will be prompted to set up an integration for a particular Infisical project and environment."
> >
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
<div className="inline flex items-center"> <div className="flex items-center">
<Image <Image
src="/images/integrations/Amazon Web Services.png" src="/images/integrations/Amazon Web Services.png"
height={35} height={35}
@@ -1,4 +1,4 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import Head from "next/head"; import Head from "next/head";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
@@ -12,6 +12,7 @@ import {
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { import {
@@ -48,7 +49,7 @@ export default function CircleCICreateIntegrationPage() {
const [targetOrganization, setTargetOrganization] = useState(""); const [targetOrganization, setTargetOrganization] = useState("");
const [secretPath, setSecretPath] = useState("/"); const [secretPath, setSecretPath] = useState("/");
const [targetApp, setTargetApp] = useState(""); const [targetProjectId, setTargetProjectId] = useState("");
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
@@ -62,18 +63,34 @@ export default function CircleCICreateIntegrationPage() {
try { try {
if (!integrationAuth?.id) return; if (!integrationAuth?.id) return;
if (!targetApp || targetOrganization === "none") return; if (!targetProjectId || targetOrganization === "none") {
createNotification({
type: "error",
text: "Please select a project"
});
return;
}
setIsLoading(true); setIsLoading(true);
const selectedApp = integrationAuthApps?.find(
(integrationAuthApp) => integrationAuthApp.appId === targetProjectId
);
if (!selectedApp) {
createNotification({
type: "error",
text: "Invalid project selected"
});
return;
}
await mutateAsync({ await mutateAsync({
integrationAuthId: integrationAuth?.id, integrationAuthId: integrationAuth?.id,
isActive: true, isActive: true,
app: targetApp, app: selectedApp.name, // project name
owner: targetOrganization, owner: selectedApp.owner, // organization name
appId: integrationAuthApps?.find( appId: selectedApp.appId, // project id (used for syncing)
(integrationAuthApp) => integrationAuthApp.name === targetApp
)?.appId,
sourceEnvironment: selectedSourceEnvironment, sourceEnvironment: selectedSourceEnvironment,
secretPath secretPath
}); });
@@ -86,6 +103,14 @@ export default function CircleCICreateIntegrationPage() {
} }
}; };
const filteredProjects = useMemo(() => {
if (!integrationAuthApps) return [];
return integrationAuthApps.filter((integrationAuthApp) => {
return integrationAuthApp.owner === targetOrganization;
});
}, [integrationAuthApps, targetOrganization]);
return integrationAuth && workspace && selectedSourceEnvironment && integrationAuthApps ? ( return integrationAuth && workspace && selectedSourceEnvironment && integrationAuthApps ? (
<div className="flex h-full w-full flex-col items-center justify-center"> <div className="flex h-full w-full flex-col items-center justify-center">
<Head> <Head>
@@ -151,6 +176,7 @@ export default function CircleCICreateIntegrationPage() {
value={targetOrganization} value={targetOrganization}
onValueChange={(val) => { onValueChange={(val) => {
setTargetOrganization(val); setTargetOrganization(val);
setTargetProjectId("none");
}} }}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
isDisabled={integrationAuthApps.length === 0} isDisabled={integrationAuthApps.length === 0}
@@ -158,10 +184,10 @@ export default function CircleCICreateIntegrationPage() {
{integrationAuthApps.length > 0 ? ( {integrationAuthApps.length > 0 ? (
integrationAuthApps.map((integrationAuthApp) => ( integrationAuthApps.map((integrationAuthApp) => (
<SelectItem <SelectItem
value={integrationAuthApp.appId!} value={integrationAuthApp.owner!}
key={`target-org-${integrationAuthApp.owner}`} key={`target-org-${integrationAuthApp.owner}`}
> >
{integrationAuthApp.name} {integrationAuthApp.owner}
</SelectItem> </SelectItem>
)) ))
) : ( ) : (
@@ -174,11 +200,26 @@ export default function CircleCICreateIntegrationPage() {
{targetOrganization && ( {targetOrganization && (
<FormControl label="CircleCI Project ID" className="px-6"> <FormControl label="CircleCI Project ID" className="px-6">
<Input <Select
placeholder="55a2071d-97b5-4f71-b285-990d73c41268" value={targetProjectId}
value={targetApp} onValueChange={(val) => {
onChange={(evt) => setTargetApp(evt.target.value)} setTargetProjectId(val);
/> }}
className="w-full border border-mineshaft-500"
isDisabled={filteredProjects.length === 0}
>
{filteredProjects.length > 0 ? (
filteredProjects.map((project) => (
<SelectItem value={project.appId!} key={`target-project-${project.owner}`}>
{project.name}
</SelectItem>
))
) : (
<SelectItem value="none" key="target-app-none">
No projects found
</SelectItem>
)}
</Select>
</FormControl> </FormControl>
)} )}
<Button <Button
@@ -99,7 +99,7 @@ export const ConfiguredIntegrationItem = ({
<FormLabel <FormLabel
label={ label={
(integration.integration === "qovery" && integration?.scope) || (integration.integration === "qovery" && integration?.scope) ||
(integration.integration === "circleci" && "Project ID") || (integration.integration === "circleci" && "Project") ||
(integration.integration === "aws-secret-manager" && "Secret") || (integration.integration === "aws-secret-manager" && "Secret") ||
(["aws-parameter-store", "rundeck"].includes(integration.integration) && "Path") || (["aws-parameter-store", "rundeck"].includes(integration.integration) && "Path") ||
(integration?.integration === "terraform-cloud" && "Project") || (integration?.integration === "terraform-cloud" && "Project") ||
@@ -143,6 +143,14 @@ export const ConfiguredIntegrationItem = ({
</div> </div>
</div> </div>
)} )}
{integration.integration === "circleci" && integration.owner && (
<div className="ml-2">
<FormLabel label="Organization" />
<div className="rounded-md border border-mineshaft-700 bg-mineshaft-900 px-3 py-2 font-inter text-sm text-bunker-200">
{integration.owner}
</div>
</div>
)}
{integration.integration === "terraform-cloud" && integration.targetService && ( {integration.integration === "terraform-cloud" && integration.targetService && (
<div className="ml-2"> <div className="ml-2">
<FormLabel label="Category" /> <FormLabel label="Category" />