mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Cases] Add telemetry to the cases APIs (#125928)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
45a003fa06
commit
61fc407e90
35 changed files with 1123 additions and 832 deletions
|
@ -11,7 +11,8 @@
|
|||
"kibanaVersion":"kibana",
|
||||
"optionalPlugins":[
|
||||
"security",
|
||||
"spaces"
|
||||
"spaces",
|
||||
"usageCollection"
|
||||
],
|
||||
"owner":{
|
||||
"githubTeam":"response-ops",
|
||||
|
|
|
@ -25,7 +25,7 @@ import {
|
|||
getIDsAndIndicesAsArrays,
|
||||
} from '../../common/utils';
|
||||
import { createCaseError } from '../../common/error';
|
||||
import { defaultPage, defaultPerPage } from '../../routes/api';
|
||||
import { DEFAULT_PAGE, DEFAULT_PER_PAGE } from '../../routes/api';
|
||||
import { CasesClientArgs } from '../types';
|
||||
import { combineFilters, stringToKueryNode } from '../utils';
|
||||
import { Operations } from '../../authorization';
|
||||
|
@ -170,8 +170,8 @@ export async function find(
|
|||
// We need this because the default behavior of getAllCaseComments is to return all the comments
|
||||
// unless the page and/or perPage is specified. Since we're spreading the query after the request can
|
||||
// still override this behavior.
|
||||
page: defaultPage,
|
||||
perPage: defaultPerPage,
|
||||
page: DEFAULT_PAGE,
|
||||
perPage: DEFAULT_PER_PAGE,
|
||||
sortField: 'created_at',
|
||||
filter: combinedFilter,
|
||||
...queryWithoutFilter,
|
||||
|
@ -183,8 +183,8 @@ export async function find(
|
|||
unsecuredSavedObjectsClient,
|
||||
id,
|
||||
options: {
|
||||
page: defaultPage,
|
||||
perPage: defaultPerPage,
|
||||
page: DEFAULT_PAGE,
|
||||
perPage: DEFAULT_PER_PAGE,
|
||||
sortField: 'created_at',
|
||||
filter: combinedFilter,
|
||||
},
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { IContextProvider, KibanaRequest, Logger, PluginInitializerContext } from 'kibana/server';
|
||||
import { CoreSetup, CoreStart } from 'src/core/server';
|
||||
|
||||
import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/server';
|
||||
import { SecurityPluginSetup, SecurityPluginStart } from '../../security/server';
|
||||
import {
|
||||
PluginSetupContract as ActionsPluginSetup,
|
||||
|
@ -15,7 +16,6 @@ import {
|
|||
} from '../../actions/server';
|
||||
import { APP_ID } from '../common/constants';
|
||||
|
||||
import { initCaseApi } from './routes/api';
|
||||
import {
|
||||
createCaseCommentSavedObjectType,
|
||||
caseConfigureSavedObjectType,
|
||||
|
@ -30,11 +30,14 @@ import { CasesClientFactory } from './client/factory';
|
|||
import { SpacesPluginStart } from '../../spaces/server';
|
||||
import { PluginStartContract as FeaturesPluginStart } from '../../features/server';
|
||||
import { LensServerPluginSetup } from '../../lens/server';
|
||||
import { registerRoutes } from './routes/api/register_routes';
|
||||
import { getExternalRoutes } from './routes/api/get_external_routes';
|
||||
|
||||
export interface PluginsSetup {
|
||||
security?: SecurityPluginSetup;
|
||||
actions: ActionsPluginSetup;
|
||||
lens: LensServerPluginSetup;
|
||||
usageCollection?: UsageCollectionSetup;
|
||||
security?: SecurityPluginSetup;
|
||||
}
|
||||
|
||||
export interface PluginsStart {
|
||||
|
@ -100,10 +103,14 @@ export class CasePlugin {
|
|||
);
|
||||
|
||||
const router = core.http.createRouter<CasesRequestHandlerContext>();
|
||||
initCaseApi({
|
||||
logger: this.log,
|
||||
const telemetryUsageCounter = plugins.usageCollection?.createUsageCounter(APP_ID);
|
||||
|
||||
registerRoutes({
|
||||
router,
|
||||
routes: getExternalRoutes(),
|
||||
logger: this.log,
|
||||
kibanaVersion: this.kibanaVersion,
|
||||
telemetryUsageCounter,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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');
|
||||
const params = {
|
||||
params: schema.object({
|
||||
case_id: schema.string(),
|
||||
}),
|
||||
query: schema.object({
|
||||
/**
|
||||
* @deprecated since version 8.1.0
|
||||
*/
|
||||
includeComments: schema.boolean({ defaultValue: true }),
|
||||
}),
|
||||
};
|
||||
|
||||
if (isIncludeCommentsParamProvidedByTheUser) {
|
||||
logDeprecatedEndpoint(
|
||||
logger,
|
||||
request.headers,
|
||||
`The query parameter 'includeComments' of the get case API '${CASE_DETAILS_URL}' is deprecated`
|
||||
);
|
||||
}
|
||||
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');
|
||||
|
||||
const casesClient = await context.cases.getCasesClient();
|
||||
const id = request.params.case_id;
|
||||
|
||||
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(),
|
||||
}),
|
||||
query: schema.object({
|
||||
includeComments: schema.boolean({ defaultValue: true }),
|
||||
}),
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
try {
|
||||
const casesClient = await context.cases.getCasesClient();
|
||||
const id = request.params.case_id;
|
||||
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) {
|
||||
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));
|
||||
}
|
||||
return response.ok({
|
||||
...(isIncludeCommentsParamProvidedByTheUser && {
|
||||
headers: {
|
||||
...getWarningHeader(kibanaVersion, 'Deprecated query parameter includeComments'),
|
||||
},
|
||||
}),
|
||||
body: await casesClient.cases.get({
|
||||
id,
|
||||
includeComments: request.query.includeComments,
|
||||
}),
|
||||
});
|
||||
} catch (error) {
|
||||
throw createCaseError({
|
||||
message: `Failed to retrieve case in route case id: ${request.params.case_id} \ninclude comments: ${request.query.includeComments}: ${error}`,
|
||||
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,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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';
|
||||
import { CaseRoute } from '../types';
|
||||
import { createCaseError } from '../../../common/error';
|
||||
import { createCasesRoute } from '../create_cases_route';
|
||||
|
||||
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' });
|
||||
}
|
||||
export const pushCaseRoute: CaseRoute = createCasesRoute({
|
||||
method: 'post',
|
||||
path: CASE_PUSH_URL,
|
||||
handler: async ({ context, request, response }) => {
|
||||
try {
|
||||
const casesClient = await context.cases.getCasesClient();
|
||||
|
||||
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,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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(),
|
||||
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,
|
||||
}),
|
||||
query: escapeHatch,
|
||||
},
|
||||
},
|
||||
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));
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
throw createCaseError({
|
||||
message: `Failed to find comments in route case id: ${request.params.case_id}: ${error}`,
|
||||
error,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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(),
|
||||
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();
|
||||
|
||||
return response.ok({
|
||||
headers: {
|
||||
...getWarningHeader(kibanaVersion),
|
||||
},
|
||||
body: await client.attachments.getAll({
|
||||
caseID: request.params.case_id,
|
||||
}),
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
try {
|
||||
logDeprecatedEndpoint(
|
||||
logger,
|
||||
request.headers,
|
||||
`The get all cases comments API '${CASE_COMMENTS_URL}' is deprecated.`
|
||||
);
|
||||
|
||||
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));
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
throw createCaseError({
|
||||
message: `Failed to get all comments in route case id: ${request.params.case_id}: ${error}`,
|
||||
error,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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(),
|
||||
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,
|
||||
}),
|
||||
},
|
||||
},
|
||||
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));
|
||||
}
|
||||
});
|
||||
} 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,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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(),
|
||||
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();
|
||||
|
||||
return response.ok({
|
||||
body: await client.attachments.update({
|
||||
caseID: request.params.case_id,
|
||||
updateRequest: query,
|
||||
}),
|
||||
body: escapeHatch,
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
try {
|
||||
const query = pipe(
|
||||
CommentPatchRequestRt.decode(request.body),
|
||||
fold(throwErrors(Boom.badRequest), identity)
|
||||
);
|
||||
|
||||
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));
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
throw createCaseError({
|
||||
message: `Failed to patch comment in route case id: ${request.params.case_id}: ${error}`,
|
||||
error,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
10
x-pack/plugins/cases/server/routes/api/create_cases_route.ts
Normal file
10
x-pack/plugins/cases/server/routes/api/create_cases_route.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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 { CaseRoute } from './types';
|
||||
|
||||
export const createCasesRoute = <P, Q, B>(route: CaseRoute<P, Q, B>) => route;
|
|
@ -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[];
|
|
@ -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;
|
||||
|
|
|
@ -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 }),
|
||||
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,
|
||||
}),
|
||||
query: schema.object({
|
||||
features: schema.arrayOf(schema.string({ minLength: 1 })),
|
||||
}),
|
||||
},
|
||||
},
|
||||
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,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
280
x-pack/plugins/cases/server/routes/api/register_routes.test.ts
Normal file
280
x-pack/plugins/cases/server/routes/api/register_routes.test.ts
Normal file
|
@ -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<CasesRouter>;
|
||||
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<CasesRouter, 'get' | 'post'>;
|
||||
path: string;
|
||||
context?: Record<string, unknown>;
|
||||
headers?: Record<string, unknown>;
|
||||
}) => {
|
||||
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<string, unknown>]
|
||||
> = [
|
||||
['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',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
88
x-pack/plugins/cases/server/routes/api/register_routes.ts
Normal file
88
x-pack/plugins/cases/server/routes/api/register_routes.ts
Normal file
|
@ -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<RegisterRoutesDeps['telemetryUsageCounter'], undefined>;
|
||||
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<typeof method, CasesRequestHandlerContext>)(
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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<UsageCollectionSetup['createUsageCounter']>;
|
||||
|
||||
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<P, Q, B> {
|
||||
request: KibanaRequest<P, Q, B>;
|
||||
context: CasesRequestHandlerContext;
|
||||
response: KibanaResponseFactory;
|
||||
logger: Logger;
|
||||
kibanaVersion: PluginInitializerContext['env']['packageInfo']['version'];
|
||||
}
|
||||
|
||||
export interface CaseRoute<P = unknown, Q = unknown, B = unknown> {
|
||||
method: 'get' | 'post' | 'put' | 'delete' | 'patch';
|
||||
path: string;
|
||||
params?: RouteValidatorConfig<P, Q, B>;
|
||||
handler: (args: CaseRouteHandlerArguments<P, Q, B>) => Promise<IKibanaResponse>;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue