mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[SIEM] [Case] Case workflow api schema (#51535)
This commit is contained in:
parent
26ce6104a9
commit
303e4842ea
38 changed files with 1909 additions and 0 deletions
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
import {
|
||||
NewCaseFormatted,
|
||||
NewCommentFormatted,
|
||||
} from '../../../../../../../x-pack/plugins/case/server';
|
||||
import { ElasticsearchMappingOf } from '../../utils/typed_elasticsearch_mappings';
|
||||
|
||||
// Temporary file to write mappings for case
|
||||
// while Saved Object Mappings API is programmed for the NP
|
||||
// See: https://github.com/elastic/kibana/issues/50309
|
||||
|
||||
export const caseSavedObjectType = 'case-workflow';
|
||||
export const caseCommentSavedObjectType = 'case-workflow-comment';
|
||||
|
||||
export const caseSavedObjectMappings: {
|
||||
[caseSavedObjectType]: ElasticsearchMappingOf<NewCaseFormatted>;
|
||||
} = {
|
||||
[caseSavedObjectType]: {
|
||||
properties: {
|
||||
assignees: {
|
||||
properties: {
|
||||
username: {
|
||||
type: 'keyword',
|
||||
},
|
||||
full_name: {
|
||||
type: 'keyword',
|
||||
},
|
||||
},
|
||||
},
|
||||
created_at: {
|
||||
type: 'date',
|
||||
},
|
||||
description: {
|
||||
type: 'text',
|
||||
},
|
||||
title: {
|
||||
type: 'keyword',
|
||||
},
|
||||
created_by: {
|
||||
properties: {
|
||||
username: {
|
||||
type: 'keyword',
|
||||
},
|
||||
full_name: {
|
||||
type: 'keyword',
|
||||
},
|
||||
},
|
||||
},
|
||||
state: {
|
||||
type: 'keyword',
|
||||
},
|
||||
tags: {
|
||||
type: 'keyword',
|
||||
},
|
||||
case_type: {
|
||||
type: 'keyword',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const caseCommentSavedObjectMappings: {
|
||||
[caseCommentSavedObjectType]: ElasticsearchMappingOf<NewCommentFormatted>;
|
||||
} = {
|
||||
[caseCommentSavedObjectType]: {
|
||||
properties: {
|
||||
comment: {
|
||||
type: 'text',
|
||||
},
|
||||
created_at: {
|
||||
type: 'date',
|
||||
},
|
||||
created_by: {
|
||||
properties: {
|
||||
full_name: {
|
||||
type: 'keyword',
|
||||
},
|
||||
username: {
|
||||
type: 'keyword',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
9
x-pack/plugins/case/README.md
Normal file
9
x-pack/plugins/case/README.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Case Workflow
|
||||
|
||||
*Experimental Feature*
|
||||
|
||||
Elastic is developing a Case Management Workflow. Follow our progress:
|
||||
|
||||
- [Case API Documentation](https://documenter.getpostman.com/view/172706/SW7c2SuF?version=latest)
|
||||
- [Github Meta](https://github.com/elastic/kibana/issues/50103)
|
||||
|
9
x-pack/plugins/case/kibana.json
Normal file
9
x-pack/plugins/case/kibana.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"configPath": ["xpack", "case"],
|
||||
"id": "case",
|
||||
"kibanaVersion": "kibana",
|
||||
"requiredPlugins": ["security"],
|
||||
"server": true,
|
||||
"ui": false,
|
||||
"version": "8.0.0"
|
||||
}
|
15
x-pack/plugins/case/server/config.ts
Normal file
15
x-pack/plugins/case/server/config.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { schema, TypeOf } from '@kbn/config-schema';
|
||||
|
||||
export const ConfigSchema = schema.object({
|
||||
enabled: schema.boolean({ defaultValue: false }),
|
||||
indexPattern: schema.string({ defaultValue: '.case-test-2' }),
|
||||
secret: schema.string({ defaultValue: 'Cool secret huh?' }),
|
||||
});
|
||||
|
||||
export type ConfigType = TypeOf<typeof ConfigSchema>;
|
8
x-pack/plugins/case/server/constants.ts
Normal file
8
x-pack/plugins/case/server/constants.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const CASE_SAVED_OBJECT = 'case-workflow';
|
||||
export const CASE_COMMENT_SAVED_OBJECT = 'case-workflow-comment';
|
14
x-pack/plugins/case/server/index.ts
Normal file
14
x-pack/plugins/case/server/index.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { PluginInitializerContext } from '../../../../src/core/server';
|
||||
import { ConfigSchema } from './config';
|
||||
import { CasePlugin } from './plugin';
|
||||
export { NewCaseFormatted, NewCommentFormatted } from './routes/api/types';
|
||||
|
||||
export const config = { schema: ConfigSchema };
|
||||
export const plugin = (initializerContext: PluginInitializerContext) =>
|
||||
new CasePlugin(initializerContext);
|
63
x-pack/plugins/case/server/plugin.ts
Normal file
63
x-pack/plugins/case/server/plugin.ts
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { first, map } from 'rxjs/operators';
|
||||
import { CoreSetup, Logger, PluginInitializerContext } from 'kibana/server';
|
||||
import { ConfigType } from './config';
|
||||
import { initCaseApi } from './routes/api';
|
||||
import { CaseService } from './services';
|
||||
import { PluginSetupContract as SecurityPluginSetup } from '../../security/server';
|
||||
|
||||
function createConfig$(context: PluginInitializerContext) {
|
||||
return context.config.create<ConfigType>().pipe(map(config => config));
|
||||
}
|
||||
|
||||
export interface PluginsSetup {
|
||||
security: SecurityPluginSetup;
|
||||
}
|
||||
|
||||
export class CasePlugin {
|
||||
private readonly log: Logger;
|
||||
|
||||
constructor(private readonly initializerContext: PluginInitializerContext) {
|
||||
this.log = this.initializerContext.logger.get();
|
||||
}
|
||||
|
||||
public async setup(core: CoreSetup, plugins: PluginsSetup) {
|
||||
const config = await createConfig$(this.initializerContext)
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
if (!config.enabled) {
|
||||
return;
|
||||
}
|
||||
const service = new CaseService(this.log);
|
||||
|
||||
this.log.debug(
|
||||
`Setting up Case Workflow with core contract [${Object.keys(
|
||||
core
|
||||
)}] and plugins [${Object.keys(plugins)}]`
|
||||
);
|
||||
|
||||
const caseService = await service.setup({
|
||||
authentication: plugins.security.authc,
|
||||
});
|
||||
|
||||
const router = core.http.createRouter();
|
||||
initCaseApi({
|
||||
caseService,
|
||||
router,
|
||||
});
|
||||
}
|
||||
|
||||
public start() {
|
||||
this.log.debug(`Starting Case Workflow`);
|
||||
}
|
||||
|
||||
public stop() {
|
||||
this.log.debug(`Stopping Case Workflow`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { Authentication } from '../../../../../security/server';
|
||||
|
||||
const getCurrentUser = jest.fn().mockReturnValue({
|
||||
username: 'awesome',
|
||||
full_name: 'Awesome D00d',
|
||||
});
|
||||
const getCurrentUserThrow = jest.fn().mockImplementation(() => {
|
||||
throw new Error('Bad User - the user is not authenticated');
|
||||
});
|
||||
|
||||
export const authenticationMock = {
|
||||
create: (): jest.Mocked<Authentication> => ({
|
||||
login: jest.fn(),
|
||||
createAPIKey: jest.fn(),
|
||||
getCurrentUser,
|
||||
invalidateAPIKey: jest.fn(),
|
||||
isAuthenticated: jest.fn(),
|
||||
logout: jest.fn(),
|
||||
getSessionInfo: jest.fn(),
|
||||
}),
|
||||
createInvalid: (): jest.Mocked<Authentication> => ({
|
||||
login: jest.fn(),
|
||||
createAPIKey: jest.fn(),
|
||||
getCurrentUser: getCurrentUserThrow,
|
||||
invalidateAPIKey: jest.fn(),
|
||||
isAuthenticated: jest.fn(),
|
||||
logout: jest.fn(),
|
||||
getSessionInfo: jest.fn(),
|
||||
}),
|
||||
};
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { SavedObjectsClientContract, SavedObjectsErrorHelpers } from 'src/core/server';
|
||||
import { CASE_COMMENT_SAVED_OBJECT } from '../../../constants';
|
||||
|
||||
export const createMockSavedObjectsRepository = (savedObject: any[] = []) => {
|
||||
const mockSavedObjectsClientContract = ({
|
||||
get: jest.fn((type, id) => {
|
||||
const result = savedObject.filter(s => s.id === id);
|
||||
if (!result.length) {
|
||||
throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
|
||||
}
|
||||
return result[0];
|
||||
}),
|
||||
find: jest.fn(findArgs => {
|
||||
if (findArgs.hasReference && findArgs.hasReference.id === 'bad-guy') {
|
||||
throw SavedObjectsErrorHelpers.createBadRequestError('Error thrown for testing');
|
||||
}
|
||||
return {
|
||||
total: savedObject.length,
|
||||
saved_objects: savedObject,
|
||||
};
|
||||
}),
|
||||
create: jest.fn((type, attributes, references) => {
|
||||
if (attributes.description === 'Throw an error' || attributes.comment === 'Throw an error') {
|
||||
throw SavedObjectsErrorHelpers.createBadRequestError('Error thrown for testing');
|
||||
}
|
||||
if (type === CASE_COMMENT_SAVED_OBJECT) {
|
||||
return {
|
||||
type,
|
||||
id: 'mock-comment',
|
||||
attributes,
|
||||
...references,
|
||||
updated_at: '2019-12-02T22:48:08.327Z',
|
||||
version: 'WzksMV0=',
|
||||
};
|
||||
}
|
||||
return {
|
||||
type,
|
||||
id: 'mock-it',
|
||||
attributes,
|
||||
references: [],
|
||||
updated_at: '2019-12-02T22:48:08.327Z',
|
||||
version: 'WzksMV0=',
|
||||
};
|
||||
}),
|
||||
update: jest.fn((type, id, attributes) => {
|
||||
if (!savedObject.find(s => s.id === id)) {
|
||||
throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
|
||||
}
|
||||
return {
|
||||
id,
|
||||
type,
|
||||
updated_at: '2019-11-22T22:50:55.191Z',
|
||||
version: 'WzE3LDFd',
|
||||
attributes,
|
||||
};
|
||||
}),
|
||||
delete: jest.fn((type: string, id: string) => {
|
||||
const result = savedObject.filter(s => s.id === id);
|
||||
if (!result.length) {
|
||||
throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
|
||||
}
|
||||
if (type === 'case-workflow-comment' && id === 'bad-guy') {
|
||||
throw SavedObjectsErrorHelpers.createBadRequestError('Error thrown for testing');
|
||||
}
|
||||
return {};
|
||||
}),
|
||||
deleteByNamespace: jest.fn(),
|
||||
} as unknown) as jest.Mocked<SavedObjectsClientContract>;
|
||||
|
||||
return mockSavedObjectsClientContract;
|
||||
};
|
11
x-pack/plugins/case/server/routes/api/__fixtures__/index.ts
Normal file
11
x-pack/plugins/case/server/routes/api/__fixtures__/index.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { mockCases, mockCasesErrorTriggerData, mockCaseComments } from './mock_saved_objects';
|
||||
export { createMockSavedObjectsRepository } from './create_mock_so_repository';
|
||||
export { createRouteContext } from './route_contexts';
|
||||
export { authenticationMock } from './authc_mock';
|
||||
export { createRoute } from './mock_router';
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { IRouter } from 'kibana/server';
|
||||
import { loggingServiceMock, httpServiceMock } from '../../../../../../../src/core/server/mocks';
|
||||
import { CaseService } from '../../../services';
|
||||
import { authenticationMock } from '../__fixtures__';
|
||||
import { RouteDeps } from '../index';
|
||||
|
||||
export const createRoute = async (
|
||||
api: (deps: RouteDeps) => void,
|
||||
method: 'get' | 'post' | 'delete',
|
||||
badAuth = false
|
||||
) => {
|
||||
const httpService = httpServiceMock.createSetupContract();
|
||||
const router = httpService.createRouter('') as jest.Mocked<IRouter>;
|
||||
|
||||
const log = loggingServiceMock.create().get('case');
|
||||
|
||||
const service = new CaseService(log);
|
||||
const caseService = await service.setup({
|
||||
authentication: badAuth ? authenticationMock.createInvalid() : authenticationMock.create(),
|
||||
});
|
||||
|
||||
api({
|
||||
router,
|
||||
caseService,
|
||||
});
|
||||
|
||||
return router[method].mock.calls[0][1];
|
||||
};
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const mockCases = [
|
||||
{
|
||||
type: 'case-workflow',
|
||||
id: 'mock-id-1',
|
||||
attributes: {
|
||||
created_at: 1574718888885,
|
||||
created_by: {
|
||||
full_name: null,
|
||||
username: 'elastic',
|
||||
},
|
||||
description: 'This is a brand new case of a bad meanie defacing data',
|
||||
title: 'Super Bad Security Issue',
|
||||
state: 'open',
|
||||
tags: ['defacement'],
|
||||
case_type: 'security',
|
||||
assignees: [],
|
||||
},
|
||||
references: [],
|
||||
updated_at: '2019-11-25T21:54:48.952Z',
|
||||
version: 'WzAsMV0=',
|
||||
},
|
||||
{
|
||||
type: 'case-workflow',
|
||||
id: 'mock-id-2',
|
||||
attributes: {
|
||||
created_at: 1574721120834,
|
||||
created_by: {
|
||||
full_name: null,
|
||||
username: 'elastic',
|
||||
},
|
||||
description: 'Oh no, a bad meanie destroying data!',
|
||||
title: 'Damaging Data Destruction Detected',
|
||||
state: 'open',
|
||||
tags: ['Data Destruction'],
|
||||
case_type: 'security',
|
||||
assignees: [],
|
||||
},
|
||||
references: [],
|
||||
updated_at: '2019-11-25T22:32:00.900Z',
|
||||
version: 'WzQsMV0=',
|
||||
},
|
||||
{
|
||||
type: 'case-workflow',
|
||||
id: 'mock-id-3',
|
||||
attributes: {
|
||||
created_at: 1574721137881,
|
||||
created_by: {
|
||||
full_name: null,
|
||||
username: 'elastic',
|
||||
},
|
||||
description: 'Oh no, a bad meanie going LOLBins all over the place!',
|
||||
title: 'Another bad one',
|
||||
state: 'open',
|
||||
tags: ['LOLBins'],
|
||||
case_type: 'security',
|
||||
assignees: [],
|
||||
},
|
||||
references: [],
|
||||
updated_at: '2019-11-25T22:32:17.947Z',
|
||||
version: 'WzUsMV0=',
|
||||
},
|
||||
];
|
||||
|
||||
export const mockCasesErrorTriggerData = [
|
||||
{
|
||||
id: 'valid-id',
|
||||
},
|
||||
{
|
||||
id: 'bad-guy',
|
||||
},
|
||||
];
|
||||
|
||||
export const mockCaseComments = [
|
||||
{
|
||||
type: 'case-workflow-comment',
|
||||
id: 'mock-comment-1',
|
||||
attributes: {
|
||||
comment: 'Wow, good luck catching that bad meanie!',
|
||||
created_at: 1574718900112,
|
||||
created_by: {
|
||||
full_name: null,
|
||||
username: 'elastic',
|
||||
},
|
||||
},
|
||||
references: [
|
||||
{
|
||||
type: 'case-workflow',
|
||||
name: 'associated-case-workflow',
|
||||
id: 'mock-id-1',
|
||||
},
|
||||
],
|
||||
updated_at: '2019-11-25T21:55:00.177Z',
|
||||
version: 'WzEsMV0=',
|
||||
},
|
||||
{
|
||||
type: 'case-workflow-comment',
|
||||
id: 'mock-comment-2',
|
||||
attributes: {
|
||||
comment: 'Well I decided to update my comment. So what? Deal with it.',
|
||||
created_at: 1574718902724,
|
||||
created_by: {
|
||||
full_name: null,
|
||||
username: 'elastic',
|
||||
},
|
||||
},
|
||||
references: [
|
||||
{
|
||||
type: 'case-workflow',
|
||||
name: 'associated-case-workflow',
|
||||
id: 'mock-id-1',
|
||||
},
|
||||
],
|
||||
updated_at: '2019-11-25T21:55:14.633Z',
|
||||
version: 'WzMsMV0=',
|
||||
},
|
||||
{
|
||||
type: 'case-workflow-comment',
|
||||
id: 'mock-comment-3',
|
||||
attributes: {
|
||||
comment: 'Wow, good luck catching that bad meanie!',
|
||||
created_at: 1574721150542,
|
||||
created_by: {
|
||||
full_name: null,
|
||||
username: 'elastic',
|
||||
},
|
||||
},
|
||||
references: [
|
||||
{
|
||||
type: 'case-workflow',
|
||||
name: 'associated-case-workflow',
|
||||
id: 'mock-id-3',
|
||||
},
|
||||
],
|
||||
updated_at: '2019-11-25T22:32:30.608Z',
|
||||
version: 'WzYsMV0=',
|
||||
},
|
||||
];
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { RequestHandlerContext } from 'src/core/server';
|
||||
|
||||
export const createRouteContext = (client: any) => {
|
||||
return ({
|
||||
core: {
|
||||
savedObjects: {
|
||||
client,
|
||||
},
|
||||
},
|
||||
} as unknown) as RequestHandlerContext;
|
||||
};
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import {
|
||||
createMockSavedObjectsRepository,
|
||||
createRoute,
|
||||
createRouteContext,
|
||||
mockCases,
|
||||
mockCasesErrorTriggerData,
|
||||
} from '../__fixtures__';
|
||||
import { initDeleteCaseApi } from '../delete_case';
|
||||
import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
import { httpServerMock } from 'src/core/server/mocks';
|
||||
|
||||
describe('DELETE case', () => {
|
||||
let routeHandler: RequestHandler<any, any, any>;
|
||||
beforeAll(async () => {
|
||||
routeHandler = await createRoute(initDeleteCaseApi, 'delete');
|
||||
});
|
||||
it(`deletes the case. responds with 204`, async () => {
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases/{id}',
|
||||
method: 'delete',
|
||||
params: {
|
||||
id: 'mock-id-1',
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(createMockSavedObjectsRepository(mockCases));
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
expect(response.status).toEqual(204);
|
||||
});
|
||||
it(`returns an error when thrown from deleteCase service`, async () => {
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases/{id}',
|
||||
method: 'delete',
|
||||
params: {
|
||||
id: 'not-real',
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(createMockSavedObjectsRepository(mockCases));
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
expect(response.status).toEqual(404);
|
||||
});
|
||||
it(`returns an error when thrown from getAllCaseComments service`, async () => {
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases/{id}',
|
||||
method: 'delete',
|
||||
params: {
|
||||
id: 'bad-guy',
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(
|
||||
createMockSavedObjectsRepository(mockCasesErrorTriggerData)
|
||||
);
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
expect(response.status).toEqual(400);
|
||||
});
|
||||
it(`returns an error when thrown from deleteComment service`, async () => {
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases/{id}',
|
||||
method: 'delete',
|
||||
params: {
|
||||
id: 'valid-id',
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(
|
||||
createMockSavedObjectsRepository(mockCasesErrorTriggerData)
|
||||
);
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
expect(response.status).toEqual(400);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import {
|
||||
createMockSavedObjectsRepository,
|
||||
createRoute,
|
||||
createRouteContext,
|
||||
mockCases,
|
||||
mockCasesErrorTriggerData,
|
||||
} from '../__fixtures__';
|
||||
import { initDeleteCommentApi } from '../delete_comment';
|
||||
import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
import { httpServerMock } from 'src/core/server/mocks';
|
||||
|
||||
describe('DELETE comment', () => {
|
||||
let routeHandler: RequestHandler<any, any, any>;
|
||||
beforeAll(async () => {
|
||||
routeHandler = await createRoute(initDeleteCommentApi, 'delete');
|
||||
});
|
||||
it(`deletes the comment. responds with 204`, async () => {
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases/comments/{comment_id}',
|
||||
method: 'delete',
|
||||
params: {
|
||||
comment_id: 'mock-id-1',
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(createMockSavedObjectsRepository(mockCases));
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
expect(response.status).toEqual(204);
|
||||
});
|
||||
it(`returns an error when thrown from deleteComment service`, async () => {
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases/comments/{comment_id}',
|
||||
method: 'delete',
|
||||
params: {
|
||||
comment_id: 'bad-guy',
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(
|
||||
createMockSavedObjectsRepository(mockCasesErrorTriggerData)
|
||||
);
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
expect(response.status).toEqual(400);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import {
|
||||
createMockSavedObjectsRepository,
|
||||
createRoute,
|
||||
createRouteContext,
|
||||
mockCases,
|
||||
} from '../__fixtures__';
|
||||
import { initGetAllCasesApi } from '../get_all_cases';
|
||||
import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
import { httpServerMock } from 'src/core/server/mocks';
|
||||
|
||||
describe('GET all cases', () => {
|
||||
let routeHandler: RequestHandler<any, any, any>;
|
||||
beforeAll(async () => {
|
||||
routeHandler = await createRoute(initGetAllCasesApi, 'get');
|
||||
});
|
||||
it(`returns the case without case comments when includeComments is false`, async () => {
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases',
|
||||
method: 'get',
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(createMockSavedObjectsRepository(mockCases));
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.payload.saved_objects).toHaveLength(3);
|
||||
});
|
||||
});
|
101
x-pack/plugins/case/server/routes/api/__tests__/get_case.test.ts
Normal file
101
x-pack/plugins/case/server/routes/api/__tests__/get_case.test.ts
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import {
|
||||
createMockSavedObjectsRepository,
|
||||
createRoute,
|
||||
createRouteContext,
|
||||
mockCases,
|
||||
mockCasesErrorTriggerData,
|
||||
} from '../__fixtures__';
|
||||
import { initGetCaseApi } from '../get_case';
|
||||
import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
import { httpServerMock } from 'src/core/server/mocks';
|
||||
|
||||
describe('GET case', () => {
|
||||
let routeHandler: RequestHandler<any, any, any>;
|
||||
beforeAll(async () => {
|
||||
routeHandler = await createRoute(initGetCaseApi, 'get');
|
||||
});
|
||||
it(`returns the case without case comments when includeComments is false`, async () => {
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases/{id}',
|
||||
params: {
|
||||
id: 'mock-id-1',
|
||||
},
|
||||
method: 'get',
|
||||
query: {
|
||||
includeComments: false,
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(createMockSavedObjectsRepository(mockCases));
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.payload).toEqual(mockCases.find(s => s.id === 'mock-id-1'));
|
||||
expect(response.payload.comments).toBeUndefined();
|
||||
});
|
||||
it(`returns an error when thrown from getCase`, async () => {
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases/{id}',
|
||||
params: {
|
||||
id: 'abcdefg',
|
||||
},
|
||||
method: 'get',
|
||||
query: {
|
||||
includeComments: false,
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(createMockSavedObjectsRepository(mockCases));
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
|
||||
expect(response.status).toEqual(404);
|
||||
expect(response.payload.isBoom).toEqual(true);
|
||||
});
|
||||
it(`returns the case with case comments when includeComments is true`, async () => {
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases/{id}',
|
||||
params: {
|
||||
id: 'mock-id-1',
|
||||
},
|
||||
method: 'get',
|
||||
query: {
|
||||
includeComments: true,
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(createMockSavedObjectsRepository(mockCases));
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.payload.comments.saved_objects).toHaveLength(3);
|
||||
});
|
||||
it(`returns an error when thrown from getAllCaseComments`, async () => {
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases/{id}',
|
||||
params: {
|
||||
id: 'bad-guy',
|
||||
},
|
||||
method: 'get',
|
||||
query: {
|
||||
includeComments: true,
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(
|
||||
createMockSavedObjectsRepository(mockCasesErrorTriggerData)
|
||||
);
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
|
||||
expect(response.status).toEqual(400);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import {
|
||||
createMockSavedObjectsRepository,
|
||||
createRoute,
|
||||
createRouteContext,
|
||||
mockCaseComments,
|
||||
} from '../__fixtures__';
|
||||
import { initGetCommentApi } from '../get_comment';
|
||||
import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
import { httpServerMock } from 'src/core/server/mocks';
|
||||
|
||||
describe('GET comment', () => {
|
||||
let routeHandler: RequestHandler<any, any, any>;
|
||||
beforeAll(async () => {
|
||||
routeHandler = await createRoute(initGetCommentApi, 'get');
|
||||
});
|
||||
it(`returns the comment`, async () => {
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases/comments/{id}',
|
||||
method: 'get',
|
||||
params: {
|
||||
id: 'mock-comment-1',
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(createMockSavedObjectsRepository(mockCaseComments));
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.payload).toEqual(mockCaseComments.find(s => s.id === 'mock-comment-1'));
|
||||
});
|
||||
it(`returns an error when getComment throws`, async () => {
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases/comments/{id}',
|
||||
method: 'get',
|
||||
params: {
|
||||
id: 'not-real',
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(createMockSavedObjectsRepository(mockCaseComments));
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
expect(response.status).toEqual(404);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import {
|
||||
createMockSavedObjectsRepository,
|
||||
createRoute,
|
||||
createRouteContext,
|
||||
mockCases,
|
||||
} from '../__fixtures__';
|
||||
import { initPostCaseApi } from '../post_case';
|
||||
import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
import { httpServerMock } from 'src/core/server/mocks';
|
||||
|
||||
describe('POST cases', () => {
|
||||
let routeHandler: RequestHandler<any, any, any>;
|
||||
beforeAll(async () => {
|
||||
routeHandler = await createRoute(initPostCaseApi, 'post');
|
||||
});
|
||||
it(`Posts a new case`, async () => {
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases',
|
||||
method: 'post',
|
||||
body: {
|
||||
description: 'This is a brand new case of a bad meanie defacing data',
|
||||
title: 'Super Bad Security Issue',
|
||||
state: 'open',
|
||||
tags: ['defacement'],
|
||||
case_type: 'security',
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(createMockSavedObjectsRepository(mockCases));
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.payload.id).toEqual('mock-it');
|
||||
expect(response.payload.attributes.created_by.username).toEqual('awesome');
|
||||
});
|
||||
it(`Returns an error if postNewCase throws`, async () => {
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases',
|
||||
method: 'post',
|
||||
body: {
|
||||
description: 'Throw an error',
|
||||
title: 'Super Bad Security Issue',
|
||||
state: 'open',
|
||||
tags: ['error'],
|
||||
case_type: 'security',
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(createMockSavedObjectsRepository(mockCases));
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
expect(response.status).toEqual(400);
|
||||
expect(response.payload.isBoom).toEqual(true);
|
||||
});
|
||||
it(`Returns an error if user authentication throws`, async () => {
|
||||
routeHandler = await createRoute(initPostCaseApi, 'post', true);
|
||||
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases',
|
||||
method: 'post',
|
||||
body: {
|
||||
description: 'This is a brand new case of a bad meanie defacing data',
|
||||
title: 'Super Bad Security Issue',
|
||||
state: 'open',
|
||||
tags: ['defacement'],
|
||||
case_type: 'security',
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(createMockSavedObjectsRepository(mockCases));
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
expect(response.status).toEqual(500);
|
||||
expect(response.payload.isBoom).toEqual(true);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import {
|
||||
createMockSavedObjectsRepository,
|
||||
createRoute,
|
||||
createRouteContext,
|
||||
mockCases,
|
||||
} from '../__fixtures__';
|
||||
import { initPostCommentApi } from '../post_comment';
|
||||
import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
import { httpServerMock } from 'src/core/server/mocks';
|
||||
|
||||
describe('POST comment', () => {
|
||||
let routeHandler: RequestHandler<any, any, any>;
|
||||
beforeAll(async () => {
|
||||
routeHandler = await createRoute(initPostCommentApi, 'post');
|
||||
});
|
||||
it(`Posts a new comment`, async () => {
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases/{id}/comment',
|
||||
method: 'post',
|
||||
params: {
|
||||
id: 'mock-id-1',
|
||||
},
|
||||
body: {
|
||||
comment: 'Wow, good luck catching that bad meanie!',
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(createMockSavedObjectsRepository(mockCases));
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.payload.id).toEqual('mock-comment');
|
||||
expect(response.payload.references[0].id).toEqual('mock-id-1');
|
||||
});
|
||||
it(`Returns an error if the case does not exist`, async () => {
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases/{id}/comment',
|
||||
method: 'post',
|
||||
params: {
|
||||
id: 'this-is-not-real',
|
||||
},
|
||||
body: {
|
||||
comment: 'Wow, good luck catching that bad meanie!',
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(createMockSavedObjectsRepository(mockCases));
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
expect(response.status).toEqual(404);
|
||||
expect(response.payload.isBoom).toEqual(true);
|
||||
});
|
||||
it(`Returns an error if postNewCase throws`, async () => {
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases/{id}/comment',
|
||||
method: 'post',
|
||||
params: {
|
||||
id: 'mock-id-1',
|
||||
},
|
||||
body: {
|
||||
comment: 'Throw an error',
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(createMockSavedObjectsRepository(mockCases));
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
expect(response.status).toEqual(400);
|
||||
expect(response.payload.isBoom).toEqual(true);
|
||||
});
|
||||
it(`Returns an error if user authentication throws`, async () => {
|
||||
routeHandler = await createRoute(initPostCommentApi, 'post', true);
|
||||
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases/{id}/comment',
|
||||
method: 'post',
|
||||
params: {
|
||||
id: 'mock-id-1',
|
||||
},
|
||||
body: {
|
||||
comment: 'Wow, good luck catching that bad meanie!',
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(createMockSavedObjectsRepository(mockCases));
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
expect(response.status).toEqual(500);
|
||||
expect(response.payload.isBoom).toEqual(true);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import {
|
||||
createMockSavedObjectsRepository,
|
||||
createRoute,
|
||||
createRouteContext,
|
||||
mockCases,
|
||||
} from '../__fixtures__';
|
||||
import { initUpdateCaseApi } from '../update_case';
|
||||
import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
import { httpServerMock } from 'src/core/server/mocks';
|
||||
|
||||
describe('UPDATE case', () => {
|
||||
let routeHandler: RequestHandler<any, any, any>;
|
||||
beforeAll(async () => {
|
||||
routeHandler = await createRoute(initUpdateCaseApi, 'post');
|
||||
});
|
||||
it(`Updates a case`, async () => {
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases/{id}',
|
||||
method: 'post',
|
||||
params: {
|
||||
id: 'mock-id-1',
|
||||
},
|
||||
body: {
|
||||
state: 'closed',
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(createMockSavedObjectsRepository(mockCases));
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.payload.id).toEqual('mock-id-1');
|
||||
expect(response.payload.attributes.state).toEqual('closed');
|
||||
});
|
||||
it(`Returns an error if updateCase throws`, async () => {
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases/{id}',
|
||||
method: 'post',
|
||||
params: {
|
||||
id: 'mock-id-does-not-exist',
|
||||
},
|
||||
body: {
|
||||
state: 'closed',
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(createMockSavedObjectsRepository(mockCases));
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
expect(response.status).toEqual(404);
|
||||
expect(response.payload.isBoom).toEqual(true);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import {
|
||||
createMockSavedObjectsRepository,
|
||||
createRoute,
|
||||
createRouteContext,
|
||||
mockCaseComments,
|
||||
} from '../__fixtures__';
|
||||
import { initUpdateCommentApi } from '../update_comment';
|
||||
import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
import { httpServerMock } from 'src/core/server/mocks';
|
||||
|
||||
describe('UPDATE comment', () => {
|
||||
let routeHandler: RequestHandler<any, any, any>;
|
||||
beforeAll(async () => {
|
||||
routeHandler = await createRoute(initUpdateCommentApi, 'post');
|
||||
});
|
||||
it(`Updates a comment`, async () => {
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases/comment/{id}',
|
||||
method: 'post',
|
||||
params: {
|
||||
id: 'mock-comment-1',
|
||||
},
|
||||
body: {
|
||||
comment: 'Update my comment',
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(createMockSavedObjectsRepository(mockCaseComments));
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.payload.id).toEqual('mock-comment-1');
|
||||
expect(response.payload.attributes.comment).toEqual('Update my comment');
|
||||
});
|
||||
it(`Returns an error if updateComment throws`, async () => {
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
path: '/api/cases/comment/{id}',
|
||||
method: 'post',
|
||||
params: {
|
||||
id: 'mock-comment-does-not-exist',
|
||||
},
|
||||
body: {
|
||||
comment: 'Update my comment',
|
||||
},
|
||||
});
|
||||
|
||||
const theContext = createRouteContext(createMockSavedObjectsRepository(mockCaseComments));
|
||||
|
||||
const response = await routeHandler(theContext, request, kibanaResponseFactory);
|
||||
expect(response.status).toEqual(404);
|
||||
expect(response.payload.isBoom).toEqual(true);
|
||||
});
|
||||
});
|
56
x-pack/plugins/case/server/routes/api/delete_case.ts
Normal file
56
x-pack/plugins/case/server/routes/api/delete_case.ts
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { RouteDeps } from '.';
|
||||
import { wrapError } from './utils';
|
||||
|
||||
export function initDeleteCaseApi({ caseService, router }: RouteDeps) {
|
||||
router.delete(
|
||||
{
|
||||
path: '/api/cases/{id}',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
id: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
let allCaseComments;
|
||||
try {
|
||||
await caseService.deleteCase({
|
||||
client: context.core.savedObjects.client,
|
||||
caseId: request.params.id,
|
||||
});
|
||||
} catch (error) {
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
try {
|
||||
allCaseComments = await caseService.getAllCaseComments({
|
||||
client: context.core.savedObjects.client,
|
||||
caseId: request.params.id,
|
||||
});
|
||||
} catch (error) {
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
try {
|
||||
if (allCaseComments.saved_objects.length > 0) {
|
||||
await Promise.all(
|
||||
allCaseComments.saved_objects.map(({ id }) =>
|
||||
caseService.deleteComment({
|
||||
client: context.core.savedObjects.client,
|
||||
commentId: id,
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
return response.noContent();
|
||||
} catch (error) {
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
34
x-pack/plugins/case/server/routes/api/delete_comment.ts
Normal file
34
x-pack/plugins/case/server/routes/api/delete_comment.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { RouteDeps } from '.';
|
||||
import { wrapError } from './utils';
|
||||
|
||||
export function initDeleteCommentApi({ caseService, router }: RouteDeps) {
|
||||
router.delete(
|
||||
{
|
||||
path: '/api/cases/comments/{comment_id}',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
comment_id: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
const client = context.core.savedObjects.client;
|
||||
try {
|
||||
await caseService.deleteComment({
|
||||
client,
|
||||
commentId: request.params.comment_id,
|
||||
});
|
||||
return response.noContent();
|
||||
} catch (error) {
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { RouteDeps } from '.';
|
||||
import { wrapError } from './utils';
|
||||
|
||||
export function initGetAllCaseCommentsApi({ caseService, router }: RouteDeps) {
|
||||
router.get(
|
||||
{
|
||||
path: '/api/cases/{id}/comments',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
id: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
try {
|
||||
const theComments = await caseService.getAllCaseComments({
|
||||
client: context.core.savedObjects.client,
|
||||
caseId: request.params.id,
|
||||
});
|
||||
return response.ok({ body: theComments });
|
||||
} catch (error) {
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
27
x-pack/plugins/case/server/routes/api/get_all_cases.ts
Normal file
27
x-pack/plugins/case/server/routes/api/get_all_cases.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { RouteDeps } from '.';
|
||||
import { wrapError } from './utils';
|
||||
|
||||
export function initGetAllCasesApi({ caseService, router }: RouteDeps) {
|
||||
router.get(
|
||||
{
|
||||
path: '/api/cases',
|
||||
validate: false,
|
||||
},
|
||||
async (context, request, response) => {
|
||||
try {
|
||||
const cases = await caseService.getAllCases({
|
||||
client: context.core.savedObjects.client,
|
||||
});
|
||||
return response.ok({ body: cases });
|
||||
} catch (error) {
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
49
x-pack/plugins/case/server/routes/api/get_case.ts
Normal file
49
x-pack/plugins/case/server/routes/api/get_case.ts
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { RouteDeps } from '.';
|
||||
import { wrapError } from './utils';
|
||||
|
||||
export function initGetCaseApi({ caseService, router }: RouteDeps) {
|
||||
router.get(
|
||||
{
|
||||
path: '/api/cases/{id}',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
id: schema.string(),
|
||||
}),
|
||||
query: schema.object({
|
||||
includeComments: schema.string({ defaultValue: 'true' }),
|
||||
}),
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
let theCase;
|
||||
const includeComments = JSON.parse(request.query.includeComments);
|
||||
try {
|
||||
theCase = await caseService.getCase({
|
||||
client: context.core.savedObjects.client,
|
||||
caseId: request.params.id,
|
||||
});
|
||||
} catch (error) {
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
if (!includeComments) {
|
||||
return response.ok({ body: theCase });
|
||||
}
|
||||
try {
|
||||
const theComments = await caseService.getAllCaseComments({
|
||||
client: context.core.savedObjects.client,
|
||||
caseId: request.params.id,
|
||||
});
|
||||
return response.ok({ body: { ...theCase, comments: theComments } });
|
||||
} catch (error) {
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
33
x-pack/plugins/case/server/routes/api/get_comment.ts
Normal file
33
x-pack/plugins/case/server/routes/api/get_comment.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { RouteDeps } from '.';
|
||||
import { wrapError } from './utils';
|
||||
|
||||
export function initGetCommentApi({ caseService, router }: RouteDeps) {
|
||||
router.get(
|
||||
{
|
||||
path: '/api/cases/comments/{id}',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
id: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
try {
|
||||
const theComment = await caseService.getComment({
|
||||
client: context.core.savedObjects.client,
|
||||
commentId: request.params.id,
|
||||
});
|
||||
return response.ok({ body: theComment });
|
||||
} catch (error) {
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
36
x-pack/plugins/case/server/routes/api/index.ts
Normal file
36
x-pack/plugins/case/server/routes/api/index.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { IRouter } from 'src/core/server';
|
||||
import { initDeleteCommentApi } from './delete_comment';
|
||||
import { initDeleteCaseApi } from './delete_case';
|
||||
import { initGetAllCaseCommentsApi } from './get_all_case_comments';
|
||||
import { initGetAllCasesApi } from './get_all_cases';
|
||||
import { initGetCaseApi } from './get_case';
|
||||
import { initGetCommentApi } from './get_comment';
|
||||
import { initPostCaseApi } from './post_case';
|
||||
import { initPostCommentApi } from './post_comment';
|
||||
import { initUpdateCaseApi } from './update_case';
|
||||
import { initUpdateCommentApi } from './update_comment';
|
||||
import { CaseServiceSetup } from '../../services';
|
||||
|
||||
export interface RouteDeps {
|
||||
caseService: CaseServiceSetup;
|
||||
router: IRouter;
|
||||
}
|
||||
|
||||
export function initCaseApi(deps: RouteDeps) {
|
||||
initGetAllCaseCommentsApi(deps);
|
||||
initGetAllCasesApi(deps);
|
||||
initGetCaseApi(deps);
|
||||
initGetCommentApi(deps);
|
||||
initDeleteCaseApi(deps);
|
||||
initDeleteCommentApi(deps);
|
||||
initPostCaseApi(deps);
|
||||
initPostCommentApi(deps);
|
||||
initUpdateCaseApi(deps);
|
||||
initUpdateCommentApi(deps);
|
||||
}
|
40
x-pack/plugins/case/server/routes/api/post_case.ts
Normal file
40
x-pack/plugins/case/server/routes/api/post_case.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { formatNewCase, wrapError } from './utils';
|
||||
import { NewCaseSchema } from './schema';
|
||||
import { RouteDeps } from '.';
|
||||
|
||||
export function initPostCaseApi({ caseService, router }: RouteDeps) {
|
||||
router.post(
|
||||
{
|
||||
path: '/api/cases',
|
||||
validate: {
|
||||
body: NewCaseSchema,
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
let createdBy;
|
||||
try {
|
||||
createdBy = await caseService.getUser({ request, response });
|
||||
} catch (error) {
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
|
||||
try {
|
||||
const newCase = await caseService.postNewCase({
|
||||
client: context.core.savedObjects.client,
|
||||
attributes: formatNewCase(request.body, {
|
||||
...createdBy,
|
||||
}),
|
||||
});
|
||||
return response.ok({ body: newCase });
|
||||
} catch (error) {
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
62
x-pack/plugins/case/server/routes/api/post_comment.ts
Normal file
62
x-pack/plugins/case/server/routes/api/post_comment.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { formatNewComment, wrapError } from './utils';
|
||||
import { NewCommentSchema } from './schema';
|
||||
import { RouteDeps } from '.';
|
||||
import { CASE_SAVED_OBJECT } from '../../constants';
|
||||
|
||||
export function initPostCommentApi({ caseService, router }: RouteDeps) {
|
||||
router.post(
|
||||
{
|
||||
path: '/api/cases/{id}/comment',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
id: schema.string(),
|
||||
}),
|
||||
body: NewCommentSchema,
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
let createdBy;
|
||||
let newComment;
|
||||
try {
|
||||
await caseService.getCase({
|
||||
client: context.core.savedObjects.client,
|
||||
caseId: request.params.id,
|
||||
});
|
||||
} catch (error) {
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
try {
|
||||
createdBy = await caseService.getUser({ request, response });
|
||||
} catch (error) {
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
try {
|
||||
newComment = await caseService.postNewComment({
|
||||
client: context.core.savedObjects.client,
|
||||
attributes: formatNewComment({
|
||||
newComment: request.body,
|
||||
...createdBy,
|
||||
}),
|
||||
references: [
|
||||
{
|
||||
type: CASE_SAVED_OBJECT,
|
||||
name: `associated-${CASE_SAVED_OBJECT}`,
|
||||
id: request.params.id,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return response.ok({ body: newComment });
|
||||
} catch (error) {
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
44
x-pack/plugins/case/server/routes/api/schema.ts
Normal file
44
x-pack/plugins/case/server/routes/api/schema.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
export const UserSchema = schema.object({
|
||||
username: schema.string(),
|
||||
full_name: schema.maybe(schema.string()),
|
||||
});
|
||||
|
||||
export const NewCommentSchema = schema.object({
|
||||
comment: schema.string(),
|
||||
});
|
||||
|
||||
export const CommentSchema = schema.object({
|
||||
comment: schema.string(),
|
||||
created_at: schema.number(),
|
||||
created_by: UserSchema,
|
||||
});
|
||||
|
||||
export const UpdatedCommentSchema = schema.object({
|
||||
comment: schema.string(),
|
||||
});
|
||||
|
||||
export const NewCaseSchema = schema.object({
|
||||
assignees: schema.arrayOf(UserSchema, { defaultValue: [] }),
|
||||
description: schema.string(),
|
||||
title: schema.string(),
|
||||
state: schema.oneOf([schema.literal('open'), schema.literal('closed')], { defaultValue: 'open' }),
|
||||
tags: schema.arrayOf(schema.string(), { defaultValue: [] }),
|
||||
case_type: schema.string(),
|
||||
});
|
||||
|
||||
export const UpdatedCaseSchema = schema.object({
|
||||
assignees: schema.maybe(schema.arrayOf(UserSchema)),
|
||||
description: schema.maybe(schema.string()),
|
||||
title: schema.maybe(schema.string()),
|
||||
state: schema.maybe(schema.oneOf([schema.literal('open'), schema.literal('closed')])),
|
||||
tags: schema.maybe(schema.arrayOf(schema.string())),
|
||||
case_type: schema.maybe(schema.string()),
|
||||
});
|
36
x-pack/plugins/case/server/routes/api/types.ts
Normal file
36
x-pack/plugins/case/server/routes/api/types.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { TypeOf } from '@kbn/config-schema';
|
||||
import {
|
||||
CommentSchema,
|
||||
NewCaseSchema,
|
||||
NewCommentSchema,
|
||||
UpdatedCaseSchema,
|
||||
UpdatedCommentSchema,
|
||||
UserSchema,
|
||||
} from './schema';
|
||||
|
||||
export type NewCaseType = TypeOf<typeof NewCaseSchema>;
|
||||
export type NewCommentFormatted = TypeOf<typeof CommentSchema>;
|
||||
export type NewCommentType = TypeOf<typeof NewCommentSchema>;
|
||||
export type UpdatedCaseTyped = TypeOf<typeof UpdatedCaseSchema>;
|
||||
export type UpdatedCommentType = TypeOf<typeof UpdatedCommentSchema>;
|
||||
export type UserType = TypeOf<typeof UserSchema>;
|
||||
|
||||
export interface NewCaseFormatted extends NewCaseType {
|
||||
created_at: number;
|
||||
created_by: UserType;
|
||||
}
|
||||
|
||||
export interface UpdatedCaseType {
|
||||
assignees?: UpdatedCaseTyped['assignees'];
|
||||
description?: UpdatedCaseTyped['description'];
|
||||
title?: UpdatedCaseTyped['title'];
|
||||
state?: UpdatedCaseTyped['state'];
|
||||
tags?: UpdatedCaseTyped['tags'];
|
||||
case_type?: UpdatedCaseTyped['case_type'];
|
||||
}
|
36
x-pack/plugins/case/server/routes/api/update_case.ts
Normal file
36
x-pack/plugins/case/server/routes/api/update_case.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { wrapError } from './utils';
|
||||
import { RouteDeps } from '.';
|
||||
import { UpdatedCaseSchema } from './schema';
|
||||
|
||||
export function initUpdateCaseApi({ caseService, router }: RouteDeps) {
|
||||
router.post(
|
||||
{
|
||||
path: '/api/cases/{id}',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
id: schema.string(),
|
||||
}),
|
||||
body: UpdatedCaseSchema,
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
try {
|
||||
const updatedCase = await caseService.updateCase({
|
||||
client: context.core.savedObjects.client,
|
||||
caseId: request.params.id,
|
||||
updatedAttributes: request.body,
|
||||
});
|
||||
return response.ok({ body: updatedCase });
|
||||
} catch (error) {
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
36
x-pack/plugins/case/server/routes/api/update_comment.ts
Normal file
36
x-pack/plugins/case/server/routes/api/update_comment.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { wrapError } from './utils';
|
||||
import { NewCommentSchema } from './schema';
|
||||
import { RouteDeps } from '.';
|
||||
|
||||
export function initUpdateCommentApi({ caseService, router }: RouteDeps) {
|
||||
router.post(
|
||||
{
|
||||
path: '/api/cases/comment/{id}',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
id: schema.string(),
|
||||
}),
|
||||
body: NewCommentSchema,
|
||||
},
|
||||
},
|
||||
async (context, request, response) => {
|
||||
try {
|
||||
const updatedComment = await caseService.updateComment({
|
||||
client: context.core.savedObjects.client,
|
||||
commentId: request.params.id,
|
||||
updatedAttributes: request.body,
|
||||
});
|
||||
return response.ok({ body: updatedComment });
|
||||
} catch (error) {
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
48
x-pack/plugins/case/server/routes/api/utils.ts
Normal file
48
x-pack/plugins/case/server/routes/api/utils.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { boomify, isBoom } from 'boom';
|
||||
import { CustomHttpResponseOptions, ResponseError } from 'kibana/server';
|
||||
import {
|
||||
NewCaseType,
|
||||
NewCaseFormatted,
|
||||
NewCommentType,
|
||||
NewCommentFormatted,
|
||||
UserType,
|
||||
} from './types';
|
||||
|
||||
export const formatNewCase = (
|
||||
newCase: NewCaseType,
|
||||
{ full_name, username }: { full_name?: string; username: string }
|
||||
): NewCaseFormatted => ({
|
||||
created_at: new Date().valueOf(),
|
||||
created_by: { full_name, username },
|
||||
...newCase,
|
||||
});
|
||||
|
||||
interface NewCommentArgs {
|
||||
newComment: NewCommentType;
|
||||
full_name?: UserType['full_name'];
|
||||
username: UserType['username'];
|
||||
}
|
||||
export const formatNewComment = ({
|
||||
newComment,
|
||||
full_name,
|
||||
username,
|
||||
}: NewCommentArgs): NewCommentFormatted => ({
|
||||
...newComment,
|
||||
created_at: new Date().valueOf(),
|
||||
created_by: { full_name, username },
|
||||
});
|
||||
|
||||
export function wrapError(error: any): CustomHttpResponseOptions<ResponseError> {
|
||||
const boom = isBoom(error) ? error : boomify(error);
|
||||
return {
|
||||
body: boom,
|
||||
headers: boom.output.headers,
|
||||
statusCode: boom.output.statusCode,
|
||||
};
|
||||
}
|
192
x-pack/plugins/case/server/services/index.ts
Normal file
192
x-pack/plugins/case/server/services/index.ts
Normal file
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import {
|
||||
KibanaRequest,
|
||||
KibanaResponseFactory,
|
||||
Logger,
|
||||
SavedObject,
|
||||
SavedObjectsClientContract,
|
||||
SavedObjectsFindResponse,
|
||||
SavedObjectsUpdateResponse,
|
||||
SavedObjectReference,
|
||||
} from 'kibana/server';
|
||||
import { CASE_COMMENT_SAVED_OBJECT, CASE_SAVED_OBJECT } from '../constants';
|
||||
import {
|
||||
NewCaseFormatted,
|
||||
NewCommentFormatted,
|
||||
UpdatedCaseType,
|
||||
UpdatedCommentType,
|
||||
} from '../routes/api/types';
|
||||
import {
|
||||
AuthenticatedUser,
|
||||
PluginSetupContract as SecurityPluginSetup,
|
||||
} from '../../../security/server';
|
||||
|
||||
interface ClientArgs {
|
||||
client: SavedObjectsClientContract;
|
||||
}
|
||||
|
||||
interface GetCaseArgs extends ClientArgs {
|
||||
caseId: string;
|
||||
}
|
||||
interface GetCommentArgs extends ClientArgs {
|
||||
commentId: string;
|
||||
}
|
||||
interface PostCaseArgs extends ClientArgs {
|
||||
attributes: NewCaseFormatted;
|
||||
}
|
||||
|
||||
interface PostCommentArgs extends ClientArgs {
|
||||
attributes: NewCommentFormatted;
|
||||
references: SavedObjectReference[];
|
||||
}
|
||||
interface UpdateCaseArgs extends ClientArgs {
|
||||
caseId: string;
|
||||
updatedAttributes: UpdatedCaseType;
|
||||
}
|
||||
interface UpdateCommentArgs extends ClientArgs {
|
||||
commentId: string;
|
||||
updatedAttributes: UpdatedCommentType;
|
||||
}
|
||||
|
||||
interface GetUserArgs {
|
||||
request: KibanaRequest;
|
||||
response: KibanaResponseFactory;
|
||||
}
|
||||
|
||||
interface CaseServiceDeps {
|
||||
authentication: SecurityPluginSetup['authc'];
|
||||
}
|
||||
export interface CaseServiceSetup {
|
||||
deleteCase(args: GetCaseArgs): Promise<{}>;
|
||||
deleteComment(args: GetCommentArgs): Promise<{}>;
|
||||
getAllCases(args: ClientArgs): Promise<SavedObjectsFindResponse>;
|
||||
getAllCaseComments(args: GetCaseArgs): Promise<SavedObjectsFindResponse>;
|
||||
getCase(args: GetCaseArgs): Promise<SavedObject>;
|
||||
getComment(args: GetCommentArgs): Promise<SavedObject>;
|
||||
getUser(args: GetUserArgs): Promise<AuthenticatedUser>;
|
||||
postNewCase(args: PostCaseArgs): Promise<SavedObject>;
|
||||
postNewComment(args: PostCommentArgs): Promise<SavedObject>;
|
||||
updateCase(args: UpdateCaseArgs): Promise<SavedObjectsUpdateResponse>;
|
||||
updateComment(args: UpdateCommentArgs): Promise<SavedObjectsUpdateResponse>;
|
||||
}
|
||||
|
||||
export class CaseService {
|
||||
constructor(private readonly log: Logger) {}
|
||||
public setup = async ({ authentication }: CaseServiceDeps): Promise<CaseServiceSetup> => ({
|
||||
deleteCase: async ({ client, caseId }: GetCaseArgs) => {
|
||||
try {
|
||||
this.log.debug(`Attempting to GET case ${caseId}`);
|
||||
return await client.delete(CASE_SAVED_OBJECT, caseId);
|
||||
} catch (error) {
|
||||
this.log.debug(`Error on GET case ${caseId}: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
deleteComment: async ({ client, commentId }: GetCommentArgs) => {
|
||||
try {
|
||||
this.log.debug(`Attempting to GET comment ${commentId}`);
|
||||
return await client.delete(CASE_COMMENT_SAVED_OBJECT, commentId);
|
||||
} catch (error) {
|
||||
this.log.debug(`Error on GET comment ${commentId}: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
getCase: async ({ client, caseId }: GetCaseArgs) => {
|
||||
try {
|
||||
this.log.debug(`Attempting to GET case ${caseId}`);
|
||||
return await client.get(CASE_SAVED_OBJECT, caseId);
|
||||
} catch (error) {
|
||||
this.log.debug(`Error on GET case ${caseId}: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
getComment: async ({ client, commentId }: GetCommentArgs) => {
|
||||
try {
|
||||
this.log.debug(`Attempting to GET comment ${commentId}`);
|
||||
return await client.get(CASE_COMMENT_SAVED_OBJECT, commentId);
|
||||
} catch (error) {
|
||||
this.log.debug(`Error on GET comment ${commentId}: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
getAllCases: async ({ client }: ClientArgs) => {
|
||||
try {
|
||||
this.log.debug(`Attempting to GET all cases`);
|
||||
return await client.find({ type: CASE_SAVED_OBJECT });
|
||||
} catch (error) {
|
||||
this.log.debug(`Error on GET cases: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
getAllCaseComments: async ({ client, caseId }: GetCaseArgs) => {
|
||||
try {
|
||||
this.log.debug(`Attempting to GET all comments for case ${caseId}`);
|
||||
return await client.find({
|
||||
type: CASE_COMMENT_SAVED_OBJECT,
|
||||
hasReference: { type: CASE_SAVED_OBJECT, id: caseId },
|
||||
});
|
||||
} catch (error) {
|
||||
this.log.debug(`Error on GET all comments for case ${caseId}: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
getUser: async ({ request, response }: GetUserArgs) => {
|
||||
let user;
|
||||
try {
|
||||
this.log.debug(`Attempting to authenticate a user`);
|
||||
user = await authentication!.getCurrentUser(request);
|
||||
} catch (error) {
|
||||
this.log.debug(`Error on GET user: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
if (!user) {
|
||||
this.log.debug(`Error on GET user: Bad User`);
|
||||
throw new Error('Bad User - the user is not authenticated');
|
||||
}
|
||||
return user;
|
||||
},
|
||||
postNewCase: async ({ client, attributes }: PostCaseArgs) => {
|
||||
try {
|
||||
this.log.debug(`Attempting to POST a new case`);
|
||||
return await client.create(CASE_SAVED_OBJECT, { ...attributes });
|
||||
} catch (error) {
|
||||
this.log.debug(`Error on POST a new case: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
postNewComment: async ({ client, attributes, references }: PostCommentArgs) => {
|
||||
try {
|
||||
this.log.debug(`Attempting to POST a new comment`);
|
||||
return await client.create(CASE_COMMENT_SAVED_OBJECT, attributes, { references });
|
||||
} catch (error) {
|
||||
this.log.debug(`Error on POST a new comment: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
updateCase: async ({ client, caseId, updatedAttributes }: UpdateCaseArgs) => {
|
||||
try {
|
||||
this.log.debug(`Attempting to UPDATE case ${caseId}`);
|
||||
return await client.update(CASE_SAVED_OBJECT, caseId, { ...updatedAttributes });
|
||||
} catch (error) {
|
||||
this.log.debug(`Error on UPDATE case ${caseId}: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
updateComment: async ({ client, commentId, updatedAttributes }: UpdateCommentArgs) => {
|
||||
try {
|
||||
this.log.debug(`Attempting to UPDATE comment ${commentId}`);
|
||||
return await client.update(CASE_COMMENT_SAVED_OBJECT, commentId, {
|
||||
...updatedAttributes,
|
||||
});
|
||||
} catch (error) {
|
||||
this.log.debug(`Error on UPDATE comment ${commentId}: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
|
@ -17,6 +17,7 @@ import { Plugin, PluginSetupContract, PluginSetupDependencies } from './plugin';
|
|||
// These exports are part of public Security plugin contract, any change in signature of exported
|
||||
// functions or removal of exports should be considered as a breaking change.
|
||||
export {
|
||||
Authentication,
|
||||
AuthenticationResult,
|
||||
DeauthenticationResult,
|
||||
CreateAPIKeyResult,
|
||||
|
@ -24,6 +25,7 @@ export {
|
|||
InvalidateAPIKeyResult,
|
||||
} from './authentication';
|
||||
export { PluginSetupContract };
|
||||
export { AuthenticatedUser } from '../common/model';
|
||||
|
||||
export const config: PluginConfigDescriptor<TypeOf<typeof ConfigSchema>> = {
|
||||
schema: ConfigSchema,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue