diff --git a/src/app/models/__tests__/encrypt-submission.server.model.spec.ts b/src/app/models/__tests__/encrypt-submission.server.model.spec.ts index f48656cb2f..4d554fe663 100644 --- a/src/app/models/__tests__/encrypt-submission.server.model.spec.ts +++ b/src/app/models/__tests__/encrypt-submission.server.model.spec.ts @@ -4,12 +4,11 @@ import moment from 'moment-timezone' import mongoose from 'mongoose' import getSubmissionModel, { + getEmailSubmissionModel, getEncryptSubmissionModel, } from 'src/app/models/submission.server.model' import { - IEmailSubmissionSchema, IEncryptedSubmissionSchema, - ISubmissionSchema, SubmissionMetadata, SubmissionType, } from 'src/types' @@ -17,6 +16,7 @@ import { import dbHandler from 'tests/unit/backend/helpers/jest-db' const Submission = getSubmissionModel(mongoose) +const EmailSubmission = getEmailSubmissionModel(mongoose) const EncryptSubmission = getEncryptSubmissionModel(mongoose) describe('Encrypt Submission Model', () => { @@ -33,15 +33,14 @@ describe('Encrypt Submission Model', () => { const validFormId = new ObjectId().toHexString() const createdDate = new Date() // Add valid encrypt submission. - const validSubmission = - await Submission.create({ - form: validFormId, - myInfoFields: [], - submissionType: SubmissionType.Encrypt, - encryptedContent: MOCK_ENCRYPTED_CONTENT, - version: 1, - created: createdDate, - }) + const validSubmission = await EncryptSubmission.create({ + form: validFormId, + myInfoFields: [], + submissionType: SubmissionType.Encrypt, + encryptedContent: MOCK_ENCRYPTED_CONTENT, + version: 1, + created: createdDate, + }) // Act const result = await EncryptSubmission.findSingleMetadata( @@ -110,7 +109,7 @@ describe('Encrypt Submission Model', () => { // Arrange // Add 3 valid encrypt submission. const validSubmissionPromises = times(3, (idx) => - Submission.create({ + EncryptSubmission.create({ form: VALID_FORM_ID, myInfoFields: [], submissionType: SubmissionType.Encrypt, @@ -119,9 +118,8 @@ describe('Encrypt Submission Model', () => { created: MOCK_CREATED_DATES_ASC[idx], }), ) - const validSubmissions: ISubmissionSchema[] = await Promise.all( - validSubmissionPromises, - ) + const validSubmissions: IEncryptedSubmissionSchema[] = + await Promise.all(validSubmissionPromises) // Act const actual = await EncryptSubmission.findAllMetadataByFormId( @@ -149,7 +147,7 @@ describe('Encrypt Submission Model', () => { // Arrange // Add 3 valid encrypt submission. const validSubmissionPromises = times(3, (idx) => - Submission.create({ + EncryptSubmission.create({ form: VALID_FORM_ID, myInfoFields: [], submissionType: SubmissionType.Encrypt, @@ -158,9 +156,8 @@ describe('Encrypt Submission Model', () => { created: MOCK_CREATED_DATES_ASC[idx], }), ) - const validSubmissions: ISubmissionSchema[] = await Promise.all( - validSubmissionPromises, - ) + const validSubmissions: IEncryptedSubmissionSchema[] = + await Promise.all(validSubmissionPromises) // Act const actual = await EncryptSubmission.findAllMetadataByFormId( @@ -192,7 +189,7 @@ describe('Encrypt Submission Model', () => { // Arrange // Add 3 valid encrypt submission. const validSubmissionPromises = times(3, (idx) => - Submission.create({ + EncryptSubmission.create({ form: VALID_FORM_ID, myInfoFields: [], submissionType: SubmissionType.Encrypt, @@ -201,9 +198,8 @@ describe('Encrypt Submission Model', () => { created: MOCK_CREATED_DATES_ASC[idx], }), ) - const validSubmissions: ISubmissionSchema[] = await Promise.all( - validSubmissionPromises, - ) + const validSubmissions: IEncryptedSubmissionSchema[] = + await Promise.all(validSubmissionPromises) // Act const actual = await EncryptSubmission.findAllMetadataByFormId( @@ -235,7 +231,7 @@ describe('Encrypt Submission Model', () => { // Arrange // Add 3 valid encrypt submission. const validSubmissionPromises = times(3, (idx) => - Submission.create({ + EncryptSubmission.create({ form: VALID_FORM_ID, myInfoFields: [], submissionType: SubmissionType.Encrypt, @@ -244,9 +240,8 @@ describe('Encrypt Submission Model', () => { created: MOCK_CREATED_DATES_ASC[idx], }), ) - const validSubmissions: ISubmissionSchema[] = await Promise.all( - validSubmissionPromises, - ) + const validSubmissions: IEncryptedSubmissionSchema[] = + await Promise.all(validSubmissionPromises) // Act const actual = await EncryptSubmission.findAllMetadataByFormId( @@ -288,11 +283,11 @@ describe('Encrypt Submission Model', () => { it('should return cursor that contains all the submissions', async () => { // Arrange const validFormId = new ObjectId().toHexString() - const validSubmission = await Submission.create({ + const validSubmission = await EncryptSubmission.create({ submissionType: SubmissionType.Encrypt, form: validFormId, encryptedContent: 'mock encrypted content abc', - version: 1, + version: 3, }) const expectedSubmission = pick( validSubmission, @@ -301,6 +296,7 @@ describe('Encrypt Submission Model', () => { 'verifiedContent', 'encryptedContent', 'submissionType', + 'version', ) // Act @@ -344,11 +340,11 @@ describe('Encrypt Submission Model', () => { it('should return correct submission by its id', async () => { // Arrange const validFormId = new ObjectId().toHexString() - const validSubmission = await Submission.create({ + const validSubmission = await EncryptSubmission.create({ submissionType: SubmissionType.Encrypt, form: validFormId, encryptedContent: 'mock encrypted content abc', - version: 1, + version: 33, attachmentMetadata: { someFileName: 'some url of attachment' }, }) @@ -360,15 +356,16 @@ describe('Encrypt Submission Model', () => { // Assert const expected = pick( - validSubmission.toObject(), + validSubmission.toJSON(), '_id', 'attachmentMetadata', 'created', 'encryptedContent', 'submissionType', + 'version', ) expect(actual).not.toBeNull() - expect(actual?.toObject()).toEqual(expected) + expect(actual?.toJSON()).toEqual(expected) }) it('should return null when submission id does not exist', async () => { @@ -390,14 +387,13 @@ describe('Encrypt Submission Model', () => { it('should return null when type of submission with given id is not SubmissionType.Encrypt', async () => { // Arrange const validFormId = new ObjectId().toHexString() - const validEmailSubmission = - await Submission.create({ - submissionType: SubmissionType.Email, - form: validFormId, - recipientEmails: ['any@example.com'], - responseHash: 'any hash', - responseSalt: 'any salt', - }) + const validEmailSubmission = await EmailSubmission.create({ + submissionType: SubmissionType.Email, + form: validFormId, + recipientEmails: ['any@example.com'], + responseHash: 'any hash', + responseSalt: 'any salt', + }) // Act const actual = await EncryptSubmission.findEncryptedSubmissionById( diff --git a/src/app/models/submission.server.model.ts b/src/app/models/submission.server.model.ts index 8c949bbedc..7f96c38887 100644 --- a/src/app/models/submission.server.model.ts +++ b/src/app/models/submission.server.model.ts @@ -360,6 +360,7 @@ EncryptSubmissionSchema.statics.getSubmissionCursorByFormId = function ( verifiedContent: 1, attachmentMetadata: 1, created: 1, + version: 1, id: 1, }) .batchSize(2000) @@ -384,6 +385,7 @@ EncryptSubmissionSchema.statics.findEncryptedSubmissionById = function ( verifiedContent: 1, attachmentMetadata: 1, created: 1, + version: 1, }) .exec() } diff --git a/src/app/modules/form/admin-form/__tests__/admin-form.routes.spec.ts b/src/app/modules/form/admin-form/__tests__/admin-form.routes.spec.ts index 7256c76e3f..07fea92b2c 100644 --- a/src/app/modules/form/admin-form/__tests__/admin-form.routes.spec.ts +++ b/src/app/modules/form/admin-form/__tests__/admin-form.routes.spec.ts @@ -3609,7 +3609,7 @@ describe('admin-form.routes', () => { encryptedContent: 'any encrypted content', verifiedContent: 'any verified content', } - const submission = await createSubmission({ + const submission = await createEncryptSubmission({ form: defaultForm, ...expectedSubmissionParams, }) @@ -3629,6 +3629,7 @@ describe('admin-form.routes', () => { refNo: String(submission._id), submissionTime: expect.any(String), verified: expectedSubmissionParams.verifiedContent, + version: submission.version, }) }) @@ -3642,7 +3643,7 @@ describe('admin-form.routes', () => { ['fieldId2', 'some.other.attachment.url'], ]), } - const submission = await createSubmission({ + const submission = await createEncryptSubmission({ form: defaultForm, ...expectedSubmissionParams, }) @@ -3669,6 +3670,7 @@ describe('admin-form.routes', () => { refNo: String(submission._id), submissionTime: expect.any(String), verified: expectedSubmissionParams.verifiedContent, + version: submission.version, }) }) @@ -3819,7 +3821,7 @@ describe('admin-form.routes', () => { jest .spyOn(EncryptSubmissionModel, 'findEncryptedSubmissionById') .mockRejectedValueOnce(new Error('ohno')) - const submission = await createSubmission({ + const submission = await createEncryptSubmission({ form: defaultForm, encryptedContent: 'any encrypted content', verifiedContent: 'any verified content', @@ -3848,7 +3850,7 @@ describe('admin-form.routes', () => { .spyOn(aws.s3, 'getSignedUrlPromise') .mockRejectedValueOnce(new Error('something went wrong')) - const submission = await createSubmission({ + const submission = await createEncryptSubmission({ form: defaultForm, encryptedContent: 'any encrypted content', verifiedContent: 'any verified content', @@ -4349,7 +4351,7 @@ describe('admin-form.routes', () => { // Create 11 submissions const submissions = await Promise.all( times(11, (count) => - createSubmission({ + createEncryptSubmission({ form: defaultForm, encryptedContent: `any encrypted content ${count}`, verifiedContent: `any verified content ${count}`, @@ -4384,7 +4386,7 @@ describe('admin-form.routes', () => { it('should return 200 with empty results if query.page does not have metadata', async () => { // Arrange // Create single submission - await createSubmission({ + await createEncryptSubmission({ form: defaultForm, encryptedContent: `any encrypted content`, verifiedContent: `any verified content`, @@ -4412,7 +4414,7 @@ describe('admin-form.routes', () => { // Create 3 submissions const submissions = await Promise.all( times(3, (count) => - createSubmission({ + createEncryptSubmission({ form: defaultForm, encryptedContent: `any encrypted content ${count}`, verifiedContent: `any verified content ${count}`, @@ -4603,7 +4605,7 @@ describe('admin-form.routes', () => { // Arrange const submissions = await Promise.all( times(11, (count) => - createSubmission({ + createEncryptSubmission({ form: defaultForm, encryptedContent: `any encrypted content ${count}`, verifiedContent: `any verified content ${count}`, @@ -4639,6 +4641,7 @@ describe('admin-form.routes', () => { encryptedContent: s.encryptedContent, verifiedContent: s.verifiedContent, created: s.created, + version: s.version, }), ) .sort((a, b) => String(a._id).localeCompare(String(b._id))) @@ -4659,7 +4662,7 @@ describe('admin-form.routes', () => { // Arrange const submissions = await Promise.all( times(5, (count) => - createSubmission({ + createEncryptSubmission({ form: defaultForm, encryptedContent: `any encrypted content ${count}`, verifiedContent: `any verified content ${count}`, @@ -4695,6 +4698,7 @@ describe('admin-form.routes', () => { encryptedContent: s.encryptedContent, verifiedContent: s.verifiedContent, created: s.created, + version: s.version, }), ) .sort((a, b) => String(a._id).localeCompare(String(b._id))) @@ -4723,7 +4727,7 @@ describe('admin-form.routes', () => { // Arrange const submissions = await Promise.all( times(5, (count) => - createSubmission({ + createEncryptSubmission({ form: defaultForm, encryptedContent: `any encrypted content ${count}`, verifiedContent: `any verified content ${count}`, @@ -4774,6 +4778,7 @@ describe('admin-form.routes', () => { encryptedContent: s.encryptedContent, verifiedContent: s.verifiedContent, created: s.created, + version: s.version, }), ) .filter((s) => expectedSubmissionIds.includes(s._id)) @@ -5385,7 +5390,7 @@ describe('admin-form.routes', () => { }) // Helper utils -const createSubmission = ({ +const createEncryptSubmission = ({ form, encryptedContent, verifiedContent, @@ -5398,7 +5403,7 @@ const createSubmission = ({ verifiedContent?: string created?: Date }) => { - return SubmissionModel.create({ + return EncryptSubmissionModel.create({ submissionType: SubmissionType.Encrypt, form: form._id, authType: form.authType, diff --git a/src/app/modules/submission/encrypt-submission/encrypt-submission.utils.ts b/src/app/modules/submission/encrypt-submission/encrypt-submission.utils.ts index c1f99b8203..0a61cd2641 100644 --- a/src/app/modules/submission/encrypt-submission/encrypt-submission.utils.ts +++ b/src/app/modules/submission/encrypt-submission/encrypt-submission.utils.ts @@ -208,5 +208,6 @@ export const createEncryptedSubmissionDto = ( content: submissionData.encryptedContent, verified: submissionData.verifiedContent, attachmentMetadata: attachmentPresignedUrls, + version: submissionData.version, } } diff --git a/src/app/routes/api/v3/admin/forms/__tests__/admin-forms.submissions.routes.spec.ts b/src/app/routes/api/v3/admin/forms/__tests__/admin-forms.submissions.routes.spec.ts index 4b1f5fa894..b83cfc8f02 100644 --- a/src/app/routes/api/v3/admin/forms/__tests__/admin-forms.submissions.routes.spec.ts +++ b/src/app/routes/api/v3/admin/forms/__tests__/admin-forms.submissions.routes.spec.ts @@ -535,7 +535,7 @@ describe('admin-form.submissions.routes', () => { // Arrange const submissions = await Promise.all( times(11, (count) => - createSubmission({ + createEncryptSubmission({ form: defaultForm, encryptedContent: `any encrypted content ${count}`, verifiedContent: `any verified content ${count}`, @@ -571,6 +571,7 @@ describe('admin-form.submissions.routes', () => { encryptedContent: s.encryptedContent, verifiedContent: s.verifiedContent, created: s.created, + version: s.version, }), ) .sort((a, b) => String(a._id).localeCompare(String(b._id))) @@ -591,7 +592,7 @@ describe('admin-form.submissions.routes', () => { // Arrange const submissions = await Promise.all( times(5, (count) => - createSubmission({ + createEncryptSubmission({ form: defaultForm, encryptedContent: `any encrypted content ${count}`, verifiedContent: `any verified content ${count}`, @@ -627,6 +628,7 @@ describe('admin-form.submissions.routes', () => { encryptedContent: s.encryptedContent, verifiedContent: s.verifiedContent, created: s.created, + version: s.version, }), ) .sort((a, b) => String(a._id).localeCompare(String(b._id))) @@ -655,7 +657,7 @@ describe('admin-form.submissions.routes', () => { // Arrange const submissions = await Promise.all( times(5, (count) => - createSubmission({ + createEncryptSubmission({ form: defaultForm, encryptedContent: `any encrypted content ${count}`, verifiedContent: `any verified content ${count}`, @@ -705,6 +707,7 @@ describe('admin-form.submissions.routes', () => { encryptedContent: s.encryptedContent, verifiedContent: s.verifiedContent, created: s.created, + version: s.version, }), ) .filter((s) => expectedSubmissionIds.includes(s._id)) @@ -726,7 +729,7 @@ describe('admin-form.submissions.routes', () => { // Arrange const submissions = await Promise.all( times(5, (count) => - createSubmission({ + createEncryptSubmission({ form: defaultForm, encryptedContent: `any encrypted content ${count}`, verifiedContent: `any verified content ${count}`, @@ -777,6 +780,7 @@ describe('admin-form.submissions.routes', () => { encryptedContent: s.encryptedContent, verifiedContent: s.verifiedContent, created: s.created, + version: s.version, }), ) .filter((s) => expectedSubmissionIds.includes(s._id)) @@ -928,7 +932,7 @@ describe('admin-form.submissions.routes', () => { encryptedContent: 'any encrypted content', verifiedContent: 'any verified content', } - const submission = await createSubmission({ + const submission = await createEncryptSubmission({ form: defaultForm, ...expectedSubmissionParams, }) @@ -948,6 +952,7 @@ describe('admin-form.submissions.routes', () => { refNo: String(submission._id), submissionTime: expect.any(String), verified: expectedSubmissionParams.verifiedContent, + version: submission.version, }) }) @@ -961,7 +966,7 @@ describe('admin-form.submissions.routes', () => { ['fieldId2', 'some.other.attachment.url'], ]), } - const submission = await createSubmission({ + const submission = await createEncryptSubmission({ form: defaultForm, ...expectedSubmissionParams, }) @@ -988,6 +993,7 @@ describe('admin-form.submissions.routes', () => { refNo: String(submission._id), submissionTime: expect.any(String), verified: expectedSubmissionParams.verifiedContent, + version: submission.version, }) }) @@ -1139,7 +1145,7 @@ describe('admin-form.submissions.routes', () => { jest .spyOn(EncryptSubmissionModel, 'findEncryptedSubmissionById') .mockRejectedValueOnce(new Error('ohno')) - const submission = await createSubmission({ + const submission = await createEncryptSubmission({ form: defaultForm, encryptedContent: 'any encrypted content', verifiedContent: 'any verified content', @@ -1168,7 +1174,7 @@ describe('admin-form.submissions.routes', () => { .spyOn(aws.s3, 'getSignedUrlPromise') .mockRejectedValueOnce(new Error('something went wrong')) - const submission = await createSubmission({ + const submission = await createEncryptSubmission({ form: defaultForm, encryptedContent: 'any encrypted content', verifiedContent: 'any verified content', @@ -1223,7 +1229,7 @@ describe('admin-form.submissions.routes', () => { // Create 11 submissions const submissions = await Promise.all( times(11, (count) => - createSubmission({ + createEncryptSubmission({ form: defaultForm, encryptedContent: `any encrypted content ${count}`, verifiedContent: `any verified content ${count}`, @@ -1258,7 +1264,7 @@ describe('admin-form.submissions.routes', () => { it('should return 200 with empty results if query.page does not have metadata', async () => { // Arrange // Create single submission - await createSubmission({ + await createEncryptSubmission({ form: defaultForm, encryptedContent: `any encrypted content`, verifiedContent: `any verified content`, @@ -1286,7 +1292,7 @@ describe('admin-form.submissions.routes', () => { // Create 3 submissions const submissions = await Promise.all( times(3, (count) => - createSubmission({ + createEncryptSubmission({ form: defaultForm, encryptedContent: `any encrypted content ${count}`, verifiedContent: `any verified content ${count}`, @@ -1463,7 +1469,7 @@ describe('admin-form.submissions.routes', () => { }) // Helper utils -const createSubmission = ({ +const createEncryptSubmission = ({ form, encryptedContent, verifiedContent, @@ -1476,7 +1482,7 @@ const createSubmission = ({ verifiedContent?: string created?: Date }) => { - return SubmissionModel.create({ + return EncryptSubmissionModel.create({ submissionType: SubmissionType.Encrypt, form: form._id, authType: form.authType, diff --git a/src/public/modules/forms/admin/controllers/view-responses.client.controller.js b/src/public/modules/forms/admin/controllers/view-responses.client.controller.js index 98889fe6a3..8dd600f492 100644 --- a/src/public/modules/forms/admin/controllers/view-responses.client.controller.js +++ b/src/public/modules/forms/admin/controllers/view-responses.client.controller.js @@ -209,7 +209,7 @@ function ViewResponsesController( if (vm.encryptionKey !== null) { vm.attachmentDownloadUrls = new Map() - const { content, verified, attachmentMetadata } = response + const { content, verified, attachmentMetadata, version } = response let displayedContent try { @@ -218,6 +218,7 @@ function ViewResponsesController( { encryptedContent: content, verifiedContent: verified, + version, }, ) diff --git a/src/public/modules/forms/helpers/decryption.worker.js b/src/public/modules/forms/helpers/decryption.worker.js index 647fa0f0bf..9fcf7825d6 100644 --- a/src/public/modules/forms/helpers/decryption.worker.js +++ b/src/public/modules/forms/helpers/decryption.worker.js @@ -190,6 +190,7 @@ async function decryptIntoCsv(data) { formsgSdk.crypto.decrypt(secretKey, { encryptedContent: submission.encryptedContent, verifiedContent: submission.verifiedContent, + version: submission.version, }), ) diff --git a/src/types/submission.ts b/src/types/submission.ts index 3c2e640352..be9f3c7541 100644 --- a/src/types/submission.ts +++ b/src/types/submission.ts @@ -15,11 +15,12 @@ export type SubmissionMetadata = { } export type EncryptedSubmissionDto = { - refNo: IEncryptedSubmissionSchema['_id'] + refNo: string submissionTime: string - content: IEncryptedSubmissionSchema['encryptedContent'] - verified: IEncryptedSubmissionSchema['verifiedContent'] + content: string + verified?: string attachmentMetadata: Record + version: number } export type SubmissionMetadataList = { @@ -132,7 +133,7 @@ export interface IWebhookResponse { // Due to schema changes, some objects may not have attachmentMetadata key. export type SubmissionCursorData = Pick< IEncryptedSubmissionSchema, - 'encryptedContent' | 'verifiedContent' | 'created' | 'id' + 'encryptedContent' | 'verifiedContent' | 'created' | 'id' | 'version' > & { attachmentMetadata?: Record } & Document export type SubmissionData = Pick< @@ -141,8 +142,9 @@ export type SubmissionData = Pick< | 'verifiedContent' | 'attachmentMetadata' | 'created' - | '_id' -> + | 'version' +> & + Document export type IEmailSubmissionModel = Model & ISubmissionModel