[Ingest Manager] Add ability to sort to agent configs and package configs (#70676)

* Add sorting params to list endpoints; allow sorting on agent config and package config tables; normalize casing of 'desc' and 'asc'

* Fix es archiver data

* Fix tests
This commit is contained in:
Jen Huang 2020-07-02 23:26:56 -07:00 committed by GitHub
parent 5226ea2112
commit 54348a761e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 83 additions and 49 deletions

View file

@ -5,7 +5,9 @@
*/
export interface ListWithKuery {
page: number;
perPage: number;
page?: number;
perPage?: number;
sortField?: string;
sortOrder?: 'desc' | 'asc';
kuery?: string;
}

View file

@ -13,6 +13,7 @@ export { useLink } from './use_link';
export { useKibanaLink } from './use_kibana_link';
export { usePackageIconType, UsePackageIconType } from './use_package_icon_type';
export { usePagination, Pagination } from './use_pagination';
export { useSorting } from './use_sorting';
export { useDebounce } from './use_debounce';
export * from './use_request';
export * from './use_input';

View file

@ -0,0 +1,16 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { useState } from 'react';
import { CriteriaWithPagination } from '@elastic/eui/src/components/basic_table/basic_table';
export function useSorting<T>(defaultSorting: CriteriaWithPagination<T>['sort']) {
const [sorting, setSorting] = useState<CriteriaWithPagination<T>['sort']>(defaultSorting);
return {
sorting,
setSorting,
};
}

View file

@ -31,7 +31,12 @@ export const StepSelectConfig: React.FunctionComponent<{
data: agentConfigsData,
error: agentConfigsError,
isLoading: isAgentConfigsLoading,
} = useGetAgentConfigs();
} = useGetAgentConfigs({
page: 1,
perPage: 1000,
sortField: 'name',
sortOrder: 'asc',
});
const agentConfigs = agentConfigsData?.items || [];
const agentConfigsById = agentConfigs.reduce(
(acc: { [key: string]: GetAgentConfigsResponseItem }, config) => {

View file

@ -118,6 +118,7 @@ export const PackageConfigsTable: React.FunctionComponent<Props> = ({
(): EuiInMemoryTableProps<InMemoryPackageConfig>['columns'] => [
{
field: 'name',
sortable: true,
name: i18n.translate(
'xpack.ingestManager.configDetails.packageConfigsTable.nameColumnTitle',
{
@ -137,6 +138,7 @@ export const PackageConfigsTable: React.FunctionComponent<Props> = ({
},
{
field: 'packageTitle',
sortable: true,
name: i18n.translate(
'xpack.ingestManager.configDetails.packageConfigsTable.packageNameColumnTitle',
{

View file

@ -17,6 +17,7 @@ import {
EuiTableFieldDataColumnType,
EuiTextColor,
} from '@elastic/eui';
import { CriteriaWithPagination } from '@elastic/eui/src/components/basic_table/basic_table';
import { i18n } from '@kbn/i18n';
import { FormattedMessage, FormattedDate } from '@kbn/i18n/react';
import { useHistory } from 'react-router-dom';
@ -27,6 +28,7 @@ import {
useCapabilities,
useGetAgentConfigs,
usePagination,
useSorting,
useLink,
useConfig,
useUrlParams,
@ -84,6 +86,10 @@ export const AgentConfigListPage: React.FunctionComponent<{}> = () => {
: urlParams.kuery ?? ''
);
const { pagination, pageSizeOptions, setPagination } = usePagination();
const { sorting, setSorting } = useSorting<AgentConfig>({
field: 'updated_at',
direction: 'desc',
});
const history = useHistory();
const isCreateAgentConfigFlyoutOpen = 'create' in urlParams;
const setIsCreateAgentConfigFlyoutOpen = useCallback(
@ -106,6 +112,8 @@ export const AgentConfigListPage: React.FunctionComponent<{}> = () => {
const { isLoading, data: agentConfigData, sendRequest } = useGetAgentConfigs({
page: pagination.currentPage,
perPage: pagination.pageSize,
sortField: sorting?.field,
sortOrder: sorting?.direction,
kuery: search,
});
@ -116,6 +124,7 @@ export const AgentConfigListPage: React.FunctionComponent<{}> = () => {
> = [
{
field: 'name',
sortable: true,
name: i18n.translate('xpack.ingestManager.agentConfigList.nameColumnTitle', {
defaultMessage: 'Name',
}),
@ -158,6 +167,7 @@ export const AgentConfigListPage: React.FunctionComponent<{}> = () => {
},
{
field: 'updated_at',
sortable: true,
name: i18n.translate('xpack.ingestManager.agentConfigList.updatedOnColumnTitle', {
defaultMessage: 'Last updated on',
}),
@ -240,6 +250,16 @@ export const AgentConfigListPage: React.FunctionComponent<{}> = () => {
[createAgentConfigButton]
);
const onTableChange = (criteria: CriteriaWithPagination<AgentConfig>) => {
const newPagination = {
...pagination,
currentPage: criteria.page.index + 1,
pageSize: criteria.page.size,
};
setPagination(newPagination);
setSorting(criteria.sort);
};
return (
<AgentConfigListPageLayout>
{isCreateAgentConfigFlyoutOpen ? (
@ -276,7 +296,7 @@ export const AgentConfigListPage: React.FunctionComponent<{}> = () => {
</EuiFlexGroup>
<EuiSpacer size="m" />
<EuiBasicTable
<EuiBasicTable<AgentConfig>
loading={isLoading}
hasActions={true}
noItemsMessage={
@ -314,14 +334,8 @@ export const AgentConfigListPage: React.FunctionComponent<{}> = () => {
totalItemCount: agentConfigData ? agentConfigData.total : 0,
pageSizeOptions,
}}
onChange={({ page }: { page: { index: number; size: number } }) => {
const newPagination = {
...pagination,
currentPage: page.index + 1,
pageSize: page.size,
};
setPagination(newPagination);
}}
sorting={{ sort: sorting }}
onChange={onTableChange}
/>
</AgentConfigListPageLayout>
);

View file

@ -36,7 +36,10 @@ export const AgentReassignConfigFlyout: React.FunctionComponent<Props> = ({ onCl
agent.config_id
);
const agentConfigsRequest = useGetAgentConfigs();
const agentConfigsRequest = useGetAgentConfigs({
page: 1,
perPage: 1000,
});
const agentConfigs = agentConfigsRequest.data ? agentConfigsRequest.data.items : [];
const [isSubmitting, setIsSubmitting] = useState(false);

View file

@ -119,8 +119,7 @@ const savedObjectTypes: { [key: string]: SavedObjectsType } = {
},
mappings: {
properties: {
id: { type: 'keyword' },
name: { type: 'text' },
name: { type: 'keyword' },
description: { type: 'text' },
namespace: { type: 'keyword' },
is_default: { type: 'boolean' },

View file

@ -143,10 +143,12 @@ class AgentConfigService {
soClient: SavedObjectsClientContract,
options: ListWithKuery
): Promise<{ items: AgentConfig[]; total: number; page: number; perPage: number }> {
const { page = 1, perPage = 20, kuery } = options;
const { page = 1, perPage = 20, sortField = 'updated_at', sortOrder = 'desc', kuery } = options;
const agentConfigs = await soClient.find<AgentConfigSOAttributes>({
type: SAVED_OBJECT_TYPE,
sortField,
sortOrder,
page,
perPage,
// To ensure users don't need to know about SO data structure...
@ -273,7 +275,6 @@ class AgentConfigService {
soClient,
id,
{
...oldAgentConfig,
package_configs: uniq(
[...((oldAgentConfig.package_configs || []) as string[])].filter(
(pkgConfigId) => !packageConfigIds.includes(pkgConfigId)

View file

@ -12,20 +12,24 @@ import {
AGENT_TYPE_EPHEMERAL,
AGENT_POLLING_THRESHOLD_MS,
} from '../../constants';
import { AgentSOAttributes, Agent, AgentEventSOAttributes } from '../../types';
import { AgentSOAttributes, Agent, AgentEventSOAttributes, ListWithKuery } from '../../types';
import { savedObjectToAgent } from './saved_objects';
import { escapeSearchQueryPhrase } from '../saved_object';
export async function listAgents(
soClient: SavedObjectsClientContract,
options: {
page: number;
perPage: number;
kuery?: string;
options: ListWithKuery & {
showInactive: boolean;
}
) {
const { page, perPage, kuery, showInactive = false } = options;
const {
page = 1,
perPage = 20,
sortField = 'enrolled_at',
sortOrder = 'desc',
kuery,
showInactive = false,
} = options;
const filters = [];
@ -49,10 +53,11 @@ export async function listAgents(
const { saved_objects, total } = await soClient.find<AgentSOAttributes>({
type: AGENT_SAVED_OBJECT_TYPE,
sortField,
sortOrder,
page,
perPage,
filter: _joinFilters(filters),
..._getSortFields(),
});
const agents: Agent[] = saved_objects.map(savedObjectToAgent);
@ -137,23 +142,6 @@ export async function deleteAgent(soClient: SavedObjectsClientContract, agentId:
});
}
function _getSortFields(sortOption?: string) {
switch (sortOption) {
case 'ASC':
return {
sortField: 'enrolled_at',
sortOrder: 'ASC',
};
case 'DESC':
default:
return {
sortField: 'enrolled_at',
sortOrder: 'DESC',
};
}
}
function _joinFilters(filters: string[], operator = 'AND') {
return filters.reduce((acc: string | undefined, filter) => {
if (acc) {

View file

@ -31,7 +31,7 @@ export async function getAgentEvents(
perPage,
page,
sortField: 'timestamp',
sortOrder: 'DESC',
sortOrder: 'desc',
defaultSearchOperator: 'AND',
search: agentId,
searchFields: ['agent_id'],

View file

@ -61,7 +61,7 @@ async function getEventsCount(soClient: SavedObjectsClientContract, configId?: s
perPage: 0,
page: 1,
sortField: 'timestamp',
sortOrder: 'DESC',
sortOrder: 'desc',
defaultSearchOperator: 'AND',
});

View file

@ -29,7 +29,7 @@ export async function listEnrollmentApiKeys(
page,
perPage,
sortField: 'created_at',
sortOrder: 'DESC',
sortOrder: 'desc',
filter:
kuery && kuery !== ''
? kuery.replace(

View file

@ -145,10 +145,12 @@ class PackageConfigService {
soClient: SavedObjectsClientContract,
options: ListWithKuery
): Promise<{ items: PackageConfig[]; total: number; page: number; perPage: number }> {
const { page = 1, perPage = 20, kuery } = options;
const { page = 1, perPage = 20, sortField = 'updated_at', sortOrder = 'desc', kuery } = options;
const packageConfigs = await soClient.find<PackageConfigSOAttributes>({
type: SAVED_OBJECT_TYPE,
sortField,
sortOrder,
page,
perPage,
// To ensure users don't need to know about SO data structure...

View file

@ -6,8 +6,10 @@
import { schema, TypeOf } from '@kbn/config-schema';
export const ListWithKuerySchema = schema.object({
page: schema.number({ defaultValue: 1 }),
perPage: schema.number({ defaultValue: 20 }),
page: schema.maybe(schema.number({ defaultValue: 1 })),
perPage: schema.maybe(schema.number({ defaultValue: 20 })),
sortField: schema.maybe(schema.string()),
sortOrder: schema.maybe(schema.oneOf([schema.literal('desc'), schema.literal('asc')])),
kuery: schema.maybe(schema.string()),
});

View file

@ -220,8 +220,7 @@
],
"revision": 2,
"updated_at": "2020-05-07T19:34:42.533Z",
"updated_by": "system",
"id": "config1"
"updated_by": "system"
}
}
}