From 54348a761e4fa075d6537dd2f459efdb0a37c1fe Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Thu, 2 Jul 2020 23:26:56 -0700 Subject: [PATCH] [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 --- .../common/types/rest_spec/common.ts | 6 ++-- .../ingest_manager/hooks/index.ts | 1 + .../ingest_manager/hooks/use_sorting.tsx | 16 +++++++++ .../step_select_config.tsx | 7 +++- .../package_configs/package_configs_table.tsx | 2 ++ .../sections/agent_config/list_page/index.tsx | 32 ++++++++++++----- .../agent_reassign_config_flyout/index.tsx | 5 ++- .../server/saved_objects/index.ts | 3 +- .../server/services/agent_config.ts | 5 +-- .../server/services/agents/crud.ts | 36 +++++++------------ .../server/services/agents/events.ts | 2 +- .../server/services/agents/status.ts | 2 +- .../services/api_keys/enrollment_api_key.ts | 2 +- .../server/services/package_config.ts | 4 ++- .../server/types/rest_spec/common.ts | 6 ++-- .../es_archives/fleet/agents/data.json | 3 +- 16 files changed, 83 insertions(+), 49 deletions(-) create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_sorting.tsx diff --git a/x-pack/plugins/ingest_manager/common/types/rest_spec/common.ts b/x-pack/plugins/ingest_manager/common/types/rest_spec/common.ts index c52471ccfb4f5..0d1f72afa16f1 100644 --- a/x-pack/plugins/ingest_manager/common/types/rest_spec/common.ts +++ b/x-pack/plugins/ingest_manager/common/types/rest_spec/common.ts @@ -5,7 +5,9 @@ */ export interface ListWithKuery { - page: number; - perPage: number; + page?: number; + perPage?: number; + sortField?: string; + sortOrder?: 'desc' | 'asc'; kuery?: string; } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts index 6ebfd3f28fd9b..36b7d412bf276 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts @@ -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'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_sorting.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_sorting.tsx new file mode 100644 index 0000000000000..b00809249897b --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_sorting.tsx @@ -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(defaultSorting: CriteriaWithPagination['sort']) { + const [sorting, setSorting] = useState['sort']>(defaultSorting); + + return { + sorting, + setSorting, + }; +} diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_package_config_page/step_select_config.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_package_config_page/step_select_config.tsx index 70668c2856f98..849d7bfc63f34 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_package_config_page/step_select_config.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_package_config_page/step_select_config.tsx @@ -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) => { diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/package_configs/package_configs_table.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/package_configs/package_configs_table.tsx index 19243084f6821..42d1075e2ee1f 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/package_configs/package_configs_table.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/package_configs/package_configs_table.tsx @@ -118,6 +118,7 @@ export const PackageConfigsTable: React.FunctionComponent = ({ (): EuiInMemoryTableProps['columns'] => [ { field: 'name', + sortable: true, name: i18n.translate( 'xpack.ingestManager.configDetails.packageConfigsTable.nameColumnTitle', { @@ -137,6 +138,7 @@ export const PackageConfigsTable: React.FunctionComponent = ({ }, { field: 'packageTitle', + sortable: true, name: i18n.translate( 'xpack.ingestManager.configDetails.packageConfigsTable.packageNameColumnTitle', { diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx index 0a9daf0038aab..4e79bd4fa7997 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx @@ -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({ + 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) => { + const newPagination = { + ...pagination, + currentPage: criteria.page.index + 1, + pageSize: criteria.page.size, + }; + setPagination(newPagination); + setSorting(criteria.sort); + }; + return ( {isCreateAgentConfigFlyoutOpen ? ( @@ -276,7 +296,7 @@ export const AgentConfigListPage: React.FunctionComponent<{}> = () => { - 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} /> ); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_reassign_config_flyout/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_reassign_config_flyout/index.tsx index 450def54ba1d0..592ca7f7b8380 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_reassign_config_flyout/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_reassign_config_flyout/index.tsx @@ -36,7 +36,10 @@ export const AgentReassignConfigFlyout: React.FunctionComponent = ({ 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); diff --git a/x-pack/plugins/ingest_manager/server/saved_objects/index.ts b/x-pack/plugins/ingest_manager/server/saved_objects/index.ts index 98de9ac217af9..9819a4fa5d750 100644 --- a/x-pack/plugins/ingest_manager/server/saved_objects/index.ts +++ b/x-pack/plugins/ingest_manager/server/saved_objects/index.ts @@ -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' }, diff --git a/x-pack/plugins/ingest_manager/server/services/agent_config.ts b/x-pack/plugins/ingest_manager/server/services/agent_config.ts index ada35d1825069..bd00727714c33 100644 --- a/x-pack/plugins/ingest_manager/server/services/agent_config.ts +++ b/x-pack/plugins/ingest_manager/server/services/agent_config.ts @@ -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({ 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) diff --git a/x-pack/plugins/ingest_manager/server/services/agents/crud.ts b/x-pack/plugins/ingest_manager/server/services/agents/crud.ts index c78a9ff8bb7b5..4420135aec952 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/crud.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/crud.ts @@ -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({ 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) { diff --git a/x-pack/plugins/ingest_manager/server/services/agents/events.ts b/x-pack/plugins/ingest_manager/server/services/agents/events.ts index b6d87c9ca5b2f..55970607c74ab 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/events.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/events.ts @@ -31,7 +31,7 @@ export async function getAgentEvents( perPage, page, sortField: 'timestamp', - sortOrder: 'DESC', + sortOrder: 'desc', defaultSearchOperator: 'AND', search: agentId, searchFields: ['agent_id'], diff --git a/x-pack/plugins/ingest_manager/server/services/agents/status.ts b/x-pack/plugins/ingest_manager/server/services/agents/status.ts index 0efb202eff532..016a2344cf532 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/status.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/status.ts @@ -61,7 +61,7 @@ async function getEventsCount(soClient: SavedObjectsClientContract, configId?: s perPage: 0, page: 1, sortField: 'timestamp', - sortOrder: 'DESC', + sortOrder: 'desc', defaultSearchOperator: 'AND', }); diff --git a/x-pack/plugins/ingest_manager/server/services/api_keys/enrollment_api_key.ts b/x-pack/plugins/ingest_manager/server/services/api_keys/enrollment_api_key.ts index 3b003f47eb6f9..02e2c8151fac7 100644 --- a/x-pack/plugins/ingest_manager/server/services/api_keys/enrollment_api_key.ts +++ b/x-pack/plugins/ingest_manager/server/services/api_keys/enrollment_api_key.ts @@ -29,7 +29,7 @@ export async function listEnrollmentApiKeys( page, perPage, sortField: 'created_at', - sortOrder: 'DESC', + sortOrder: 'desc', filter: kuery && kuery !== '' ? kuery.replace( diff --git a/x-pack/plugins/ingest_manager/server/services/package_config.ts b/x-pack/plugins/ingest_manager/server/services/package_config.ts index c886f4868ad30..5a7546bfee2e0 100644 --- a/x-pack/plugins/ingest_manager/server/services/package_config.ts +++ b/x-pack/plugins/ingest_manager/server/services/package_config.ts @@ -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({ type: SAVED_OBJECT_TYPE, + sortField, + sortOrder, page, perPage, // To ensure users don't need to know about SO data structure... diff --git a/x-pack/plugins/ingest_manager/server/types/rest_spec/common.ts b/x-pack/plugins/ingest_manager/server/types/rest_spec/common.ts index 2c8134d2e8f92..dc0f111680490 100644 --- a/x-pack/plugins/ingest_manager/server/types/rest_spec/common.ts +++ b/x-pack/plugins/ingest_manager/server/types/rest_spec/common.ts @@ -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()), }); diff --git a/x-pack/test/functional/es_archives/fleet/agents/data.json b/x-pack/test/functional/es_archives/fleet/agents/data.json index c317aad8ba05b..b3d49199b0d9e 100644 --- a/x-pack/test/functional/es_archives/fleet/agents/data.json +++ b/x-pack/test/functional/es_archives/fleet/agents/data.json @@ -220,8 +220,7 @@ ], "revision": 2, "updated_at": "2020-05-07T19:34:42.533Z", - "updated_by": "system", - "id": "config1" + "updated_by": "system" } } }