mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 17:08:53 +00:00
Add UI for viewing config profile install status and enable resending profiles to failed hosts. (#28964)
For [#28757](https://github.com/fleetdm/fleet/issues/28757) implements UI for viewing a config profiles install status info as well as allows user to resend the profile to hosts that it failed on. this includes **New info icon on the profile list item** <img width="618" alt="image" src="https://github.com/user-attachments/assets/2eb515fe-caea-43da-8e1c-02672825cf84" /> **modal to show config profile install status** <img width="656" alt="image" src="https://github.com/user-attachments/assets/21b6c483-1d36-40d9-9e5c-a74e7a9fcd50" /> **modal to confirm resending profile to failed hosts** <img width="666" alt="image" src="https://github.com/user-attachments/assets/c29d0d24-f6ba-4567-b954-f3908cdfed85" /> - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. - [ ] Added/updated automated tests - [x] Manual QA for all new/changed functionality
This commit is contained in:
parent
340d63e0f5
commit
b9e321e545
18 changed files with 520 additions and 6 deletions
|
|
@ -0,0 +1 @@
|
|||
- add UI for seeing custom profile status and to batch resend to hosts its failed on.
|
||||
|
|
@ -26,6 +26,8 @@ import DeleteProfileModal from "./components/DeleteProfileModal/DeleteProfileMod
|
|||
import ProfileLabelsModal from "./components/ProfileLabelsModal/ProfileLabelsModal";
|
||||
import ProfileListItem from "./components/ProfileListItem";
|
||||
import ProfileListHeading from "./components/ProfileListHeading";
|
||||
import ConfigProfileStatusModal from "./components/ConfigProfileStatusModal";
|
||||
import ResendConfigProfileModal from "./components/ResendConfigProfileModal";
|
||||
|
||||
const PROFILES_PER_PAGE = 10;
|
||||
|
||||
|
|
@ -59,9 +61,18 @@ const CustomSettings = ({
|
|||
setProfileLabelsModalData,
|
||||
] = useState<IMdmProfile | null>(null);
|
||||
const [showDeleteProfileModal, setShowDeleteProfileModal] = useState(false);
|
||||
const [
|
||||
showConfigProfileStatusModal,
|
||||
setShowConfigProfileStatusModal,
|
||||
] = useState(false);
|
||||
const [
|
||||
showResendConfigProfileModal,
|
||||
setShowResendConfigProfileModal,
|
||||
] = useState(false);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
|
||||
const selectedProfile = useRef<IMdmProfile | null>(null);
|
||||
const selectedStatusHostCount = useRef<number | null>(null);
|
||||
|
||||
const {
|
||||
data: profilesData,
|
||||
|
|
@ -96,6 +107,11 @@ const CustomSettings = ({
|
|||
onMutation();
|
||||
};
|
||||
|
||||
const onCancelInfo = () => {
|
||||
selectedProfile.current = null;
|
||||
setShowConfigProfileStatusModal(false);
|
||||
};
|
||||
|
||||
const onCancelDelete = () => {
|
||||
selectedProfile.current = null;
|
||||
setShowDeleteProfileModal(false);
|
||||
|
|
@ -129,6 +145,11 @@ const CustomSettings = ({
|
|||
router.push(path.concat(`${queryString}page=${currentPage + 1}`));
|
||||
}, [router, path, currentPage, queryString]);
|
||||
|
||||
const onClickInfo = (profile: IMdmProfile) => {
|
||||
selectedProfile.current = profile;
|
||||
setShowConfigProfileStatusModal(true);
|
||||
};
|
||||
|
||||
const onClickDelete = (profile: IMdmProfile) => {
|
||||
selectedProfile.current = profile;
|
||||
setShowDeleteProfileModal(true);
|
||||
|
|
@ -162,7 +183,8 @@ const CustomSettings = ({
|
|||
isPremium={!!isPremiumTier}
|
||||
profile={listItem}
|
||||
setProfileLabelsModalData={setProfileLabelsModalData}
|
||||
onDelete={onClickDelete}
|
||||
onClickInfo={onClickInfo}
|
||||
onClickDelete={onClickDelete}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
|
@ -213,8 +235,8 @@ const CustomSettings = ({
|
|||
)}
|
||||
{showDeleteProfileModal && selectedProfile.current && (
|
||||
<DeleteProfileModal
|
||||
profileName={selectedProfile.current?.name}
|
||||
profileId={selectedProfile.current?.profile_uuid}
|
||||
profileName={selectedProfile.current.name}
|
||||
profileId={selectedProfile.current.profile_uuid}
|
||||
onCancel={onCancelDelete}
|
||||
onDelete={onDeleteProfile}
|
||||
isDeleting={isDeleting}
|
||||
|
|
@ -226,6 +248,33 @@ const CustomSettings = ({
|
|||
setModalData={setProfileLabelsModalData}
|
||||
/>
|
||||
)}
|
||||
{showConfigProfileStatusModal && selectedProfile.current && (
|
||||
<ConfigProfileStatusModal
|
||||
teamId={currentTeamId}
|
||||
name={selectedProfile.current.name}
|
||||
uuid={selectedProfile.current.profile_uuid}
|
||||
onClickResend={(hostCount) => {
|
||||
selectedStatusHostCount.current = hostCount;
|
||||
setShowConfigProfileStatusModal(false);
|
||||
setShowResendConfigProfileModal(true);
|
||||
}}
|
||||
onExit={onCancelInfo}
|
||||
/>
|
||||
)}
|
||||
{showResendConfigProfileModal &&
|
||||
selectedProfile.current &&
|
||||
selectedStatusHostCount.current && (
|
||||
<ResendConfigProfileModal
|
||||
name={selectedProfile.current.name}
|
||||
uuid={selectedProfile.current.profile_uuid}
|
||||
count={selectedStatusHostCount.current}
|
||||
onExit={() => {
|
||||
selectedStatusHostCount.current = null;
|
||||
setShowResendConfigProfileModal(false);
|
||||
setShowConfigProfileStatusModal(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
import Button from "components/buttons/Button";
|
||||
import Icon from "components/Icon";
|
||||
import React from "react";
|
||||
import { Link } from "react-router";
|
||||
|
||||
import PATHS from "router/paths";
|
||||
import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants";
|
||||
import { buildQueryStringFromParams } from "utilities/url";
|
||||
|
||||
const baseClass = "config-profile-host-count-cell";
|
||||
|
||||
interface IConfigProfileHostCountCellProps {
|
||||
teamId: number;
|
||||
uuid: string;
|
||||
status: string;
|
||||
count: number;
|
||||
onClickResend: () => void;
|
||||
}
|
||||
|
||||
const ConfigProfileHostCountCell = ({
|
||||
teamId,
|
||||
uuid,
|
||||
status,
|
||||
count,
|
||||
onClickResend,
|
||||
}: IConfigProfileHostCountCellProps) => {
|
||||
const hostPath = `${PATHS.MANAGE_HOSTS}?${buildQueryStringFromParams({
|
||||
team_id: teamId,
|
||||
profile_uuid: uuid,
|
||||
profile_status: status,
|
||||
})}`;
|
||||
|
||||
const renderCount = () => {
|
||||
if (count === 0) {
|
||||
return <div>{DEFAULT_EMPTY_CELL_VALUE}</div>;
|
||||
}
|
||||
|
||||
return <Link to={hostPath}>{count}</Link>;
|
||||
};
|
||||
|
||||
const renderResendButton = () => {
|
||||
// we check if the count is 0 or if the uuid starts with "d" which means it
|
||||
// is a DDM profile.
|
||||
if (count === 0 || uuid[0] === "d" || status !== "failed") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
className={`${baseClass}__resend-button`}
|
||||
onClick={onClickResend}
|
||||
variant="text-icon"
|
||||
>
|
||||
<Icon name="refresh" color="core-fleet-blue" size="small" />
|
||||
<span>Resend</span>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<>{renderCount()}</>
|
||||
<>{renderResendButton()}</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfigProfileHostCountCell;
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
.config-profile-host-count-cell {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.children-wrapper {
|
||||
.icon {
|
||||
vertical-align: middle;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./ConfigProfileHostCountCell";
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
import React from "react";
|
||||
import { useQuery } from "react-query";
|
||||
|
||||
import configProfileAPI from "services/entities/config_profiles";
|
||||
import { DEFAULT_USE_QUERY_OPTIONS } from "utilities/constants";
|
||||
|
||||
import Modal from "components/Modal";
|
||||
import Spinner from "components/Spinner";
|
||||
import DataError from "components/DataError";
|
||||
import Button from "components/buttons/Button";
|
||||
import ConfigProfileStatusTable from "../ConfigProfileStatusTable";
|
||||
|
||||
const baseClass = "config-profile-status-modal";
|
||||
|
||||
interface IConfigProfileStatusModalProps {
|
||||
name: string;
|
||||
uuid: string;
|
||||
teamId: number;
|
||||
onClickResend: (hostCount: number) => void;
|
||||
onExit: () => void;
|
||||
}
|
||||
|
||||
const ConfigProfileStatusModal = ({
|
||||
name,
|
||||
uuid,
|
||||
teamId,
|
||||
onClickResend,
|
||||
onExit,
|
||||
}: IConfigProfileStatusModalProps) => {
|
||||
const { data, isLoading, isError } = useQuery(
|
||||
["config-profile-status", uuid],
|
||||
() => configProfileAPI.getConfigProfileStatus(uuid),
|
||||
{
|
||||
...DEFAULT_USE_QUERY_OPTIONS,
|
||||
}
|
||||
);
|
||||
|
||||
const renderContent = () => {
|
||||
if (isLoading) {
|
||||
return <Spinner />;
|
||||
}
|
||||
if (isError) {
|
||||
return <DataError verticalPaddingSize="pad-medium" />;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ConfigProfileStatusTable
|
||||
teamId={teamId}
|
||||
uuid={uuid}
|
||||
profileStatus={data}
|
||||
onClickResend={onClickResend}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal className={baseClass} title={name} onExit={onExit}>
|
||||
<>
|
||||
{renderContent()}
|
||||
<div className="modal-cta-wrap">
|
||||
<Button onClick={onExit}>Done</Button>
|
||||
</div>
|
||||
</>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfigProfileStatusModal;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
.restrictions-modal {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./ConfigProfileStatusModal";
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
import React, { useMemo } from "react";
|
||||
|
||||
import { IGetConfigProfileStatusResponse } from "services/entities/config_profiles";
|
||||
|
||||
import TableContainer from "components/TableContainer";
|
||||
import EmptyTable from "components/EmptyTable";
|
||||
|
||||
import {
|
||||
generateTableConfig,
|
||||
generateTableData,
|
||||
} from "./ConfigProfileStatusTableConfig";
|
||||
|
||||
const baseClass = "config-profile-status-table";
|
||||
|
||||
interface IConfigProfileStatusTableProps {
|
||||
teamId: number;
|
||||
uuid: string;
|
||||
profileStatus: IGetConfigProfileStatusResponse;
|
||||
onClickResend: (hostCount: number, status: string) => void;
|
||||
}
|
||||
|
||||
const ConfigProfileStatusTable = ({
|
||||
teamId,
|
||||
uuid,
|
||||
profileStatus,
|
||||
onClickResend,
|
||||
}: IConfigProfileStatusTableProps) => {
|
||||
const columnConfigs = useMemo(() => {
|
||||
return generateTableConfig(teamId, uuid, profileStatus, onClickResend);
|
||||
}, [profileStatus, teamId, uuid, onClickResend]);
|
||||
const tableData = generateTableData(profileStatus);
|
||||
|
||||
return (
|
||||
<TableContainer
|
||||
className={baseClass}
|
||||
columnConfigs={columnConfigs}
|
||||
data={tableData}
|
||||
isLoading={false}
|
||||
emptyComponent={() => <EmptyTable />}
|
||||
showMarkAllPages={false}
|
||||
isAllPagesSelected={false}
|
||||
manualSortBy
|
||||
disableTableHeader
|
||||
disablePagination
|
||||
disableCount
|
||||
hideFooter
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfigProfileStatusTable;
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
import React from "react";
|
||||
import { Column } from "react-table";
|
||||
|
||||
import StatusIndicatorWithIcon from "components/StatusIndicatorWithIcon";
|
||||
import {
|
||||
INumberCellProps,
|
||||
IStringCellProps,
|
||||
} from "interfaces/datatable_config";
|
||||
import { IGetConfigProfileStatusResponse } from "services/entities/config_profiles";
|
||||
import ConfigProfileHostCountCell from "../ConfigProfileHostCountCell";
|
||||
|
||||
type IConfigProfileStatus = "verified" | "verifying" | "pending" | "failed";
|
||||
|
||||
interface IConfigProfileRowData {
|
||||
status: string;
|
||||
hosts: number;
|
||||
}
|
||||
|
||||
// This is the order in which the statuses will be displayed in the table. It
|
||||
// will always be in this order.
|
||||
const STAUTS_ORDER = ["verified", "verifying", "pending", "failed"];
|
||||
|
||||
export interface IStatusCellValue {
|
||||
displayName: string;
|
||||
statusName: IConfigProfileStatus;
|
||||
value: IConfigProfileStatus;
|
||||
}
|
||||
|
||||
const STATUS_DISPLAY_OPTIONS = {
|
||||
verified: {
|
||||
displayName: "Verified",
|
||||
statusName: "success",
|
||||
},
|
||||
verifying: {
|
||||
displayName: "Verifying",
|
||||
statusName: "successPartial",
|
||||
},
|
||||
pending: {
|
||||
displayName: "Pending",
|
||||
statusName: "pendingPartial",
|
||||
},
|
||||
failed: {
|
||||
displayName: "Failed",
|
||||
statusName: "error",
|
||||
},
|
||||
} as const;
|
||||
|
||||
type IConfigProfileStatusColumnConfig = Column<IConfigProfileRowData>;
|
||||
type IStatusCellProps = IStringCellProps<IConfigProfileRowData>;
|
||||
type IHostCellProps = INumberCellProps<IConfigProfileRowData>;
|
||||
|
||||
export const generateTableConfig = (
|
||||
teamId: number,
|
||||
uuid: string,
|
||||
profileStatus: IGetConfigProfileStatusResponse,
|
||||
onClickResend: (hostCount: number, status: string) => void
|
||||
): IConfigProfileStatusColumnConfig[] => {
|
||||
return [
|
||||
{
|
||||
Header: "Status",
|
||||
disableSortBy: true,
|
||||
accessor: "status",
|
||||
Cell: ({ cell: { value } }: IStatusCellProps) => {
|
||||
const statusOption =
|
||||
STATUS_DISPLAY_OPTIONS[value as keyof typeof STATUS_DISPLAY_OPTIONS];
|
||||
return (
|
||||
<StatusIndicatorWithIcon
|
||||
status={statusOption.statusName}
|
||||
value={statusOption.displayName}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
Header: "Hosts",
|
||||
accessor: "hosts",
|
||||
disableSortBy: true,
|
||||
Cell: ({ cell }: IHostCellProps) => {
|
||||
return (
|
||||
<ConfigProfileHostCountCell
|
||||
teamId={teamId}
|
||||
count={cell.value}
|
||||
uuid={uuid}
|
||||
status={cell.row.original.status}
|
||||
onClickResend={() =>
|
||||
onClickResend(cell.value, cell.row.original.status)
|
||||
}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
export const generateTableData = (
|
||||
profileStatus: IGetConfigProfileStatusResponse
|
||||
): IConfigProfileRowData[] => {
|
||||
const tableData = STAUTS_ORDER.map((status) => ({
|
||||
status,
|
||||
hosts: profileStatus[
|
||||
status as keyof IGetConfigProfileStatusResponse
|
||||
] as number,
|
||||
}));
|
||||
|
||||
return tableData;
|
||||
};
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
.config-profile-status-table {
|
||||
|
||||
.config-profile-host-count-cell__resend-button {
|
||||
transition: opacity 250ms;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
.config-profile-host-count-cell__resend-button {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./ConfigProfileStatusTable";
|
||||
|
|
@ -73,7 +73,8 @@ const createFileContent = async (profile: IMdmProfile) => {
|
|||
interface IProfileListItemProps {
|
||||
isPremium: boolean;
|
||||
profile: IMdmProfile;
|
||||
onDelete: (profile: IMdmProfile) => void;
|
||||
onClickInfo: (profile: IMdmProfile) => void;
|
||||
onClickDelete: (profile: IMdmProfile) => void;
|
||||
setProfileLabelsModalData: React.Dispatch<
|
||||
React.SetStateAction<IMdmProfile | null>
|
||||
>;
|
||||
|
|
@ -82,7 +83,8 @@ interface IProfileListItemProps {
|
|||
const ProfileListItem = ({
|
||||
isPremium,
|
||||
profile,
|
||||
onDelete,
|
||||
onClickInfo,
|
||||
onClickDelete,
|
||||
setProfileLabelsModalData,
|
||||
}: IProfileListItemProps) => {
|
||||
const {
|
||||
|
|
@ -138,6 +140,13 @@ const ProfileListItem = ({
|
|||
<div className={`${subClass}__actions-wrap`}>
|
||||
{renderLabelInfo()}
|
||||
<div className={`${subClass}__actions`}>
|
||||
<Button
|
||||
className={`${subClass}__action-button`}
|
||||
variant="text-icon"
|
||||
onClick={() => onClickInfo(profile)}
|
||||
>
|
||||
<Icon name="info" color="ui-fleet-black-75" size="medium" />
|
||||
</Button>
|
||||
{isPremium && labels !== undefined && labels.length && (
|
||||
<Button
|
||||
className={`${subClass}__action-button`}
|
||||
|
|
@ -160,7 +169,7 @@ const ProfileListItem = ({
|
|||
disabled={disableChildren}
|
||||
className={`${subClass}__action-button`}
|
||||
variant="text-icon"
|
||||
onClick={() => onDelete(profile)}
|
||||
onClick={() => onClickDelete(profile)}
|
||||
>
|
||||
<Icon name="trash" color="ui-fleet-black-75" />
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
import React, { useContext } from "react";
|
||||
|
||||
import configProfilesAPI from "services/entities/config_profiles";
|
||||
|
||||
import Modal from "components/Modal";
|
||||
import Button from "components/buttons/Button";
|
||||
import { NotificationContext } from "context/notification";
|
||||
|
||||
const baseClass = "resend-config-profile-modal";
|
||||
|
||||
interface IResendConfigProfileModalProps {
|
||||
name: string;
|
||||
uuid: string;
|
||||
count: number;
|
||||
onExit: () => void;
|
||||
}
|
||||
|
||||
const ResendConfigProfileModal = ({
|
||||
name,
|
||||
uuid,
|
||||
count,
|
||||
onExit,
|
||||
}: IResendConfigProfileModalProps) => {
|
||||
const { renderFlash } = useContext(NotificationContext);
|
||||
const [isResending, setIsResending] = React.useState(false);
|
||||
|
||||
const countText = `${count} ${count === 1 ? "host" : "hosts"}`;
|
||||
|
||||
const onClickResend = async () => {
|
||||
setIsResending(true);
|
||||
try {
|
||||
await configProfilesAPI.batchResendConfigProfile(uuid);
|
||||
renderFlash(
|
||||
"success",
|
||||
<>
|
||||
Resent the <b>{name}</b> configuration profile.
|
||||
</>
|
||||
);
|
||||
} catch (error) {
|
||||
renderFlash(
|
||||
"error",
|
||||
"Couldn't resend the configuration profile. Please try again."
|
||||
);
|
||||
}
|
||||
setIsResending(false);
|
||||
onExit();
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className={baseClass}
|
||||
title="Resend configuration profile"
|
||||
onExit={onExit}
|
||||
>
|
||||
<>
|
||||
<p>
|
||||
This action will resend the <b>{name}</b> configuration profile to{" "}
|
||||
<b>{countText}</b>. To cancel after resending, delete and re-add the
|
||||
profile.
|
||||
</p>
|
||||
<div className="modal-cta-wrap">
|
||||
<Button
|
||||
onClick={onClickResend}
|
||||
isLoading={isResending}
|
||||
disabled={isResending}
|
||||
>
|
||||
Resend
|
||||
</Button>
|
||||
<Button variant="inverse" onClick={onExit} disabled={isResending}>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResendConfigProfileModal;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
.resend-config-profile-modal {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./ResendConfigProfileModal";
|
||||
40
frontend/services/entities/config_profiles.ts
Normal file
40
frontend/services/entities/config_profiles.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import { getConfig } from "@testing-library/react";
|
||||
import { profile } from "console";
|
||||
import sendRequest from "services";
|
||||
import endpoints from "utilities/endpoints";
|
||||
|
||||
export interface IGetConfigProfileStatusResponse {
|
||||
verified: number;
|
||||
verifying: number;
|
||||
failed: number;
|
||||
pending: number;
|
||||
}
|
||||
|
||||
export default {
|
||||
getConfigProfileStatus: (
|
||||
uuid: string
|
||||
): Promise<IGetConfigProfileStatusResponse> => {
|
||||
const { CONFIG_PROFILE_STATUS } = endpoints;
|
||||
// return sendRequest("GET", CONFIG_PROFILE_STATUS(uuid));
|
||||
|
||||
return new Promise((resolve) => {
|
||||
resolve({
|
||||
verified: 0,
|
||||
verifying: 1,
|
||||
failed: 2,
|
||||
pending: 3,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
batchResendConfigProfile: (uuid: string): Promise<void> => {
|
||||
const { CONFIG_PROFILE_BATCH_RESEND } = endpoints;
|
||||
const body = {
|
||||
profile_uuid: uuid,
|
||||
filters: {
|
||||
profile_status: "failed",
|
||||
},
|
||||
};
|
||||
return sendRequest("POST", CONFIG_PROFILE_BATCH_RESEND, body);
|
||||
},
|
||||
};
|
||||
|
|
@ -270,4 +270,9 @@ export default {
|
|||
|
||||
// idp endpoints
|
||||
SCIM_DETAILS: `/${API_VERSION}/fleet/scim/details`,
|
||||
|
||||
// configuration profile endpoints
|
||||
CONFIG_PROFILE_STATUS: (uuid: string) =>
|
||||
`/${API_VERSION}/fleet/configuration_profiles/${uuid}/status`,
|
||||
CONFIG_PROFILE_BATCH_RESEND: `/${API_VERSION}/fleet/configuration_profiles/batch/resend`,
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue