mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 08:55:24 +00:00
Fix for mdm solution rollup to handle empty string for rollup correctly. (#17366)
relates to #17335 This fixes the issue with empty strings for mdm solution names. I also cleans up a bit around the code and typing for this feature. - [x] Added/updated tests - [x] Manual QA for all new/changed functionality
This commit is contained in:
parent
7a7b043014
commit
252848720c
@ -1,5 +1,9 @@
|
||||
import { IHostMdmData } from "interfaces/host";
|
||||
import { IMdmSolution, IMdmProfile } from "interfaces/mdm";
|
||||
import {
|
||||
IMdmSolution,
|
||||
IMdmProfile,
|
||||
IMdmSummaryMdmSolution,
|
||||
} from "interfaces/mdm";
|
||||
|
||||
const DEFAULT_MDM_SOLUTION_MOCK: IMdmSolution = {
|
||||
id: 1,
|
||||
@ -14,6 +18,19 @@ export const createMockMdmSolution = (
|
||||
return { ...DEFAULT_MDM_SOLUTION_MOCK, ...overrides };
|
||||
};
|
||||
|
||||
const DEFAULT_HOST_SUMMARY_MDM_SOLUTION_MOCK: IMdmSummaryMdmSolution = {
|
||||
id: 1,
|
||||
name: "MDM Solution",
|
||||
server_url: "http://mdmsolution.com",
|
||||
hosts_count: 5,
|
||||
};
|
||||
|
||||
export const createMockMdmSummaryMdmSolution = (
|
||||
overrides?: Partial<IMdmSummaryMdmSolution>
|
||||
): IMdmSummaryMdmSolution => {
|
||||
return { ...DEFAULT_HOST_SUMMARY_MDM_SOLUTION_MOCK, ...overrides };
|
||||
};
|
||||
|
||||
const DEFAULT_MDM_PROFILE_DATA: IMdmProfile = {
|
||||
profile_uuid: "123-abc",
|
||||
team_id: 0,
|
||||
|
@ -41,6 +41,12 @@ export interface IMdmSolution {
|
||||
hosts_count: number;
|
||||
}
|
||||
|
||||
/** This is the mdm solution that comes back from the host/summary/mdm
|
||||
request. We will always get a string for the solution name in this case */
|
||||
export interface IMdmSummaryMdmSolution extends IMdmSolution {
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface IMdmStatus {
|
||||
enrolled_manual_hosts_count: number;
|
||||
enrolled_automated_hosts_count: number;
|
||||
@ -52,7 +58,7 @@ interface IMdmStatus {
|
||||
export interface IMdmSummaryResponse {
|
||||
counts_updated_at: string;
|
||||
mobile_device_management_enrollment_status: IMdmStatus;
|
||||
mobile_device_management_solution: IMdmSolution[] | null;
|
||||
mobile_device_management_solution: IMdmSummaryMdmSolution[] | null;
|
||||
}
|
||||
|
||||
export type ProfilePlatform = "darwin" | "windows";
|
||||
|
@ -18,8 +18,8 @@ import {
|
||||
} from "interfaces/macadmins";
|
||||
import {
|
||||
IMdmStatusCardData,
|
||||
IMdmSolution,
|
||||
IMdmSummaryResponse,
|
||||
IMdmSummaryMdmSolution,
|
||||
} from "interfaces/mdm";
|
||||
import { SelectedPlatform } from "interfaces/platform";
|
||||
import { ISoftwareResponse, ISoftwareCountResponse } from "interfaces/software";
|
||||
@ -137,9 +137,11 @@ const DashboardPage = ({ router, location }: IDashboardProps): JSX.Element => {
|
||||
const [showOperatingSystemsUI, setShowOperatingSystemsUI] = useState(false);
|
||||
const [showHostsUI, setShowHostsUI] = useState(false); // Hides UI on first load only
|
||||
const [mdmStatusData, setMdmStatusData] = useState<IMdmStatusCardData[]>([]);
|
||||
const [mdmSolutions, setMdmSolutions] = useState<IMdmSolution[] | null>([]);
|
||||
const [mdmSolutions, setMdmSolutions] = useState<
|
||||
IMdmSummaryMdmSolution[] | null
|
||||
>([]);
|
||||
|
||||
const selectedMdmSolution = useRef<string | null>(null);
|
||||
const selectedMdmSolutionName = useRef<string>("");
|
||||
|
||||
const [munkiIssuesData, setMunkiIssuesData] = useState<
|
||||
IMunkiIssuesAggregate[]
|
||||
@ -619,7 +621,7 @@ const DashboardPage = ({ router, location }: IDashboardProps): JSX.Element => {
|
||||
selectedPlatformLabelId={selectedPlatformLabelId}
|
||||
selectedTeamId={currentTeamId}
|
||||
onClickMdmSolution={(mdmSolution) => {
|
||||
selectedMdmSolution.current = mdmSolution.name;
|
||||
selectedMdmSolutionName.current = mdmSolution.name;
|
||||
setShowMdmSolutionModal(true);
|
||||
}}
|
||||
/>
|
||||
@ -727,7 +729,7 @@ const DashboardPage = ({ router, location }: IDashboardProps): JSX.Element => {
|
||||
}
|
||||
|
||||
const selectedMdmSolutions = mdmSolutions?.filter(
|
||||
(solution) => solution.name === selectedMdmSolution.current
|
||||
(solution) => solution.name === selectedMdmSolutionName.current
|
||||
);
|
||||
|
||||
return (
|
||||
@ -737,7 +739,7 @@ const DashboardPage = ({ router, location }: IDashboardProps): JSX.Element => {
|
||||
selectedTeamId={currentTeamId}
|
||||
onCancel={() => {
|
||||
setShowMdmSolutionModal(false);
|
||||
selectedMdmSolution.current = null;
|
||||
selectedMdmSolutionName.current = "";
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -3,26 +3,27 @@ import { noop } from "lodash";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { renderWithSetup } from "test/test-utils";
|
||||
|
||||
import { createMockMdmSolution } from "__mocks__/mdmMock";
|
||||
import { createMockMdmSummaryMdmSolution } from "__mocks__/mdmMock";
|
||||
|
||||
import MDM from "./MDM";
|
||||
|
||||
describe("MDM Card", () => {
|
||||
it("rolls up the data by mdm solution name and render the correct number of MDM solutions", () => {
|
||||
const { debug } = render(
|
||||
render(
|
||||
<MDM
|
||||
onClickMdmSolution={noop}
|
||||
error={null}
|
||||
isFetching={false}
|
||||
mdmStatusData={[]}
|
||||
mdmSolutions={[
|
||||
createMockMdmSolution(),
|
||||
createMockMdmSolution({ id: 2 }),
|
||||
createMockMdmSolution({ name: "Test Solution", id: 3 }),
|
||||
createMockMdmSolution({ name: "Test Solution", id: 4 }),
|
||||
createMockMdmSolution({ name: "Test Solution 2", id: 5 }),
|
||||
createMockMdmSolution({ name: null, id: 6 }),
|
||||
createMockMdmSolution({ name: null, id: 7 }),
|
||||
createMockMdmSummaryMdmSolution(),
|
||||
createMockMdmSummaryMdmSolution({ id: 2 }),
|
||||
createMockMdmSummaryMdmSolution({ name: "Test Solution", id: 3 }),
|
||||
createMockMdmSummaryMdmSolution({ name: "Test Solution", id: 4 }),
|
||||
createMockMdmSummaryMdmSolution({ name: "Test Solution 2", id: 5 }),
|
||||
// "" should render a row of "Unknown"
|
||||
createMockMdmSummaryMdmSolution({ name: "", id: 8 }),
|
||||
createMockMdmSummaryMdmSolution({ name: "", id: 9 }),
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
@ -2,7 +2,11 @@ import React, { useMemo, useState } from "react";
|
||||
import { Tab, Tabs, TabList, TabPanel } from "react-tabs";
|
||||
import { Row } from "react-table";
|
||||
|
||||
import { IMdmStatusCardData, IMdmSolution } from "interfaces/mdm";
|
||||
import {
|
||||
IMdmStatusCardData,
|
||||
IMdmSolution,
|
||||
IMdmSummaryMdmSolution,
|
||||
} from "interfaces/mdm";
|
||||
|
||||
import TabsWrapper from "components/TabsWrapper";
|
||||
import TableContainer from "components/TableContainer";
|
||||
@ -20,18 +24,23 @@ import {
|
||||
generateStatusDataSet,
|
||||
} from "./MDMStatusTableConfig";
|
||||
|
||||
export type IMdmSolutionTableData = Pick<
|
||||
IMdmSummaryMdmSolution,
|
||||
"name" | "hosts_count"
|
||||
>;
|
||||
|
||||
interface IRowProps extends Row {
|
||||
original: IMdmSolution;
|
||||
original: IMdmSolutionTableData;
|
||||
}
|
||||
|
||||
interface IMdmCardProps {
|
||||
error: Error | null;
|
||||
isFetching: boolean;
|
||||
mdmStatusData: IMdmStatusCardData[];
|
||||
mdmSolutions: IMdmSolution[] | null;
|
||||
mdmSolutions: IMdmSummaryMdmSolution[] | null;
|
||||
selectedPlatformLabelId?: number;
|
||||
selectedTeamId?: number;
|
||||
onClickMdmSolution: (solution: IMdmSolution) => void;
|
||||
onClickMdmSolution: (solution: IMdmSolutionTableData) => void;
|
||||
}
|
||||
|
||||
const DEFAULT_SORT_DIRECTION = "desc";
|
||||
@ -65,22 +74,17 @@ const EmptyMdmSolutions = (): JSX.Element => (
|
||||
/>
|
||||
);
|
||||
|
||||
type IMdmSolutionMap = Record<string, IMdmSolution>;
|
||||
type IMdmSolutionMap = Record<string, IMdmSolutionTableData>;
|
||||
|
||||
const reduceSolutionsToObj = (mdmSolutions: IMdmSolution[]) => {
|
||||
const reduceSolutionsToObj = (mdmSolutions: IMdmSummaryMdmSolution[]) => {
|
||||
return mdmSolutions.reduce<IMdmSolutionMap>((acc, nextSolution) => {
|
||||
// The solution name can be null so we add an Unknown key to the
|
||||
// accumulator in this case.
|
||||
if (nextSolution.name === null) {
|
||||
if (acc.Unknown) {
|
||||
acc.Unknown.hosts_count += nextSolution.hosts_count;
|
||||
} else {
|
||||
acc.Unknown = Object.assign({ ...nextSolution });
|
||||
}
|
||||
} else if (acc[nextSolution.name]) {
|
||||
acc[nextSolution.name].hosts_count += nextSolution.hosts_count;
|
||||
// The solution name can be an empty string so we add a key for "Unknown"
|
||||
// for this case.
|
||||
const key = nextSolution.name || "Unknown";
|
||||
if (acc[key]) {
|
||||
acc[key].hosts_count += nextSolution.hosts_count;
|
||||
} else {
|
||||
acc[nextSolution.name] = Object.assign({ ...nextSolution });
|
||||
acc[key] = Object.assign({ ...nextSolution });
|
||||
}
|
||||
|
||||
return acc;
|
||||
|
@ -4,6 +4,7 @@ import { IMdmSolution } from "interfaces/mdm";
|
||||
|
||||
import TextCell from "components/TableContainer/DataTable/TextCell";
|
||||
import InternalLinkCell from "../../../../components/TableContainer/DataTable/InternalLinkCell";
|
||||
import { IMdmSolutionTableData } from "./MDM";
|
||||
|
||||
// NOTE: cellProps come from react-table
|
||||
// more info here https://react-table.tanstack.com/docs/api/useTable#cell-properties
|
||||
@ -58,16 +59,12 @@ export const generateSolutionsTableHeaders = (): IDataColumn[] => [
|
||||
];
|
||||
|
||||
export const generateSolutionsDataSet = (
|
||||
solutions: IMdmSolution[] | null
|
||||
): IMdmSolution[] => {
|
||||
if (!solutions) {
|
||||
return [];
|
||||
}
|
||||
|
||||
solutions: IMdmSolutionTableData[]
|
||||
): IMdmSolutionTableData[] => {
|
||||
return solutions.map((solution) => {
|
||||
return {
|
||||
...solution,
|
||||
displayName: solution.name ?? "Unknown",
|
||||
displayName: solution.name || "Unknown",
|
||||
};
|
||||
});
|
||||
};
|
||||
|
@ -15,6 +15,7 @@ const baseClass = "mdm-solution-modal";
|
||||
|
||||
const SOLUTIONS_DEFAULT_SORT_HEADER = "hosts_count";
|
||||
const DEFAULT_SORT_DIRECTION = "desc";
|
||||
const DEFAULT_TITLE = "Unknown MDM solution";
|
||||
|
||||
interface IMdmSolutionModalProps {
|
||||
mdmSolutions: IMdmSolution[];
|
||||
@ -41,7 +42,7 @@ const MdmSolutionsModal = ({
|
||||
return (
|
||||
<Modal
|
||||
className={baseClass}
|
||||
title={mdmSolutions[0].name ?? "Unknown mdm solution"}
|
||||
title={mdmSolutions[0].name || DEFAULT_TITLE}
|
||||
width="large"
|
||||
onExit={onCancel}
|
||||
>
|
||||
|
Loading…
Reference in New Issue
Block a user