Fix edge case for delete humanitec secret and improvements on docs

This commit is contained in:
carlosmonastyrski
2025-03-14 09:27:21 -03:00
parent 155e59e571
commit 16866d46bf
8 changed files with 81 additions and 102 deletions
@@ -2,7 +2,6 @@ import { AxiosError, AxiosResponse } from "axios";
import { request } from "@app/lib/config/request"; import { request } from "@app/lib/config/request";
import { BadRequestError, InternalServerError } from "@app/lib/errors"; import { BadRequestError, InternalServerError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
import { AppConnection } from "@app/services/app-connection/app-connection-enums"; import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import { IntegrationUrls } from "@app/services/integration-auth/integration-list"; import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
@@ -73,30 +72,23 @@ export const listOrganizations = async (appConnection: THumanitecConnection): Pr
const orgsWithApps: HumanitecOrgWithApps[] = []; const orgsWithApps: HumanitecOrgWithApps[] = [];
for (const org of orgs) { for (const org of orgs) {
try { // eslint-disable-next-line no-await-in-loop
// eslint-disable-next-line no-await-in-loop const appsResponse = await request.get<HumanitecApp[]>(`${IntegrationUrls.HUMANITEC_API_URL}/orgs/${org.id}/apps`, {
const appsResponse = await request.get<HumanitecApp[]>( headers: {
`${IntegrationUrls.HUMANITEC_API_URL}/orgs/${org.id}/apps`, Authorization: `Bearer ${apiToken}`
{
headers: {
Authorization: `Bearer ${apiToken}`
}
}
);
if (appsResponse.data) {
const apps = appsResponse.data;
orgsWithApps.push({
...org,
apps: apps.map((app) => ({
name: app.name,
id: app.id,
envs: app.envs
}))
});
} }
} catch (error) { });
logger.error(error, `Failed to get apps for organization ${org.name}`);
if (appsResponse.data) {
const apps = appsResponse.data;
orgsWithApps.push({
...org,
apps: apps.map((app) => ({
name: app.name,
id: app.id,
envs: app.envs
}))
});
} }
} }
return orgsWithApps; return orgsWithApps;
@@ -1,4 +1,5 @@
import { request } from "@app/lib/config/request"; import { request } from "@app/lib/config/request";
import { logger } from "@app/lib/logger";
import { IntegrationUrls } from "@app/services/integration-auth/integration-list"; import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors"; import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
import { SECRET_SYNC_NAME_MAP } from "@app/services/secret-sync/secret-sync-maps"; import { SECRET_SYNC_NAME_MAP } from "@app/services/secret-sync/secret-sync-maps";
@@ -39,6 +40,13 @@ const deleteSecret = async (secretSync: THumanitecSyncWithCredentials, encrypted
} }
} = secretSync; } = secretSync;
if (destinationConfig.scope === HumanitecSyncScope.Environment && encryptedSecret.source === "app") {
logger.info(
`Humanitec secret ${encryptedSecret.key} on app ${destinationConfig.app} has no environment override, not deleted as it is an app-level secret`
);
return;
}
try { try {
let url = `${IntegrationUrls.HUMANITEC_API_URL}/orgs/${destinationConfig.org}/apps/${destinationConfig.app}`; let url = `${IntegrationUrls.HUMANITEC_API_URL}/orgs/${destinationConfig.org}/apps/${destinationConfig.app}`;
if (destinationConfig.scope === HumanitecSyncScope.Environment) { if (destinationConfig.scope === HumanitecSyncScope.Environment) {
@@ -69,11 +77,12 @@ const createSecret = async (secretSync: THumanitecSyncWithCredentials, secretMap
} }
} = secretSync; } = secretSync;
const appLevelSecret = destinationConfig.scope === HumanitecSyncScope.Application ? secretMap[key].value : "";
await request.post( await request.post(
`${IntegrationUrls.HUMANITEC_API_URL}/orgs/${destinationConfig.org}/apps/${destinationConfig.app}/values`, `${IntegrationUrls.HUMANITEC_API_URL}/orgs/${destinationConfig.org}/apps/${destinationConfig.app}/values`,
{ {
key, key,
value: "", value: appLevelSecret,
description: secretMap[key].comment || "", description: secretMap[key].comment || "",
is_secret: true is_secret: true
}, },
@@ -1,10 +0,0 @@
---
title: "Create"
openapi: "POST /api/v1/app-connections/humanitec"
---
<Note>
Humanitec Connections must be created through the Infisical UI.
Check out the configuration docs for [Humanitec Connections](/integrations/app-connections/humanitec) for a step-by-step
guide.
</Note>
@@ -1,10 +0,0 @@
---
title: "Update"
openapi: "PATCH /api/v1/app-connections/humanitec/{connectionId}"
---
<Note>
Humanitec Connections must be updated through the Infisical UI.
Check out the configuration docs for [Humanitec Connections](/integrations/app-connections/humanitec) for a step-by-step
guide.
</Note>
@@ -22,8 +22,10 @@ Infisical supports connecting to Humanitec using a service user.
</Step> </Step>
<Step title="Create the API Token for the Service User"> <Step title="Create the API Token for the Service User">
Create the API token for the service user. Create the API token for the service user.
If you configure an expiry date for your API token you will need to manually rotate to a new token prior to expiration to avoid integration downtime.
This token's permission will be limited to the **Service User** role. This token's permission will be limited to the **Service User** role.
<Note>
If you configure an expiry date for your API token you will need to manually rotate to a new token prior to expiration to avoid integration downtime.
</Note>
![Humanitec Create API Token](/images/app-connections/humanitec/humanitec-create-api-token.png) ![Humanitec Create API Token](/images/app-connections/humanitec/humanitec-create-api-token.png)
</Step> </Step>
<Step title="Copy the API Token"> <Step title="Copy the API Token">
-2
View File
@@ -908,8 +908,6 @@
"api-reference/endpoints/app-connections/humanitec/available", "api-reference/endpoints/app-connections/humanitec/available",
"api-reference/endpoints/app-connections/humanitec/get-by-id", "api-reference/endpoints/app-connections/humanitec/get-by-id",
"api-reference/endpoints/app-connections/humanitec/get-by-name", "api-reference/endpoints/app-connections/humanitec/get-by-name",
"api-reference/endpoints/app-connections/humanitec/create",
"api-reference/endpoints/app-connections/humanitec/update",
"api-reference/endpoints/app-connections/humanitec/delete" "api-reference/endpoints/app-connections/humanitec/delete"
] ]
} }
@@ -45,58 +45,6 @@ export const HumanitecSyncFields = () => {
setValue("destinationConfig.env", ""); setValue("destinationConfig.env", "");
}} }}
/> />
<Controller
name="destinationConfig.scope"
control={control}
defaultValue={HumanitecSyncScope.Application}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="Scope"
tooltipClassName="max-w-lg py-3"
tooltipText={
<div className="flex flex-col gap-3">
<p>
Specify how Infisical should manage secrets from Humanitec. The following options
are available:
</p>
<ul className="flex list-disc flex-col gap-3 pl-4">
{Object.values(HUMANITEC_SYNC_SCOPES).map(({ name, description }) => {
return (
<li key={name}>
<p className="text-mineshaft-300">
<span className="font-medium text-bunker-200">{name}</span>: {description}
</p>
</li>
);
})}
</ul>
</div>
}
>
<Select
value={value}
onValueChange={(val) => {
onChange(val);
setValue("destinationConfig.env", "");
setValue("destinationConfig.app", "");
setValue("destinationConfig.org", "");
}}
className="w-full border border-mineshaft-500 capitalize"
position="popper"
placeholder="Select a scope..."
dropdownContainerClassName="max-w-none"
>
{Object.values(HumanitecSyncScope).map((scope) => (
<SelectItem className="capitalize" value={scope} key={scope}>
{scope.replace("-", " ")}
</SelectItem>
))}
</Select>
</FormControl>
)}
/>
<Controller <Controller
name="destinationConfig.org" name="destinationConfig.org"
control={control} control={control}
@@ -166,6 +114,56 @@ export const HumanitecSyncFields = () => {
</FormControl> </FormControl>
)} )}
/> />
<Controller
name="destinationConfig.scope"
control={control}
defaultValue={HumanitecSyncScope.Application}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="Scope"
tooltipClassName="max-w-lg py-3"
tooltipText={
<div className="flex flex-col gap-3">
<p>
Specify how Infisical should manage secrets from Humanitec. The following options
are available:
</p>
<ul className="flex list-disc flex-col gap-3 pl-4">
{Object.values(HUMANITEC_SYNC_SCOPES).map(({ name, description }) => {
return (
<li key={name}>
<p className="text-mineshaft-300">
<span className="font-medium text-bunker-200">{name}</span>: {description}
</p>
</li>
);
})}
</ul>
</div>
}
>
<Select
value={value}
onValueChange={(val) => {
onChange(val);
setValue("destinationConfig.env", "");
}}
className="w-full border border-mineshaft-500 capitalize"
position="popper"
placeholder="Select a scope..."
dropdownContainerClassName="max-w-none"
>
{Object.values(HumanitecSyncScope).map((scope) => (
<SelectItem className="capitalize" value={scope} key={scope}>
{scope.replace("-", " ")}
</SelectItem>
))}
</Select>
</FormControl>
)}
/>
{currentScope === HumanitecSyncScope.Environment && ( {currentScope === HumanitecSyncScope.Environment && (
<Controller <Controller
name="destinationConfig.env" name="destinationConfig.env"
+2 -2
View File
@@ -76,11 +76,11 @@ export const HUMANITEC_SYNC_SCOPES: Record<
[HumanitecSyncScope.Application]: { [HumanitecSyncScope.Application]: {
name: "Application", name: "Application",
description: description:
"Infisical will import any secrets present in the selected Humanitec application. This includes all secrets that are not limited to a specific environment." "Infisical will sync secrets as application level shared values to the specified Humanitec application."
}, },
[HumanitecSyncScope.Environment]: { [HumanitecSyncScope.Environment]: {
name: "Environment", name: "Environment",
description: description:
"Infisical will import any secrets present in the selected Humanitec environment. This includes all secrets that are limited to the selected environment." "Infisical will sync secrets as environment level shared values to the specified Humanitec application environment."
} }
}; };