Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Cases] Migrate connector ID to references #104221

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e6dc819
Starting configure migration
jonathan-buttner Jul 1, 2021
a62c282
Merge branch 'master' of github.com:elastic/kibana into migrate-conne…
jonathan-buttner Jul 1, 2021
ab586b0
Initial refactor of configuration connector id
jonathan-buttner Jul 1, 2021
6a0d9e2
Merge branch 'master' of github.com:elastic/kibana into migrate-conne…
jonathan-buttner Jul 1, 2021
d3be649
Merge branch 'master' of github.com:elastic/kibana into migrate-conne…
jonathan-buttner Jul 12, 2021
dbf84dd
Additional clean up and tests
jonathan-buttner Jul 13, 2021
adbb016
Merge branch 'master' of github.com:elastic/kibana into migrate-conne…
jonathan-buttner Jul 14, 2021
6c00c6b
Adding some tests
jonathan-buttner Jul 14, 2021
54cd2d2
Finishing configure tests
jonathan-buttner Jul 15, 2021
aed1b30
Starting case attributes transformation refactor
jonathan-buttner Jul 16, 2021
636c586
adding more tests for the cases service
jonathan-buttner Jul 26, 2021
37ef451
Adding more functionality and tests for cases migration
jonathan-buttner Jul 27, 2021
48a449d
Finished unit tests for cases transition
jonathan-buttner Jul 28, 2021
68137ea
Merge branch 'master' of github.com:elastic/kibana into migrate-conne…
jonathan-buttner Jul 29, 2021
2029b4e
Finished tests and moved types
jonathan-buttner Jul 29, 2021
3b530ba
Cleaning up type names
jonathan-buttner Jul 29, 2021
f653634
Fixing types and renaming
jonathan-buttner Jul 29, 2021
a1a01d2
Adding more tests directly for the transformations
jonathan-buttner Jul 29, 2021
3affb65
Fixing tests and renaming some functions
jonathan-buttner Jul 30, 2021
b27d7b8
Merge branch 'master' of github.com:elastic/kibana into migrate-conne…
jonathan-buttner Jul 30, 2021
3be7768
Adding transformation helper tests
jonathan-buttner Jul 30, 2021
d418f91
Adding migration utility tests and some clean up
jonathan-buttner Jul 30, 2021
13d8ffd
Begining logic to remove references when it is the none connector
jonathan-buttner Jul 30, 2021
17cb102
Fixing merge reference bug
jonathan-buttner Aug 2, 2021
7689a83
Merge branch 'master' of github.com:elastic/kibana into migrate-conne…
jonathan-buttner Aug 2, 2021
290ec82
Addressing feedback
jonathan-buttner Aug 2, 2021
7f7a35b
Changing test name and creating constants file
jonathan-buttner Aug 3, 2021
eb94ffd
Merge branch 'master' of github.com:elastic/kibana into migrate-conne…
jonathan-buttner Aug 3, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fixing merge reference bug
  • Loading branch information
jonathan-buttner committed Aug 2, 2021
commit 17cb10222bfff382edf58ddc502c262ead8be94e
1 change: 1 addition & 0 deletions x-pack/plugins/cases/server/client/cases/push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ export const push = async (

const [updatedCase, updatedComments] = await Promise.all([
caseService.patchCase({
originalCase: myCase,
unsecuredSavedObjectsClient,
caseId,
updatedAttributes: {
Expand Down
150 changes: 76 additions & 74 deletions x-pack/plugins/cases/server/client/cases/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,14 @@ import { Operations, OwnerEntity } from '../../authorization';
/**
* Throws an error if any of the requests attempt to update a collection style cases' status field.
*/
function throwIfUpdateStatusOfCollection(
requests: CasePatchRequest[],
casesMap: Map<string, SavedObject<CaseAttributes>>
) {
function throwIfUpdateStatusOfCollection(requests: UpdateRequestWithOriginalCase[]) {
const requestsUpdatingStatusOfCollection = requests.filter(
(req) =>
req.status !== undefined && casesMap.get(req.id)?.attributes.type === CaseType.collection
({ updateReq, originalCase }) =>
updateReq.status !== undefined && originalCase.attributes.type === CaseType.collection
);

if (requestsUpdatingStatusOfCollection.length > 0) {
const ids = requestsUpdatingStatusOfCollection.map((req) => req.id);
const ids = requestsUpdatingStatusOfCollection.map(({ updateReq }) => updateReq.id);
throw Boom.badRequest(
`Updating the status of a collection is not allowed ids: [${ids.join(', ')}]`
);
Expand All @@ -79,18 +76,14 @@ function throwIfUpdateStatusOfCollection(
/**
* Throws an error if any of the requests attempt to update a collection style case to an individual one.
*/
function throwIfUpdateTypeCollectionToIndividual(
requests: CasePatchRequest[],
casesMap: Map<string, SavedObject<CaseAttributes>>
) {
function throwIfUpdateTypeCollectionToIndividual(requests: UpdateRequestWithOriginalCase[]) {
const requestsUpdatingTypeCollectionToInd = requests.filter(
(req) =>
req.type === CaseType.individual &&
casesMap.get(req.id)?.attributes.type === CaseType.collection
({ updateReq, originalCase }) =>
updateReq.type === CaseType.individual && originalCase.attributes.type === CaseType.collection
);

if (requestsUpdatingTypeCollectionToInd.length > 0) {
const ids = requestsUpdatingTypeCollectionToInd.map((req) => req.id);
const ids = requestsUpdatingTypeCollectionToInd.map(({ updateReq }) => updateReq.id);
throw Boom.badRequest(
`Converting a collection to an individual case is not allowed ids: [${ids.join(', ')}]`
);
Expand All @@ -100,11 +93,11 @@ function throwIfUpdateTypeCollectionToIndividual(
/**
* Throws an error if any of the requests attempt to update the type of a case.
*/
function throwIfUpdateType(requests: CasePatchRequest[]) {
const requestsUpdatingType = requests.filter((req) => req.type !== undefined);
function throwIfUpdateType(requests: UpdateRequestWithOriginalCase[]) {
const requestsUpdatingType = requests.filter(({ updateReq }) => updateReq.type !== undefined);

if (requestsUpdatingType.length > 0) {
const ids = requestsUpdatingType.map((req) => req.id);
const ids = requestsUpdatingType.map(({ updateReq }) => updateReq.id);
throw Boom.badRequest(
`Updating the type of a case when sub cases are disabled is not allowed ids: [${ids.join(
', '
Expand All @@ -116,11 +109,11 @@ function throwIfUpdateType(requests: CasePatchRequest[]) {
/**
* Throws an error if any of the requests attempt to update the owner of a case.
*/
function throwIfUpdateOwner(requests: CasePatchRequest[]) {
const requestsUpdatingOwner = requests.filter((req) => req.owner !== undefined);
function throwIfUpdateOwner(requests: UpdateRequestWithOriginalCase[]) {
const requestsUpdatingOwner = requests.filter(({ updateReq }) => updateReq.owner !== undefined);

if (requestsUpdatingOwner.length > 0) {
const ids = requestsUpdatingOwner.map((req) => req.id);
const ids = requestsUpdatingOwner.map(({ updateReq }) => updateReq.id);
throw Boom.badRequest(`Updating the owner of a case is not allowed ids: [${ids.join(', ')}]`);
}
}
Expand All @@ -134,14 +127,14 @@ async function throwIfInvalidUpdateOfTypeWithAlerts({
caseService,
unsecuredSavedObjectsClient,
}: {
requests: CasePatchRequest[];
requests: UpdateRequestWithOriginalCase[];
caseService: CasesService;
unsecuredSavedObjectsClient: SavedObjectsClientContract;
}) {
const getAlertsForID = async (caseToUpdate: CasePatchRequest) => {
const getAlertsForID = async ({ updateReq }: UpdateRequestWithOriginalCase) => {
const alerts = await caseService.getAllCaseComments({
unsecuredSavedObjectsClient,
id: caseToUpdate.id,
id: updateReq.id,
options: {
fields: [],
// there should never be generated alerts attached to an individual case but we'll check anyway
Expand All @@ -157,11 +150,14 @@ async function throwIfInvalidUpdateOfTypeWithAlerts({
},
});

return { id: caseToUpdate.id, alerts };
return { id: updateReq.id, alerts };
};

const requestsUpdatingTypeField = requests.filter((req) => req.type === CaseType.collection);
const getAlertsMapper = async (caseToUpdate: CasePatchRequest) => getAlertsForID(caseToUpdate);
const requestsUpdatingTypeField = requests.filter(
({ updateReq }) => updateReq.type === CaseType.collection
);
const getAlertsMapper = async (caseToUpdate: UpdateRequestWithOriginalCase) =>
getAlertsForID(caseToUpdate);
// Ensuring we don't too many concurrent get running.
const casesAlertTotals = await pMap(requestsUpdatingTypeField, getAlertsMapper, {
concurrency: MAX_CONCURRENT_SEARCHES,
Expand All @@ -183,13 +179,13 @@ async function throwIfInvalidUpdateOfTypeWithAlerts({
/**
* Throws an error if any of the requests updates a title and the length is over MAX_TITLE_LENGTH.
*/
function throwIfTitleIsInvalid(requests: CasePatchRequest[]) {
function throwIfTitleIsInvalid(requests: UpdateRequestWithOriginalCase[]) {
const requestsInvalidTitle = requests.filter(
(req) => req.title !== undefined && req.title.length > MAX_TITLE_LENGTH
({ updateReq }) => updateReq.title !== undefined && updateReq.title.length > MAX_TITLE_LENGTH
);

if (requestsInvalidTitle.length > 0) {
const ids = requestsInvalidTitle.map((req) => req.id);
const ids = requestsInvalidTitle.map(({ updateReq }) => updateReq.id);
throw Boom.badRequest(
`The length of the title is too long. The maximum length is ${MAX_TITLE_LENGTH}, ids: [${ids.join(
', '
Expand All @@ -216,11 +212,11 @@ async function getAlertComments({
caseService,
unsecuredSavedObjectsClient,
}: {
casesToSync: CasePatchRequest[];
casesToSync: UpdateRequestWithOriginalCase[];
caseService: CasesService;
unsecuredSavedObjectsClient: SavedObjectsClientContract;
}): Promise<SavedObjectsFindResponse<CommentAttributes>> {
const idsOfCasesToSync = casesToSync.map((casePatchReq) => casePatchReq.id);
const idsOfCasesToSync = casesToSync.map(({ updateReq }) => updateReq.id);

// getAllCaseComments will by default get all the comments, unless page or perPage fields are set
return caseService.getAllCaseComments({
Expand Down Expand Up @@ -308,14 +304,12 @@ function getSyncStatusForComment({
async function updateAlerts({
casesWithSyncSettingChangedToOn,
casesWithStatusChangedAndSynced,
casesMap,
caseService,
unsecuredSavedObjectsClient,
casesClientInternal,
}: {
casesWithSyncSettingChangedToOn: CasePatchRequest[];
casesWithStatusChangedAndSynced: CasePatchRequest[];
casesMap: Map<string, SavedObject<CaseAttributes>>;
casesWithSyncSettingChangedToOn: UpdateRequestWithOriginalCase[];
casesWithStatusChangedAndSynced: UpdateRequestWithOriginalCase[];
caseService: CasesService;
unsecuredSavedObjectsClient: SavedObjectsClientContract;
casesClientInternal: CasesClientInternal;
Expand All @@ -329,11 +323,8 @@ async function updateAlerts({
// build a map of case id to the status it has
// this will have collections in it but the alerts should be associated to sub cases and not collections so it shouldn't
// matter.
const casesToSyncToStatus = casesToSync.reduce((acc, caseInfo) => {
acc.set(
caseInfo.id,
caseInfo.status ?? casesMap.get(caseInfo.id)?.attributes.status ?? CaseStatuses.open
);
const casesToSyncToStatus = casesToSync.reduce((acc, { updateReq, originalCase }) => {
acc.set(updateReq.id, updateReq.status ?? originalCase.attributes.status ?? CaseStatuses.open);
return acc;
}, new Map<string, CaseStatuses>());

Expand Down Expand Up @@ -407,6 +398,11 @@ function partitionPatchRequest(
};
}

interface UpdateRequestWithOriginalCase {
updateReq: CasePatchRequest;
originalCase: SavedObject<CaseAttributes>;
}

/**
* Updates the specified cases with new values
*
Expand Down Expand Up @@ -467,33 +463,41 @@ export const update = async (
);
}

const updateCases: CasePatchRequest[] = query.cases.map((updateCase) => {
const currentCase = myCases.saved_objects.find((c) => c.id === updateCase.id);
const { id, version } = updateCase;
return currentCase != null
? getCaseToUpdate(currentCase.attributes, updateCase)
: { id, version };
});
const updateCases: UpdateRequestWithOriginalCase[] = query.cases.reduce(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block now zips togethers the original case if it existed and the updated request. This is particularly useful for when we call the patch request so that we can populate the originalCase field. If the original case cannot be found within the map we skip the request. This shouldn't even happen though because the code above should handle finding requests that don't exist.

(acc: UpdateRequestWithOriginalCase[], updateCase) => {
const originalCase = casesMap.get(updateCase.id);

const updateFilterCases = updateCases.filter((updateCase) => {
const { id, version, ...updateCaseAttributes } = updateCase;
return Object.keys(updateCaseAttributes).length > 0;
});
if (!originalCase) {
return acc;
}

const fieldsToUpdate = getCaseToUpdate(originalCase.attributes, updateCase);

if (updateFilterCases.length <= 0) {
const { id, version, ...restFields } = fieldsToUpdate;

if (Object.keys(restFields).length > 0) {
acc.push({ originalCase, updateReq: fieldsToUpdate });
}

return acc;
},
[]
);

if (updateCases.length <= 0) {
throw Boom.notAcceptable('All update fields are identical to current version.');
}

if (!ENABLE_CASE_CONNECTOR) {
throwIfUpdateType(updateFilterCases);
throwIfUpdateType(updateCases);
}

throwIfUpdateOwner(updateFilterCases);
throwIfTitleIsInvalid(updateFilterCases);
throwIfUpdateStatusOfCollection(updateFilterCases, casesMap);
throwIfUpdateTypeCollectionToIndividual(updateFilterCases, casesMap);
throwIfUpdateOwner(updateCases);
throwIfTitleIsInvalid(updateCases);
throwIfUpdateStatusOfCollection(updateCases);
throwIfUpdateTypeCollectionToIndividual(updateCases);
await throwIfInvalidUpdateOfTypeWithAlerts({
requests: updateFilterCases,
requests: updateCases,
caseService,
unsecuredSavedObjectsClient,
});
Expand All @@ -503,9 +507,9 @@ export const update = async (
const updatedDt = new Date().toISOString();
const updatedCases = await caseService.patchCases({
unsecuredSavedObjectsClient,
cases: updateFilterCases.map((thisCase) => {
cases: updateCases.map(({ updateReq, originalCase }) => {
// intentionally removing owner from the case so that we don't accidentally allow it to be updated
const { id: caseId, version, owner, ...updateCaseAttributes } = thisCase;
const { id: caseId, version, owner, ...updateCaseAttributes } = updateReq;
let closedInfo = {};
if (updateCaseAttributes.status && updateCaseAttributes.status === CaseStatuses.closed) {
closedInfo = {
Expand All @@ -524,6 +528,7 @@ export const update = async (
}
return {
caseId,
originalCase,
updatedAttributes: {
...updateCaseAttributes,
...closedInfo,
Expand All @@ -537,25 +542,23 @@ export const update = async (

// If a status update occurred and the case is synced then we need to update all alerts' status
// attached to the case to the new status.
const casesWithStatusChangedAndSynced = updateFilterCases.filter((caseToUpdate) => {
const currentCase = myCases.saved_objects.find((c) => c.id === caseToUpdate.id);
const casesWithStatusChangedAndSynced = updateCases.filter(({ updateReq, originalCase }) => {
return (
currentCase != null &&
caseToUpdate.status != null &&
currentCase.attributes.status !== caseToUpdate.status &&
currentCase.attributes.settings.syncAlerts
originalCase != null &&
updateReq.status != null &&
originalCase.attributes.status !== updateReq.status &&
originalCase.attributes.settings.syncAlerts
);
});

// If syncAlerts setting turned on we need to update all alerts' status
// attached to the case to the current status.
const casesWithSyncSettingChangedToOn = updateFilterCases.filter((caseToUpdate) => {
const currentCase = myCases.saved_objects.find((c) => c.id === caseToUpdate.id);
const casesWithSyncSettingChangedToOn = updateCases.filter(({ updateReq, originalCase }) => {
return (
currentCase != null &&
caseToUpdate.settings?.syncAlerts != null &&
currentCase.attributes.settings.syncAlerts !== caseToUpdate.settings.syncAlerts &&
caseToUpdate.settings.syncAlerts
originalCase != null &&
updateReq.settings?.syncAlerts != null &&
originalCase.attributes.settings.syncAlerts !== updateReq.settings.syncAlerts &&
updateReq.settings.syncAlerts
);
});

Expand All @@ -566,7 +569,6 @@ export const update = async (
caseService,
unsecuredSavedObjectsClient,
casesClientInternal,
casesMap,
});

const returnUpdatedCase = myCases.saved_objects
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/cases/server/client/configure/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ async function update(
updated_at: updateDate,
updated_by: user,
},
originalConfiguration: configuration,
});

return CaseConfigureResponseRt.encode({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ export class CommentableCase {
}

const updatedCase = await this.caseService.patchCase({
originalCase: this.collection,
unsecuredSavedObjectsClient: this.unsecuredSavedObjectsClient,
caseId: this.collection.id,
updatedAttributes: {
Expand Down
Loading