Skip to content

Commit

Permalink
Merge pull request #1371 from RoundingWell/attachments-ws-msgs
Browse files Browse the repository at this point in the history
Handle attachments web socket notifications on patient flow page
  • Loading branch information
paulfalgout authored Jan 2, 2025
2 parents c102601 + 04b8037 commit 57f3cb4
Show file tree
Hide file tree
Showing 5 changed files with 247 additions and 13 deletions.
22 changes: 16 additions & 6 deletions src/js/apps/patients/sidebar/action-sidebar_app.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ export default App.extend(extend({
this.listenTo(action, {
'change:_owner': this.onChangeOwner,
'destroy': this.onDestroy,
'add:comment': this.onCommentAdded,
'ws:add:comment': this.onWsAddComment,
'ws:add:attachment': this.onWsAddAttachment,
});

this.showChildView('heading', new HeadingView({ model: this.action }));
Expand Down Expand Up @@ -76,12 +77,17 @@ export default App.extend(extend({
/* istanbul ignore else : Covers edge case when owner changes prior to beforeStart */
if (this.isRunning()) this.showAttachments();
},
onCommentAdded(model) {
onWsAddComment(model) {
this.activityCollection.add(model);
this.comments.add(model);

Radio.request('ws', 'add', model);
},
onWsAddAttachment(model) {
this.attachments.add(model);

Radio.request('ws', 'add', model);
},
onStart(options, activity, comments, attachments) {
this.activityCollection = new Backbone.Collection([...activity.models, ...comments.models]);
this.comments = comments;
Expand Down Expand Up @@ -230,20 +236,24 @@ export default App.extend(extend({
});
attachment.upload(file);

Radio.request('ws', 'add', attachment);

this.listenTo(attachment, 'upload:failed', () => {
Radio.request('alert', 'show:error', intl.patients.sidebar.actionSidebarApp.uploadError);
});
},
onRemoveAttachment(model) {
model.destroy();

Radio.request('ws', 'unsubscribe', model);
},
addSubscriptions() {
Radio.request('ws', 'add', this.comments.models);
Radio.request('ws', 'add', [...this.comments.models, ...this.attachments.models]);
},
removeSubscriptions() {
// for when sidebar is closed before comments api request is finished
if (!this.comments) return;
// for when the sidebar is closed prior to beforeStart being able to finish
if (!this.isRunning()) return;

Radio.request('ws', 'unsubscribe', this.comments.models);
Radio.request('ws', 'unsubscribe', [...this.comments.models, ...this.attachments.models]);
},
}, SidebarMixin));
4 changes: 3 additions & 1 deletion src/js/base/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import JsonApiMixin from './jsonapi-mixin';

export default Backbone.Model.extend(extend({
destroy(options) {
// isDeleted means the model is already deleted from the db
if (options && options.isDeleted) {
this.stopListening();
this.trigger('destroy', this, this.collection, options);
Expand Down Expand Up @@ -90,8 +91,9 @@ export default Backbone.Model.extend(extend({

return isFunction(messages[category]) ? messages[category] : this[messages[category]];
},
handleMessage({ category, author, payload }) {
handleMessage({ category, resource, author, payload }) {
payload.attributes = extend({}, payload.attributes, { updated_at: dayjs.utc().format() });
payload.resource = resource;
payload.author = author;

const handler = this._getMessageHandler(category);
Expand Down
26 changes: 25 additions & 1 deletion src/js/entities-service/entities/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,23 @@ const _Model = BaseModel.extend({
_clinician: author,
});

this.trigger('add:comment', commentModel);
this.trigger('ws:add:comment', commentModel);
},
AttachmentAdded({ file, attributes }) {
const attachmentModel = Radio.request('entities', 'files:model', {
id: file.id,
path: attributes.path,
created_at: dayjs.utc().format(),
_action: this.id,
_patient: this.getPatient().id,
_view: attributes.urls.view,
_download: attributes.urls.download,
});

const newFilesRelationship = [...this.get('_files'), { id: file.id, type: 'files' }];
this.set({ _files: newFilesRelationship });

this.trigger('ws:add:attachment', attachmentModel);
},
ResourceDeleted() {
this.destroy({ isDeleted: true });
Expand Down Expand Up @@ -234,6 +250,14 @@ const _Model = BaseModel.extend({

return !!size(programAction.get('allowed_uploads'));
},
removeAttachment(resource) {
const files = this.get('_files');
const newFilesRelationship = files.filter(file => {
return file.id !== resource.id;
});

this.set({ _files: newFilesRelationship });
},
parseRelationship: _parseRelationship,
});

Expand Down
16 changes: 16 additions & 0 deletions src/js/entities-service/entities/files.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Radio from 'backbone.radio';
import { get, first } from 'underscore';
import Store from 'backbone.store';
import BaseCollection from 'js/base/collection';
Expand All @@ -17,6 +18,21 @@ const _Model = BaseModel.extend({
_progress: 0,
},
type: TYPE,
messages: {
FileReplaced({ attributes }) {
this.set({
path: attributes.path,
_view: attributes.urls.view,
_download: attributes.urls.download,
});
},
FileRemoved({ resource }) {
const action = Radio.request('entities', 'actions:model', this.get('_action'));
action.removeAttachment(resource);

this.destroy({ isDeleted: true });
},
},
urlRoot() {
if (this.isNew()) {
const actionId = this.get('_action');
Expand Down
192 changes: 187 additions & 5 deletions test/integration/patients/patient/patient-flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ context('patient flow page', function() {

specify('patient flow action sidebar', function() {
const testCommentId = uuid();
const testFileId = uuid();
const testOtherFileId = uuid();

const testPatient = getPatient({
attributes: {
Expand All @@ -119,25 +121,39 @@ context('patient flow page', function() {
},
});

const testProgramAction = getProgramAction({
attributes: {
allowed_uploads: ['pdf'],
},
});

const testFlowAction = getAction({
attributes: {
name: 'Test Action',
duration: 10,
outreach: 'disabled',
sharing: 'disabled',
updated_at: testTsSubtract(1),
allowed_uploads: ['pdf'],
},
relationships: {
flow: getRelationship(testFlow),
state: getRelationship(stateTodo),
owner: getRelationship(teamNurse),
form: getRelationship(testForm),
patient: getRelationship(testPatient),
'flow': getRelationship(testFlow),
'state': getRelationship(stateTodo),
'owner': getRelationship(teamNurse),
'form': getRelationship(testForm),
'patient': getRelationship(testPatient),
'program-action': getRelationship(testProgramAction),
'files': getRelationship([{ id: testOtherFileId }], 'files'),
},
});

cy
.routesForPatientAction()
.routeSettings(fx => {
fx.data.push({ id: 'upload_attachments', attributes: { value: true } });

return fx;
})
.routeFlow(fx => {
fx.data = mergeJsonApi(testFlow, {
relationships: {
Expand All @@ -150,13 +166,32 @@ context('patient flow page', function() {
.routeFlowActions(fx => {
fx.data = [testFlowAction];

fx.included.push(testProgramAction);

return fx;
})
.routePatientByFlow(fx => {
fx.data = testPatient;

return fx;
})
.routeActionFiles(fx => {
fx.data = [
{
id: testOtherFileId,
attributes: {
path: `patients/${ testPatient.id }/Other_file.pdf`,
created_at: testTsSubtract(1),
},
meta: {
view: `https://www.bucket_name.s3.amazonaws.com/patients/${ testPatient.id }/view/Other_File.pdf`,
download: `https://www.bucket_name.s3.amazonaws.com/patients/${ testPatient.id }/download/Other_File.pdf`,
},
},
];

return fx;
})
.routeActionActivity()
.visit(`/flow/${ testFlow.id }/action/${ testFlowAction.id }`)
.wait('@routeFlow')
Expand Down Expand Up @@ -389,6 +424,106 @@ context('patient flow page', function() {
.find('.comment__item')
.should('have.length', 3);

cy.sendWs({
category: 'AttachmentAdded',
resource: {
type: 'patient-actions',
id: testFlowAction.id,
},
payload: {
clinician: {
type: 'clinicians',
id: uuid(),
},
file: {
type: 'files',
id: testFileId,
},
attributes: {
path: `patients/${ testPatient.id }/HRA.pdf`,
bucket: 'bucket_name',
urls: {
view: `https://www.bucket_name.s3.amazonaws.com/patients/${ testPatient.id }/view/HRA.pdf`,
download: `https://www.bucket_name.s3.amazonaws.com/patients/${ testPatient.id }/download/HRA.pdf`,
},
},
},
});

cy
.get('[data-attachments-files-region]')
.children()
.as('attachmentItems')
.should('have.length', 2);

cy
.get('@attachmentItems')
.first()
.as('attachmentItem')
.contains('HRA.pdf')
.should('have.attr', 'href')
.and('contain', `https://www.bucket_name.s3.amazonaws.com/patients/${ testPatient.id }/view/HRA.pdf`);

cy
.get('@attachmentItem')
.contains('Download')
.should('have.attr', 'href')
.and('contain', `https://www.bucket_name.s3.amazonaws.com/patients/${ testPatient.id }/download/HRA.pdf`);

cy.sendWs({
category: 'FileReplaced',
resource: {
type: 'files',
id: testFileId,
},
payload: {
attributes: {
path: `patients/${ testPatient.id }/HRA_v2.pdf`,
bucket: 'bucket_name',
urls: {
view: `https://www.bucket_name.s3.amazonaws.com/patients/${ testPatient.id }/view/HRA_v2.pdf`,
download: `https://www.bucket_name.s3.amazonaws.com/patients/${ testPatient.id }/download/HRA_v2.pdf`,
},
},
},
});

cy
.get('[data-attachments-files-region]')
.children()
.should('have.length', 2);

cy
.get('@attachmentItem')
.contains('HRA_v2.pdf')
.should('have.attr', 'href')
.and('contain', `https://www.bucket_name.s3.amazonaws.com/patients/${ testPatient.id }/view/HRA_v2.pdf`);

cy
.get('@attachmentItem')
.contains('Download')
.should('have.attr', 'href')
.and('contain', `https://www.bucket_name.s3.amazonaws.com/patients/${ testPatient.id }/download/HRA_v2.pdf`);

cy.sendWs({
category: 'FileRemoved',
resource: {
type: 'files',
id: testFileId,
},
payload: {},
});

cy
.get('[data-attachments-files-region]')
.children()
.should('have.length', 1);

cy
.get('@attachmentItems')
.contains('HRA_v2.pdf')
.should('not.exist');

cy.sendWs({
category: 'ResourceDeleted',
resource: {
Expand Down Expand Up @@ -2492,6 +2627,8 @@ context('patient flow page', function() {
});

specify('socket notifications', function() {
const testSocketFileId = uuid();

const testSocketFlow = getFlow({
attributes: {
name: 'Flow Test',
Expand Down Expand Up @@ -2795,6 +2932,51 @@ context('patient flow page', function() {
.find('[data-state-region]')
.should('contain', 'Done');

cy.sendWs({
category: 'AttachmentAdded',
resource: {
type: 'patient-actions',
id: testSocketAction.id,
},
payload: {
clinician: {
type: 'clinicians',
id: uuid(),
},
file: {
type: 'files',
id: testSocketFileId,
},
attributes: {
path: 'patients/1/HRA.pdf',
bucket: 'bucket_name',
urls: {
view: 'https://www.bucket_name.s3.amazonaws.com/patients/1/view/HRA.pdf',
download: 'https://www.bucket_name.s3.amazonaws.com/patients/1/download/HRA.pdf',
},
},
},
});

cy
.get('.patient-flow__list')
.find('.table-list__item .fa-paperclip')
.should('exist');

cy.sendWs({
category: 'FileRemoved',
resource: {
type: 'files',
id: testSocketFileId,
},
payload: {},
});

cy
.get('.patient-flow__list')
.find('.table-list__item .fa-paperclip')
.should('not.exist');

cy.sendWs({
category: 'ResourceDeleted',
resource: {
Expand Down

0 comments on commit 57f3cb4

Please sign in to comment.