[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:
parent
5226ea2112
commit
54348a761e
16 changed files with 83 additions and 49 deletions
|
@ -5,7 +5,9 @@
|
|||
*/
|
||||
|
||||
export interface ListWithKuery {
|
||||
page: number;
|
||||
perPage: number;
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
sortField?: string;
|
||||
sortOrder?: 'desc' | 'asc';
|
||||
kuery?: string;
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
|
@ -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) => {
|
||||
|
|
|
@ -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',
|
||||
{
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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' },
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -31,7 +31,7 @@ export async function getAgentEvents(
|
|||
perPage,
|
||||
page,
|
||||
sortField: 'timestamp',
|
||||
sortOrder: 'DESC',
|
||||
sortOrder: 'desc',
|
||||
defaultSearchOperator: 'AND',
|
||||
search: agentId,
|
||||
searchFields: ['agent_id'],
|
||||
|
|
|
@ -61,7 +61,7 @@ async function getEventsCount(soClient: SavedObjectsClientContract, configId?: s
|
|||
perPage: 0,
|
||||
page: 1,
|
||||
sortField: 'timestamp',
|
||||
sortOrder: 'DESC',
|
||||
sortOrder: 'desc',
|
||||
defaultSearchOperator: 'AND',
|
||||
});
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ export async function listEnrollmentApiKeys(
|
|||
page,
|
||||
perPage,
|
||||
sortField: 'created_at',
|
||||
sortOrder: 'DESC',
|
||||
sortOrder: 'desc',
|
||||
filter:
|
||||
kuery && kuery !== ''
|
||||
? kuery.replace(
|
||||
|
|
|
@ -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...
|
||||
|
|
|
@ -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()),
|
||||
});
|
||||
|
||||
|
|
|
@ -220,8 +220,7 @@
|
|||
],
|
||||
"revision": 2,
|
||||
"updated_at": "2020-05-07T19:34:42.533Z",
|
||||
"updated_by": "system",
|
||||
"id": "config1"
|
||||
"updated_by": "system"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue