diff --git a/sims.code-workspace b/sims.code-workspace index 33cf529057..fcfd28b4a2 100644 --- a/sims.code-workspace +++ b/sims.code-workspace @@ -2,32 +2,32 @@ "folders": [ { "name": "Web UI", - "path": "./sources/packages/web" + "path": "./sources/packages/web", }, { "name": "Backend", - "path": "./sources/packages/backend" + "path": "./sources/packages/backend", }, { "name": "Forms", - "path": "./sources/packages/forms" + "path": "./sources/packages/forms", }, { "name": "DevOps", - "path": "./devops" + "path": "./devops", }, { "name": "Load Tests", - "path": "./sources/packages/load-test" + "path": "./sources/packages/load-test", }, { "name": "Sources", - "path": "./sources" + "path": "./sources", }, { "name": "All", - "path": "." - } + "path": ".", + }, ], "extensions": { "recommendations": [ @@ -40,8 +40,9 @@ "adpyke.vscode-sql-formatter", "redhat.vscode-yaml", "mongodb.mongodb-vscode", - "sonarsource.sonarlint-vscode" - ] + "sonarsource.sonarlint-vscode", + "orta.vscode-jest", + ], }, "tasks": { "version": "2.0.0", @@ -51,83 +52,83 @@ "type": "shell", "command": "make deploy-camunda-definitions", "options": { - "cwd": "${workspaceFolder:Sources}" + "cwd": "${workspaceFolder:Sources}", }, - "problemMatcher": [] + "problemMatcher": [], }, { "label": "Deploy Form Definitions", "type": "shell", "command": "make deploy-form-definitions", "options": { - "cwd": "${workspaceFolder:Sources}" + "cwd": "${workspaceFolder:Sources}", }, - "problemMatcher": [] + "problemMatcher": [], }, { "label": "Database - Run Migrations", "type": "shell", "command": "npm run migration:run", "options": { - "cwd": "${workspaceFolder:Backend}" + "cwd": "${workspaceFolder:Backend}", }, - "problemMatcher": [] + "problemMatcher": [], }, { "label": "Database - Clean E2E Test DB", "type": "shell", "command": "npm run db:seed:test:clean", "options": { - "cwd": "${workspaceFolder:Backend}" + "cwd": "${workspaceFolder:Backend}", }, - "problemMatcher": [] + "problemMatcher": [], }, { "label": "Database - Reset for API E2E Tests", "type": "shell", "command": "npm run db:seed:test:clean && npm run test:e2e:api:seed", "options": { - "cwd": "${workspaceFolder:Backend}" + "cwd": "${workspaceFolder:Backend}", }, - "problemMatcher": [] + "problemMatcher": [], }, { "label": "Database - Reset for Queue-consumers E2E Tests", "type": "shell", "command": "npm run db:seed:test:clean && npm run test:e2e:queue-consumers:seed", "options": { - "cwd": "${workspaceFolder:Backend}" + "cwd": "${workspaceFolder:Backend}", }, - "problemMatcher": [] + "problemMatcher": [], }, { "label": "View Outdated Web Packages", "type": "shell", "command": "npm outdated", "options": { - "cwd": "${workspaceFolder:Web UI}" + "cwd": "${workspaceFolder:Web UI}", }, - "problemMatcher": [] + "problemMatcher": [], }, { "label": "View Outdated Backend Packages", "type": "shell", "command": "npm outdated", "options": { - "cwd": "${workspaceFolder:Backend}" + "cwd": "${workspaceFolder:Backend}", }, - "problemMatcher": [] + "problemMatcher": [], }, { "label": "View Outdated Forms Packages", "type": "shell", "command": "npm outdated", "options": { - "cwd": "${workspaceFolder:Forms}" + "cwd": "${workspaceFolder:Forms}", }, - "problemMatcher": [] - } - ] + "problemMatcher": [], + }, + ], }, "settings": { "eslint.workingDirectories": [{ "mode": "auto" }], @@ -135,7 +136,7 @@ "**/bin": true, "**/obj": true, "**/dist": true, - "**/node_modules": true + "**/node_modules": true, }, "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, @@ -145,8 +146,8 @@ "editor.codeActionsOnSave": { "source.addMissingImports": "explicit", "source.fixAll.eslint": "explicit", - "source.removeUnusedImports": "always" - } + "source.removeUnusedImports": "always", + }, }, "typescript.preferences.importModuleSpecifier": "non-relative", "cSpell.words": [ @@ -238,14 +239,23 @@ "unparse", "VALD", "WTHD", - "Zeebe" + "Zeebe", ], "[json]": { - "editor.defaultFormatter": "vscode.json-language-features" + "editor.defaultFormatter": "vscode.json-language-features", }, "[sql]": { - "editor.defaultFormatter": "adpyke.vscode-sql-formatter" + "editor.defaultFormatter": "adpyke.vscode-sql-formatter", }, - "sql-formatter.uppercase": true - } + "sql-formatter.uppercase": true, + "jest.disabledWorkspaceFolders": [ + "Web UI", + "Forms", + "DevOps", + "Load Tests", + "Sources", + "All", + ], + "jest.jestCommandLine": "npm run test:e2e:api:local --", + }, } diff --git a/sources/packages/backend/apps/api/src/route-controllers/application/_tests_/application.students.controller.applicationChangeRequest.e2e-spec.ts b/sources/packages/backend/apps/api/src/route-controllers/application/_tests_/application.students.controller.applicationChangeRequest.e2e-spec.ts index 82213cff91..6c1cd1a1a7 100644 --- a/sources/packages/backend/apps/api/src/route-controllers/application/_tests_/application.students.controller.applicationChangeRequest.e2e-spec.ts +++ b/sources/packages/backend/apps/api/src/route-controllers/application/_tests_/application.students.controller.applicationChangeRequest.e2e-spec.ts @@ -135,6 +135,7 @@ describe("ApplicationStudentsController(e2e)-applicationChangeRequest", () => { applicationEditStatus: true, applicationEditStatusUpdatedOn: true, applicationEditStatusUpdatedBy: { id: true }, + creator: { id: true }, currentAssessment: { id: true, studentAssessmentStatus: true, @@ -152,6 +153,7 @@ describe("ApplicationStudentsController(e2e)-applicationChangeRequest", () => { precedingApplication: true, student: true, applicationEditStatusUpdatedBy: true, + creator: true, currentAssessment: { offering: true, studentAppeal: true, @@ -180,6 +182,7 @@ describe("ApplicationStudentsController(e2e)-applicationChangeRequest", () => { applicationEditStatus: ApplicationEditStatus.ChangeInProgress, applicationEditStatusUpdatedOn: now, applicationEditStatusUpdatedBy: { id: student.user.id }, + creator: { id: student.user.id }, currentAssessment: { id: expect.any(Number), studentAssessmentStatus: StudentAssessmentStatus.Submitted, diff --git a/sources/packages/backend/apps/api/src/services/application/application.service.ts b/sources/packages/backend/apps/api/src/services/application/application.service.ts index ee0361c704..de5590045d 100644 --- a/sources/packages/backend/apps/api/src/services/application/application.service.ts +++ b/sources/packages/backend/apps/api/src/services/application/application.service.ts @@ -298,6 +298,7 @@ export class ApplicationService extends RecordDataModelService { associatedFiles, ); newApplication.location = institutionLocation; + newApplication.creator = auditUser; // While editing an application, a new application record is created and a new // assessment record is also created to be the used as a "current Assessment" record. // The application and the assessment records have a DB relationship and the @@ -319,8 +320,6 @@ export class ApplicationService extends RecordDataModelService { auditUserId, transactionalEntityManager, ); - - newApplication.creator = auditUser; newApplication.studentAssessments = [originalAssessment]; newApplication.currentAssessment = originalAssessment; @@ -492,7 +491,9 @@ export class ApplicationService extends RecordDataModelService { newApplication.applicationEditStatus = ApplicationEditStatus.ChangeInProgress; newApplication.applicationEditStatusUpdatedOn = now; + newApplication.applicationEditStatusUpdatedBy = auditUser; + newApplication.creator = auditUser; // While editing an application, a new application record is created and a new // assessment record is also created to be the used as a "current Assessment" record. // The application and the assessment records have a DB relationship and the diff --git a/sources/packages/backend/apps/db-migrations/src/migrations/1770252146185-SetApplicationCreatorFromStudentId.ts b/sources/packages/backend/apps/db-migrations/src/migrations/1770252146185-SetApplicationCreatorFromStudentId.ts new file mode 100644 index 0000000000..dd3be990e0 --- /dev/null +++ b/sources/packages/backend/apps/db-migrations/src/migrations/1770252146185-SetApplicationCreatorFromStudentId.ts @@ -0,0 +1,24 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; +import { getSQLFileData } from "../utilities/sqlLoader"; + +/** + * Migration to set the creator field for applications using the mapped user ID from the student ID. + * This ensures all applications have a non-null creator field that references the student who created/submitted them. + * This addresses the first acceptance criterion of ticket #5154. + */ +export class SetApplicationCreatorFromStudentId1770252146185 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + getSQLFileData("Set-creator-from-student-id.sql", "Applications"), + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + getSQLFileData( + "Rollback-set-creator-from-student-id.sql", + "Applications", + ), + ); + } +} diff --git a/sources/packages/backend/apps/db-migrations/src/sql/Applications/Rollback-set-creator-from-student-id.sql b/sources/packages/backend/apps/db-migrations/src/sql/Applications/Rollback-set-creator-from-student-id.sql new file mode 100644 index 0000000000..26454ae0f2 --- /dev/null +++ b/sources/packages/backend/apps/db-migrations/src/sql/Applications/Rollback-set-creator-from-student-id.sql @@ -0,0 +1,9 @@ +/* + Since there is no way to know the records that were affected by the migration we are leaving the creator field as is and only rolling back + the alter column to allow nulls. + */ +-- Rollback the creator field to allow NULLs again. +ALTER TABLE + sims.applications +ALTER COLUMN + creator DROP NOT NULL; \ No newline at end of file diff --git a/sources/packages/backend/apps/db-migrations/src/sql/Applications/Set-creator-from-student-id.sql b/sources/packages/backend/apps/db-migrations/src/sql/Applications/Set-creator-from-student-id.sql new file mode 100644 index 0000000000..8bb3652b29 --- /dev/null +++ b/sources/packages/backend/apps/db-migrations/src/sql/Applications/Set-creator-from-student-id.sql @@ -0,0 +1,20 @@ +-- Set the creator field for applications using the mapped user ID from the student ID. +-- This migration handles application change requests and other applications that were created with a NULL creator. +-- The creator is set to the student's user ID from the student table. +UPDATE + sims.applications +SET + creator = s.user_id +FROM + sims.students s +WHERE + sims.applications.student_id = s.id + AND sims.applications.creator IS NULL; + +-- Alter the creator field to NOT NULL after setting the values. +ALTER TABLE + sims.applications +ALTER COLUMN + creator +SET + NOT NULL; \ No newline at end of file diff --git a/sources/packages/backend/libs/test-utils/src/factories/application.ts b/sources/packages/backend/libs/test-utils/src/factories/application.ts index 86b9c1d425..741cd590d9 100644 --- a/sources/packages/backend/libs/test-utils/src/factories/application.ts +++ b/sources/packages/backend/libs/test-utils/src/factories/application.ts @@ -38,6 +38,7 @@ import { createFakeStudent } from "./student"; export function createFakeApplication( relations?: { + auditUser?: User; student?: Student; programYear?: ProgramYear; currentStudentAssessment?: StudentAssessment; @@ -91,6 +92,10 @@ export function createFakeApplication( options?.initialValue?.applicationEditStatusUpdatedOn ?? now; application.applicationEditStatusUpdatedBy = relations?.applicationEditStatusUpdatedBy; + application.creator = + options?.initialValue?.creator ?? + relations?.auditUser ?? + application.student.user; return application; } @@ -331,6 +336,7 @@ export async function saveFakeApplication( const fakeApplication = createFakeApplication( { student: savedStudent, + auditUser: savedUser, location: relations?.institutionLocation, programYear: relations?.programYear, applicationException: relations?.applicationException, diff --git a/sources/packages/backend/package.json b/sources/packages/backend/package.json index 5f4d83fad5..255759275e 100644 --- a/sources/packages/backend/package.json +++ b/sources/packages/backend/package.json @@ -34,6 +34,7 @@ "test:e2e:queue-consumers:seed": "npm run db:seed:test filter=CreateCASDistributionAccounts", "test:e2e:queue-consumers": "npm run test:e2e:queue-consumers:seed && cross-env ENVIRONMENT=test jest --collect-coverage --verbose --config ./apps/queue-consumers/test/jest-e2e.json --runInBand --forceExit", "test:e2e:queue-consumers:local": "cross-env ENVIRONMENT=test TZ=UTC jest --verbose --config ./apps/queue-consumers/test/jest-e2e.json --runInBand --forceExit", + "test:e2e:watch": "jest --watch --config ./jest-e2e-tests.json", "migration": "ts-node -r tsconfig-paths/register --transpile-only apps/db-migrations/src/main.ts", "migration:run": "npm run migration run", "migration:list": "npm run migration list", @@ -137,4 +138,4 @@ "typescript-eslint": "^8.50.0", "webpack": "^5.104.1" } -} +} \ No newline at end of file