Skip to content

Commit

Permalink
Move sub case routes to case client
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathan-buttner committed Apr 7, 2021
1 parent 5c879fb commit 25384fc
Show file tree
Hide file tree
Showing 8 changed files with 656 additions and 631 deletions.
1 change: 1 addition & 0 deletions x-pack/plugins/cases/common/api/cases/sub_case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,4 @@ export type SubCasesResponse = rt.TypeOf<typeof SubCasesResponseRt>;
export type SubCasesFindResponse = rt.TypeOf<typeof SubCasesFindResponseRt>;
export type SubCasePatchRequest = rt.TypeOf<typeof SubCasePatchRequestRt>;
export type SubCasesPatchRequest = rt.TypeOf<typeof SubCasesPatchRequestRt>;
export type SubCasesFindRequest = rt.TypeOf<typeof SubCasesFindRequestRt>;
4 changes: 2 additions & 2 deletions x-pack/plugins/cases/server/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { CasesSubClient, createCasesSubClient } from './cases/client';
import { AttachmentsSubClient, createAttachmentsSubClient } from './attachments/client';
import { UserActionsSubClient, createUserActionsSubClient } from './user_actions/client';
import { CasesClientInternal, createCasesClientInternal } from './client_internal';
import { SubCasesClient } from './sub_cases/client';
import { createSubCasesClient, SubCasesClient } from './sub_cases/client';

export class CasesClient {
private readonly _casesClientInternal: CasesClientInternal;
Expand All @@ -24,7 +24,7 @@ export class CasesClient {
this._cases = createCasesSubClient(args, this, this._casesClientInternal);
this._attachments = createAttachmentsSubClient(args, this._casesClientInternal);
this._userActions = createUserActionsSubClient(args);
this._subCases = new SubCasesClient(args);
this._subCases = createSubCasesClient(args, this);
}

public get cases() {
Expand Down
263 changes: 214 additions & 49 deletions x-pack/plugins/cases/server/client/sub_cases/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,68 +5,233 @@
* 2.0.
*/

import Boom from '@hapi/boom';

import { SavedObjectsClientContract } from 'kibana/server';
import { SubCaseResponseRt } from '../../../common/api';
import {
caseStatuses,
SubCaseResponse,
SubCaseResponseRt,
SubCasesFindRequest,
SubCasesFindResponse,
SubCasesFindResponseRt,
SubCasesResponse,
User,
} from '../../../common/api';
import { CasesClientArgs } from '..';
import { flattenSubCaseSavedObject } from '../../routes/api/utils';
import { flattenSubCaseSavedObject, transformSubCases } from '../../routes/api/utils';
import { countAlertsForID } from '../../common';
import { createCaseError } from '../../common/error';
import { CASE_SAVED_OBJECT } from '../../../common/constants';
import { buildCaseUserActionItem } from '../../services/user_actions/helpers';
import { constructQueryOptions } from '../../routes/api/cases/helpers';
import { defaultPage, defaultPerPage } from '../../routes/api';
import { CasesClient } from '../client';
import { update, UpdateArgs } from './update';

interface DeleteArgs {
soClient: SavedObjectsClientContract;
ids: string[];
user: User;
}

interface FindArgs {
soClient: SavedObjectsClientContract;
caseID: string;
queryParams: SubCasesFindRequest;
}

interface GetArgs {
includeComments: boolean;
id: string;
soClient: SavedObjectsClientContract;
}

/**
* Client for handling the different exposed API routes for interacting with sub cases.
* The API routes for interacting with sub cases.
*/
export class SubCasesClient {
constructor(private readonly args: CasesClientArgs) {}

public async get({
includeComments,
id,
soClient,
}: {
includeComments: boolean;
id: string;
soClient: SavedObjectsClientContract;
}) {
try {
const subCase = await this.args.caseService.getSubCase({
soClient,
id,
});

if (!includeComments) {
return SubCaseResponseRt.encode(
flattenSubCaseSavedObject({
savedObject: subCase,
})
);
}

const theComments = await this.args.caseService.getAllSubCaseComments({
soClient,
id,
options: {
sortField: 'created_at',
sortOrder: 'asc',
},
});
export interface SubCasesClient {
delete(deleteArgs: DeleteArgs): Promise<void>;
find(findArgs: FindArgs): Promise<SubCasesFindResponse>;
get(getArgs: GetArgs): Promise<SubCaseResponse>;
update(updateArgs: UpdateArgs): Promise<SubCasesResponse>;
}

/**
* Creates a client for handling the different exposed API routes for interacting with sub cases.
*/
export function createSubCasesClient(
clientArgs: CasesClientArgs,
casesClient: CasesClient
): SubCasesClient {
return Object.freeze({
delete: (deleteArgs: DeleteArgs) => deleteSubCase(deleteArgs, clientArgs),
find: (findArgs: FindArgs) => find(findArgs, clientArgs),
get: (getArgs: GetArgs) => get(getArgs, clientArgs),
update: (updateArgs: UpdateArgs) => update(updateArgs, clientArgs, casesClient),
});
}

async function deleteSubCase(
{ soClient, ids, user }: DeleteArgs,
clientArgs: CasesClientArgs
): Promise<void> {
try {
const [comments, subCases] = await Promise.all([
clientArgs.caseService.getAllSubCaseComments({ soClient, id: ids }),
clientArgs.caseService.getSubCases({ soClient, ids }),
]);

const subCaseErrors = subCases.saved_objects.filter((subCase) => subCase.error !== undefined);

if (subCaseErrors.length > 0) {
throw Boom.notFound(
`These sub cases ${subCaseErrors
.map((c) => c.id)
.join(', ')} do not exist. Please check you have the correct ids.`
);
}

const subCaseIDToParentID = subCases.saved_objects.reduce((acc, subCase) => {
const parentID = subCase.references.find((ref) => ref.type === CASE_SAVED_OBJECT);
acc.set(subCase.id, parentID?.id);
return acc;
}, new Map<string, string | undefined>());

await Promise.all(
comments.saved_objects.map((comment) =>
clientArgs.attachmentService.delete({ soClient, attachmentId: comment.id })
)
);

await Promise.all(ids.map((id) => clientArgs.caseService.deleteSubCase(soClient, id)));

const deleteDate = new Date().toISOString();

await clientArgs.userActionService.bulkCreate({
soClient,
actions: ids.map((id) =>
buildCaseUserActionItem({
action: 'delete',
actionAt: deleteDate,
actionBy: user,
// if for some reason the sub case didn't have a reference to its parent, we'll still log a user action
// but we won't have the case ID
caseId: subCaseIDToParentID.get(id) ?? '',
subCaseId: id,
fields: ['sub_case', 'comment', 'status'],
})
),
});
} catch (error) {
throw createCaseError({
message: `Failed to delete sub cases ids: ${JSON.stringify(ids)}: ${error}`,
error,
logger: clientArgs.logger,
});
}
}

async function find(
{ soClient, caseID, queryParams }: FindArgs,
clientArgs: CasesClientArgs
): Promise<SubCasesFindResponse> {
try {
const ids = [caseID];
const { subCase: subCaseQueryOptions } = constructQueryOptions({
status: queryParams.status,
sortByField: queryParams.sortField,
});

const subCases = await clientArgs.caseService.findSubCasesGroupByCase({
soClient,
ids,
options: {
sortField: 'created_at',
page: defaultPage,
perPage: defaultPerPage,
...queryParams,
...subCaseQueryOptions,
},
});

const [open, inProgress, closed] = await Promise.all([
...caseStatuses.map((status) => {
const { subCase: statusQueryOptions } = constructQueryOptions({
status,
sortByField: queryParams.sortField,
});
return clientArgs.caseService.findSubCaseStatusStats({
soClient,
options: statusQueryOptions ?? {},
ids,
});
}),
]);

return SubCasesFindResponseRt.encode(
transformSubCases({
page: subCases.page,
perPage: subCases.perPage,
total: subCases.total,
subCasesMap: subCases.subCasesMap,
open,
inProgress,
closed,
})
);
} catch (error) {
throw createCaseError({
message: `Failed to find sub cases for case id: ${caseID}: ${error}`,
error,
logger: clientArgs.logger,
});
}
}

async function get(
{ includeComments, id, soClient }: GetArgs,
clientArgs: CasesClientArgs
): Promise<SubCaseResponse> {
try {
const subCase = await clientArgs.caseService.getSubCase({
soClient,
id,
});

if (!includeComments) {
return SubCaseResponseRt.encode(
flattenSubCaseSavedObject({
savedObject: subCase,
comments: theComments.saved_objects,
totalComment: theComments.total,
totalAlerts: countAlertsForID({
comments: theComments,
id,
}),
})
);
} catch (error) {
throw createCaseError({
message: `Failed to get sub case id: ${id}: ${error}`,
error,
logger: this.args.logger,
});
}

const theComments = await clientArgs.caseService.getAllSubCaseComments({
soClient,
id,
options: {
sortField: 'created_at',
sortOrder: 'asc',
},
});

return SubCaseResponseRt.encode(
flattenSubCaseSavedObject({
savedObject: subCase,
comments: theComments.saved_objects,
totalComment: theComments.total,
totalAlerts: countAlertsForID({
comments: theComments,
id,
}),
})
);
} catch (error) {
throw createCaseError({
message: `Failed to get sub case id: ${id}: ${error}`,
error,
logger: clientArgs.logger,
});
}
}
Loading

0 comments on commit 25384fc

Please sign in to comment.