add UI for uploading setup experience script (#22691)
relates to #22374 > NOTE: we still need integration with the API which will be done in another PR. >NOTE: Please review https://github.com/fleetdm/fleet/pull/22651 first, as this PR is based off of that branch. This adds the UI for uploading a setup experience script. this includes: **setup experience script uploader:**  **script card:**  **delete script modal:**  **script run preview:**  <!-- Note that API documentation changes are now addressed by the product design team. --> - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. - [ ] Added/updated tests - [x] Manual QA for all new/changed functionality
This commit is contained in:
@@ -0,0 +1 @@
|
||||
- add UI for adding a setup experience script
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
IMdmProfile,
|
||||
IMdmSummaryMdmSolution,
|
||||
} from "interfaces/mdm";
|
||||
import { IGetSetupExperienceScriptResponse } from "services/entities/mdm";
|
||||
|
||||
const DEFAULT_MDM_SOLUTION_MOCK: IMdmSolution = {
|
||||
id: 1,
|
||||
@@ -79,3 +80,17 @@ export const createMockHostMdmData = (
|
||||
): IHostMdmData => {
|
||||
return { ...DEFAULT_HOST_MDM_DATA, ...overrides };
|
||||
};
|
||||
|
||||
const DEFAULT_SETUP_EXPIERENCE_SCRIPT: IGetSetupExperienceScriptResponse = {
|
||||
id: 1,
|
||||
team_id: null,
|
||||
name: "Test Script",
|
||||
created_at: "2021-01-01T00:00:00Z",
|
||||
updated_at: "2021-01-01T00:00:00Z",
|
||||
};
|
||||
|
||||
export const createMockSetupExperienceScript = (
|
||||
overrides?: Partial<IGetSetupExperienceScriptResponse>
|
||||
): IGetSetupExperienceScriptResponse => {
|
||||
return { ...DEFAULT_SETUP_EXPIERENCE_SCRIPT, ...overrides };
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@ import EndUserAuthentication from "./cards/EndUserAuthentication/EndUserAuthenti
|
||||
import BootstrapPackage from "./cards/BootstrapPackage";
|
||||
import SetupAssistant from "./cards/SetupAssistant";
|
||||
import InstallSoftware from "./cards/InstallSoftware";
|
||||
import SetupExperienceScript from "./cards/SetupExperienceScript";
|
||||
|
||||
interface ISetupExperienceCardProps {
|
||||
currentTeamId?: number;
|
||||
@@ -39,6 +40,12 @@ const SETUP_EXPERIENCE_NAV_ITEMS: ISideNavItem<
|
||||
path: PATHS.CONTROLS_INSTALL_SOFTWARE,
|
||||
Card: InstallSoftware,
|
||||
},
|
||||
{
|
||||
title: "5. Run script",
|
||||
urlSection: "run-script",
|
||||
path: PATHS.CONTROLS_RUN_SCRIPT,
|
||||
Card: SetupExperienceScript,
|
||||
},
|
||||
];
|
||||
|
||||
export default SETUP_EXPERIENCE_NAV_ITEMS;
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ import CustomLink from "components/CustomLink";
|
||||
|
||||
import SetupAssistantPreview from "./components/SetupAssistantPreview";
|
||||
import SetupAssistantProfileUploader from "./components/SetupAssistantProfileUploader";
|
||||
import SetuAssistantProfileCard from "./components/SetupAssistantProfileCard/SetupAssistantProfileCard";
|
||||
import SetupAssistantProfileCard from "./components/SetupAssistantProfileCard/SetupAssistantProfileCard";
|
||||
import DeleteAutoEnrollmentProfile from "./components/DeleteAutoEnrollmentProfile";
|
||||
import AdvancedOptionsForm from "./components/AdvancedOptionsForm";
|
||||
|
||||
@@ -112,7 +112,7 @@ const SetupAssistant = ({ currentTeamId }: ISetupAssistantProps) => {
|
||||
onUpload={onUpload}
|
||||
/>
|
||||
) : (
|
||||
<SetuAssistantProfileCard
|
||||
<SetupAssistantProfileCard
|
||||
profile={enrollmentProfileData}
|
||||
onDelete={() => setShowDeleteProfileModal(true)}
|
||||
/>
|
||||
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
import React, { useState } from "react";
|
||||
import { useQuery } from "react-query";
|
||||
|
||||
import { DEFAULT_USE_QUERY_OPTIONS } from "utilities/constants";
|
||||
import mdmAPI from "services/entities/mdm";
|
||||
|
||||
import SectionHeader from "components/SectionHeader";
|
||||
import DataError from "components/DataError";
|
||||
import Spinner from "components/Spinner";
|
||||
|
||||
import CustomLink from "components/CustomLink";
|
||||
|
||||
import SetupExperiencePreview from "./components/SetupExperienceScriptPreview";
|
||||
import SetupExperienceScriptUploader from "./components/SetupExperienceScriptUploader";
|
||||
import SetupExperienceScriptCard from "./components/SetupExperienceScriptCard";
|
||||
import DeleteSetupExperienceScriptModal from "./components/DeleteSetupExperienceScriptModal";
|
||||
|
||||
const baseClass = "setup-experience-script";
|
||||
|
||||
interface ISetupExperienceScriptProps {
|
||||
currentTeamId: number;
|
||||
}
|
||||
|
||||
const SetupExperienceScript = ({
|
||||
currentTeamId,
|
||||
}: ISetupExperienceScriptProps) => {
|
||||
const [showDeleteScriptModal, setShowDeleteScriptModal] = useState(false);
|
||||
|
||||
const {
|
||||
data: script,
|
||||
isLoading,
|
||||
isError,
|
||||
refetch: refetchScript,
|
||||
} = useQuery(
|
||||
["setup-experience-script", currentTeamId],
|
||||
() => mdmAPI.getSetupExperienceScript(currentTeamId),
|
||||
{ ...DEFAULT_USE_QUERY_OPTIONS }
|
||||
);
|
||||
|
||||
const onUpload = () => {
|
||||
refetchScript();
|
||||
};
|
||||
|
||||
const onDelete = () => {
|
||||
setShowDeleteScriptModal(false);
|
||||
refetchScript();
|
||||
};
|
||||
|
||||
const scriptUploaded = true;
|
||||
|
||||
const renderContent = () => {
|
||||
if (isLoading) {
|
||||
<Spinner />;
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
return <DataError />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`${baseClass}__content`}>
|
||||
<div className={`${baseClass}__description-container`}>
|
||||
<p className={`${baseClass}__description`}>
|
||||
Upload a script to run on hosts that automatically enroll to Fleet.
|
||||
</p>
|
||||
<CustomLink
|
||||
className={`${baseClass}__learn-how-link`}
|
||||
newTab
|
||||
url=""
|
||||
text="Learn how"
|
||||
/>
|
||||
{!scriptUploaded || !script ? (
|
||||
<SetupExperienceScriptUploader
|
||||
currentTeamId={currentTeamId}
|
||||
onUpload={onUpload}
|
||||
/>
|
||||
) : (
|
||||
<SetupExperienceScriptCard
|
||||
script={script}
|
||||
onDelete={() => setShowDeleteScriptModal(true)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<SetupExperiencePreview />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<SectionHeader title="Run script" />
|
||||
<>{renderContent()}</>
|
||||
{showDeleteScriptModal && (
|
||||
<DeleteSetupExperienceScriptModal
|
||||
onDeleted={onDelete}
|
||||
onExit={() => setShowDeleteScriptModal(false)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SetupExperienceScript;
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
.setup-experience-script {
|
||||
&__content {
|
||||
max-width: $break-xxl;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: $pad-xxlarge;
|
||||
}
|
||||
|
||||
&__description {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__learn-how-link {
|
||||
margin-bottom: $pad-large;
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import Button from "components/buttons/Button";
|
||||
import Modal from "components/Modal";
|
||||
import React from "react";
|
||||
|
||||
const baseClass = "delete-setup-experience-script-modal";
|
||||
|
||||
interface IDeleteSetupExperienceScriptModalProps {
|
||||
onExit: () => void;
|
||||
onDeleted: () => void;
|
||||
}
|
||||
|
||||
const DeleteSetupExperienceScriptModal = ({
|
||||
onExit,
|
||||
onDeleted,
|
||||
}: IDeleteSetupExperienceScriptModalProps) => {
|
||||
const onDelete = () => {
|
||||
onDeleted();
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal className={baseClass} title="Delete setup script" onExit={onExit}>
|
||||
<>
|
||||
<p>Delete the setup script to upload a new one.</p>
|
||||
<div className="modal-cta-wrap">
|
||||
<Button type="button" onClick={onDelete} variant="alert">
|
||||
Delete
|
||||
</Button>
|
||||
<Button onClick={onExit} variant="inverse-alert">
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteSetupExperienceScriptModal;
|
||||
+1
@@ -0,0 +1 @@
|
||||
export { default } from "./DeleteSetupExperienceScriptModal";
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
import React, { useContext } from "react";
|
||||
import FileSaver from "file-saver";
|
||||
|
||||
import mdmAPI, {
|
||||
IGetSetupExperienceScriptResponse,
|
||||
} from "services/entities/mdm";
|
||||
|
||||
import { uploadedFromNow } from "utilities/date_format";
|
||||
|
||||
import Button from "components/buttons/Button";
|
||||
import Card from "components/Card";
|
||||
import Graphic from "components/Graphic";
|
||||
import Icon from "components/Icon";
|
||||
import { NotificationContext } from "context/notification";
|
||||
import { getErrorReason } from "interfaces/errors";
|
||||
import { API_NO_TEAM_ID } from "interfaces/team";
|
||||
|
||||
const baseClass = "setup-experience-script-card";
|
||||
|
||||
interface ISetupExperienceScriptCardProps {
|
||||
script: IGetSetupExperienceScriptResponse;
|
||||
onDelete: () => void;
|
||||
}
|
||||
|
||||
const SetupExperienceScriptCard = ({
|
||||
script,
|
||||
onDelete,
|
||||
}: ISetupExperienceScriptCardProps) => {
|
||||
const { renderFlash } = useContext(NotificationContext);
|
||||
|
||||
const onDownload = async () => {
|
||||
try {
|
||||
const teamId = script.team_id ?? API_NO_TEAM_ID;
|
||||
await mdmAPI.downloadSetupExperienceScript(teamId);
|
||||
} catch (e) {
|
||||
renderFlash("error", getErrorReason(e));
|
||||
}
|
||||
// TODO: download script integration
|
||||
|
||||
// const date = new Date();
|
||||
// const filename = `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}_${
|
||||
// script.name
|
||||
// }`;
|
||||
// const file = new global.window.File(
|
||||
// [JSON.stringify(script.enrollment_profile)],
|
||||
// filename
|
||||
// );
|
||||
|
||||
// FileSaver.saveAs(file);
|
||||
};
|
||||
|
||||
return (
|
||||
<Card paddingSize="medium" className={baseClass}>
|
||||
<Graphic name="file-sh" />
|
||||
<div className={`${baseClass}__info`}>
|
||||
<span className={`${baseClass}__profile-name`}>{script.name}</span>
|
||||
<span className={`${baseClass}__uploaded-at`}>
|
||||
{uploadedFromNow(script.created_at)}
|
||||
</span>
|
||||
</div>
|
||||
<div className={`${baseClass}__actions`}>
|
||||
<Button
|
||||
className={`${baseClass}__download-button`}
|
||||
variant="text-icon"
|
||||
onClick={onDownload}
|
||||
>
|
||||
<Icon name="download" />
|
||||
</Button>
|
||||
<Button
|
||||
className={`${baseClass}__delete-button`}
|
||||
variant="text-icon"
|
||||
onClick={onDelete}
|
||||
>
|
||||
<Icon name="trash" color="ui-fleet-black-75" />
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default SetupExperienceScriptCard;
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
.setup-experience-script-card {
|
||||
display: flex;
|
||||
gap: $pad-medium;
|
||||
align-items: center;
|
||||
|
||||
// TODO: create reusable list item component and use instead of all these styles.
|
||||
&__info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&__profile-name {
|
||||
font-size: $x-small;
|
||||
font-weight: $bold;
|
||||
}
|
||||
|
||||
&__uploaded-at {
|
||||
font-size: $xx-small;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
display: flex;
|
||||
gap: $pad-medium;
|
||||
flex: 1;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&__download-button,
|
||||
&__delete-button {
|
||||
padding: 11px; // TODO: use a padding value from existing variables. talk to design.
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export { default } from "./SetupExperienceScriptCard";
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import React from "react";
|
||||
|
||||
import Card from "components/Card";
|
||||
|
||||
import InstallSoftwarePreviewImg from "../../../../../../../../assets/images/install-software-preview.png";
|
||||
|
||||
const baseClass = "setup-experience-script-preview";
|
||||
|
||||
const SetupExperienceScriptPreview = () => {
|
||||
return (
|
||||
<Card color="gray" paddingSize="xxlarge" className={baseClass}>
|
||||
<h3>End user experience</h3>
|
||||
<p>
|
||||
When the end user completes the macOS Setup Assistant, they will see
|
||||
scripts being run. User will not be able to continue until scripts
|
||||
complete.
|
||||
</p>
|
||||
<p>
|
||||
If there are any errors, the end user will be able to continue and will
|
||||
be instructed to contact their IT department.
|
||||
</p>
|
||||
<img
|
||||
className={`${baseClass}__preview-img`}
|
||||
src={InstallSoftwarePreviewImg}
|
||||
alt="install software preview"
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default SetupExperienceScriptPreview;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
.setup-experience-script-preview {
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: $small;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
&__preview-img {
|
||||
margin-top: $pad-xxlarge;
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export { default } from "./SetupExperienceScriptPreview";
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
import React, { useContext, useState } from "react";
|
||||
import classnames from "classnames";
|
||||
|
||||
import mdmAPI from "services/entities/mdm";
|
||||
|
||||
import { NotificationContext } from "context/notification";
|
||||
import FileUploader from "components/FileUploader";
|
||||
import { getErrorReason } from "interfaces/errors";
|
||||
|
||||
const baseClass = "setup-experience-script-uploader";
|
||||
|
||||
interface ISetupExperienceScriptUploaderProps {
|
||||
currentTeamId: number;
|
||||
onUpload: () => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const SetupExperienceScriptUploader = ({
|
||||
currentTeamId,
|
||||
onUpload,
|
||||
className,
|
||||
}: ISetupExperienceScriptUploaderProps) => {
|
||||
const { renderFlash } = useContext(NotificationContext);
|
||||
const [showLoading, setShowLoading] = useState(false);
|
||||
|
||||
const classNames = classnames(baseClass, className);
|
||||
|
||||
const onUploadFile = async (files: FileList | null) => {
|
||||
setShowLoading(true);
|
||||
|
||||
if (!files || files.length === 0) {
|
||||
setShowLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const file = files[0];
|
||||
|
||||
try {
|
||||
await mdmAPI.uploadSetupExperienceScript(file, currentTeamId);
|
||||
renderFlash("success", "Successfully uploaded!");
|
||||
onUpload();
|
||||
} catch (e) {
|
||||
// TODO: what errors?
|
||||
renderFlash("error", getErrorReason(e));
|
||||
}
|
||||
|
||||
setShowLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<FileUploader
|
||||
className={classNames}
|
||||
message="Shell (.sh) for macOS"
|
||||
graphicName="file-sh"
|
||||
accept=".sh"
|
||||
buttonMessage="Upload"
|
||||
onFileUpload={onUploadFile}
|
||||
isLoading={showLoading}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SetupExperienceScriptUploader;
|
||||
+1
@@ -0,0 +1 @@
|
||||
export { default } from "./SetupExperienceScriptUploader";
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./SetupExperienceScript";
|
||||
@@ -18,6 +18,7 @@ export default {
|
||||
CONTROLS_BOOTSTRAP_PACKAGE: `${URL_PREFIX}/controls/setup-experience/bootstrap-package`,
|
||||
CONTROLS_SETUP_ASSITANT: `${URL_PREFIX}/controls/setup-experience/setup-assistant`,
|
||||
CONTROLS_INSTALL_SOFTWARE: `${URL_PREFIX}/controls/setup-experience/install-software`,
|
||||
CONTROLS_RUN_SCRIPT: `${URL_PREFIX}/controls/setup-experience/run-script`,
|
||||
CONTROLS_SCRIPTS: `${URL_PREFIX}/controls/scripts`,
|
||||
|
||||
// Dashboard pages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { createMockMdmProfile } from "__mocks__/mdmMock";
|
||||
import { createMockSetupExperienceScript } from "__mocks__/mdmMock";
|
||||
import {
|
||||
DiskEncryptionStatus,
|
||||
IHostMdmProfile,
|
||||
@@ -82,6 +82,14 @@ export interface IGetMdmCommandResultsResponse {
|
||||
results: IMdmCommandResult[];
|
||||
}
|
||||
|
||||
export interface IGetSetupExperienceScriptResponse {
|
||||
id: number;
|
||||
team_id: number | null; // The API return null for no team in this case.
|
||||
name: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
const mdmService = {
|
||||
unenrollHostFromMdm: (hostId: number, timeout?: number) => {
|
||||
const { HOST_MDM_UNENROLL } = endpoints;
|
||||
@@ -276,6 +284,7 @@ const mdmService = {
|
||||
|
||||
return sendRequest("PATCH", MDM_SETUP_EXPERIENCE, body);
|
||||
},
|
||||
|
||||
getSetupEnrollmentProfile: (teamId?: number) => {
|
||||
const { MDM_APPLE_SETUP_ENROLLMENT_PROFILE } = endpoints;
|
||||
if (!teamId || teamId === API_NO_TEAM_ID) {
|
||||
@@ -287,6 +296,7 @@ const mdmService = {
|
||||
)}`;
|
||||
return sendRequest("GET", path);
|
||||
},
|
||||
|
||||
uploadSetupEnrollmentProfile: (file: File, teamId: number) => {
|
||||
const { MDM_APPLE_SETUP_ENROLLMENT_PROFILE } = endpoints;
|
||||
|
||||
@@ -313,6 +323,7 @@ const mdmService = {
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
deleteSetupEnrollmentProfile: (teamId: number) => {
|
||||
const { MDM_APPLE_SETUP_ENROLLMENT_PROFILE } = endpoints;
|
||||
if (teamId === API_NO_TEAM_ID) {
|
||||
@@ -324,6 +335,7 @@ const mdmService = {
|
||||
)}`;
|
||||
return sendRequest("DELETE", path);
|
||||
},
|
||||
|
||||
getCommandResults: (
|
||||
command_uuid: string
|
||||
): Promise<IGetMdmCommandResultsResponse> => {
|
||||
@@ -331,6 +343,47 @@ const mdmService = {
|
||||
const url = `${MDM_COMMANDS_RESULTS}?command_uuid=${command_uuid}`;
|
||||
return sendRequest("GET", url);
|
||||
},
|
||||
|
||||
getSetupExperienceScript: (
|
||||
teamId: number
|
||||
): Promise<IGetSetupExperienceScriptResponse> => {
|
||||
const { MDM_SETUP_EXPERIENCE_SCRIPT } = endpoints;
|
||||
|
||||
let path = MDM_SETUP_EXPERIENCE_SCRIPT;
|
||||
if (teamId) {
|
||||
path += `?${buildQueryStringFromParams({ team_id: teamId })}`;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(createMockSetupExperienceScript());
|
||||
});
|
||||
|
||||
// return sendRequest("GET", path);
|
||||
},
|
||||
|
||||
downloadSetupExperienceScript: (teamId: number) => {
|
||||
const { MDM_SETUP_EXPERIENCE_SCRIPT } = endpoints;
|
||||
|
||||
let path = MDM_SETUP_EXPERIENCE_SCRIPT;
|
||||
if (teamId) {
|
||||
path += `?${buildQueryStringFromParams({ team_id: teamId })}`;
|
||||
}
|
||||
|
||||
return sendRequest("GET", path);
|
||||
},
|
||||
|
||||
uploadSetupExperienceScript: (file: File, teamId: number) => {
|
||||
const { MDM_SETUP_EXPERIENCE_SCRIPT } = endpoints;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("script", file);
|
||||
|
||||
if (teamId) {
|
||||
formData.append("team_id", teamId.toString());
|
||||
}
|
||||
|
||||
return sendRequest("POST", MDM_SETUP_EXPERIENCE_SCRIPT, formData);
|
||||
},
|
||||
};
|
||||
|
||||
export default mdmService;
|
||||
|
||||
@@ -127,7 +127,6 @@ export default {
|
||||
MDM_BOOTSTRAP_PACKAGE: `/${API_VERSION}/fleet/mdm/bootstrap`,
|
||||
MDM_BOOTSTRAP_PACKAGE_SUMMARY: `/${API_VERSION}/fleet/mdm/bootstrap/summary`,
|
||||
MDM_SETUP: `/${API_VERSION}/fleet/mdm/apple/setup`,
|
||||
MDM_SETUP_EXPERIENCE: `/${API_VERSION}/fleet/setup_experience`,
|
||||
MDM_EULA: (token: string) => `/${API_VERSION}/fleet/mdm/setup/eula/${token}`,
|
||||
MDM_EULA_UPLOAD: `/${API_VERSION}/fleet/mdm/setup/eula`,
|
||||
MDM_EULA_METADATA: `/${API_VERSION}/fleet/mdm/setup/eula/metadata`,
|
||||
@@ -139,6 +138,10 @@ export default {
|
||||
|
||||
ME: `/${API_VERSION}/fleet/me`,
|
||||
|
||||
// Setup experiece endpoints
|
||||
MDM_SETUP_EXPERIENCE: `/${API_VERSION}/fleet/setup_experience`,
|
||||
MDM_SETUP_EXPERIENCE_SCRIPT: `/${API_VERSION}/fleet/setup_experience/script`,
|
||||
|
||||
// OS Version endpoints
|
||||
OS_VERSIONS: `/${API_VERSION}/fleet/os_versions`,
|
||||
OS_VERSION: (id: number) => `/${API_VERSION}/fleet/os_versions/${id}`,
|
||||
|
||||
Reference in New Issue
Block a user