mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[SecuritySolution][DataQualityDashboard] Migrate data quality dashboard APIs to versioned router (#169037)
## Summary https://github.com/elastic/kibana/issues/168334 https://github.com/elastic/kibana/issues/166271 Before: <img width="2560" alt="Screenshot 2023-10-16 at 21 45 02" src="2945179a
-fb5b-4946-8b51-4769d20ad30f"> <img width="2558" alt="Screenshot 2023-10-16 at 21 45 09" src="254e00dd
-bee9-4325-b418-e7ee481dea5d"> After: <img width="2546" alt="Screenshot 2023-10-16 at 22 41 31" src="166a99a0
-6f28-453a-a785-1197508170b5"> --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
0554daacb8
commit
843aaf2f04
21 changed files with 285 additions and 167 deletions
|
@ -27,6 +27,7 @@ import type {
|
|||
} from './types';
|
||||
|
||||
const EMPTY_INDEX_NAMES: string[] = [];
|
||||
export const INTERNAL_API_VERSION = '1';
|
||||
|
||||
export const getIndexNames = ({
|
||||
ilmExplain,
|
||||
|
@ -282,7 +283,7 @@ export const getSizeInBytes = ({
|
|||
}: {
|
||||
indexName: string;
|
||||
stats: Record<string, IndicesStatsIndicesStats> | null;
|
||||
}): number => (stats && stats[indexName]?.primaries?.store?.size_in_bytes) ?? 0;
|
||||
}): number => (stats && stats[indexName]?.primaries?.store?.total_data_set_size_in_bytes) ?? 0;
|
||||
|
||||
export const getTotalDocsCount = ({
|
||||
indexNames,
|
||||
|
|
|
@ -53,6 +53,7 @@ export const auditbeatNoResults: PatternRollup = {
|
|||
primaries: {
|
||||
store: {
|
||||
size_in_bytes: 18791790,
|
||||
total_data_set_size_in_bytes: 18791790,
|
||||
reserved_in_bytes: 0,
|
||||
},
|
||||
},
|
||||
|
@ -70,6 +71,7 @@ export const auditbeatNoResults: PatternRollup = {
|
|||
primaries: {
|
||||
store: {
|
||||
size_in_bytes: 247,
|
||||
total_data_set_size_in_bytes: 247,
|
||||
reserved_in_bytes: 0,
|
||||
},
|
||||
},
|
||||
|
@ -87,6 +89,7 @@ export const auditbeatNoResults: PatternRollup = {
|
|||
primaries: {
|
||||
store: {
|
||||
size_in_bytes: 28409,
|
||||
total_data_set_size_in_bytes: 28409,
|
||||
reserved_in_bytes: 0,
|
||||
},
|
||||
},
|
||||
|
@ -182,6 +185,7 @@ export const auditbeatWithAllResults: PatternRollup = {
|
|||
primaries: {
|
||||
store: {
|
||||
size_in_bytes: 18791790,
|
||||
total_data_set_size_in_bytes: 18791790,
|
||||
reserved_in_bytes: 0,
|
||||
},
|
||||
},
|
||||
|
@ -199,6 +203,7 @@ export const auditbeatWithAllResults: PatternRollup = {
|
|||
primaries: {
|
||||
store: {
|
||||
size_in_bytes: 247,
|
||||
total_data_set_size_in_bytes: 247,
|
||||
reserved_in_bytes: 0,
|
||||
},
|
||||
},
|
||||
|
@ -216,6 +221,7 @@ export const auditbeatWithAllResults: PatternRollup = {
|
|||
primaries: {
|
||||
store: {
|
||||
size_in_bytes: 28409,
|
||||
total_data_set_size_in_bytes: 28409,
|
||||
reserved_in_bytes: 0,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -51,6 +51,7 @@ export const packetbeatNoResults: PatternRollup = {
|
|||
primaries: {
|
||||
store: {
|
||||
size_in_bytes: 512194751,
|
||||
total_data_set_size_in_bytes: 512194751,
|
||||
reserved_in_bytes: 0,
|
||||
},
|
||||
},
|
||||
|
@ -68,6 +69,7 @@ export const packetbeatNoResults: PatternRollup = {
|
|||
primaries: {
|
||||
store: {
|
||||
size_in_bytes: 584326147,
|
||||
total_data_set_size_in_bytes: 584326147,
|
||||
reserved_in_bytes: 0,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -9,6 +9,7 @@ import type { IlmExplainLifecycleLifecycleExplain } from '@elastic/elasticsearch
|
|||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { useDataQualityContext } from '../data_quality_panel/data_quality_context';
|
||||
import { INTERNAL_API_VERSION } from '../helpers';
|
||||
import * as i18n from '../translations';
|
||||
|
||||
const ILM_EXPLAIN_ENDPOINT = '/internal/ecs_data_quality_dashboard/ilm_explain';
|
||||
|
@ -43,6 +44,7 @@ export const useIlmExplain = (pattern: string): UseIlmExplain => {
|
|||
{
|
||||
method: 'GET',
|
||||
signal: abortController.signal,
|
||||
version: INTERNAL_API_VERSION,
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import type { HttpHandler } from '@kbn/core-http-browser';
|
|||
import type { IndicesGetMappingIndexMappingRecord } from '@elastic/elasticsearch/lib/api/types';
|
||||
|
||||
import * as i18n from '../translations';
|
||||
import { INTERNAL_API_VERSION } from '../helpers';
|
||||
|
||||
export const MAPPINGS_API_ROUTE = '/internal/ecs_data_quality_dashboard/mappings';
|
||||
|
||||
|
@ -29,6 +30,7 @@ export async function fetchMappings({
|
|||
{
|
||||
method: 'GET',
|
||||
signal: abortController.signal,
|
||||
version: INTERNAL_API_VERSION,
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
|
|
|
@ -11,6 +11,7 @@ import { HttpFetchQuery } from '@kbn/core/public';
|
|||
|
||||
import { useDataQualityContext } from '../data_quality_panel/data_quality_context';
|
||||
import * as i18n from '../translations';
|
||||
import { INTERNAL_API_VERSION } from '../helpers';
|
||||
|
||||
const STATS_ENDPOINT = '/internal/ecs_data_quality_dashboard/stats';
|
||||
|
||||
|
@ -53,6 +54,7 @@ export const useStats = ({
|
|||
const response = await httpFetch<Record<string, IndicesStatsIndicesStats>>(
|
||||
`${STATS_ENDPOINT}/${encodedIndexName}`,
|
||||
{
|
||||
version: INTERNAL_API_VERSION,
|
||||
method: 'GET',
|
||||
signal: abortController.signal,
|
||||
query,
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
} from './helpers';
|
||||
import { mockUnallowedValuesResponse } from '../mock/unallowed_values/mock_unallowed_values';
|
||||
import { UnallowedValueRequestItem, UnallowedValueSearchResult } from '../types';
|
||||
import { INTERNAL_API_VERSION } from '../helpers';
|
||||
|
||||
describe('helpers', () => {
|
||||
let originalFetch: typeof global['fetch'];
|
||||
|
@ -406,6 +407,7 @@ describe('helpers', () => {
|
|||
headers: { 'Content-Type': 'application/json' },
|
||||
method: 'POST',
|
||||
signal: abortController.signal,
|
||||
version: INTERNAL_API_VERSION,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import type { HttpHandler } from '@kbn/core-http-browser';
|
||||
import { INTERNAL_API_VERSION } from '../helpers';
|
||||
import * as i18n from '../translations';
|
||||
import type {
|
||||
Bucket,
|
||||
|
@ -81,6 +82,7 @@ export async function fetchUnallowedValues({
|
|||
headers: { 'Content-Type': 'application/json' },
|
||||
method: 'POST',
|
||||
signal: abortController.signal,
|
||||
version: INTERNAL_API_VERSION,
|
||||
});
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
|
|
|
@ -13,3 +13,4 @@ export const GET_INDEX_STATS = `${BASE_PATH}/stats/{pattern}`;
|
|||
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 INTERNAL_API_VERSION = '1';
|
||||
|
|
|
@ -5,35 +5,73 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import { httpServiceMock } from '@kbn/core/server/mocks';
|
||||
import type { RequestHandler, RouteConfig, KibanaRequest } from '@kbn/core/server';
|
||||
import type { IRouter, RouteMethod, RequestHandler, KibanaRequest } from '@kbn/core/server';
|
||||
import type { RequestHandlerContext } from '@kbn/core-http-request-handler-context-server';
|
||||
import type { RouterMock } from '@kbn/core-http-router-server-mocks';
|
||||
import type { AddVersionOpts, VersionedRouteConfig } from '@kbn/core-http-server';
|
||||
|
||||
import { requestMock } from './request';
|
||||
import { responseMock as responseFactoryMock } from './response';
|
||||
import { requestContextMock } from './request_context';
|
||||
import { responseAdapter } from './test_adapters';
|
||||
import { INTERNAL_API_VERSION } from '../../common/constants';
|
||||
|
||||
interface Route {
|
||||
config: RouteConfig<unknown, unknown, unknown, 'get' | 'post' | 'delete' | 'patch' | 'put'>;
|
||||
config: AddVersionOpts<unknown, unknown, unknown>;
|
||||
handler: RequestHandler;
|
||||
}
|
||||
|
||||
const getRoute = (routerMock: MockServer['router']): Route => {
|
||||
const routeCalls = [
|
||||
...routerMock.get.mock.calls,
|
||||
...routerMock.post.mock.calls,
|
||||
...routerMock.put.mock.calls,
|
||||
...routerMock.patch.mock.calls,
|
||||
...routerMock.delete.mock.calls,
|
||||
interface RegisteredVersionedRoute {
|
||||
routeConfig: VersionedRouteConfig<RouterMethod>;
|
||||
versionConfig: AddVersionOpts<unknown, unknown, unknown>;
|
||||
routeHandler: RequestHandler;
|
||||
}
|
||||
|
||||
type RouterMethod = Extract<keyof IRouter, RouteMethod>;
|
||||
|
||||
export const getRegisteredVersionedRouteMock = (
|
||||
routerMock: RouterMock,
|
||||
method: RouterMethod,
|
||||
path: string,
|
||||
version: string
|
||||
): RegisteredVersionedRoute => {
|
||||
const route = routerMock.versioned.getRoute(method, path);
|
||||
const routeVersion = route.versions[version];
|
||||
|
||||
if (!routeVersion) {
|
||||
throw new Error(`Handler for [${method}][${path}] with version [${version}] no found!`);
|
||||
}
|
||||
|
||||
return {
|
||||
routeConfig: route.config,
|
||||
versionConfig: routeVersion.config,
|
||||
routeHandler: routeVersion.handler,
|
||||
};
|
||||
};
|
||||
|
||||
const getRoute = (routerMock: MockServer['router'], request: KibanaRequest): Route => {
|
||||
const versionedRouteCalls = [
|
||||
...routerMock.versioned.get.mock.calls,
|
||||
...routerMock.versioned.post.mock.calls,
|
||||
...routerMock.versioned.put.mock.calls,
|
||||
...routerMock.versioned.patch.mock.calls,
|
||||
...routerMock.versioned.delete.mock.calls,
|
||||
];
|
||||
|
||||
const [route] = routeCalls;
|
||||
if (!route) {
|
||||
const [versionedRoute] = versionedRouteCalls;
|
||||
|
||||
if (!versionedRoute) {
|
||||
throw new Error('No route registered!');
|
||||
}
|
||||
|
||||
const [config, handler] = route;
|
||||
return { config, handler };
|
||||
const { routeHandler, versionConfig } = getRegisteredVersionedRouteMock(
|
||||
routerMock,
|
||||
request.route.method,
|
||||
request.route.path,
|
||||
INTERNAL_API_VERSION
|
||||
);
|
||||
|
||||
return { config: versionConfig, handler: routeHandler };
|
||||
};
|
||||
|
||||
const buildResultMock = () => ({ ok: jest.fn((x) => x), badRequest: jest.fn((x) => x) });
|
||||
|
@ -53,17 +91,19 @@ class MockServer {
|
|||
|
||||
public async inject(request: KibanaRequest, context: RequestHandlerContext = this.contextMock) {
|
||||
const validatedRequest = this.validateRequest(request);
|
||||
|
||||
const [rejection] = this.resultMock.badRequest.mock.calls;
|
||||
if (rejection) {
|
||||
throw new Error(`Request was rejected with message: '${rejection}'`);
|
||||
}
|
||||
|
||||
await this.getRoute().handler(context, validatedRequest, this.responseMock);
|
||||
await this.getRoute(validatedRequest).handler(context, validatedRequest, this.responseMock);
|
||||
|
||||
return responseAdapter(this.responseMock);
|
||||
}
|
||||
|
||||
private getRoute(): Route {
|
||||
return getRoute(this.router);
|
||||
private getRoute(request: KibanaRequest): Route {
|
||||
return getRoute(this.router, request);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
@ -72,7 +112,8 @@ class MockServer {
|
|||
}
|
||||
|
||||
private validateRequest(request: KibanaRequest): KibanaRequest {
|
||||
const validations = this.getRoute().config.validate;
|
||||
const config = this.getRoute(request).config;
|
||||
const validations = config.validate && config.validate?.request;
|
||||
if (!validations) {
|
||||
return request;
|
||||
}
|
||||
|
|
|
@ -29,10 +29,10 @@ export class EcsDataQualityDashboardPlugin
|
|||
const router = core.http.createRouter(); // this would be deleted when plugin is removed
|
||||
|
||||
// Register server side APIs
|
||||
getIndexMappingsRoute(router);
|
||||
getIndexStatsRoute(router);
|
||||
getUnallowedFieldValuesRoute(router);
|
||||
getILMExplainRoute(router);
|
||||
getIndexMappingsRoute(router, this.logger);
|
||||
getIndexStatsRoute(router, this.logger);
|
||||
getUnallowedFieldValuesRoute(router, this.logger);
|
||||
getILMExplainRoute(router, this.logger);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import { serverMock } from '../__mocks__/server';
|
|||
import { requestMock } from '../__mocks__/request';
|
||||
import { requestContextMock } from '../__mocks__/request_context';
|
||||
import { getILMExplainRoute } from './get_ilm_explain';
|
||||
import { loggerMock, MockedLogger } from '@kbn/logging-mocks';
|
||||
|
||||
jest.mock('../lib', () => ({
|
||||
fetchILMExplain: jest.fn(),
|
||||
|
@ -20,6 +21,8 @@ jest.mock('../lib', () => ({
|
|||
describe('getILMExplainRoute route', () => {
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
let { context } = requestContextMock.createTools();
|
||||
let logger: MockedLogger;
|
||||
|
||||
const req = requestMock.create({
|
||||
method: 'get',
|
||||
path: GET_ILM_EXPLAIN,
|
||||
|
@ -32,9 +35,10 @@ describe('getILMExplainRoute route', () => {
|
|||
jest.clearAllMocks();
|
||||
|
||||
server = serverMock.create();
|
||||
logger = loggerMock.create();
|
||||
({ context } = requestContextMock.createTools());
|
||||
|
||||
getILMExplainRoute(server.router);
|
||||
getILMExplainRoute(server.router, logger);
|
||||
});
|
||||
|
||||
test('Returns index ilm information', async () => {
|
||||
|
@ -91,11 +95,13 @@ describe('getILMExplainRoute route', () => {
|
|||
|
||||
describe('request validation', () => {
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
let logger: MockedLogger;
|
||||
|
||||
beforeEach(() => {
|
||||
server = serverMock.create();
|
||||
logger = loggerMock.create();
|
||||
|
||||
getILMExplainRoute(server.router);
|
||||
getILMExplainRoute(server.router, logger);
|
||||
});
|
||||
|
||||
test('disallows invalid pattern', () => {
|
||||
|
|
|
@ -5,41 +5,51 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { IRouter } from '@kbn/core/server';
|
||||
import { IRouter, Logger } from '@kbn/core/server';
|
||||
import { transformError } from '@kbn/securitysolution-es-utils';
|
||||
|
||||
import { GET_ILM_EXPLAIN } from '../../common/constants';
|
||||
import { GET_ILM_EXPLAIN, INTERNAL_API_VERSION } from '../../common/constants';
|
||||
import { fetchILMExplain } from '../lib';
|
||||
import { buildResponse } from '../lib/build_response';
|
||||
import { buildRouteValidation } from '../schemas/common';
|
||||
import { GetILMExplainParams } from '../schemas/get_ilm_explain';
|
||||
|
||||
export const getILMExplainRoute = (router: IRouter) => {
|
||||
router.get(
|
||||
{
|
||||
export const getILMExplainRoute = (router: IRouter, logger: Logger) => {
|
||||
router.versioned
|
||||
.get({
|
||||
path: GET_ILM_EXPLAIN,
|
||||
validate: { params: buildRouteValidation(GetILMExplainParams) },
|
||||
},
|
||||
async (context, request, response) => {
|
||||
const resp = buildResponse(response);
|
||||
access: 'internal',
|
||||
})
|
||||
.addVersion(
|
||||
{
|
||||
version: INTERNAL_API_VERSION,
|
||||
validate: {
|
||||
request: {
|
||||
params: buildRouteValidation(GetILMExplainParams),
|
||||
},
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
const resp = buildResponse(response);
|
||||
|
||||
try {
|
||||
const { client } = (await context.core).elasticsearch;
|
||||
const decodedIndexName = decodeURIComponent(request.params.pattern);
|
||||
try {
|
||||
const { client } = (await context.core).elasticsearch;
|
||||
const decodedIndexName = decodeURIComponent(request.params.pattern);
|
||||
|
||||
const ilmExplain = await fetchILMExplain(client, decodedIndexName);
|
||||
const ilmExplain = await fetchILMExplain(client, decodedIndexName);
|
||||
|
||||
return response.ok({
|
||||
body: ilmExplain.indices,
|
||||
});
|
||||
} catch (err) {
|
||||
const error = transformError(err);
|
||||
return response.ok({
|
||||
body: ilmExplain.indices,
|
||||
});
|
||||
} catch (err) {
|
||||
const error = transformError(err);
|
||||
|
||||
return resp.error({
|
||||
body: error.message,
|
||||
statusCode: error.statusCode,
|
||||
});
|
||||
logger.error(error.message);
|
||||
return resp.error({
|
||||
body: error.message,
|
||||
statusCode: error.statusCode,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
};
|
||||
|
|
|
@ -12,6 +12,7 @@ import { serverMock } from '../__mocks__/server';
|
|||
import { requestMock } from '../__mocks__/request';
|
||||
import { requestContextMock } from '../__mocks__/request_context';
|
||||
import { getIndexMappingsRoute } from './get_index_mappings';
|
||||
import { loggerMock, MockedLogger } from '@kbn/logging-mocks';
|
||||
|
||||
jest.mock('../lib', () => ({
|
||||
fetchMappings: jest.fn(),
|
||||
|
@ -20,6 +21,8 @@ jest.mock('../lib', () => ({
|
|||
describe('getIndexMappingsRoute route', () => {
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
let { context } = requestContextMock.createTools();
|
||||
let logger: MockedLogger;
|
||||
|
||||
const req = requestMock.create({
|
||||
method: 'get',
|
||||
path: GET_INDEX_MAPPINGS,
|
||||
|
@ -32,9 +35,11 @@ describe('getIndexMappingsRoute route', () => {
|
|||
jest.clearAllMocks();
|
||||
|
||||
server = serverMock.create();
|
||||
logger = loggerMock.create();
|
||||
|
||||
({ context } = requestContextMock.createTools());
|
||||
|
||||
getIndexMappingsRoute(server.router);
|
||||
getIndexMappingsRoute(server.router, logger);
|
||||
});
|
||||
|
||||
test('Returns index stats', async () => {
|
||||
|
@ -58,11 +63,11 @@ describe('getIndexMappingsRoute route', () => {
|
|||
|
||||
describe('request validation', () => {
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
|
||||
let logger: MockedLogger;
|
||||
beforeEach(() => {
|
||||
server = serverMock.create();
|
||||
|
||||
getIndexMappingsRoute(server.router);
|
||||
logger = loggerMock.create();
|
||||
getIndexMappingsRoute(server.router, logger);
|
||||
});
|
||||
|
||||
test('disallows invalid pattern', () => {
|
||||
|
|
|
@ -5,41 +5,47 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { IRouter } from '@kbn/core/server';
|
||||
import { IRouter, Logger } from '@kbn/core/server';
|
||||
import { transformError } from '@kbn/securitysolution-es-utils';
|
||||
|
||||
import { fetchMappings } from '../lib';
|
||||
import { buildResponse } from '../lib/build_response';
|
||||
import { GET_INDEX_MAPPINGS } from '../../common/constants';
|
||||
import { GET_INDEX_MAPPINGS, INTERNAL_API_VERSION } from '../../common/constants';
|
||||
import { GetIndexMappingsParams } from '../schemas/get_index_mappings';
|
||||
import { buildRouteValidation } from '../schemas/common';
|
||||
|
||||
export const getIndexMappingsRoute = (router: IRouter) => {
|
||||
router.get(
|
||||
{
|
||||
export const getIndexMappingsRoute = (router: IRouter, logger: Logger) => {
|
||||
router.versioned
|
||||
.get({
|
||||
path: GET_INDEX_MAPPINGS,
|
||||
validate: { params: buildRouteValidation(GetIndexMappingsParams) },
|
||||
},
|
||||
async (context, request, response) => {
|
||||
const resp = buildResponse(response);
|
||||
access: 'internal',
|
||||
})
|
||||
.addVersion(
|
||||
{
|
||||
version: INTERNAL_API_VERSION,
|
||||
validate: { request: { params: buildRouteValidation(GetIndexMappingsParams) } },
|
||||
},
|
||||
async (context, request, response) => {
|
||||
const resp = buildResponse(response);
|
||||
|
||||
try {
|
||||
const { client } = (await context.core).elasticsearch;
|
||||
const decodedIndexName = decodeURIComponent(request.params.pattern);
|
||||
try {
|
||||
const { client } = (await context.core).elasticsearch;
|
||||
const decodedIndexName = decodeURIComponent(request.params.pattern);
|
||||
|
||||
const mappings = await fetchMappings(client, decodedIndexName);
|
||||
const mappings = await fetchMappings(client, decodedIndexName);
|
||||
|
||||
return response.ok({
|
||||
body: mappings,
|
||||
});
|
||||
} catch (err) {
|
||||
const error = transformError(err);
|
||||
return response.ok({
|
||||
body: mappings,
|
||||
});
|
||||
} catch (err) {
|
||||
const error = transformError(err);
|
||||
logger.error(error.message);
|
||||
|
||||
return resp.error({
|
||||
body: error.message,
|
||||
statusCode: error.statusCode,
|
||||
});
|
||||
return resp.error({
|
||||
body: error.message,
|
||||
statusCode: error.statusCode,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
};
|
||||
|
|
|
@ -12,6 +12,7 @@ import { serverMock } from '../__mocks__/server';
|
|||
import { requestMock } from '../__mocks__/request';
|
||||
import { requestContextMock } from '../__mocks__/request_context';
|
||||
import { getIndexStatsRoute } from './get_index_stats';
|
||||
import { loggerMock, MockedLogger } from '@kbn/logging-mocks';
|
||||
|
||||
jest.mock('../lib', () => ({
|
||||
fetchStats: jest.fn(),
|
||||
|
@ -21,6 +22,8 @@ jest.mock('../lib', () => ({
|
|||
describe('getIndexStatsRoute route', () => {
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
let { context } = requestContextMock.createTools();
|
||||
let logger: MockedLogger;
|
||||
|
||||
const req = requestMock.create({
|
||||
method: 'get',
|
||||
path: GET_INDEX_STATS,
|
||||
|
@ -38,9 +41,11 @@ describe('getIndexStatsRoute route', () => {
|
|||
jest.clearAllMocks();
|
||||
|
||||
server = serverMock.create();
|
||||
logger = loggerMock.create();
|
||||
|
||||
({ context } = requestContextMock.createTools());
|
||||
|
||||
getIndexStatsRoute(server.router);
|
||||
getIndexStatsRoute(server.router, logger);
|
||||
});
|
||||
|
||||
test('Returns index stats', async () => {
|
||||
|
@ -127,11 +132,13 @@ describe('getIndexStatsRoute route', () => {
|
|||
|
||||
describe('request validation', () => {
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
let logger: MockedLogger;
|
||||
|
||||
beforeEach(() => {
|
||||
server = serverMock.create();
|
||||
logger = loggerMock.create();
|
||||
|
||||
getIndexStatsRoute(server.router);
|
||||
getIndexStatsRoute(server.router, logger);
|
||||
});
|
||||
|
||||
test('disallows invalid pattern', () => {
|
||||
|
|
|
@ -5,87 +5,96 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { IRouter } from '@kbn/core/server';
|
||||
import { IRouter, Logger } from '@kbn/core/server';
|
||||
import { transformError } from '@kbn/securitysolution-es-utils';
|
||||
|
||||
import { IndicesStatsIndicesStats } from '@elastic/elasticsearch/lib/api/types';
|
||||
import { fetchStats, fetchAvailableIndices } from '../lib';
|
||||
import { buildResponse } from '../lib/build_response';
|
||||
import { GET_INDEX_STATS } from '../../common/constants';
|
||||
import { GET_INDEX_STATS, INTERNAL_API_VERSION } from '../../common/constants';
|
||||
import { buildRouteValidation } from '../schemas/common';
|
||||
import { GetIndexStatsParams, GetIndexStatsQuery } from '../schemas/get_index_stats';
|
||||
|
||||
export const getIndexStatsRoute = (router: IRouter) => {
|
||||
router.get(
|
||||
{
|
||||
export const getIndexStatsRoute = (router: IRouter, logger: Logger) => {
|
||||
router.versioned
|
||||
.get({
|
||||
path: GET_INDEX_STATS,
|
||||
validate: {
|
||||
params: buildRouteValidation(GetIndexStatsParams),
|
||||
query: buildRouteValidation(GetIndexStatsQuery),
|
||||
access: 'internal',
|
||||
})
|
||||
.addVersion(
|
||||
{
|
||||
version: INTERNAL_API_VERSION,
|
||||
validate: {
|
||||
request: {
|
||||
params: buildRouteValidation(GetIndexStatsParams),
|
||||
query: buildRouteValidation(GetIndexStatsQuery),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
const resp = buildResponse(response);
|
||||
async (context, request, response) => {
|
||||
const resp = buildResponse(response);
|
||||
|
||||
try {
|
||||
const { client } = (await context.core).elasticsearch;
|
||||
const esClient = client.asCurrentUser;
|
||||
try {
|
||||
const { client } = (await context.core).elasticsearch;
|
||||
const esClient = client.asCurrentUser;
|
||||
|
||||
const decodedIndexName = decodeURIComponent(request.params.pattern);
|
||||
const decodedIndexName = decodeURIComponent(request.params.pattern);
|
||||
|
||||
const stats = await fetchStats(client, decodedIndexName);
|
||||
const { isILMAvailable, startDate, endDate } = request.query;
|
||||
const stats = await fetchStats(client, decodedIndexName);
|
||||
const { isILMAvailable, startDate, endDate } = request.query;
|
||||
|
||||
if (isILMAvailable === true) {
|
||||
return response.ok({
|
||||
body: stats.indices,
|
||||
});
|
||||
}
|
||||
if (isILMAvailable === true) {
|
||||
return response.ok({
|
||||
body: stats.indices,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* If ILM is not available, we need to fetch the available indices with the given date range.
|
||||
* `fetchAvailableIndices` returns indices that have data in the given date range.
|
||||
*/
|
||||
if (startDate && endDate) {
|
||||
const decodedStartDate = decodeURIComponent(startDate);
|
||||
const decodedEndDate = decodeURIComponent(endDate);
|
||||
/**
|
||||
* If ILM is not available, we need to fetch the available indices with the given date range.
|
||||
* `fetchAvailableIndices` returns indices that have data in the given date range.
|
||||
*/
|
||||
if (startDate && endDate) {
|
||||
const decodedStartDate = decodeURIComponent(startDate);
|
||||
const decodedEndDate = decodeURIComponent(endDate);
|
||||
|
||||
const indices = await fetchAvailableIndices(esClient, {
|
||||
indexPattern: decodedIndexName,
|
||||
startDate: decodedStartDate,
|
||||
endDate: decodedEndDate,
|
||||
});
|
||||
const availableIndices = indices?.aggregations?.index?.buckets?.reduce(
|
||||
(acc: Record<string, IndicesStatsIndicesStats>, { key }: { key: string }) => {
|
||||
if (stats.indices?.[key]) {
|
||||
acc[key] = stats.indices?.[key];
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
const indices = await fetchAvailableIndices(esClient, {
|
||||
indexPattern: decodedIndexName,
|
||||
startDate: decodedStartDate,
|
||||
endDate: decodedEndDate,
|
||||
});
|
||||
const availableIndices = indices?.aggregations?.index?.buckets?.reduce(
|
||||
(acc: Record<string, IndicesStatsIndicesStats>, { key }: { key: string }) => {
|
||||
if (stats.indices?.[key]) {
|
||||
acc[key] = stats.indices?.[key];
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
return response.ok({
|
||||
body: availableIndices,
|
||||
});
|
||||
} else {
|
||||
return resp.error({
|
||||
body: i18n.translate(
|
||||
'xpack.ecsDataQualityDashboard.getIndexStats.dateRangeRequiredErrorMessage',
|
||||
{
|
||||
defaultMessage: 'startDate and endDate are required',
|
||||
}
|
||||
),
|
||||
statusCode: 400,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
const error = transformError(err);
|
||||
logger.error(error.message);
|
||||
|
||||
return response.ok({
|
||||
body: availableIndices,
|
||||
});
|
||||
} else {
|
||||
return resp.error({
|
||||
body: i18n.translate(
|
||||
'xpack.ecsDataQualityDashboard.getIndexStats.dateRangeRequiredErrorMessage',
|
||||
{
|
||||
defaultMessage: 'startDate and endDate are required',
|
||||
}
|
||||
),
|
||||
statusCode: 400,
|
||||
body: error.message,
|
||||
statusCode: error.statusCode,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
const error = transformError(err);
|
||||
return resp.error({
|
||||
body: error.message,
|
||||
statusCode: error.statusCode,
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
};
|
||||
|
|
|
@ -12,6 +12,7 @@ import { serverMock } from '../__mocks__/server';
|
|||
import { requestMock } from '../__mocks__/request';
|
||||
import { requestContextMock } from '../__mocks__/request_context';
|
||||
import { getUnallowedFieldValuesRoute } from './get_unallowed_field_values';
|
||||
import { loggerMock, MockedLogger } from '@kbn/logging-mocks';
|
||||
|
||||
jest.mock('../lib', () => ({
|
||||
getUnallowedFieldValues: jest.fn(),
|
||||
|
@ -20,6 +21,8 @@ jest.mock('../lib', () => ({
|
|||
describe('getUnallowedFieldValuesRoute route', () => {
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
let { context } = requestContextMock.createTools();
|
||||
let logger: MockedLogger;
|
||||
|
||||
const req = requestMock.create({
|
||||
method: 'post',
|
||||
path: GET_UNALLOWED_FIELD_VALUES,
|
||||
|
@ -37,8 +40,9 @@ describe('getUnallowedFieldValuesRoute route', () => {
|
|||
|
||||
server = serverMock.create();
|
||||
({ context } = requestContextMock.createTools());
|
||||
logger = loggerMock.create();
|
||||
|
||||
getUnallowedFieldValuesRoute(server.router);
|
||||
getUnallowedFieldValuesRoute(server.router, logger);
|
||||
});
|
||||
|
||||
test('Returns unallowedValues', async () => {
|
||||
|
@ -107,11 +111,13 @@ describe('getUnallowedFieldValuesRoute route', () => {
|
|||
|
||||
describe('request validation', () => {
|
||||
let server: ReturnType<typeof serverMock.create>;
|
||||
let logger: MockedLogger;
|
||||
|
||||
beforeEach(() => {
|
||||
server = serverMock.create();
|
||||
logger = loggerMock.create();
|
||||
|
||||
getUnallowedFieldValuesRoute(server.router);
|
||||
getUnallowedFieldValuesRoute(server.router, logger);
|
||||
});
|
||||
|
||||
test('disallows invalid pattern', () => {
|
||||
|
|
|
@ -5,40 +5,46 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { IRouter } from '@kbn/core/server';
|
||||
import { IRouter, Logger } from '@kbn/core/server';
|
||||
import { transformError } from '@kbn/securitysolution-es-utils';
|
||||
|
||||
import { getUnallowedFieldValues } from '../lib';
|
||||
import { buildResponse } from '../lib/build_response';
|
||||
import { GET_UNALLOWED_FIELD_VALUES } from '../../common/constants';
|
||||
import { GET_UNALLOWED_FIELD_VALUES, INTERNAL_API_VERSION } from '../../common/constants';
|
||||
import { buildRouteValidation } from '../schemas/common';
|
||||
import { GetUnallowedFieldValuesBody } from '../schemas/get_unallowed_field_values';
|
||||
|
||||
export const getUnallowedFieldValuesRoute = (router: IRouter) => {
|
||||
router.post(
|
||||
{
|
||||
export const getUnallowedFieldValuesRoute = (router: IRouter, logger: Logger) => {
|
||||
router.versioned
|
||||
.post({
|
||||
path: GET_UNALLOWED_FIELD_VALUES,
|
||||
validate: { body: buildRouteValidation(GetUnallowedFieldValuesBody) },
|
||||
},
|
||||
async (context, request, response) => {
|
||||
const resp = buildResponse(response);
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
access: 'internal',
|
||||
})
|
||||
.addVersion(
|
||||
{
|
||||
version: INTERNAL_API_VERSION,
|
||||
validate: { request: { body: buildRouteValidation(GetUnallowedFieldValuesBody) } },
|
||||
},
|
||||
async (context, request, response) => {
|
||||
const resp = buildResponse(response);
|
||||
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
|
||||
|
||||
try {
|
||||
const items = request.body;
|
||||
try {
|
||||
const items = request.body;
|
||||
|
||||
const { responses } = await getUnallowedFieldValues(esClient, items);
|
||||
return response.ok({
|
||||
body: responses,
|
||||
});
|
||||
} catch (err) {
|
||||
const error = transformError(err);
|
||||
const { responses } = await getUnallowedFieldValues(esClient, items);
|
||||
return response.ok({
|
||||
body: responses,
|
||||
});
|
||||
} catch (err) {
|
||||
const error = transformError(err);
|
||||
logger.error(error.message);
|
||||
|
||||
return resp.error({
|
||||
body: error.message,
|
||||
statusCode: error.statusCode,
|
||||
});
|
||||
return resp.error({
|
||||
body: error.message,
|
||||
statusCode: error.statusCode,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { fold } from 'fp-ts/lib/Either';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import type * as rt from 'io-ts';
|
||||
import * as rt from 'io-ts';
|
||||
import { exactCheck, formatErrors } from '@kbn/securitysolution-io-ts-utils';
|
||||
import type {
|
||||
RouteValidationFunction,
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
"@kbn/securitysolution-io-ts-utils",
|
||||
"@kbn/securitysolution-io-ts-types",
|
||||
"@kbn/i18n",
|
||||
"@kbn/core-http-router-server-mocks",
|
||||
"@kbn/logging-mocks",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue