[Cases] Performance and RBAC improvements (#101465) (#101652)

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Christos Nasikas 2021-06-08 22:05:37 +03:00 committed by GitHub
parent 11ecb4e6fc
commit f2a5c47484
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 181 additions and 196 deletions

View file

@ -9,13 +9,11 @@ import * as rt from 'io-ts';
import { UserRT } from '../user';
import { CaseConnectorRt, ConnectorMappingsRt, ESCaseConnector } from '../connectors';
import { OmitProp } from '../runtime_types';
import { OWNER_FIELD } from './constants';
// TODO: we will need to add this type rt.literal('close-by-third-party')
const ClosureTypeRT = rt.union([rt.literal('close-by-user'), rt.literal('close-by-pushing')]);
const CasesConfigureBasicRt = rt.type({
const CasesConfigureBasicWithoutOwnerRt = rt.type({
/**
* The external connector
*/
@ -24,15 +22,17 @@ const CasesConfigureBasicRt = rt.type({
* Whether to close the case after it has been synced with the external system
*/
closure_type: ClosureTypeRT,
/**
* The plugin owner that manages this configuration
*/
owner: rt.string,
});
const CasesConfigureBasicWithoutOwnerRt = rt.type(
OmitProp(CasesConfigureBasicRt.props, OWNER_FIELD)
);
const CasesConfigureBasicRt = rt.intersection([
CasesConfigureBasicWithoutOwnerRt,
rt.type({
/**
* The plugin owner that manages this configuration
*/
owner: rt.string,
}),
]);
export const CasesConfigureRequestRt = CasesConfigureBasicRt;
export const CasesConfigurePatchRt = rt.intersection([

View file

@ -5,7 +5,6 @@
* 2.0.
*/
import { omit } from 'lodash';
import { either, fold } from 'fp-ts/lib/Either';
import { identity } from 'fp-ts/lib/function';
import { pipe } from 'fp-ts/lib/pipeable';
@ -14,9 +13,6 @@ import { isObject } from 'lodash/fp';
type ErrorFactory = (message: string) => Error;
export const OmitProp = <O extends rt.Props, K extends keyof O>(o: O, k: K): Omit<O, K> =>
omit(o, k);
/**
* @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/format_errors/index.ts
* Bug fix for the TODO is in the format_errors package

View file

@ -92,3 +92,6 @@ export const ENABLE_CASE_CONNECTOR = false;
if (ENABLE_CASE_CONNECTOR) {
SAVED_OBJECT_TYPES.push(SUB_CASE_SAVED_OBJECT);
}
export const MAX_DOCS_PER_PAGE = 10000;
export const MAX_CONCURRENT_SEARCHES = 10;

View file

@ -6,9 +6,15 @@
*/
import Boom from '@hapi/boom';
import { CASE_SAVED_OBJECT, SUB_CASE_SAVED_OBJECT } from '../../../common/constants';
import pMap from 'p-map';
import { AssociationType } from '../../../common/api';
import { SavedObject } from 'kibana/public';
import {
CASE_SAVED_OBJECT,
MAX_CONCURRENT_SEARCHES,
SUB_CASE_SAVED_OBJECT,
} from '../../../common/constants';
import { AssociationType, CommentAttributes } from '../../../common/api';
import { CasesClientArgs } from '../types';
import { buildCommentUserActionItem } from '../../services/user_actions/helpers';
import { createCaseError } from '../../common/error';
@ -88,14 +94,16 @@ export async function deleteAll(
})),
});
await Promise.all(
comments.saved_objects.map((comment) =>
attachmentService.delete({
unsecuredSavedObjectsClient,
attachmentId: comment.id,
})
)
);
const mapper = async (comment: SavedObject<CommentAttributes>) =>
attachmentService.delete({
unsecuredSavedObjectsClient,
attachmentId: comment.id,
});
// Ensuring we don't too many concurrent deletions running.
await pMap(comments.saved_objects, mapper, {
concurrency: MAX_CONCURRENT_SEARCHES,
});
const deleteDate = new Date().toISOString();

View file

@ -5,15 +5,16 @@
* 2.0.
*/
import pMap from 'p-map';
import { Boom } from '@hapi/boom';
import { SavedObjectsClientContract } from 'kibana/server';
import { ENABLE_CASE_CONNECTOR } from '../../../common/constants';
import { SavedObject, SavedObjectsClientContract, SavedObjectsFindResponse } from 'kibana/server';
import { ENABLE_CASE_CONNECTOR, MAX_CONCURRENT_SEARCHES } from '../../../common/constants';
import { CasesClientArgs } from '..';
import { createCaseError } from '../../common/error';
import { AttachmentService, CasesService } from '../../services';
import { buildCaseUserActionItem } from '../../services/user_actions/helpers';
import { Operations, OwnerEntity } from '../../authorization';
import { OWNER_FIELD } from '../../../common/api';
import { OWNER_FIELD, SubCaseAttributes, CommentAttributes } from '../../../common/api';
async function deleteSubCases({
attachmentService,
@ -37,19 +38,24 @@ async function deleteSubCases({
id: subCaseIDs,
});
// This shouldn't actually delete anything because all the comments should be deleted when comments are deleted
// per case ID
await Promise.all(
commentsForSubCases.saved_objects.map((commentSO) =>
attachmentService.delete({ unsecuredSavedObjectsClient, attachmentId: commentSO.id })
)
);
const commentMapper = (commentSO: SavedObject<CommentAttributes>) =>
attachmentService.delete({ unsecuredSavedObjectsClient, attachmentId: commentSO.id });
await Promise.all(
subCasesForCaseIds.saved_objects.map((subCaseSO) =>
caseService.deleteSubCase(unsecuredSavedObjectsClient, subCaseSO.id)
)
);
const subCasesMapper = (subCaseSO: SavedObject<SubCaseAttributes>) =>
caseService.deleteSubCase(unsecuredSavedObjectsClient, subCaseSO.id);
/**
* This shouldn't actually delete anything because
* all the comments should be deleted when comments are deleted
* per case ID. We also ensure that we don't too many concurrent deletions running.
*/
await pMap(commentsForSubCases.saved_objects, commentMapper, {
concurrency: MAX_CONCURRENT_SEARCHES,
});
await pMap(subCasesForCaseIds.saved_objects, subCasesMapper, {
concurrency: MAX_CONCURRENT_SEARCHES,
});
}
/**
@ -88,38 +94,46 @@ export async function deleteCases(ids: string[], clientArgs: CasesClientArgs): P
entities: Array.from(entities.values()),
});
await Promise.all(
ids.map((id) =>
caseService.deleteCase({
unsecuredSavedObjectsClient,
id,
})
)
);
const deleteCasesMapper = async (id: string) =>
caseService.deleteCase({
unsecuredSavedObjectsClient,
id,
});
const comments = await Promise.all(
ids.map((id) =>
caseService.getAllCaseComments({
unsecuredSavedObjectsClient,
id,
})
)
);
// Ensuring we don't too many concurrent deletions running.
await pMap(ids, deleteCasesMapper, {
concurrency: MAX_CONCURRENT_SEARCHES,
});
if (comments.some((c) => c.saved_objects.length > 0)) {
await Promise.all(
comments.map((c) =>
Promise.all(
c.saved_objects.map(({ id }) =>
attachmentService.delete({
unsecuredSavedObjectsClient,
attachmentId: id,
})
)
)
)
const getCommentsMapper = async (id: string) =>
caseService.getAllCaseComments({
unsecuredSavedObjectsClient,
id,
});
// Ensuring we don't too many concurrent get running.
const comments = await pMap(ids, getCommentsMapper, {
concurrency: MAX_CONCURRENT_SEARCHES,
});
/**
* This is a nested pMap.Mapper.
* Each element of the comments array contains all comments of a particular case.
* For that reason we need first to create a map that iterate over all cases
* and return a pMap that deletes the comments for that case
*/
const deleteCommentsMapper = async (commentRes: SavedObjectsFindResponse<CommentAttributes>) =>
pMap(commentRes.saved_objects, (comment) =>
attachmentService.delete({
unsecuredSavedObjectsClient,
attachmentId: comment.id,
})
);
}
// Ensuring we don't too many concurrent deletions running.
await pMap(comments, deleteCommentsMapper, {
concurrency: MAX_CONCURRENT_SEARCHES,
});
if (ENABLE_CASE_CONNECTOR) {
await deleteSubCases({

View file

@ -77,6 +77,7 @@ export const find = async (
ensureSavedObjectsAreAuthorized([...cases.casesMap.values()]);
// casesStatuses are bounded by us. No need to limit concurrent calls.
const [openCases, inProgressCases, closedCases] = await Promise.all([
...caseStatuses.map((status) => {
const statusQuery = constructQueryOptions({ ...queryArgs, status, authorizationFilter });

View file

@ -5,6 +5,7 @@
* 2.0.
*/
import pMap from 'p-map';
import Boom from '@hapi/boom';
import { pipe } from 'fp-ts/lib/pipeable';
import { fold } from 'fp-ts/lib/Either';
@ -42,6 +43,7 @@ import { CasesService } from '../../services';
import {
CASE_COMMENT_SAVED_OBJECT,
CASE_SAVED_OBJECT,
MAX_CONCURRENT_SEARCHES,
SUB_CASE_SAVED_OBJECT,
} from '../../../common/constants';
import {
@ -162,9 +164,11 @@ async function throwIfInvalidUpdateOfTypeWithAlerts({
};
const requestsUpdatingTypeField = requests.filter((req) => req.type === CaseType.collection);
const casesAlertTotals = await Promise.all(
requestsUpdatingTypeField.map((caseToUpdate) => getAlertsForID(caseToUpdate))
);
const getAlertsMapper = async (caseToUpdate: ESCasePatchRequest) => getAlertsForID(caseToUpdate);
// Ensuring we don't too many concurrent get running.
const casesAlertTotals = await pMap(requestsUpdatingTypeField, getAlertsMapper, {
concurrency: MAX_CONCURRENT_SEARCHES,
});
// grab the cases that have at least one alert comment attached to them
const typeUpdateWithAlerts = casesAlertTotals.filter((caseInfo) => caseInfo.alerts.total > 0);

View file

@ -4,13 +4,19 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import pMap from 'p-map';
import Boom from '@hapi/boom';
import { pipe } from 'fp-ts/lib/pipeable';
import { fold } from 'fp-ts/lib/Either';
import { identity } from 'fp-ts/lib/function';
import { SavedObjectsFindResponse, SavedObjectsUtils } from '../../../../../../src/core/server';
import { SUPPORTED_CONNECTORS } from '../../../common/constants';
import {
SavedObject,
SavedObjectsFindResponse,
SavedObjectsUtils,
} from '../../../../../../src/core/server';
import { MAX_CONCURRENT_SEARCHES, SUPPORTED_CONNECTORS } from '../../../common/constants';
import {
CaseConfigureResponseRt,
CasesConfigurePatch,
@ -26,6 +32,7 @@ import {
CaseConfigurationsResponseRt,
CasesConfigurePatchRt,
ConnectorMappings,
ESCasesConfigureAttributes,
} from '../../../common/api';
import { createCaseError } from '../../common/error';
import {
@ -175,8 +182,9 @@ async function get(
}))
);
const configurations = await Promise.all(
myCaseConfigure.saved_objects.map(async (configuration) => {
const configurations = await pMap(
myCaseConfigure.saved_objects,
async (configuration: SavedObject<ESCasesConfigureAttributes>) => {
const { connector, ...caseConfigureWithoutConnector } = configuration?.attributes ?? {
connector: null,
};
@ -204,7 +212,7 @@ async function get(
error,
id: configuration.id,
};
})
}
);
return CaseConfigurationsResponseRt.encode(configurations);
@ -400,11 +408,13 @@ async function create(
);
if (myCaseConfigure.saved_objects.length > 0) {
await Promise.all(
myCaseConfigure.saved_objects.map((cc) =>
caseConfigureService.delete({ unsecuredSavedObjectsClient, configurationId: cc.id })
)
);
const deleteConfigurationMapper = async (c: SavedObject<ESCasesConfigureAttributes>) =>
caseConfigureService.delete({ unsecuredSavedObjectsClient, configurationId: c.id });
// Ensuring we don't too many concurrent deletions running.
await pMap(myCaseConfigure.saved_objects, deleteConfigurationMapper, {
concurrency: MAX_CONCURRENT_SEARCHES,
});
}
const savedObjectID = SavedObjectsUtils.generateId();

View file

@ -63,6 +63,7 @@ async function getStatusTotalsByType(
ensureSavedObjectsAreAuthorized,
} = await authorization.getAuthorizationFilter(Operations.getCaseStatuses);
// casesStatuses are bounded by us. No need to limit concurrent calls.
const [openCases, inProgressCases, closedCases] = await Promise.all([
...caseStatuses.map((status) => {
const statusQuery = constructQueryOptions({

View file

@ -5,10 +5,13 @@
* 2.0.
*/
import pMap from 'p-map';
import Boom from '@hapi/boom';
import { SavedObject } from 'kibana/server';
import {
caseStatuses,
CommentAttributes,
SubCaseResponse,
SubCaseResponseRt,
SubCasesFindRequest,
@ -19,7 +22,7 @@ import {
import { CasesClientArgs, CasesClientInternal } from '..';
import { countAlertsForID, flattenSubCaseSavedObject, transformSubCases } from '../../common';
import { createCaseError } from '../../common/error';
import { CASE_SAVED_OBJECT } from '../../../common/constants';
import { CASE_SAVED_OBJECT, MAX_CONCURRENT_SEARCHES } from '../../../common/constants';
import { buildCaseUserActionItem } from '../../services/user_actions/helpers';
import { constructQueryOptions } from '../utils';
import { defaultPage, defaultPerPage } from '../../routes/api';
@ -121,13 +124,20 @@ async function deleteSubCase(ids: string[], clientArgs: CasesClientArgs): Promis
return acc;
}, new Map<string, string | undefined>());
await Promise.all(
comments.saved_objects.map((comment) =>
attachmentService.delete({ unsecuredSavedObjectsClient, attachmentId: comment.id })
)
);
const deleteCommentMapper = async (comment: SavedObject<CommentAttributes>) =>
attachmentService.delete({ unsecuredSavedObjectsClient, attachmentId: comment.id });
await Promise.all(ids.map((id) => caseService.deleteSubCase(unsecuredSavedObjectsClient, id)));
const deleteSubCasesMapper = async (id: string) =>
caseService.deleteSubCase(unsecuredSavedObjectsClient, id);
// Ensuring we don't too many concurrent deletions running.
await pMap(comments.saved_objects, deleteCommentMapper, {
concurrency: MAX_CONCURRENT_SEARCHES,
});
await pMap(ids, deleteSubCasesMapper, {
concurrency: MAX_CONCURRENT_SEARCHES,
});
const deleteDate = new Date().toISOString();
@ -181,6 +191,7 @@ async function find(
},
});
// casesStatuses are bounded by us. No need to limit concurrent calls.
const [open, inProgress, closed] = await Promise.all([
...caseStatuses.map((status) => {
const { subCase: statusQueryOptions } = constructQueryOptions({

View file

@ -34,7 +34,11 @@ import {
flattenSubCaseSavedObject,
transformNewComment,
} from '..';
import { CASE_SAVED_OBJECT, SUB_CASE_SAVED_OBJECT } from '../../../common/constants';
import {
CASE_SAVED_OBJECT,
MAX_DOCS_PER_PAGE,
SUB_CASE_SAVED_OBJECT,
} from '../../../common/constants';
import { AttachmentService, CasesService } from '../../services';
import { createCaseError } from '../error';
import { countAlertsForID } from '../index';
@ -309,23 +313,13 @@ export class CommentableCase {
public async encode(): Promise<CaseResponse> {
try {
const collectionCommentStats = await this.caseService.getAllCaseComments({
unsecuredSavedObjectsClient: this.unsecuredSavedObjectsClient,
id: this.collection.id,
options: {
fields: [],
page: 1,
perPage: 1,
},
});
const collectionComments = await this.caseService.getAllCaseComments({
unsecuredSavedObjectsClient: this.unsecuredSavedObjectsClient,
id: this.collection.id,
options: {
fields: [],
page: 1,
perPage: collectionCommentStats.total,
perPage: MAX_DOCS_PER_PAGE,
},
});
@ -335,7 +329,7 @@ export class CommentableCase {
const caseResponse = {
comments: flattenCommentSavedObjects(collectionComments.saved_objects),
totalAlerts: collectionTotalAlerts,
...this.formatCollectionForEncoding(collectionCommentStats.total),
...this.formatCollectionForEncoding(collectionComments.total),
};
if (this.subCase) {

View file

@ -57,10 +57,10 @@ export class AttachmentService {
public async delete({ unsecuredSavedObjectsClient, attachmentId }: GetAttachmentArgs) {
try {
this.log.debug(`Attempting to GET attachment ${attachmentId}`);
this.log.debug(`Attempting to DELETE attachment ${attachmentId}`);
return await unsecuredSavedObjectsClient.delete(CASE_COMMENT_SAVED_OBJECT, attachmentId);
} catch (error) {
this.log.error(`Error on GET attachment ${attachmentId}: ${error}`);
this.log.error(`Error on DELETE attachment ${attachmentId}: ${error}`);
throw error;
}
}

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { cloneDeep } from 'lodash';
import pMap from 'p-map';
import {
KibanaRequest,
Logger,
@ -43,7 +43,11 @@ import {
groupTotalAlertsByID,
SavedObjectFindOptionsKueryNode,
} from '../../common';
import { ENABLE_CASE_CONNECTOR } from '../../../common/constants';
import {
ENABLE_CASE_CONNECTOR,
MAX_CONCURRENT_SEARCHES,
MAX_DOCS_PER_PAGE,
} from '../../../common/constants';
import { defaultPage, defaultPerPage } from '../../routes/api';
import {
CASE_SAVED_OBJECT,
@ -250,7 +254,7 @@ export class CasesService {
filter,
]);
let response = await unsecuredSavedObjectsClient.find<
const response = await unsecuredSavedObjectsClient.find<
CommentAttributes,
GetCaseIdsByAlertIdAggs
>({
@ -259,23 +263,9 @@ export class CasesService {
page: 1,
perPage: 1,
sortField: defaultSortField,
aggs: this.buildCaseIdsAggs(),
aggs: this.buildCaseIdsAggs(MAX_DOCS_PER_PAGE),
filter: combinedFilter,
});
if (response.total > 100) {
response = await unsecuredSavedObjectsClient.find<
CommentAttributes,
GetCaseIdsByAlertIdAggs
>({
type: CASE_COMMENT_SAVED_OBJECT,
fields: includeFieldsRequiredForAuthentication(),
page: 1,
perPage: 1,
sortField: defaultSortField,
aggs: this.buildCaseIdsAggs(response.total),
filter: combinedFilter,
});
}
return response;
} catch (error) {
this.log.error(`Error on GET all cases for alert id ${alertId}: ${error}`);
@ -393,16 +383,6 @@ export class CasesService {
ensureSavedObjectsAreAuthorized: EnsureSOAuthCallback;
subCaseOptions?: SavedObjectFindOptionsKueryNode;
}): Promise<number> {
const casesStats = await this.findCases({
unsecuredSavedObjectsClient,
options: {
...caseOptions,
fields: [],
page: 1,
perPage: 1,
},
});
/**
* This could be made more performant. What we're doing here is retrieving all cases
* that match the API request's filters instead of just counts. This is because we need to grab
@ -429,7 +409,7 @@ export class CasesService {
...caseOptions,
fields: includeFieldsRequiredForAuthentication([caseTypeField]),
page: 1,
perPage: casesStats.total,
perPage: MAX_DOCS_PER_PAGE,
},
});
@ -447,7 +427,7 @@ export class CasesService {
if (ENABLE_CASE_CONNECTOR && subCaseOptions) {
subCasesTotal = await this.findSubCaseStatusStats({
unsecuredSavedObjectsClient,
options: cloneDeep(subCaseOptions),
options: subCaseOptions,
ids: caseIds,
});
}
@ -505,16 +485,18 @@ export class CasesService {
const refType =
associationType === AssociationType.case ? CASE_SAVED_OBJECT : SUB_CASE_SAVED_OBJECT;
const allComments = await Promise.all(
ids.map((id) =>
this.getCommentsByAssociation({
unsecuredSavedObjectsClient,
associationType,
id,
options: { page: 1, perPage: 1 },
})
)
);
const getCommentsMapper = async (id: string) =>
this.getCommentsByAssociation({
unsecuredSavedObjectsClient,
associationType,
id,
options: { page: 1, perPage: 1 },
});
// Ensuring we don't too many concurrent get running.
const allComments = await pMap(ids, getCommentsMapper, {
concurrency: MAX_CONCURRENT_SEARCHES,
});
const alerts = await this.getCommentsByAssociation({
unsecuredSavedObjectsClient,
@ -795,7 +777,7 @@ export class CasesService {
this.log.debug(`Attempting to find cases`);
return await unsecuredSavedObjectsClient.find<ESCaseAttributes>({
sortField: defaultSortField,
...cloneDeep(options),
...options,
type: CASE_SAVED_OBJECT,
});
} catch (error) {
@ -815,24 +797,16 @@ export class CasesService {
if (options?.page !== undefined || options?.perPage !== undefined) {
return unsecuredSavedObjectsClient.find<SubCaseAttributes>({
sortField: defaultSortField,
...cloneDeep(options),
...options,
type: SUB_CASE_SAVED_OBJECT,
});
}
const stats = await unsecuredSavedObjectsClient.find<SubCaseAttributes>({
fields: [],
page: 1,
perPage: 1,
sortField: defaultSortField,
...cloneDeep(options),
type: SUB_CASE_SAVED_OBJECT,
});
return unsecuredSavedObjectsClient.find<SubCaseAttributes>({
page: 1,
perPage: stats.total,
perPage: MAX_DOCS_PER_PAGE,
sortField: defaultSortField,
...cloneDeep(options),
...options,
type: SUB_CASE_SAVED_OBJECT,
});
} catch (error) {
@ -902,26 +876,16 @@ export class CasesService {
return unsecuredSavedObjectsClient.find<CommentAttributes>({
type: CASE_COMMENT_SAVED_OBJECT,
sortField: defaultSortField,
...cloneDeep(options),
...options,
});
}
// get the total number of comments that are in ES then we'll grab them all in one go
const stats = await unsecuredSavedObjectsClient.find<CommentAttributes>({
type: CASE_COMMENT_SAVED_OBJECT,
fields: [],
page: 1,
perPage: 1,
sortField: defaultSortField,
// spread the options after so the caller can override the default behavior if they want
...cloneDeep(options),
});
return unsecuredSavedObjectsClient.find<CommentAttributes>({
type: CASE_COMMENT_SAVED_OBJECT,
page: 1,
perPage: stats.total,
perPage: MAX_DOCS_PER_PAGE,
sortField: defaultSortField,
...cloneDeep(options),
...options,
});
} catch (error) {
this.log.error(`Error on GET all comments internal for ${JSON.stringify(id)}: ${error}`);
@ -1022,20 +986,13 @@ export class CasesService {
}: GetReportersArgs): Promise<SavedObjectsFindResponse<ESCaseAttributes>> {
try {
this.log.debug(`Attempting to GET all reporters`);
const firstReporters = await unsecuredSavedObjectsClient.find({
type: CASE_SAVED_OBJECT,
fields: ['created_by', OWNER_FIELD],
page: 1,
perPage: 1,
filter: cloneDeep(filter),
});
return await unsecuredSavedObjectsClient.find<ESCaseAttributes>({
type: CASE_SAVED_OBJECT,
fields: ['created_by', OWNER_FIELD],
page: 1,
perPage: firstReporters.total,
filter: cloneDeep(filter),
perPage: MAX_DOCS_PER_PAGE,
filter,
});
} catch (error) {
this.log.error(`Error on GET all reporters: ${error}`);
@ -1049,20 +1006,13 @@ export class CasesService {
}: GetTagsArgs): Promise<SavedObjectsFindResponse<ESCaseAttributes>> {
try {
this.log.debug(`Attempting to GET all cases`);
const firstTags = await unsecuredSavedObjectsClient.find({
type: CASE_SAVED_OBJECT,
fields: ['tags', OWNER_FIELD],
page: 1,
perPage: 1,
filter: cloneDeep(filter),
});
return await unsecuredSavedObjectsClient.find<ESCaseAttributes>({
type: CASE_SAVED_OBJECT,
fields: ['tags', OWNER_FIELD],
page: 1,
perPage: firstTags.total,
filter: cloneDeep(filter),
perPage: MAX_DOCS_PER_PAGE,
filter,
});
} catch (error) {
this.log.error(`Error on GET tags: ${error}`);

View file

@ -5,7 +5,6 @@
* 2.0.
*/
import { cloneDeep } from 'lodash';
import { Logger, SavedObjectsClientContract } from 'kibana/server';
import { SavedObjectFindOptionsKueryNode } from '../../common';
@ -63,7 +62,7 @@ export class CaseConfigureService {
try {
this.log.debug(`Attempting to find all case configuration`);
return await unsecuredSavedObjectsClient.find<ESCasesConfigureAttributes>({
...cloneDeep(options),
...options,
// Get the latest configuration
sortField: 'created_at',
sortOrder: 'desc',

View file

@ -12,6 +12,7 @@ import {
CASE_USER_ACTION_SAVED_OBJECT,
CASE_SAVED_OBJECT,
SUB_CASE_SAVED_OBJECT,
MAX_DOCS_PER_PAGE,
} from '../../../common/constants';
import { ClientArgs } from '..';
@ -36,19 +37,12 @@ export class CaseUserActionService {
try {
const id = subCaseId ?? caseId;
const type = subCaseId ? SUB_CASE_SAVED_OBJECT : CASE_SAVED_OBJECT;
const caseUserActionInfo = await unsecuredSavedObjectsClient.find<CaseUserActionAttributes>({
type: CASE_USER_ACTION_SAVED_OBJECT,
fields: [],
hasReference: { type, id },
page: 1,
perPage: 1,
});
return await unsecuredSavedObjectsClient.find<CaseUserActionAttributes>({
type: CASE_USER_ACTION_SAVED_OBJECT,
hasReference: { type, id },
page: 1,
perPage: caseUserActionInfo.total,
perPage: MAX_DOCS_PER_PAGE,
sortField: 'action_at',
sortOrder: 'asc',
});