diff --git a/backend/src/integrations/apps.ts b/backend/src/integrations/apps.ts index cac83dca2..878b16f47 100644 --- a/backend/src/integrations/apps.ts +++ b/backend/src/integrations/apps.ts @@ -129,7 +129,7 @@ const getApps = async ({ case INTEGRATION_TERRAFORM_CLOUD: apps = await getAppsTerraformCloud({ accessToken, - organizationName: accessId, + workspacesId: accessId, }); break; case INTEGRATION_TRAVISCI: @@ -544,18 +544,19 @@ const getAppsTravisCI = async ({ accessToken }: { accessToken: string }) => { * Return list of projects for Terraform Cloud integration * @param {Object} obj * @param {String} obj.accessToken - access token for Terraform Cloud API + * @param {String} obj.workspacesId - workspace id of Terraform Cloud projects * @returns {Object[]} apps - names and ids of Terraform Cloud projects * @returns {String} apps.name - name of Terraform Cloud projects */ const getAppsTerraformCloud = async ({ accessToken, - organizationName + workspacesId }: { accessToken: string; - organizationName?: string; + workspacesId?: string; }) => { const res = ( - await standardRequest.get(`${INTEGRATION_TERRAFORM_CLOUD_API_URL}/api/v2/organizations/${organizationName}/projects`, { + await standardRequest.get(`${INTEGRATION_TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${workspacesId}`, { headers: { Authorization: `Bearer ${accessToken}`, Accept: "application/json", @@ -563,11 +564,14 @@ const getAppsTerraformCloud = async ({ }) ).data.data; - const apps = res?.map((a: any) => { - return { - name: a?.attributes.name, - }; - }); + const apps = [] + + const appsObj = { + name: res?.attributes.name, + appId: res?.id, + }; + + apps.push(appsObj) return apps; }; diff --git a/backend/src/integrations/sync.ts b/backend/src/integrations/sync.ts index 97893659d..66c584385 100644 --- a/backend/src/integrations/sync.ts +++ b/backend/src/integrations/sync.ts @@ -191,7 +191,6 @@ const syncSecrets = async ({ await syncSecretsTerraformCloud({ integration, secrets, - accessId, accessToken, }); break; @@ -1821,29 +1820,42 @@ const syncSecretsCheckly = async ({ * @param {Object} obj * @param {IIntegration} obj.integration - integration details * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Terraform Cloud integration + * @param {String} obj.accessToken - access token for Terraform Cloud API */ const syncSecretsTerraformCloud = async ({ integration, secrets, - accessId, accessToken, }: { integration: IIntegration; secrets: any; - accessId: string | null; accessToken: string; }) => { - await standardRequest.put( - `${INTEGRATION_TERRAFORM_CLOUD_API_URL}/api/v2/organizations/${accessId}/${integration.app}`, - Object.keys(secrets).map((key) => ({ - key, - value: secrets[key], - })), + + // const entries = Object.entries(secrets); + + // const data = entries.map((a: any) => { + // const obj = { + // "data": { + // "attributes": { + // "key": a.key, + // "value": a.value, + // "category": "env" + // } + // } + // } + // return Object.fromEntries(obj) + // }) + + console.log("Testing Out:", secrets) + + await standardRequest.post( + `${INTEGRATION_TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${integration.appId}/vars`, + {}, { headers: { Authorization: `Bearer ${accessToken}`, - Accept: "application/json", + "Content-Type": "application/vnd.api+json" }, } ); diff --git a/frontend/public/data/frequentConstants.ts b/frontend/public/data/frequentConstants.ts index c3c74091d..f4eda94e7 100644 --- a/frontend/public/data/frequentConstants.ts +++ b/frontend/public/data/frequentConstants.ts @@ -19,6 +19,7 @@ const integrationSlugNameMapping: Mapping = { 'travisci': 'TravisCI', 'supabase': 'Supabase', 'checkly': 'Checkly', + 'terraform-cloud': 'Terraform Cloud', 'hashicorp-vault': 'Vault', 'cloudflare-pages': 'Cloudflare Pages' } diff --git a/frontend/src/pages/integrations/terraform-cloud/authorize.tsx b/frontend/src/pages/integrations/terraform-cloud/authorize.tsx index e69de29bb..91608a406 100644 --- a/frontend/src/pages/integrations/terraform-cloud/authorize.tsx +++ b/frontend/src/pages/integrations/terraform-cloud/authorize.tsx @@ -0,0 +1,80 @@ +import { useState } from "react"; +import { useRouter } from "next/router"; + +import { Button, Card, CardTitle, FormControl, Input } from "../../../components/v2"; +import saveIntegrationAccessToken from "../../api/integrations/saveIntegrationAccessToken"; + +export default function TerraformCloudCreateIntegrationPage() { + const router = useRouter(); + const [apiKey, setApiKey] = useState(""); + const [apiKeyErrorText, setApiKeyErrorText] = useState(""); + const [workspacesId, setWorkSpacesId] = useState(""); + const [workspacesIdErrorText, setWorkspacesIdErrorText] = useState(""); + const [isLoading, setIsLoading] = useState(false); + + const handleButtonClick = async () => { + try { + setApiKeyErrorText(""); + setWorkspacesIdErrorText(""); + + if (apiKey.length === 0) { + setApiKeyErrorText("API Token cannot be blank"); + return; + } + + if (workspacesId.length === 0) { + setWorkspacesIdErrorText("Workspace Id cannot be blank"); + return; + } + + setIsLoading(true); + + const integrationAuth = await saveIntegrationAccessToken({ + workspaceId: localStorage.getItem("projectData.id"), + integration: "terraform-cloud", + accessId: workspacesId, + accessToken: apiKey, + url: null, + namespace: null + }); + + setIsLoading(false); + + router.push(`/integrations/terraform-cloud/create?integrationAuthId=${integrationAuth._id}`); + } catch (err) { + console.error(err); + } + }; + + return ( +
+ + Terraform Cloud Integration + + setApiKey(e.target.value)} /> + + + setWorkSpacesId(e.target.value)} /> + + + +
+ ); +} + +TerraformCloudCreateIntegrationPage.requireAuth = true; diff --git a/frontend/src/pages/integrations/terraform-cloud/create.tsx b/frontend/src/pages/integrations/terraform-cloud/create.tsx index e69de29bb..826f0eb16 100644 --- a/frontend/src/pages/integrations/terraform-cloud/create.tsx +++ b/frontend/src/pages/integrations/terraform-cloud/create.tsx @@ -0,0 +1,155 @@ +import { useEffect, useState } from "react"; +import { useRouter } from "next/router"; +import queryString from "query-string"; + +import { + Button, + Card, + CardTitle, + FormControl, + Input, + Select, + SelectItem +} from "../../../components/v2"; +import { + useGetIntegrationAuthApps, + useGetIntegrationAuthById +} from "../../../hooks/api/integrationAuth"; +import { useGetWorkspaceById } from "../../../hooks/api/workspace"; +import createIntegration from "../../api/integrations/createIntegration"; + +export default function TerraformCloudCreateIntegrationPage() { + const router = useRouter(); + + const { integrationAuthId } = queryString.parse(router.asPath.split("?")[1]); + + const { data: workspace } = useGetWorkspaceById(localStorage.getItem("projectData.id") ?? ""); + const { data: integrationAuth } = useGetIntegrationAuthById((integrationAuthId as string) ?? ""); + const { data: integrationAuthApps } = useGetIntegrationAuthApps({ + integrationAuthId: (integrationAuthId as string) ?? "" + }); + + const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(""); + const [targetApp, setTargetApp] = useState(""); + const [secretPath, setSecretPath] = useState("/"); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + if (workspace) { + setSelectedSourceEnvironment(workspace.environments[0].slug); + } + }, [workspace]); + + useEffect(() => { + if (integrationAuthApps) { + if (integrationAuthApps.length > 0) { + setTargetApp(integrationAuthApps[0].name); + } else { + setTargetApp("none"); + } + } + }, [integrationAuthApps]); + + const handleButtonClick = async () => { + try { + if (!integrationAuth?._id) return; + + setIsLoading(true); + + await createIntegration({ + integrationAuthId: integrationAuth?._id, + isActive: true, + app: targetApp, + appId: + integrationAuthApps?.find((integrationAuthApp) => integrationAuthApp.name === targetApp) + ?.appId ?? null, + sourceEnvironment: selectedSourceEnvironment, + targetEnvironment: null, + targetEnvironmentId: null, + targetService: null, + targetServiceId: null, + owner: null, + path: null, + region: null, + secretPath + }); + + setIsLoading(false); + + router.push(`/integrations/${localStorage.getItem("projectData.id")}`); + } catch (err) { + console.error(err); + } + }; + + return integrationAuth && + workspace && + selectedSourceEnvironment && + integrationAuthApps && + targetApp ? ( +
+ + Terraform Cloud Integration + + + + + setSecretPath(evt.target.value)} + placeholder="Provide a path, default is /" + /> + + + + + + +
+ ) : ( +
+ ); +} + +TerraformCloudCreateIntegrationPage.requireAuth = true;