feat(review-workflows): add update route on entity for stages

This commit is contained in:
nathan-pichon 2023-03-21 14:59:20 +01:00
parent 782009fded
commit 1cf3da856c
No known key found for this signature in database
6 changed files with 144 additions and 13 deletions

View File

@ -1,7 +1,11 @@
'use strict';
const { ApplicationError } = require('@strapi/utils/lib/errors');
const { getService } = require('../../../utils');
const { validateUpdateStages } = require('../../../validation/review-workflows');
const {
validateUpdateStages,
validateUpdateStageOnEntity,
} = require('../../../validation/review-workflows');
module.exports = {
/**
@ -54,4 +58,24 @@ module.exports = {
ctx.body = { data };
},
async updateEntity(ctx) {
const stagesService = getService('stages');
const reviewWorkflowsService = getService('review-workflows');
const { model_uid: modelUID, id: entityId } = ctx.params;
const { id: stageId } = await validateUpdateStageOnEntity(
ctx.request?.body?.data,
'You shall pass an id to the body of the put request.'
);
if (!reviewWorkflowsService.isReviewWorkflowEnabled({ strapi }, modelUID)) {
throw new ApplicationError(`Review workflows is not activated on ${modelUID}.`);
}
// TODO When multiple workflows are possible, check if the stage is part of the right one
// Didn't need this today as their can only be one workflow
ctx.body = await stagesService.updateEntity({ id: entityId, modelUID }, stageId);
},
};

View File

@ -209,4 +209,21 @@ module.exports = [
],
},
},
{
method: 'PUT',
path: '/content-manager/(collection|single)-types/:model_uid/:id/stage',
handler: 'stages.updateEntity',
config: {
middlewares: [enableFeatureMiddleware('review-workflows')],
policies: [
'admin::isAuthenticatedAdmin',
{
name: 'admin::hasPermissions',
config: {
actions: ['admin::review-workflows.read'],
},
},
],
},
},
];

View File

@ -11,7 +11,7 @@ const { ENTITY_STAGE_ATTRIBUTE } = require('../../constants/workflows');
const {
disableOnContentTypes: disableReviewWorkflows,
} = require('../../migrations/review-workflows');
const { getDefaultWorkflow } = require('../../utils/review-workflows');
const { getDefaultWorkflow, hasRWEnabled } = require('../../utils/review-workflows');
const getContentTypeUIDsWithActivatedReviewWorkflows = pipe([
// Pick only content-types with reviewWorkflows options set to true
@ -170,5 +170,9 @@ module.exports = ({ strapi }) => {
strapi.hook('strapi::content-types.afterSync').register(enableReviewWorkflow({ strapi }));
strapi.hook('strapi::content-types.afterSync').register(disableReviewWorkflows);
},
isReviewWorkflowEnabled({ strapi }, modelUID) {
const contentType = strapi.container.get('content-types').get(modelUID);
return hasRWEnabled(contentType);
},
};
};

View File

@ -5,7 +5,7 @@ const {
errors: { ApplicationError },
} = require('@strapi/utils');
const { STAGE_MODEL_UID } = require('../../constants/workflows');
const { STAGE_MODEL_UID, ENTITY_STAGE_ATTRIBUTE } = require('../../constants/workflows');
const { getService } = require('../../utils');
module.exports = ({ strapi }) => {
@ -69,6 +69,21 @@ module.exports = ({ strapi }) => {
});
});
},
/**
* Update the stage of an entity
*
* @param {object} entityInfo
* @param {string} entityInfo.id - Entity id
* @param {string} entityInfo.modelUID - the content-type of the entity
* @param {string} stageId - The id of the stage to assign to the entity
*/
updateEntity(entityInfo, stageId) {
return strapi.entityService.update(entityInfo.modelUID, entityInfo.id, {
data: { [ENTITY_STAGE_ATTRIBUTE]: Number(stageId) },
populate: [ENTITY_STAGE_ATTRIBUTE],
});
},
};
};

View File

@ -101,6 +101,20 @@ describeOnCondition(edition === 'EE')('Review workflows', () => {
await builder.cleanup();
});
beforeEach(async () => {
testWorkflow = await strapi.query(WORKFLOW_MODEL_UID).update({
where: { id: testWorkflow.id },
data: {
uid: 'workflow',
stages: [defaultStage.id, secondStage.id],
},
});
await updateContentType(productUID, {
components: [],
contentType: model,
});
});
describe('Get workflows', () => {
test("It shouldn't be available for public", async () => {
const res = await requests.public.get('/admin/review-workflows/workflows');
@ -328,8 +342,6 @@ describeOnCondition(edition === 'EE')('Review workflows', () => {
});
describe('Enabling/Disabling review workflows on a content type', () => {
let response;
beforeAll(async () => {
await createEntry(productUID, { name: 'Product' });
await createEntry(productUID, { name: 'Product 1' });
@ -343,18 +355,18 @@ describeOnCondition(edition === 'EE')('Review workflows', () => {
});
await restart();
response = await requests.admin({
const response = await requests.admin({
method: 'GET',
url: `/content-type-builder/content-types/api::product.product`,
});
expect(response.body.data.schema.reviewWorkflows).toBeTruthy();
response = await getRWMorphTableResults(strapi.db.getConnection());
const morphTableResults = await getRWMorphTableResults(strapi.db.getConnection());
expect(response.length).toEqual(3);
for (let i = 0; i < response.length; i += 1) {
const entry = response[i];
expect(morphTableResults.length).toEqual(3);
for (let i = 0; i < morphTableResults.length; i += 1) {
const entry = morphTableResults[i];
expect(entry.related_id).toEqual(i + 1);
expect(entry.order).toEqual(1);
}
@ -368,14 +380,66 @@ describeOnCondition(edition === 'EE')('Review workflows', () => {
await restart();
response = await requests.admin({
const response = await requests.admin({
method: 'GET',
url: `/content-type-builder/content-types/api::product.product`,
});
expect(response.body.data.schema.reviewWorkflows).toBeFalsy();
response = await getRWMorphTableResults(strapi.db.getConnection());
expect(response.length).toEqual(0);
const morphTableResults = await getRWMorphTableResults(strapi.db.getConnection());
expect(morphTableResults.length).toEqual(0);
});
});
describe('update a stage on an entity', () => {
describe('Review Workflow is enabled', () => {
beforeAll(async () => {
await updateContentType(productUID, {
components: [],
contentType: { ...model, reviewWorkflows: true },
});
await restart();
});
test('Should update the accordingly on an entity', async () => {
const entry = await createEntry(productUID, { name: 'Product' });
const response = await requests.admin({
method: 'PUT',
url: `/admin/content-manager/collection-types/${productUID}/${entry.id}/stage`,
body: {
data: { id: secondStage.id },
},
});
expect(response.status).toEqual(200);
expect(response.body[ENTITY_STAGE_ATTRIBUTE]).toEqual(
expect.objectContaining({ id: secondStage.id })
);
});
});
describe('Review Workflow is disabled', () => {
beforeAll(async () => {
await updateContentType(productUID, {
components: [],
contentType: { ...model, reviewWorkflows: false },
});
await restart();
});
test('Should not update the entity', async () => {
const entry = await createEntry(productUID, { name: 'Product' });
const response = await requests.admin({
method: 'PUT',
url: `/admin/content-manager/collection-types/${productUID}/${entry.id}/stage`,
body: {
data: { id: secondStage.id },
},
});
expect(response.status).toEqual(400);
expect(response.body.error).toBeDefined();
expect(response.body.error.name).toBe('ApplicationError');
});
});
});

View File

@ -8,10 +8,17 @@ const stageObject = yup.object().shape({
});
const validateUpdateStagesSchema = yup.array().of(stageObject).required();
const validateUpdateStageOnEntity = yup
.object()
.shape({
id: yup.number().integer().min(1).required(),
})
.required();
module.exports = {
validateUpdateStages: validateYupSchema(validateUpdateStagesSchema, {
strict: false,
stripUnknown: true,
}),
validateUpdateStageOnEntity: validateYupSchema(validateUpdateStageOnEntity),
};