mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-21 12:28:38 +00:00
Merge branch 'main' into feature/secret-reference-shortcut
This commit is contained in:
+7
-3
@@ -160,14 +160,18 @@ export const SecretApprovalRequestChanges = ({ approvalRequestId, onGoBack }: Pr
|
||||
secretApprovalRequestDetails.policy.bypassers.some(({ userId }) => userId === userSession.id);
|
||||
|
||||
const reviewedUsers = secretApprovalRequestDetails?.reviewers?.reduce<
|
||||
Record<string, { status: ApprovalStatus; comment: string; isOrgMembershipActive: boolean }>
|
||||
Record<
|
||||
string,
|
||||
{ status: ApprovalStatus; comment: string; isOrgMembershipActive: boolean; createdAt: Date }
|
||||
>
|
||||
>(
|
||||
(prev, curr) => ({
|
||||
...prev,
|
||||
[curr.userId]: {
|
||||
status: curr.status,
|
||||
comment: curr.comment,
|
||||
isOrgMembershipActive: curr.isOrgMembershipActive
|
||||
isOrgMembershipActive: curr.isOrgMembershipActive,
|
||||
createdAt: curr.createdAt
|
||||
}
|
||||
}),
|
||||
{}
|
||||
@@ -488,7 +492,7 @@ export const SecretApprovalRequestChanges = ({ approvalRequestId, onGoBack }: Pr
|
||||
</span>{" "}
|
||||
the request on{" "}
|
||||
{format(
|
||||
new Date(secretApprovalRequestDetails.createdAt),
|
||||
new Date(reviewer ? reviewer.createdAt : new Date()),
|
||||
"MM/dd/yyyy h:mm:ss aa"
|
||||
)}
|
||||
.
|
||||
|
||||
+1
-1
@@ -111,7 +111,7 @@ export const WorkflowIntegrationTab = () => {
|
||||
<Td>Provider</Td>
|
||||
<Td>Access Request Notifications Destination</Td>
|
||||
<Td>Secret Request Notifications Destination</Td>
|
||||
<Td />
|
||||
<Td>Secret Sync Error Notifications Destination</Td>
|
||||
</Tr>
|
||||
</THead>
|
||||
<TBody>
|
||||
|
||||
+3
-1
@@ -99,7 +99,9 @@ export const MicrosoftTeamsConfigRow = ({
|
||||
</Badge>
|
||||
)}
|
||||
</Td>
|
||||
|
||||
<Td>
|
||||
<Badge variant="danger">Disabled</Badge>
|
||||
</Td>
|
||||
<Td>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild className="rounded-lg">
|
||||
|
||||
+19
-1
@@ -96,7 +96,25 @@ export const SlackConfigRow = ({ handlePopUpOpen, isSlackConfigLoading, slackCon
|
||||
</Badge>
|
||||
)}
|
||||
</Td>
|
||||
|
||||
<Td>
|
||||
{slackConfig.isSecretSyncErrorNotificationEnabled &&
|
||||
!isLoadingConfig &&
|
||||
slackConfig.secretSyncErrorChannels.length > 0 ? (
|
||||
<p>
|
||||
{slackConfig.secretSyncErrorChannels
|
||||
.split(", ")
|
||||
.map((channel) => slackChannelIdToName[channel])
|
||||
.join(", ")}
|
||||
</p>
|
||||
) : isLoadingConfig ? (
|
||||
<Spinner size="xs" />
|
||||
) : (
|
||||
<Badge variant="neutral">
|
||||
<BanIcon />
|
||||
Disabled
|
||||
</Badge>
|
||||
)}
|
||||
</Td>
|
||||
<Td>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild className="rounded-lg">
|
||||
|
||||
+144
-114
@@ -37,11 +37,76 @@ const formSchema = z.object({
|
||||
isSecretRequestNotificationEnabled: z.boolean(),
|
||||
secretRequestChannels: z.string().array(),
|
||||
isAccessRequestNotificationEnabled: z.boolean(),
|
||||
accessRequestChannels: z.string().array()
|
||||
accessRequestChannels: z.string().array(),
|
||||
isSecretSyncErrorNotificationEnabled: z.boolean(),
|
||||
secretSyncErrorChannels: z.string().array()
|
||||
});
|
||||
|
||||
type TSlackConfigForm = z.infer<typeof formSchema>;
|
||||
|
||||
type TChannelSelectorProps = {
|
||||
value: string[];
|
||||
onChange: (value: string[]) => void;
|
||||
error?: { message?: string };
|
||||
slackChannelIdToName: Record<string, string>;
|
||||
sortedSlackChannels?: { id: string; name: string }[];
|
||||
keyPrefix: string;
|
||||
};
|
||||
|
||||
const ChannelSelector = ({
|
||||
value,
|
||||
onChange,
|
||||
error,
|
||||
slackChannelIdToName,
|
||||
sortedSlackChannels,
|
||||
keyPrefix
|
||||
}: TChannelSelectorProps) => (
|
||||
<FormControl label="Slack channels" isError={Boolean(error)} errorText={error?.message}>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Input
|
||||
isReadOnly
|
||||
value={value
|
||||
?.filter(Boolean)
|
||||
.map((entry) => slackChannelIdToName[entry])
|
||||
.join(", ")}
|
||||
className="text-left"
|
||||
/>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
style={{
|
||||
width: "var(--radix-dropdown-menu-trigger-width)",
|
||||
maxHeight: "350px",
|
||||
overflowY: "auto"
|
||||
}}
|
||||
side="bottom"
|
||||
align="start"
|
||||
>
|
||||
{sortedSlackChannels?.map((slackChannel) => {
|
||||
const isChecked = value?.includes(slackChannel.id);
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
onClick={(evt) => {
|
||||
evt.preventDefault();
|
||||
onChange(
|
||||
isChecked
|
||||
? value?.filter((el: string) => el !== slackChannel.id)
|
||||
: [...(value || []), slackChannel.id]
|
||||
);
|
||||
}}
|
||||
key={`${keyPrefix}-slack-channel-${slackChannel.id}`}
|
||||
iconPos="right"
|
||||
icon={isChecked && <FontAwesomeIcon icon={faCheckCircle} />}
|
||||
>
|
||||
{slackChannel.name}
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
})}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</FormControl>
|
||||
);
|
||||
|
||||
type Props = {
|
||||
onClose: () => void;
|
||||
};
|
||||
@@ -71,7 +136,9 @@ export const SlackIntegrationForm = ({ onClose }: Props) => {
|
||||
isAccessRequestNotificationEnabled: false,
|
||||
accessRequestChannels: [],
|
||||
isSecretRequestNotificationEnabled: false,
|
||||
secretRequestChannels: []
|
||||
secretRequestChannels: [],
|
||||
isSecretSyncErrorNotificationEnabled: false,
|
||||
secretSyncErrorChannels: []
|
||||
}
|
||||
});
|
||||
|
||||
@@ -86,7 +153,8 @@ export const SlackIntegrationForm = ({ onClose }: Props) => {
|
||||
integration: WorkflowIntegrationPlatform.SLACK,
|
||||
integrationId: data.slackIntegrationId,
|
||||
accessRequestChannels: data.accessRequestChannels.filter(Boolean).join(", "),
|
||||
secretRequestChannels: data.secretRequestChannels.filter(Boolean).join(", ")
|
||||
secretRequestChannels: data.secretRequestChannels.filter(Boolean).join(", "),
|
||||
secretSyncErrorChannels: data.secretSyncErrorChannels.filter(Boolean).join(", ")
|
||||
});
|
||||
|
||||
createNotification({
|
||||
@@ -100,6 +168,7 @@ export const SlackIntegrationForm = ({ onClose }: Props) => {
|
||||
const secretRequestNotifState = watch("isSecretRequestNotificationEnabled");
|
||||
const selectedSlackIntegrationId = watch("slackIntegrationId");
|
||||
const accessRequestNotifState = watch("isAccessRequestNotificationEnabled");
|
||||
const secretSyncErrorNotifState = watch("isSecretSyncErrorNotificationEnabled");
|
||||
|
||||
const { data: slackChannels } = useGetSlackIntegrationChannels(selectedSlackIntegrationId);
|
||||
const slackChannelIdToName = Object.fromEntries(
|
||||
@@ -110,7 +179,7 @@ export const SlackIntegrationForm = ({ onClose }: Props) => {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (slackConfig) {
|
||||
if (slackConfig && slackConfig.integration === WorkflowIntegrationPlatform.SLACK) {
|
||||
setValue("slackIntegrationId", slackConfig.integrationId);
|
||||
setValue(
|
||||
"isSecretRequestNotificationEnabled",
|
||||
@@ -120,22 +189,30 @@ export const SlackIntegrationForm = ({ onClose }: Props) => {
|
||||
"isAccessRequestNotificationEnabled",
|
||||
slackConfig.isAccessRequestNotificationEnabled
|
||||
);
|
||||
setValue(
|
||||
"isSecretSyncErrorNotificationEnabled",
|
||||
slackConfig.isSecretSyncErrorNotificationEnabled
|
||||
);
|
||||
|
||||
if (slackConfig.integration === WorkflowIntegrationPlatform.SLACK) {
|
||||
if (slackChannels) {
|
||||
setValue(
|
||||
"secretRequestChannels",
|
||||
slackConfig.secretRequestChannels
|
||||
.split(", ")
|
||||
.filter((channel) => channel in slackChannelIdToName)
|
||||
);
|
||||
setValue(
|
||||
"accessRequestChannels",
|
||||
slackConfig.accessRequestChannels
|
||||
.split(", ")
|
||||
.filter((channel) => channel in slackChannelIdToName)
|
||||
);
|
||||
}
|
||||
if (slackChannels) {
|
||||
setValue(
|
||||
"secretRequestChannels",
|
||||
(slackConfig.secretRequestChannels || "")
|
||||
.split(", ")
|
||||
.filter((channel) => channel in slackChannelIdToName)
|
||||
);
|
||||
setValue(
|
||||
"accessRequestChannels",
|
||||
(slackConfig.accessRequestChannels || "")
|
||||
.split(", ")
|
||||
.filter((channel) => channel in slackChannelIdToName)
|
||||
);
|
||||
setValue(
|
||||
"secretSyncErrorChannels",
|
||||
(slackConfig.secretSyncErrorChannels || "")
|
||||
.split(", ")
|
||||
.filter((channel) => channel in slackChannelIdToName)
|
||||
);
|
||||
}
|
||||
}
|
||||
}, [slackConfig, slackChannels]);
|
||||
@@ -208,54 +285,14 @@ export const SlackIntegrationForm = ({ onClose }: Props) => {
|
||||
control={control}
|
||||
name="secretRequestChannels"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Slack channels"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Input
|
||||
isReadOnly
|
||||
value={value
|
||||
?.filter(Boolean)
|
||||
.map((entry) => slackChannelIdToName[entry])
|
||||
.join(", ")}
|
||||
className="text-left"
|
||||
/>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
style={{
|
||||
width: "var(--radix-dropdown-menu-trigger-width)",
|
||||
maxHeight: "350px",
|
||||
overflowY: "auto"
|
||||
}}
|
||||
side="bottom"
|
||||
align="start"
|
||||
>
|
||||
{sortedSlackChannels?.map((slackChannel) => {
|
||||
const isChecked = value?.includes(slackChannel.id);
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
onClick={(evt) => {
|
||||
evt.preventDefault();
|
||||
onChange(
|
||||
isChecked
|
||||
? value?.filter((el: string) => el !== slackChannel.id)
|
||||
: [...(value || []), slackChannel.id]
|
||||
);
|
||||
}}
|
||||
key={`secret-requests-slack-channel-${slackChannel.id}`}
|
||||
iconPos="right"
|
||||
icon={isChecked && <FontAwesomeIcon icon={faCheckCircle} />}
|
||||
>
|
||||
{slackChannel.name}
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
})}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</FormControl>
|
||||
<ChannelSelector
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
error={error}
|
||||
slackChannelIdToName={slackChannelIdToName}
|
||||
sortedSlackChannels={sortedSlackChannels}
|
||||
keyPrefix="secret-requests"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
@@ -281,54 +318,47 @@ export const SlackIntegrationForm = ({ onClose }: Props) => {
|
||||
control={control}
|
||||
name="accessRequestChannels"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Slack channels"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Input
|
||||
isReadOnly
|
||||
value={value
|
||||
?.filter(Boolean)
|
||||
.map((entry) => slackChannelIdToName[entry])
|
||||
.join(", ")}
|
||||
className="text-left"
|
||||
/>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
style={{
|
||||
width: "var(--radix-dropdown-menu-trigger-width)",
|
||||
maxHeight: "350px",
|
||||
overflowY: "auto"
|
||||
}}
|
||||
side="bottom"
|
||||
align="start"
|
||||
>
|
||||
{sortedSlackChannels?.map((slackChannel) => {
|
||||
const isChecked = value?.includes(slackChannel.id);
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
onClick={(evt) => {
|
||||
evt.preventDefault();
|
||||
onChange(
|
||||
isChecked
|
||||
? value?.filter((el: string) => el !== slackChannel.id)
|
||||
: [...(value || []), slackChannel.id]
|
||||
);
|
||||
}}
|
||||
key={`access-requests-slack-channel-${slackChannel.id}`}
|
||||
iconPos="right"
|
||||
icon={isChecked && <FontAwesomeIcon icon={faCheckCircle} />}
|
||||
>
|
||||
{slackChannel.name}
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
})}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<ChannelSelector
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
error={error}
|
||||
slackChannelIdToName={slackChannelIdToName}
|
||||
sortedSlackChannels={sortedSlackChannels}
|
||||
keyPrefix="access-requests"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<Controller
|
||||
control={control}
|
||||
name="isSecretSyncErrorNotificationEnabled"
|
||||
render={({ field, fieldState: { error } }) => {
|
||||
return (
|
||||
<FormControl isError={Boolean(error)} errorText={error?.message} className="mb-2">
|
||||
<Switch
|
||||
id="secret-sync-error-notification"
|
||||
onCheckedChange={(value) => field.onChange(value)}
|
||||
isChecked={field.value}
|
||||
>
|
||||
<p className="w-full">Secret Sync Errors</p>
|
||||
</Switch>
|
||||
</FormControl>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{secretSyncErrorNotifState && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="secretSyncErrorChannels"
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<ChannelSelector
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
error={error}
|
||||
slackChannelIdToName={slackChannelIdToName}
|
||||
sortedSlackChannels={sortedSlackChannels}
|
||||
keyPrefix="secret-sync-errors"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user