mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 13:37:30 +00:00
Covers #36760, #36758. # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements) ## Testing - [x] Added/updated automated tests - [x] Where appropriate, [automated tests simulate multiple hosts and test for host isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing) (updates to one hosts's records do not affect another) - [ ] QA'd all new/changed functionality manually
160 lines
4.4 KiB
TypeScript
160 lines
4.4 KiB
TypeScript
import React, { useContext } from "react";
|
|
import { useQuery } from "react-query";
|
|
import { RouteComponentProps } from "react-router";
|
|
import { AxiosError } from "axios";
|
|
|
|
import PATHS from "router/paths";
|
|
import labelsAPI, {
|
|
IGetHostsInLabelResponse,
|
|
IGetLabelResponse,
|
|
} from "services/entities/labels";
|
|
import { DEFAULT_USE_QUERY_OPTIONS } from "utilities/constants";
|
|
import { ILabel } from "interfaces/label";
|
|
import { IHost } from "interfaces/host";
|
|
import { NotificationContext } from "context/notification";
|
|
import { AppContext } from "context/app";
|
|
|
|
import MainContent from "components/MainContent";
|
|
import Spinner from "components/Spinner";
|
|
import DataError from "components/DataError";
|
|
|
|
import DynamicLabelForm from "../components/DynamicLabelForm";
|
|
import ManualLabelForm from "../components/ManualLabelForm";
|
|
import { IDynamicLabelFormData } from "../components/DynamicLabelForm/DynamicLabelForm";
|
|
import { IManualLabelFormData } from "../components/ManualLabelForm/ManualLabelForm";
|
|
import { hasEditPermission } from "../ManageLabelsPage/LabelsTable/LabelsTableConfig";
|
|
|
|
const baseClass = "edit-label-page";
|
|
|
|
interface IEditLabelPageRouteParams {
|
|
label_id: string;
|
|
}
|
|
|
|
type IEditLabelPageProps = RouteComponentProps<
|
|
never,
|
|
IEditLabelPageRouteParams
|
|
>;
|
|
|
|
const EditLabelPage = ({ routeParams, router }: IEditLabelPageProps) => {
|
|
const { renderFlash } = useContext(NotificationContext);
|
|
const { currentUser } = useContext(AppContext);
|
|
|
|
const labelId = parseInt(routeParams.label_id, 10);
|
|
|
|
const {
|
|
data: label,
|
|
isLoading: isLoadingLabel,
|
|
isError: isErrorLabel,
|
|
} = useQuery<IGetLabelResponse, AxiosError, ILabel>(
|
|
["label", labelId, currentUser],
|
|
() => labelsAPI.getLabel(labelId),
|
|
{
|
|
...DEFAULT_USE_QUERY_OPTIONS,
|
|
select: (data) => data.label,
|
|
onSuccess: (data) => {
|
|
// can't edit host_vitals labels yet
|
|
if (data.label_membership_type === "host_vitals") {
|
|
renderFlash(
|
|
"error",
|
|
"Host vitals labels are not editable. Delete the label and re-add it to make changes."
|
|
);
|
|
router.replace(PATHS.MANAGE_LABELS);
|
|
}
|
|
|
|
if (currentUser && !hasEditPermission(currentUser, data)) {
|
|
renderFlash(
|
|
"error",
|
|
"You do not have permission to edit this label."
|
|
);
|
|
router.replace(PATHS.MANAGE_LABELS);
|
|
}
|
|
},
|
|
}
|
|
);
|
|
|
|
const {
|
|
data: targetedHosts,
|
|
isLoading: isLoadingHosts,
|
|
isError: isErrorHosts,
|
|
} = useQuery<IGetHostsInLabelResponse, AxiosError, IHost[]>(
|
|
["hosts"],
|
|
() => {
|
|
return labelsAPI.getHostsInLabel(labelId);
|
|
},
|
|
{
|
|
...DEFAULT_USE_QUERY_OPTIONS,
|
|
select: (data) => data.hosts,
|
|
enabled: label?.label_membership_type === "manual",
|
|
}
|
|
);
|
|
|
|
const onCancelEdit = () => {
|
|
router.goBack();
|
|
};
|
|
|
|
const onUpdateLabel = async (
|
|
formData: IDynamicLabelFormData | IManualLabelFormData
|
|
) => {
|
|
try {
|
|
await labelsAPI.update(labelId, formData);
|
|
renderFlash("success", "Label updated successfully.");
|
|
} catch {
|
|
renderFlash("error", "Couldn't edit label. Please try again.");
|
|
}
|
|
};
|
|
|
|
const renderContent = () => {
|
|
if (isLoadingLabel || isLoadingHosts) {
|
|
return <Spinner />;
|
|
}
|
|
|
|
if (isErrorLabel || isErrorHosts) {
|
|
return <DataError />;
|
|
}
|
|
|
|
if (!label) return null;
|
|
|
|
if (label.label_type === "builtin") {
|
|
return (
|
|
<DataError
|
|
description="Built in labels cannot be edited"
|
|
excludeIssueLink
|
|
/>
|
|
);
|
|
}
|
|
|
|
return label.label_membership_type === "dynamic" ? (
|
|
<DynamicLabelForm
|
|
defaultName={label.name}
|
|
defaultDescription={label.description}
|
|
defaultQuery={label.query}
|
|
defaultPlatform={label.platform}
|
|
teamName={label.team_name || null}
|
|
isEditing
|
|
onSave={onUpdateLabel}
|
|
onCancel={onCancelEdit}
|
|
/>
|
|
) : (
|
|
<ManualLabelForm
|
|
key={targetedHosts?.toString()}
|
|
defaultName={label.name}
|
|
defaultDescription={label.description}
|
|
defaultTargetedHosts={targetedHosts}
|
|
teamName={label.team_name || null}
|
|
onSave={onUpdateLabel}
|
|
onCancel={onCancelEdit}
|
|
/>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<MainContent className={baseClass}>
|
|
<h1 className="page-header">Edit label</h1>
|
|
{renderContent()}
|
|
</MainContent>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default EditLabelPage;
|