mirror of
https://github.com/Infisical/infisical.git
synced 2025-03-31 22:09:57 +00:00
Enable users to change the ordering of environments
This commit is contained in:
backend/src
frontend/src
hooks/api/workspace
views/Settings/ProjectSettingsPage/components/EnvironmentSection
@ -85,6 +85,43 @@ export const createWorkspaceEnvironment = async (
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Swaps the ordering of two environments in the database. This is purely for asthetic purposes.
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const reorderWorkspaceEnvironments = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
) => {
|
||||
const { workspaceId } = req.params;
|
||||
const { environmentSlug, environmentName, otherEnvironmentSlug, otherEnvironmentName } = req.body;
|
||||
|
||||
// atomic update the env to avoid conflict
|
||||
const workspace = await Workspace.findById(workspaceId).exec();
|
||||
if (!workspace) {
|
||||
throw new Error("Failed to create workspace environment");
|
||||
}
|
||||
|
||||
const environmentIndex = workspace.environments.findIndex((env) => env.name === environmentName && env.slug === environmentSlug)
|
||||
const otherEnvironmentIndex = workspace.environments.findIndex((env) => env.name === otherEnvironmentName && env.slug === otherEnvironmentSlug)
|
||||
|
||||
if (environmentIndex === undefined || otherEnvironmentIndex === undefined) {
|
||||
throw new Error("environment or otherEnvironment couldn't be found")
|
||||
}
|
||||
|
||||
// swap the order of the environments
|
||||
[workspace.environments[environmentIndex], workspace.environments[otherEnvironmentIndex]] = [workspace.environments[otherEnvironmentIndex], workspace.environments[environmentIndex]]
|
||||
|
||||
await workspace.save()
|
||||
|
||||
return res.status(200).send({
|
||||
message: "Successfully reordered environments",
|
||||
workspace: workspaceId,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Rename workspace environment with new name and slug of a workspace with [workspaceId]
|
||||
* Old slug [oldEnvironmentSlug] must be provided
|
||||
@ -124,7 +161,7 @@ export const renameWorkspaceEnvironment = async (
|
||||
if (envIndex === -1) {
|
||||
throw new Error("Invalid environment given");
|
||||
}
|
||||
|
||||
|
||||
const oldEnvironment = workspace.environments[envIndex];
|
||||
|
||||
workspace.environments[envIndex].name = environmentName;
|
||||
@ -159,7 +196,7 @@ export const renameWorkspaceEnvironment = async (
|
||||
{ $set: { "deniedPermissions.$[element].environmentSlug": environmentSlug } },
|
||||
{ arrayFilters: [{ "element.environmentSlug": oldEnvironmentSlug }] }
|
||||
);
|
||||
|
||||
|
||||
await EEAuditLogService.createAuditLog(
|
||||
req.authData,
|
||||
{
|
||||
@ -210,7 +247,7 @@ export const deleteWorkspaceEnvironment = async (
|
||||
if (envIndex === -1) {
|
||||
throw new Error("Invalid environment given");
|
||||
}
|
||||
|
||||
|
||||
const oldEnvironment = workspace.environments[envIndex];
|
||||
|
||||
workspace.environments.splice(envIndex, 1);
|
||||
|
@ -46,6 +46,24 @@ router.put(
|
||||
environmentController.renameWorkspaceEnvironment
|
||||
);
|
||||
|
||||
router.patch(
|
||||
"/:workspaceId/environments",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT],
|
||||
}),
|
||||
requireWorkspaceAuth({
|
||||
acceptedRoles: [ADMIN, MEMBER],
|
||||
locationWorkspaceId: "params",
|
||||
}),
|
||||
param("workspaceId").exists().trim(),
|
||||
body("environmentSlug").exists().isString().trim(),
|
||||
body("environmentName").exists().isString().trim(),
|
||||
body("otherEnvironmentSlug").exists().isString().trim(),
|
||||
body("otherEnvironmentName").exists().isString().trim(),
|
||||
validateRequest,
|
||||
environmentController.reorderWorkspaceEnvironments
|
||||
);
|
||||
|
||||
router.delete(
|
||||
"/:workspaceId/environments",
|
||||
requireAuth({
|
||||
|
@ -16,6 +16,7 @@ export {
|
||||
useGetWorkspaceUsers,
|
||||
useNameWorkspaceSecrets,
|
||||
useRenameWorkspace,
|
||||
useReorderWsEnvironment,
|
||||
useToggleAutoCapitalization,
|
||||
useUpdateUserWorkspaceRole,
|
||||
useUpdateWsEnvironment} from "./queries";
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
GetWsEnvironmentDTO,
|
||||
NameWorkspaceSecretsDTO,
|
||||
RenameWorkspaceDTO,
|
||||
ReorderEnvironmentsDTO,
|
||||
ToggleAutoCapitalizationDTO,
|
||||
UpdateEnvironmentDTO,
|
||||
Workspace,
|
||||
@ -244,6 +245,21 @@ export const useCreateWsEnvironment = () => {
|
||||
});
|
||||
};
|
||||
|
||||
export const useReorderWsEnvironment = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<{}, {}, ReorderEnvironmentsDTO>({
|
||||
mutationFn: ({ workspaceID, environmentSlug, environmentName, otherEnvironmentSlug, otherEnvironmentName}) => {
|
||||
return apiRequest.patch(`/api/v2/workspace/${workspaceID}/environments`, {
|
||||
environmentSlug, environmentName, otherEnvironmentSlug, otherEnvironmentName
|
||||
});
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries(workspaceKeys.getAllUserWorkspace);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateWsEnvironment = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
|
@ -46,6 +46,15 @@ export type CreateEnvironmentDTO = {
|
||||
environmentName: string;
|
||||
};
|
||||
|
||||
export type ReorderEnvironmentsDTO = {
|
||||
workspaceID: string;
|
||||
environmentSlug: string;
|
||||
environmentName: string;
|
||||
otherEnvironmentSlug: string;
|
||||
otherEnvironmentName: string;
|
||||
|
||||
};
|
||||
|
||||
export type UpdateEnvironmentDTO = {
|
||||
workspaceID: string;
|
||||
oldEnvironmentSlug: string;
|
||||
|
69
frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/EnvironmentTable.tsx
69
frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/EnvironmentTable.tsx
@ -1,6 +1,7 @@
|
||||
import { faPencil, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faArrowDown,faArrowUp, faPencil, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import {
|
||||
EmptyState,
|
||||
IconButton,
|
||||
@ -14,6 +15,9 @@ import {
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import { useWorkspace } from "@app/context";
|
||||
import {
|
||||
useReorderWsEnvironment
|
||||
} from "@app/hooks/api";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
type Props = {
|
||||
@ -31,6 +35,43 @@ type Props = {
|
||||
|
||||
export const EnvironmentTable = ({ handlePopUpOpen }: Props) => {
|
||||
const { currentWorkspace, isLoading } = useWorkspace();
|
||||
const { createNotification } = useNotificationContext();
|
||||
const reorderWsEnvironment = useReorderWsEnvironment();
|
||||
|
||||
const reorderEnvs = async (moveUp: boolean, name: string, slug: string) => {
|
||||
try {
|
||||
if (!currentWorkspace?._id) return;
|
||||
|
||||
const indexOfEnv = currentWorkspace.environments.findIndex((env) => env.name === name && env.slug === slug);
|
||||
|
||||
// check that this reordering is possible
|
||||
if (indexOfEnv === 0 && moveUp || indexOfEnv === currentWorkspace.environments.length - 1 && !moveUp) {
|
||||
return
|
||||
}
|
||||
|
||||
const indexToSwap = moveUp ? indexOfEnv - 1 : indexOfEnv + 1
|
||||
|
||||
await reorderWsEnvironment.mutateAsync({
|
||||
workspaceID: currentWorkspace._id,
|
||||
environmentSlug: slug,
|
||||
environmentName: name,
|
||||
otherEnvironmentSlug: currentWorkspace.environments[indexToSwap].slug,
|
||||
otherEnvironmentName: currentWorkspace.environments[indexToSwap].name
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Successfully re-ordered environments",
|
||||
type: "success"
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: "Failed to re-order environments",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<TableContainer>
|
||||
<Table>
|
||||
@ -45,11 +86,35 @@ export const EnvironmentTable = ({ handlePopUpOpen }: Props) => {
|
||||
{isLoading && <TableSkeleton columns={3} innerKey="project-envs" />}
|
||||
{!isLoading &&
|
||||
currentWorkspace &&
|
||||
currentWorkspace.environments.map(({ name, slug }) => (
|
||||
currentWorkspace.environments.map(({ name, slug }, pos) => (
|
||||
<Tr key={name}>
|
||||
<Td>{name}</Td>
|
||||
<Td>{slug}</Td>
|
||||
<Td className="flex items-center justify-end">
|
||||
<IconButton
|
||||
className="mr-3 py-2"
|
||||
onClick={() => {
|
||||
reorderEnvs(false, name, slug)
|
||||
}}
|
||||
colorSchema="primary"
|
||||
variant="plain"
|
||||
ariaLabel="update"
|
||||
isDisabled={pos === currentWorkspace.environments.length - 1}
|
||||
>
|
||||
<FontAwesomeIcon icon={faArrowDown} />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
className="mr-3 py-2"
|
||||
onClick={() => {
|
||||
reorderEnvs(true, name, slug)
|
||||
}}
|
||||
colorSchema="primary"
|
||||
variant="plain"
|
||||
ariaLabel="update"
|
||||
isDisabled={pos === 0}
|
||||
>
|
||||
<FontAwesomeIcon icon={faArrowUp} />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
className="mr-3 py-2"
|
||||
onClick={() => {
|
||||
|
Reference in New Issue
Block a user