mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Co-authored-by: Joey F. Poon <joey.poon@elastic.co>
This commit is contained in:
parent
0787862f74
commit
76f073f846
11 changed files with 2114 additions and 992 deletions
|
@ -177,7 +177,7 @@ export interface ResolverPaginatedEvents {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returned by the server via /api/endpoint/metadata
|
||||
* Returned by the server via POST /api/endpoint/metadata
|
||||
*/
|
||||
export interface HostResultList {
|
||||
/* the hosts restricted by the page size */
|
||||
|
@ -1231,3 +1231,22 @@ export interface ListPageRouteState {
|
|||
/** The label for the button */
|
||||
backButtonLabel?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API standard base response for list types
|
||||
*/
|
||||
export interface BaseListResponse {
|
||||
data: unknown[];
|
||||
page: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
sort?: string;
|
||||
sortOrder?: 'asc' | 'desc';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returned by the server via GET /api/endpoint/metadata
|
||||
*/
|
||||
export interface MetadataListResponse extends BaseListResponse {
|
||||
data: HostInfo[];
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import { TypeOf } from '@kbn/config-schema';
|
|||
import {
|
||||
IKibanaResponse,
|
||||
IScopedClusterClient,
|
||||
KibanaRequest,
|
||||
KibanaResponseFactory,
|
||||
Logger,
|
||||
RequestHandler,
|
||||
|
@ -22,6 +21,7 @@ import {
|
|||
HostMetadata,
|
||||
HostResultList,
|
||||
HostStatus,
|
||||
MetadataListResponse,
|
||||
} from '../../../../common/endpoint/types';
|
||||
import type { SecuritySolutionRequestHandlerContext } from '../../../types';
|
||||
|
||||
|
@ -33,7 +33,11 @@ import {
|
|||
import { Agent, PackagePolicy } from '../../../../../fleet/common/types/models';
|
||||
import { AgentNotFoundError } from '../../../../../fleet/server';
|
||||
import { EndpointAppContext, HostListQueryResult } from '../../types';
|
||||
import { GetMetadataListRequestSchema, GetMetadataRequestSchema } from './index';
|
||||
import {
|
||||
GetMetadataListRequestSchema,
|
||||
GetMetadataListRequestSchemaV2,
|
||||
GetMetadataRequestSchema,
|
||||
} from './index';
|
||||
import { findAllUnenrolledAgentIds } from './support/unenroll';
|
||||
import { getAllEndpointPackagePolicies } from './support/endpoint_package_policies';
|
||||
import { findAgentIdsByStatus } from './support/agent_status';
|
||||
|
@ -125,33 +129,35 @@ export const getMetadataListRequestHandler = function (
|
|||
context.core.savedObjects.client
|
||||
);
|
||||
|
||||
body = await legacyListMetadataQuery(
|
||||
context,
|
||||
request,
|
||||
endpointAppContext,
|
||||
logger,
|
||||
endpointPolicies
|
||||
);
|
||||
const pagingProperties = await getPagingProperties(request, endpointAppContext);
|
||||
|
||||
body = await legacyListMetadataQuery(context, endpointAppContext, logger, endpointPolicies, {
|
||||
page: pagingProperties.pageIndex,
|
||||
pageSize: pagingProperties.pageSize,
|
||||
kuery: request?.body?.filters?.kql || '',
|
||||
hostStatuses: request?.body?.filters?.host_status || [],
|
||||
});
|
||||
return response.ok({ body });
|
||||
}
|
||||
|
||||
// Unified index is installed and being used - perform search using new approach
|
||||
try {
|
||||
const pagingProperties = await getPagingProperties(request, endpointAppContext);
|
||||
const { data, page, total, pageSize } = await endpointMetadataService.getHostMetadataList(
|
||||
const { data, total } = await endpointMetadataService.getHostMetadataList(
|
||||
context.core.elasticsearch.client.asCurrentUser,
|
||||
{
|
||||
page: pagingProperties.pageIndex + 1,
|
||||
page: pagingProperties.pageIndex,
|
||||
pageSize: pagingProperties.pageSize,
|
||||
filters: request.body?.filters || {},
|
||||
hostStatuses: request.body?.filters.host_status || [],
|
||||
kuery: request.body?.filters.kql || '',
|
||||
}
|
||||
);
|
||||
|
||||
body = {
|
||||
hosts: data,
|
||||
request_page_index: page - 1,
|
||||
total,
|
||||
request_page_size: pageSize,
|
||||
request_page_index: pagingProperties.pageIndex * pagingProperties.pageSize,
|
||||
request_page_size: pagingProperties.pageSize,
|
||||
};
|
||||
} catch (error) {
|
||||
return errorHandler(logger, response, error);
|
||||
|
@ -161,6 +167,83 @@ export const getMetadataListRequestHandler = function (
|
|||
};
|
||||
};
|
||||
|
||||
export function getMetadataListRequestHandlerV2(
|
||||
endpointAppContext: EndpointAppContext,
|
||||
logger: Logger
|
||||
): RequestHandler<
|
||||
unknown,
|
||||
TypeOf<typeof GetMetadataListRequestSchemaV2.query>,
|
||||
unknown,
|
||||
SecuritySolutionRequestHandlerContext
|
||||
> {
|
||||
return async (context, request, response) => {
|
||||
const endpointMetadataService = endpointAppContext.service.getEndpointMetadataService();
|
||||
if (!endpointMetadataService) {
|
||||
throw new EndpointError('endpoint metadata service not available');
|
||||
}
|
||||
|
||||
let doesUnitedIndexExist = false;
|
||||
let didUnitedIndexError = false;
|
||||
let body: MetadataListResponse = {
|
||||
data: [],
|
||||
total: 0,
|
||||
page: 0,
|
||||
pageSize: 0,
|
||||
};
|
||||
|
||||
try {
|
||||
doesUnitedIndexExist = await endpointMetadataService.doesUnitedIndexExist(
|
||||
context.core.elasticsearch.client.asCurrentUser
|
||||
);
|
||||
} catch (error) {
|
||||
// for better UX, try legacy query instead of immediately failing on united index error
|
||||
didUnitedIndexError = true;
|
||||
}
|
||||
|
||||
// If no unified Index present, then perform a search using the legacy approach
|
||||
if (!doesUnitedIndexExist || didUnitedIndexError) {
|
||||
const endpointPolicies = await getAllEndpointPackagePolicies(
|
||||
endpointAppContext.service.getPackagePolicyService(),
|
||||
context.core.savedObjects.client
|
||||
);
|
||||
|
||||
const legacyResponse = await legacyListMetadataQuery(
|
||||
context,
|
||||
endpointAppContext,
|
||||
logger,
|
||||
endpointPolicies,
|
||||
request.query
|
||||
);
|
||||
body = {
|
||||
data: legacyResponse.hosts,
|
||||
total: legacyResponse.total,
|
||||
page: request.query.page,
|
||||
pageSize: request.query.pageSize,
|
||||
};
|
||||
return response.ok({ body });
|
||||
}
|
||||
|
||||
// Unified index is installed and being used - perform search using new approach
|
||||
try {
|
||||
const { data, total } = await endpointMetadataService.getHostMetadataList(
|
||||
context.core.elasticsearch.client.asCurrentUser,
|
||||
request.query
|
||||
);
|
||||
|
||||
body = {
|
||||
data,
|
||||
total,
|
||||
page: request.query.page,
|
||||
pageSize: request.query.pageSize,
|
||||
};
|
||||
} catch (error) {
|
||||
return errorHandler(logger, response, error);
|
||||
}
|
||||
|
||||
return response.ok({ body });
|
||||
};
|
||||
}
|
||||
|
||||
export const getMetadataRequestHandler = function (
|
||||
endpointAppContext: EndpointAppContext,
|
||||
logger: Logger
|
||||
|
@ -420,11 +503,10 @@ export async function enrichHostMetadata(
|
|||
|
||||
async function legacyListMetadataQuery(
|
||||
context: SecuritySolutionRequestHandlerContext,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
request: KibanaRequest<any, any, any>,
|
||||
endpointAppContext: EndpointAppContext,
|
||||
logger: Logger,
|
||||
endpointPolicies: PackagePolicy[]
|
||||
endpointPolicies: PackagePolicy[],
|
||||
queryOptions: TypeOf<typeof GetMetadataListRequestSchemaV2.query>
|
||||
): Promise<HostResultList> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const agentService = endpointAppContext.service.getAgentService()!;
|
||||
|
@ -447,14 +529,16 @@ async function legacyListMetadataQuery(
|
|||
endpointPolicyIds
|
||||
);
|
||||
|
||||
const statusesToFilter = request?.body?.filters?.host_status ?? [];
|
||||
const statusAgentIds = await findAgentIdsByStatus(
|
||||
agentService,
|
||||
context.core.elasticsearch.client.asCurrentUser,
|
||||
statusesToFilter
|
||||
queryOptions.hostStatuses
|
||||
);
|
||||
|
||||
const queryParams = await kibanaRequestToMetadataListESQuery(request, endpointAppContext, {
|
||||
const queryParams = await kibanaRequestToMetadataListESQuery({
|
||||
page: queryOptions.page,
|
||||
pageSize: queryOptions.pageSize,
|
||||
kuery: queryOptions.kuery,
|
||||
unenrolledAgentIds,
|
||||
statusAgentIds,
|
||||
});
|
||||
|
|
|
@ -9,7 +9,12 @@ import { schema } from '@kbn/config-schema';
|
|||
|
||||
import { HostStatus } from '../../../../common/endpoint/types';
|
||||
import { EndpointAppContext } from '../../types';
|
||||
import { getLogger, getMetadataListRequestHandler, getMetadataRequestHandler } from './handlers';
|
||||
import {
|
||||
getLogger,
|
||||
getMetadataListRequestHandler,
|
||||
getMetadataRequestHandler,
|
||||
getMetadataListRequestHandlerV2,
|
||||
} from './handlers';
|
||||
import type { SecuritySolutionPluginRouter } from '../../../types';
|
||||
import {
|
||||
HOST_METADATA_GET_ROUTE,
|
||||
|
@ -60,27 +65,54 @@ export const GetMetadataListRequestSchema = {
|
|||
),
|
||||
};
|
||||
|
||||
export const GetMetadataListRequestSchemaV2 = {
|
||||
query: schema.object({
|
||||
page: schema.number({ defaultValue: 0 }),
|
||||
pageSize: schema.number({ defaultValue: 10, min: 1, max: 10000 }),
|
||||
kuery: schema.maybe(schema.string()),
|
||||
hostStatuses: schema.arrayOf(
|
||||
schema.oneOf([
|
||||
schema.literal(HostStatus.HEALTHY.toString()),
|
||||
schema.literal(HostStatus.OFFLINE.toString()),
|
||||
schema.literal(HostStatus.UPDATING.toString()),
|
||||
schema.literal(HostStatus.UNHEALTHY.toString()),
|
||||
schema.literal(HostStatus.INACTIVE.toString()),
|
||||
]),
|
||||
{ defaultValue: [] }
|
||||
),
|
||||
}),
|
||||
};
|
||||
|
||||
export function registerEndpointRoutes(
|
||||
router: SecuritySolutionPluginRouter,
|
||||
endpointAppContext: EndpointAppContext
|
||||
) {
|
||||
const logger = getLogger(endpointAppContext);
|
||||
|
||||
router.post(
|
||||
router.get(
|
||||
{
|
||||
path: `${HOST_METADATA_LIST_ROUTE}`,
|
||||
validate: GetMetadataListRequestSchema,
|
||||
path: HOST_METADATA_LIST_ROUTE,
|
||||
validate: GetMetadataListRequestSchemaV2,
|
||||
options: { authRequired: true, tags: ['access:securitySolution'] },
|
||||
},
|
||||
getMetadataListRequestHandler(endpointAppContext, logger)
|
||||
getMetadataListRequestHandlerV2(endpointAppContext, logger)
|
||||
);
|
||||
|
||||
router.get(
|
||||
{
|
||||
path: `${HOST_METADATA_GET_ROUTE}`,
|
||||
path: HOST_METADATA_GET_ROUTE,
|
||||
validate: GetMetadataRequestSchema,
|
||||
options: { authRequired: true, tags: ['access:securitySolution'] },
|
||||
},
|
||||
getMetadataRequestHandler(endpointAppContext, logger)
|
||||
);
|
||||
|
||||
router.post(
|
||||
{
|
||||
path: HOST_METADATA_LIST_ROUTE,
|
||||
validate: GetMetadataListRequestSchema,
|
||||
options: { authRequired: true, tags: ['access:securitySolution'] },
|
||||
},
|
||||
getMetadataListRequestHandler(endpointAppContext, logger)
|
||||
);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -5,39 +5,35 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { httpServerMock, loggingSystemMock } from '../../../../../../../src/core/server/mocks';
|
||||
import {
|
||||
kibanaRequestToMetadataListESQuery,
|
||||
getESQueryHostMetadataByID,
|
||||
buildUnitedIndexQuery,
|
||||
} from './query_builders';
|
||||
import { EndpointAppContextService } from '../../endpoint_app_context_services';
|
||||
import { createMockConfig } from '../../../lib/detection_engine/routes/__mocks__';
|
||||
import { metadataCurrentIndexPattern } from '../../../../common/endpoint/constants';
|
||||
import { parseExperimentalConfigValue } from '../../../../common/experimental_features';
|
||||
import { get } from 'lodash';
|
||||
import { expectedCompleteUnitedIndexQuery } from './query_builders.fixtures';
|
||||
|
||||
describe('query builder', () => {
|
||||
describe('MetadataListESQuery', () => {
|
||||
it('queries the correct index', async () => {
|
||||
const mockRequest = httpServerMock.createKibanaRequest({ body: {} });
|
||||
const query = await kibanaRequestToMetadataListESQuery(mockRequest, {
|
||||
logFactory: loggingSystemMock.create(),
|
||||
service: new EndpointAppContextService(),
|
||||
config: () => Promise.resolve(createMockConfig()),
|
||||
experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
|
||||
const query = await kibanaRequestToMetadataListESQuery({
|
||||
page: 0,
|
||||
pageSize: 10,
|
||||
kuery: '',
|
||||
unenrolledAgentIds: [],
|
||||
statusAgentIds: [],
|
||||
});
|
||||
expect(query.index).toEqual(metadataCurrentIndexPattern);
|
||||
});
|
||||
|
||||
it('sorts using *event.created', async () => {
|
||||
const mockRequest = httpServerMock.createKibanaRequest({ body: {} });
|
||||
const query = await kibanaRequestToMetadataListESQuery(mockRequest, {
|
||||
logFactory: loggingSystemMock.create(),
|
||||
service: new EndpointAppContextService(),
|
||||
config: () => Promise.resolve(createMockConfig()),
|
||||
experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
|
||||
const query = await kibanaRequestToMetadataListESQuery({
|
||||
page: 0,
|
||||
pageSize: 10,
|
||||
kuery: '',
|
||||
unenrolledAgentIds: [],
|
||||
statusAgentIds: [],
|
||||
});
|
||||
expect(query.body.sort).toContainEqual({
|
||||
'event.created': {
|
||||
|
@ -55,21 +51,13 @@ describe('query builder', () => {
|
|||
|
||||
it('excludes unenrolled elastic agents when they exist, by default', async () => {
|
||||
const unenrolledElasticAgentId = '1fdca33f-799f-49f4-939c-ea4383c77672';
|
||||
const mockRequest = httpServerMock.createKibanaRequest({
|
||||
body: {},
|
||||
const query = await kibanaRequestToMetadataListESQuery({
|
||||
page: 0,
|
||||
pageSize: 10,
|
||||
kuery: '',
|
||||
unenrolledAgentIds: [unenrolledElasticAgentId],
|
||||
statusAgentIds: [],
|
||||
});
|
||||
const query = await kibanaRequestToMetadataListESQuery(
|
||||
mockRequest,
|
||||
{
|
||||
logFactory: loggingSystemMock.create(),
|
||||
service: new EndpointAppContextService(),
|
||||
config: () => Promise.resolve(createMockConfig()),
|
||||
experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
|
||||
},
|
||||
{
|
||||
unenrolledAgentIds: [unenrolledElasticAgentId],
|
||||
}
|
||||
);
|
||||
|
||||
expect(query.body.query).toEqual({
|
||||
bool: {
|
||||
|
@ -100,16 +88,12 @@ describe('query builder', () => {
|
|||
|
||||
describe('test query builder with kql filter', () => {
|
||||
it('test default query params for all endpoints metadata when body filter is provided', async () => {
|
||||
const mockRequest = httpServerMock.createKibanaRequest({
|
||||
body: {
|
||||
filters: { kql: 'not host.ip:10.140.73.246' },
|
||||
},
|
||||
});
|
||||
const query = await kibanaRequestToMetadataListESQuery(mockRequest, {
|
||||
logFactory: loggingSystemMock.create(),
|
||||
service: new EndpointAppContextService(),
|
||||
config: () => Promise.resolve(createMockConfig()),
|
||||
experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
|
||||
const query = await kibanaRequestToMetadataListESQuery({
|
||||
page: 0,
|
||||
pageSize: 10,
|
||||
kuery: 'not host.ip:10.140.73.246',
|
||||
unenrolledAgentIds: [],
|
||||
statusAgentIds: [],
|
||||
});
|
||||
|
||||
expect(query.body.query.bool.must).toContainEqual({
|
||||
|
@ -135,25 +119,13 @@ describe('query builder', () => {
|
|||
'and when body filter is provided',
|
||||
async () => {
|
||||
const unenrolledElasticAgentId = '1fdca33f-799f-49f4-939c-ea4383c77672';
|
||||
const mockRequest = httpServerMock.createKibanaRequest({
|
||||
body: {
|
||||
filters: { kql: 'not host.ip:10.140.73.246' },
|
||||
},
|
||||
const query = await kibanaRequestToMetadataListESQuery({
|
||||
page: 0,
|
||||
pageSize: 10,
|
||||
kuery: 'not host.ip:10.140.73.246',
|
||||
unenrolledAgentIds: [unenrolledElasticAgentId],
|
||||
statusAgentIds: [],
|
||||
});
|
||||
const query = await kibanaRequestToMetadataListESQuery(
|
||||
mockRequest,
|
||||
{
|
||||
logFactory: loggingSystemMock.create(),
|
||||
service: new EndpointAppContextService(),
|
||||
config: () => Promise.resolve(createMockConfig()),
|
||||
experimentalFeatures: parseExperimentalConfigValue(
|
||||
createMockConfig().enableExperimental
|
||||
),
|
||||
},
|
||||
{
|
||||
unenrolledAgentIds: [unenrolledElasticAgentId],
|
||||
}
|
||||
);
|
||||
|
||||
expect(query.body.query.bool.must).toEqual([
|
||||
{
|
||||
|
@ -222,7 +194,10 @@ describe('query builder', () => {
|
|||
|
||||
describe('buildUnitedIndexQuery', () => {
|
||||
it('correctly builds empty query', async () => {
|
||||
const query = await buildUnitedIndexQuery({ page: 1, pageSize: 10, filters: {} }, []);
|
||||
const query = await buildUnitedIndexQuery(
|
||||
{ page: 1, pageSize: 10, hostStatuses: [], kuery: '' },
|
||||
[]
|
||||
);
|
||||
const expected = {
|
||||
bool: {
|
||||
must_not: {
|
||||
|
@ -267,10 +242,8 @@ describe('query builder', () => {
|
|||
{
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
filters: {
|
||||
kql: 'united.endpoint.host.os.name : *',
|
||||
host_status: ['healthy'],
|
||||
},
|
||||
kuery: 'united.endpoint.host.os.name : *',
|
||||
hostStatuses: ['healthy'],
|
||||
},
|
||||
['test-endpoint-policy-id']
|
||||
);
|
||||
|
|
|
@ -6,14 +6,16 @@
|
|||
*/
|
||||
|
||||
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import { TypeOf } from '@kbn/config-schema';
|
||||
import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query';
|
||||
import {
|
||||
metadataCurrentIndexPattern,
|
||||
METADATA_UNITED_INDEX,
|
||||
} from '../../../../common/endpoint/constants';
|
||||
import { KibanaRequest } from '../../../../../../../src/core/server';
|
||||
import { EndpointAppContext, GetHostMetadataListQuery } from '../../types';
|
||||
import { EndpointAppContext } from '../../types';
|
||||
import { buildStatusesKuery } from './support/agent_status';
|
||||
import { GetMetadataListRequestSchemaV2 } from '.';
|
||||
|
||||
/**
|
||||
* 00000000-0000-0000-0000-000000000000 is initial Elastic Agent id sent by Endpoint before policy is configured
|
||||
|
@ -25,6 +27,9 @@ const IGNORED_ELASTIC_AGENT_IDS = [
|
|||
];
|
||||
|
||||
export interface QueryBuilderOptions {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
kuery?: string;
|
||||
unenrolledAgentIds?: string[];
|
||||
statusAgentIds?: string[];
|
||||
}
|
||||
|
@ -50,26 +55,21 @@ export const MetadataSortMethod: estypes.SearchSortContainer[] = [
|
|||
];
|
||||
|
||||
export async function kibanaRequestToMetadataListESQuery(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
request: KibanaRequest<any, any, any>,
|
||||
endpointAppContext: EndpointAppContext,
|
||||
queryBuilderOptions?: QueryBuilderOptions
|
||||
queryBuilderOptions: QueryBuilderOptions
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
): Promise<Record<string, any>> {
|
||||
const pagingProperties = await getPagingProperties(request, endpointAppContext);
|
||||
|
||||
return {
|
||||
body: {
|
||||
query: buildQueryBody(
|
||||
request,
|
||||
queryBuilderOptions?.kuery,
|
||||
IGNORED_ELASTIC_AGENT_IDS.concat(queryBuilderOptions?.unenrolledAgentIds ?? []),
|
||||
queryBuilderOptions?.statusAgentIds
|
||||
),
|
||||
track_total_hits: true,
|
||||
sort: MetadataSortMethod,
|
||||
},
|
||||
from: pagingProperties.pageIndex * pagingProperties.pageSize,
|
||||
size: pagingProperties.pageSize,
|
||||
from: queryBuilderOptions.page * queryBuilderOptions.pageSize,
|
||||
size: queryBuilderOptions.pageSize,
|
||||
index: metadataCurrentIndexPattern,
|
||||
};
|
||||
}
|
||||
|
@ -96,8 +96,7 @@ export async function getPagingProperties(
|
|||
}
|
||||
|
||||
function buildQueryBody(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
request: KibanaRequest<any, any, any>,
|
||||
kuery: string = '',
|
||||
unerolledAgentIds: string[] | undefined,
|
||||
statusAgentIds: string[] | undefined
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
@ -136,8 +135,8 @@ function buildQueryBody(
|
|||
},
|
||||
};
|
||||
|
||||
if (request?.body?.filters?.kql) {
|
||||
const kqlQuery = toElasticsearchQuery(fromKueryExpression(request.body.filters.kql));
|
||||
if (kuery) {
|
||||
const kqlQuery = toElasticsearchQuery(fromKueryExpression(kuery));
|
||||
const q = [];
|
||||
if (filterUnenrolledAgents || filterStatusAgents) {
|
||||
q.push(idFilter);
|
||||
|
@ -233,12 +232,17 @@ interface BuildUnitedIndexQueryResponse {
|
|||
size: number;
|
||||
index: string;
|
||||
}
|
||||
|
||||
export async function buildUnitedIndexQuery(
|
||||
{ page = 1, pageSize = 10, filters = {} }: GetHostMetadataListQuery,
|
||||
{
|
||||
page = 0,
|
||||
pageSize = 10,
|
||||
hostStatuses = [],
|
||||
kuery = '',
|
||||
}: TypeOf<typeof GetMetadataListRequestSchemaV2.query>,
|
||||
endpointPolicyIds: string[] = []
|
||||
): Promise<BuildUnitedIndexQueryResponse> {
|
||||
const statusesToFilter = filters?.host_status ?? [];
|
||||
const statusesKuery = buildStatusesKuery(statusesToFilter);
|
||||
const statusesKuery = buildStatusesKuery(hostStatuses);
|
||||
|
||||
const filterIgnoredAgents = {
|
||||
must_not: { terms: { 'agent.id': IGNORED_ELASTIC_AGENT_IDS } },
|
||||
|
@ -272,8 +276,8 @@ export async function buildUnitedIndexQuery(
|
|||
|
||||
let query: BuildUnitedIndexQueryResponse['body']['query'] = idFilter;
|
||||
|
||||
if (statusesKuery || filters?.kql) {
|
||||
const kqlQuery = toElasticsearchQuery(fromKueryExpression(filters.kql ?? ''));
|
||||
if (statusesKuery || kuery) {
|
||||
const kqlQuery = toElasticsearchQuery(fromKueryExpression(kuery ?? ''));
|
||||
const q = [];
|
||||
|
||||
if (filterIgnoredAgents || filterEndpointPolicyAgents) {
|
||||
|
@ -295,7 +299,7 @@ export async function buildUnitedIndexQuery(
|
|||
track_total_hits: true,
|
||||
sort: MetadataSortMethod,
|
||||
},
|
||||
from: (page - 1) * pageSize,
|
||||
from: page * pageSize,
|
||||
size: pageSize,
|
||||
index: METADATA_UNITED_INDEX,
|
||||
};
|
||||
|
|
|
@ -36,7 +36,7 @@ export function buildStatusesKuery(statusesToFilter: string[]): string | undefin
|
|||
export async function findAgentIdsByStatus(
|
||||
agentService: AgentService,
|
||||
esClient: ElasticsearchClient,
|
||||
statuses: string[],
|
||||
statuses: string[] = [],
|
||||
pageSize: number = 1000
|
||||
): Promise<string[]> {
|
||||
if (!statuses.length) {
|
||||
|
|
|
@ -117,7 +117,12 @@ describe('EndpointMetadataService', () => {
|
|||
it('should throw wrapped error if es error', async () => {
|
||||
const esMockResponse = elasticsearchServiceMock.createErrorTransportRequestPromise({});
|
||||
esClient.search.mockResolvedValue(esMockResponse);
|
||||
const metadataListResponse = metadataService.getHostMetadataList(esClient);
|
||||
const metadataListResponse = metadataService.getHostMetadataList(esClient, {
|
||||
page: 0,
|
||||
pageSize: 10,
|
||||
kuery: '',
|
||||
hostStatuses: [],
|
||||
});
|
||||
await expect(metadataListResponse).rejects.toThrow(EndpointError);
|
||||
});
|
||||
|
||||
|
@ -168,18 +173,16 @@ describe('EndpointMetadataService', () => {
|
|||
}
|
||||
);
|
||||
|
||||
const metadataListResponse = await metadataService.getHostMetadataList(esClient);
|
||||
const unitedIndexQuery = await buildUnitedIndexQuery(
|
||||
{ page: 1, pageSize: 10, filters: {} },
|
||||
packagePolicyIds
|
||||
const queryOptions = { page: 1, pageSize: 10, kuery: '', hostStatuses: [] };
|
||||
const metadataListResponse = await metadataService.getHostMetadataList(
|
||||
esClient,
|
||||
queryOptions
|
||||
);
|
||||
const unitedIndexQuery = await buildUnitedIndexQuery(queryOptions, packagePolicyIds);
|
||||
|
||||
expect(esClient.search).toBeCalledWith(unitedIndexQuery);
|
||||
expect(agentPolicyServiceMock.getByIds).toBeCalledWith(expect.anything(), agentPolicyIds);
|
||||
expect(metadataListResponse).toEqual({
|
||||
pageSize: 10,
|
||||
page: 1,
|
||||
total: 1,
|
||||
data: [
|
||||
{
|
||||
metadata: endpointMetadataDoc,
|
||||
|
@ -202,6 +205,7 @@ describe('EndpointMetadataService', () => {
|
|||
},
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,12 +12,14 @@ import {
|
|||
SavedObjectsServiceStart,
|
||||
} from 'kibana/server';
|
||||
|
||||
import { TypeOf } from '@kbn/config-schema';
|
||||
import { TransportResult } from '@elastic/elasticsearch';
|
||||
import { SearchTotalHits, SearchResponse } from '@elastic/elasticsearch/lib/api/types';
|
||||
import {
|
||||
HostInfo,
|
||||
HostMetadata,
|
||||
MaybeImmutable,
|
||||
MetadataListResponse,
|
||||
PolicyData,
|
||||
UnitedAgentMetadata,
|
||||
} from '../../../../common/endpoint/types';
|
||||
|
@ -52,10 +54,10 @@ import {
|
|||
} from '../../utils';
|
||||
import { EndpointError } from '../../errors';
|
||||
import { createInternalReadonlySoClient } from '../../utils/create_internal_readonly_so_client';
|
||||
import { GetHostMetadataListQuery } from '../../types';
|
||||
import { METADATA_UNITED_INDEX } from '../../../../common/endpoint/constants';
|
||||
import { getAllEndpointPackagePolicies } from '../../routes/metadata/support/endpoint_package_policies';
|
||||
import { getAgentStatus } from '../../../../../fleet/common/services/agent_status';
|
||||
import { GetMetadataListRequestSchemaV2 } from '../../routes/metadata';
|
||||
|
||||
type AgentPolicyWithPackagePolicies = Omit<AgentPolicy, 'package_policies'> & {
|
||||
package_policies: PackagePolicy[];
|
||||
|
@ -401,8 +403,8 @@ export class EndpointMetadataService {
|
|||
*/
|
||||
async getHostMetadataList(
|
||||
esClient: ElasticsearchClient,
|
||||
queryOptions: GetHostMetadataListQuery = {}
|
||||
): Promise<{ data: HostInfo[]; total: number; page: number; pageSize: number }> {
|
||||
queryOptions: TypeOf<typeof GetMetadataListRequestSchemaV2.query>
|
||||
): Promise<Pick<MetadataListResponse, 'data' | 'total'>> {
|
||||
const endpointPolicies = await getAllEndpointPackagePolicies(
|
||||
this.packagePolicyService,
|
||||
this.DANGEROUS_INTERNAL_SO_CLIENT
|
||||
|
@ -474,8 +476,6 @@ export class EndpointMetadataService {
|
|||
|
||||
return {
|
||||
data: hosts,
|
||||
pageSize: unitedIndexQuery.size,
|
||||
page: unitedIndexQuery.from + 1,
|
||||
total: (docsCount as unknown as SearchTotalHits).value,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,12 +7,10 @@
|
|||
|
||||
import { LoggerFactory } from 'kibana/server';
|
||||
|
||||
import { TypeOf } from '@kbn/config-schema';
|
||||
import { ConfigType } from '../config';
|
||||
import { EndpointAppContextService } from './endpoint_app_context_services';
|
||||
import { HostMetadata } from '../../common/endpoint/types';
|
||||
import { ExperimentalFeatures } from '../../common/experimental_features';
|
||||
import { endpointFilters } from './routes/metadata';
|
||||
|
||||
/**
|
||||
* The context for Endpoint apps.
|
||||
|
@ -37,11 +35,3 @@ export interface HostQueryResult {
|
|||
resultLength: number;
|
||||
result: HostMetadata | undefined;
|
||||
}
|
||||
|
||||
// FIXME: when new Host Metadata list API is created (and existing one deprecated - 8.0?), move this type out of here and created it from Schema
|
||||
export interface GetHostMetadataListQuery {
|
||||
/* page number 1 based - not an index */
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
filters?: Partial<TypeOf<typeof endpointFilters>>;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue