[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:
Angela Chuang 2023-10-17 20:04:20 +01:00 committed by GitHub
parent 0554daacb8
commit 843aaf2f04
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 285 additions and 167 deletions

View file

@ -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,

View file

@ -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,
},
},

View file

@ -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,
},
},

View file

@ -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,
}
);

View file

@ -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) {

View file

@ -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,

View file

@ -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,
}
);
});

View file

@ -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(

View file

@ -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';

View file

@ -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;
}

View file

@ -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 {};
}

View file

@ -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', () => {

View file

@ -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,
});
}
}
}
);
);
};

View file

@ -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', () => {

View file

@ -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,
});
}
}
}
);
);
};

View file

@ -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', () => {

View file

@ -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,
});
}
}
);
);
};

View file

@ -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', () => {

View file

@ -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,
});
}
}
}
);
);
};

View file

@ -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,

View file

@ -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/**/*",