[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(
'/internal/ecs_data_quality_dashboard/results',
'/internal/ecs_data_quality_dashboard/results/indices_latest/auditbeat-*',
expect.objectContaining({
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_INDICES_LATEST_ROUTE =
'/internal/ecs_data_quality_dashboard/results/indices_latest/{pattern}';
export interface StorageResult {
batchId: string;
@ -565,11 +567,11 @@ export async function getStorageResults({
abortController: AbortController;
}): Promise<StorageResult[]> {
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',
signal: abortController.signal,
version: INTERNAL_API_VERSION,
query: { pattern },
});
return results;
} 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_ILM_EXPLAIN = `${BASE_PATH}/ilm_explain/{pattern}`;
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';

View file

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

View file

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

View file

@ -8,7 +8,7 @@
import type { IRouter, Logger } from '@kbn/core/server';
import { postResultsRoute } from './post_results';
import { getResultsRoute } from './get_results';
import { getResultsIndicesLatestRoute } from './get_results_indices_latest';
import type { DataQualityDashboardRequestHandlerContext } from '../../types';
export const resultsRoutes = (
@ -16,5 +16,5 @@ export const resultsRoutes = (
logger: 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 GetResultQuery = t.type({ pattern: t.string });
export type GetResultQuery = t.TypeOf<typeof GetResultQuery>;
export const GetResultParams = t.type({ pattern: t.string });
export type GetResultParams = t.TypeOf<typeof GetResultParams>;