fix(ee): logic for finding an entities target stage

This commit is contained in:
Jamie Howard 2023-03-29 14:43:47 +01:00
parent f5279be28c
commit d8da5b5214
2 changed files with 55 additions and 15 deletions

View File

@ -156,7 +156,7 @@ describe('Review workflows - Stages service', () => {
});
test('Should move entities in a deleted stage to the previous stage', async () => {
await stagesService.replaceWorkflowStages(1, workflowMock.stages.slice(0, 1));
await stagesService.replaceWorkflowStages(1, workflowMock.stages.slice(0, 2));
expect(servicesMock['admin::workflows'].findById).toBeCalled();
expect(entityServiceMock.create).not.toBeCalled();
@ -173,7 +173,7 @@ describe('Review workflows - Stages service', () => {
expect(servicesMock['admin::workflows'].update).toBeCalled();
expect(servicesMock['admin::workflows'].update).toBeCalledWith(workflowMock.id, {
stages: [workflowMock.stages[0].id],
stages: [workflowMock.stages[0].id, workflowMock.stages[1].id],
});
});

View File

@ -65,6 +65,8 @@ module.exports = ({ strapi }) => {
const defaultWorkflow = await getDefaultWorkflow({ strapi });
await mapAsync(updated, (stage) => this.update(stage.id, stage));
const entitiesToMove = [];
await mapAsync(deleted, async (stage) => {
// Find any entities related to this stage
const stageInfo = await this.findById(stage.id, {
@ -73,25 +75,39 @@ module.exports = ({ strapi }) => {
});
if ((stageInfo?.related?.length ?? 0) === 0) {
// If there are no related entities, just delete the stage
return this.delete(stage.id);
}
// This stage has related entities that we need to move to the
// previous stage
const previousStage =
defaultWorkflow.stages[defaultWorkflow.stages.findIndex((s) => s.id === stage.id) - 1];
// This stage has related entities that need to be moved to their
// target stage
// Find the nearest stage in the workflow that is not deleted,
// prioritizing the previous stages
const targetStageId = findNearestMatchingStageID(
defaultWorkflow.stages,
defaultWorkflow.stages.findIndex((s) => s.id === stage.id),
(targetStage) => {
return !deleted.find((s) => s.id === targetStage.id);
}
);
// Keep track of the entities to move and their target stages
entitiesToMove.push(...stageInfo.related.map((entity) => ({ ...entity, targetStageId })));
mapAsync(stageInfo.related, (entity) => {
return this.updateEntity(
{
id: entity.id,
modelUID: entity.__type,
},
previousStage.id
);
});
return this.delete(stage.id);
});
// Move all the entities whose stage was deleted to their target stage
mapAsync(entitiesToMove, (entity) => {
return this.updateEntity(
{
id: entity.id,
modelUID: entity.__type,
},
entity.targetStageId
);
});
return workflowsService.update(workflowId, {
stages: stagesIds,
});
@ -174,3 +190,27 @@ function assertAtLeastOneStageRemain(workflowStages, diffStages) {
throw new ApplicationError('At least one stage must remain in the workflow.');
}
}
/**
* Find the nearest object in an array that matches a condition.
* Starts by searching the elements before the index, then the remaining elements in the array.
*
* @param {Array} array of stages
* @param {Number} index the index to start searching from
* @param {Function} condition must evaluate to true for the object to be considered a match
* @returns {Object}
*/
function findNearestMatchingStageID(array, index, condition) {
// Start by searching the elements before the index
for (let i = index; i >= 0; i -= 1) {
if (condition(array[i])) {
return array[i].id;
}
}
// If no matching element is found before the index,
// search the remaining elements in the array
const remainingArray = array.slice(index + 1);
const nearestObject = remainingArray.filter(condition)[0];
return nearestObject.id;
}