[Security Solution][DQD][API] update results API to use path params in place of query params (#183696)

- Changed the `RESULTS_API_ROUTE` to `RESULTS_INDICES_LATEST_ROUTE` with
path parameter `{pattern}`.
- Updated `getStorageResults` function to use the new route.
- Modified tests to reflect the new route and parameter usage.
- Updated server route validation to use path parameters instead of
query parameters.

closes #182868

**This is an internal route api change, so no breaking changes**

**Before**

![image](248e07e0-2a10-4658-8541-24330e2dc2ad)

**After:**

![image](d0469b33-d240-4de0-9a39-4ab510aa342b)
This commit is contained in:
Karen Grigoryan 2024-05-17 19:06:08 +02:00 committed by GitHub
parent 60cf299af8
commit 3c4b33b989
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 34 additions and 28 deletions

View file

@ -1483,10 +1483,9 @@ describe('helpers', () => {
}); });
expect(fetch).toHaveBeenCalledWith( expect(fetch).toHaveBeenCalledWith(
'/internal/ecs_data_quality_dashboard/results', '/internal/ecs_data_quality_dashboard/results/indices_latest/auditbeat-*',
expect.objectContaining({ expect.objectContaining({
method: 'GET', method: 'GET',
query: { pattern: 'auditbeat-*' },
}) })
); );
}); });

View file

@ -457,6 +457,8 @@ export const getErrorSummaries = (
}; };
export const RESULTS_API_ROUTE = '/internal/ecs_data_quality_dashboard/results'; export const RESULTS_API_ROUTE = '/internal/ecs_data_quality_dashboard/results';
export const RESULTS_INDICES_LATEST_ROUTE =
'/internal/ecs_data_quality_dashboard/results/indices_latest/{pattern}';
export interface StorageResult { export interface StorageResult {
batchId: string; batchId: string;
@ -565,11 +567,11 @@ export async function getStorageResults({
abortController: AbortController; abortController: AbortController;
}): Promise<StorageResult[]> { }): Promise<StorageResult[]> {
try { try {
const results = await httpFetch<StorageResult[]>(RESULTS_API_ROUTE, { const route = RESULTS_INDICES_LATEST_ROUTE.replace('{pattern}', pattern);
const results = await httpFetch<StorageResult[]>(route, {
method: 'GET', method: 'GET',
signal: abortController.signal, signal: abortController.signal,
version: INTERNAL_API_VERSION, version: INTERNAL_API_VERSION,
query: { pattern },
}); });
return results; return results;
} catch (err) { } catch (err) {

View file

@ -14,4 +14,5 @@ export const GET_INDEX_MAPPINGS = `${BASE_PATH}/mappings/{pattern}`;
export const GET_UNALLOWED_FIELD_VALUES = `${BASE_PATH}/unallowed_field_values`; export const GET_UNALLOWED_FIELD_VALUES = `${BASE_PATH}/unallowed_field_values`;
export const GET_ILM_EXPLAIN = `${BASE_PATH}/ilm_explain/{pattern}`; export const GET_ILM_EXPLAIN = `${BASE_PATH}/ilm_explain/{pattern}`;
export const RESULTS_ROUTE_PATH = `${BASE_PATH}/results`; export const RESULTS_ROUTE_PATH = `${BASE_PATH}/results`;
export const RESULTS_INDICES_LATEST_ROUTE_PATH = `${BASE_PATH}/results/indices_latest/{pattern}`;
export const INTERNAL_API_VERSION = '1'; export const INTERNAL_API_VERSION = '1';

View file

@ -4,13 +4,13 @@
* 2.0; you may not use this file except in compliance with the Elastic License * 2.0; you may not use this file except in compliance with the Elastic License
* 2.0. * 2.0.
*/ */
import { RESULTS_ROUTE_PATH } from '../../../common/constants'; import { RESULTS_INDICES_LATEST_ROUTE_PATH } from '../../../common/constants';
import { serverMock } from '../../__mocks__/server'; import { serverMock } from '../../__mocks__/server';
import { requestMock } from '../../__mocks__/request'; import { requestMock } from '../../__mocks__/request';
import { requestContextMock } from '../../__mocks__/request_context'; import { requestContextMock } from '../../__mocks__/request_context';
import type { LatestAggResponseBucket } from './get_results'; import type { LatestAggResponseBucket } from './get_results_indices_latest';
import { getResultsRoute, getQuery } from './get_results'; import { getResultsIndicesLatestRoute, getQuery } from './get_results_indices_latest';
import { loggerMock, type MockedLogger } from '@kbn/logging-mocks'; import { loggerMock, type MockedLogger } from '@kbn/logging-mocks';
import { resultDocument } from './results.mock'; import { resultDocument } from './results.mock';
import type { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; import type { SearchResponse } from '@elastic/elasticsearch/lib/api/types';
@ -41,7 +41,7 @@ jest.mock('./privileges', () => ({
mockCheckIndicesPrivileges(params), mockCheckIndicesPrivileges(params),
})); }));
describe('getResultsRoute route', () => { describe('getResultsIndicesLatestRoute route', () => {
describe('querying', () => { describe('querying', () => {
let server: ReturnType<typeof serverMock.create>; let server: ReturnType<typeof serverMock.create>;
let { context } = requestContextMock.createTools(); let { context } = requestContextMock.createTools();
@ -49,8 +49,8 @@ describe('getResultsRoute route', () => {
const req = requestMock.create({ const req = requestMock.create({
method: 'get', method: 'get',
path: RESULTS_ROUTE_PATH, path: RESULTS_INDICES_LATEST_ROUTE_PATH,
query: { pattern: 'logs-*' }, params: { pattern: 'logs-*' },
}); });
beforeEach(() => { beforeEach(() => {
@ -65,7 +65,7 @@ describe('getResultsRoute route', () => {
[resultDocument.indexName]: {}, [resultDocument.indexName]: {},
}); });
getResultsRoute(server.router, logger); getResultsIndicesLatestRoute(server.router, logger);
}); });
it('gets result', async () => { it('gets result', async () => {
@ -114,8 +114,8 @@ describe('getResultsRoute route', () => {
const req = requestMock.create({ const req = requestMock.create({
method: 'get', method: 'get',
path: RESULTS_ROUTE_PATH, path: RESULTS_INDICES_LATEST_ROUTE_PATH,
query: { pattern: 'logs-*' }, params: { pattern: 'logs-*' },
}); });
beforeEach(() => { beforeEach(() => {
@ -132,7 +132,7 @@ describe('getResultsRoute route', () => {
[resultDocument.indexName]: {}, [resultDocument.indexName]: {},
}); });
getResultsRoute(server.router, logger); getResultsIndicesLatestRoute(server.router, logger);
}); });
it('should authorize indices from pattern', async () => { it('should authorize indices from pattern', async () => {
@ -225,14 +225,14 @@ describe('getResultsRoute route', () => {
beforeEach(() => { beforeEach(() => {
server = serverMock.create(); server = serverMock.create();
logger = loggerMock.create(); logger = loggerMock.create();
getResultsRoute(server.router, logger); getResultsIndicesLatestRoute(server.router, logger);
}); });
test('disallows invalid query param', () => { test('disallows invalid path param', () => {
const req = requestMock.create({ const req = requestMock.create({
method: 'get', method: 'get',
path: RESULTS_ROUTE_PATH, path: RESULTS_INDICES_LATEST_ROUTE_PATH,
query: {}, params: {},
}); });
const result = server.validate(req); const result = server.validate(req);

View file

@ -7,10 +7,10 @@
import type { IRouter, Logger } from '@kbn/core/server'; import type { IRouter, Logger } from '@kbn/core/server';
import { RESULTS_ROUTE_PATH, INTERNAL_API_VERSION } from '../../../common/constants'; import { INTERNAL_API_VERSION, RESULTS_INDICES_LATEST_ROUTE_PATH } from '../../../common/constants';
import { buildResponse } from '../../lib/build_response'; import { buildResponse } from '../../lib/build_response';
import { buildRouteValidation } from '../../schemas/common'; import { buildRouteValidation } from '../../schemas/common';
import { GetResultQuery } from '../../schemas/result'; import { GetResultParams } from '../../schemas/result';
import type { ResultDocument } from '../../schemas/result'; import type { ResultDocument } from '../../schemas/result';
import { API_DEFAULT_ERROR_MESSAGE } from '../../translations'; import { API_DEFAULT_ERROR_MESSAGE } from '../../translations';
import type { DataQualityDashboardRequestHandlerContext } from '../../types'; import type { DataQualityDashboardRequestHandlerContext } from '../../types';
@ -33,20 +33,24 @@ export interface LatestAggResponseBucket {
latest_doc: { hits: { hits: Array<{ _source: ResultDocument }> } }; latest_doc: { hits: { hits: Array<{ _source: ResultDocument }> } };
} }
export const getResultsRoute = ( export const getResultsIndicesLatestRoute = (
router: IRouter<DataQualityDashboardRequestHandlerContext>, router: IRouter<DataQualityDashboardRequestHandlerContext>,
logger: Logger logger: Logger
) => { ) => {
router.versioned router.versioned
.get({ .get({
path: RESULTS_ROUTE_PATH, path: RESULTS_INDICES_LATEST_ROUTE_PATH,
access: 'internal', access: 'internal',
options: { tags: ['access:securitySolution'] }, options: { tags: ['access:securitySolution'] },
}) })
.addVersion( .addVersion(
{ {
version: INTERNAL_API_VERSION, version: INTERNAL_API_VERSION,
validate: { request: { query: buildRouteValidation(GetResultQuery) } }, validate: {
request: {
params: buildRouteValidation(GetResultParams),
},
},
}, },
async (context, request, response) => { async (context, request, response) => {
const services = await context.resolve(['core', 'dataQualityDashboard']); const services = await context.resolve(['core', 'dataQualityDashboard']);
@ -65,7 +69,7 @@ export const getResultsRoute = (
try { try {
const { client } = services.core.elasticsearch; const { client } = services.core.elasticsearch;
const { pattern } = request.query; const { pattern } = request.params;
// Discover all indices for the pattern using internal user // Discover all indices for the pattern using internal user
const indicesResponse = await client.asInternalUser.indices.get({ const indicesResponse = await client.asInternalUser.indices.get({

View file

@ -8,7 +8,7 @@
import type { IRouter, Logger } from '@kbn/core/server'; import type { IRouter, Logger } from '@kbn/core/server';
import { postResultsRoute } from './post_results'; import { postResultsRoute } from './post_results';
import { getResultsRoute } from './get_results'; import { getResultsIndicesLatestRoute } from './get_results_indices_latest';
import type { DataQualityDashboardRequestHandlerContext } from '../../types'; import type { DataQualityDashboardRequestHandlerContext } from '../../types';
export const resultsRoutes = ( export const resultsRoutes = (
@ -16,5 +16,5 @@ export const resultsRoutes = (
logger: Logger logger: Logger
) => { ) => {
postResultsRoute(router, logger); postResultsRoute(router, logger);
getResultsRoute(router, logger); getResultsIndicesLatestRoute(router, logger);
}; };

View file

@ -39,5 +39,5 @@ export type ResultDocument = t.TypeOf<typeof ResultDocument>;
export const PostResultBody = ResultDocument; export const PostResultBody = ResultDocument;
export const GetResultQuery = t.type({ pattern: t.string }); export const GetResultParams = t.type({ pattern: t.string });
export type GetResultQuery = t.TypeOf<typeof GetResultQuery>; export type GetResultParams = t.TypeOf<typeof GetResultParams>;