mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 18:27:19 +00:00
add initial sync options to terraform cloud integration
This commit is contained in:
@@ -2157,16 +2157,29 @@ const syncSecretsQovery = async ({
|
|||||||
* @param {String} obj.accessToken - access token for Terraform Cloud API
|
* @param {String} obj.accessToken - access token for Terraform Cloud API
|
||||||
*/
|
*/
|
||||||
const syncSecretsTerraformCloud = async ({
|
const syncSecretsTerraformCloud = async ({
|
||||||
|
createManySecretsRawFn,
|
||||||
|
updateManySecretsRawFn,
|
||||||
integration,
|
integration,
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken,
|
||||||
|
integrationDAL
|
||||||
}: {
|
}: {
|
||||||
integration: TIntegrations;
|
createManySecretsRawFn: (params: TCreateManySecretsRawFn) => Promise<Array<TSecrets & { _id: string }>>;
|
||||||
secrets: Record<string, { value: string; comment?: string }>;
|
updateManySecretsRawFn: (params: TUpdateManySecretsRawFn) => Promise<Array<TSecrets & { _id: string }>>;
|
||||||
|
integration: TIntegrations & {
|
||||||
|
projectId: string;
|
||||||
|
environment: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
secrets: Record<string, { value: string; comment?: string } | null>;
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
|
integrationDAL: Pick<TIntegrationDALFactory, "updateById">;
|
||||||
}) => {
|
}) => {
|
||||||
// get secrets from Terraform Cloud
|
// get secrets from Terraform Cloud
|
||||||
const getSecretsRes = (
|
const terraformSecrets = (
|
||||||
await request.get<{ data: { attributes: { key: string; value: string }; id: string }[] }>(
|
await request.get<{ data: { attributes: { key: string; value: string }; id: string }[] }>(
|
||||||
`${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${integration.appId}/vars`,
|
`${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${integration.appId}/vars`,
|
||||||
{
|
{
|
||||||
@@ -2184,9 +2197,74 @@ const syncSecretsTerraformCloud = async ({
|
|||||||
{} as Record<string, { attributes: { key: string; value: string }; id: string }>
|
{} as Record<string, { attributes: { key: string; value: string }; id: string }>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const secretsToAdd: { [key: string]: string } = {};
|
||||||
|
const secretsToUpdate: { [key: string]: string } = {};
|
||||||
|
|
||||||
|
const metadata = z.record(z.any()).parse(integration.metadata);
|
||||||
|
|
||||||
|
Object.keys(terraformSecrets).forEach((key) => {
|
||||||
|
if (!integration.lastUsed) {
|
||||||
|
// first time using integration
|
||||||
|
// -> apply initial sync behavior
|
||||||
|
switch (metadata.initialSyncBehavior) {
|
||||||
|
case IntegrationInitialSyncBehavior.PREFER_TARGET: {
|
||||||
|
if (!(key in secrets)) {
|
||||||
|
secretsToAdd[key] = terraformSecrets[key].attributes.value;
|
||||||
|
} else if (secrets[key]?.value !== terraformSecrets[key].attributes.value) {
|
||||||
|
secretsToUpdate[key] = terraformSecrets[key].attributes.value;
|
||||||
|
}
|
||||||
|
secrets[key] = {
|
||||||
|
value: terraformSecrets[key].attributes.value
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IntegrationInitialSyncBehavior.PREFER_SOURCE: {
|
||||||
|
if (!(key in secrets)) {
|
||||||
|
secrets[key] = {
|
||||||
|
value: terraformSecrets[key].attributes.value
|
||||||
|
};
|
||||||
|
secretsToAdd[key] = terraformSecrets[key].attributes.value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (!(key in secrets)) secrets[key] = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Object.keys(secretsToAdd).length) {
|
||||||
|
await createManySecretsRawFn({
|
||||||
|
projectId: integration.projectId,
|
||||||
|
environment: integration.environment.slug,
|
||||||
|
path: integration.secretPath,
|
||||||
|
secrets: Object.keys(secretsToAdd).map((key) => ({
|
||||||
|
secretName: key,
|
||||||
|
secretValue: secretsToAdd[key],
|
||||||
|
type: SecretType.Shared,
|
||||||
|
secretComment: ""
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(secretsToUpdate).length) {
|
||||||
|
await updateManySecretsRawFn({
|
||||||
|
projectId: integration.projectId,
|
||||||
|
environment: integration.environment.slug,
|
||||||
|
path: integration.secretPath,
|
||||||
|
secrets: Object.keys(secretsToUpdate).map((key) => ({
|
||||||
|
secretName: key,
|
||||||
|
secretValue: secretsToUpdate[key],
|
||||||
|
type: SecretType.Shared,
|
||||||
|
secretComment: ""
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// create or update secrets on Terraform Cloud
|
// create or update secrets on Terraform Cloud
|
||||||
for await (const key of Object.keys(secrets)) {
|
for await (const key of Object.keys(secrets)) {
|
||||||
if (!(key in getSecretsRes)) {
|
if (!(key in terraformSecrets)) {
|
||||||
// case: secret does not exist in Terraform Cloud
|
// case: secret does not exist in Terraform Cloud
|
||||||
// -> add secret
|
// -> add secret
|
||||||
await request.post(
|
await request.post(
|
||||||
@@ -2196,7 +2274,7 @@ const syncSecretsTerraformCloud = async ({
|
|||||||
type: "vars",
|
type: "vars",
|
||||||
attributes: {
|
attributes: {
|
||||||
key,
|
key,
|
||||||
value: secrets[key].value,
|
value: secrets[key]?.value,
|
||||||
category: integration.targetService
|
category: integration.targetService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2210,17 +2288,17 @@ const syncSecretsTerraformCloud = async ({
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
// case: secret exists in Terraform Cloud
|
// case: secret exists in Terraform Cloud
|
||||||
} else if (secrets[key].value !== getSecretsRes[key].attributes.value) {
|
} else if (secrets[key]?.value !== terraformSecrets[key].attributes.value) {
|
||||||
// -> update secret
|
// -> update secret
|
||||||
await request.patch(
|
await request.patch(
|
||||||
`${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${integration.appId}/vars/${getSecretsRes[key].id}`,
|
`${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${integration.appId}/vars/${terraformSecrets[key].id}`,
|
||||||
{
|
{
|
||||||
data: {
|
data: {
|
||||||
type: "vars",
|
type: "vars",
|
||||||
id: getSecretsRes[key].id,
|
id: terraformSecrets[key].id,
|
||||||
attributes: {
|
attributes: {
|
||||||
...getSecretsRes[key],
|
...terraformSecrets[key],
|
||||||
value: secrets[key].value
|
value: secrets[key]?.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2235,11 +2313,11 @@ const syncSecretsTerraformCloud = async ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for await (const key of Object.keys(getSecretsRes)) {
|
for await (const key of Object.keys(terraformSecrets)) {
|
||||||
if (!(key in secrets)) {
|
if (!(key in secrets)) {
|
||||||
// case: delete secret
|
// case: delete secret
|
||||||
await request.delete(
|
await request.delete(
|
||||||
`${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${integration.appId}/vars/${getSecretsRes[key].id}`,
|
`${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${integration.appId}/vars/${terraformSecrets[key].id}`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
@@ -2250,6 +2328,10 @@ const syncSecretsTerraformCloud = async ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await integrationDAL.updateById(integration.id, {
|
||||||
|
lastUsed: new Date()
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -3291,9 +3373,12 @@ export const syncIntegrationSecrets = async ({
|
|||||||
break;
|
break;
|
||||||
case Integrations.TERRAFORM_CLOUD:
|
case Integrations.TERRAFORM_CLOUD:
|
||||||
await syncSecretsTerraformCloud({
|
await syncSecretsTerraformCloud({
|
||||||
|
createManySecretsRawFn,
|
||||||
|
updateManySecretsRawFn,
|
||||||
integration,
|
integration,
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken,
|
||||||
|
integrationDAL
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case Integrations.HASHICORP_VAULT:
|
case Integrations.HASHICORP_VAULT:
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import Head from "next/head";
|
||||||
|
import Image from "next/image";
|
||||||
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import { useSaveIntegrationAccessToken } from "@app/hooks/api";
|
import { useSaveIntegrationAccessToken } from "@app/hooks/api";
|
||||||
|
|
||||||
@@ -49,12 +54,56 @@ export default function TerraformCloudCreateIntegrationPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
<Card className="max-w-md rounded-md p-8">
|
<Head>
|
||||||
<CardTitle className="text-center">Terraform Cloud Integration</CardTitle>
|
<title>Authorize Terraform Cloud Integration</title>
|
||||||
|
<link rel="icon" href="/infisical.ico" />
|
||||||
|
</Head>
|
||||||
|
<Card className="max-w-lg rounded-md border border-mineshaft-600">
|
||||||
|
<CardTitle
|
||||||
|
className="px-6 text-left text-xl"
|
||||||
|
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="inline flex items-center">
|
||||||
|
<Image
|
||||||
|
src="/images/integrations/Terraform.png"
|
||||||
|
height={35}
|
||||||
|
width={35}
|
||||||
|
alt="Terraform logo"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span className="ml-1.5">Terraform Cloud Integration </span>
|
||||||
|
<Link href="https://infisical.com/docs/integrations/cloud/terraform-cloud" passHref>
|
||||||
|
<a target="_blank" rel="noopener noreferrer">
|
||||||
|
<div className="ml-2 mb-1 inline-block cursor-default rounded-md bg-yellow/20 px-1.5 pb-[0.03rem] pt-[0.04rem] text-sm text-yellow opacity-80 hover:opacity-100">
|
||||||
|
<FontAwesomeIcon icon={faBookOpen} className="mr-1.5" />
|
||||||
|
Docs
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faArrowUpRightFromSquare}
|
||||||
|
className="ml-1.5 mb-[0.07rem] text-xxs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</CardTitle>
|
||||||
|
<FormControl
|
||||||
|
label="Terraform Cloud Workspace ID"
|
||||||
|
errorText={workspacesIdErrorText}
|
||||||
|
isError={workspacesIdErrorText !== "" ?? false}
|
||||||
|
className="px-6"
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
placeholder="Workspace Id"
|
||||||
|
value={workspacesId}
|
||||||
|
onChange={(e) => setWorkSpacesId(e.target.value)}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
<FormControl
|
<FormControl
|
||||||
label="Terraform Cloud API Token"
|
label="Terraform Cloud API Token"
|
||||||
errorText={apiKeyErrorText}
|
errorText={apiKeyErrorText}
|
||||||
isError={apiKeyErrorText !== "" ?? false}
|
isError={apiKeyErrorText !== "" ?? false}
|
||||||
|
className="px-6"
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
placeholder="API Token"
|
placeholder="API Token"
|
||||||
@@ -64,21 +113,11 @@ export default function TerraformCloudCreateIntegrationPage() {
|
|||||||
onChange={(e) => setApiKey(e.target.value)}
|
onChange={(e) => setApiKey(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl
|
|
||||||
label="Terraform Cloud Workspace ID"
|
|
||||||
errorText={workspacesIdErrorText}
|
|
||||||
isError={workspacesIdErrorText !== "" ?? false}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
placeholder="Workspace Id"
|
|
||||||
value={workspacesId}
|
|
||||||
onChange={(e) => setWorkSpacesId(e.target.value)}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<Button
|
<Button
|
||||||
onClick={handleButtonClick}
|
onClick={handleButtonClick}
|
||||||
color="mineshaft"
|
colorSchema="primary"
|
||||||
className="mt-4"
|
variant="outline_bg"
|
||||||
|
className="mb-6 mt-2 ml-auto mr-6 w-min"
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
>
|
>
|
||||||
Connect to Terraform Cloud
|
Connect to Terraform Cloud
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import Head from "next/head";
|
||||||
|
import Image from "next/image";
|
||||||
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
|
|
||||||
import { useCreateIntegration } from "@app/hooks/api";
|
import { useCreateIntegration } from "@app/hooks/api";
|
||||||
|
import { IntegrationSyncBehavior } from "@app/hooks/api/integrations/types";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -19,6 +25,15 @@ import {
|
|||||||
} from "../../../hooks/api/integrationAuth";
|
} from "../../../hooks/api/integrationAuth";
|
||||||
import { useGetWorkspaceById } from "../../../hooks/api/workspace";
|
import { useGetWorkspaceById } from "../../../hooks/api/workspace";
|
||||||
|
|
||||||
|
const initialSyncBehaviors = [
|
||||||
|
{
|
||||||
|
label: "No Import - Overwrite all values in Terraform Cloud",
|
||||||
|
value: IntegrationSyncBehavior.OVERWRITE_TARGET
|
||||||
|
},
|
||||||
|
{ label: "Import non-sensitive - Prefer values from Terraform Cloud", value: IntegrationSyncBehavior.PREFER_TARGET },
|
||||||
|
{ label: "Import non-sensitive - Prefer values from Infisical", value: IntegrationSyncBehavior.PREFER_SOURCE }
|
||||||
|
];
|
||||||
|
|
||||||
const variableTypes = [{ name: "env" }, { name: "terraform" }];
|
const variableTypes = [{ name: "env" }, { name: "terraform" }];
|
||||||
|
|
||||||
export default function TerraformCloudCreateIntegrationPage() {
|
export default function TerraformCloudCreateIntegrationPage() {
|
||||||
@@ -39,6 +54,7 @@ export default function TerraformCloudCreateIntegrationPage() {
|
|||||||
const [variableType, setVariableType] = useState("");
|
const [variableType, setVariableType] = useState("");
|
||||||
const [variableTypeErrorText, setVariableTypeErrorText] = useState("");
|
const [variableTypeErrorText, setVariableTypeErrorText] = useState("");
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [initialSyncBehavior, setInitialSyncBehavior] = useState("prefer-source")
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (workspace) {
|
if (workspace) {
|
||||||
@@ -78,7 +94,10 @@ export default function TerraformCloudCreateIntegrationPage() {
|
|||||||
)?.appId,
|
)?.appId,
|
||||||
sourceEnvironment: selectedSourceEnvironment,
|
sourceEnvironment: selectedSourceEnvironment,
|
||||||
targetService: variableType,
|
targetService: variableType,
|
||||||
secretPath
|
secretPath,
|
||||||
|
metadata: {
|
||||||
|
initialSyncBehavior
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
@@ -95,9 +114,40 @@ export default function TerraformCloudCreateIntegrationPage() {
|
|||||||
integrationAuthApps &&
|
integrationAuthApps &&
|
||||||
targetApp ? (
|
targetApp ? (
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
<Card className="max-w-md rounded-md p-8">
|
<Head>
|
||||||
<CardTitle className="text-center">Terraform Cloud Integration</CardTitle>
|
<title>Create Terraform Cloud Integration</title>
|
||||||
<FormControl label="Project Environment" className="mt-4">
|
<link rel="icon" href="/infisical.ico" />
|
||||||
|
</Head>
|
||||||
|
<Card className="max-w-lg rounded-md border border-mineshaft-600">
|
||||||
|
<CardTitle
|
||||||
|
className="px-6 text-left text-xl"
|
||||||
|
subTitle="Specify the encironment and path within Infisical that you want to push to which project in Terraform."
|
||||||
|
>
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
|
<div className="inline flex items-center">
|
||||||
|
<Image
|
||||||
|
src="/images/integrations/Terraform.png"
|
||||||
|
height={35}
|
||||||
|
width={35}
|
||||||
|
alt="Terraform logo"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span className="ml-1.5">Terraform Cloud Integration </span>
|
||||||
|
<Link href="https://infisical.com/docs/integrations/cloud/terraform-cloud" passHref>
|
||||||
|
<a target="_blank" rel="noopener noreferrer">
|
||||||
|
<div className="ml-2 mb-1 inline-block cursor-default rounded-md bg-yellow/20 px-1.5 pb-[0.03rem] pt-[0.04rem] text-sm text-yellow opacity-80 hover:opacity-100">
|
||||||
|
<FontAwesomeIcon icon={faBookOpen} className="mr-1.5" />
|
||||||
|
Docs
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faArrowUpRightFromSquare}
|
||||||
|
className="ml-1.5 mb-[0.07rem] text-xxs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</CardTitle>
|
||||||
|
<FormControl label="Project Environment" className="px-6">
|
||||||
<Select
|
<Select
|
||||||
value={selectedSourceEnvironment}
|
value={selectedSourceEnvironment}
|
||||||
onValueChange={(val) => setSelectedSourceEnvironment(val)}
|
onValueChange={(val) => setSelectedSourceEnvironment(val)}
|
||||||
@@ -113,7 +163,7 @@ export default function TerraformCloudCreateIntegrationPage() {
|
|||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl label="Secrets Path">
|
<FormControl label="Secrets Path" className="px-6">
|
||||||
<Input
|
<Input
|
||||||
value={secretPath}
|
value={secretPath}
|
||||||
onChange={(evt) => setSecretPath(evt.target.value)}
|
onChange={(evt) => setSecretPath(evt.target.value)}
|
||||||
@@ -122,7 +172,7 @@ export default function TerraformCloudCreateIntegrationPage() {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl
|
<FormControl
|
||||||
label="Category"
|
label="Category"
|
||||||
className="mt-4"
|
className="px-6"
|
||||||
errorText={variableTypeErrorText}
|
errorText={variableTypeErrorText}
|
||||||
isError={variableTypeErrorText !== "" ?? false}
|
isError={variableTypeErrorText !== "" ?? false}
|
||||||
>
|
>
|
||||||
@@ -138,7 +188,7 @@ export default function TerraformCloudCreateIntegrationPage() {
|
|||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl label="Terraform Cloud Project" className="mt-4">
|
<FormControl label="Terraform Cloud Project" className="px-6">
|
||||||
<Select
|
<Select
|
||||||
value={targetApp}
|
value={targetApp}
|
||||||
onValueChange={(val) => setTargetApp(val)}
|
onValueChange={(val) => setTargetApp(val)}
|
||||||
@@ -161,10 +211,22 @@ export default function TerraformCloudCreateIntegrationPage() {
|
|||||||
)}
|
)}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
<FormControl label="Initial Sync Behavior" className="px-6">
|
||||||
|
<Select value={initialSyncBehavior} onValueChange={(e) => setInitialSyncBehavior(e)} className="w-full border border-mineshaft-600">
|
||||||
|
{initialSyncBehaviors.map((b) => {
|
||||||
|
return (
|
||||||
|
<SelectItem value={b.value} key={`sync-behavior-${b.value}`}>
|
||||||
|
{b.label}
|
||||||
|
</SelectItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
<Button
|
<Button
|
||||||
onClick={handleButtonClick}
|
onClick={handleButtonClick}
|
||||||
color="mineshaft"
|
colorSchema="primary"
|
||||||
className="mt-4"
|
variant="outline_bg"
|
||||||
|
className="mb-6 mt-2 ml-auto mr-6 w-min"
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
isDisabled={integrationAuthApps.length === 0}
|
isDisabled={integrationAuthApps.length === 0}
|
||||||
>
|
>
|
||||||
|
|||||||
+9
@@ -130,6 +130,7 @@ export const IntegrationsSection = ({
|
|||||||
(integration.integration === "qovery" && integration?.scope) ||
|
(integration.integration === "qovery" && integration?.scope) ||
|
||||||
(integration.integration === "aws-secret-manager" && "Secret") ||
|
(integration.integration === "aws-secret-manager" && "Secret") ||
|
||||||
(integration.integration === "aws-parameter-store" && "Path") ||
|
(integration.integration === "aws-parameter-store" && "Path") ||
|
||||||
|
(integration?.integration === "terraform-cloud" && "Project") ||
|
||||||
(integration?.scope === "github-org" && "Organization") ||
|
(integration?.scope === "github-org" && "Organization") ||
|
||||||
(["github-repo", "github-env"].includes(integration?.scope as string) &&
|
(["github-repo", "github-env"].includes(integration?.scope as string) &&
|
||||||
"Repository") ||
|
"Repository") ||
|
||||||
@@ -168,6 +169,14 @@ export const IntegrationsSection = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{integration.integration === "terraform-cloud" && integration.targetService && (
|
||||||
|
<div className="ml-2">
|
||||||
|
<FormLabel label="Category" />
|
||||||
|
<div className="rounded-md border border-mineshaft-700 bg-mineshaft-900 px-3 py-2 font-inter text-sm text-bunker-200">
|
||||||
|
{integration.targetService}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{(integration.integration === "checkly" ||
|
{(integration.integration === "checkly" ||
|
||||||
integration.integration === "github") && (
|
integration.integration === "github") && (
|
||||||
<div className="ml-2">
|
<div className="ml-2">
|
||||||
|
|||||||
Reference in New Issue
Block a user