Update UI to support filtering by software install status (#18888)

This commit is contained in:
Sarah Gillespie
2024-05-10 10:57:47 -05:00
committed by GitHub
parent 7f803e4629
commit fa2b295a5e
15 changed files with 296 additions and 41 deletions
+4
View File
@@ -0,0 +1,4 @@
- Added functionality to filter hosts by software installer status.
- Added endpoints to upload, delete, and download software installers.
- Added endpoints to get host software install results.
- Updated activity feeds to include software installer activities.
+72 -3
View File
@@ -1,6 +1,6 @@
import { useCallback, useContext, useEffect, useMemo } from "react";
import { InjectedRouter } from "react-router";
import { findLastIndex, trimStart } from "lodash";
import { findLastIndex, over, trimStart } from "lodash";
import { AppContext } from "context/app";
import { TableContext } from "context/table";
@@ -16,6 +16,30 @@ import {
import { IUser, IUserRole } from "interfaces/user";
import permissions from "utilities/permissions";
import sort from "utilities/sort";
import { HOSTS_QUERY_PARAMS } from "services/entities/hosts";
type OnTeamChangeFuncShouldStripParam = (
teamIdForApi: number | undefined
) => boolean;
type OnTeamChangeFuncShouldReplaceParam = (
teamIdForApi: number | undefined
) => [boolean, string];
/**
* This type is used to define functions that determine whether a query parameter should be stripped or replaced
* when the team id changes.
*
* The key is the name of the query parameter and the value is a function that receives the new team
* id with a return type of either:
* - boolean indicating whether the query parameter should be stripped
* - tuple of a boolean and a string, where the boolean indicates whether the query parameter should be replaced
* and the string is the new value for the query parameter
*/
export type IConfigOverrideParamsOnTeamChange = Record<
string,
OnTeamChangeFuncShouldReplaceParam | OnTeamChangeFuncShouldStripParam
>;
const splitQueryStringParts = (queryString: string) =>
trimStart(queryString, "?")
@@ -27,7 +51,8 @@ const joinQueryStringParts = (parts: string[]) =>
const rebuildQueryStringWithTeamId = (
queryString: string,
newTeamId: number
newTeamId: number,
configAdditionalParams?: IConfigOverrideParamsOnTeamChange
) => {
const parts = splitQueryStringParts(queryString);
@@ -67,6 +92,41 @@ const rebuildQueryStringWithTeamId = (
parts.splice(teamIndex, 1); // just remove the old team part
}
if (configAdditionalParams) {
Object.entries(configAdditionalParams).forEach(([paramName, fn]) => {
let shouldStrip = false;
let shouldReplace = false;
let replaceString = "";
const val = fn(newTeamId);
if (Array.isArray(val)) {
[shouldReplace, replaceString] = val;
} else if (typeof val === "boolean") {
shouldStrip = val;
}
if (shouldStrip || shouldReplace) {
const paramIndex = parts.findIndex((p) =>
p.startsWith(`${paramName}=`)
);
if (shouldStrip && paramIndex !== -1) {
parts.splice(paramIndex, 1);
return;
}
if (shouldReplace) {
const newPart = `${paramName}=${replaceString}`;
if (paramIndex === -1) {
parts.splice(paramIndex, 1, newPart);
} else {
parts.push(newPart);
}
}
}
});
}
return joinQueryStringParts(parts);
};
@@ -223,6 +283,7 @@ export const useTeamIdParam = ({
includeNoTeam,
permittedAccessByTeamRole,
resetSelectedRowsOnTeamChange = true,
overrideParamsOnTeamChange,
}: {
location?: {
pathname: string;
@@ -235,6 +296,7 @@ export const useTeamIdParam = ({
includeNoTeam: boolean;
permittedAccessByTeamRole?: Record<IUserRole, boolean>;
resetSelectedRowsOnTeamChange?: boolean;
overrideParamsOnTeamChange?: IConfigOverrideParamsOnTeamChange;
}) => {
const { hash, pathname, query, search } = location;
const {
@@ -282,11 +344,18 @@ export const useTeamIdParam = ({
router.replace(
pathname
.concat(rebuildQueryStringWithTeamId(search, teamId))
.concat(
rebuildQueryStringWithTeamId(
search,
teamId,
overrideParamsOnTeamChange
)
)
.concat(hash || "")
);
},
[
overrideParamsOnTeamChange,
resetSelectedRowsOnTeamChange,
router,
pathname,
+21 -9
View File
@@ -148,17 +148,29 @@ export const formatSoftwareType = ({
}
return type;
};
/*
* SoftwareInstallStatus represents the possible states of software install operations.
*
*/
export type ISoftwareInstallStatus = "pending" | "installed" | "failed";
/**
*
* This list comprises all possible states of software install operations.
*/
export const SOFTWARE_INSTALL_STATUSES = [
"failed",
"installed",
"pending",
] as const;
/*
* SoftwareInstallStatus represents the possible states of software install operations.
*/
export type SoftwareInstallStatus = typeof SOFTWARE_INSTALL_STATUSES[number];
export const isValidSoftwareInstallStatus = (
s: string | undefined
): s is SoftwareInstallStatus =>
!!s && SOFTWARE_INSTALL_STATUSES.includes(s as SoftwareInstallStatus);
/**
* ISoftwareInstallResult is the shape of a software install result object
* returned by the Fleet API.
*
*/
export interface ISoftwareInstallResult {
install_uuid: string;
@@ -167,7 +179,7 @@ export interface ISoftwareInstallResult {
software_package: string;
host_id: number;
host_display_name: string;
status: ISoftwareInstallStatus;
status: SoftwareInstallStatus;
detail: string;
output: string;
pre_install_query_output: string;
@@ -200,7 +212,7 @@ export interface IHostSoftware {
package_available_for_install?: string | null;
source: string;
bundle_identifier?: string;
status: ISoftwareInstallStatus | null;
status: SoftwareInstallStatus | null;
last_install: ISoftwareLastInstall | null;
installed_versions: ISoftwareInstallVersion[] | null;
}
@@ -1,7 +1,7 @@
import React, { useContext, useState } from "react";
import endpoints from "utilities/endpoints";
import { ISoftwareInstallStatus, ISoftwarePackage } from "interfaces/software";
import { SoftwareInstallStatus, ISoftwarePackage } from "interfaces/software";
import PATHS from "router/paths";
import { AppContext } from "context/app";
import { buildQueryStringFromParams } from "utilities/url";
@@ -27,7 +27,7 @@ interface IStatusDisplayOption {
}
const STATUS_DISPLAY_OPTIONS: Record<
ISoftwareInstallStatus,
SoftwareInstallStatus,
IStatusDisplayOption
> = {
installed: {
@@ -49,7 +49,7 @@ const STATUS_DISPLAY_OPTIONS: Record<
interface IPackageStatusCountProps {
softwareId: number;
status: ISoftwareInstallStatus;
status: SoftwareInstallStatus;
count: number;
teamId?: number;
}
@@ -4,7 +4,7 @@ import { useQuery } from "react-query";
import {
ISoftwareInstallResult,
ISoftwareInstallResults,
ISoftwareInstallStatus,
SoftwareInstallStatus,
} from "interfaces/software";
import softwareAPI from "services/entities/software";
@@ -18,13 +18,13 @@ import { IconNames } from "components/icons";
const baseClass = "software-install-details";
const STATUS_ICONS: Record<ISoftwareInstallStatus, IconNames> = {
const STATUS_ICONS: Record<SoftwareInstallStatus, IconNames> = {
pending: "pending-outline",
installed: "success-outline",
failed: "error-outline",
} as const;
const STATUS_PREDICATES: Record<ISoftwareInstallStatus, string> = {
const STATUS_PREDICATES: Record<SoftwareInstallStatus, string> = {
pending: "will install",
installed: "installed",
failed: "failed to install",
@@ -10,6 +10,7 @@ export const MANAGE_HOSTS_PAGE_FILTER_KEYS = [
"policy_response",
"macos_settings",
"software_id",
HOSTS_QUERY_PARAMS.SOFTWARE_STATUS,
"status",
"mdm_id",
"mdm_enrollment_status",
@@ -32,6 +33,7 @@ export const MANAGE_HOSTS_PAGE_LABEL_INCOMPATIBLE_QUERY_PARAMS = [
"software_id",
"software_version_id",
"software_title_id",
HOSTS_QUERY_PARAMS.SOFTWARE_STATUS,
"bootstrap_package",
"macos_settings",
HOSTS_QUERY_PARAMS.OS_SETTINGS,
@@ -23,6 +23,7 @@ import hostsAPI, {
ILoadHostsResponse,
ISortOption,
MacSettingsStatusQueryParam,
HOSTS_QUERY_PARAMS,
} from "services/entities/hosts";
import hostCountAPI, {
IHostsCountQueryKey,
@@ -49,7 +50,11 @@ import { getErrorReason } from "interfaces/errors";
import { ILabel } from "interfaces/label";
import { IOperatingSystemVersion } from "interfaces/operating_system";
import { IPolicy, IStoredPolicyResponse } from "interfaces/policy";
import { ITeam } from "interfaces/team";
import {
isValidSoftwareInstallStatus,
SoftwareInstallStatus,
} from "interfaces/software";
import { API_NO_TEAM_ID, ITeam } from "interfaces/team";
import { IEmptyTableProps } from "interfaces/empty_table";
import {
DiskEncryptionStatus,
@@ -162,6 +167,11 @@ const ManageHostsPage = ({
router,
includeAllTeams: true,
includeNoTeam: true,
overrideParamsOnTeamChange: {
// remove the software status filter when selecting all teams or no team
[HOSTS_QUERY_PARAMS.SOFTWARE_STATUS]: (newTeamId?: number) =>
!newTeamId || newTeamId < 1,
},
});
const hostHiddenColumns = localStorage.getItem("hostHiddenColumns");
@@ -232,6 +242,11 @@ const ManageHostsPage = ({
queryParams?.software_title_id !== undefined
? parseInt(queryParams.software_title_id, 10)
: undefined;
const softwareStatus = isValidSoftwareInstallStatus(
queryParams?.[HOSTS_QUERY_PARAMS.SOFTWARE_STATUS]
)
? (queryParams[HOSTS_QUERY_PARAMS.SOFTWARE_STATUS] as SoftwareInstallStatus)
: undefined;
const status = isAcceptableStatus(queryParams?.status)
? queryParams?.status
: undefined;
@@ -380,6 +395,7 @@ const ManageHostsPage = ({
softwareId,
softwareTitleId,
softwareVersionId,
softwareStatus,
status,
mdmId,
mdmEnrollmentStatus,
@@ -423,6 +439,7 @@ const ManageHostsPage = ({
softwareId,
softwareTitleId,
softwareVersionId,
softwareStatus,
status,
mdmId,
mdmEnrollmentStatus,
@@ -517,10 +534,10 @@ const ManageHostsPage = ({
useEffect(() => {
if (
location.search.match(
/software_id|software_version_id|software_title_id/gi
/software_id|software_version_id|software_title_id|software_status/gi
)
) {
// regex matches any of "software_id", "software_version_id", or "software_title_id"
// regex matches any of "software_id", "software_version_id", "software_title_id", or "software_status"
// so we don't set the filtered hosts path in those cases
return;
}
@@ -712,6 +729,25 @@ const ManageHostsPage = ({
);
};
const handleSoftwareInstallStatausChange = (
newStatus: SoftwareInstallStatus
) => {
handleResetPageIndex();
router.replace(
getNextLocationPath({
pathPrefix: PATHS.MANAGE_HOSTS,
routeTemplate,
routeParams,
queryParams: {
...queryParams,
[HOSTS_QUERY_PARAMS.SOFTWARE_STATUS]: newStatus,
page: 0, // resets page index
},
})
);
};
const onAddLabelClick = () => {
router.push(`${PATHS.NEW_LABEL}`);
};
@@ -815,6 +851,10 @@ const ManageHostsPage = ({
newQueryParams.software_version_id = softwareVersionId;
} else if (softwareTitleId) {
newQueryParams.software_title_id = softwareTitleId;
if (softwareStatus && teamIdForApi && teamIdForApi > 0) {
// software_status is only valid when software_title_id is present and a team is selected
newQueryParams[HOSTS_QUERY_PARAMS.SOFTWARE_STATUS] = softwareStatus;
}
} else if (mdmId) {
newQueryParams.mdm_id = mdmId;
} else if (mdmEnrollmentStatus) {
@@ -864,6 +904,7 @@ const ManageHostsPage = ({
softwareId,
softwareVersionId,
softwareTitleId,
softwareStatus,
mdmId,
mdmEnrollmentStatus,
munkiIssueId,
@@ -1062,6 +1103,7 @@ const ManageHostsPage = ({
softwareId,
softwareTitleId,
softwareVersionId,
softwareStatus,
osName,
osVersionId,
osVersion,
@@ -1114,6 +1156,7 @@ const ManageHostsPage = ({
softwareId,
softwareTitleId,
softwareVersionId,
softwareStatus,
osName,
osVersionId,
osVersion,
@@ -1325,6 +1368,7 @@ const ManageHostsPage = ({
softwareId,
softwareTitleId,
softwareVersionId,
softwareStatus,
status,
mdmId,
mdmEnrollmentStatus,
@@ -1527,6 +1571,7 @@ const ManageHostsPage = ({
softwareId ||
softwareTitleId ||
softwareVersionId ||
softwareStatus ||
osName ||
osVersionId ||
osVersion ||
@@ -1665,6 +1710,7 @@ const ManageHostsPage = ({
softwareId,
softwareTitleId,
softwareVersionId,
softwareStatus,
mdmId,
mdmEnrollmentStatus,
lowDiskSpaceHosts,
@@ -1696,6 +1742,9 @@ const ManageHostsPage = ({
handleChangeBootstrapPackageStatusFilter
}
onChangeMacSettingsFilter={handleMacSettingsStatusDropdownChange}
onChangeSoftwareInstallStatusFilter={
handleSoftwareInstallStatausChange
}
onClickEditLabel={onEditLabelClick}
onClickDeleteLabel={toggleDeleteLabelModal}
isSandboxMode={isSandboxMode}
@@ -15,6 +15,8 @@ import {
} from "interfaces/mdm";
import { IMunkiIssuesAggregate } from "interfaces/macadmins";
import { IPolicy } from "interfaces/policy";
import { SoftwareInstallStatus } from "interfaces/software";
import {
HOSTS_QUERY_PARAMS,
MacSettingsStatusQueryParam,
@@ -70,6 +72,7 @@ interface IHostsFilterBlockProps {
osSettingsStatus?: MdmProfileStatus;
diskEncryptionStatus?: DiskEncryptionStatus;
bootstrapPackageStatus?: BootstrapPackageStatus;
softwareStatus?: SoftwareInstallStatus;
};
selectedLabel?: ILabel;
isOnlyObserver?: boolean;
@@ -84,6 +87,9 @@ interface IHostsFilterBlockProps {
onChangeMacSettingsFilter: (
newMacSettingsStatus: MacSettingsStatusQueryParam
) => void;
onChangeSoftwareInstallStatusFilter: (
newStatus: SoftwareInstallStatus
) => void;
onClickEditLabel: (evt: React.MouseEvent<HTMLButtonElement>) => void;
onClickDeleteLabel: () => void;
isSandboxMode?: boolean;
@@ -117,6 +123,7 @@ const HostsFilterBlock = ({
osSettingsStatus,
diskEncryptionStatus,
bootstrapPackageStatus,
softwareStatus,
},
selectedLabel,
isOnlyObserver,
@@ -127,6 +134,7 @@ const HostsFilterBlock = ({
onChangeDiskEncryptionStatusFilter,
onChangeBootstrapPackageStatusFilter,
onChangeMacSettingsFilter,
onChangeSoftwareInstallStatusFilter,
onClickEditLabel,
onClickDeleteLabel,
isSandboxMode = false,
@@ -254,7 +262,7 @@ const HostsFilterBlock = ({
);
};
const renderSoftwareFilterBlock = () => {
const renderSoftwareFilterBlock = (additionalClearParams?: string[]) => {
if (!softwareDetails) return null;
const { name, version } = softwareDetails;
@@ -264,6 +272,16 @@ const HostsFilterBlock = ({
}
label = label.trim() || "Unknown software";
const clearParams = [
"software_id",
"software_version_id",
"software_title_id",
];
if (additionalClearParams?.length) {
clearParams.push(...additionalClearParams);
}
// const TooltipDescription = (
// <span>
// Hosts with {name || "Unknown software"},
@@ -275,13 +293,7 @@ const HostsFilterBlock = ({
return (
<FilterPill
label={label}
onClear={() =>
handleClearFilter([
"software_id",
"software_version_id",
"software_title_id",
])
}
onClear={() => handleClearFilter(clearParams)}
// tooltipDescription={TooltipDescription}
/>
);
@@ -452,6 +464,26 @@ const HostsFilterBlock = ({
);
};
const renderSoftwareInstallStatusBlock = () => {
const OPTIONS = [
{ value: "installed", label: "Installed" },
{ value: "failed", label: "Failed" },
{ value: "pending", label: "Pending" },
];
return (
<>
<Dropdown
value={softwareStatus}
className={`${baseClass}__sw-install-status-dropdown`}
options={OPTIONS}
onChange={onChangeSoftwareInstallStatusFilter}
/>
{renderSoftwareFilterBlock([HOSTS_QUERY_PARAMS.SOFTWARE_STATUS])}
</>
);
};
const showSelectedLabel = selectedLabel && selectedLabel.type !== "all";
if (
@@ -461,6 +493,7 @@ const HostsFilterBlock = ({
softwareId ||
softwareTitleId ||
softwareVersionId ||
softwareStatus ||
mdmId ||
mdmEnrollmentStatus ||
lowDiskSpaceHosts ||
@@ -501,6 +534,8 @@ const HostsFilterBlock = ({
return renderPoliciesFilterBlock();
case !!macSettingsStatus:
return renderMacSettingsStatusFilterBlock();
case !!softwareStatus:
return renderSoftwareInstallStatusBlock();
case !!softwareId || !!softwareVersionId || !!softwareTitleId:
return renderSoftwareFilterBlock();
case !!mdmId:
@@ -14,7 +14,8 @@
// NOTE: Look more into this styling
&__os_settings-dropdown,
&__macsettings-dropdown {
&__macsettings-dropdown,
&__sw-install-status-dropdown {
.Select-value {
display: flex;
align-items: center;
@@ -1,6 +1,6 @@
import React from "react";
import { ISoftwareInstallStatus } from "interfaces/software";
import { SoftwareInstallStatus } from "interfaces/software";
import { IHostActivityItemComponentPropsWithShowDetails } from "../../ActivityConfig";
import HostActivityItem from "../../HostActivityItem";
@@ -8,7 +8,7 @@ import ShowDetailsButton from "../../ShowDetailsButton";
const baseClass = "installed-software-activity-item";
const STATUS_PREDICATES: Record<ISoftwareInstallStatus, string> = {
const STATUS_PREDICATES: Record<SoftwareInstallStatus, string> = {
failed: "failed to install",
installed: "installed",
pending: "told Fleet to install",
@@ -21,7 +21,7 @@ export const getSoftwareInstallStatusPredicate = (
return STATUS_PREDICATES.pending;
}
return (
STATUS_PREDICATES[status as ISoftwareInstallStatus] ||
STATUS_PREDICATES[status as SoftwareInstallStatus] ||
STATUS_PREDICATES.pending
);
};
@@ -5,7 +5,7 @@ import { cloneDeep } from "lodash";
import {
IHostSoftware,
ISoftwareInstallStatus,
SoftwareInstallStatus,
formatSoftwareType,
} from "interfaces/software";
import {
@@ -49,7 +49,7 @@ type IVulnerabilitiesCellProps = IInstalledVersionsCellProps;
const generateActions = (
softwareId: number,
status: ISoftwareInstallStatus | null,
status: SoftwareInstallStatus | null,
installingSoftwareId: number | null,
canInstall: boolean,
packageToInstall?: string | null
@@ -1,6 +1,6 @@
import React, { ReactNode } from "react";
import { ISoftwareInstallStatus } from "interfaces/software";
import { SoftwareInstallStatus } from "interfaces/software";
import { dateAgo } from "utilities/date_format";
import Icon from "components/Icon";
@@ -9,7 +9,7 @@ import TextCell from "components/TableContainer/DataTable/TextCell";
const baseClass = "install-status-cell";
type IStatusValue = ISoftwareInstallStatus | "avaiableForInstall";
type IStatusValue = SoftwareInstallStatus | "avaiableForInstall";
type IStatusDisplayConfig = {
iconName: "success" | "pending-outline" | "error" | "install";
@@ -56,7 +56,7 @@ const CELL_DISPLAY_OPTIONS: Record<IStatusValue, IStatusDisplayConfig> = {
};
interface IInstallStatusCellProps {
status: ISoftwareInstallStatus | null;
status: SoftwareInstallStatus | null;
packageToInstall?: string | null;
installedAt?: string;
}
+6
View File
@@ -42,6 +42,7 @@ export interface IHostCountLoadOptions {
softwareId?: number;
softwareTitleId?: number;
softwareVersionId?: number;
softwareStatus?: string;
lowDiskSpaceHosts?: number;
mdmId?: number;
mdmEnrollmentStatus?: string;
@@ -67,6 +68,7 @@ export default {
const softwareId = options?.softwareId;
const softwareTitleId = options?.softwareTitleId;
const softwareVersionId = options?.softwareVersionId;
const softwareStatus = options?.softwareStatus;
const macSettingsStatus = options?.macSettingsStatus;
const status = options?.status;
const mdmId = options?.mdmId;
@@ -89,7 +91,10 @@ export default {
macSettingsStatus,
osSettings,
}),
// TODO: shouldn't macSettingsStatus be included in the mutually exclusive query params too?
// If so, this todo applies in other places.
...reconcileMutuallyExclusiveHostParams({
teamId,
label,
policyId,
policyResponse,
@@ -98,6 +103,7 @@ export default {
munkiIssueId,
softwareId,
softwareTitleId,
softwareStatus,
softwareVersionId,
lowDiskSpaceHosts,
osName,
+20 -1
View File
@@ -9,7 +9,12 @@ import {
reconcileMutuallyInclusiveHostParams,
} from "utilities/url";
import { SelectedPlatform } from "interfaces/platform";
import { ISoftwareTitle, ISoftware, IHostSoftware } from "interfaces/software";
import {
IHostSoftware,
ISoftwareTitle,
ISoftware,
SoftwareInstallStatus,
} from "interfaces/software";
import {
DiskEncryptionStatus,
BootstrapPackageStatus,
@@ -46,6 +51,7 @@ export type IUnlockHostResponse =
export const HOSTS_QUERY_PARAMS = {
OS_SETTINGS: "os_settings",
DISK_ENCRYPTION: "os_settings_disk_encryption",
SOFTWARE_STATUS: "software_status",
} as const;
export interface ILoadHostsQueryKey extends ILoadHostsOptions {
@@ -67,6 +73,7 @@ export interface ILoadHostsOptions {
softwareId?: number;
softwareTitleId?: number;
softwareVersionId?: number;
softwareStatus?: SoftwareInstallStatus;
status?: HostStatus;
mdmId?: number;
mdmEnrollmentStatus?: string;
@@ -97,6 +104,7 @@ export interface IExportHostsOptions {
softwareId?: number;
softwareTitleId?: number;
softwareVersionId?: number;
softwareStatus?: SoftwareInstallStatus;
status?: HostStatus;
mdmId?: number;
munkiIssueId?: number;
@@ -126,6 +134,7 @@ export interface IActionByFilter {
softwareId?: number | null;
softwareTitleId?: number | null;
softwareVersionId?: number | null;
softwareStatus?: SoftwareInstallStatus;
osName?: string;
osVersion?: string;
osVersionId?: number | null;
@@ -220,6 +229,7 @@ export default {
softwareId,
softwareTitleId,
softwareVersionId,
softwareStatus,
osName,
osVersion,
osVersionId,
@@ -245,6 +255,7 @@ export default {
software_id: softwareId,
software_title_id: softwareTitleId,
software_version_id: softwareVersionId,
[HOSTS_QUERY_PARAMS.SOFTWARE_STATUS]: softwareStatus,
os_name: osName,
os_version: osVersion,
os_version_id: osVersionId,
@@ -270,6 +281,7 @@ export default {
const softwareId = options?.softwareId;
const softwareTitleId = options?.softwareTitleId;
const softwareVersionId = options?.softwareVersionId;
const softwareStatus = options?.softwareStatus;
const macSettingsStatus = options?.macSettingsStatus;
const osName = options?.osName;
const osVersionId = options?.osVersionId;
@@ -301,6 +313,7 @@ export default {
osSettings,
}),
...reconcileMutuallyExclusiveHostParams({
teamId,
label,
policyId,
policyResponse,
@@ -310,6 +323,7 @@ export default {
softwareId,
softwareTitleId,
softwareVersionId,
softwareStatus,
osName,
osVersionId,
osVersion,
@@ -354,6 +368,7 @@ export default {
softwareId,
softwareTitleId,
softwareVersionId,
softwareStatus,
status,
mdmId,
mdmEnrollmentStatus,
@@ -388,6 +403,7 @@ export default {
osSettings,
}),
...reconcileMutuallyExclusiveHostParams({
teamId,
label,
policyId,
policyResponse,
@@ -397,6 +413,7 @@ export default {
softwareId,
softwareTitleId,
softwareVersionId,
softwareStatus,
lowDiskSpaceHosts,
osVersionId,
osName,
@@ -462,6 +479,7 @@ export default {
softwareId,
softwareTitleId,
softwareVersionId,
softwareStatus,
osName,
osVersion,
osVersionId,
@@ -488,6 +506,7 @@ export default {
software_id: softwareId,
software_title_id: softwareTitleId,
software_version_id: softwareVersionId,
[HOSTS_QUERY_PARAMS.SOFTWARE_STATUS]: softwareStatus,
os_name: osName,
os_version: osVersion,
os_version_id: osVersionId,
+60 -2
View File
@@ -9,6 +9,7 @@ import {
HOSTS_QUERY_PARAMS,
MacSettingsStatusQueryParam,
} from "services/entities/hosts";
import { isValidSoftwareInstallStatus } from "interfaces/software";
type QueryValues = string | number | boolean | undefined | null;
export type QueryParams = Record<string, QueryValues>;
@@ -23,6 +24,7 @@ interface IMutuallyInclusiveHostParams {
}
interface IMutuallyExclusiveHostParams {
teamId?: number;
label?: string;
policyId?: number;
policyResponse?: string;
@@ -33,6 +35,7 @@ interface IMutuallyExclusiveHostParams {
softwareId?: number;
softwareVersionId?: number;
softwareTitleId?: number;
softwareStatus?: string;
osVersionId?: number;
osName?: string;
osVersion?: string;
@@ -77,6 +80,48 @@ export const buildQueryStringFromParams = (queryParams: QueryParams) => {
return queryString;
};
export const reconcileSoftwareParams = ({
teamId,
softwareId,
softwareVersionId,
softwareTitleId,
softwareStatus,
}: Pick<
IMutuallyExclusiveHostParams,
| "teamId"
| "softwareId"
| "softwareVersionId"
| "softwareTitleId"
| "softwareStatus"
>) => {
if (
isValidSoftwareInstallStatus(softwareStatus) &&
softwareTitleId &&
teamId &&
teamId > 0
) {
return {
software_title_id: softwareTitleId,
[HOSTS_QUERY_PARAMS.SOFTWARE_STATUS]: softwareStatus,
team_id: teamId,
};
}
if (softwareTitleId) {
return { software_title_id: softwareTitleId };
}
if (softwareVersionId) {
return { software_version_id: softwareVersionId };
}
if (softwareId) {
return { software_id: softwareId };
}
return {};
};
export const reconcileMutuallyInclusiveHostParams = ({
label,
teamId,
@@ -102,9 +147,12 @@ export const reconcileMutuallyInclusiveHostParams = ({
reconciled[HOSTS_QUERY_PARAMS.OS_SETTINGS] = osSettings;
reconciled.team_id = teamId ?? 0;
}
return reconciled;
};
export const reconcileMutuallyExclusiveHostParams = ({
teamId,
label,
policyId,
policyResponse,
@@ -115,6 +163,7 @@ export const reconcileMutuallyExclusiveHostParams = ({
softwareId,
softwareVersionId,
softwareTitleId,
softwareStatus,
osVersionId,
osName,
osVersion,
@@ -147,8 +196,17 @@ export const reconcileMutuallyExclusiveHostParams = ({
return { mdm_enrollment_status: mdmEnrollmentStatus };
case !!munkiIssueId:
return { munki_issue_id: munkiIssueId };
case !!softwareTitleId:
return { software_title_id: softwareTitleId };
case !!softwareStatus ||
!!softwareTitleId ||
!!softwareVersionId ||
!!softwareId:
return reconcileSoftwareParams({
teamId,
softwareId,
softwareVersionId,
softwareTitleId,
softwareStatus,
});
case !!softwareVersionId:
return { software_version_id: softwareVersionId };
case !!softwareId: