();
- initCaseApi({
- logger: this.log,
+ const telemetryUsageCounter = plugins.usageCollection?.createUsageCounter(APP_ID);
+
+ registerRoutes({
router,
+ routes: getExternalRoutes(),
+ logger: this.log,
kibanaVersion: this.kibanaVersion,
+ telemetryUsageCounter,
});
}
diff --git a/x-pack/plugins/cases/server/routes/api/cases/alerts/get_cases.ts b/x-pack/plugins/cases/server/routes/api/cases/alerts/get_cases.ts
index 8a490e2f68bd0d..00a368e834a0a2 100644
--- a/x-pack/plugins/cases/server/routes/api/cases/alerts/get_cases.ts
+++ b/x-pack/plugins/cases/server/routes/api/cases/alerts/get_cases.ts
@@ -6,42 +6,35 @@
*/
import { schema } from '@kbn/config-schema';
-import Boom from '@hapi/boom';
-import { RouteDeps } from '../../types';
-import { escapeHatch, wrapError } from '../../utils';
import { CasesByAlertIDRequest } from '../../../../../common/api';
import { CASE_ALERTS_URL } from '../../../../../common/constants';
+import { createCaseError } from '../../../../common/error';
+import { createCasesRoute } from '../../create_cases_route';
-export function initGetCasesByAlertIdApi({ router, logger }: RouteDeps) {
- router.get(
- {
- path: CASE_ALERTS_URL,
- validate: {
- params: schema.object({
- alert_id: schema.string(),
- }),
- query: escapeHatch,
- },
- },
- async (context, request, response) => {
- try {
- const alertID = request.params.alert_id;
- if (alertID == null || alertID === '') {
- throw Boom.badRequest('The `alertId` is not valid');
- }
- const casesClient = await context.cases.getCasesClient();
- const options = request.query as CasesByAlertIDRequest;
+export const getCasesByAlertIdRoute = createCasesRoute({
+ method: 'get',
+ path: CASE_ALERTS_URL,
+ params: {
+ params: schema.object({
+ alert_id: schema.string({ minLength: 1 }),
+ }),
+ },
+ handler: async ({ context, request, response }) => {
+ try {
+ const alertID = request.params.alert_id;
- return response.ok({
- body: await casesClient.cases.getCasesByAlertID({ alertID, options }),
- });
- } catch (error) {
- logger.error(
- `Failed to retrieve case ids for this alert id: ${request.params.alert_id}: ${error}`
- );
- return response.customError(wrapError(error));
- }
+ const casesClient = await context.cases.getCasesClient();
+ const options = request.query as CasesByAlertIDRequest;
+
+ return response.ok({
+ body: await casesClient.cases.getCasesByAlertID({ alertID, options }),
+ });
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to retrieve case ids for this alert id: ${request.params.alert_id}: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/cases/delete_cases.ts b/x-pack/plugins/cases/server/routes/api/cases/delete_cases.ts
index 1784a434292cc3..a63d07037de01b 100644
--- a/x-pack/plugins/cases/server/routes/api/cases/delete_cases.ts
+++ b/x-pack/plugins/cases/server/routes/api/cases/delete_cases.ts
@@ -7,32 +7,31 @@
import { schema } from '@kbn/config-schema';
-import { RouteDeps } from '../types';
-import { wrapError } from '../utils';
import { CASES_URL } from '../../../../common/constants';
+import { createCaseError } from '../../../common/error';
+import { createCasesRoute } from '../create_cases_route';
-export function initDeleteCasesApi({ router, logger }: RouteDeps) {
- router.delete(
- {
- path: CASES_URL,
- validate: {
- query: schema.object({
- ids: schema.arrayOf(schema.string()),
- }),
- },
- },
- async (context, request, response) => {
- try {
- const client = await context.cases.getCasesClient();
- await client.cases.delete(request.query.ids);
+export const deleteCaseRoute = createCasesRoute({
+ method: 'delete',
+ path: CASES_URL,
+ params: {
+ query: schema.object({
+ ids: schema.arrayOf(schema.string()),
+ }),
+ },
+ handler: async ({ context, request, response }) => {
+ try {
+ const client = await context.cases.getCasesClient();
+ await client.cases.delete(request.query.ids);
- return response.noContent();
- } catch (error) {
- logger.error(
- `Failed to delete cases in route ids: ${JSON.stringify(request.query.ids)}: ${error}`
- );
- return response.customError(wrapError(error));
- }
+ return response.noContent();
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to delete cases in route ids: ${JSON.stringify(
+ request.query.ids
+ )}: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/cases/find_cases.ts b/x-pack/plugins/cases/server/routes/api/cases/find_cases.ts
index 8474d781a202a1..711c6909df46c0 100644
--- a/x-pack/plugins/cases/server/routes/api/cases/find_cases.ts
+++ b/x-pack/plugins/cases/server/routes/api/cases/find_cases.ts
@@ -7,32 +7,25 @@
import { CasesFindRequest } from '../../../../common/api';
import { CASES_URL } from '../../../../common/constants';
-import { wrapError, escapeHatch } from '../utils';
-import { RouteDeps } from '../types';
+import { createCaseError } from '../../../common/error';
+import { createCasesRoute } from '../create_cases_route';
-export function initFindCasesApi({ router, logger }: RouteDeps) {
- router.get(
- {
- path: `${CASES_URL}/_find`,
- validate: {
- query: escapeHatch,
- },
- },
- async (context, request, response) => {
- try {
- if (!context.cases) {
- return response.badRequest({ body: 'RouteHandlerContext is not registered for cases' });
- }
- const casesClient = await context.cases.getCasesClient();
- const options = request.query as CasesFindRequest;
+export const findCaseRoute = createCasesRoute({
+ method: 'get',
+ path: `${CASES_URL}/_find`,
+ handler: async ({ context, request, response }) => {
+ try {
+ const casesClient = await context.cases.getCasesClient();
+ const options = request.query as CasesFindRequest;
- return response.ok({
- body: await casesClient.cases.find({ ...options }),
- });
- } catch (error) {
- logger.error(`Failed to find cases in route: ${error}`);
- return response.customError(wrapError(error));
- }
+ return response.ok({
+ body: await casesClient.cases.find({ ...options }),
+ });
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to find cases in route: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/cases/get_case.ts b/x-pack/plugins/cases/server/routes/api/cases/get_case.ts
index c8558d09e5c5fd..f0e53e82f14940 100644
--- a/x-pack/plugins/cases/server/routes/api/cases/get_case.ts
+++ b/x-pack/plugins/cases/server/routes/api/cases/get_case.ts
@@ -7,91 +7,83 @@
import { schema } from '@kbn/config-schema';
-import { RouteDeps } from '../types';
-import { getWarningHeader, logDeprecatedEndpoint, wrapError } from '../utils';
+import { getWarningHeader, logDeprecatedEndpoint } from '../utils';
import { CASE_DETAILS_URL } from '../../../../common/constants';
+import { createCaseError } from '../../../common/error';
+import { createCasesRoute } from '../create_cases_route';
-export function initGetCaseApi({ router, logger, kibanaVersion }: RouteDeps) {
- router.get(
- {
- path: CASE_DETAILS_URL,
- validate: {
- params: schema.object({
- case_id: schema.string(),
- }),
- query: schema.object({
- /**
- * @deprecated since version 8.1.0
- */
- includeComments: schema.boolean({ defaultValue: true }),
- }),
- },
- },
- async (context, request, response) => {
- try {
- const isIncludeCommentsParamProvidedByTheUser =
- request.url.searchParams.has('includeComments');
-
- if (isIncludeCommentsParamProvidedByTheUser) {
- logDeprecatedEndpoint(
- logger,
- request.headers,
- `The query parameter 'includeComments' of the get case API '${CASE_DETAILS_URL}' is deprecated`
- );
- }
+const params = {
+ params: schema.object({
+ case_id: schema.string(),
+ }),
+ query: schema.object({
+ /**
+ * @deprecated since version 8.1.0
+ */
+ includeComments: schema.boolean({ defaultValue: true }),
+ }),
+};
- const casesClient = await context.cases.getCasesClient();
- const id = request.params.case_id;
+export const getCaseRoute = createCasesRoute({
+ method: 'get',
+ path: CASE_DETAILS_URL,
+ params,
+ handler: async ({ context, request, response, logger, kibanaVersion }) => {
+ try {
+ const isIncludeCommentsParamProvidedByTheUser =
+ request.url.searchParams.has('includeComments');
- return response.ok({
- ...(isIncludeCommentsParamProvidedByTheUser && {
- headers: {
- ...getWarningHeader(kibanaVersion, 'Deprecated query parameter includeComments'),
- },
- }),
- body: await casesClient.cases.get({
- id,
- includeComments: request.query.includeComments,
- }),
- });
- } catch (error) {
- logger.error(
- `Failed to retrieve case in route case id: ${request.params.case_id} \ninclude comments: ${request.query.includeComments}: ${error}`
+ if (isIncludeCommentsParamProvidedByTheUser) {
+ logDeprecatedEndpoint(
+ logger,
+ request.headers,
+ `The query parameter 'includeComments' of the get case API '${CASE_DETAILS_URL}' is deprecated`
);
- return response.customError(wrapError(error));
}
- }
- );
- router.get(
- {
- path: `${CASE_DETAILS_URL}/resolve`,
- validate: {
- params: schema.object({
- case_id: schema.string(),
+ const casesClient = await context.cases.getCasesClient();
+ const id = request.params.case_id;
+
+ return response.ok({
+ ...(isIncludeCommentsParamProvidedByTheUser && {
+ headers: {
+ ...getWarningHeader(kibanaVersion, 'Deprecated query parameter includeComments'),
+ },
}),
- query: schema.object({
- includeComments: schema.boolean({ defaultValue: true }),
+ body: await casesClient.cases.get({
+ id,
+ includeComments: request.query.includeComments,
}),
- },
- },
- async (context, request, response) => {
- try {
- const casesClient = await context.cases.getCasesClient();
- const id = request.params.case_id;
+ });
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to retrieve case in route case id: ${request.params.case_id} \ninclude comments: ${request.query.includeComments}: ${error}`,
+ error,
+ });
+ }
+ },
+});
- return response.ok({
- body: await casesClient.cases.resolve({
- id,
- includeComments: request.query.includeComments,
- }),
- });
- } catch (error) {
- logger.error(
- `Failed to retrieve case in resolve route case id: ${request.params.case_id} \ninclude comments: ${request.query.includeComments}: ${error}`
- );
- return response.customError(wrapError(error));
- }
+export const resolveCaseRoute = createCasesRoute({
+ method: 'get',
+ path: `${CASE_DETAILS_URL}/resolve`,
+ params,
+ handler: async ({ context, request, response }) => {
+ try {
+ const casesClient = await context.cases.getCasesClient();
+ const id = request.params.case_id;
+
+ return response.ok({
+ body: await casesClient.cases.resolve({
+ id,
+ includeComments: request.query.includeComments,
+ }),
+ });
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to retrieve case in resolve route case id: ${request.params.case_id} \ninclude comments: ${request.query.includeComments}: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/cases/patch_cases.ts b/x-pack/plugins/cases/server/routes/api/cases/patch_cases.ts
index 5cde28bcb01f94..c148a45220a74d 100644
--- a/x-pack/plugins/cases/server/routes/api/cases/patch_cases.ts
+++ b/x-pack/plugins/cases/server/routes/api/cases/patch_cases.ts
@@ -5,35 +5,27 @@
* 2.0.
*/
-import { escapeHatch, wrapError } from '../utils';
-import { RouteDeps } from '../types';
import { CasesPatchRequest } from '../../../../common/api';
import { CASES_URL } from '../../../../common/constants';
+import { createCaseError } from '../../../common/error';
+import { createCasesRoute } from '../create_cases_route';
-export function initPatchCasesApi({ router, logger }: RouteDeps) {
- router.patch(
- {
- path: CASES_URL,
- validate: {
- body: escapeHatch,
- },
- },
- async (context, request, response) => {
- try {
- if (!context.cases) {
- return response.badRequest({ body: 'RouteHandlerContext is not registered for cases' });
- }
+export const patchCaseRoute = createCasesRoute({
+ method: 'patch',
+ path: CASES_URL,
+ handler: async ({ context, request, response }) => {
+ try {
+ const casesClient = await context.cases.getCasesClient();
+ const cases = request.body as CasesPatchRequest;
- const casesClient = await context.cases.getCasesClient();
- const cases = request.body as CasesPatchRequest;
-
- return response.ok({
- body: await casesClient.cases.update(cases),
- });
- } catch (error) {
- logger.error(`Failed to patch cases in route: ${error}`);
- return response.customError(wrapError(error));
- }
+ return response.ok({
+ body: await casesClient.cases.update(cases),
+ });
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to patch cases in route: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/cases/post_case.ts b/x-pack/plugins/cases/server/routes/api/cases/post_case.ts
index df994f18c5bbdc..226d0308a3152b 100644
--- a/x-pack/plugins/cases/server/routes/api/cases/post_case.ts
+++ b/x-pack/plugins/cases/server/routes/api/cases/post_case.ts
@@ -5,35 +5,27 @@
* 2.0.
*/
-import { wrapError, escapeHatch } from '../utils';
-
-import { RouteDeps } from '../types';
import { CasePostRequest } from '../../../../common/api';
import { CASES_URL } from '../../../../common/constants';
+import { createCaseError } from '../../../common/error';
+import { createCasesRoute } from '../create_cases_route';
-export function initPostCaseApi({ router, logger }: RouteDeps) {
- router.post(
- {
- path: CASES_URL,
- validate: {
- body: escapeHatch,
- },
- },
- async (context, request, response) => {
- try {
- if (!context.cases) {
- return response.badRequest({ body: 'RouteHandlerContext is not registered for cases' });
- }
- const casesClient = await context.cases.getCasesClient();
- const theCase = request.body as CasePostRequest;
+export const postCaseRoute = createCasesRoute({
+ method: 'post',
+ path: CASES_URL,
+ handler: async ({ context, request, response }) => {
+ try {
+ const casesClient = await context.cases.getCasesClient();
+ const theCase = request.body as CasePostRequest;
- return response.ok({
- body: await casesClient.cases.create({ ...theCase }),
- });
- } catch (error) {
- logger.error(`Failed to post case in route: ${error}`);
- return response.customError(wrapError(error));
- }
+ return response.ok({
+ body: await casesClient.cases.create({ ...theCase }),
+ });
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to post case in route: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/cases/push_case.ts b/x-pack/plugins/cases/server/routes/api/cases/push_case.ts
index 2b3e7954febfee..175838a9d313c0 100644
--- a/x-pack/plugins/cases/server/routes/api/cases/push_case.ts
+++ b/x-pack/plugins/cases/server/routes/api/cases/push_case.ts
@@ -10,44 +10,35 @@ import { pipe } from 'fp-ts/lib/pipeable';
import { fold } from 'fp-ts/lib/Either';
import { identity } from 'fp-ts/lib/function';
-import { wrapError, escapeHatch } from '../utils';
-
import { throwErrors, CasePushRequestParamsRt } from '../../../../common/api';
import { CASE_PUSH_URL } from '../../../../common/constants';
-import { RouteDeps } from '../types';
-
-export function initPushCaseApi({ router, logger }: RouteDeps) {
- router.post(
- {
- path: CASE_PUSH_URL,
- validate: {
- params: escapeHatch,
- body: escapeHatch,
- },
- },
- async (context, request, response) => {
- try {
- if (!context.cases) {
- return response.badRequest({ body: 'RouteHandlerContext is not registered for cases' });
- }
+import { CaseRoute } from '../types';
+import { createCaseError } from '../../../common/error';
+import { createCasesRoute } from '../create_cases_route';
- const casesClient = await context.cases.getCasesClient();
+export const pushCaseRoute: CaseRoute = createCasesRoute({
+ method: 'post',
+ path: CASE_PUSH_URL,
+ handler: async ({ context, request, response }) => {
+ try {
+ const casesClient = await context.cases.getCasesClient();
- const params = pipe(
- CasePushRequestParamsRt.decode(request.params),
- fold(throwErrors(Boom.badRequest), identity)
- );
+ const params = pipe(
+ CasePushRequestParamsRt.decode(request.params),
+ fold(throwErrors(Boom.badRequest), identity)
+ );
- return response.ok({
- body: await casesClient.cases.push({
- caseId: params.case_id,
- connectorId: params.connector_id,
- }),
- });
- } catch (error) {
- logger.error(`Failed to push case in route: ${error}`);
- return response.customError(wrapError(error));
- }
+ return response.ok({
+ body: await casesClient.cases.push({
+ caseId: params.case_id,
+ connectorId: params.connector_id,
+ }),
+ });
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to push case in route: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/cases/reporters/get_reporters.ts b/x-pack/plugins/cases/server/routes/api/cases/reporters/get_reporters.ts
index 8e0d0640263ec9..ee413d73565ee1 100644
--- a/x-pack/plugins/cases/server/routes/api/cases/reporters/get_reporters.ts
+++ b/x-pack/plugins/cases/server/routes/api/cases/reporters/get_reporters.ts
@@ -5,33 +5,25 @@
* 2.0.
*/
-import { RouteDeps } from '../../types';
-import { wrapError, escapeHatch } from '../../utils';
import { AllReportersFindRequest } from '../../../../../common/api';
import { CASE_REPORTERS_URL } from '../../../../../common/constants';
+import { createCaseError } from '../../../../common/error';
+import { createCasesRoute } from '../../create_cases_route';
-export function initGetReportersApi({ router, logger }: RouteDeps) {
- router.get(
- {
- path: CASE_REPORTERS_URL,
- validate: {
- query: escapeHatch,
- },
- },
- async (context, request, response) => {
- try {
- if (!context.cases) {
- return response.badRequest({ body: 'RouteHandlerContext is not registered for cases' });
- }
+export const getReportersRoute = createCasesRoute({
+ method: 'get',
+ path: CASE_REPORTERS_URL,
+ handler: async ({ context, request, response }) => {
+ try {
+ const client = await context.cases.getCasesClient();
+ const options = request.query as AllReportersFindRequest;
- const client = await context.cases.getCasesClient();
- const options = request.query as AllReportersFindRequest;
-
- return response.ok({ body: await client.cases.getReporters({ ...options }) });
- } catch (error) {
- logger.error(`Failed to get reporters in route: ${error}`);
- return response.customError(wrapError(error));
- }
+ return response.ok({ body: await client.cases.getReporters({ ...options }) });
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to find cases in route: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/cases/tags/get_tags.ts b/x-pack/plugins/cases/server/routes/api/cases/tags/get_tags.ts
index 2afa96be95bc10..7dfa948aa623cd 100644
--- a/x-pack/plugins/cases/server/routes/api/cases/tags/get_tags.ts
+++ b/x-pack/plugins/cases/server/routes/api/cases/tags/get_tags.ts
@@ -5,33 +5,25 @@
* 2.0.
*/
-import { RouteDeps } from '../../types';
-import { wrapError, escapeHatch } from '../../utils';
import { AllTagsFindRequest } from '../../../../../common/api';
import { CASE_TAGS_URL } from '../../../../../common/constants';
+import { createCaseError } from '../../../../common/error';
+import { createCasesRoute } from '../../create_cases_route';
-export function initGetTagsApi({ router, logger }: RouteDeps) {
- router.get(
- {
- path: CASE_TAGS_URL,
- validate: {
- query: escapeHatch,
- },
- },
- async (context, request, response) => {
- try {
- if (!context.cases) {
- return response.badRequest({ body: 'RouteHandlerContext is not registered for cases' });
- }
+export const getTagsRoute = createCasesRoute({
+ method: 'get',
+ path: CASE_TAGS_URL,
+ handler: async ({ context, request, response }) => {
+ try {
+ const client = await context.cases.getCasesClient();
+ const options = request.query as AllTagsFindRequest;
- const client = await context.cases.getCasesClient();
- const options = request.query as AllTagsFindRequest;
-
- return response.ok({ body: await client.cases.getTags({ ...options }) });
- } catch (error) {
- logger.error(`Failed to retrieve tags in route: ${error}`);
- return response.customError(wrapError(error));
- }
+ return response.ok({ body: await client.cases.getTags({ ...options }) });
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to retrieve tags in route: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/comments/delete_all_comments.ts b/x-pack/plugins/cases/server/routes/api/comments/delete_all_comments.ts
index d79f90ac43935e..0a1ebd3b66a74a 100644
--- a/x-pack/plugins/cases/server/routes/api/comments/delete_all_comments.ts
+++ b/x-pack/plugins/cases/server/routes/api/comments/delete_all_comments.ts
@@ -6,35 +6,32 @@
*/
import { schema } from '@kbn/config-schema';
-import { RouteDeps } from '../types';
-import { wrapError } from '../utils';
import { CASE_COMMENTS_URL } from '../../../../common/constants';
+import { createCasesRoute } from '../create_cases_route';
+import { createCaseError } from '../../../common/error';
-export function initDeleteAllCommentsApi({ router, logger }: RouteDeps) {
- router.delete(
- {
- path: CASE_COMMENTS_URL,
- validate: {
- params: schema.object({
- case_id: schema.string(),
- }),
- },
- },
- async (context, request, response) => {
- try {
- const client = await context.cases.getCasesClient();
+export const deleteAllCommentsRoute = createCasesRoute({
+ method: 'delete',
+ path: CASE_COMMENTS_URL,
+ params: {
+ params: schema.object({
+ case_id: schema.string(),
+ }),
+ },
+ handler: async ({ context, request, response }) => {
+ try {
+ const client = await context.cases.getCasesClient();
- await client.attachments.deleteAll({
- caseID: request.params.case_id,
- });
+ await client.attachments.deleteAll({
+ caseID: request.params.case_id,
+ });
- return response.noContent();
- } catch (error) {
- logger.error(
- `Failed to delete all comments in route case id: ${request.params.case_id}: ${error}`
- );
- return response.customError(wrapError(error));
- }
+ return response.noContent();
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to delete all comments in route case id: ${request.params.case_id}: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/comments/delete_comment.ts b/x-pack/plugins/cases/server/routes/api/comments/delete_comment.ts
index b27be46d7220d1..220fbffc76cc03 100644
--- a/x-pack/plugins/cases/server/routes/api/comments/delete_comment.ts
+++ b/x-pack/plugins/cases/server/routes/api/comments/delete_comment.ts
@@ -7,36 +7,33 @@
import { schema } from '@kbn/config-schema';
-import { RouteDeps } from '../types';
-import { wrapError } from '../utils';
import { CASE_COMMENT_DETAILS_URL } from '../../../../common/constants';
+import { createCasesRoute } from '../create_cases_route';
+import { createCaseError } from '../../../common/error';
-export function initDeleteCommentApi({ router, logger }: RouteDeps) {
- router.delete(
- {
- path: CASE_COMMENT_DETAILS_URL,
- validate: {
- params: schema.object({
- case_id: schema.string(),
- comment_id: schema.string(),
- }),
- },
- },
- async (context, request, response) => {
- try {
- const client = await context.cases.getCasesClient();
- await client.attachments.delete({
- attachmentID: request.params.comment_id,
- caseID: request.params.case_id,
- });
+export const deleteCommentRoute = createCasesRoute({
+ method: 'delete',
+ path: CASE_COMMENT_DETAILS_URL,
+ params: {
+ params: schema.object({
+ case_id: schema.string(),
+ comment_id: schema.string(),
+ }),
+ },
+ handler: async ({ context, request, response }) => {
+ try {
+ const client = await context.cases.getCasesClient();
+ await client.attachments.delete({
+ attachmentID: request.params.comment_id,
+ caseID: request.params.case_id,
+ });
- return response.noContent();
- } catch (error) {
- logger.error(
- `Failed to delete comment in route case id: ${request.params.case_id} comment id: ${request.params.comment_id}: ${error}`
- );
- return response.customError(wrapError(error));
- }
+ return response.noContent();
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to delete comment in route case id: ${request.params.case_id} comment id: ${request.params.comment_id}: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/comments/find_comments.ts b/x-pack/plugins/cases/server/routes/api/comments/find_comments.ts
index d4c65e6306a636..14c6090d62ea17 100644
--- a/x-pack/plugins/cases/server/routes/api/comments/find_comments.ts
+++ b/x-pack/plugins/cases/server/routes/api/comments/find_comments.ts
@@ -14,40 +14,36 @@ import { identity } from 'fp-ts/lib/function';
import { FindQueryParamsRt, throwErrors, excess } from '../../../../common/api';
import { CASE_COMMENTS_URL } from '../../../../common/constants';
-import { RouteDeps } from '../types';
-import { escapeHatch, wrapError } from '../utils';
+import { createCasesRoute } from '../create_cases_route';
+import { createCaseError } from '../../../common/error';
-export function initFindCaseCommentsApi({ router, logger }: RouteDeps) {
- router.get(
- {
- path: `${CASE_COMMENTS_URL}/_find`,
- validate: {
- params: schema.object({
- case_id: schema.string(),
- }),
- query: escapeHatch,
- },
- },
- async (context, request, response) => {
- try {
- const query = pipe(
- excess(FindQueryParamsRt).decode(request.query),
- fold(throwErrors(Boom.badRequest), identity)
- );
+export const findCommentsRoute = createCasesRoute({
+ method: 'get',
+ path: `${CASE_COMMENTS_URL}/_find`,
+ params: {
+ params: schema.object({
+ case_id: schema.string(),
+ }),
+ },
+ handler: async ({ context, request, response }) => {
+ try {
+ const query = pipe(
+ excess(FindQueryParamsRt).decode(request.query),
+ fold(throwErrors(Boom.badRequest), identity)
+ );
- const client = await context.cases.getCasesClient();
- return response.ok({
- body: await client.attachments.find({
- caseID: request.params.case_id,
- queryParams: query,
- }),
- });
- } catch (error) {
- logger.error(
- `Failed to find comments in route case id: ${request.params.case_id}: ${error}`
- );
- return response.customError(wrapError(error));
- }
+ const client = await context.cases.getCasesClient();
+ return response.ok({
+ body: await client.attachments.find({
+ caseID: request.params.case_id,
+ queryParams: query,
+ }),
+ });
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to find comments in route case id: ${request.params.case_id}: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/comments/get_alerts.ts b/x-pack/plugins/cases/server/routes/api/comments/get_alerts.ts
index 9c0bfac4d9c6e7..4fa793059ed630 100644
--- a/x-pack/plugins/cases/server/routes/api/comments/get_alerts.ts
+++ b/x-pack/plugins/cases/server/routes/api/comments/get_alerts.ts
@@ -7,35 +7,32 @@
import { schema } from '@kbn/config-schema';
-import { RouteDeps } from '../types';
-import { wrapError } from '../utils';
import { CASE_DETAILS_ALERTS_URL } from '../../../../common/constants';
+import { createCaseError } from '../../../common/error';
+import { createCasesRoute } from '../create_cases_route';
-export function initGetAllAlertsAttachToCaseApi({ router, logger }: RouteDeps) {
- router.get(
- {
- path: CASE_DETAILS_ALERTS_URL,
- validate: {
- params: schema.object({
- case_id: schema.string({ minLength: 1 }),
- }),
- },
- },
- async (context, request, response) => {
- try {
- const caseId = request.params.case_id;
+export const getAllAlertsAttachedToCaseRoute = createCasesRoute({
+ method: 'get',
+ path: CASE_DETAILS_ALERTS_URL,
+ params: {
+ params: schema.object({
+ case_id: schema.string({ minLength: 1 }),
+ }),
+ },
+ handler: async ({ context, request, response }) => {
+ try {
+ const caseId = request.params.case_id;
- const casesClient = await context.cases.getCasesClient();
+ const casesClient = await context.cases.getCasesClient();
- return response.ok({
- body: await casesClient.attachments.getAllAlertsAttachToCase({ caseId }),
- });
- } catch (error) {
- logger.error(
- `Failed to retrieve alert ids for this case id: ${request.params.case_id}: ${error}`
- );
- return response.customError(wrapError(error));
- }
+ return response.ok({
+ body: await casesClient.attachments.getAllAlertsAttachToCase({ caseId }),
+ });
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to retrieve alert ids for this case id: ${request.params.case_id}: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts b/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts
index e94b19cdd9a1c1..d1e47276af1ab6 100644
--- a/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts
+++ b/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts
@@ -7,47 +7,45 @@
import { schema } from '@kbn/config-schema';
-import { RouteDeps } from '../types';
-import { wrapError, getWarningHeader, logDeprecatedEndpoint } from '../utils';
+import { getWarningHeader, logDeprecatedEndpoint } from '../utils';
import { CASE_COMMENTS_URL } from '../../../../common/constants';
+import { createCaseError } from '../../../common/error';
+import { createCasesRoute } from '../create_cases_route';
/**
* @deprecated since version 8.1.0
*/
-export function initGetAllCommentsApi({ router, logger, kibanaVersion }: RouteDeps) {
- router.get(
- {
- path: CASE_COMMENTS_URL,
- validate: {
- params: schema.object({
- case_id: schema.string(),
- }),
- },
- },
- async (context, request, response) => {
- try {
- logDeprecatedEndpoint(
- logger,
- request.headers,
- `The get all cases comments API '${CASE_COMMENTS_URL}' is deprecated.`
- );
+export const getAllCommentsRoute = createCasesRoute({
+ method: 'get',
+ path: CASE_COMMENTS_URL,
+ params: {
+ params: schema.object({
+ case_id: schema.string(),
+ }),
+ },
+ handler: async ({ context, request, response, logger, kibanaVersion }) => {
+ try {
+ logDeprecatedEndpoint(
+ logger,
+ request.headers,
+ `The get all cases comments API '${CASE_COMMENTS_URL}' is deprecated.`
+ );
- const client = await context.cases.getCasesClient();
+ const client = await context.cases.getCasesClient();
- return response.ok({
- headers: {
- ...getWarningHeader(kibanaVersion),
- },
- body: await client.attachments.getAll({
- caseID: request.params.case_id,
- }),
- });
- } catch (error) {
- logger.error(
- `Failed to get all comments in route case id: ${request.params.case_id}: ${error}`
- );
- return response.customError(wrapError(error));
- }
+ return response.ok({
+ headers: {
+ ...getWarningHeader(kibanaVersion),
+ },
+ body: await client.attachments.getAll({
+ caseID: request.params.case_id,
+ }),
+ });
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to get all comments in route case id: ${request.params.case_id}: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/comments/get_comment.ts b/x-pack/plugins/cases/server/routes/api/comments/get_comment.ts
index 09805c00cb10a0..91adf832f1ea69 100644
--- a/x-pack/plugins/cases/server/routes/api/comments/get_comment.ts
+++ b/x-pack/plugins/cases/server/routes/api/comments/get_comment.ts
@@ -7,37 +7,34 @@
import { schema } from '@kbn/config-schema';
-import { RouteDeps } from '../types';
-import { wrapError } from '../utils';
import { CASE_COMMENT_DETAILS_URL } from '../../../../common/constants';
+import { createCaseError } from '../../../common/error';
+import { createCasesRoute } from '../create_cases_route';
-export function initGetCommentApi({ router, logger }: RouteDeps) {
- router.get(
- {
- path: CASE_COMMENT_DETAILS_URL,
- validate: {
- params: schema.object({
- case_id: schema.string(),
- comment_id: schema.string(),
- }),
- },
- },
- async (context, request, response) => {
- try {
- const client = await context.cases.getCasesClient();
+export const getCommentRoute = createCasesRoute({
+ method: 'get',
+ path: CASE_COMMENT_DETAILS_URL,
+ params: {
+ params: schema.object({
+ case_id: schema.string(),
+ comment_id: schema.string(),
+ }),
+ },
+ handler: async ({ context, request, response }) => {
+ try {
+ const client = await context.cases.getCasesClient();
- return response.ok({
- body: await client.attachments.get({
- attachmentID: request.params.comment_id,
- caseID: request.params.case_id,
- }),
- });
- } catch (error) {
- logger.error(
- `Failed to get comment in route case id: ${request.params.case_id} comment id: ${request.params.comment_id}: ${error}`
- );
- return response.customError(wrapError(error));
- }
+ return response.ok({
+ body: await client.attachments.get({
+ attachmentID: request.params.comment_id,
+ caseID: request.params.case_id,
+ }),
+ });
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to get comment in route case id: ${request.params.case_id} comment id: ${request.params.comment_id}: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/comments/patch_comment.ts b/x-pack/plugins/cases/server/routes/api/comments/patch_comment.ts
index 5f9d885178404c..ebc17daa256110 100644
--- a/x-pack/plugins/cases/server/routes/api/comments/patch_comment.ts
+++ b/x-pack/plugins/cases/server/routes/api/comments/patch_comment.ts
@@ -11,43 +11,39 @@ import { identity } from 'fp-ts/lib/function';
import { schema } from '@kbn/config-schema';
import Boom from '@hapi/boom';
-import { RouteDeps } from '../types';
-import { escapeHatch, wrapError } from '../utils';
import { CommentPatchRequestRt, throwErrors } from '../../../../common/api';
import { CASE_COMMENTS_URL } from '../../../../common/constants';
+import { createCaseError } from '../../../common/error';
+import { createCasesRoute } from '../create_cases_route';
-export function initPatchCommentApi({ router, logger }: RouteDeps) {
- router.patch(
- {
- path: CASE_COMMENTS_URL,
- validate: {
- params: schema.object({
- case_id: schema.string(),
- }),
- body: escapeHatch,
- },
- },
- async (context, request, response) => {
- try {
- const query = pipe(
- CommentPatchRequestRt.decode(request.body),
- fold(throwErrors(Boom.badRequest), identity)
- );
+export const patchCommentRoute = createCasesRoute({
+ method: 'patch',
+ path: CASE_COMMENTS_URL,
+ params: {
+ params: schema.object({
+ case_id: schema.string(),
+ }),
+ },
+ handler: async ({ context, request, response }) => {
+ try {
+ const query = pipe(
+ CommentPatchRequestRt.decode(request.body),
+ fold(throwErrors(Boom.badRequest), identity)
+ );
- const client = await context.cases.getCasesClient();
+ const client = await context.cases.getCasesClient();
- return response.ok({
- body: await client.attachments.update({
- caseID: request.params.case_id,
- updateRequest: query,
- }),
- });
- } catch (error) {
- logger.error(
- `Failed to patch comment in route case id: ${request.params.case_id}: ${error}`
- );
- return response.customError(wrapError(error));
- }
+ return response.ok({
+ body: await client.attachments.update({
+ caseID: request.params.case_id,
+ updateRequest: query,
+ }),
+ });
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to patch comment in route case id: ${request.params.case_id}: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/comments/post_comment.ts b/x-pack/plugins/cases/server/routes/api/comments/post_comment.ts
index ed9c9170084172..1ececb3653741d 100644
--- a/x-pack/plugins/cases/server/routes/api/comments/post_comment.ts
+++ b/x-pack/plugins/cases/server/routes/api/comments/post_comment.ts
@@ -6,41 +6,33 @@
*/
import { schema } from '@kbn/config-schema';
-import { escapeHatch, wrapError } from '../utils';
-import { RouteDeps } from '../types';
import { CASE_COMMENTS_URL } from '../../../../common/constants';
import { CommentRequest } from '../../../../common/api';
+import { createCaseError } from '../../../common/error';
+import { createCasesRoute } from '../create_cases_route';
-export function initPostCommentApi({ router, logger }: RouteDeps) {
- router.post(
- {
- path: CASE_COMMENTS_URL,
- validate: {
- params: schema.object({
- case_id: schema.string(),
- }),
- body: escapeHatch,
- },
- },
- async (context, request, response) => {
- try {
- if (!context.cases) {
- return response.badRequest({ body: 'RouteHandlerContext is not registered for cases' });
- }
+export const postCommentRoute = createCasesRoute({
+ method: 'post',
+ path: CASE_COMMENTS_URL,
+ params: {
+ params: schema.object({
+ case_id: schema.string(),
+ }),
+ },
+ handler: async ({ context, request, response }) => {
+ try {
+ const casesClient = await context.cases.getCasesClient();
+ const caseId = request.params.case_id;
+ const comment = request.body as CommentRequest;
- const casesClient = await context.cases.getCasesClient();
- const caseId = request.params.case_id;
- const comment = request.body as CommentRequest;
-
- return response.ok({
- body: await casesClient.attachments.add({ caseId, comment }),
- });
- } catch (error) {
- logger.error(
- `Failed to post comment in route case id: ${request.params.case_id}: ${error}`
- );
- return response.customError(wrapError(error));
- }
+ return response.ok({
+ body: await casesClient.attachments.add({ caseId, comment }),
+ });
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to post comment in route case id: ${request.params.case_id}: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/configure/get_configure.ts b/x-pack/plugins/cases/server/routes/api/configure/get_configure.ts
index 8222ac8fe56909..8dabf7862fc88c 100644
--- a/x-pack/plugins/cases/server/routes/api/configure/get_configure.ts
+++ b/x-pack/plugins/cases/server/routes/api/configure/get_configure.ts
@@ -5,31 +5,27 @@
* 2.0.
*/
-import { RouteDeps } from '../types';
-import { escapeHatch, wrapError } from '../utils';
import { CASE_CONFIGURE_URL } from '../../../../common/constants';
import { GetConfigureFindRequest } from '../../../../common/api';
+import { createCaseError } from '../../../common/error';
+import { createCasesRoute } from '../create_cases_route';
-export function initGetCaseConfigure({ router, logger }: RouteDeps) {
- router.get(
- {
- path: CASE_CONFIGURE_URL,
- validate: {
- query: escapeHatch,
- },
- },
- async (context, request, response) => {
- try {
- const client = await context.cases.getCasesClient();
- const options = request.query as GetConfigureFindRequest;
+export const getCaseConfigureRoute = createCasesRoute({
+ method: 'get',
+ path: CASE_CONFIGURE_URL,
+ handler: async ({ context, request, response }) => {
+ try {
+ const client = await context.cases.getCasesClient();
+ const options = request.query as GetConfigureFindRequest;
- return response.ok({
- body: await client.configure.get({ ...options }),
- });
- } catch (error) {
- logger.error(`Failed to get case configure in route: ${error}`);
- return response.customError(wrapError(error));
- }
+ return response.ok({
+ body: await client.configure.get({ ...options }),
+ });
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to get case configure in route: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/configure/get_connectors.ts b/x-pack/plugins/cases/server/routes/api/configure/get_connectors.ts
index 46c110bbb8ba52..da99cd19065d6a 100644
--- a/x-pack/plugins/cases/server/routes/api/configure/get_connectors.ts
+++ b/x-pack/plugins/cases/server/routes/api/configure/get_connectors.ts
@@ -5,29 +5,26 @@
* 2.0.
*/
-import { RouteDeps } from '../types';
-import { wrapError } from '../utils';
-
import { CASE_CONFIGURE_CONNECTORS_URL } from '../../../../common/constants';
+import { createCaseError } from '../../../common/error';
+import { createCasesRoute } from '../create_cases_route';
/*
* Be aware that this api will only return 20 connectors
*/
-export function initCaseConfigureGetActionConnector({ router, logger }: RouteDeps) {
- router.get(
- {
- path: `${CASE_CONFIGURE_CONNECTORS_URL}/_find`,
- validate: false,
- },
- async (context, request, response) => {
- try {
- const client = await context.cases.getCasesClient();
+export const getConnectorsRoute = createCasesRoute({
+ method: 'get',
+ path: `${CASE_CONFIGURE_CONNECTORS_URL}/_find`,
+ handler: async ({ context, response }) => {
+ try {
+ const client = await context.cases.getCasesClient();
- return response.ok({ body: await client.configure.getConnectors() });
- } catch (error) {
- logger.error(`Failed to get connectors in route: ${error}`);
- return response.customError(wrapError(error));
- }
+ return response.ok({ body: await client.configure.getConnectors() });
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to get connectors in route: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/configure/patch_configure.ts b/x-pack/plugins/cases/server/routes/api/configure/patch_configure.ts
index e856a568f387a5..40b0d5123f4294 100644
--- a/x-pack/plugins/cases/server/routes/api/configure/patch_configure.ts
+++ b/x-pack/plugins/cases/server/routes/api/configure/patch_configure.ts
@@ -17,35 +17,30 @@ import {
excess,
} from '../../../../common/api';
import { CASE_CONFIGURE_DETAILS_URL } from '../../../../common/constants';
-import { RouteDeps } from '../types';
-import { wrapError, escapeHatch } from '../utils';
+import { createCaseError } from '../../../common/error';
+import { createCasesRoute } from '../create_cases_route';
-export function initPatchCaseConfigure({ router, logger }: RouteDeps) {
- router.patch(
- {
- path: CASE_CONFIGURE_DETAILS_URL,
- validate: {
- params: escapeHatch,
- body: escapeHatch,
- },
- },
- async (context, request, response) => {
- try {
- const params = pipe(
- excess(CaseConfigureRequestParamsRt).decode(request.params),
- fold(throwErrors(Boom.badRequest), identity)
- );
+export const patchCaseConfigureRoute = createCasesRoute({
+ method: 'patch',
+ path: CASE_CONFIGURE_DETAILS_URL,
+ handler: async ({ context, request, response }) => {
+ try {
+ const params = pipe(
+ excess(CaseConfigureRequestParamsRt).decode(request.params),
+ fold(throwErrors(Boom.badRequest), identity)
+ );
- const client = await context.cases.getCasesClient();
- const configuration = request.body as CasesConfigurePatch;
+ const client = await context.cases.getCasesClient();
+ const configuration = request.body as CasesConfigurePatch;
- return response.ok({
- body: await client.configure.update(params.configuration_id, configuration),
- });
- } catch (error) {
- logger.error(`Failed to get patch configure in route: ${error}`);
- return response.customError(wrapError(error));
- }
+ return response.ok({
+ body: await client.configure.update(params.configuration_id, configuration),
+ });
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to patch configure in route: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/configure/post_configure.ts b/x-pack/plugins/cases/server/routes/api/configure/post_configure.ts
index ed4c3529f2ca08..bb64175fb52adf 100644
--- a/x-pack/plugins/cases/server/routes/api/configure/post_configure.ts
+++ b/x-pack/plugins/cases/server/routes/api/configure/post_configure.ts
@@ -12,33 +12,29 @@ import { identity } from 'fp-ts/lib/function';
import { CasesConfigureRequestRt, throwErrors } from '../../../../common/api';
import { CASE_CONFIGURE_URL } from '../../../../common/constants';
-import { RouteDeps } from '../types';
-import { wrapError, escapeHatch } from '../utils';
+import { createCaseError } from '../../../common/error';
+import { createCasesRoute } from '../create_cases_route';
-export function initPostCaseConfigure({ router, logger }: RouteDeps) {
- router.post(
- {
- path: CASE_CONFIGURE_URL,
- validate: {
- body: escapeHatch,
- },
- },
- async (context, request, response) => {
- try {
- const query = pipe(
- CasesConfigureRequestRt.decode(request.body),
- fold(throwErrors(Boom.badRequest), identity)
- );
+export const postCaseConfigureRoute = createCasesRoute({
+ method: 'post',
+ path: CASE_CONFIGURE_URL,
+ handler: async ({ context, request, response }) => {
+ try {
+ const query = pipe(
+ CasesConfigureRequestRt.decode(request.body),
+ fold(throwErrors(Boom.badRequest), identity)
+ );
- const client = await context.cases.getCasesClient();
+ const client = await context.cases.getCasesClient();
- return response.ok({
- body: await client.configure.create(query),
- });
- } catch (error) {
- logger.error(`Failed to post case configure in route: ${error}`);
- return response.customError(wrapError(error));
- }
+ return response.ok({
+ body: await client.configure.create(query),
+ });
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to post case configure in route: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/reporting/server/export_types/common/pdf/pdfmaker_errors.ts b/x-pack/plugins/cases/server/routes/api/create_cases_route.ts
similarity index 61%
rename from x-pack/plugins/reporting/server/export_types/common/pdf/pdfmaker_errors.ts
rename to x-pack/plugins/cases/server/routes/api/create_cases_route.ts
index d55921d791aded..eb6a1079440a0c 100644
--- a/x-pack/plugins/reporting/server/export_types/common/pdf/pdfmaker_errors.ts
+++ b/x-pack/plugins/cases/server/routes/api/create_cases_route.ts
@@ -5,9 +5,6 @@
* 2.0.
*/
-export class PdfWorkerOutOfMemoryError extends Error {
- constructor(message: string) {
- super(message);
- this.name = 'PdfWorkerOutOfMemoryError';
- }
-}
+import { CaseRoute } from './types';
+
+export const createCasesRoute = (route: CaseRoute
) => route;
diff --git a/x-pack/plugins/cases/server/routes/api/get_external_routes.ts b/x-pack/plugins/cases/server/routes/api/get_external_routes.ts
new file mode 100644
index 00000000000000..7908e4eb84359f
--- /dev/null
+++ b/x-pack/plugins/cases/server/routes/api/get_external_routes.ts
@@ -0,0 +1,61 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { getCasesByAlertIdRoute } from './cases/alerts/get_cases';
+import { deleteCaseRoute } from './cases/delete_cases';
+import { findCaseRoute } from './cases/find_cases';
+import { getCaseRoute, resolveCaseRoute } from './cases/get_case';
+import { patchCaseRoute } from './cases/patch_cases';
+import { postCaseRoute } from './cases/post_case';
+import { pushCaseRoute } from './cases/push_case';
+import { getReportersRoute } from './cases/reporters/get_reporters';
+import { getStatusRoute } from './stats/get_status';
+import { getUserActionsRoute } from './user_actions/get_all_user_actions';
+import { CaseRoute } from './types';
+import { getTagsRoute } from './cases/tags/get_tags';
+import { deleteAllCommentsRoute } from './comments/delete_all_comments';
+import { deleteCommentRoute } from './comments/delete_comment';
+import { findCommentsRoute } from './comments/find_comments';
+import { getCommentRoute } from './comments/get_comment';
+import { getAllCommentsRoute } from './comments/get_all_comment';
+import { patchCommentRoute } from './comments/patch_comment';
+import { postCommentRoute } from './comments/post_comment';
+import { getCaseConfigureRoute } from './configure/get_configure';
+import { getConnectorsRoute } from './configure/get_connectors';
+import { patchCaseConfigureRoute } from './configure/patch_configure';
+import { postCaseConfigureRoute } from './configure/post_configure';
+import { getAllAlertsAttachedToCaseRoute } from './comments/get_alerts';
+import { getCaseMetricRoute } from './metrics/get_case_metrics';
+
+export const getExternalRoutes = () =>
+ [
+ deleteCaseRoute,
+ findCaseRoute,
+ getCaseRoute,
+ resolveCaseRoute,
+ patchCaseRoute,
+ postCaseRoute,
+ pushCaseRoute,
+ getUserActionsRoute,
+ getStatusRoute,
+ getCasesByAlertIdRoute,
+ getReportersRoute,
+ getTagsRoute,
+ deleteCommentRoute,
+ deleteAllCommentsRoute,
+ findCommentsRoute,
+ getCommentRoute,
+ getAllCommentsRoute,
+ patchCommentRoute,
+ postCommentRoute,
+ getCaseConfigureRoute,
+ getConnectorsRoute,
+ patchCaseConfigureRoute,
+ postCaseConfigureRoute,
+ getAllAlertsAttachedToCaseRoute,
+ getCaseMetricRoute,
+ ] as CaseRoute[];
diff --git a/x-pack/plugins/cases/server/routes/api/index.ts b/x-pack/plugins/cases/server/routes/api/index.ts
index 8298f7469f2369..31eafe0f29d28e 100644
--- a/x-pack/plugins/cases/server/routes/api/index.ts
+++ b/x-pack/plugins/cases/server/routes/api/index.ts
@@ -5,76 +5,11 @@
* 2.0.
*/
-import { initDeleteCasesApi } from './cases/delete_cases';
-import { initFindCasesApi } from '././cases/find_cases';
-import { initGetCaseApi } from './cases/get_case';
-import { initPatchCasesApi } from './cases/patch_cases';
-import { initPostCaseApi } from './cases/post_case';
-import { initPushCaseApi } from './cases/push_case';
-import { initGetReportersApi } from './cases/reporters/get_reporters';
-import { initGetCasesStatusApi } from './stats/get_status';
-import { initGetTagsApi } from './cases/tags/get_tags';
-import { initGetAllCaseUserActionsApi } from './user_actions/get_all_user_actions';
-
-import { initDeleteCommentApi } from './comments/delete_comment';
-import { initDeleteAllCommentsApi } from './comments/delete_all_comments';
-import { initFindCaseCommentsApi } from './comments/find_comments';
-import { initGetAllCommentsApi } from './comments/get_all_comment';
-import { initGetCommentApi } from './comments/get_comment';
-import { initPatchCommentApi } from './comments/patch_comment';
-import { initPostCommentApi } from './comments/post_comment';
-
-import { initCaseConfigureGetActionConnector } from './configure/get_connectors';
-import { initGetCaseConfigure } from './configure/get_configure';
-import { initPatchCaseConfigure } from './configure/patch_configure';
-import { initPostCaseConfigure } from './configure/post_configure';
-
-import { RouteDeps } from './types';
-import { initGetCasesByAlertIdApi } from './cases/alerts/get_cases';
-import { initGetAllAlertsAttachToCaseApi } from './comments/get_alerts';
-import { initGetCaseMetricsApi } from './metrics/get_case_metrics';
-
/**
* Default page number when interacting with the saved objects API.
*/
-export const defaultPage = 1;
+export const DEFAULT_PAGE = 1;
/**
* Default number of results when interacting with the saved objects API.
*/
-export const defaultPerPage = 20;
-
-export function initCaseApi(deps: RouteDeps) {
- // Cases
- initDeleteCasesApi(deps);
- initFindCasesApi(deps);
- initGetCaseApi(deps);
- initPatchCasesApi(deps);
- initPostCaseApi(deps);
- initPushCaseApi(deps);
- initGetAllCaseUserActionsApi(deps);
-
- // Comments
- initDeleteCommentApi(deps);
- initDeleteAllCommentsApi(deps);
- initFindCaseCommentsApi(deps);
- initGetCommentApi(deps);
- initGetAllCommentsApi(deps);
- initPatchCommentApi(deps);
- initPostCommentApi(deps);
- // Cases Configure
- initCaseConfigureGetActionConnector(deps);
- initGetCaseConfigure(deps);
- initPatchCaseConfigure(deps);
- initPostCaseConfigure(deps);
- // Reporters
- initGetReportersApi(deps);
- // Status
- initGetCasesStatusApi(deps);
- // Tags
- initGetTagsApi(deps);
- // Alerts
- initGetCasesByAlertIdApi(deps);
- initGetAllAlertsAttachToCaseApi(deps);
- // Metrics
- initGetCaseMetricsApi(deps);
-}
+export const DEFAULT_PER_PAGE = 20;
diff --git a/x-pack/plugins/cases/server/routes/api/metrics/get_case_metrics.ts b/x-pack/plugins/cases/server/routes/api/metrics/get_case_metrics.ts
index 0cfad10b28316e..b86b84410abe62 100644
--- a/x-pack/plugins/cases/server/routes/api/metrics/get_case_metrics.ts
+++ b/x-pack/plugins/cases/server/routes/api/metrics/get_case_metrics.ts
@@ -7,37 +7,35 @@
import { schema } from '@kbn/config-schema';
-import { RouteDeps } from '../types';
-import { wrapError } from '../utils';
-
import { CASE_METRICS_DETAILS_URL } from '../../../../common/constants';
+import { createCaseError } from '../../../common/error';
+import { createCasesRoute } from '../create_cases_route';
-export function initGetCaseMetricsApi({ router, logger }: RouteDeps) {
- router.get(
- {
- path: CASE_METRICS_DETAILS_URL,
- validate: {
- params: schema.object({
- case_id: schema.string({ minLength: 1 }),
- }),
- query: schema.object({
- features: schema.arrayOf(schema.string({ minLength: 1 })),
+export const getCaseMetricRoute = createCasesRoute({
+ method: 'get',
+ path: CASE_METRICS_DETAILS_URL,
+ params: {
+ params: schema.object({
+ case_id: schema.string({ minLength: 1 }),
+ }),
+ query: schema.object({
+ features: schema.arrayOf(schema.string({ minLength: 1 })),
+ }),
+ },
+ handler: async ({ context, request, response }) => {
+ try {
+ const client = await context.cases.getCasesClient();
+ return response.ok({
+ body: await client.metrics.getCaseMetrics({
+ caseId: request.params.case_id,
+ features: request.query.features,
}),
- },
- },
- async (context, request, response) => {
- try {
- const client = await context.cases.getCasesClient();
- return response.ok({
- body: await client.metrics.getCaseMetrics({
- caseId: request.params.case_id,
- features: request.query.features,
- }),
- });
- } catch (error) {
- logger.error(`Failed to get case metrics in route: ${error}`);
- return response.customError(wrapError(error));
- }
+ });
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to get case metrics in route: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/register_routes.test.ts b/x-pack/plugins/cases/server/routes/api/register_routes.test.ts
new file mode 100644
index 00000000000000..71ec6f2bce5cd6
--- /dev/null
+++ b/x-pack/plugins/cases/server/routes/api/register_routes.test.ts
@@ -0,0 +1,280 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { schema } from '@kbn/config-schema';
+
+import {
+ httpServerMock,
+ httpServiceMock,
+ loggingSystemMock,
+} from '../../../../../../src/core/server/mocks';
+
+import { usageCollectionPluginMock } from '../../../../../../src/plugins/usage_collection/server/mocks';
+
+import { CasesRouter } from '../../types';
+import { createCasesRoute } from './create_cases_route';
+import { registerRoutes } from './register_routes';
+import { CaseRoute } from './types';
+
+describe('registerRoutes', () => {
+ let router: jest.Mocked;
+ const logger = loggingSystemMock.createLogger();
+ const response = httpServerMock.createResponseFactory();
+ const telemetryUsageCounter = usageCollectionPluginMock
+ .createSetupContract()
+ .createUsageCounter('test');
+
+ const handler = jest.fn();
+ const customError = jest.fn();
+ const badRequest = jest.fn();
+
+ const routes = [
+ createCasesRoute({
+ method: 'get',
+ path: '/foo/{case_id}',
+ params: {
+ params: schema.object({
+ case_id: schema.string(),
+ }),
+ query: schema.object({
+ includeComments: schema.boolean(),
+ }),
+ },
+ handler,
+ }),
+
+ createCasesRoute({
+ method: 'post',
+ path: '/bar',
+ params: {
+ body: schema.object({
+ title: schema.string(),
+ }),
+ },
+ handler: async () => response.ok(),
+ }),
+ createCasesRoute({
+ method: 'put',
+ path: '/baz',
+ handler: async () => response.ok(),
+ }),
+ createCasesRoute({
+ method: 'patch',
+ path: '/qux',
+ handler: async () => response.ok(),
+ }),
+ createCasesRoute({
+ method: 'delete',
+ path: '/quux',
+ handler: async () => response.ok(),
+ }),
+ ] as CaseRoute[];
+
+ const initApi = (casesRoutes: CaseRoute[]) => {
+ registerRoutes({
+ router,
+ logger,
+ routes: casesRoutes,
+ kibanaVersion: '8.2.0',
+ telemetryUsageCounter,
+ });
+
+ const simulateRequest = async ({
+ method,
+ path,
+ context = { cases: {} },
+ headers = {},
+ }: {
+ method: keyof Pick;
+ path: string;
+ context?: Record;
+ headers?: Record;
+ }) => {
+ const [, registeredRouteHandler] =
+ // @ts-ignore
+ router[method].mock.calls.find((call) => {
+ return call[0].path === path;
+ }) ?? [];
+
+ const result = await registeredRouteHandler(
+ context,
+ { headers },
+ { customError, badRequest }
+ );
+ return result;
+ };
+
+ return {
+ simulateRequest,
+ };
+ };
+
+ const initAndSimulateError = async () => {
+ const { simulateRequest } = initApi([
+ ...routes,
+ createCasesRoute({
+ method: 'get',
+ path: '/error',
+ handler: async () => {
+ throw new Error('API error');
+ },
+ }),
+ ]);
+
+ await simulateRequest({
+ method: 'get',
+ path: '/error',
+ });
+ };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ router = httpServiceMock.createRouter();
+ });
+
+ describe('api registration', () => {
+ const endpoints: Array<[CaseRoute['method'], string]> = [
+ ['get', '/foo/{case_id}'],
+ ['post', '/bar'],
+ ['put', '/baz'],
+ ['patch', '/qux'],
+ ['delete', '/quux'],
+ ];
+
+ it('registers the endpoints correctly', () => {
+ initApi(routes);
+
+ for (const endpoint of endpoints) {
+ const [method, path] = endpoint;
+
+ expect(router[method]).toHaveBeenCalledTimes(1);
+ expect(router[method]).toBeCalledWith(
+ { path, validate: expect.anything() },
+ expect.anything()
+ );
+ }
+ });
+ });
+
+ describe('api validation', () => {
+ const validation: Array<
+ ['params' | 'query' | 'body', keyof CasesRouter, Record]
+ > = [
+ ['params', 'get', { case_id: '123' }],
+ ['query', 'get', { includeComments: false }],
+ ['body', 'post', { title: 'test' }],
+ ];
+
+ describe.each(validation)('%s', (type, method, value) => {
+ it(`validates ${type} correctly`, () => {
+ initApi(routes);
+ // @ts-ignore
+ const params = router[method].mock.calls[0][0].validate[type];
+ expect(() => params.validate(value)).not.toThrow();
+ });
+
+ it(`throws if ${type} is wrong`, () => {
+ initApi(routes);
+ // @ts-ignore
+ const params = router[method].mock.calls[0][0].validate[type];
+ expect(() => params.validate({})).toThrow();
+ });
+
+ it(`skips path parameter validation if ${type} is not provided`, () => {
+ initApi(routes);
+ // @ts-ignore
+ const params = router.put.mock.calls[0][0].validate[type];
+ expect(() => params.validate({})).not.toThrow();
+ });
+ });
+ });
+
+ describe('handler execution', () => {
+ it('calls the handler correctly', async () => {
+ const { simulateRequest } = initApi(routes);
+ await simulateRequest({ method: 'get', path: '/foo/{case_id}' });
+ expect(handler).toHaveBeenCalled();
+ });
+ });
+
+ describe('telemetry', () => {
+ it('increases the counters correctly on a successful kibana request', async () => {
+ const { simulateRequest } = initApi(routes);
+ await simulateRequest({
+ method: 'get',
+ path: '/foo/{case_id}',
+ headers: { 'kbn-version': '8.2.0', referer: 'https://example.com' },
+ });
+ expect(telemetryUsageCounter.incrementCounter).toHaveBeenCalledWith({
+ counterName: 'GET /foo/{case_id}',
+ counterType: 'success',
+ });
+
+ expect(telemetryUsageCounter.incrementCounter).toHaveBeenCalledWith({
+ counterName: 'GET /foo/{case_id}',
+ counterType: 'kibanaRequest.yes',
+ });
+ });
+
+ it('increases the counters correctly on a successful non kibana request', async () => {
+ const { simulateRequest } = initApi(routes);
+ await simulateRequest({
+ method: 'get',
+ path: '/foo/{case_id}',
+ });
+ expect(telemetryUsageCounter.incrementCounter).toHaveBeenCalledWith({
+ counterName: 'GET /foo/{case_id}',
+ counterType: 'success',
+ });
+
+ expect(telemetryUsageCounter.incrementCounter).toHaveBeenCalledWith({
+ counterName: 'GET /foo/{case_id}',
+ counterType: 'kibanaRequest.no',
+ });
+ });
+
+ it('increases the counters correctly on an error', async () => {
+ await initAndSimulateError();
+
+ expect(telemetryUsageCounter.incrementCounter).toHaveBeenCalledWith({
+ counterName: 'GET /error',
+ counterType: 'error',
+ });
+ });
+ });
+
+ describe('errors', () => {
+ it('logs the error', async () => {
+ await initAndSimulateError();
+
+ expect(logger.error).toBeCalledWith('API error');
+ });
+
+ it('returns an error response', async () => {
+ await initAndSimulateError();
+
+ expect(customError).toBeCalledWith({
+ body: expect.anything(),
+ headers: {},
+ statusCode: 500,
+ });
+ });
+
+ it('returns an error response when the case context is not registered', async () => {
+ const { simulateRequest } = initApi(routes);
+ await simulateRequest({
+ method: 'get',
+ path: '/foo/{case_id}',
+ context: {},
+ });
+
+ expect(badRequest).toBeCalledWith({
+ body: 'RouteHandlerContext is not registered for cases',
+ });
+ });
+ });
+});
diff --git a/x-pack/plugins/cases/server/routes/api/register_routes.ts b/x-pack/plugins/cases/server/routes/api/register_routes.ts
new file mode 100644
index 00000000000000..843009f3b22c5a
--- /dev/null
+++ b/x-pack/plugins/cases/server/routes/api/register_routes.ts
@@ -0,0 +1,88 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { schema } from '@kbn/config-schema';
+import { RouteRegistrar } from 'kibana/server';
+import { CasesRequestHandlerContext } from '../../types';
+import { CaseRoute, RegisterRoutesDeps } from './types';
+import { escapeHatch, getIsKibanaRequest, wrapError } from './utils';
+
+const increaseTelemetryCounters = ({
+ telemetryUsageCounter,
+ method,
+ path,
+ isKibanaRequest,
+ isError = false,
+}: {
+ telemetryUsageCounter: Exclude;
+ method: string;
+ path: string;
+ isKibanaRequest: boolean;
+ isError?: boolean;
+}) => {
+ const counterName = `${method.toUpperCase()} ${path}`;
+
+ telemetryUsageCounter.incrementCounter({
+ counterName,
+ counterType: isError ? 'error' : 'success',
+ });
+
+ telemetryUsageCounter.incrementCounter({
+ counterName,
+ counterType: `kibanaRequest.${isKibanaRequest ? 'yes' : 'no'}`,
+ });
+};
+
+export const registerRoutes = (deps: RegisterRoutesDeps) => {
+ const { router, routes, logger, kibanaVersion, telemetryUsageCounter } = deps;
+
+ routes.forEach((route) => {
+ const { method, path, params, handler } = route as CaseRoute;
+
+ (router[method] as RouteRegistrar)(
+ {
+ path,
+ validate: {
+ params: params?.params ?? escapeHatch,
+ query: params?.query ?? escapeHatch,
+ body: params?.body ?? schema.nullable(escapeHatch),
+ },
+ },
+ async (context, request, response) => {
+ const isKibanaRequest = getIsKibanaRequest(request.headers);
+
+ if (!context.cases) {
+ return response.badRequest({ body: 'RouteHandlerContext is not registered for cases' });
+ }
+
+ try {
+ const res = await handler({ logger, context, request, response, kibanaVersion });
+
+ if (telemetryUsageCounter) {
+ increaseTelemetryCounters({ telemetryUsageCounter, method, path, isKibanaRequest });
+ }
+
+ return res;
+ } catch (error) {
+ logger.error(error.message);
+
+ if (telemetryUsageCounter) {
+ increaseTelemetryCounters({
+ telemetryUsageCounter,
+ method,
+ path,
+ isError: true,
+ isKibanaRequest,
+ });
+ }
+
+ return response.customError(wrapError(error));
+ }
+ }
+ );
+ });
+};
diff --git a/x-pack/plugins/cases/server/routes/api/stats/get_status.ts b/x-pack/plugins/cases/server/routes/api/stats/get_status.ts
index 90044c9516b3a8..4cd5bd7eebd0a6 100644
--- a/x-pack/plugins/cases/server/routes/api/stats/get_status.ts
+++ b/x-pack/plugins/cases/server/routes/api/stats/get_status.ts
@@ -5,40 +5,40 @@
* 2.0.
*/
-import { RouteDeps } from '../types';
-import { escapeHatch, wrapError, getWarningHeader, logDeprecatedEndpoint } from '../utils';
+import { CaseRoute } from '../types';
+import { getWarningHeader, logDeprecatedEndpoint } from '../utils';
import { CasesStatusRequest } from '../../../../common/api';
import { CASE_STATUS_URL } from '../../../../common/constants';
+import { createCaseError } from '../../../common/error';
+import { createCasesRoute } from '../create_cases_route';
/**
* @deprecated since version 8.1.0
*/
-export function initGetCasesStatusApi({ router, logger, kibanaVersion }: RouteDeps) {
- router.get(
- {
- path: CASE_STATUS_URL,
- validate: { query: escapeHatch },
- },
- async (context, request, response) => {
- try {
- logDeprecatedEndpoint(
- logger,
- request.headers,
- `The get cases status API '${CASE_STATUS_URL}' is deprecated.`
- );
+export const getStatusRoute: CaseRoute = createCasesRoute({
+ method: 'get',
+ path: CASE_STATUS_URL,
+ handler: async ({ context, request, response, logger, kibanaVersion }) => {
+ try {
+ logDeprecatedEndpoint(
+ logger,
+ request.headers,
+ `The get cases status API '${CASE_STATUS_URL}' is deprecated.`
+ );
- const client = await context.cases.getCasesClient();
- return response.ok({
- headers: {
- ...getWarningHeader(kibanaVersion),
- },
- body: await client.metrics.getStatusTotalsByType(request.query as CasesStatusRequest),
- });
- } catch (error) {
- logger.error(`Failed to get status stats in route: ${error}`);
- return response.customError(wrapError(error));
- }
+ const client = await context.cases.getCasesClient();
+ return response.ok({
+ headers: {
+ ...getWarningHeader(kibanaVersion),
+ },
+ body: await client.metrics.getStatusTotalsByType(request.query as CasesStatusRequest),
+ });
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to get status stats in route: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/types.ts b/x-pack/plugins/cases/server/routes/api/types.ts
index e3aa6e0e970fa2..2b1893ebb75c7e 100644
--- a/x-pack/plugins/cases/server/routes/api/types.ts
+++ b/x-pack/plugins/cases/server/routes/api/types.ts
@@ -5,17 +5,44 @@
* 2.0.
*/
-import type { Logger, PluginInitializerContext } from 'kibana/server';
+import type {
+ Logger,
+ PluginInitializerContext,
+ KibanaRequest,
+ IKibanaResponse,
+ KibanaResponseFactory,
+ RouteValidatorConfig,
+} from 'kibana/server';
-import type { CasesRouter } from '../../types';
+import { UsageCollectionSetup } from '../../../../../../src/plugins/usage_collection/server';
+import type { CasesRequestHandlerContext, CasesRouter } from '../../types';
-export interface RouteDeps {
+type TelemetryUsageCounter = ReturnType;
+
+export interface RegisterRoutesDeps {
router: CasesRouter;
+ routes: CaseRoute[];
logger: Logger;
kibanaVersion: PluginInitializerContext['env']['packageInfo']['version'];
+ telemetryUsageCounter?: TelemetryUsageCounter;
}
export interface TotalCommentByCase {
caseId: string;
totalComments: number;
}
+
+interface CaseRouteHandlerArguments {
+ request: KibanaRequest
;
+ context: CasesRequestHandlerContext;
+ response: KibanaResponseFactory;
+ logger: Logger;
+ kibanaVersion: PluginInitializerContext['env']['packageInfo']['version'];
+}
+
+export interface CaseRoute
{
+ method: 'get' | 'post' | 'put' | 'delete' | 'patch';
+ path: string;
+ params?: RouteValidatorConfig
;
+ handler: (args: CaseRouteHandlerArguments
) => Promise;
+}
diff --git a/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts b/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts
index 2e38ac8b4ebc79..b9b6ce43a9fdfd 100644
--- a/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts
+++ b/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts
@@ -7,50 +7,44 @@
import { schema } from '@kbn/config-schema';
-import { RouteDeps } from '../types';
-import { getWarningHeader, logDeprecatedEndpoint, wrapError } from '../utils';
+import { getWarningHeader, logDeprecatedEndpoint } from '../utils';
import { CASE_USER_ACTIONS_URL } from '../../../../common/constants';
+import { createCaseError } from '../../../common/error';
+import { createCasesRoute } from '../create_cases_route';
/**
* @deprecated since version 8.1.0
*/
-export function initGetAllCaseUserActionsApi({ router, logger, kibanaVersion }: RouteDeps) {
- router.get(
- {
- path: CASE_USER_ACTIONS_URL,
- validate: {
- params: schema.object({
- case_id: schema.string(),
- }),
- },
- },
- async (context, request, response) => {
- try {
- if (!context.cases) {
- return response.badRequest({ body: 'RouteHandlerContext is not registered for cases' });
- }
+export const getUserActionsRoute = createCasesRoute({
+ method: 'get',
+ path: CASE_USER_ACTIONS_URL,
+ params: {
+ params: schema.object({
+ case_id: schema.string(),
+ }),
+ },
+ handler: async ({ context, request, response, logger, kibanaVersion }) => {
+ try {
+ logDeprecatedEndpoint(
+ logger,
+ request.headers,
+ `The get all cases user actions API '${CASE_USER_ACTIONS_URL}' is deprecated.`
+ );
- logDeprecatedEndpoint(
- logger,
- request.headers,
- `The get all cases user actions API '${CASE_USER_ACTIONS_URL}' is deprecated.`
- );
+ const casesClient = await context.cases.getCasesClient();
+ const caseId = request.params.case_id;
- const casesClient = await context.cases.getCasesClient();
- const caseId = request.params.case_id;
-
- return response.ok({
- headers: {
- ...getWarningHeader(kibanaVersion),
- },
- body: await casesClient.userActions.getAll({ caseId }),
- });
- } catch (error) {
- logger.error(
- `Failed to retrieve case user actions in route case id: ${request.params.case_id}: ${error}`
- );
- return response.customError(wrapError(error));
- }
+ return response.ok({
+ headers: {
+ ...getWarningHeader(kibanaVersion),
+ },
+ body: await casesClient.userActions.getAll({ caseId }),
+ });
+ } catch (error) {
+ throw createCaseError({
+ message: `Failed to retrieve case user actions in route case id: ${request.params.case_id}: ${error}`,
+ error,
+ });
}
- );
-}
+ },
+});
diff --git a/x-pack/plugins/cases/server/routes/api/utils.ts b/x-pack/plugins/cases/server/routes/api/utils.ts
index 532a316e9a7b84..3536e4db346679 100644
--- a/x-pack/plugins/cases/server/routes/api/utils.ts
+++ b/x-pack/plugins/cases/server/routes/api/utils.ts
@@ -52,10 +52,10 @@ export const getWarningHeader = (
* https://github.com/elastic/kibana/blob/ec30f2aeeb10fb64b507935e558832d3ef5abfaa/x-pack/plugins/spaces/server/usage_stats/usage_stats_client.ts#L113-L118
*/
-const getIsKibanaRequest = (headers?: Headers) => {
+export const getIsKibanaRequest = (headers?: Headers): boolean => {
// The presence of these two request headers gives us a good indication that this is a first-party request from the Kibana client.
// We can't be 100% certain, but this is a reasonable attempt.
- return headers && headers['kbn-version'] && headers.referer;
+ return !!(headers && headers['kbn-version'] && headers.referer);
};
export const logDeprecatedEndpoint = (logger: Logger, headers: Headers, msg: string) => {
diff --git a/x-pack/plugins/cases/server/services/cases/index.ts b/x-pack/plugins/cases/server/services/cases/index.ts
index 832d12071b4662..684edcc077f8e2 100644
--- a/x-pack/plugins/cases/server/services/cases/index.ts
+++ b/x-pack/plugins/cases/server/services/cases/index.ts
@@ -39,7 +39,7 @@ import {
} from '../../../common/api';
import { SavedObjectFindOptionsKueryNode } from '../../common/types';
import { defaultSortField, flattenCaseSavedObject } from '../../common/utils';
-import { defaultPage, defaultPerPage } from '../../routes/api';
+import { DEFAULT_PAGE, DEFAULT_PER_PAGE } from '../../routes/api';
import { combineFilters } from '../../client/utils';
import { includeFieldsRequiredForAuthentication } from '../../authorization/utils';
import {
@@ -420,8 +420,8 @@ export class CasesService {
return {
saved_objects: [],
total: 0,
- per_page: options?.perPage ?? defaultPerPage,
- page: options?.page ?? defaultPage,
+ per_page: options?.perPage ?? DEFAULT_PER_PAGE,
+ page: options?.page ?? DEFAULT_PAGE,
};
}
diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/full_time_range_selector/full_time_range_selector_service.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/full_time_range_selector/full_time_range_selector_service.ts
index 303d54c9d45ccf..e7ad239e0c9801 100644
--- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/full_time_range_selector/full_time_range_selector_service.ts
+++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/full_time_range_selector/full_time_range_selector_service.ts
@@ -6,7 +6,6 @@
*/
import moment from 'moment';
-import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { TimefilterContract } from 'src/plugins/data/public';
import dateMath from '@elastic/datemath';
import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
@@ -27,8 +26,7 @@ export async function setFullTimeRange(
query?: QueryDslQueryContainer,
excludeFrozenData?: boolean
): Promise {
- const runtimeMappings = indexPattern.getComputedFields()
- .runtimeFields as estypes.MappingRuntimeFields;
+ const runtimeMappings = indexPattern.getRuntimeMappings();
const resp = await getTimeFieldRange({
index: indexPattern.title,
timeFieldName: indexPattern.timeFieldName,
diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_data_visualizer_grid_data.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_data_visualizer_grid_data.ts
index a6176b8e3c1ce7..c4e9e898454d51 100644
--- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_data_visualizer_grid_data.ts
+++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_data_visualizer_grid_data.ts
@@ -198,7 +198,7 @@ export const useDataVisualizerGridData = (
sessionId: searchSessionId,
index: currentIndexPattern.title,
timeFieldName: currentIndexPattern.timeFieldName,
- runtimeFieldMap: currentIndexPattern.getComputedFields().runtimeFields,
+ runtimeFieldMap: currentIndexPattern.getRuntimeMappings(),
aggregatableFields,
nonAggregatableFields,
fieldsToFetch,
diff --git a/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts b/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts
index 5ff3b6c481d746..22898ac54db5ac 100644
--- a/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts
+++ b/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts
@@ -393,7 +393,9 @@ describe('setIndexToHidden', () => {
expect(clusterClient.indices.putSettings).toHaveBeenCalledWith({
index: 'foo-bar-000001',
body: {
- 'index.hidden': true,
+ index: {
+ hidden: true,
+ },
},
});
});
diff --git a/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts b/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts
index 010d162c62ea13..bb958c3ce2b54f 100644
--- a/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts
+++ b/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts
@@ -178,7 +178,6 @@ export class ClusterClientAdapter;
+
+export interface PostLogstashApiKeyResponse {
+ api_key: string;
+}
diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx
index a8354237bbcb7c..655875d0448938 100644
--- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx
+++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx
@@ -70,6 +70,11 @@ export const SelectCreateAgentPolicy: React.FC = ({
[onAgentPolicyChange]
);
+ const onClickCreatePolicy = () => {
+ setCreateState({ status: CREATE_STATUS.INITIAL });
+ setShowCreatePolicy(true);
+ };
+
return (
<>
{showCreatePolicy ? (
@@ -86,7 +91,7 @@ export const SelectCreateAgentPolicy: React.FC = ({
onKeyChange={onKeyChange}
onAgentPolicyChange={onAgentPolicyChange}
excludeFleetServer={excludeFleetServer}
- onClickCreatePolicy={() => setShowCreatePolicy(true)}
+ onClickCreatePolicy={onClickCreatePolicy}
selectedAgentPolicy={selectedAgentPolicy}
isFleetServerPolicy={isFleetServerPolicy}
/>
diff --git a/x-pack/plugins/fleet/server/routes/output/handler.ts b/x-pack/plugins/fleet/server/routes/output/handler.ts
index 6de9fe1204f1c0..d4372c22f32de7 100644
--- a/x-pack/plugins/fleet/server/routes/output/handler.ts
+++ b/x-pack/plugins/fleet/server/routes/output/handler.ts
@@ -18,10 +18,12 @@ import type {
DeleteOutputResponse,
GetOneOutputResponse,
GetOutputsResponse,
+ PostLogstashApiKeyResponse,
} from '../../../common';
import { outputService } from '../../services/output';
-import { defaultIngestErrorHandler } from '../../errors';
+import { defaultIngestErrorHandler, FleetUnauthorizedError } from '../../errors';
import { agentPolicyService } from '../../services';
+import { generateLogstashApiKey, canCreateLogstashApiKey } from '../../services/api_keys';
export const getOutputsHandler: RequestHandler = async (context, request, response) => {
const soClient = context.core.savedObjects.client;
@@ -142,3 +144,24 @@ export const deleteOutputHandler: RequestHandler<
return defaultIngestErrorHandler({ error, response });
}
};
+
+export const postLogstashApiKeyHandler: RequestHandler = async (context, request, response) => {
+ const esClient = context.core.elasticsearch.client.asCurrentUser;
+ try {
+ const hasCreatePrivileges = await canCreateLogstashApiKey(esClient);
+ if (!hasCreatePrivileges) {
+ throw new FleetUnauthorizedError('Missing permissions to create logstash API key');
+ }
+
+ const apiKey = await generateLogstashApiKey(esClient);
+
+ const body: PostLogstashApiKeyResponse = {
+ // Logstash expect the key to be formatted like this id:key
+ api_key: `${apiKey.id}:${apiKey.api_key}`,
+ };
+
+ return response.ok({ body });
+ } catch (error) {
+ return defaultIngestErrorHandler({ error, response });
+ }
+};
diff --git a/x-pack/plugins/fleet/server/routes/output/index.ts b/x-pack/plugins/fleet/server/routes/output/index.ts
index b9dfb1f7f742bf..f74c1bb88aeb03 100644
--- a/x-pack/plugins/fleet/server/routes/output/index.ts
+++ b/x-pack/plugins/fleet/server/routes/output/index.ts
@@ -21,6 +21,7 @@ import {
getOutputsHandler,
postOuputHandler,
putOuputHandler,
+ postLogstashApiKeyHandler,
} from './handler';
export const registerRoutes = (router: FleetAuthzRouter) => {
@@ -76,4 +77,15 @@ export const registerRoutes = (router: FleetAuthzRouter) => {
},
deleteOutputHandler
);
+
+ router.post(
+ {
+ path: OUTPUT_API_ROUTES.LOGSTASH_API_KEY_PATTERN,
+ validate: false,
+ fleetAuthz: {
+ fleet: { all: true },
+ },
+ },
+ postLogstashApiKeyHandler
+ );
};
diff --git a/x-pack/plugins/fleet/server/services/api_keys/index.ts b/x-pack/plugins/fleet/server/services/api_keys/index.ts
index c781b2d01943fc..7b96d71c7ac9cd 100644
--- a/x-pack/plugins/fleet/server/services/api_keys/index.ts
+++ b/x-pack/plugins/fleet/server/services/api_keys/index.ts
@@ -5,36 +5,6 @@
* 2.0.
*/
-import type { KibanaRequest } from 'src/core/server';
-
export { invalidateAPIKeys } from './security';
+export { generateLogstashApiKey, canCreateLogstashApiKey } from './logstash_api_keys';
export * from './enrollment_api_key';
-
-export function parseApiKeyFromHeaders(headers: KibanaRequest['headers']) {
- const authorizationHeader = headers.authorization;
-
- if (!authorizationHeader) {
- throw new Error('Authorization header must be set');
- }
-
- if (Array.isArray(authorizationHeader)) {
- throw new Error('Authorization header must be `string` not `string[]`');
- }
-
- if (!authorizationHeader.startsWith('ApiKey ')) {
- throw new Error('Authorization header is malformed');
- }
-
- const apiKey = authorizationHeader.split(' ')[1];
-
- return parseApiKey(apiKey);
-}
-
-export function parseApiKey(apiKey: string) {
- const apiKeyId = Buffer.from(apiKey, 'base64').toString('utf8').split(':')[0];
-
- return {
- apiKey,
- apiKeyId,
- };
-}
diff --git a/x-pack/plugins/fleet/server/services/api_keys/logstash_api_keys.ts b/x-pack/plugins/fleet/server/services/api_keys/logstash_api_keys.ts
new file mode 100644
index 00000000000000..d55aa37fd5150d
--- /dev/null
+++ b/x-pack/plugins/fleet/server/services/api_keys/logstash_api_keys.ts
@@ -0,0 +1,70 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { ElasticsearchClient } from 'src/core/server';
+
+/**
+ * Check if an esClient has enought permission to create a valid API key for logstash
+ *
+ * @param esClient
+ */
+export async function canCreateLogstashApiKey(esClient: ElasticsearchClient) {
+ const res = await esClient.security.hasPrivileges({
+ cluster: ['monitor', 'manage_own_api_key'],
+ index: [
+ {
+ names: [
+ 'logs-*-*',
+ 'metrics-*-*',
+ 'traces-*-*',
+ 'synthetics-*-*',
+ '.logs-endpoint.diagnostic.collection-*',
+ '.logs-endpoint.action.responses-*',
+ ],
+ privileges: ['auto_configure', 'create_doc'],
+ },
+ ],
+ });
+
+ return res.has_all_requested;
+}
+
+/**
+ * Generate an Elasticsearch API key to use in logstash ES output
+ *
+ * @param esClient
+ */
+export async function generateLogstashApiKey(esClient: ElasticsearchClient) {
+ const apiKey = await esClient.security.createApiKey({
+ name: 'Fleet Logstash output',
+ metadata: {
+ managed_by: 'fleet',
+ managed: true,
+ type: 'logstash',
+ },
+ role_descriptors: {
+ 'logstash-output': {
+ cluster: ['monitor'],
+ index: [
+ {
+ names: [
+ 'logs-*-*',
+ 'metrics-*-*',
+ 'traces-*-*',
+ 'synthetics-*-*',
+ '.logs-endpoint.diagnostic.collection-*',
+ '.logs-endpoint.action.responses-*',
+ ],
+ privileges: ['auto_configure', 'create_doc'],
+ },
+ ],
+ },
+ },
+ });
+
+ return apiKey;
+}
diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ml_model/install.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ml_model/install.ts
index e5c96bea871818..52951377c6e2e7 100644
--- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ml_model/install.ts
+++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ml_model/install.ts
@@ -74,13 +74,20 @@ async function handleMlModelInstall({
try {
await retryTransientEsErrors(
() =>
- esClient.ml.putTrainedModel({
- model_id: mlModel.installationName,
- defer_definition_decompression: true,
- timeout: '45s',
- // @ts-expect-error expects an object not a string
- body: mlModel.content,
- }),
+ esClient.ml.putTrainedModel(
+ {
+ model_id: mlModel.installationName,
+ defer_definition_decompression: true,
+ timeout: '45s',
+ // @ts-expect-error expects an object not a string
+ body: mlModel.content,
+ },
+ {
+ headers: {
+ 'content-type': 'application/json',
+ },
+ }
+ ),
{ logger }
);
} catch (err) {
diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts
index 5d144435bbee11..7650caf73d7145 100644
--- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts
+++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts
@@ -714,42 +714,53 @@ describe('EPM template', () => {
expect(mappings).toEqual(expectedMapping);
});
- it('processes meta fields', () => {
- const metaFieldLiteralYaml = `
-- name: fieldWithMetas
- type: integer
- unit: byte
+ it('tests processing metric_type field', () => {
+ const literalYml = `
+- name: total.norm.pct
+ type: scaled_float
metric_type: gauge
- `;
- const metaFieldMapping = {
+ unit: percent
+ format: percent
+`;
+ const expectedMapping = {
properties: {
- fieldWithMetas: {
- type: 'long',
- meta: {
- metric_type: 'gauge',
- unit: 'byte',
+ total: {
+ properties: {
+ norm: {
+ properties: {
+ pct: {
+ scaling_factor: 1000,
+ type: 'scaled_float',
+ meta: {
+ metric_type: 'gauge',
+ unit: 'percent',
+ },
+ time_series_metric: 'gauge',
+ },
+ },
+ },
},
},
},
};
- const fields: Field[] = safeLoad(metaFieldLiteralYaml);
+ const fields: Field[] = safeLoad(literalYml);
const processedFields = processFields(fields);
const mappings = generateMappings(processedFields);
- expect(JSON.stringify(mappings)).toEqual(JSON.stringify(metaFieldMapping));
+ expect(mappings).toEqual(expectedMapping);
});
- it('processes meta fields with only one meta value', () => {
+ it('processes meta fields', () => {
const metaFieldLiteralYaml = `
- name: fieldWithMetas
type: integer
- metric_type: gauge
+ unit: byte
`;
const metaFieldMapping = {
properties: {
fieldWithMetas: {
type: 'long',
meta: {
- metric_type: 'gauge',
+ unit: 'byte',
},
},
},
@@ -765,16 +776,13 @@ describe('EPM template', () => {
- name: groupWithMetas
type: group
unit: byte
- metric_type: gauge
fields:
- name: fieldA
type: integer
unit: byte
- metric_type: gauge
- name: fieldB
type: integer
unit: byte
- metric_type: gauge
`;
const metaFieldMapping = {
properties: {
@@ -783,14 +791,12 @@ describe('EPM template', () => {
fieldA: {
type: 'long',
meta: {
- metric_type: 'gauge',
unit: 'byte',
},
},
fieldB: {
type: 'long',
meta: {
- metric_type: 'gauge',
unit: 'byte',
},
},
diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts
index f88f5aeb1c727b..73ad218d1a9fd0 100644
--- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts
+++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts
@@ -132,6 +132,9 @@ export function generateMappings(fields: Field[]): IndexTemplateMappings {
case 'scaled_float':
fieldProps.type = 'scaled_float';
fieldProps.scaling_factor = field.scaling_factor || DEFAULT_SCALING_FACTOR;
+ if (field.metric_type) {
+ fieldProps.time_series_metric = field.metric_type;
+ }
break;
case 'text':
const textMapping = generateTextMapping(field);
diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/remove.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/remove.ts
index 6a2284e0df742a..07748c1635b3a6 100644
--- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/remove.ts
+++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/remove.ts
@@ -46,7 +46,6 @@ export const deleteTransforms = async (esClient: ElasticsearchClient, transformI
await esClient.transport.request(
{
method: 'DELETE',
- // @ts-expect-error @elastic/elasticsearch Transform is empty interface
path: `/${transform?.dest?.index}`,
},
{
diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transform.test.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transform.test.ts
index 879c7614fedbf4..4a1909cc813e7a 100644
--- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transform.test.ts
+++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transform.test.ts
@@ -106,6 +106,7 @@ describe('test transform install', () => {
esClient.transform.getTransform.mockResponseOnce({
count: 1,
transforms: [
+ // @ts-expect-error incomplete data
{
dest: {
index: 'index',
@@ -394,6 +395,7 @@ describe('test transform install', () => {
esClient.transform.getTransform.mockResponseOnce({
count: 1,
transforms: [
+ // @ts-expect-error incomplete data
{
dest: {
index: 'index',
diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts
index 1acbce4f22cd1d..546ae9c6fb9acf 100644
--- a/x-pack/plugins/fleet/server/services/package_policy.ts
+++ b/x-pack/plugins/fleet/server/services/package_policy.ts
@@ -28,6 +28,7 @@ import {
doesAgentPolicyAlreadyIncludePackage,
validatePackagePolicy,
validationHasErrors,
+ SO_SEARCH_LIMIT,
} from '../../common';
import type {
DeletePackagePoliciesResponse,
@@ -369,9 +370,10 @@ class PackagePolicyService implements PackagePolicyServiceInterface {
}
// Check that the name does not exist already but exclude the current package policy
const existingPoliciesWithName = await this.list(soClient, {
- perPage: 1,
- kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.name: "${packagePolicy.name}"`,
+ perPage: SO_SEARCH_LIMIT,
+ kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.name:"${packagePolicy.name}"`,
});
+
const filtered = (existingPoliciesWithName?.items || []).filter((p) => p.id !== id);
if (filtered.length > 0) {
diff --git a/x-pack/plugins/index_management/server/lib/fetch_indices.ts b/x-pack/plugins/index_management/server/lib/fetch_indices.ts
index 9e8a8b23a7d9d0..cec763a247ed7e 100644
--- a/x-pack/plugins/index_management/server/lib/fetch_indices.ts
+++ b/x-pack/plugins/index_management/server/lib/fetch_indices.ts
@@ -51,9 +51,7 @@ async function fetchIndicesCall(
const indexStats = indicesStats[indexName];
const aliases = Object.keys(indexData.aliases!);
return {
- // @ts-expect-error new property https://github.com/elastic/elasticsearch-specification/issues/1253
health: indexStats?.health,
- // @ts-expect-error new property https://github.com/elastic/elasticsearch-specification/issues/1253
status: indexStats?.status,
name: indexName,
uuid: indexStats?.uuid,
diff --git a/x-pack/plugins/infra/common/dependency_mocks/index_patterns.ts b/x-pack/plugins/infra/common/dependency_mocks/index_patterns.ts
index 03d3ec757bf552..46f961d6de4725 100644
--- a/x-pack/plugins/infra/common/dependency_mocks/index_patterns.ts
+++ b/x-pack/plugins/infra/common/dependency_mocks/index_patterns.ts
@@ -5,15 +5,17 @@
* 2.0.
*/
+import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { from, of } from 'rxjs';
import { delay } from 'rxjs/operators';
import { DataView, DataViewsContract } from '../../../../../src/plugins/data_views/common';
-import { fieldList, FieldSpec, RuntimeField } from '../../../../../src/plugins/data/common';
+import { fieldList, FieldSpec } from '../../../../../src/plugins/data/common';
type IndexPatternMock = Pick<
DataView,
| 'fields'
| 'getComputedFields'
+ | 'getRuntimeMappings'
| 'getFieldByName'
| 'getTimeField'
| 'id'
@@ -23,6 +25,7 @@ type IndexPatternMock = Pick<
>;
type IndexPatternMockSpec = Pick & {
fields: FieldSpec[];
+ runtimeFields?: estypes.MappingRuntimeFields;
};
export const createIndexPatternMock = ({
@@ -30,6 +33,7 @@ export const createIndexPatternMock = ({
title,
type = undefined,
fields,
+ runtimeFields,
timeFieldName,
}: IndexPatternMockSpec): IndexPatternMock => {
const indexPatternFieldList = fieldList(fields);
@@ -43,21 +47,12 @@ export const createIndexPatternMock = ({
isTimeBased: () => timeFieldName != null,
getFieldByName: (fieldName) => indexPatternFieldList.find(({ name }) => name === fieldName),
getComputedFields: () => ({
- runtimeFields: indexPatternFieldList.reduce>(
- (accumulatedFields, { name, runtimeField }) => ({
- ...accumulatedFields,
- ...(runtimeField != null
- ? {
- [name]: runtimeField,
- }
- : {}),
- }),
- {}
- ),
+ runtimeFields: runtimeFields ?? {},
scriptFields: {},
storedFields: [],
docvalueFields: [],
}),
+ getRuntimeMappings: () => runtimeFields ?? {},
};
};
diff --git a/x-pack/plugins/infra/common/log_sources/resolved_log_source_configuration.ts b/x-pack/plugins/infra/common/log_sources/resolved_log_source_configuration.ts
index 70f8e1cebabfe5..914c55824373a4 100644
--- a/x-pack/plugins/infra/common/log_sources/resolved_log_source_configuration.ts
+++ b/x-pack/plugins/infra/common/log_sources/resolved_log_source_configuration.ts
@@ -7,7 +7,6 @@
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { DataView, DataViewsContract } from '../../../../../src/plugins/data_views/common';
-import { ObjectEntries } from '../utility_types';
import { TIMESTAMP_FIELD, TIEBREAKER_FIELD } from '../constants';
import { ResolveLogSourceConfigurationError } from './errors';
import {
@@ -106,27 +105,5 @@ const resolveKibanaIndexPatternReference = async (
// this might take other sources of runtime fields into account in the future
const resolveRuntimeMappings = (indexPattern: DataView): estypes.MappingRuntimeFields => {
- const { runtimeFields } = indexPattern.getComputedFields();
-
- const runtimeMappingsFromIndexPattern = (
- Object.entries(runtimeFields) as ObjectEntries
- ).reduce(
- (accumulatedMappings, [runtimeFieldName, runtimeFieldSpec]) => ({
- ...accumulatedMappings,
- [runtimeFieldName]: {
- type: runtimeFieldSpec.type,
- ...(runtimeFieldSpec.script != null
- ? {
- script: {
- lang: 'painless', // required in the es types
- source: runtimeFieldSpec.script.source,
- },
- }
- : {}),
- },
- }),
- {}
- );
-
- return runtimeMappingsFromIndexPattern;
+ return indexPattern.getRuntimeMappings();
};
diff --git a/x-pack/plugins/infra/public/alerting/inventory/components/alert_flyout.tsx b/x-pack/plugins/infra/public/alerting/inventory/components/alert_flyout.tsx
index 33fe3c7af30c78..c0a475ea8029b6 100644
--- a/x-pack/plugins/infra/public/alerting/inventory/components/alert_flyout.tsx
+++ b/x-pack/plugins/infra/public/alerting/inventory/components/alert_flyout.tsx
@@ -34,7 +34,7 @@ export const AlertFlyout = ({ options, nodeType, filter, visible, setVisible }:
consumer: 'infrastructure',
onClose: onCloseFlyout,
canChangeTrigger: false,
- alertTypeId: METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID,
+ ruleTypeId: METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID,
metadata: {
options,
nodeType,
diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_flyout.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_flyout.tsx
index bee7f93a538be9..d7270aa0ef0e58 100644
--- a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_flyout.tsx
+++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_flyout.tsx
@@ -25,7 +25,7 @@ export const AlertFlyout = (props: Props) => {
consumer: 'logs',
onClose: onCloseFlyout,
canChangeTrigger: false,
- alertTypeId: LOG_DOCUMENT_COUNT_RULE_TYPE_ID,
+ ruleTypeId: LOG_DOCUMENT_COUNT_RULE_TYPE_ID,
metadata: {
isInternal: true,
},
diff --git a/x-pack/plugins/infra/public/alerting/metric_anomaly/components/alert_flyout.tsx b/x-pack/plugins/infra/public/alerting/metric_anomaly/components/alert_flyout.tsx
index 9d467e1df7e36a..e0e9946b1c7898 100644
--- a/x-pack/plugins/infra/public/alerting/metric_anomaly/components/alert_flyout.tsx
+++ b/x-pack/plugins/infra/public/alerting/metric_anomaly/components/alert_flyout.tsx
@@ -32,7 +32,7 @@ export const AlertFlyout = ({ metric, nodeType, visible, setVisible }: Props) =>
consumer: 'infrastructure',
onClose: onCloseFlyout,
canChangeTrigger: false,
- alertTypeId: METRIC_ANOMALY_ALERT_TYPE_ID,
+ ruleTypeId: METRIC_ANOMALY_ALERT_TYPE_ID,
metadata: {
metric,
nodeType,
diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_flyout.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_flyout.tsx
index e7e4ade5257fc1..642b7dbf079c03 100644
--- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_flyout.tsx
+++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_flyout.tsx
@@ -30,7 +30,7 @@ export const AlertFlyout = (props: Props) => {
consumer: 'infrastructure',
onClose: onCloseFlyout,
canChangeTrigger: false,
- alertTypeId: METRIC_THRESHOLD_ALERT_TYPE_ID,
+ ruleTypeId: METRIC_THRESHOLD_ALERT_TYPE_ID,
metadata: {
currentOptions: props.options,
series: props.series,
diff --git a/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx
index 66abef53f021e5..8aaac2f1b9a46c 100644
--- a/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx
+++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx
@@ -6,10 +6,10 @@
*/
import { i18n } from '@kbn/i18n';
-import { flowRight } from 'lodash';
import React from 'react';
import { Redirect, RouteComponentProps } from 'react-router-dom';
import useMount from 'react-use/lib/useMount';
+import { flowRight } from 'lodash';
import { findInventoryFields } from '../../../common/inventory_models';
import { InventoryItemType } from '../../../common/inventory_models/types';
import { LoadingPage } from '../../components/loading_page';
diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_rule.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_rule.ts
index 744fd80134aec9..81c714b30cb0d9 100644
--- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_rule.ts
+++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_rule.ts
@@ -6,8 +6,8 @@
*/
import { ElasticsearchClient } from 'kibana/server';
-import { difference, first, has, isNaN, isNumber, isObject, last, mapValues } from 'lodash';
import moment from 'moment';
+import { difference, first, has, isNaN, isNumber, isObject, last, mapValues } from 'lodash';
import {
Aggregators,
Comparator,
diff --git a/x-pack/plugins/infra/server/services/log_entries/log_entries_search_strategy.test.ts b/x-pack/plugins/infra/server/services/log_entries/log_entries_search_strategy.test.ts
index 73f832e834c610..cfa9e84fb3651a 100644
--- a/x-pack/plugins/infra/server/services/log_entries/log_entries_search_strategy.test.ts
+++ b/x-pack/plugins/infra/server/services/log_entries/log_entries_search_strategy.test.ts
@@ -80,7 +80,6 @@ describe('LogEntries search strategy', () => {
runtime_field: {
type: 'keyword',
script: {
- lang: 'painless',
source: 'emit("runtime value")',
},
},
@@ -359,6 +358,14 @@ const createDataPluginMock = (esSearchStrategyMock: ISearchStrategy): any => ({
searchable: true,
},
],
+ runtimeFields: {
+ runtime_field: {
+ type: 'keyword',
+ script: {
+ source: 'emit("runtime value")',
+ },
+ },
+ },
}),
]),
});
diff --git a/x-pack/plugins/infra/server/services/log_entries/log_entry_search_strategy.test.ts b/x-pack/plugins/infra/server/services/log_entries/log_entry_search_strategy.test.ts
index 20f3e41cef159a..c2f3c70580040f 100644
--- a/x-pack/plugins/infra/server/services/log_entries/log_entry_search_strategy.test.ts
+++ b/x-pack/plugins/infra/server/services/log_entries/log_entry_search_strategy.test.ts
@@ -79,7 +79,6 @@ describe('LogEntry search strategy', () => {
runtime_field: {
type: 'keyword',
script: {
- lang: 'painless',
source: 'emit("runtime value")',
},
},
@@ -314,6 +313,14 @@ const createDataPluginMock = (esSearchStrategyMock: ISearchStrategy): any => ({
searchable: true,
},
],
+ runtimeFields: {
+ runtime_field: {
+ type: 'keyword',
+ script: {
+ source: 'emit("runtime value")',
+ },
+ },
+ },
}),
]),
});
diff --git a/x-pack/plugins/ingest_pipelines/__jest__/client_integration/ingest_pipelines_create_from_csv.test.tsx b/x-pack/plugins/ingest_pipelines/__jest__/client_integration/ingest_pipelines_create_from_csv.test.tsx
index 5f1230f004684a..d6a5b4e01a9b70 100644
--- a/x-pack/plugins/ingest_pipelines/__jest__/client_integration/ingest_pipelines_create_from_csv.test.tsx
+++ b/x-pack/plugins/ingest_pipelines/__jest__/client_integration/ingest_pipelines_create_from_csv.test.tsx
@@ -66,7 +66,7 @@ describe('', () => {
expect(find('pageTitle').text()).toEqual('Create pipeline from CSV');
expect(exists('documentationLink')).toBe(true);
- expect(find('documentationLink').text()).toBe('Create pipeline docs');
+ expect(find('documentationLink').text()).toBe('CSV to pipeline docs');
});
describe('form validation', () => {
diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/pipelines_create.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/pipelines_create.tsx
index 097ec3d98e162b..a7fbf6afaebf83 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/pipelines_create.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/pipelines_create.tsx
@@ -85,7 +85,7 @@ export const PipelinesCreate: React.FunctionComponent
,
]}
diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx
index a902f4a34af293..c927a324b0774d 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx
@@ -134,7 +134,7 @@ export const PipelinesEdit: React.FunctionComponent
- {
- const newMode = id.replace(idPrefix, '') as LineStyle;
- setConfig({ forAccessor: accessor, lineStyle: newMode });
- }}
- />
-
-
- {
- setConfig({ forAccessor: accessor, lineWidth: value });
- }}
- />
+
+
+ {
+ setConfig({ forAccessor: accessor, lineWidth: value });
+ }}
+ />
+
+
+ {
+ const newMode = id.replace(idPrefix, '') as LineStyle;
+ setConfig({ forAccessor: accessor, lineStyle: newMode });
+ }}
+ isIconOnly
+ />
+
+
>
);
@@ -108,11 +116,10 @@ const LineThicknessSlider = ({
const [unsafeValue, setUnsafeValue] = useState(String(value));
return (
- f.isScript);
- const runtimeFields = fields.filter((f) => f.runtimeField);
const result = await client.search(
{
index,
@@ -242,11 +246,7 @@ async function fetchIndexPatternStats({
sort: timeFieldName && fromDate && toDate ? [{ [timeFieldName]: 'desc' }] : [],
fields: ['*'],
_source: false,
- runtime_mappings: runtimeFields.reduce((acc, field) => {
- if (!field.runtimeField) return acc;
- acc[field.name] = field.runtimeField;
- return acc;
- }, {} as Record),
+ runtime_mappings: runtimeMappings,
script_fields: scriptedFields.reduce((acc, field) => {
acc[field.name] = {
script: {
diff --git a/x-pack/plugins/lens/server/routes/field_stats.ts b/x-pack/plugins/lens/server/routes/field_stats.ts
index 6c1a93759030a6..127d798cb76c2f 100644
--- a/x-pack/plugins/lens/server/routes/field_stats.ts
+++ b/x-pack/plugins/lens/server/routes/field_stats.ts
@@ -84,6 +84,7 @@ export async function initFieldsRoute(setup: CoreSetup) {
.filter((f) => f.runtimeField)
.reduce((acc, f) => {
if (!f.runtimeField) return acc;
+ // @ts-expect-error The MappingRuntimeField from @elastic/elasticsearch does not expose the "composite" runtime type yet
acc[f.name] = f.runtimeField;
return acc;
}, {} as Record);
diff --git a/x-pack/plugins/license_management/server/lib/license.ts b/x-pack/plugins/license_management/server/lib/license.ts
index 12f831f3d780a3..915d3a8b50a3d9 100644
--- a/x-pack/plugins/license_management/server/lib/license.ts
+++ b/x-pack/plugins/license_management/server/lib/license.ts
@@ -18,6 +18,7 @@ interface PutLicenseArg {
export async function putLicense({ acknowledge, client, licensing, license }: PutLicenseArg) {
try {
const response = await client.asCurrentUser.license.post({
+ // @ts-expect-error license is not typed in LM code
body: license,
acknowledge,
});
diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx b/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx
index 9079390cb301a4..206b1a5dd6f85f 100644
--- a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx
+++ b/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx
@@ -43,7 +43,7 @@ import { getEmptyValue } from '../../../common/empty_value';
import * as i18n from './translations';
-const MyValuesInput = styled(EuiFlexItem)`
+const FieldFlexItem = styled(EuiFlexItem)`
overflow: hidden;
`;
@@ -166,19 +166,22 @@ export const BuilderEntryItem: React.FC = ({
isDisabled={isDisabled || indexPattern == null}
onChange={handleFieldChange}
data-test-subj="exceptionBuilderEntryField"
- fieldInputWidth={275}
/>
);
if (isFirst) {
return (
-
+
{comboBox}
);
} else {
return (
-
+
{comboBox}
);
@@ -319,14 +322,14 @@ export const BuilderEntryItem: React.FC = ({
className="exceptionItemEntryContainer"
data-test-subj="exceptionItemEntryContainer"
>
- {renderFieldInput(showLabel)}
+ {renderFieldInput(showLabel)}
{renderOperatorInput(showLabel)}
-
+
{renderFieldValueInput(
showLabel,
entry.nested === 'parent' ? OperatorTypeEnum.EXISTS : entry.operator.type
)}
-
+