mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 19:28:09 +00:00
Merge pull request #868 from daninge98/custom-environment-sorting
Adds user customizable environment ordering
This commit is contained in:
@@ -85,6 +85,43 @@ export const createWorkspaceEnvironment = async (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Swaps the ordering of two environments in the database. This is purely for aesthetic 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 BadRequestError({message: "Couldn't load workspace"});
|
||||||
|
}
|
||||||
|
|
||||||
|
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 === -1 || otherEnvironmentIndex === -1) {
|
||||||
|
throw BadRequestError({message: "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]
|
* Rename workspace environment with new name and slug of a workspace with [workspaceId]
|
||||||
* Old slug [oldEnvironmentSlug] must be provided
|
* Old slug [oldEnvironmentSlug] must be provided
|
||||||
@@ -124,7 +161,7 @@ export const renameWorkspaceEnvironment = async (
|
|||||||
if (envIndex === -1) {
|
if (envIndex === -1) {
|
||||||
throw new Error("Invalid environment given");
|
throw new Error("Invalid environment given");
|
||||||
}
|
}
|
||||||
|
|
||||||
const oldEnvironment = workspace.environments[envIndex];
|
const oldEnvironment = workspace.environments[envIndex];
|
||||||
|
|
||||||
workspace.environments[envIndex].name = environmentName;
|
workspace.environments[envIndex].name = environmentName;
|
||||||
@@ -159,7 +196,7 @@ export const renameWorkspaceEnvironment = async (
|
|||||||
{ $set: { "deniedPermissions.$[element].environmentSlug": environmentSlug } },
|
{ $set: { "deniedPermissions.$[element].environmentSlug": environmentSlug } },
|
||||||
{ arrayFilters: [{ "element.environmentSlug": oldEnvironmentSlug }] }
|
{ arrayFilters: [{ "element.environmentSlug": oldEnvironmentSlug }] }
|
||||||
);
|
);
|
||||||
|
|
||||||
await EEAuditLogService.createAuditLog(
|
await EEAuditLogService.createAuditLog(
|
||||||
req.authData,
|
req.authData,
|
||||||
{
|
{
|
||||||
@@ -210,7 +247,7 @@ export const deleteWorkspaceEnvironment = async (
|
|||||||
if (envIndex === -1) {
|
if (envIndex === -1) {
|
||||||
throw new Error("Invalid environment given");
|
throw new Error("Invalid environment given");
|
||||||
}
|
}
|
||||||
|
|
||||||
const oldEnvironment = workspace.environments[envIndex];
|
const oldEnvironment = workspace.environments[envIndex];
|
||||||
|
|
||||||
workspace.environments.splice(envIndex, 1);
|
workspace.environments.splice(envIndex, 1);
|
||||||
|
|||||||
@@ -46,6 +46,24 @@ router.put(
|
|||||||
environmentController.renameWorkspaceEnvironment
|
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(
|
router.delete(
|
||||||
"/:workspaceId/environments",
|
"/:workspaceId/environments",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export {
|
|||||||
useGetWorkspaceUsers,
|
useGetWorkspaceUsers,
|
||||||
useNameWorkspaceSecrets,
|
useNameWorkspaceSecrets,
|
||||||
useRenameWorkspace,
|
useRenameWorkspace,
|
||||||
|
useReorderWsEnvironment,
|
||||||
useToggleAutoCapitalization,
|
useToggleAutoCapitalization,
|
||||||
useUpdateUserWorkspaceRole,
|
useUpdateUserWorkspaceRole,
|
||||||
useUpdateWsEnvironment} from "./queries";
|
useUpdateWsEnvironment} from "./queries";
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
GetWsEnvironmentDTO,
|
GetWsEnvironmentDTO,
|
||||||
NameWorkspaceSecretsDTO,
|
NameWorkspaceSecretsDTO,
|
||||||
RenameWorkspaceDTO,
|
RenameWorkspaceDTO,
|
||||||
|
ReorderEnvironmentsDTO,
|
||||||
ToggleAutoCapitalizationDTO,
|
ToggleAutoCapitalizationDTO,
|
||||||
UpdateEnvironmentDTO,
|
UpdateEnvironmentDTO,
|
||||||
Workspace,
|
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 = () => {
|
export const useUpdateWsEnvironment = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,15 @@ export type CreateEnvironmentDTO = {
|
|||||||
environmentName: string;
|
environmentName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ReorderEnvironmentsDTO = {
|
||||||
|
workspaceID: string;
|
||||||
|
environmentSlug: string;
|
||||||
|
environmentName: string;
|
||||||
|
otherEnvironmentSlug: string;
|
||||||
|
otherEnvironmentName: string;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
export type UpdateEnvironmentDTO = {
|
export type UpdateEnvironmentDTO = {
|
||||||
workspaceID: string;
|
workspaceID: string;
|
||||||
oldEnvironmentSlug: string;
|
oldEnvironmentSlug: string;
|
||||||
|
|||||||
+67
-2
@@ -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 { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
|
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||||
import {
|
import {
|
||||||
EmptyState,
|
EmptyState,
|
||||||
IconButton,
|
IconButton,
|
||||||
@@ -14,6 +15,9 @@ import {
|
|||||||
Tr
|
Tr
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useWorkspace } from "@app/context";
|
import { useWorkspace } from "@app/context";
|
||||||
|
import {
|
||||||
|
useReorderWsEnvironment
|
||||||
|
} from "@app/hooks/api";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -31,6 +35,43 @@ type Props = {
|
|||||||
|
|
||||||
export const EnvironmentTable = ({ handlePopUpOpen }: Props) => {
|
export const EnvironmentTable = ({ handlePopUpOpen }: Props) => {
|
||||||
const { currentWorkspace, isLoading } = useWorkspace();
|
const { currentWorkspace, isLoading } = useWorkspace();
|
||||||
|
const { createNotification } = useNotificationContext();
|
||||||
|
const reorderWsEnvironment = useReorderWsEnvironment();
|
||||||
|
|
||||||
|
const handleReorderEnv= async (shouldMoveUp: 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 && shouldMoveUp || indexOfEnv === currentWorkspace.environments.length - 1 && !shouldMoveUp) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const indexToSwap = shouldMoveUp ? 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 (
|
return (
|
||||||
<TableContainer>
|
<TableContainer>
|
||||||
<Table>
|
<Table>
|
||||||
@@ -45,11 +86,35 @@ export const EnvironmentTable = ({ handlePopUpOpen }: Props) => {
|
|||||||
{isLoading && <TableSkeleton columns={3} innerKey="project-envs" />}
|
{isLoading && <TableSkeleton columns={3} innerKey="project-envs" />}
|
||||||
{!isLoading &&
|
{!isLoading &&
|
||||||
currentWorkspace &&
|
currentWorkspace &&
|
||||||
currentWorkspace.environments.map(({ name, slug }) => (
|
currentWorkspace.environments.map(({ name, slug }, pos) => (
|
||||||
<Tr key={name}>
|
<Tr key={name}>
|
||||||
<Td>{name}</Td>
|
<Td>{name}</Td>
|
||||||
<Td>{slug}</Td>
|
<Td>{slug}</Td>
|
||||||
<Td className="flex items-center justify-end">
|
<Td className="flex items-center justify-end">
|
||||||
|
<IconButton
|
||||||
|
className="mr-3 py-2"
|
||||||
|
onClick={() => {
|
||||||
|
handleReorderEnv(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={() => {
|
||||||
|
handleReorderEnv(true, name, slug)
|
||||||
|
}}
|
||||||
|
colorSchema="primary"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
isDisabled={pos === 0}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faArrowUp} />
|
||||||
|
</IconButton>
|
||||||
<IconButton
|
<IconButton
|
||||||
className="mr-3 py-2"
|
className="mr-3 py-2"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user