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

feat(datatrakWeb): RN-1391: Generate system comments for tasks #5806

Merged
merged 49 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
c9db4eb
Create task comment table and generate types
alexd-bes Jul 19, 2024
743a53a
Create Model
alexd-bes Jul 19, 2024
cc3bae3
WIP
alexd-bes Jul 19, 2024
580a6d4
WIP
alexd-bes Jul 19, 2024
ced6958
Merge branch 'epic-tasks' into rn-1331-task-comments-setup
alexd-bes Jul 21, 2024
54f00c1
Working endpoints
alexd-bes Jul 21, 2024
07e162d
Generate types
alexd-bes Jul 21, 2024
fb63c4f
Fix test
alexd-bes Jul 21, 2024
307570f
Create task with comments
alexd-bes Jul 21, 2024
8daeb38
Update 20240719015050-AddTaskCommentsTable-modifies-schema.js
alexd-bes Jul 21, 2024
912095b
View comments on task details
alexd-bes Jul 21, 2024
45cd9dc
Allow create comment on edit task
alexd-bes Jul 21, 2024
bc2afa9
Display comments count on table
alexd-bes Jul 21, 2024
6243d92
Show comment count on tasks tile on landing page
alexd-bes Jul 21, 2024
5e1dc87
Fix up types
alexd-bes Jul 22, 2024
4271c1d
Order comments descending
alexd-bes Jul 22, 2024
0288187
Update schemas.ts
alexd-bes Jul 22, 2024
ca95795
addTaskComment on model
alexd-bes Jul 22, 2024
b822a6d
Generate comment on edit task
alexd-bes Jul 22, 2024
8dc501a
Generate comments on create of task
alexd-bes Jul 22, 2024
802e108
Merge branch 'epic-tasks' into rn-1331-task-comments-setup
alexd-bes Jul 22, 2024
a19a58f
Merge branch 'rn-1331-task-comments-setup' into rn-1331-task-comments-ui
alexd-bes Jul 22, 2024
ea5799d
Merge branch 'rn-1331-task-comments-ui' into rn-1391-system-comments
alexd-bes Jul 22, 2024
eca48fd
Generate system message on task completion
alexd-bes Jul 22, 2024
5bce652
Syetm task styling
alexd-bes Jul 22, 2024
3302f55
Handle editing a task with just a comment
alexd-bes Jul 22, 2024
28f02c0
Move comment handling to central server
alexd-bes Jul 22, 2024
ccd8d07
Move comment adding to central server
alexd-bes Jul 22, 2024
ce615db
Update schemas.ts
alexd-bes Jul 22, 2024
de21d53
Fix types
alexd-bes Jul 22, 2024
498b1be
Remove task comments endpoints
alexd-bes Jul 22, 2024
25bcb36
Merge branch 'rn-1331-task-comments-setup' into rn-1331-task-comments-ui
alexd-bes Jul 22, 2024
0446634
Merge branch 'rn-1331-task-comments-ui' into rn-1391-system-comments
alexd-bes Jul 22, 2024
73e9bbf
build fixes
alexd-bes Jul 23, 2024
0cb896d
Merge branch 'rn-1331-task-comments-ui' into rn-1391-system-comments
alexd-bes Jul 23, 2024
b7e6a90
Add comments
alexd-bes Jul 23, 2024
4b71fd5
Revert "Remove task comments endpoints"
alexd-bes Jul 23, 2024
3d46511
Remove task comment create endpoint as no longer needed
alexd-bes Jul 23, 2024
baf0fa8
Revert "Remove task comments endpoints"
alexd-bes Jul 23, 2024
d0c82a2
Fix revert
alexd-bes Jul 23, 2024
20fe19b
Merge branch 'rn-1331-task-comments-setup' into rn-1331-task-comments-ui
alexd-bes Jul 23, 2024
c5af14a
Merge branch 'rn-1331-task-comments-ui' into rn-1391-system-comments
alexd-bes Jul 23, 2024
3739d5b
Handle recurring task comments
alexd-bes Jul 23, 2024
b92b94f
feat(datatrakWeb): RN-1331: Task comments UI (#5801)
alexd-bes Jul 25, 2024
267a06e
Merge branch 'rn-1331-task-comments-setup' into rn-1391-system-comments
alexd-bes Jul 25, 2024
7421982
Fix import order
alexd-bes Jul 25, 2024
8653553
PR changes
alexd-bes Jul 25, 2024
8dbd6e4
Merge branch 'epic-tasks' into rn-1391-system-comments
alexd-bes Jul 25, 2024
befced2
Fix change handler tests
alexd-bes Jul 26, 2024
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
16 changes: 11 additions & 5 deletions packages/central-server/src/apiV2/tasks/CreateTask.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,19 @@ export class CreateTask extends CreateHandler {
}

async createRecord() {
const { comment, ...data } = this.newRecordData;

return this.models.wrapInTransaction(async transactingModels => {
const task = await transactingModels.task.create(data);
const { comment, ...newRecordData } = this.newRecordData;
await this.models.wrapInTransaction(async transactingModels => {
const task = await transactingModels.task.create(newRecordData);
// Add the user comment first, since the transaction will mean that all comments have the same created_at time, but we want the user comment to be the 'most recent'
if (comment) {
await task.addComment(comment, this.req.user.id);
await task.addComment(comment, this.req.user.id, transactingModels.taskComment.types.User);
}
await task.addComment(
'Created this task',
this.req.user.id,
transactingModels.taskComment.types.System,
);

return {
id: task.id,
};
Expand Down
1 change: 1 addition & 0 deletions packages/central-server/src/apiV2/tasks/EditTask.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export class EditTask extends EditHandler {
if (comment) {
await originalTask.addComment(comment, this.req.user.id);
}
await originalTask.addSystemCommentsOnUpdate(this.updatedFields, this.req.user.id);
return task;
});
}
Expand Down
304 changes: 244 additions & 60 deletions packages/central-server/src/tests/apiV2/tasks/EditTask.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,83 +129,267 @@ describe('Permissions checker for EditTask', async () => {
});

describe('PUT /tasks/:id', async () => {
it('Sufficient permissions: allows a user to edit a task if they have BES Admin permission', async () => {
await app.grantAccess(BES_ADMIN_POLICY);
await app.put(`tasks/${tasks[1].id}`, {
body: {
entity_id: facilities[0].id,
survey_id: surveys[0].survey.id,
},
describe('Permissions', async () => {
it('Sufficient permissions: allows a user to edit a task if they have BES Admin permission', async () => {
await app.grantAccess(BES_ADMIN_POLICY);
await app.put(`tasks/${tasks[1].id}`, {
body: {
entity_id: facilities[0].id,
survey_id: surveys[0].survey.id,
},
});
const result = await models.task.find({
id: tasks[1].id,
});
expect(result[0].entity_id).to.equal(facilities[0].id);
expect(result[0].survey_id).to.equal(surveys[0].survey.id);
});
const result = await models.task.find({
id: tasks[1].id,

it('Sufficient permissions: allows a user to edit a task if they have access to the task, and entity and survey are not being updated', async () => {
await app.grantAccess(DEFAULT_POLICY);
await app.put(`tasks/${tasks[1].id}`, {
body: {
status: 'completed',
},
});
const result = await models.task.find({
id: tasks[1].id,
});
expect(result[0].status).to.equal('completed');
});

it('Sufficient permissions: allows a user to edit a task if they have access to the task, and the entity and survey that are being linked to the task', async () => {
await app.grantAccess({
DL: ['Donor'],
TO: ['Donor'],
});
await app.put(`tasks/${tasks[1].id}`, {
body: {
survey_id: surveys[1].survey.id,
entity_id: facilities[1].id,
},
});
const result = await models.task.find({
id: tasks[1].id,
});
expect(result[0].entity_id).to.equal(facilities[1].id);
expect(result[0].survey_id).to.equal(surveys[1].survey.id);
});

it('Insufficient permissions: throws an error if the user does not have access to the task being edited', async () => {
await app.grantAccess(DEFAULT_POLICY);
const { body: result } = await app.put(`tasks/${tasks[0].id}`, {
body: {
status: 'completed',
},
});
expect(result).to.have.keys('error');
expect(result.error).to.include('Need to have access to the country of the task');
});

it('Insufficient permissions: throws an error if the user does not have access to the survey being linked to the task', async () => {
await app.grantAccess(DEFAULT_POLICY);
const { body: result } = await app.put(`tasks/${tasks[1].id}`, {
body: {
survey_id: surveys[0].survey.id,
},
});
expect(result).to.have.keys('error');
expect(result.error).to.include('Need to have access to the new survey of the task');
});

it('Insufficient permissions: throws an error if the user does not have access to the entity being linked to the task', async () => {
await app.grantAccess(DEFAULT_POLICY);
const { body: result } = await app.put(`tasks/${tasks[1].id}`, {
body: {
entity_id: facilities[0].id,
},
});
expect(result).to.have.keys('error');
expect(result.error).to.include('Need to have access to the new entity of the task');
});
expect(result[0].entity_id).to.equal(facilities[0].id);
expect(result[0].survey_id).to.equal(surveys[0].survey.id);
});

it('Sufficient permissions: allows a user to edit a task if they have access to the task, and entity and survey are not being updated', async () => {
await app.grantAccess(DEFAULT_POLICY);
await app.put(`tasks/${tasks[1].id}`, {
body: {
status: 'completed',
},
describe('User generated comments', async () => {
it('Handles adding a comment when editing a task', async () => {
await app.grantAccess({
DL: ['Donor'],
TO: ['Donor'],
});
await app.put(`tasks/${tasks[1].id}`, {
body: {
survey_id: surveys[1].survey.id,
entity_id: facilities[1].id,
comment: 'This is a test comment',
},
});
const result = await models.task.find({
id: tasks[1].id,
});
expect(result[0].entity_id).to.equal(facilities[1].id);
expect(result[0].survey_id).to.equal(surveys[1].survey.id);

const comment = await models.taskComment.findOne({
task_id: tasks[1].id,
message: 'This is a test comment',
});
expect(comment).not.to.be.null;
});
const result = await models.task.find({
id: tasks[1].id,

it('Handles adding a comment when no other edits are made', async () => {
await app.grantAccess({
DL: ['Donor'],
TO: ['Donor'],
});
await app.put(`tasks/${tasks[1].id}`, {
body: {
comment: 'This is a test comment',
},
});
const result = await models.task.find({
id: tasks[1].id,
});
expect(result[0].entity_id).to.equal(tasks[1].entity_id);

const comment = await models.taskComment.findOne({
task_id: tasks[1].id,
message: 'This is a test comment',
});
expect(comment).not.to.be.null;
});
expect(result[0].status).to.equal('completed');
});

it('Sufficient permissions: allows a user to edit a task if they have access to the task, and the entity and survey that are being linked to the task', async () => {
await app.grantAccess({
DL: ['Donor'],
TO: ['Donor'],
describe('System generated comments', () => {
it('Adds a comment when the due date changes on a task', async () => {
await app.grantAccess({
DL: ['Donor'],
TO: ['Donor'],
});
await app.put(`tasks/${tasks[1].id}`, {
body: {
due_date: new Date('2021-11-30').toISOString(),
},
});

const comment = await models.taskComment.findOne({
task_id: tasks[1].id,
type: models.taskComment.types.System,
message: 'Changed due date from 31 December 21 to 30 November 21',
});
expect(comment).not.to.be.null;
});
await app.put(`tasks/${tasks[1].id}`, {
body: {
survey_id: surveys[1].survey.id,
entity_id: facilities[1].id,
},

it('Adds a comment when the repeat schedule changes from not repeating to repeating on a task', async () => {
await app.grantAccess({
DL: ['Donor'],
TO: ['Donor'],
});
await app.put(`tasks/${tasks[1].id}`, {
body: {
// this is currently null when setting a task to repeat
due_date: null,
repeat_schedule: {
frequency: 'daily',
},
},
});

const repeatComment = await models.taskComment.findOne({
task_id: tasks[1].id,
type: models.taskComment.types.System,
message: "Changed recurring task from Doesn't repeat to Daily",
});

const dueDateComment = await models.taskComment.findOne({
task_id: tasks[1].id,
type: models.taskComment.types.System,
message: 'Changed due date from 31 December 21 to null',
});
expect(repeatComment).not.to.be.null;
// should not add a comment about the due date changing in this case
expect(dueDateComment).to.be.null;
});
const result = await models.task.find({
id: tasks[1].id,

it('Adds a comment when the repeat schedule changes from repeating to not repeating on a task', async () => {
await app.grantAccess({
DL: ['Donor'],
TO: ['Donor'],
});
await app.put(`tasks/${tasks[1].id}`, {
body: {
repeat_schedule: {
frequency: 'daily',
},
},
});

await app.put(`tasks/${tasks[1].id}`, {
body: {
repeat_schedule: null,
},
});

const comment = await models.taskComment.findOne({
task_id: tasks[1].id,
type: models.taskComment.types.System,
message: "Changed recurring task from Daily to Doesn't repeat",
});
expect(comment).not.to.be.null;
});
expect(result[0].entity_id).to.equal(facilities[1].id);
expect(result[0].survey_id).to.equal(surveys[1].survey.id);
});

it('Insufficient permissions: throws an error if the user does not have access to the task being edited', async () => {
await app.grantAccess(DEFAULT_POLICY);
const { body: result } = await app.put(`tasks/${tasks[0].id}`, {
body: {
status: 'completed',
},
it('Adds a comment when the status changes on a task', async () => {
await app.grantAccess({
DL: ['Donor'],
TO: ['Donor'],
});
await app.put(`tasks/${tasks[1].id}`, {
body: {
status: 'completed',
},
});

const comment = await models.taskComment.findOne({
task_id: tasks[1].id,
type: models.taskComment.types.System,
message: 'Changed status from To do to Completed',
});
expect(comment).not.to.be.null;
});
expect(result).to.have.keys('error');
expect(result.error).to.include('Need to have access to the country of the task');
});

it('Insufficient permissions: throws an error if the user does not have access to the survey being linked to the task', async () => {
await app.grantAccess(DEFAULT_POLICY);
const { body: result } = await app.put(`tasks/${tasks[1].id}`, {
body: {
survey_id: surveys[0].survey.id,
},
it('Adds a comment when the assignee changes to Unassigned on a task', async () => {
await app.grantAccess({
DL: ['Donor'],
TO: ['Donor'],
});
await app.put(`tasks/${tasks[1].id}`, {
body: {
assignee_id: null,
},
});

const comment = await models.taskComment.findOne({
task_id: tasks[1].id,
type: models.taskComment.types.System,
message: 'Changed assignee from Peter Pan to Unassigned',
});
expect(comment).not.to.be.null;
});
expect(result).to.have.keys('error');
expect(result.error).to.include('Need to have access to the new survey of the task');
});

it('Insufficient permissions: throws an error if the user does not have access to the entity being linked to the task', async () => {
await app.grantAccess(DEFAULT_POLICY);
const { body: result } = await app.put(`tasks/${tasks[1].id}`, {
body: {
entity_id: facilities[0].id,
},
it('Adds a comment when the assignee changes from unassigned to assigned on a task', async () => {
await app.grantAccess(BES_ADMIN_POLICY);
await app.put(`tasks/${tasks[0].id}`, {
body: {
assignee_id: assignee.id,
},
});

const comment = await models.taskComment.findOne({
task_id: tasks[0].id,
type: models.taskComment.types.System,
message: 'Changed assignee from Unassigned to Peter Pan',
});
expect(comment).not.to.be.null;
});
expect(result).to.have.keys('error');
expect(result.error).to.include('Need to have access to the new entity of the task');
});

it('Handles adding a comment when editing a task', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ describe('TaskCompletionHandler', () => {
{ entity_id: tonga.id, date: '2024-07-21' },
]);
await assertTaskStatus(task.id, 'completed', responseIds[0]);
const comments = await models.taskComment.find({ task_id: task.id });
expect(comments[0]).toMatchObject({
message: 'Completed this task',
user_id: userId,
type: models.taskComment.types.System,
});
});

it('created response marks associated tasks as completed if created_time === data_time', async () => {
Expand All @@ -106,6 +112,18 @@ describe('TaskCompletionHandler', () => {
await createResponses([{ entity_id: tonga.id, date: '2021-07-08' }]);
await assertTaskStatus(task.id, 'to_do', null);
});

it('created response does not mark associated tasks as completed if status is null, but should still create a comment', async () => {
await models.task.update({ id: task.id }, { status: null });
await createResponses([{ entity_id: tonga.id, date: '2021-07-08' }]);
await assertTaskStatus(task.id, null, null);
const comments = await models.taskComment.find({ task_id: task.id });
expect(comments[0]).toMatchObject({
message: 'Completed this task',
user_id: userId,
type: 'system',
});
});
});

describe('updating a survey response', () => {
Expand Down
Loading