Use server-side sort for host certificates (#26898)
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { IListSort } from "./list_options";
|
||||
|
||||
export interface IHostCertificate {
|
||||
id: number;
|
||||
not_valid_after: string;
|
||||
@@ -22,3 +24,8 @@ export interface IHostCertificate {
|
||||
common_name: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const CERTIFICATES_DEFAULT_SORT: IListSort = {
|
||||
order_key: "common_name",
|
||||
order_direction: "asc",
|
||||
} as const;
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Represents query params used as list options by the Fleet API
|
||||
*/
|
||||
export interface IListOptions {
|
||||
page: number;
|
||||
per_page: number;
|
||||
order_key: string;
|
||||
order_direction: string;
|
||||
}
|
||||
|
||||
export type IListSort = Pick<IListOptions, "order_key" | "order_direction">;
|
||||
|
||||
export type IListPagination = Pick<IListOptions, "page" | "per_page">;
|
||||
@@ -6,7 +6,9 @@ import { Tab, Tabs, TabList, TabPanel } from "react-tabs";
|
||||
import { pick, findIndex } from "lodash";
|
||||
|
||||
import { NotificationContext } from "context/notification";
|
||||
|
||||
import deviceUserAPI, {
|
||||
IGetDeviceCertsRequestParams,
|
||||
IGetDeviceCertificatesResponse,
|
||||
} from "services/entities/device_user";
|
||||
import diskEncryptionAPI from "services/entities/disk_encryption";
|
||||
@@ -16,10 +18,14 @@ import {
|
||||
IDeviceUserResponse,
|
||||
IHostDevice,
|
||||
} from "interfaces/host";
|
||||
import { IListSort } from "interfaces/list_options";
|
||||
import { IHostPolicy } from "interfaces/policy";
|
||||
import { IDeviceGlobalConfig } from "interfaces/config";
|
||||
import { IHostSoftware } from "interfaces/software";
|
||||
import { IHostCertificate } from "interfaces/certificates";
|
||||
import {
|
||||
IHostCertificate,
|
||||
CERTIFICATES_DEFAULT_SORT,
|
||||
} from "interfaces/certificates";
|
||||
import { isAppleDevice } from "interfaces/platform";
|
||||
|
||||
import DeviceUserError from "components/DeviceUserError";
|
||||
@@ -137,6 +143,9 @@ const DeviceUserPage = ({
|
||||
const [certificatePage, setCertificatePage] = useState(
|
||||
DEFAULT_CERTIFICATES_PAGE
|
||||
);
|
||||
const [sortCerts, setSortCerts] = useState<IListSort>({
|
||||
...CERTIFICATES_DEFAULT_SORT,
|
||||
});
|
||||
|
||||
const { data: deviceMapping, refetch: refetchDeviceMapping } = useQuery(
|
||||
["deviceMapping", deviceAuthToken],
|
||||
@@ -174,24 +183,27 @@ const DeviceUserPage = ({
|
||||
IGetDeviceCertificatesResponse,
|
||||
Error,
|
||||
IGetDeviceCertificatesResponse,
|
||||
Array<{ scope: string; token: string; page: number; perPage: number }>
|
||||
Array<IGetDeviceCertsRequestParams & { scope: "device-certificates" }>
|
||||
>(
|
||||
[
|
||||
{
|
||||
scope: "device-certificates",
|
||||
token: deviceAuthToken,
|
||||
page: certificatePage,
|
||||
perPage: DEFAULT_CERTIFICATES_PAGE_SIZE,
|
||||
per_page: DEFAULT_CERTIFICATES_PAGE_SIZE,
|
||||
order_key: sortCerts.order_key,
|
||||
order_direction: sortCerts.order_direction,
|
||||
},
|
||||
],
|
||||
({ queryKey: [{ token, page, perPage }] }) =>
|
||||
deviceUserAPI.getDeviceCertificates(token, page, perPage),
|
||||
({ queryKey }) => deviceUserAPI.getDeviceCertificates(queryKey[0]),
|
||||
{
|
||||
...DEFAULT_USE_QUERY_OPTIONS,
|
||||
// FIXME: is it worth disabling for unsupported platforms? we'd have to workaround the a
|
||||
// catch-22 where we need to know the platform to know if it's supported but we also need to
|
||||
// be able to include the cert refetch in the hosts query hook.
|
||||
enabled: !!deviceUserAPI,
|
||||
keepPreviousData: true,
|
||||
staleTime: 15000,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -486,12 +498,15 @@ const DeviceUserPage = ({
|
||||
isError={isErrorDeviceCertificates}
|
||||
page={certificatePage}
|
||||
pageSize={DEFAULT_CERTIFICATES_PAGE_SIZE}
|
||||
sortHeader={sortCerts.order_key}
|
||||
sortDirection={sortCerts.order_direction}
|
||||
hostPlatform={host.platform}
|
||||
onSelectCertificate={onSelectCertificate}
|
||||
onNextPage={() => setCertificatePage(certificatePage + 1)}
|
||||
onPreviousPage={() =>
|
||||
setCertificatePage(certificatePage - 1)
|
||||
}
|
||||
onSortChange={setSortCerts}
|
||||
/>
|
||||
)}
|
||||
</TabPanel>
|
||||
|
||||
@@ -15,7 +15,10 @@ import activitiesAPI, {
|
||||
IHostPastActivitiesResponse,
|
||||
IHostUpcomingActivitiesResponse,
|
||||
} from "services/entities/activities";
|
||||
import hostAPI, { IGetHostCertificatesResponse } from "services/entities/hosts";
|
||||
import hostAPI, {
|
||||
IGetHostCertificatesResponse,
|
||||
IGetHostCertsRequestParams,
|
||||
} from "services/entities/hosts";
|
||||
import teamAPI, { ILoadTeamsResponse } from "services/entities/teams";
|
||||
|
||||
import {
|
||||
@@ -27,12 +30,16 @@ import {
|
||||
IPackStats,
|
||||
} from "interfaces/host";
|
||||
import { ILabel } from "interfaces/label";
|
||||
import { IListSort } from "interfaces/list_options";
|
||||
import { IHostPolicy } from "interfaces/policy";
|
||||
import { IQueryStats } from "interfaces/query_stats";
|
||||
import { IHostSoftware } from "interfaces/software";
|
||||
import { ITeam } from "interfaces/team";
|
||||
import { IHostUpcomingActivity } from "interfaces/activity";
|
||||
import { IHostCertificate } from "interfaces/certificates";
|
||||
import {
|
||||
IHostCertificate,
|
||||
CERTIFICATES_DEFAULT_SORT,
|
||||
} from "interfaces/certificates";
|
||||
|
||||
import { normalizeEmptyValues, wrapFleetHelper } from "utilities/helpers";
|
||||
import permissions from "utilities/permissions";
|
||||
@@ -225,6 +232,9 @@ const HostDetailsPage = ({
|
||||
const [certificatePage, setCertificatePage] = useState(
|
||||
DEFAULT_CERTIFICATES_PAGE
|
||||
);
|
||||
const [sortCerts, setSortCerts] = useState<IListSort>({
|
||||
...CERTIFICATES_DEFAULT_SORT,
|
||||
});
|
||||
|
||||
const { data: teams } = useQuery<ILoadTeamsResponse, Error, ITeam[]>(
|
||||
"teams",
|
||||
@@ -283,31 +293,33 @@ const HostDetailsPage = ({
|
||||
|
||||
const {
|
||||
data: hostCertificates,
|
||||
isLoading: isLoadingHostCertificates,
|
||||
isError: isErrorHostCertificates,
|
||||
refetch: refetchHostCertificates,
|
||||
} = useQuery<
|
||||
IGetHostCertificatesResponse,
|
||||
Error,
|
||||
IGetHostCertificatesResponse,
|
||||
Array<{ scope: string; hostId: number; page: number; perPage: number }>
|
||||
Array<IGetHostCertsRequestParams & { scope: "host-certificates" }>
|
||||
>(
|
||||
[
|
||||
{
|
||||
scope: "host-certificates",
|
||||
hostId: hostIdFromURL,
|
||||
host_id: hostIdFromURL,
|
||||
page: certificatePage,
|
||||
perPage: DEFAULT_CERTIFICATES_PAGE_SIZE,
|
||||
per_page: DEFAULT_CERTIFICATES_PAGE_SIZE,
|
||||
order_key: sortCerts.order_key,
|
||||
order_direction: sortCerts.order_direction,
|
||||
},
|
||||
],
|
||||
({ queryKey: [{ hostId, page, perPage }] }) =>
|
||||
hostAPI.getHostCertificates(hostId, page, perPage),
|
||||
({ queryKey }) => hostAPI.getHostCertificates(queryKey[0]),
|
||||
{
|
||||
...DEFAULT_USE_QUERY_OPTIONS,
|
||||
// FIXME: is it worth disabling for unsupported platforms? we'd have to workaround the a
|
||||
// catch-22 where we need to know the platform to know if it's supported but we also need to
|
||||
// be able to include the cert refetch in the hosts query hook.
|
||||
enabled: !!hostIdFromURL,
|
||||
keepPreviousData: true,
|
||||
staleTime: 15000,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -786,8 +798,7 @@ const HostDetailsPage = ({
|
||||
!host ||
|
||||
isLoadingHost ||
|
||||
pastActivitiesIsLoading ||
|
||||
upcomingActivitiesIsLoading ||
|
||||
isLoadingHostCertificates
|
||||
upcomingActivitiesIsLoading
|
||||
) {
|
||||
return <Spinner />;
|
||||
}
|
||||
@@ -980,6 +991,9 @@ const HostDetailsPage = ({
|
||||
onPreviousPage={() =>
|
||||
setCertificatePage(certificatePage - 1)
|
||||
}
|
||||
sortDirection={sortCerts.order_direction}
|
||||
sortHeader={sortCerts.order_key}
|
||||
onSortChange={setSortCerts}
|
||||
/>
|
||||
)}
|
||||
</TabPanel>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import React from "react";
|
||||
|
||||
import { IHostCertificate } from "interfaces/certificates";
|
||||
import { HostPlatform } from "interfaces/platform";
|
||||
import { IGetHostCertificatesResponse } from "services/entities/hosts";
|
||||
|
||||
import { IHostCertificate } from "interfaces/certificates";
|
||||
import { IListSort } from "interfaces/list_options";
|
||||
import { HostPlatform } from "interfaces/platform";
|
||||
|
||||
import Card from "components/Card";
|
||||
import DataError from "components/DataError";
|
||||
|
||||
@@ -16,11 +18,14 @@ interface ICertificatesProps {
|
||||
hostPlatform: HostPlatform;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
sortHeader: string;
|
||||
sortDirection: string;
|
||||
isError: boolean;
|
||||
isMyDevicePage?: boolean;
|
||||
onSelectCertificate: (certificate: IHostCertificate) => void;
|
||||
onNextPage: () => void;
|
||||
onPreviousPage: () => void;
|
||||
onSortChange: ({ order_key, order_direction }: IListSort) => void;
|
||||
}
|
||||
|
||||
const CertificatesCard = ({
|
||||
@@ -29,10 +34,13 @@ const CertificatesCard = ({
|
||||
isError,
|
||||
page,
|
||||
pageSize,
|
||||
sortHeader,
|
||||
sortDirection,
|
||||
isMyDevicePage = false,
|
||||
onSelectCertificate,
|
||||
onNextPage,
|
||||
onPreviousPage,
|
||||
onSortChange,
|
||||
}: ICertificatesProps) => {
|
||||
const renderContent = () => {
|
||||
if (isError) return <DataError />;
|
||||
@@ -43,6 +51,9 @@ const CertificatesCard = ({
|
||||
showHelpText={!isMyDevicePage && hostPlatform === "darwin"}
|
||||
page={page}
|
||||
pageSize={pageSize}
|
||||
sortDirection={sortDirection}
|
||||
sortHeader={sortHeader}
|
||||
onSortChange={onSortChange}
|
||||
onSelectCertificate={onSelectCertificate}
|
||||
onNextPage={onNextPage}
|
||||
onPreviousPage={onPreviousPage}
|
||||
|
||||
+25
-10
@@ -2,6 +2,7 @@ import React, { useCallback } from "react";
|
||||
|
||||
import { IHostCertificate } from "interfaces/certificates";
|
||||
import { IGetHostCertificatesResponse } from "services/entities/hosts";
|
||||
import { IListSort } from "interfaces/list_options";
|
||||
|
||||
import TableContainer from "components/TableContainer";
|
||||
import CustomLink from "components/CustomLink";
|
||||
@@ -17,9 +18,12 @@ interface ICertificatesTableProps {
|
||||
showHelpText: boolean;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
sortHeader: string;
|
||||
sortDirection: string;
|
||||
onSelectCertificate: (certificate: IHostCertificate) => void;
|
||||
onNextPage: () => void;
|
||||
onPreviousPage: () => void;
|
||||
onSortChange: ({ order_key, order_direction }: IListSort) => void;
|
||||
}
|
||||
|
||||
const CertificatesTable = ({
|
||||
@@ -27,9 +31,12 @@ const CertificatesTable = ({
|
||||
showHelpText,
|
||||
page,
|
||||
pageSize,
|
||||
sortDirection,
|
||||
sortHeader,
|
||||
onSelectCertificate,
|
||||
onNextPage,
|
||||
onPreviousPage,
|
||||
onSortChange,
|
||||
}: ICertificatesTableProps) => {
|
||||
const tableConfig = generateTableConfig();
|
||||
|
||||
@@ -38,18 +45,23 @@ const CertificatesTable = ({
|
||||
};
|
||||
|
||||
const onQueryChange = useCallback(
|
||||
async (newTableQuery: ITableQueryData) => {
|
||||
console.log(newTableQuery);
|
||||
|
||||
if (page === newTableQuery.pageIndex) return;
|
||||
|
||||
if (newTableQuery.pageIndex > page) {
|
||||
onNextPage();
|
||||
} else {
|
||||
onPreviousPage();
|
||||
(newQuery: ITableQueryData) => {
|
||||
switch (true) {
|
||||
case newQuery.pageIndex > page:
|
||||
return onNextPage();
|
||||
case newQuery.pageIndex < page:
|
||||
return onPreviousPage();
|
||||
case newQuery.sortHeader !== sortHeader ||
|
||||
newQuery.sortDirection !== sortDirection:
|
||||
return onSortChange({
|
||||
order_key: newQuery.sortHeader,
|
||||
order_direction: newQuery.sortDirection || "asc",
|
||||
});
|
||||
default:
|
||||
return undefined; // noop
|
||||
}
|
||||
},
|
||||
[onNextPage, onPreviousPage, page]
|
||||
[onNextPage, onPreviousPage, onSortChange, page, sortDirection, sortHeader]
|
||||
);
|
||||
|
||||
const helpText = showHelpText ? (
|
||||
@@ -81,8 +93,11 @@ const CertificatesTable = ({
|
||||
)}
|
||||
pageSize={pageSize}
|
||||
defaultPageIndex={page}
|
||||
defaultSortHeader={sortHeader}
|
||||
defaultSortDirection={sortDirection}
|
||||
onQueryChange={onQueryChange}
|
||||
disableNextPage={data?.meta.has_next_results === false}
|
||||
manualSortBy
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { IDeviceUserResponse } from "interfaces/host";
|
||||
import { IListOptions } from "interfaces/list_options";
|
||||
import { IDeviceSoftware } from "interfaces/software";
|
||||
import { IHostCertificate } from "interfaces/certificates";
|
||||
import sendRequest from "services";
|
||||
import endpoints from "utilities/endpoints";
|
||||
import { buildQueryStringFromParams } from "utilities/url";
|
||||
import { createMockGetHostCertificatesResponse } from "__mocks__/certificatesMock";
|
||||
|
||||
import { IHostSoftwareQueryParams } from "./hosts";
|
||||
|
||||
@@ -38,6 +38,10 @@ export interface IGetDeviceCertificatesResponse {
|
||||
};
|
||||
}
|
||||
|
||||
export interface IGetDeviceCertsRequestParams extends IListOptions {
|
||||
token: string;
|
||||
}
|
||||
|
||||
export default {
|
||||
loadHostDetails: ({
|
||||
token,
|
||||
@@ -86,17 +90,19 @@ export default {
|
||||
return sendRequest("POST", path);
|
||||
},
|
||||
|
||||
getDeviceCertificates: (
|
||||
deviceToken: string,
|
||||
page = 0,
|
||||
perPage = 10
|
||||
): Promise<IGetDeviceCertificatesResponse> => {
|
||||
getDeviceCertificates: ({
|
||||
token,
|
||||
page,
|
||||
per_page,
|
||||
order_key,
|
||||
order_direction,
|
||||
}: IGetDeviceCertsRequestParams): Promise<IGetDeviceCertificatesResponse> => {
|
||||
const { DEVICE_CERTIFICATES } = endpoints;
|
||||
const path = `${DEVICE_CERTIFICATES(
|
||||
deviceToken
|
||||
)}?${buildQueryStringFromParams({
|
||||
const path = `${DEVICE_CERTIFICATES(token)}?${buildQueryStringFromParams({
|
||||
page,
|
||||
per_page: perPage,
|
||||
per_page,
|
||||
order_key,
|
||||
order_direction,
|
||||
})}`;
|
||||
|
||||
return sendRequest("GET", path);
|
||||
|
||||
@@ -24,10 +24,7 @@ import {
|
||||
import { IMunkiIssuesAggregate } from "interfaces/macadmins";
|
||||
import { PlatformValueOptions, PolicyResponse } from "utilities/constants";
|
||||
import { IHostCertificate } from "interfaces/certificates";
|
||||
import {
|
||||
createMockGetHostCertificatesResponse,
|
||||
createMockHostCertificate,
|
||||
} from "__mocks__/certificatesMock";
|
||||
import { IListOptions } from "interfaces/list_options";
|
||||
|
||||
export interface ISortOption {
|
||||
key: string;
|
||||
@@ -176,6 +173,10 @@ export interface IHostSoftwareQueryKey extends IHostSoftwareQueryParams {
|
||||
softwareUpdatedAt?: string;
|
||||
}
|
||||
|
||||
export interface IGetHostCertsRequestParams extends IListOptions {
|
||||
host_id: number;
|
||||
}
|
||||
|
||||
export interface IGetHostCertificatesResponse {
|
||||
certificates: IHostCertificate[];
|
||||
meta: {
|
||||
@@ -601,15 +602,19 @@ export default {
|
||||
);
|
||||
},
|
||||
|
||||
getHostCertificates: (
|
||||
hostId: number,
|
||||
page = 0,
|
||||
perPage = 10
|
||||
): Promise<IGetHostCertificatesResponse> => {
|
||||
getHostCertificates: ({
|
||||
host_id,
|
||||
page,
|
||||
per_page,
|
||||
order_key,
|
||||
order_direction,
|
||||
}: IGetHostCertsRequestParams): Promise<IGetHostCertificatesResponse> => {
|
||||
const { HOST_CERTIFICATES } = endpoints;
|
||||
const path = `${HOST_CERTIFICATES(hostId)}?${buildQueryStringFromParams({
|
||||
const path = `${HOST_CERTIFICATES(host_id)}?${buildQueryStringFromParams({
|
||||
page,
|
||||
per_page: perPage,
|
||||
per_page,
|
||||
order_key,
|
||||
order_direction,
|
||||
})}`;
|
||||
|
||||
return sendRequest("GET", path);
|
||||
|
||||
Reference in New Issue
Block a user