feat(core): block workflow update on interim change (#4397)

*  Add `updatedAt` to store

*  Set `updatedAt` in store

* 👕 Update FE types

* 👕 Update BE types

*  Set `updatedAt` on workflow open

*  Add endpoint check

*  Add first update check

* 🔥 Remove log

*  Simplify check

*  Make `makeWorkflow` more flexible

* 🗃️ Make `updatedAt` default consistent

* 🧪 Adjust tests checking for `updatedAt`

* 🧪 Add tests for interim changes block

* ✏️ Remove unneeded quotes

*  Simplify without using `-1`

* 👕 Simplify interfaces

* 🐛 Fix calls to `setWorkflowUpdatedAt` setter

* :track: Move update to API call

*  Restrict check to multiple users only

* 🧪 Add more tests

* 🐛 Account for activation outside of canvas

* ✏️ Add warning comment

* 🔥 Remove unneeded check

*  Revert to `new Date()` for `-1`

* 🐛 Fix display for never updated
This commit is contained in:
Iván Ovejero
2022-10-25 09:08:06 +02:00
committed by GitHub
parent 77233f2370
commit cddd012a2f
11 changed files with 208 additions and 10 deletions

View File

@@ -295,3 +295,153 @@ describe('POST /workflows', () => {
expect(usedCredentials).toHaveLength(1);
});
});
describe('PATCH /workflows/:id', () => {
it('should block owner update on interim update by member', async () => {
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
const member = await testDb.createUser({ globalRole: globalMemberRole });
// owner creates and shares workflow
const createResponse = await authAgent(owner).post('/workflows').send(makeWorkflow());
const { id, updatedAt: ownerLastKnownDate } = createResponse.body.data;
await authAgent(owner)
.put(`/workflows/${id}/share`)
.send({ shareWithIds: [member.id] });
// member accesses and updates workflow
const memberGetResponse = await authAgent(member).get(`/workflows/${id}`);
const { updatedAt: memberLastKnownDate } = memberGetResponse.body.data;
await authAgent(member)
.patch(`/workflows/${id}`)
.send({ name: 'Update by member', updatedAt: memberLastKnownDate });
// owner blocked from updating workflow
const updateAttemptResponse = await authAgent(owner)
.patch(`/workflows/${id}`)
.send({ name: 'Update attempt by owner', updatedAt: ownerLastKnownDate });
expect(updateAttemptResponse.status).toBe(400);
expect(updateAttemptResponse.body.message).toContain(
'cannot be saved because it was changed by another user',
);
});
it('should block member update on interim update by owner', async () => {
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
const member = await testDb.createUser({ globalRole: globalMemberRole });
// owner creates, updates and shares workflow
const createResponse = await authAgent(owner).post('/workflows').send(makeWorkflow());
const { id, updatedAt: ownerFirstUpdateDate } = createResponse.body.data;
const updateResponse = await authAgent(owner)
.patch(`/workflows/${id}`)
.send({ name: 'Update by owner', updatedAt: ownerFirstUpdateDate });
const { updatedAt: ownerSecondUpdateDate } = updateResponse.body.data;
await authAgent(owner)
.put(`/workflows/${id}/share`)
.send({ shareWithIds: [member.id] });
// member accesses workflow
const memberGetResponse = await authAgent(member).get(`/workflows/${id}`);
const { updatedAt: memberLastKnownDate } = memberGetResponse.body.data;
// owner re-updates workflow
await authAgent(owner)
.patch(`/workflows/${id}`)
.send({ name: 'Owner update again', updatedAt: ownerSecondUpdateDate });
// member blocked from updating workflow
const updateAttemptResponse = await authAgent(member)
.patch(`/workflows/${id}`)
.send({ name: 'Update attempt by member', updatedAt: memberLastKnownDate });
expect(updateAttemptResponse.status).toBe(400);
expect(updateAttemptResponse.body.message).toContain(
'cannot be saved because it was changed by another user',
);
});
it('should block owner activation on interim activation by member', async () => {
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
const member = await testDb.createUser({ globalRole: globalMemberRole });
// owner creates and shares workflow
const createResponse = await authAgent(owner).post('/workflows').send(makeWorkflow());
const { id, updatedAt: ownerLastKnownDate } = createResponse.body.data;
await authAgent(owner)
.put(`/workflows/${id}/share`)
.send({ shareWithIds: [member.id] });
// member accesses and activates workflow
const memberGetResponse = await authAgent(member).get(`/workflows/${id}`);
const { updatedAt: memberLastKnownDate } = memberGetResponse.body.data;
await authAgent(member)
.patch(`/workflows/${id}`)
.send({ active: true, updatedAt: memberLastKnownDate });
// owner blocked from activating workflow
const activationAttemptResponse = await authAgent(owner)
.patch(`/workflows/${id}`)
.send({ active: true, updatedAt: ownerLastKnownDate });
expect(activationAttemptResponse.status).toBe(400);
expect(activationAttemptResponse.body.message).toContain(
'cannot be saved because it was changed by another user',
);
});
it('should block member activation on interim activation by owner', async () => {
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
const member = await testDb.createUser({ globalRole: globalMemberRole });
// owner creates, updates and shares workflow
const createResponse = await authAgent(owner).post('/workflows').send(makeWorkflow());
const { id, updatedAt: ownerFirstUpdateDate } = createResponse.body.data;
const updateResponse = await authAgent(owner)
.patch(`/workflows/${id}`)
.send({ name: 'Update by owner', updatedAt: ownerFirstUpdateDate });
const { updatedAt: ownerSecondUpdateDate } = updateResponse.body.data;
await authAgent(owner)
.put(`/workflows/${id}/share`)
.send({ shareWithIds: [member.id] });
// member accesses workflow
const memberGetResponse = await authAgent(member).get(`/workflows/${id}`);
const { updatedAt: memberLastKnownDate } = memberGetResponse.body.data;
// owner activates workflow
await authAgent(owner)
.patch(`/workflows/${id}`)
.send({ active: true, updatedAt: ownerSecondUpdateDate });
// member blocked from activating workflow
const updateAttemptResponse = await authAgent(member)
.patch(`/workflows/${id}`)
.send({ active: true, updatedAt: memberLastKnownDate });
expect(updateAttemptResponse.status).toBe(400);
expect(updateAttemptResponse.body.message).toContain(
'cannot be saved because it was changed by another user',
);
});
});