mirror of
https://github.com/strapi/strapi.git
synced 2025-09-25 00:09:18 +00:00
Merge pull request #15637 from strapi/feature/review-worflow/default-workflow
feat(review-workflows): add default workflow
This commit is contained in:
commit
bbfdda2b14
7
packages/core/admin/ee/server/bootstrap.js
vendored
7
packages/core/admin/ee/server/bootstrap.js
vendored
@ -17,7 +17,12 @@ module.exports = async () => {
|
||||
await actionProvider.registerMany(actions.auditLogs);
|
||||
}
|
||||
|
||||
// TODO: check admin seats
|
||||
if (features.isEnabled('review-workflows')) {
|
||||
const { bootstrap: rwBootstrap } = getService('review-workflows');
|
||||
|
||||
await rwBootstrap();
|
||||
}
|
||||
|
||||
// TODO: check admin seats
|
||||
await executeCEBootstrap();
|
||||
};
|
||||
|
14
packages/core/admin/ee/server/constants/default-stages.json
Normal file
14
packages/core/admin/ee/server/constants/default-stages.json
Normal file
@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"name": "To do"
|
||||
},
|
||||
{
|
||||
"name": "Ready to review"
|
||||
},
|
||||
{
|
||||
"name": "In progress"
|
||||
},
|
||||
{
|
||||
"name": "Reviewed"
|
||||
}
|
||||
]
|
@ -0,0 +1 @@
|
||||
{}
|
@ -0,0 +1,83 @@
|
||||
'use strict';
|
||||
|
||||
const reviewWorkflowsServiceFactory = require('../review-workflows/review-workflows');
|
||||
|
||||
const workflowMock = {
|
||||
id: 1,
|
||||
};
|
||||
const stagesMock = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'stage 1',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'stage 2',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'stage 3',
|
||||
},
|
||||
];
|
||||
|
||||
const workflowsServiceMock = {
|
||||
count: jest.fn(() => 0),
|
||||
create: jest.fn(() => workflowMock),
|
||||
};
|
||||
const stagesServiceMock = {
|
||||
count: jest.fn(() => 0),
|
||||
createMany: jest.fn(() => stagesMock),
|
||||
};
|
||||
|
||||
const strapiMock = {
|
||||
service(serviceName) {
|
||||
switch (serviceName) {
|
||||
case 'admin::stages':
|
||||
return stagesServiceMock;
|
||||
case 'admin::workflows':
|
||||
return workflowsServiceMock;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const reviewWorkflowsService = reviewWorkflowsServiceFactory({ strapi: strapiMock });
|
||||
|
||||
describe('Review workflows service', () => {
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('bootstrap', () => {
|
||||
test('Without stages or workflows in DB', async () => {
|
||||
await reviewWorkflowsService.bootstrap();
|
||||
|
||||
expect(workflowsServiceMock.count).toBeCalled();
|
||||
expect(stagesServiceMock.count).toBeCalled();
|
||||
|
||||
expect(stagesServiceMock.createMany).toBeCalled();
|
||||
expect(workflowsServiceMock.create).toBeCalled();
|
||||
});
|
||||
test('With a workflow in DB', async () => {
|
||||
workflowsServiceMock.count.mockResolvedValue(1);
|
||||
await reviewWorkflowsService.bootstrap();
|
||||
|
||||
expect(workflowsServiceMock.count).toBeCalled();
|
||||
expect(stagesServiceMock.count).toBeCalled();
|
||||
|
||||
expect(stagesServiceMock.createMany).not.toBeCalled();
|
||||
expect(workflowsServiceMock.create).not.toBeCalled();
|
||||
});
|
||||
test('With stages in DB', async () => {
|
||||
stagesServiceMock.count.mockResolvedValue(5);
|
||||
await reviewWorkflowsService.bootstrap();
|
||||
|
||||
expect(workflowsServiceMock.count).toBeCalled();
|
||||
expect(stagesServiceMock.count).toBeCalled();
|
||||
|
||||
expect(stagesServiceMock.createMany).not.toBeCalled();
|
||||
expect(workflowsServiceMock.create).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
});
|
@ -49,7 +49,7 @@ describe('Review workflows - Stages service', () => {
|
||||
expect(entityServiceMock.findOne).not.toBeCalled();
|
||||
expect(entityServiceMock.findMany).toBeCalled();
|
||||
expect(entityServiceMock.findMany).toBeCalledWith(STAGE_MODEL_UID, {
|
||||
filter: { workflow: 1 },
|
||||
filters: { workflow: 1 },
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -60,7 +60,7 @@ describe('Review workflows - Stages service', () => {
|
||||
expect(entityServiceMock.findMany).not.toBeCalled();
|
||||
expect(entityServiceMock.findOne).toBeCalled();
|
||||
expect(entityServiceMock.findOne).toBeCalledWith(STAGE_MODEL_UID, 1, {
|
||||
filter: { workflow: 1 },
|
||||
filters: { workflow: 1 },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -5,4 +5,5 @@ module.exports = {
|
||||
role: require('./role'),
|
||||
workflows: require('./review-workflows/workflows'),
|
||||
stages: require('./review-workflows/stages'),
|
||||
'review-workflows': require('./review-workflows/review-workflows'),
|
||||
};
|
||||
|
@ -0,0 +1,54 @@
|
||||
'use strict';
|
||||
|
||||
const { getService } = require('../../utils');
|
||||
|
||||
/**
|
||||
* Map every stage in the array to be ordered in the relation
|
||||
* @param {Object[]} stages
|
||||
* @param {number} stages.id
|
||||
* @return {Object[]}
|
||||
*/
|
||||
function buildStagesConnectArray(stages) {
|
||||
return stages.map((stage, index) => {
|
||||
const connect = {
|
||||
id: stage.id,
|
||||
position: {},
|
||||
};
|
||||
|
||||
if (index === 0) {
|
||||
connect.position.start = true;
|
||||
} else {
|
||||
connect.position.after = stages[index - 1].id;
|
||||
}
|
||||
return connect;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = ({ strapi }) => {
|
||||
const workflowsService = getService('workflows', { strapi });
|
||||
const stagesService = getService('stages', { strapi });
|
||||
|
||||
return {
|
||||
async bootstrap() {
|
||||
const wfCount = await workflowsService.count();
|
||||
const stagesCount = await stagesService.count();
|
||||
|
||||
// Check if there is nothing about review-workflow in DB
|
||||
// If any, the feature has already been initialized with a workflow and stages
|
||||
if (wfCount === 0 && stagesCount === 0) {
|
||||
const defaultStages = require('../../constants/default-stages.json');
|
||||
const defaultWorkflow = require('../../constants/default-workflow.json');
|
||||
|
||||
const stages = await stagesService.createMany(defaultStages, { fields: ['id'] });
|
||||
const workflow = {
|
||||
...defaultWorkflow,
|
||||
stages: {
|
||||
connect: buildStagesConnectArray(stages),
|
||||
},
|
||||
};
|
||||
|
||||
await workflowsService.create(workflow);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
@ -5,7 +5,7 @@ const { STAGE_MODEL_UID } = require('../../constants/workflows');
|
||||
module.exports = ({ strapi }) => ({
|
||||
find({ workflowId, populate }) {
|
||||
const params = {
|
||||
filter: { workflow: workflowId },
|
||||
filters: { workflow: workflowId },
|
||||
populate,
|
||||
};
|
||||
return strapi.entityService.findMany(STAGE_MODEL_UID, params);
|
||||
@ -13,9 +13,24 @@ module.exports = ({ strapi }) => ({
|
||||
|
||||
findById(id, { workflowId, populate }) {
|
||||
const params = {
|
||||
filter: { workflow: workflowId },
|
||||
filters: { workflow: workflowId },
|
||||
populate,
|
||||
};
|
||||
return strapi.entityService.findOne(STAGE_MODEL_UID, id, params);
|
||||
},
|
||||
|
||||
createMany(stagesList, { fields }) {
|
||||
const params = {
|
||||
select: fields,
|
||||
};
|
||||
return Promise.all(
|
||||
stagesList.map((stage) =>
|
||||
strapi.entityService.create(STAGE_MODEL_UID, { data: stage, ...params })
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
count() {
|
||||
return strapi.entityService.count(STAGE_MODEL_UID);
|
||||
},
|
||||
});
|
||||
|
@ -10,4 +10,12 @@ module.exports = ({ strapi }) => ({
|
||||
findById(id, opts) {
|
||||
return strapi.entityService.findOne(WORKFLOW_MODEL_UID, id, opts);
|
||||
},
|
||||
|
||||
create(workflowData) {
|
||||
return strapi.entityService.create(WORKFLOW_MODEL_UID, { data: workflowData });
|
||||
},
|
||||
|
||||
count() {
|
||||
return strapi.entityService.count(WORKFLOW_MODEL_UID);
|
||||
},
|
||||
});
|
||||
|
@ -16,7 +16,7 @@ describeOnCondition(edition === 'EE')('Review workflows', () => {
|
||||
let hasRW;
|
||||
let defaultStage;
|
||||
let secondStage;
|
||||
let defaultWorkflow;
|
||||
let testWorkflow;
|
||||
|
||||
beforeAll(async () => {
|
||||
strapi = await createStrapiInstance();
|
||||
@ -32,7 +32,7 @@ describeOnCondition(edition === 'EE')('Review workflows', () => {
|
||||
secondStage = await strapi.query(STAGE_MODEL_UID).create({
|
||||
data: { name: 'Stage 2' },
|
||||
});
|
||||
defaultWorkflow = await strapi.query(WORKFLOW_MODEL_UID).create({
|
||||
testWorkflow = await strapi.query(WORKFLOW_MODEL_UID).create({
|
||||
data: {
|
||||
uid: 'workflow',
|
||||
stages: [defaultStage.id, secondStage.id],
|
||||
@ -61,7 +61,8 @@ describeOnCondition(edition === 'EE')('Review workflows', () => {
|
||||
if (hasRW) {
|
||||
expect(res.status).toBe(200);
|
||||
expect(Array.isArray(res.body.data)).toBeTruthy();
|
||||
expect(res.body.data).toHaveLength(1);
|
||||
// Why 2 workflows ? One added by the test, the other one should be the default workflow added in bootstrap
|
||||
expect(res.body.data).toHaveLength(2);
|
||||
} else {
|
||||
expect(res.status).toBe(404);
|
||||
expect(Array.isArray(res.body)).toBeFalsy();
|
||||
@ -70,9 +71,34 @@ describeOnCondition(edition === 'EE')('Review workflows', () => {
|
||||
});
|
||||
|
||||
describe('Get one workflow', () => {
|
||||
test("It shouldn't be available for public", async () => {
|
||||
const res = await requests.public.get(`/admin/review-workflows/workflows/${testWorkflow.id}`);
|
||||
|
||||
if (hasRW) {
|
||||
expect(res.status).toBe(401);
|
||||
} else {
|
||||
expect(res.status).toBe(404);
|
||||
expect(res.body.data).toBeUndefined();
|
||||
}
|
||||
});
|
||||
test('It should be available for every connected users (admin)', async () => {
|
||||
const res = await requests.admin.get(`/admin/review-workflows/workflows/${testWorkflow.id}`);
|
||||
|
||||
if (hasRW) {
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body.data).toBeInstanceOf(Object);
|
||||
expect(res.body.data).toEqual(testWorkflow);
|
||||
} else {
|
||||
expect(res.status).toBe(404);
|
||||
expect(res.body.data).toBeUndefined();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Get workflow stages', () => {
|
||||
test("It shouldn't be available for public", async () => {
|
||||
const res = await requests.public.get(
|
||||
`/admin/review-workflows/workflows/${defaultWorkflow.id}`
|
||||
`/admin/review-workflows/workflows/${testWorkflow.id}?populate=stages`
|
||||
);
|
||||
|
||||
if (hasRW) {
|
||||
@ -84,40 +110,14 @@ describeOnCondition(edition === 'EE')('Review workflows', () => {
|
||||
});
|
||||
test('It should be available for every connected users (admin)', async () => {
|
||||
const res = await requests.admin.get(
|
||||
`/admin/review-workflows/workflows/${defaultWorkflow.id}`
|
||||
`/admin/review-workflows/workflows/${testWorkflow.id}?populate=stages`
|
||||
);
|
||||
|
||||
if (hasRW) {
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body.data).toBeInstanceOf(Object);
|
||||
expect(res.body.data).toEqual(defaultWorkflow);
|
||||
} else {
|
||||
expect(res.status).toBe(404);
|
||||
expect(res.body.data).toBeUndefined();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Get workflow stages', () => {
|
||||
test("It shouldn't be available for public", async () => {
|
||||
const res = await requests.public.get('/admin/review-workflows/workflows?populate=stages');
|
||||
|
||||
if (hasRW) {
|
||||
expect(res.status).toBe(401);
|
||||
} else {
|
||||
expect(res.status).toBe(404);
|
||||
expect(res.body.data).toBeUndefined();
|
||||
}
|
||||
});
|
||||
test('It should be available for every connected users (admin)', async () => {
|
||||
const res = await requests.admin.get('/admin/review-workflows/workflows?populate=stages');
|
||||
|
||||
if (hasRW) {
|
||||
expect(res.status).toBe(200);
|
||||
expect(Array.isArray(res.body.data)).toBeTruthy();
|
||||
expect(res.body.data).toHaveLength(1);
|
||||
expect(res.body.data[0].stages).toHaveLength(2);
|
||||
expect(res.body.data[0].stages[0]).toEqual(defaultStage);
|
||||
expect(res.body.data.stages).toBeInstanceOf(Array);
|
||||
expect(res.body.data.stages).toHaveLength(2);
|
||||
} else {
|
||||
expect(res.status).toBe(404);
|
||||
expect(Array.isArray(res.body)).toBeFalsy();
|
||||
@ -128,7 +128,7 @@ describeOnCondition(edition === 'EE')('Review workflows', () => {
|
||||
describe('Get stages', () => {
|
||||
test("It shouldn't be available for public", async () => {
|
||||
const res = await requests.public.get(
|
||||
`/admin/review-workflows/workflows/${defaultWorkflow.id}/stages`
|
||||
`/admin/review-workflows/workflows/${testWorkflow.id}/stages`
|
||||
);
|
||||
|
||||
if (hasRW) {
|
||||
@ -140,7 +140,7 @@ describeOnCondition(edition === 'EE')('Review workflows', () => {
|
||||
});
|
||||
test('It should be available for every connected users (admin)', async () => {
|
||||
const res = await requests.admin.get(
|
||||
`/admin/review-workflows/workflows/${defaultWorkflow.id}/stages`
|
||||
`/admin/review-workflows/workflows/${testWorkflow.id}/stages`
|
||||
);
|
||||
|
||||
if (hasRW) {
|
||||
@ -157,7 +157,7 @@ describeOnCondition(edition === 'EE')('Review workflows', () => {
|
||||
describe('Get stage by id', () => {
|
||||
test("It shouldn't be available for public", async () => {
|
||||
const res = await requests.public.get(
|
||||
`/admin/review-workflows/workflows/${defaultWorkflow.id}/stages/${secondStage.id}`
|
||||
`/admin/review-workflows/workflows/${testWorkflow.id}/stages/${secondStage.id}`
|
||||
);
|
||||
|
||||
if (hasRW) {
|
||||
@ -169,7 +169,7 @@ describeOnCondition(edition === 'EE')('Review workflows', () => {
|
||||
});
|
||||
test('It should be available for every connected users (admin)', async () => {
|
||||
const res = await requests.admin.get(
|
||||
`/admin/review-workflows/workflows/${defaultWorkflow.id}/stages/${secondStage.id}`
|
||||
`/admin/review-workflows/workflows/${testWorkflow.id}/stages/${secondStage.id}`
|
||||
);
|
||||
|
||||
if (hasRW) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const getService = (name) => {
|
||||
const getService = (name, { strapi } = { strapi: global.strapi }) => {
|
||||
return strapi.service(`admin::${name}`);
|
||||
};
|
||||
module.exports = {
|
||||
|
7
packages/core/utils/lib/async.d.ts
vendored
7
packages/core/utils/lib/async.d.ts
vendored
@ -1 +1,6 @@
|
||||
export type MapAsync<T = any, R = any> = lodash.CurriedFunction3<T[], (element: T, index: number) => R | Promise<R>, { concurrency?: number }, Promise<R[]>>;
|
||||
export type MapAsync<T = any, R = any> = lodash.CurriedFunction3<
|
||||
T[],
|
||||
(element: T, index: number) => R | Promise<R>,
|
||||
{ concurrency?: number },
|
||||
Promise<R[]>
|
||||
>;
|
||||
|
Loading…
x
Reference in New Issue
Block a user