From f6cee1374d9b2f48fa554152de92aa16a554e1a1 Mon Sep 17 00:00:00 2001 From: R Ranathunga Date: Fri, 13 Sep 2024 08:02:28 -0700 Subject: [PATCH 01/21] fix: correct line number for GIS import --- app/backend/lib/gis-upload.ts | 11 ++- app/package.json | 1 + app/pages/analyst/gis/index.tsx | 8 +- app/tests/backend/lib/gis-upload.test.ts | 113 +++++++++++------------ app/yarn.lock | 5 + 5 files changed, 77 insertions(+), 61 deletions(-) diff --git a/app/backend/lib/gis-upload.ts b/app/backend/lib/gis-upload.ts index 7337066bc..70f53602d 100644 --- a/app/backend/lib/gis-upload.ts +++ b/app/backend/lib/gis-upload.ts @@ -3,6 +3,7 @@ import formidable, { File } from 'formidable'; import Ajv, { ErrorObject } from 'ajv'; import fs from 'fs'; import RateLimit from 'express-rate-limit'; +import jsonSourceMap from 'json-source-map'; import schema from './gis-schema.json'; import { performQuery } from './graphql'; import getAuthRole from '../../utils/getAuthRole'; @@ -35,19 +36,23 @@ const FIELD_NAME_POSITION = 2; const formatAjv = (data: Record, errors: ErrorObject[]) => { const reply = []; + const sourceMap = jsonSourceMap.stringify(data, null, 2); errors.forEach((e) => { const parts = e.instancePath.split('/'); + const ccbcNumber = data?.[parts[LINE_NUMBER_POSITION]]?.ccbc_number ?? null; + const errorPointer = sourceMap.pointers[e.instancePath]; if (parts.length > MIN_PATH_DEPTH) { const item = { - line: parseInt(parts[LINE_NUMBER_POSITION], 10) + 1, - ccbc_number: data[parts[LINE_NUMBER_POSITION]].ccbc_number, + line: (errorPointer?.key?.line || 0) + 1, + ccbc_number: ccbcNumber, message: `${parts[FIELD_NAME_POSITION]} ${e.message}`, }; reply.push(item); } else { // errors on root level const item = { - line: 1, + line: e.keyword === 'required' ? null : 1, + ccbc_number: ccbcNumber, message: e.message, }; reply.push(item); diff --git a/app/package.json b/app/package.json index b66980747..da1773f05 100644 --- a/app/package.json +++ b/app/package.json @@ -82,6 +82,7 @@ "js-cookie": "^3.0.5", "json-diff": "^1.0.6", "json-schema": "^0.4.0", + "json-source-map": "^0.6.1", "jsonlint": "^1.6.3", "jsonwebtoken": "^9.0.2", "lightship": "7.2.0", diff --git a/app/pages/analyst/gis/index.tsx b/app/pages/analyst/gis/index.tsx index e67c29c84..b11634fd3 100644 --- a/app/pages/analyst/gis/index.tsx +++ b/app/pages/analyst/gis/index.tsx @@ -166,11 +166,17 @@ const GisTab = () => { const col = err?.posiition ? `and column ${err?.posiition}` : ''; + const erroneousCcbcNumber = err?.ccbc_number + ? `for ${err?.ccbc_number}` + : ''; + const errorAt = err?.line + ? `at line ${err?.line}` + : erroneousCcbcNumber; return (
{`Parsing error: ${err?.message} at line ${err?.line} ${col}`}
+ >{`Parsing error: ${err?.message} ${errorAt} ${col}`} ); })}{' '} Please check your file and try again. diff --git a/app/tests/backend/lib/gis-upload.test.ts b/app/tests/backend/lib/gis-upload.test.ts index 0918c5d0d..7659b1494 100644 --- a/app/tests/backend/lib/gis-upload.test.ts +++ b/app/tests/backend/lib/gis-upload.test.ts @@ -18,9 +18,8 @@ function FormDataMock() { } global.FormData = jest.fn(() => { - return FormDataMock(); - } -) as jest.Mock; + return FormDataMock(); +}) as jest.Mock; jest.setTimeout(10000000); @@ -55,18 +54,18 @@ describe('The GIS import', () => { mocked(performQuery).mockImplementation(async () => { return { - data: { createGisData: {gisData:{rowId:1}}} + data: { createGisData: { gisData: { rowId: 1 } } }, }; }); const response = await request(app) - .post('/api/analyst/gis') - .set("Content-Type", "application/json") + .post('/api/analyst/gis') + .set('Content-Type', 'application/json') .set('Connection', 'keep-alive') - .field("data", JSON.stringify({ name: "gis-data" })) - .attach("gis-data", `${__dirname}/gis-data-200.json`) + .field('data', JSON.stringify({ name: 'gis-data' })) + .attach('gis-data', `${__dirname}/gis-data-200.json`) .expect(200); - + expect(response.status).toBe(200); }); @@ -80,26 +79,27 @@ describe('The GIS import', () => { mocked(performQuery).mockImplementation(async () => { return { - data: { createGisData: {gisData:{rowId:1}}} + data: { createGisData: { gisData: { rowId: 1 } } }, }; }); - const expected={ - "errors": [ + const expected = { + errors: [ { - "line": 1, - "message": "must be array" - } - ] + line: 1, + message: 'must be array', + ccbc_number: null, + }, + ], }; const response = await request(app) - .post('/api/analyst/gis') - .set("Content-Type", "application/json") + .post('/api/analyst/gis') + .set('Content-Type', 'application/json') .set('Connection', 'keep-alive') - .field("data", JSON.stringify({ name: "gis-data" })) - .attach("gis-data", `${__dirname}/gis-data-400a.json`) + .field('data', JSON.stringify({ name: 'gis-data' })) + .attach('gis-data', `${__dirname}/gis-data-400a.json`) .expect(400); - expect(response.status).toBe(400); + expect(response.status).toBe(400); expect(response.body).toEqual(expected); }); @@ -113,38 +113,38 @@ describe('The GIS import', () => { mocked(performQuery).mockImplementation(async () => { return { - data: { createGisData: {gisData:{rowId:1}}} + data: { createGisData: { gisData: { rowId: 1 } } }, }; }); - const expected={ - "errors": [ + const expected = { + errors: [ { - "line": 10, - "position": 26, - "message": "Value expected" + line: 10, + position: 26, + message: 'Value expected', }, { - "line": 5, - "position": 20, - "message": "Expected comma" + line: 5, + position: 20, + message: 'Expected comma', }, { - "line": 2, - "position": 17, - "message": "Value expected" - } - ] + line: 2, + position: 17, + message: 'Value expected', + }, + ], }; - + const response = await request(app) - .post('/api/analyst/gis') - .set("Content-Type", "application/json") + .post('/api/analyst/gis') + .set('Content-Type', 'application/json') .set('Connection', 'keep-alive') - .field("data", JSON.stringify({ name: "gis-data" })) - .attach("gis-data", `${__dirname}/gis-data-400b.json`) + .field('data', JSON.stringify({ name: 'gis-data' })) + .attach('gis-data', `${__dirname}/gis-data-400b.json`) .expect(400); - expect(response.status).toBe(400); + expect(response.status).toBe(400); expect(response.body).toEqual(expected); }); @@ -158,20 +158,19 @@ describe('The GIS import', () => { mocked(performQuery).mockImplementation(async () => { return { - data: { createGisData: {gisData:{rowId:1}}} + data: { createGisData: { gisData: { rowId: 1 } } }, }; }); const response = await request(app) - .post('/api/analyst/gis') - .set("Content-Type", "application/json") + .post('/api/analyst/gis') + .set('Content-Type', 'application/json') .set('Connection', 'keep-alive') - .field("data", JSON.stringify({ name: "gis-data" })) - .attach("gis-data", `${__dirname}/gis-data-400.json`) + .field('data', JSON.stringify({ name: 'gis-data' })) + .attach('gis-data', `${__dirname}/gis-data-400.json`) .expect(400); - - expect(response.status).toBe(400); + expect(response.status).toBe(400); }); it('should return details about validation errors', async () => { @@ -184,26 +183,26 @@ describe('The GIS import', () => { mocked(performQuery).mockImplementation(async () => { return { - data: { createGisData: {gisData:{rowId:1}}} + data: { createGisData: { gisData: { rowId: 1 } } }, }; }); const response = await request(app) - .post('/api/analyst/gis') - .set("Content-Type", "application/json") + .post('/api/analyst/gis') + .set('Content-Type', 'application/json') .set('Connection', 'keep-alive') - .field("data", JSON.stringify({ name: "gis-data" })) - .attach("gis-data", `${__dirname}/gis-data-errors.json`) + .field('data', JSON.stringify({ name: 'gis-data' })) + .attach('gis-data', `${__dirname}/gis-data-errors.json`) .expect(400); - expect(response.status).toBe(400); - - const {errors} = response.body; + expect(response.status).toBe(400); + + const { errors } = response.body; expect(errors).toBeTruthy(); expect(errors.length).toBe(2); const first = errors[0]; - expect(first.line).toBe(1); + expect(first.line).toBe(6); expect(first.ccbc_number).toBe('CCBC-010001'); expect(first.message).toBe('GIS_TOTAL_HH must be number'); }); diff --git a/app/yarn.lock b/app/yarn.lock index e226f833c..4965e7bc5 100644 --- a/app/yarn.lock +++ b/app/yarn.lock @@ -10507,6 +10507,11 @@ json-schema@0.4.0, json-schema@^0.4.0: resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== +json-source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/json-source-map/-/json-source-map-0.6.1.tgz#e0b1f6f4ce13a9ad57e2ae165a24d06e62c79a0f" + integrity sha512-1QoztHPsMQqhDq0hlXY5ZqcEdUzxQEIxgFkKl4WUp2pgShObl+9ovi4kRh2TfvAfxAoHOJ9vIMEqk3k4iex7tg== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" From d65c1d0c393805eed7e830a2725269488de904d4 Mon Sep 17 00:00:00 2001 From: R Ranathunga Date: Fri, 27 Sep 2024 11:19:30 -0700 Subject: [PATCH 02/21] fix: user config in auto merge --- .github/workflows/release-process.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-process.yaml b/.github/workflows/release-process.yaml index 2efa095eb..fc76a54a2 100644 --- a/.github/workflows/release-process.yaml +++ b/.github/workflows/release-process.yaml @@ -111,7 +111,7 @@ jobs: PR_URL="${{ github.event.pull_request.html_url }}" gh pr merge --auto --merge "$PR_URL" env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.RENOVATE_GITHUB_TOKEN }} - name: Uncheck the checkbox if: steps.checkbox.outputs.result == 'true' && steps.up_to_date.outputs.result == 'true' && steps.pr_approval.outputs.result == 'true' && !github.event.pull_request.draft uses: actions/github-script@v7 From 5e20052abbb11c25cbc09d799a16e2d76c3fed70 Mon Sep 17 00:00:00 2001 From: CCBC Service Account <116113628+ccbc-service-account@users.noreply.github.com> Date: Tue, 1 Oct 2024 14:54:46 +0000 Subject: [PATCH 03/21] chore: release v1.193.8 --- CHANGELOG.md | 6 ++++++ db/sqitch.plan | 1 + package.json | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d714cdd7..af33a7712 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [1.193.8](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.193.7...v1.193.8) (2024-10-01) + +### Bug Fixes + +- correct line number for GIS import ([f6cee13](https://github.com/bcgov/CONN-CCBC-portal/commit/f6cee1374d9b2f48fa554152de92aa16a554e1a1)) + ## [1.193.7](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.193.5...v1.193.7) (2024-09-27) ### Bug Fixes diff --git a/db/sqitch.plan b/db/sqitch.plan index 14e73f394..93860b4a6 100644 --- a/db/sqitch.plan +++ b/db/sqitch.plan @@ -685,3 +685,4 @@ computed_columns/cbc_history 2024-09-03T15:16:07Z Anthony Bushara # release v1.193.5 @1.193.6 2024-09-26T21:30:05Z CCBC Service Account # release v1.193.6 @1.193.7 2024-09-27T17:07:14Z CCBC Service Account # release v1.193.7 +@1.193.8 2024-10-01T14:54:44Z CCBC Service Account # release v1.193.8 diff --git a/package.json b/package.json index 17befa3b3..b346e4eac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "CONN-CCBC-portal", - "version": "1.193.7", + "version": "1.193.8", "main": "index.js", "repository": "https://github.com/bcgov/CONN-CCBC-portal.git", "author": "Romer, Meherzad CITZ:EX ", From a225de5ca1d8462b2b8a2cd43a01d1609b8a9e03 Mon Sep 17 00:00:00 2001 From: CCBC Service Account <116113628+ccbc-service-account@users.noreply.github.com> Date: Tue, 1 Oct 2024 14:56:10 +0000 Subject: [PATCH 04/21] chore: release v1.193.9 --- CHANGELOG.md | 2 ++ db/sqitch.plan | 1 + package.json | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af33a7712..aa9b6ba07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## [1.193.9](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.193.8...v1.193.9) (2024-10-01) + ## [1.193.8](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.193.7...v1.193.8) (2024-10-01) ### Bug Fixes diff --git a/db/sqitch.plan b/db/sqitch.plan index 93860b4a6..08d817b55 100644 --- a/db/sqitch.plan +++ b/db/sqitch.plan @@ -686,3 +686,4 @@ computed_columns/cbc_history 2024-09-03T15:16:07Z Anthony Bushara # release v1.193.6 @1.193.7 2024-09-27T17:07:14Z CCBC Service Account # release v1.193.7 @1.193.8 2024-10-01T14:54:44Z CCBC Service Account # release v1.193.8 +@1.193.9 2024-10-01T14:56:08Z CCBC Service Account # release v1.193.9 diff --git a/package.json b/package.json index b346e4eac..88d7bc3fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "CONN-CCBC-portal", - "version": "1.193.8", + "version": "1.193.9", "main": "index.js", "repository": "https://github.com/bcgov/CONN-CCBC-portal.git", "author": "Romer, Meherzad CITZ:EX ", From c62db6656128de79316eb50ecca1cd7f9659b8e5 Mon Sep 17 00:00:00 2001 From: Anthony Bushara Date: Tue, 24 Sep 2024 17:37:11 -0400 Subject: [PATCH 05/21] feat: cutover work for cbc spreadsheet --- app/components/Navigation.tsx | 9 --------- helm/app/templates/cronJobs/trigger-sp-import.yaml | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/app/components/Navigation.tsx b/app/components/Navigation.tsx index 351bbc5b6..c0d5a31e7 100644 --- a/app/components/Navigation.tsx +++ b/app/components/Navigation.tsx @@ -124,15 +124,6 @@ const Navigation: React.FC = ({ isLoggedIn = false, title = '' }) => { - {isCbcPage && ( - - )} {isApplicantPortal && } ); diff --git a/helm/app/templates/cronJobs/trigger-sp-import.yaml b/helm/app/templates/cronJobs/trigger-sp-import.yaml index 9e6a700f6..150508253 100644 --- a/helm/app/templates/cronJobs/trigger-sp-import.yaml +++ b/helm/app/templates/cronJobs/trigger-sp-import.yaml @@ -5,7 +5,7 @@ metadata: name: {{ template "ccbc.fullname" . }}-cron-sp labels: {{ include "ccbc.labels" . | nindent 4 }} spec: - suspend: false + suspend: true schedule: {{ .Values.cronsp.schedule }} timeZone: "America/Vancouver" jobTemplate: From aacb6b3c5667de41d52044def875d3cad02ce25a Mon Sep 17 00:00:00 2001 From: CCBC Service Account <116113628+ccbc-service-account@users.noreply.github.com> Date: Tue, 1 Oct 2024 15:59:20 +0000 Subject: [PATCH 06/21] chore: release v1.194.0 --- CHANGELOG.md | 6 ++++++ db/sqitch.plan | 1 + package.json | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa9b6ba07..86d71509c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# [1.194.0](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.193.9...v1.194.0) (2024-10-01) + +### Features + +- cutover work for cbc spreadsheet ([c62db66](https://github.com/bcgov/CONN-CCBC-portal/commit/c62db6656128de79316eb50ecca1cd7f9659b8e5)) + ## [1.193.9](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.193.8...v1.193.9) (2024-10-01) ## [1.193.8](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.193.7...v1.193.8) (2024-10-01) diff --git a/db/sqitch.plan b/db/sqitch.plan index 08d817b55..a9cd62cb9 100644 --- a/db/sqitch.plan +++ b/db/sqitch.plan @@ -687,3 +687,4 @@ computed_columns/cbc_history 2024-09-03T15:16:07Z Anthony Bushara # release v1.193.7 @1.193.8 2024-10-01T14:54:44Z CCBC Service Account # release v1.193.8 @1.193.9 2024-10-01T14:56:08Z CCBC Service Account # release v1.193.9 +@1.194.0 2024-10-01T15:59:19Z CCBC Service Account # release v1.194.0 diff --git a/package.json b/package.json index 88d7bc3fc..22c653e50 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "CONN-CCBC-portal", - "version": "1.193.9", + "version": "1.194.0", "main": "index.js", "repository": "https://github.com/bcgov/CONN-CCBC-portal.git", "author": "Romer, Meherzad CITZ:EX ", From 973691ad86239a4082e3fd59632a8fc8132f20b3 Mon Sep 17 00:00:00 2001 From: CCBC Service Account <116113628+ccbc-service-account@users.noreply.github.com> Date: Tue, 1 Oct 2024 17:34:08 +0000 Subject: [PATCH 07/21] chore: release v1.194.1 --- CHANGELOG.md | 6 ++++++ db/sqitch.plan | 1 + package.json | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86d71509c..55a9f4c69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [1.194.1](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.194.0...v1.194.1) (2024-10-01) + +### Bug Fixes + +- user config in auto merge ([d65c1d0](https://github.com/bcgov/CONN-CCBC-portal/commit/d65c1d0c393805eed7e830a2725269488de904d4)) + # [1.194.0](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.193.9...v1.194.0) (2024-10-01) ### Features diff --git a/db/sqitch.plan b/db/sqitch.plan index a9cd62cb9..f6083523f 100644 --- a/db/sqitch.plan +++ b/db/sqitch.plan @@ -688,3 +688,4 @@ computed_columns/cbc_history 2024-09-03T15:16:07Z Anthony Bushara # release v1.193.8 @1.193.9 2024-10-01T14:56:08Z CCBC Service Account # release v1.193.9 @1.194.0 2024-10-01T15:59:19Z CCBC Service Account # release v1.194.0 +@1.194.1 2024-10-01T17:34:06Z CCBC Service Account # release v1.194.1 diff --git a/package.json b/package.json index 22c653e50..684ea67b1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "CONN-CCBC-portal", - "version": "1.194.0", + "version": "1.194.1", "main": "index.js", "repository": "https://github.com/bcgov/CONN-CCBC-portal.git", "author": "Romer, Meherzad CITZ:EX ", From 997898edf54b4647dba063ca95570208fdaf2686 Mon Sep 17 00:00:00 2001 From: CCBC Service Account <116113628+ccbc-service-account@users.noreply.github.com> Date: Tue, 1 Oct 2024 17:35:29 +0000 Subject: [PATCH 08/21] chore: release v1.194.2 --- CHANGELOG.md | 2 ++ db/sqitch.plan | 1 + package.json | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55a9f4c69..b0dbfc9cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## [1.194.2](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.194.1...v1.194.2) (2024-10-01) + ## [1.194.1](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.194.0...v1.194.1) (2024-10-01) ### Bug Fixes diff --git a/db/sqitch.plan b/db/sqitch.plan index f6083523f..072f29368 100644 --- a/db/sqitch.plan +++ b/db/sqitch.plan @@ -689,3 +689,4 @@ computed_columns/cbc_history 2024-09-03T15:16:07Z Anthony Bushara # release v1.193.9 @1.194.0 2024-10-01T15:59:19Z CCBC Service Account # release v1.194.0 @1.194.1 2024-10-01T17:34:06Z CCBC Service Account # release v1.194.1 +@1.194.2 2024-10-01T17:35:28Z CCBC Service Account # release v1.194.2 diff --git a/package.json b/package.json index 684ea67b1..3933ccc30 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "CONN-CCBC-portal", - "version": "1.194.1", + "version": "1.194.2", "main": "index.js", "repository": "https://github.com/bcgov/CONN-CCBC-portal.git", "author": "Romer, Meherzad CITZ:EX ", From 2b3757dcc84f48f36368c87560b3223aa37670ef Mon Sep 17 00:00:00 2001 From: Anthony Bushara Date: Fri, 20 Sep 2024 15:09:56 -0400 Subject: [PATCH 09/21] feat: email notification when failed template scrape --- app/backend/lib/emails/email.ts | 12 +++++ .../lib/emails/handleEmailNotification.ts | 2 +- .../notifyFailedReadOfTemplateData.ts | 47 +++++++++++++++++++ app/components/Form/ApplicationForm.tsx | 31 +++++++++++- app/lib/theme/widgets/FileWidget.tsx | 4 ++ .../form/[id]/rfi/[applicantRfiId].tsx | 36 +++++++++++++- 6 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 app/backend/lib/emails/templates/notifyFailedReadOfTemplateData.ts diff --git a/app/backend/lib/emails/email.ts b/app/backend/lib/emails/email.ts index d871781e9..86de29458 100644 --- a/app/backend/lib/emails/email.ts +++ b/app/backend/lib/emails/email.ts @@ -9,6 +9,7 @@ import householdCountUpdate from './templates/householdCountUpdate'; import rfiCoverageMapKmzUploaded from './templates/rfiCoverageMapKmzUploaded'; import notifyConditionallyApproved from './templates/notifyConditionallyApproved'; import notifyApplicationSubmission from './templates/notifyApplicationSubmission'; +import notifyFailedReadOfTemplateData from './templates/notifyFailedReadOfTemplateData'; const email = Router(); @@ -78,4 +79,15 @@ email.post('/api/email/notifyApplicationSubmission', limiter, (req, res) => { }); }); +email.post('/api/email/notifyFailedReadOfTemplateData', limiter, (req, res) => { + const { params } = req.body; + console.log(params, req.body); + return handleEmailNotification( + req, + res, + notifyFailedReadOfTemplateData, + params + ); +}); + export default email; diff --git a/app/backend/lib/emails/handleEmailNotification.ts b/app/backend/lib/emails/handleEmailNotification.ts index dbea34c59..a025a12d3 100644 --- a/app/backend/lib/emails/handleEmailNotification.ts +++ b/app/backend/lib/emails/handleEmailNotification.ts @@ -151,7 +151,7 @@ const handleEmailNotification = async ( return res.status(404).end(); } const eventInitiator = getAuthUser(req); - + console.log(params); const { applicationId, host } = req.body; const { emailTo, diff --git a/app/backend/lib/emails/templates/notifyFailedReadOfTemplateData.ts b/app/backend/lib/emails/templates/notifyFailedReadOfTemplateData.ts new file mode 100644 index 000000000..e35623757 --- /dev/null +++ b/app/backend/lib/emails/templates/notifyFailedReadOfTemplateData.ts @@ -0,0 +1,47 @@ +import getConfig from 'next/config'; +import { + EmailTemplate, + EmailTemplateProvider, +} from '../handleEmailNotification'; + +interface EmailTemplateParams { + templateNumber: string; + uuid: string; + organizationName: string | undefined; + projectTitle: string | undefined; +} + +const notifyFailedReadOfTemplateData: EmailTemplateProvider = ( + applicationId: string, + url: string, + initiator, + params: EmailTemplateParams +): EmailTemplate => { + const namespace = getConfig()?.publicRuntimeConfig?.OPENSHIFT_APP_NAMESPACE; + let env = 'Dev'; + if (namespace?.endsWith('-prod')) { + env = 'Prod'; + } else if (namespace?.endsWith('-test')) { + env = 'Test'; + } + + return { + emailTo: [111, 112, 113, 114, 115], + emailCC: [], + tag: 'failed-read-of-template-data', + subject: `Template ${params.templateNumber} - Failed Response`, + body: ` +

+ The following template upload by an applicant had a a failed response at: +

+
    +
  • Environment: ${env}
  • +
  • File UUID: ${params.uuid}
  • +
  • Template Number: ${params.templateNumber}
  • +
  • Application ID: ${applicationId}
  • +
+ `, + }; +}; + +export default notifyFailedReadOfTemplateData; diff --git a/app/components/Form/ApplicationForm.tsx b/app/components/Form/ApplicationForm.tsx index 0a9884d08..8e1334cdb 100644 --- a/app/components/Form/ApplicationForm.tsx +++ b/app/components/Form/ApplicationForm.tsx @@ -560,7 +560,7 @@ const ApplicationForm: React.FC = ({ } if (templateData) { - if (templateData.templateNumber === 1) { + if (templateData.templateNumber === 1 && !templateData.error) { newFormData = { ...newFormData, benefits: { @@ -571,7 +571,7 @@ const ApplicationForm: React.FC = ({ templateData.data.result.finalEligibleHouseholds, }, }; - } else if (templateData.templateNumber === 2) { + } else if (templateData.templateNumber === 2 && !templateData.error) { newFormData = { ...newFormData, budgetDetails: { @@ -580,6 +580,33 @@ const ApplicationForm: React.FC = ({ totalProjectCost: templateData.data.result.totalProjectCosts, }, }; + } else if (templateData.error && templateData.templateNumber === 1) { + fetch(`/api/email/notifyFailedReadOfTemplateData`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + applicationId: rowId, + host: window.location.origin, + params: { + uuid: newFormData.templateUploads + ?.eligibilityAndImpactsCalculator?.[0]?.uuid, + templateNumber: templateData.templateNumber, + }, + }), + }); + } else if (templateData.error && templateData.templateNumber === 2) { + fetch(`/api/email/notifyFailedReadOfTemplateData`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + applicationId: rowId, + host: window.location.origin, + params: { + uuid: newFormData.templateUploads?.detailedBudget?.[0]?.uuid, + templateNumber: templateData.templateNumber, + }, + }), + }); } } diff --git a/app/lib/theme/widgets/FileWidget.tsx b/app/lib/theme/widgets/FileWidget.tsx index 3c2a691c2..d605b4ccd 100644 --- a/app/lib/theme/widgets/FileWidget.tsx +++ b/app/lib/theme/widgets/FileWidget.tsx @@ -102,6 +102,10 @@ const FileWidget: React.FC = ({ }); } else { isTemplateValid = false; + setTemplateData({ + templateNumber, + error: true, + }); } }); } diff --git a/app/pages/applicantportal/form/[id]/rfi/[applicantRfiId].tsx b/app/pages/applicantportal/form/[id]/rfi/[applicantRfiId].tsx index dbe4851f2..b08905965 100644 --- a/app/pages/applicantportal/form/[id]/rfi/[applicantRfiId].tsx +++ b/app/pages/applicantportal/form/[id]/rfi/[applicantRfiId].tsx @@ -78,7 +78,7 @@ const ApplicantRfiPage = ({ useRfiCoverageMapKmzUploadedEmail(); useEffect(() => { - if (templateData?.templateNumber === 1) { + if (templateData?.templateNumber === 1 && !templateData.error) { const newFormDataWithTemplateOne = { ...newFormData, benefits: { @@ -89,7 +89,7 @@ const ApplicantRfiPage = ({ }, }; setNewFormData(newFormDataWithTemplateOne); - } else if (templateData?.templateNumber === 2) { + } else if (templateData?.templateNumber === 2 && !templateData.error) { const newFormDataWithTemplateTwo = { ...newFormData, budgetDetails: { @@ -99,6 +99,38 @@ const ApplicantRfiPage = ({ }, }; setNewFormData(newFormDataWithTemplateTwo); + } else if (templateData?.error && templateData?.templateNumber === 1) { + fetch(`/api/email/notifyFailedReadOfTemplateData`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + applicationId, + host: window.location.origin, + params: { + templateNumber: templateData.templateNumber, + uuid: newFormData.templateUploads + ?.eligibilityAndImpactsCalculator?.[0]?.uuid, + uploadedAt: + newFormData.templateUploads?.eligibilityAndImpactsCalculator?.[0] + ?.uploadedAt, + }, + }), + }); + } else if (templateData?.error && templateData?.templateNumber === 2) { + fetch(`/api/email/notifyFailedReadOfTemplateData`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + applicationId, + host: window.location.origin, + params: { + templateNumber: templateData.templateNumber, + uuid: newFormData.templateUploads?.detailedBudget?.[0]?.uuid, + uploadedAt: + newFormData.templateUploads?.detailedBudget?.[0]?.uploadedAt, + }, + }), + }); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [templateData]); From 504e4aba6d6fea557f929407989cb4a09c904f83 Mon Sep 17 00:00:00 2001 From: Anthony Bushara Date: Fri, 20 Sep 2024 16:29:44 -0400 Subject: [PATCH 10/21] refactor: use async/await instead of then --- app/lib/theme/widgets/FileWidget.tsx | 32 +++++++++++++++++----------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/app/lib/theme/widgets/FileWidget.tsx b/app/lib/theme/widgets/FileWidget.tsx index d605b4ccd..68abf1f75 100644 --- a/app/lib/theme/widgets/FileWidget.tsx +++ b/app/lib/theme/widgets/FileWidget.tsx @@ -86,19 +86,19 @@ const FileWidget: React.FC = ({ if (file) { fileFormData.append('file', file); if (setTemplateData) { - await fetch( - `/api/applicant/template?templateNumber=${templateNumber}`, - { - method: 'POST', - body: fileFormData, - } - ).then((response) => { + try { + const response = await fetch( + `/api/applicant/template?templateNumber=${templateNumber}`, + { + method: 'POST', + body: fileFormData, + } + ); if (response.ok) { - response.json().then((data) => { - setTemplateData({ - templateNumber, - data, - }); + const data = response.json(); + setTemplateData({ + templateNumber, + data, }); } else { isTemplateValid = false; @@ -107,7 +107,13 @@ const FileWidget: React.FC = ({ error: true, }); } - }); + } catch (error) { + isTemplateValid = false; + setTemplateData({ + templateNumber, + error: true, + }); + } } } } From e52095040721b2743af05851966a01a827a852e7 Mon Sep 17 00:00:00 2001 From: Anthony Bushara Date: Fri, 20 Sep 2024 16:30:15 -0400 Subject: [PATCH 11/21] test: add test in application form --- .../components/Form/ApplicationForm.test.tsx | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/app/tests/components/Form/ApplicationForm.test.tsx b/app/tests/components/Form/ApplicationForm.test.tsx index c09acb6f4..ed68f6444 100644 --- a/app/tests/components/Form/ApplicationForm.test.tsx +++ b/app/tests/components/Form/ApplicationForm.test.tsx @@ -663,4 +663,68 @@ describe('The application form', () => { expect(removeButton).toBeInTheDocument(); }); + + it('upload of template file', async () => { + componentTestingHelper.loadQuery(); + componentTestingHelper.renderComponent((data) => ({ + application: data.application, + pageNumber: 11, + query: data.query, + })); + + const file = new File([new ArrayBuffer(1)], 'file.xls', { + type: 'application/vnd.ms-excel', + }); + + global.fetch = jest.fn((url) => { + console.log(url, url.includes('templateNumber')); + if (url.includes('templateNumber')) { + return Promise.resolve({ + status: 300, + ok: false, + json: () => Promise.resolve({}), + }); + } + return Promise.resolve({ + status: 200, + ok: true, + json: () => Promise.resolve({}), + }); + }); + + const addTemplateOneFileInput = screen.getAllByTestId('file-test')[0]; + + const addTemplateTwoFileInput = screen.getAllByTestId('file-test')[1]; + + await act(async () => { + fireEvent.change(addTemplateOneFileInput, { target: { files: [file] } }); + }); + + await act(async () => { + fireEvent.change(addTemplateTwoFileInput, { target: { files: [file] } }); + }); + + await act(async () => { + fireEvent.click( + screen.getByRole('button', { name: 'Save and continue' }) + ); + }); + + expect(global.fetch).toHaveBeenCalledWith( + '/api/email/notifyFailedReadOfTemplateData', + { + body: JSON.stringify({ + applicationId: 42, + host: 'http://localhost', + params: { + templateNumber: 2, + }, + }), + headers: { + 'Content-Type': 'application/json', + }, + method: 'POST', + } + ); + }); }); From 4fc7a4916787b357bb09be516d3a28f875afb35e Mon Sep 17 00:00:00 2001 From: Anthony Bushara Date: Mon, 23 Sep 2024 09:42:37 -0400 Subject: [PATCH 12/21] chore: fix error with response --- app/components/Analyst/RFI/RFIAnalystUpload.tsx | 4 ++-- app/lib/theme/widgets/FileWidget.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/components/Analyst/RFI/RFIAnalystUpload.tsx b/app/components/Analyst/RFI/RFIAnalystUpload.tsx index 516f36148..c177a476a 100644 --- a/app/components/Analyst/RFI/RFIAnalystUpload.tsx +++ b/app/components/Analyst/RFI/RFIAnalystUpload.tsx @@ -74,7 +74,7 @@ const RfiAnalystUpload = ({ query }) => { useRfiCoverageMapKmzUploadedEmail(); useEffect(() => { - if (templateData?.templateNumber === 1) { + if (templateData?.templateNumber === 1 && !templateData?.error) { setExcelImportFields([...excelImportFields, 'Template 1']); const newFormDataWithTemplateOne = { ...newFormData, @@ -86,7 +86,7 @@ const RfiAnalystUpload = ({ query }) => { }, }; setNewFormData(newFormDataWithTemplateOne); - } else if (templateData?.templateNumber === 2) { + } else if (templateData?.templateNumber === 2 && !templateData?.error) { setExcelImportFields([...excelImportFields, 'Template 2']); const newFormDataWithTemplateTwo = { ...newFormData, diff --git a/app/lib/theme/widgets/FileWidget.tsx b/app/lib/theme/widgets/FileWidget.tsx index 68abf1f75..e2d7759de 100644 --- a/app/lib/theme/widgets/FileWidget.tsx +++ b/app/lib/theme/widgets/FileWidget.tsx @@ -95,7 +95,7 @@ const FileWidget: React.FC = ({ } ); if (response.ok) { - const data = response.json(); + const data = await response.json(); setTemplateData({ templateNumber, data, From 5a31968064ec9c18c273363f8503dae4920e5d84 Mon Sep 17 00:00:00 2001 From: Anthony Bushara Date: Mon, 23 Sep 2024 09:50:19 -0400 Subject: [PATCH 13/21] chore: remove console logs --- app/backend/lib/emails/email.ts | 1 - app/backend/lib/emails/handleEmailNotification.ts | 1 - app/tests/components/Form/ApplicationForm.test.tsx | 1 - 3 files changed, 3 deletions(-) diff --git a/app/backend/lib/emails/email.ts b/app/backend/lib/emails/email.ts index 86de29458..ec064fdf4 100644 --- a/app/backend/lib/emails/email.ts +++ b/app/backend/lib/emails/email.ts @@ -81,7 +81,6 @@ email.post('/api/email/notifyApplicationSubmission', limiter, (req, res) => { email.post('/api/email/notifyFailedReadOfTemplateData', limiter, (req, res) => { const { params } = req.body; - console.log(params, req.body); return handleEmailNotification( req, res, diff --git a/app/backend/lib/emails/handleEmailNotification.ts b/app/backend/lib/emails/handleEmailNotification.ts index a025a12d3..e9dd16cbe 100644 --- a/app/backend/lib/emails/handleEmailNotification.ts +++ b/app/backend/lib/emails/handleEmailNotification.ts @@ -151,7 +151,6 @@ const handleEmailNotification = async ( return res.status(404).end(); } const eventInitiator = getAuthUser(req); - console.log(params); const { applicationId, host } = req.body; const { emailTo, diff --git a/app/tests/components/Form/ApplicationForm.test.tsx b/app/tests/components/Form/ApplicationForm.test.tsx index ed68f6444..0f3be846a 100644 --- a/app/tests/components/Form/ApplicationForm.test.tsx +++ b/app/tests/components/Form/ApplicationForm.test.tsx @@ -677,7 +677,6 @@ describe('The application form', () => { }); global.fetch = jest.fn((url) => { - console.log(url, url.includes('templateNumber')); if (url.includes('templateNumber')) { return Promise.resolve({ status: 300, From 662e6b00d95801d9fae44e20de09f30fc4816444 Mon Sep 17 00:00:00 2001 From: Anthony Bushara Date: Mon, 23 Sep 2024 10:33:48 -0400 Subject: [PATCH 14/21] chore: add uploadedAt --- .../notifyFailedReadOfTemplateData.ts | 3 +- app/components/Form/ApplicationForm.tsx | 6 +++ .../form/[id]/rfi/[applicantRfiId].tsx | 18 +++++--- app/tests/backend/lib/emails/email.test.ts | 17 ++++++++ .../notifyFailedReadOfTemplateData.test.ts | 43 +++++++++++++++++++ .../components/Form/ApplicationForm.test.tsx | 1 + 6 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 app/tests/backend/lib/emails/templates/notifyFailedReadOfTemplateData.test.ts diff --git a/app/backend/lib/emails/templates/notifyFailedReadOfTemplateData.ts b/app/backend/lib/emails/templates/notifyFailedReadOfTemplateData.ts index e35623757..d2e7fbe28 100644 --- a/app/backend/lib/emails/templates/notifyFailedReadOfTemplateData.ts +++ b/app/backend/lib/emails/templates/notifyFailedReadOfTemplateData.ts @@ -9,6 +9,7 @@ interface EmailTemplateParams { uuid: string; organizationName: string | undefined; projectTitle: string | undefined; + uploadedAt: string | undefined; } const notifyFailedReadOfTemplateData: EmailTemplateProvider = ( @@ -32,7 +33,7 @@ const notifyFailedReadOfTemplateData: EmailTemplateProvider = ( subject: `Template ${params.templateNumber} - Failed Response`, body: `

- The following template upload by an applicant had a a failed response at: + The following template upload by an applicant had a failed response at ${params.uploadedAt}:

  • Environment: ${env}
  • diff --git a/app/components/Form/ApplicationForm.tsx b/app/components/Form/ApplicationForm.tsx index 8e1334cdb..cd63c406c 100644 --- a/app/components/Form/ApplicationForm.tsx +++ b/app/components/Form/ApplicationForm.tsx @@ -590,11 +590,15 @@ const ApplicationForm: React.FC = ({ params: { uuid: newFormData.templateUploads ?.eligibilityAndImpactsCalculator?.[0]?.uuid, + uploadedAt: + newFormData.templateUploads + ?.eligibilityAndImpactsCalculator?.[0]?.uploadedAt, templateNumber: templateData.templateNumber, }, }), }); } else if (templateData.error && templateData.templateNumber === 2) { + console.log(newFormData.templateUploads?.detailedBudget?.[0]); fetch(`/api/email/notifyFailedReadOfTemplateData`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -603,6 +607,8 @@ const ApplicationForm: React.FC = ({ host: window.location.origin, params: { uuid: newFormData.templateUploads?.detailedBudget?.[0]?.uuid, + uploadedAt: + newFormData.templateUploads?.detailedBudget?.[0]?.uploadedAt, templateNumber: templateData.templateNumber, }, }), diff --git a/app/pages/applicantportal/form/[id]/rfi/[applicantRfiId].tsx b/app/pages/applicantportal/form/[id]/rfi/[applicantRfiId].tsx index b08905965..d6aaf9317 100644 --- a/app/pages/applicantportal/form/[id]/rfi/[applicantRfiId].tsx +++ b/app/pages/applicantportal/form/[id]/rfi/[applicantRfiId].tsx @@ -100,6 +100,8 @@ const ApplicantRfiPage = ({ }; setNewFormData(newFormDataWithTemplateTwo); } else if (templateData?.error && templateData?.templateNumber === 1) { + const fileArrayLength = + newFormData.templateUploads?.eligibilityAndImpactsCalculator?.length; fetch(`/api/email/notifyFailedReadOfTemplateData`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -109,14 +111,17 @@ const ApplicantRfiPage = ({ params: { templateNumber: templateData.templateNumber, uuid: newFormData.templateUploads - ?.eligibilityAndImpactsCalculator?.[0]?.uuid, + ?.eligibilityAndImpactsCalculator?.[fileArrayLength - 1]?.uuid, uploadedAt: - newFormData.templateUploads?.eligibilityAndImpactsCalculator?.[0] - ?.uploadedAt, + newFormData.templateUploads?.eligibilityAndImpactsCalculator?.[ + fileArrayLength - 1 + ]?.uploadedAt, }, }), }); } else if (templateData?.error && templateData?.templateNumber === 2) { + const fileArrayLength = + newFormData.templateUploads?.detailedBudget?.length; fetch(`/api/email/notifyFailedReadOfTemplateData`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -125,9 +130,12 @@ const ApplicantRfiPage = ({ host: window.location.origin, params: { templateNumber: templateData.templateNumber, - uuid: newFormData.templateUploads?.detailedBudget?.[0]?.uuid, + uuid: newFormData.templateUploads?.detailedBudget?.[ + fileArrayLength - 1 + ]?.uuid, uploadedAt: - newFormData.templateUploads?.detailedBudget?.[0]?.uploadedAt, + newFormData.templateUploads?.detailedBudget?.[fileArrayLength - 1] + ?.uploadedAt, }, }), }); diff --git a/app/tests/backend/lib/emails/email.test.ts b/app/tests/backend/lib/emails/email.test.ts index 47ce6989c..37f788cf5 100644 --- a/app/tests/backend/lib/emails/email.test.ts +++ b/app/tests/backend/lib/emails/email.test.ts @@ -15,6 +15,7 @@ import householdCountUpdate from 'backend/lib/emails/templates/householdCountUpd import rfiCoverageMapKmzUploaded from 'backend/lib/emails/templates/rfiCoverageMapKmzUploaded'; import notifyConditionallyApproved from 'backend/lib/emails/templates/notifyConditionallyApproved'; import notifyApplicationSubmission from 'backend/lib/emails/templates/notifyApplicationSubmission'; +import notifyFailedReadOfTemplateData from 'backend/lib/emails/templates/notifyFailedReadOfTemplateData'; jest.mock('backend/lib/emails/handleEmailNotification'); @@ -210,4 +211,20 @@ describe('Email API Endpoints', () => { {} ); }); + + it('calls notifyFailedReadOfTemplateData with correct parameters once notifyFailedReadOfTemplateData called', async () => { + const reqBody = { + applicationId: '', + params: {}, + }; + await request(app) + .post('/api/email/notifyFailedReadOfTemplateData') + .send(reqBody); + expect(handleEmailNotification).toHaveBeenCalledWith( + expect.anything(), + expect.anything(), + notifyFailedReadOfTemplateData, + {} + ); + }); }); diff --git a/app/tests/backend/lib/emails/templates/notifyFailedReadOfTemplateData.test.ts b/app/tests/backend/lib/emails/templates/notifyFailedReadOfTemplateData.test.ts new file mode 100644 index 000000000..2a7257fb2 --- /dev/null +++ b/app/tests/backend/lib/emails/templates/notifyFailedReadOfTemplateData.test.ts @@ -0,0 +1,43 @@ +import notifyFailedReadOfTemplateData from 'backend/lib/emails/templates/notifyFailedReadOfTemplateData'; + +describe('notifyApplicationSubmission template', () => { + it('should return an email template with correct properties', () => { + const applicationId = '1'; + const url = 'http://mock_host.ca'; + + const emailTemplate = notifyFailedReadOfTemplateData( + applicationId, + url, + {}, + { templateNumber: 1 } + ); + + expect(emailTemplate).toEqual( + expect.objectContaining({ + emailTo: [111, 112, 113, 114, 115], + emailCC: [], + tag: 'failed-read-of-template-data', + subject: 'Template 1 - Failed Response', + body: expect.anything(), + }) + ); + }); + + it('should format parameters in body', () => { + const applicationId = '321'; + const url = 'http://mock_host.ca'; + + const emailTemplate = notifyFailedReadOfTemplateData( + applicationId, + url, + {}, + { uuid: '123', templateNumber: 1, uploadedAt: 'asdf' } + ); + + expect(emailTemplate.body).toContain(`Environment: Dev`); + expect(emailTemplate.body).toContain(`Application ID: 321`); + expect(emailTemplate.body).toContain(`File UUID: 123`); + expect(emailTemplate.body).toContain(`Template Number: 1`); + expect(emailTemplate.body).toContain(`a failed response at asdf`); + }); +}); diff --git a/app/tests/components/Form/ApplicationForm.test.tsx b/app/tests/components/Form/ApplicationForm.test.tsx index 0f3be846a..8bea9a82b 100644 --- a/app/tests/components/Form/ApplicationForm.test.tsx +++ b/app/tests/components/Form/ApplicationForm.test.tsx @@ -717,6 +717,7 @@ describe('The application form', () => { host: 'http://localhost', params: { templateNumber: 2, + uploadedAt: expect.anything(), }, }), headers: { From b9b608f316463785f28dd0be14e584b96f153881 Mon Sep 17 00:00:00 2001 From: Anthony Bushara Date: Mon, 23 Sep 2024 10:38:49 -0400 Subject: [PATCH 15/21] chore: remove console.log --- app/components/Form/ApplicationForm.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/components/Form/ApplicationForm.tsx b/app/components/Form/ApplicationForm.tsx index cd63c406c..60151fa47 100644 --- a/app/components/Form/ApplicationForm.tsx +++ b/app/components/Form/ApplicationForm.tsx @@ -598,7 +598,6 @@ const ApplicationForm: React.FC = ({ }), }); } else if (templateData.error && templateData.templateNumber === 2) { - console.log(newFormData.templateUploads?.detailedBudget?.[0]); fetch(`/api/email/notifyFailedReadOfTemplateData`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, From ac8f1f962b93c92dbbcd28eeec2f17dc752ea2c3 Mon Sep 17 00:00:00 2001 From: Anthony Bushara Date: Mon, 23 Sep 2024 10:48:34 -0400 Subject: [PATCH 16/21] chore: fix test --- app/tests/components/Form/ApplicationForm.test.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/tests/components/Form/ApplicationForm.test.tsx b/app/tests/components/Form/ApplicationForm.test.tsx index 8bea9a82b..0f3be846a 100644 --- a/app/tests/components/Form/ApplicationForm.test.tsx +++ b/app/tests/components/Form/ApplicationForm.test.tsx @@ -717,7 +717,6 @@ describe('The application form', () => { host: 'http://localhost', params: { templateNumber: 2, - uploadedAt: expect.anything(), }, }), headers: { From 11fed25326a1798d649ef385299b517c928ac40d Mon Sep 17 00:00:00 2001 From: Anthony Bushara Date: Mon, 23 Sep 2024 11:09:33 -0400 Subject: [PATCH 17/21] chore: add more tests for [applicantRfiId] --- .../form/[id]/rfi/[applicantRfiId].test.tsx | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/app/tests/pages/applicantportal/form/[id]/rfi/[applicantRfiId].test.tsx b/app/tests/pages/applicantportal/form/[id]/rfi/[applicantRfiId].test.tsx index 7e888a795..7a358b00c 100644 --- a/app/tests/pages/applicantportal/form/[id]/rfi/[applicantRfiId].test.tsx +++ b/app/tests/pages/applicantportal/form/[id]/rfi/[applicantRfiId].test.tsx @@ -217,6 +217,154 @@ describe('The applicantRfiId Page', () => { fireEvent.click(screen.getByRole('button', { name: 'Save' })); }); + pageTestingHelper.expectMutationToBeCalled( + 'updateWithTrackingRfiMutation', + { + input: { + jsonData: { + rfiType: [], + rfiAdditionalFiles: { + eligibilityAndImpactsCalculatorRfi: true, + detailedBudgetRfi: true, + eligibilityAndImpactsCalculator: [ + { + id: 1, + uuid: 'string', + name: 'file.xlsx', + size: 1, + type: 'application/vnd.ms-excel', + uploadedAt: expect.anything(), + }, + ], + detailedBudget: [ + { + id: 2, + uuid: 'string', + name: 'file.xlsx', + size: 1, + type: 'application/vnd.ms-excel', + uploadedAt: expect.anything(), + }, + ], + geographicCoverageMapRfi: true, + geographicCoverageMap: [ + { + uuid: 1, + name: '1.kmz', + size: 0, + type: '', + uploadedAt: '2024-05-31T14:05:03.509-07:00', + }, + { + uuid: 2, + name: '2.kmz', + size: 0, + type: '', + uploadedAt: '2024-05-31T14:05:03.509-07:00', + }, + ], + }, + rfiDueBy: '2022-12-22', + }, + rfiRowId: 1, + }, + } + ); + }); + it('uses template 1 and 2 data to notify if failed template read', async () => { + pageTestingHelper.loadQuery(); + pageTestingHelper.renderPage(); + + const mockSuccessResponseTemplateOne = { + totalNumberHouseholdsImpacted: 60, + finalEligibleHouseholds: 4, + }; + const mockFetchPromiseTemplateOne = Promise.resolve({ + ok: false, + status: 200, + json: () => Promise.resolve({ result: mockSuccessResponseTemplateOne }), + }); + + const mockSuccessResponseTemplateTwo = { + totalEligibleCosts: 92455, + totalProjectCosts: 101230, + }; + const mockFetchPromiseTemplateTwo = Promise.resolve({ + ok: false, + status: 200, + json: () => Promise.resolve({ result: mockSuccessResponseTemplateTwo }), + }); + global.fetch = jest.fn((url) => { + if (url.includes('templateNumber=1')) return mockFetchPromiseTemplateOne; + return mockFetchPromiseTemplateTwo; + }); + + const file = new File([new ArrayBuffer(1)], 'file.xlsx', { + type: 'application/vnd.ms-excel', + }); + + const inputFile = screen.getAllByTestId('file-test')[0]; + await act(async () => { + fireEvent.change(inputFile, { target: { files: [file] } }); + }); + + act(() => { + pageTestingHelper.environment.mock.resolveMostRecentOperation({ + data: { + createAttachment: { + attachment: { + rowId: 1, + file: 'string', + }, + }, + }, + }); + }); + const formData = new FormData(); + formData.append('file', file); + + expect(global.fetch).toHaveBeenCalledTimes(2); + expect(global.fetch).toHaveBeenCalledWith( + '/api/applicant/template?templateNumber=1', + { body: formData, method: 'POST' } + ); + expect(global.fetch).toHaveBeenCalledWith( + '/api/email/notifyFailedReadOfTemplateData', + { + body: '{"applicationId":"1","host":"http://localhost","params":{"templateNumber":1}}', + headers: { 'Content-Type': 'application/json' }, + method: 'POST', + } + ); + + const inputFile2 = screen.getAllByTestId('file-test')[1]; + await act(async () => { + fireEvent.change(inputFile2, { target: { files: [file] } }); + }); + + await act(async () => { + pageTestingHelper.environment.mock.resolveMostRecentOperation({ + data: { + createAttachment: { + attachment: { + rowId: 2, + file: 'string', + }, + }, + }, + }); + }); + + expect(global.fetch).toHaveBeenCalledTimes(4); + expect(global.fetch).toHaveBeenCalledWith( + '/api/applicant/template?templateNumber=2', + { body: formData, method: 'POST' } + ); + + await act(async () => { + fireEvent.click(screen.getByRole('button', { name: 'Save' })); + }); + pageTestingHelper.expectMutationToBeCalled( 'updateWithTrackingRfiMutation', { From 4f6411939612c504f78d28f04186ba0bb646d068 Mon Sep 17 00:00:00 2001 From: CCBC Service Account <116113628+ccbc-service-account@users.noreply.github.com> Date: Tue, 1 Oct 2024 18:17:18 +0000 Subject: [PATCH 18/21] chore: release v1.195.0 --- CHANGELOG.md | 6 ++++++ db/sqitch.plan | 1 + package.json | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0dbfc9cc..7a49cde5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# [1.195.0](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.194.2...v1.195.0) (2024-10-01) + +### Features + +- email notification when failed template scrape ([2b3757d](https://github.com/bcgov/CONN-CCBC-portal/commit/2b3757dcc84f48f36368c87560b3223aa37670ef)) + ## [1.194.2](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.194.1...v1.194.2) (2024-10-01) ## [1.194.1](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.194.0...v1.194.1) (2024-10-01) diff --git a/db/sqitch.plan b/db/sqitch.plan index 072f29368..7ae0685d2 100644 --- a/db/sqitch.plan +++ b/db/sqitch.plan @@ -690,3 +690,4 @@ computed_columns/cbc_history 2024-09-03T15:16:07Z Anthony Bushara # release v1.194.0 @1.194.1 2024-10-01T17:34:06Z CCBC Service Account # release v1.194.1 @1.194.2 2024-10-01T17:35:28Z CCBC Service Account # release v1.194.2 +@1.195.0 2024-10-01T18:17:16Z CCBC Service Account # release v1.195.0 diff --git a/package.json b/package.json index 3933ccc30..cec9bfd0f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "CONN-CCBC-portal", - "version": "1.194.2", + "version": "1.195.0", "main": "index.js", "repository": "https://github.com/bcgov/CONN-CCBC-portal.git", "author": "Romer, Meherzad CITZ:EX ", From 784fd65dec586857d4bd615918dfc7489426b97e Mon Sep 17 00:00:00 2001 From: R Ranathunga Date: Wed, 11 Sep 2024 13:51:47 -0700 Subject: [PATCH 19/21] chore: enable screening assignment email --- .../lib/emails/templates/assessmentAssigneeChange.ts | 4 ++-- .../AnalystDashboard/AssessmentAssignmentTable.tsx | 7 +++++++ .../lib/emails/templates/assessmentAssigneeChange.test.ts | 6 +++--- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/app/backend/lib/emails/templates/assessmentAssigneeChange.ts b/app/backend/lib/emails/templates/assessmentAssigneeChange.ts index 1587ebf49..13d9d80aa 100644 --- a/app/backend/lib/emails/templates/assessmentAssigneeChange.ts +++ b/app/backend/lib/emails/templates/assessmentAssigneeChange.ts @@ -84,14 +84,14 @@ const assessmentAssigneeChange: EmailTemplateProvider = async ( }; }); return { - assignors: ccbcAssignorUserList[assignor], + assignors: ccbcAssignorUserList?.[assignor] || 'A CCBC Analyst', alerts, }; } ); // Get the list of assignors for the email const assignorList = Object.keys(assignmentsByAssignor).map( - (key) => ccbcAssignorUserList[key] + (key) => ccbcAssignorUserList?.[key] || 'A CCBC Analyst' ); return { diff --git a/app/components/AnalystDashboard/AssessmentAssignmentTable.tsx b/app/components/AnalystDashboard/AssessmentAssignmentTable.tsx index ecfe6a023..4201189fb 100644 --- a/app/components/AnalystDashboard/AssessmentAssignmentTable.tsx +++ b/app/components/AnalystDashboard/AssessmentAssignmentTable.tsx @@ -467,6 +467,10 @@ const AssessmentAssignmentTable: React.FC = ({ query }) => { application.allAssessments.edges, 'screening' ), + screeningNotification: findNotification( + application.assessmentNotifications.edges, + 'assignment_screening' + ), financialRiskAssessment: findAssessment( application.allAssessments.edges, 'financialRisk' @@ -509,6 +513,7 @@ const AssessmentAssignmentTable: React.FC = ({ query }) => { const assessmentChanged = jsonData?.assignedTo && + jsonData.assignedTo !== 'Unassigned' && jsonData.assignedTo !== notification?.jsonData?.to; if (new Date(updatedAt) >= lastNotificationSentAt && assessmentChanged) { @@ -537,10 +542,12 @@ const AssessmentAssignmentTable: React.FC = ({ query }) => { 'pm', 'projectManagement' ); + const screeningAssignment = createAssignment(application, 'screening'); if (techAssignment) assignmentsList.push(techAssignment); if (financialAssignment) assignmentsList.push(financialAssignment); if (pmAssignment) assignmentsList.push(pmAssignment); + if (screeningAssignment) assignmentsList.push(screeningAssignment); return assignmentsList; }, []); diff --git a/app/tests/backend/lib/emails/templates/assessmentAssigneeChange.test.ts b/app/tests/backend/lib/emails/templates/assessmentAssigneeChange.test.ts index bfa361db3..9ac41f22c 100644 --- a/app/tests/backend/lib/emails/templates/assessmentAssigneeChange.test.ts +++ b/app/tests/backend/lib/emails/templates/assessmentAssigneeChange.test.ts @@ -50,7 +50,7 @@ describe('assessmentAssigneeChange template', () => { }, { applicationId: 2, - assessmentType: 'technical', + assessmentType: 'screening', assignedTo: 'Tester 2', assigneeEmail: 'tester2@mail.com', ccbcNumber: 'CCBC-000002', @@ -106,8 +106,8 @@ describe('assessmentAssigneeChange template', () => { { ccbcNumber: 'CCBC-000002', applicationId: 2, - type: 'Technical assessment', - url: 'http://mock_host.ca/analyst/application/2/assessments/technical', + type: 'Eligibility Screening', + url: 'http://mock_host.ca/analyst/application/2/assessments/screening', }, ], assignors: 'Assignor 2', From 67ff59b7497ac744839f1cc35ee02dd1a3c20dca Mon Sep 17 00:00:00 2001 From: CCBC Service Account <116113628+ccbc-service-account@users.noreply.github.com> Date: Tue, 1 Oct 2024 19:32:17 +0000 Subject: [PATCH 20/21] chore: release v1.195.1 --- CHANGELOG.md | 2 ++ db/sqitch.plan | 1 + package.json | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a49cde5e..55b4b1ada 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## [1.195.1](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.195.0...v1.195.1) (2024-10-01) + # [1.195.0](https://github.com/bcgov/CONN-CCBC-portal/compare/v1.194.2...v1.195.0) (2024-10-01) ### Features diff --git a/db/sqitch.plan b/db/sqitch.plan index 7ae0685d2..a537d77be 100644 --- a/db/sqitch.plan +++ b/db/sqitch.plan @@ -691,3 +691,4 @@ computed_columns/cbc_history 2024-09-03T15:16:07Z Anthony Bushara # release v1.194.1 @1.194.2 2024-10-01T17:35:28Z CCBC Service Account # release v1.194.2 @1.195.0 2024-10-01T18:17:16Z CCBC Service Account # release v1.195.0 +@1.195.1 2024-10-01T19:32:15Z CCBC Service Account # release v1.195.1 diff --git a/package.json b/package.json index cec9bfd0f..67eb9134d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "CONN-CCBC-portal", - "version": "1.195.0", + "version": "1.195.1", "main": "index.js", "repository": "https://github.com/bcgov/CONN-CCBC-portal.git", "author": "Romer, Meherzad CITZ:EX ", From 43923d9ae275e51b4099c3dcef56e458d7d8a4df Mon Sep 17 00:00:00 2001 From: Anthony Bushara Date: Thu, 12 Sep 2024 14:46:10 -0400 Subject: [PATCH 21/21] chore: update isRouteAuthorized and add needed roles for certain paths --- app/package.json | 2 +- app/utils/isRouteAuthorized.ts | 4 ++-- app/yarn.lock | 5 +++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/package.json b/app/package.json index da1773f05..0c9f085b2 100644 --- a/app/package.json +++ b/app/package.json @@ -93,7 +93,7 @@ "openid-client": "^5.6.5", "passport": "^0.7.0", "patch-package": "^8.0.0", - "path-to-regexp": "^6.2.1", + "path-to-regexp": "^6.3.0", "pg": "^8.11.5", "pluralize": "^7.0.0", "postgraphile": "^4.13.0", diff --git a/app/utils/isRouteAuthorized.ts b/app/utils/isRouteAuthorized.ts index d9e03eeff..64723025e 100644 --- a/app/utils/isRouteAuthorized.ts +++ b/app/utils/isRouteAuthorized.ts @@ -18,7 +18,7 @@ const pagesAuthorization = [ { routePaths: ['/analyst/cbc/(.*)/edit(.*)'], isProtected: true, - allowedRoles: ['cbc_admin'], + allowedRoles: ['cbc_admin', 'super_admin'], }, { routePaths: ['/analyst/cbc/(.*)'], @@ -43,7 +43,7 @@ const pagesAuthorization = [ { routePaths: ['/analyst/reporting/(.*)'], isProtected: true, - allowedRoles: ['ccbc_admin', 'ccbc_analyst', 'cbc_admin'], + allowedRoles: ['ccbc_admin', 'ccbc_analyst', 'cbc_admin', 'super_admin'], }, { routePaths: ['/analyst/admin/(.*)'], diff --git a/app/yarn.lock b/app/yarn.lock index 4965e7bc5..d3358884c 100644 --- a/app/yarn.lock +++ b/app/yarn.lock @@ -11846,6 +11846,11 @@ path-to-regexp@^6.2.1: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.1.tgz#d54934d6798eb9e5ef14e7af7962c945906918e5" integrity sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw== +path-to-regexp@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.3.0.tgz#2b6a26a337737a8e1416f9272ed0766b1c0389f4" + integrity sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ== + path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"