mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-10 01:28:33 +00:00
feat: chef secret-sync
This commit is contained in:
+97
@@ -0,0 +1,97 @@
|
||||
import { Controller, useFormContext, useWatch } from "react-hook-form";
|
||||
import { SingleValue } from "react-select";
|
||||
|
||||
import { SecretSyncConnectionField } from "@app/components/secret-syncs/forms/SecretSyncConnectionField";
|
||||
import { FilterableSelect, FormControl } from "@app/components/v2";
|
||||
import {
|
||||
TChefDataBag,
|
||||
useChefConnectionListDataBags,
|
||||
useChefConnectionListDataBagItems,
|
||||
TChefDataBagItem
|
||||
} from "@app/hooks/api/appConnections/chef";
|
||||
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||
|
||||
import { TSecretSyncForm } from "../schemas";
|
||||
|
||||
export const ChefSyncFields = () => {
|
||||
const { control, setValue } = useFormContext<
|
||||
TSecretSyncForm & { destination: SecretSync.Chef }
|
||||
>();
|
||||
|
||||
const connectionId = useWatch({ name: "connection.id", control });
|
||||
const dataBagName = useWatch({ name: "destinationConfig.dataBagName", control });
|
||||
const dataBagItemName = useWatch({ name: "destinationConfig.dataBagItemName", control });
|
||||
|
||||
const { data: dataBags, isLoading: isDataBagsLoading } = useChefConnectionListDataBags(
|
||||
connectionId,
|
||||
{
|
||||
enabled: Boolean(connectionId)
|
||||
}
|
||||
);
|
||||
|
||||
const { data: dataBagItems, isLoading: isDataBagItemsLoading } =
|
||||
useChefConnectionListDataBagItems(connectionId, dataBagName, {
|
||||
enabled: Boolean(connectionId && dataBagName)
|
||||
});
|
||||
|
||||
console.log({ dataBags, dataBagItems });
|
||||
|
||||
const handleChangeConnection = () => {
|
||||
setValue("destinationConfig.dataBagName", "");
|
||||
setValue("destinationConfig.dataBagItemName", "");
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<SecretSyncConnectionField onChange={handleChangeConnection} />
|
||||
|
||||
<Controller
|
||||
name="destinationConfig.dataBagName"
|
||||
control={control}
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl isError={Boolean(error)} errorText={error?.message} label="Data Bag">
|
||||
<FilterableSelect
|
||||
menuPlacement="top"
|
||||
isLoading={isDataBagsLoading && Boolean(connectionId)}
|
||||
isDisabled={!connectionId}
|
||||
value={dataBags?.find((dataBag) => dataBag.name === value) ?? null}
|
||||
onChange={(option) => {
|
||||
const selectedDataBag = option as SingleValue<TChefDataBag>;
|
||||
onChange(selectedDataBag?.name ?? "");
|
||||
setValue("destinationConfig.dataBagItemName", "");
|
||||
}}
|
||||
options={dataBags}
|
||||
placeholder="Select a data bag..."
|
||||
getOptionLabel={(option) => option.name}
|
||||
getOptionValue={(option) => option.name}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="destinationConfig.dataBagItemName"
|
||||
control={control}
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl isError={Boolean(error)} errorText={error?.message} label="Data Bag Item">
|
||||
<FilterableSelect
|
||||
menuPlacement="top"
|
||||
isLoading={isDataBagItemsLoading && Boolean(connectionId && dataBagName)}
|
||||
isDisabled={!connectionId || !dataBagName}
|
||||
value={dataBagItems?.find((dataBagItem) => dataBagItem.name === value) ?? null}
|
||||
onChange={(option) => {
|
||||
const selectedDataBagItem = option as SingleValue<TChefDataBagItem>;
|
||||
onChange(selectedDataBagItem?.name ?? "");
|
||||
setValue("destinationConfig.dataBagItemName", selectedDataBagItem?.name ?? "");
|
||||
}}
|
||||
options={dataBagItems}
|
||||
placeholder="Select a data bag item..."
|
||||
getOptionLabel={(option) => option.name}
|
||||
getOptionValue={(option) => option.name}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
+3
@@ -34,6 +34,7 @@ import { TerraformCloudSyncFields } from "./TerraformCloudSyncFields";
|
||||
import { VercelSyncFields } from "./VercelSyncFields";
|
||||
import { WindmillSyncFields } from "./WindmillSyncFields";
|
||||
import { ZabbixSyncFields } from "./ZabbixSyncFields";
|
||||
import { ChefSyncFields } from "./ChefSyncFields";
|
||||
|
||||
export const SecretSyncDestinationFields = () => {
|
||||
const { watch } = useFormContext<TSecretSyncForm>();
|
||||
@@ -103,6 +104,8 @@ export const SecretSyncDestinationFields = () => {
|
||||
return <BitbucketSyncFields />;
|
||||
case SecretSync.LaravelForge:
|
||||
return <LaravelForgeSyncFields />;
|
||||
case SecretSync.Chef:
|
||||
return <ChefSyncFields />;
|
||||
default:
|
||||
throw new Error(`Unhandled Destination Config Field: ${destination}`);
|
||||
}
|
||||
|
||||
+1
@@ -70,6 +70,7 @@ export const SecretSyncOptionsFields = ({ hideInitialSync }: Props) => {
|
||||
case SecretSync.Netlify:
|
||||
case SecretSync.Bitbucket:
|
||||
case SecretSync.LaravelForge:
|
||||
case SecretSync.Chef:
|
||||
AdditionalSyncOptionsFieldsComponent = null;
|
||||
break;
|
||||
default:
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { useFormContext } from "react-hook-form";
|
||||
|
||||
import { TSecretSyncForm } from "@app/components/secret-syncs/forms/schemas";
|
||||
import { GenericFieldLabel } from "@app/components/v2";
|
||||
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||
|
||||
export const ChefSyncReviewFields = () => {
|
||||
const { watch } = useFormContext<TSecretSyncForm & { destination: SecretSync.Chef }>();
|
||||
const dataBagName = watch("destinationConfig.dataBagName");
|
||||
const dataBagItemName = watch("destinationConfig.dataBagItemName");
|
||||
|
||||
return (
|
||||
<>
|
||||
<GenericFieldLabel label="Data Bag">{dataBagName}</GenericFieldLabel>
|
||||
<GenericFieldLabel label="Data Bag Item">{dataBagItemName}</GenericFieldLabel>
|
||||
</>
|
||||
);
|
||||
};
|
||||
+4
@@ -47,6 +47,7 @@ import { TerraformCloudSyncReviewFields } from "./TerraformCloudSyncReviewFields
|
||||
import { VercelSyncReviewFields } from "./VercelSyncReviewFields";
|
||||
import { WindmillSyncReviewFields } from "./WindmillSyncReviewFields";
|
||||
import { ZabbixSyncReviewFields } from "./ZabbixSyncReviewFields";
|
||||
import { ChefSyncReviewFields } from "./ChefSyncReviewFields";
|
||||
|
||||
export const SecretSyncReviewFields = () => {
|
||||
const { watch } = useFormContext<TSecretSyncForm>();
|
||||
@@ -173,6 +174,9 @@ export const SecretSyncReviewFields = () => {
|
||||
case SecretSync.LaravelForge:
|
||||
DestinationFieldsComponent = <LaravelForgeSyncReviewFields />;
|
||||
break;
|
||||
case SecretSync.Chef:
|
||||
DestinationFieldsComponent = <ChefSyncReviewFields />;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unhandled Destination Review Fields: ${destination}`);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { BaseSecretSyncSchema } from "@app/components/secret-syncs/forms/schemas/base-secret-sync-schema";
|
||||
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||
|
||||
export const ChefSyncDestinationSchema = BaseSecretSyncSchema().merge(
|
||||
z.object({
|
||||
destination: z.literal(SecretSync.Chef),
|
||||
destinationConfig: z.object({
|
||||
dataBagName: z.string().trim().min(1, "Data Bag Name required"),
|
||||
dataBagItemName: z.string().trim().min(1, "Data Bag Item Name required")
|
||||
})
|
||||
})
|
||||
);
|
||||
@@ -31,6 +31,7 @@ import { TerraformCloudSyncDestinationSchema } from "./terraform-cloud-destinati
|
||||
import { VercelSyncDestinationSchema } from "./vercel-sync-destination-schema";
|
||||
import { WindmillSyncDestinationSchema } from "./windmill-sync-destination-schema";
|
||||
import { ZabbixSyncDestinationSchema } from "./zabbix-sync-destination-schema";
|
||||
import { ChefSyncDestinationSchema } from "./chef-sync-destination-schema";
|
||||
|
||||
const SecretSyncUnionSchema = z.discriminatedUnion("destination", [
|
||||
AwsParameterStoreSyncDestinationSchema,
|
||||
@@ -63,7 +64,8 @@ const SecretSyncUnionSchema = z.discriminatedUnion("destination", [
|
||||
DigitalOceanAppPlatformSyncDestinationSchema,
|
||||
NetlifySyncDestinationSchema,
|
||||
BitbucketSyncDestinationSchema,
|
||||
LaravelForgeSyncDestinationSchema
|
||||
LaravelForgeSyncDestinationSchema,
|
||||
ChefSyncDestinationSchema
|
||||
]);
|
||||
|
||||
export const SecretSyncFormSchema = SecretSyncUnionSchema;
|
||||
|
||||
@@ -117,6 +117,10 @@ export const SECRET_SYNC_MAP: Record<SecretSync, { name: string; image: string }
|
||||
[SecretSync.LaravelForge]: {
|
||||
name: "Laravel Forge",
|
||||
image: "Laravel Forge.png"
|
||||
},
|
||||
[SecretSync.Chef]: {
|
||||
name: "Chef",
|
||||
image: "Chef.png"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -151,7 +155,8 @@ export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
||||
[SecretSync.DigitalOceanAppPlatform]: AppConnection.DigitalOcean,
|
||||
[SecretSync.Netlify]: AppConnection.Netlify,
|
||||
[SecretSync.Bitbucket]: AppConnection.Bitbucket,
|
||||
[SecretSync.LaravelForge]: AppConnection.LaravelForge
|
||||
[SecretSync.LaravelForge]: AppConnection.LaravelForge,
|
||||
[SecretSync.Chef]: AppConnection.Chef
|
||||
};
|
||||
|
||||
export const SECRET_SYNC_INITIAL_SYNC_BEHAVIOR_MAP: Record<
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./queries";
|
||||
export * from "./types";
|
||||
@@ -0,0 +1,68 @@
|
||||
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
import { appConnectionKeys } from "@app/hooks/api/appConnections";
|
||||
|
||||
import { TChefDataBag, TChefDataBagItem } from "./types";
|
||||
|
||||
const chefConnectionKeys = {
|
||||
all: [...appConnectionKeys.all, "chef"] as const,
|
||||
listDataBags: (connectionId: string) =>
|
||||
[...chefConnectionKeys.all, "data-bags", connectionId] as const,
|
||||
listDataBagItems: (connectionId: string, dataBagName: string) =>
|
||||
[...chefConnectionKeys.all, "data-bag-items", connectionId, dataBagName] as const
|
||||
};
|
||||
|
||||
export const useChefConnectionListDataBags = (
|
||||
connectionId: string,
|
||||
options?: Omit<
|
||||
UseQueryOptions<
|
||||
TChefDataBag[],
|
||||
unknown,
|
||||
TChefDataBag[],
|
||||
ReturnType<typeof chefConnectionKeys.listDataBags>
|
||||
>,
|
||||
"queryKey" | "queryFn"
|
||||
>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: chefConnectionKeys.listDataBags(connectionId),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<TChefDataBag[]>(
|
||||
`/api/v1/app-connections/chef/${connectionId}/data-bags`
|
||||
);
|
||||
|
||||
return data;
|
||||
},
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
export const useChefConnectionListDataBagItems = (
|
||||
connectionId: string,
|
||||
dataBagName: string,
|
||||
options?: Omit<
|
||||
UseQueryOptions<
|
||||
TChefDataBagItem[],
|
||||
unknown,
|
||||
TChefDataBagItem[],
|
||||
ReturnType<typeof chefConnectionKeys.listDataBagItems>
|
||||
>,
|
||||
"queryKey" | "queryFn"
|
||||
>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: chefConnectionKeys.listDataBagItems(connectionId, dataBagName),
|
||||
queryFn: async () => {
|
||||
const params = { dataBagName };
|
||||
const { data } = await apiRequest.get<TChefDataBagItem[]>(
|
||||
`/api/v1/app-connections/chef/${connectionId}/data-bag-items`,
|
||||
{ params }
|
||||
);
|
||||
|
||||
return data;
|
||||
},
|
||||
enabled: Boolean(connectionId && dataBagName),
|
||||
...options
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
export type TChefDataBag = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type TChefDataBagItem = {
|
||||
name: string;
|
||||
};
|
||||
@@ -29,7 +29,8 @@ export enum SecretSync {
|
||||
DigitalOceanAppPlatform = "digital-ocean-app-platform",
|
||||
Netlify = "netlify",
|
||||
Bitbucket = "bitbucket",
|
||||
LaravelForge = "laravel-forge"
|
||||
LaravelForge = "laravel-forge",
|
||||
Chef = "chef"
|
||||
}
|
||||
|
||||
export enum SecretSyncStatus {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||
import { TRootSecretSync } from "@app/hooks/api/secretSyncs/types/root-sync";
|
||||
|
||||
export type TChefSync = TRootSecretSync & {
|
||||
destination: SecretSync.Chef;
|
||||
destinationConfig: {
|
||||
dataBagName: string;
|
||||
dataBagItemName: string;
|
||||
};
|
||||
connection: {
|
||||
app: AppConnection.Chef;
|
||||
name: string;
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
@@ -32,6 +32,7 @@ import { TTerraformCloudSync } from "./terraform-cloud-sync";
|
||||
import { TVercelSync } from "./vercel-sync";
|
||||
import { TWindmillSync } from "./windmill-sync";
|
||||
import { TZabbixSync } from "./zabbix-sync";
|
||||
import { TChefSync } from "./chef-sync";
|
||||
|
||||
export type TSecretSyncOption = {
|
||||
name: string;
|
||||
@@ -71,7 +72,8 @@ export type TSecretSync =
|
||||
| TDigitalOceanAppPlatformSync
|
||||
| TNetlifySync
|
||||
| TBitbucketSync
|
||||
| TLaravelForgeSync;
|
||||
| TLaravelForgeSync
|
||||
| TChefSync;
|
||||
|
||||
export type TListSecretSyncs = { secretSyncs: TSecretSync[] };
|
||||
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { TChefSync } from "@app/hooks/api/secretSyncs/types/chef-sync";
|
||||
|
||||
import { getSecretSyncDestinationColValues } from "../helpers";
|
||||
import { SecretSyncTableCell } from "../SecretSyncTableCell";
|
||||
|
||||
type Props = {
|
||||
secretSync: TChefSync;
|
||||
};
|
||||
|
||||
export const ChefSyncDestinationCol = ({ secretSync }: Props) => {
|
||||
const { primaryText, secondaryText } = getSecretSyncDestinationColValues(secretSync);
|
||||
|
||||
return <SecretSyncTableCell primaryText={primaryText} secondaryText={secondaryText} />;
|
||||
};
|
||||
+3
@@ -31,6 +31,7 @@ import { TerraformCloudSyncDestinationCol } from "./TerraformCloudSyncDestinatio
|
||||
import { VercelSyncDestinationCol } from "./VercelSyncDestinationCol";
|
||||
import { WindmillSyncDestinationCol } from "./WindmillSyncDestinationCol";
|
||||
import { ZabbixSyncDestinationCol } from "./ZabbixSyncDestinationCol";
|
||||
import { ChefSyncDestinationCol } from "./ChefSyncDestinationCol";
|
||||
|
||||
type Props = {
|
||||
secretSync: TSecretSync;
|
||||
@@ -100,6 +101,8 @@ export const SecretSyncDestinationCol = ({ secretSync }: Props) => {
|
||||
return <BitbucketSyncDestinationCol secretSync={secretSync} />;
|
||||
case SecretSync.LaravelForge:
|
||||
return <LaravelForgeSyncDestinationCol secretSync={secretSync} />;
|
||||
case SecretSync.Chef:
|
||||
return <ChefSyncDestinationCol secretSync={secretSync} />;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unhandled Secret Sync Destination Col: ${(secretSync as TSecretSync).destination}`
|
||||
|
||||
+4
@@ -198,6 +198,10 @@ export const getSecretSyncDestinationColValues = (secretSync: TSecretSync) => {
|
||||
primaryText = destinationConfig.siteName || destinationConfig.siteId;
|
||||
secondaryText = destinationConfig.orgName || destinationConfig.orgSlug;
|
||||
break;
|
||||
case SecretSync.Chef:
|
||||
primaryText = destinationConfig.dataBagName;
|
||||
secondaryText = destinationConfig.dataBagItemName;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unhandled Destination Col Values ${destination}`);
|
||||
}
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { GenericFieldLabel } from "@app/components/secret-syncs";
|
||||
import { TChefSync } from "@app/hooks/api/secretSyncs/types/chef-sync";
|
||||
|
||||
type Props = {
|
||||
secretSync: TChefSync;
|
||||
};
|
||||
|
||||
export const ChefSyncDestinationSection = ({ secretSync }: Props) => {
|
||||
const { destinationConfig } = secretSync;
|
||||
|
||||
return (
|
||||
<>
|
||||
<GenericFieldLabel label="Data Bag">{destinationConfig.dataBagName}</GenericFieldLabel>
|
||||
<GenericFieldLabel label="Data Bag Item">
|
||||
{destinationConfig.dataBagItemName}
|
||||
</GenericFieldLabel>
|
||||
</>
|
||||
);
|
||||
};
|
||||
+4
@@ -42,6 +42,7 @@ import { TerraformCloudSyncDestinationSection } from "./TerraformCloudSyncDestin
|
||||
import { VercelSyncDestinationSection } from "./VercelSyncDestinationSection";
|
||||
import { WindmillSyncDestinationSection } from "./WindmillSyncDestinationSection";
|
||||
import { ZabbixSyncDestinationSection } from "./ZabbixSyncDestinationSection";
|
||||
import { ChefSyncDestinationSection } from "./ChefSyncDestinationSection";
|
||||
|
||||
type Props = {
|
||||
secretSync: TSecretSync;
|
||||
@@ -152,6 +153,9 @@ export const SecretSyncDestinationSection = ({ secretSync, onEditDestination }:
|
||||
case SecretSync.LaravelForge:
|
||||
DestinationComponents = <LaravelForgeSyncDestinationSection secretSync={secretSync} />;
|
||||
break;
|
||||
case SecretSync.Chef:
|
||||
DestinationComponents = <ChefSyncDestinationSection secretSync={secretSync} />;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unhandled Destination Section components: ${destination}`);
|
||||
}
|
||||
|
||||
+1
@@ -73,6 +73,7 @@ export const SecretSyncOptionsSection = ({ secretSync, onEditOptions }: Props) =
|
||||
case SecretSync.Netlify:
|
||||
case SecretSync.Bitbucket:
|
||||
case SecretSync.LaravelForge:
|
||||
case SecretSync.Chef:
|
||||
AdditionalSyncOptionsComponent = null;
|
||||
break;
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user