From 21f73cd2cd7283a9734dab5a8c48c6e75289c55f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CVamshiBatta=E2=80=9D?= <“batta.v@esmagico.in”> Date: Fri, 6 Oct 2023 13:13:23 +0530 Subject: [PATCH 001/175] Entire schema for WPCAS Service. --- prisma/schema.prisma | 169 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 164 insertions(+), 5 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 78e02ce..b5463e6 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -10,11 +10,170 @@ datasource db { url = env("DATABASE_URL") } -//dummy user model +// Here goes models +model UserMetadata { + id Int @id @default(autoincrement()) + userId Int + teamId Int + isNewEmployee Boolean + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model QuestionBank { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + competencyId Int + competencyLevelId String + question String + levelId Int + + @@unique([competencyLevelId, question]) + @@map("question_bank") +} + +model SurveyConfig { + id Int @id @default(autoincrement()) + maxQuestions Int + startTime DateTime + endTime DateTime + SurveyForm SurveyForm[] + + @@map("survey_config") +} + +model SurveyParameters { + id Int @id @default(autoincrement()) + onboardingTime Int + onboardingTimeUnit TimeUnitsEnum + surveyCycle Int + + @@map("survey_parameters") +} + +model SurveyForm { + id Int @id @default(autoincrement()) + userId Int + questionsJson Json + status SurveyStatusEnum + surveyConfig SurveyConfig @relation(fields: [surveyConfigId], references: [id]) + surveyConfigId Int + ResponseTracker ResponseTracker[] + SurveyScore SurveyScore[] + + @@map("survey_form") +} + +model ResponseTracker { + id Int @id @default(autoincrement()) + surveyFormId Int + surveyForm SurveyForm @relation(fields: [surveyFormId], references: [id]) + assesseeId Int + assessorId Int + responseJson Json? + status ResponseTrackerStatusEnum + + @@map("response_tracker") +} + +model SurveyScore { + id Int @id @default(autoincrement()) + userId Int + surveyFormId Int + surveyForm SurveyForm @relation(fields: [surveyFormId], references: [id]) + roleId Int? + competencyId Int + competencyLevelId Int + score Int + + @@map("survey_scores") +} + +enum SurveyStatusEnum { + CREATED + PUBLISHED +} + +enum ResponseTrackerStatusEnum { + PENDING + COMPLETED +} + +enum TimeUnitsEnum { + DAY + MONTH + YEAR +} + +//Mock Database Models - To be removed after the app is integrated with User Service and FRAC Based Competency Tagger. model User { - id Int @id @default(autoincrement()) - email String @unique - name String? + id Int @id @default(autoincrement()) + email String @unique + role UserRolesEnum + username String + password String + profilePicture String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + roles Role[] + Level Level? @relation(fields: [levelId], references: [id]) + levelId Int? + Team Team? @relation(fields: [teamId], references: [id]) + teamId Int? + + @@map("users") } -// Here goes models +model Level { + id Int @id @default(autoincrement()) + lable String + description String? + users User[] +} +model Team { + id Int @id @default(autoincrement()) + name String + description String? + users User[] +} + +model Role { + id Int @id @default(autoincrement()) + roleName String + description String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + users User[] + competencies Competency[] + + @@map("roles") +} + +model Competency { + id Int @id @default(autoincrement()) + competencyName String + description String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + roles Role[] + levels CompetencyLevel[] + + @@map("competencies") +} + +model CompetencyLevel { + id Int @id @default(autoincrement()) + name String + description String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + Competency Competency[] + + @@map("competency_levels") +} + +enum UserRolesEnum { + ADMIN + COMSUMER +} From 34eee8eb2f0e07dbe51eae0ad6a63c7245af0dcc Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Mon, 9 Oct 2023 16:10:26 +0530 Subject: [PATCH 002/175] schema relation updated --- prisma/schema.prisma | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index b5463e6..2d96552 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -13,7 +13,7 @@ datasource db { // Here goes models model UserMetadata { id Int @id @default(autoincrement()) - userId Int + userId Int @unique() teamId Int isNewEmployee Boolean createdAt DateTime @default(now()) @@ -34,11 +34,11 @@ model QuestionBank { } model SurveyConfig { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) maxQuestions Int startTime DateTime endTime DateTime - SurveyForm SurveyForm[] + surveyForm SurveyForm? @@map("survey_config") } @@ -56,9 +56,9 @@ model SurveyForm { id Int @id @default(autoincrement()) userId Int questionsJson Json + surveyConfigId Int @unique() // Add this field for the one-to-one relation status SurveyStatusEnum surveyConfig SurveyConfig @relation(fields: [surveyConfigId], references: [id]) - surveyConfigId Int ResponseTracker ResponseTracker[] SurveyScore SurveyScore[] @@ -74,6 +74,7 @@ model ResponseTracker { responseJson Json? status ResponseTrackerStatusEnum + @@unique([surveyFormId, assesseeId, assessorId]) @@map("response_tracker") } @@ -131,6 +132,7 @@ model Level { description String? users User[] } + model Team { id Int @id @default(autoincrement()) name String From b164de8d2ef1a0a463a1090e4db78c603144f2ef Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Wed, 11 Oct 2023 12:38:51 +0530 Subject: [PATCH 003/175] updated docker --- .dockerignore | 4 ++++ Dockerfile | 52 ++++++++++++++++++++++++++++----------------- e2e.Dockerfile | 17 +++++++++++---- tsconfig.build.json | 10 +++++++-- 4 files changed, 58 insertions(+), 25 deletions(-) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..42d24d3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +.vscode/ +node_modules/ +dist/ +npm-debug.log \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 7428982..c3a4084 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,30 @@ +# RUN apk add --no-cache bash +# RUN npm i -g @nestjs/cli typescript ts-node + +# COPY package*.json /tmp/app/ +# RUN cd /tmp/app && npm install + +# COPY . /usr/src/app +# RUN cp -a /tmp/app/node_modules /usr/src/app +# COPY ./wait-for-it.sh /opt/wait-for-it.sh +# COPY ./startup.dev.sh /opt/startup.dev.sh +# RUN sed -i 's/\r//g' /opt/wait-for-it.sh +# RUN sed -i 's/\r//g' /opt/startup.dev.sh + +# WORKDIR /usr/src/app +# RUN cp env-example .env + +# RUN npm run build + +# CMD ["/opt/startup.dev.sh"] + +# Use the official Node.js 18 image as a base FROM node:18.16.1-alpine +# Install bash for debugging purposes (optional) RUN apk add --no-cache bash + +# Install global Node.js packages for NestJS development RUN npm i -g @nestjs/cli typescript ts-node # Set the working directory inside the container @@ -9,33 +33,23 @@ WORKDIR /usr/src/app # Copy package.json and package-lock.json to the working directory COPY package*.json ./ +# Copy the Prisma configuration and migration files +# This line copies the "prisma" directory from your project's root into the Docker container's working directory. +COPY prisma ./prisma/ + # Install project dependencies RUN npm install # Copy the rest of the application code to the container COPY . . -# Expose the PORT environment variable (default to 4000 if not provided) -ARG PORT=4010 -ENV PORT=$APP_PORT - - -# COPY package*.json /tmp/app/ -# RUN cd /tmp/app && npm install - -# COPY . /usr/src/app -# RUN cp -a /tmp/app/node_modules /usr/src/app -# COPY ./wait-for-it.sh /opt/wait-for-it.sh -# COPY ./startup.dev.sh /opt/startup.dev.sh -# RUN sed -i 's/\r//g' /opt/wait-for-it.sh -# RUN sed -i 's/\r//g' /opt/startup.dev.sh - -# RUN cp env-example .env - # Build your Nest.js application RUN npm run build +# Expose the PORT environment variable (default to 4000 if not provided) +ARG PORT=4000 +ENV PORT=$PORT +EXPOSE $PORT + # Start the Nest.js application using the start:prod script CMD ["npm", "run", "start:prod"] - -# CMD ["/opt/startup.dev.sh"] diff --git a/e2e.Dockerfile b/e2e.Dockerfile index bbb18cd..a980ef4 100644 --- a/e2e.Dockerfile +++ b/e2e.Dockerfile @@ -20,9 +20,13 @@ # CMD ["/opt/startup.ci.sh"] +# Use the official Node.js 18 image as a base FROM node:18.16.1-alpine +# Install bash for debugging purposes (optional) RUN apk add --no-cache bash + +# Install global Node.js packages for NestJS development RUN npm i -g @nestjs/cli typescript ts-node # Set the working directory inside the container @@ -31,18 +35,23 @@ WORKDIR /usr/src/app # Copy package.json and package-lock.json to the working directory COPY package*.json ./ +# Copy the Prisma configuration and migration files +# This line copies the "prisma" directory from your project's root into the Docker container's working directory. +COPY prisma ./prisma/ + # Install project dependencies RUN npm install # Copy the rest of the application code to the container COPY . . -# Expose the PORT environment variable (default to 4000 if not provided) -ARG PORT=4010 -ENV PORT=$APP_PORT - # Build your Nest.js application RUN npm run build +# Expose the PORT environment variable (default to 4000 if not provided) +ARG PORT=4000 +ENV PORT=$PORT +EXPOSE $PORT + # Start the Nest.js application using the start:prod script CMD ["npm", "run", "start:prod"] diff --git a/tsconfig.build.json b/tsconfig.build.json index 64f86c6..7207cd1 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -1,4 +1,10 @@ { "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] -} + "exclude": [ + "node_modules", + "test", + "dist", + "**/*spec.ts", + "prisma" + ] +} \ No newline at end of file From d6ea0af16a81295a68aea80821c0fdd75c2eceac Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Wed, 11 Oct 2023 12:54:35 +0530 Subject: [PATCH 004/175] error handling --- src/utils/utils.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 7ecc693..2a1e8e3 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,3 +1,5 @@ +import { HttpStatus } from "@nestjs/common"; + export const validationOptions = { whitelist: true, transform: true, @@ -6,3 +8,26 @@ export const validationOptions = { enableImplicitConversion: true, }, }; + +export function getPrismaErrorStatusAndMessage(error: any): { + errorMessage: string | undefined; + statusCode: number; +} { + const errorCode = error?.code || "DEFAULT_ERROR_CODE"; + + const errorCodeMap: Record = { + P2000: HttpStatus.BAD_REQUEST, + P2002: HttpStatus.CONFLICT, + P2003: HttpStatus.CONFLICT, + P2025: HttpStatus.NOT_FOUND, + DEFAULT_ERROR_CODE: + error?.status || + error?.response?.status || + HttpStatus.INTERNAL_SERVER_ERROR, + }; + + const statusCode = errorCodeMap[errorCode]; + const errorMessage = error.message.split("\n").pop(); + + return { statusCode, errorMessage }; +} From 51137f0475acbcf91608d140c93b6abafa70e1e6 Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Thu, 12 Oct 2023 10:45:32 +0530 Subject: [PATCH 005/175] Prisma scripts added --- package-lock.json | 32 ++++++++++++++++---------------- package.json | 12 +++++++++--- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7cbd9fa..a2a62da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@nestjs/core": "^9.0.0", "@nestjs/platform-express": "^9.0.0", "@nestjs/swagger": "^7.1.11", - "@prisma/client": "^5.4.1", + "@prisma/client": "^5.4.2", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "reflect-metadata": "^0.1.13", @@ -36,7 +36,7 @@ "eslint-plugin-prettier": "^4.0.0", "jest": "28.1.3", "prettier": "^2.3.2", - "prisma": "^5.4.1", + "prisma": "^5.4.2", "source-map-support": "^0.5.20", "supertest": "^6.1.3", "ts-jest": "28.0.8", @@ -2492,12 +2492,12 @@ } }, "node_modules/@prisma/client": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.4.1.tgz", - "integrity": "sha512-xyD0DJ3gRNfLbPsC+YfMBBuLJtZKQfy1OD2qU/PZg+HKrr7SO+09174LMeTlWP0YF2wca9LxtVd4HnAiB5ketQ==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.4.2.tgz", + "integrity": "sha512-2xsPaz4EaMKj1WS9iW6MlPhmbqtBsXAOeVttSePp8vTFTtvzh2hZbDgswwBdSCgPzmmwF+tLB259QzggvCmJqA==", "hasInstallScript": true, "dependencies": { - "@prisma/engines-version": "5.4.1-1.2f302df92bd8945e20ad4595a73def5b96afa54f" + "@prisma/engines-version": "5.4.1-2.ac9d7041ed77bcc8a8dbd2ab6616b39013829574" }, "engines": { "node": ">=16.13" @@ -2512,16 +2512,16 @@ } }, "node_modules/@prisma/engines": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.4.1.tgz", - "integrity": "sha512-vJTdY4la/5V3N7SFvWRmSMUh4mIQnyb/MNoDjzVbh9iLmEC+uEykj/1GPviVsorvfz7DbYSQC4RiwmlEpTEvGA==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.4.2.tgz", + "integrity": "sha512-fqeucJ3LH0e1eyFdT0zRx+oETLancu5+n4lhiYECyEz6H2RDskPJHJYHkVc0LhkU4Uv7fuEnppKU3nVKNzMh8g==", "devOptional": true, "hasInstallScript": true }, "node_modules/@prisma/engines-version": { - "version": "5.4.1-1.2f302df92bd8945e20ad4595a73def5b96afa54f", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.4.1-1.2f302df92bd8945e20ad4595a73def5b96afa54f.tgz", - "integrity": "sha512-+nUQM/y8C+1GG5Ioeqcu6itFslCfxvQSAUVSMC9XM2G2Fcq0F4Afnp6m0pXF6X6iUBWen7jZBPmM9Qlq4Nr3/A==" + "version": "5.4.1-2.ac9d7041ed77bcc8a8dbd2ab6616b39013829574", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.4.1-2.ac9d7041ed77bcc8a8dbd2ab6616b39013829574.tgz", + "integrity": "sha512-wvupDL4AA1vf4TQNANg7kR7y98ITqPsk6aacfBxZKtrJKRIsWjURHkZCGcQliHdqCiW/hGreO6d6ZuSv9MhdAA==" }, "node_modules/@sinclair/typebox": { "version": "0.24.51", @@ -8837,13 +8837,13 @@ } }, "node_modules/prisma": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.4.1.tgz", - "integrity": "sha512-op9PmU8Bcw5dNAas82wBYTG0yHnpq9/O3bhxbDBrNzwZTwBqsVCxxYRLf6wHNh9HVaDGhgjjHlu1+BcW8qdnBg==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.4.2.tgz", + "integrity": "sha512-GDMZwZy7mysB2oXU+angQqJ90iaPFdD0rHaZNkn+dio5NRkGLmMqmXs31//tg/qXT3iB0cTQwnGGQNuirhSTZg==", "devOptional": true, "hasInstallScript": true, "dependencies": { - "@prisma/engines": "5.4.1" + "@prisma/engines": "5.4.2" }, "bin": { "prisma": "build/index.js" diff --git a/package.json b/package.json index fd5d923..6b10e50 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,13 @@ "test:watch": "jest --watch", "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./test/jest-e2e.json" + "test:e2e": "jest --config ./test/jest-e2e.json", + "migrate:dev": "npx prisma migrate dev", + "migrate:dev:create": "npx prisma migrate dev --create-only", + "migrate:deploy": "npx prisma migrate deploy", + "prisma:generate": "npx prisma generate", + "prisma:studio": "npx prisma studio", + "prisma:seed": "npx prisma db seed" }, "dependencies": { "@nestjs/common": "^9.0.0", @@ -26,7 +32,7 @@ "@nestjs/core": "^9.0.0", "@nestjs/platform-express": "^9.0.0", "@nestjs/swagger": "^7.1.11", - "@prisma/client": "^5.4.1", + "@prisma/client": "^5.4.2", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "reflect-metadata": "^0.1.13", @@ -48,7 +54,7 @@ "eslint-plugin-prettier": "^4.0.0", "jest": "28.1.3", "prettier": "^2.3.2", - "prisma": "^5.4.1", + "prisma": "^5.4.2", "source-map-support": "^0.5.20", "supertest": "^6.1.3", "ts-jest": "28.0.8", From 24a740ac0312be29fc7ee0b7cc1c5f51b549da1d Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Fri, 6 Oct 2023 13:45:11 +0530 Subject: [PATCH 006/175] mock folder --- src/mock/index.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/mock/index.ts diff --git a/src/mock/index.ts b/src/mock/index.ts new file mode 100644 index 0000000..e69de29 From 416707dfaf41b3a349b83ebdba9c0237c0234d1f Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Fri, 6 Oct 2023 13:46:48 +0530 Subject: [PATCH 007/175] mock folder --- src/{mock => mockModules}/index.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{mock => mockModules}/index.ts (100%) diff --git a/src/mock/index.ts b/src/mockModules/index.ts similarity index 100% rename from src/mock/index.ts rename to src/mockModules/index.ts From 1c702ca2c695bed37b4220353b3845829eec166a Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Fri, 6 Oct 2023 15:05:42 +0530 Subject: [PATCH 008/175] User, Level and Team APIs implemented --- prisma/schema.prisma | 4 +- src/app.module.ts | 6 ++ .../mock-level/dto/create-mock-level.dto.ts | 12 ++++ .../mock-level/dto/response-mock-level.dto.ts | 5 ++ .../mock-level/dto/update-mock-level.dto.ts | 4 ++ .../mock-level/mock-level.controller.spec.ts | 20 ++++++ .../mock-level/mock-level.controller.ts | 63 +++++++++++++++++++ .../mock-level/mock-level.module.ts | 10 +++ .../mock-level/mock-level.service.spec.ts | 18 ++++++ .../mock-level/mock-level.service.ts | 43 +++++++++++++ .../mock-team/dto/create-mock-team.dto.ts | 12 ++++ .../mock-team/dto/response-mock-team.dto.ts | 5 ++ .../mock-team/dto/update-mock-team.dto.ts | 4 ++ .../mock-team/entities/mock-team.entity.ts | 1 + .../mock-team/mock-team.controller.spec.ts | 20 ++++++ .../mock-team/mock-team.controller.ts | 47 ++++++++++++++ src/mockModules/mock-team/mock-team.module.ts | 10 +++ .../mock-team/mock-team.service.spec.ts | 18 ++++++ .../mock-team/mock-team.service.ts | 44 +++++++++++++ .../mock-user/dto/create-mock-user.dto.ts | 52 +++++++++++++++ .../mock-user/dto/response-mock-user.dto.ts | 12 ++++ .../mock-user/dto/update-mock-user.dto.ts | 4 ++ .../mock-user/mock-user.controller.spec.ts | 20 ++++++ .../mock-user/mock-user.controller.ts | 63 +++++++++++++++++++ src/mockModules/mock-user/mock-user.module.ts | 11 ++++ .../mock-user/mock-user.service.spec.ts | 18 ++++++ .../mock-user/mock-user.service.ts | 42 +++++++++++++ 27 files changed, 566 insertions(+), 2 deletions(-) create mode 100644 src/mockModules/mock-level/dto/create-mock-level.dto.ts create mode 100644 src/mockModules/mock-level/dto/response-mock-level.dto.ts create mode 100644 src/mockModules/mock-level/dto/update-mock-level.dto.ts create mode 100644 src/mockModules/mock-level/mock-level.controller.spec.ts create mode 100644 src/mockModules/mock-level/mock-level.controller.ts create mode 100644 src/mockModules/mock-level/mock-level.module.ts create mode 100644 src/mockModules/mock-level/mock-level.service.spec.ts create mode 100644 src/mockModules/mock-level/mock-level.service.ts create mode 100644 src/mockModules/mock-team/dto/create-mock-team.dto.ts create mode 100644 src/mockModules/mock-team/dto/response-mock-team.dto.ts create mode 100644 src/mockModules/mock-team/dto/update-mock-team.dto.ts create mode 100644 src/mockModules/mock-team/entities/mock-team.entity.ts create mode 100644 src/mockModules/mock-team/mock-team.controller.spec.ts create mode 100644 src/mockModules/mock-team/mock-team.controller.ts create mode 100644 src/mockModules/mock-team/mock-team.module.ts create mode 100644 src/mockModules/mock-team/mock-team.service.spec.ts create mode 100644 src/mockModules/mock-team/mock-team.service.ts create mode 100644 src/mockModules/mock-user/dto/create-mock-user.dto.ts create mode 100644 src/mockModules/mock-user/dto/response-mock-user.dto.ts create mode 100644 src/mockModules/mock-user/dto/update-mock-user.dto.ts create mode 100644 src/mockModules/mock-user/mock-user.controller.spec.ts create mode 100644 src/mockModules/mock-user/mock-user.controller.ts create mode 100644 src/mockModules/mock-user/mock-user.module.ts create mode 100644 src/mockModules/mock-user/mock-user.service.spec.ts create mode 100644 src/mockModules/mock-user/mock-user.service.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 2d96552..fed7f03 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -112,7 +112,7 @@ model User { id Int @id @default(autoincrement()) email String @unique role UserRolesEnum - username String + userName String password String profilePicture String? createdAt DateTime @default(now()) @@ -128,7 +128,7 @@ model User { model Level { id Int @id @default(autoincrement()) - lable String + label String description String? users User[] } diff --git a/src/app.module.ts b/src/app.module.ts index 9046544..166f816 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -4,6 +4,9 @@ import { AppController } from "./app.controller"; import { AppService } from "./app.service"; import { PrismaModule } from "./prisma/prisma.module"; import { PrismaService } from "./prisma/prisma.service"; +import { MockUserModule } from "./mockModules/mock-user/mock-user.module"; +import { MockLevelModule } from "./mockModules/mock-level/mock-level.module"; +import { MockTeamModule } from "./mockModules/mock-team/mock-team.module"; @Module({ imports: [ @@ -11,6 +14,9 @@ import { PrismaService } from "./prisma/prisma.service"; isGlobal: true, }), PrismaModule, + MockUserModule, + MockLevelModule, + MockTeamModule, ], controllers: [AppController], providers: [AppService, PrismaService], diff --git a/src/mockModules/mock-level/dto/create-mock-level.dto.ts b/src/mockModules/mock-level/dto/create-mock-level.dto.ts new file mode 100644 index 0000000..4d7efe6 --- /dev/null +++ b/src/mockModules/mock-level/dto/create-mock-level.dto.ts @@ -0,0 +1,12 @@ +import { IsNotEmpty, IsOptional, IsString } from "class-validator"; + +export class CreateMockLevelDto { + @IsString() + @IsNotEmpty() + label: string; + + @IsOptional() + @IsString() + @IsNotEmpty() + description?: string; +} diff --git a/src/mockModules/mock-level/dto/response-mock-level.dto.ts b/src/mockModules/mock-level/dto/response-mock-level.dto.ts new file mode 100644 index 0000000..82c8ab7 --- /dev/null +++ b/src/mockModules/mock-level/dto/response-mock-level.dto.ts @@ -0,0 +1,5 @@ +export class ResponseMockLevelDto { + readonly id: number; + readonly label: string; + readonly description?: string; +} diff --git a/src/mockModules/mock-level/dto/update-mock-level.dto.ts b/src/mockModules/mock-level/dto/update-mock-level.dto.ts new file mode 100644 index 0000000..64e32c1 --- /dev/null +++ b/src/mockModules/mock-level/dto/update-mock-level.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateMockLevelDto } from './create-mock-level.dto'; + +export class UpdateMockLevelDto extends PartialType(CreateMockLevelDto) {} diff --git a/src/mockModules/mock-level/mock-level.controller.spec.ts b/src/mockModules/mock-level/mock-level.controller.spec.ts new file mode 100644 index 0000000..62dcc39 --- /dev/null +++ b/src/mockModules/mock-level/mock-level.controller.spec.ts @@ -0,0 +1,20 @@ +import { Test, TestingModule } from "@nestjs/testing"; +import { MockLevelController } from "./mock-level.controller"; +import { MockLevelService } from "./mock-level.service"; + +describe("MockLevelController", () => { + let controller: MockLevelController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [MockLevelController], + providers: [MockLevelService], + }).compile(); + + controller = module.get(MockLevelController); + }); + + it("should be defined", () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/mockModules/mock-level/mock-level.controller.ts b/src/mockModules/mock-level/mock-level.controller.ts new file mode 100644 index 0000000..8639c7c --- /dev/null +++ b/src/mockModules/mock-level/mock-level.controller.ts @@ -0,0 +1,63 @@ +import { + Controller, + Get, + Post, + Body, + Patch, + Param, + Delete, + HttpStatus, +} from "@nestjs/common"; +import { MockLevelService } from "./mock-level.service"; +import { CreateMockLevelDto } from "./dto/create-mock-level.dto"; +import { UpdateMockLevelDto } from "./dto/update-mock-level.dto"; +import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { ResponseMockLevelDto } from "./dto/response-mock-level.dto"; + +@Controller("mockUserService/level") +@ApiTags("mockUserService/level") +export class MockLevelController { + constructor(private readonly mockLevelService: MockLevelService) {} + + @Post() + @ApiOperation({ summary: "create new mock level" }) + @ApiResponse({ status: HttpStatus.CREATED, type: ResponseMockLevelDto }) + async create(@Body() createMockLevelDto: CreateMockLevelDto) { + return await this.mockLevelService.create(createMockLevelDto); + } + + @Get() + @ApiOperation({ summary: "get all mock level" }) + @ApiResponse({ + status: HttpStatus.OK, + type: ResponseMockLevelDto, + isArray: true, + }) + async findAll() { + return await this.mockLevelService.findAll(); + } + + @Get(":id") + @ApiOperation({ summary: "get mock level by id" }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseMockLevelDto }) + async findOne(@Param("id") id: string) { + return await this.mockLevelService.findOne(+id); + } + + @Patch(":id") + @ApiOperation({ summary: "update mock level by id" }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseMockLevelDto }) + async update( + @Param("id") id: string, + @Body() updateMockLevelDto: UpdateMockLevelDto + ) { + return await this.mockLevelService.update(+id, updateMockLevelDto); + } + + @Delete(":id") + @ApiOperation({ summary: "delete mock level by id" }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseMockLevelDto }) + async remove(@Param("id") id: string) { + return await this.mockLevelService.remove(+id); + } +} diff --git a/src/mockModules/mock-level/mock-level.module.ts b/src/mockModules/mock-level/mock-level.module.ts new file mode 100644 index 0000000..a467147 --- /dev/null +++ b/src/mockModules/mock-level/mock-level.module.ts @@ -0,0 +1,10 @@ +import { Module } from "@nestjs/common"; +import { MockLevelService } from "./mock-level.service"; +import { MockLevelController } from "./mock-level.controller"; +import { PrismaService } from "src/prisma/prisma.service"; + +@Module({ + controllers: [MockLevelController], + providers: [MockLevelService, PrismaService], +}) +export class MockLevelModule {} diff --git a/src/mockModules/mock-level/mock-level.service.spec.ts b/src/mockModules/mock-level/mock-level.service.spec.ts new file mode 100644 index 0000000..9a86f9d --- /dev/null +++ b/src/mockModules/mock-level/mock-level.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from "@nestjs/testing"; +import { MockLevelService } from "./mock-level.service"; + +describe("MockLevelService", () => { + let service: MockLevelService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [MockLevelService], + }).compile(); + + service = module.get(MockLevelService); + }); + + it("should be defined", () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/mockModules/mock-level/mock-level.service.ts b/src/mockModules/mock-level/mock-level.service.ts new file mode 100644 index 0000000..17f5c19 --- /dev/null +++ b/src/mockModules/mock-level/mock-level.service.ts @@ -0,0 +1,43 @@ +import { Injectable } from "@nestjs/common"; +import { CreateMockLevelDto } from "./dto/create-mock-level.dto"; +import { UpdateMockLevelDto } from "./dto/update-mock-level.dto"; +import { PrismaService } from "src/prisma/prisma.service"; + +@Injectable() +export class MockLevelService { + constructor(private prisma: PrismaService) {} + public async create(createMockLevelDto: CreateMockLevelDto) { + return await this.prisma.level.create({ + data: createMockLevelDto, + }); + } + + public async findAll() { + return await this.prisma.level.findMany(); + } + + public async findOne(id: number) { + return await this.prisma.level.findUnique({ + where: { + id, + }, + }); + } + + public async update(id: number, updateMockLevelDto: UpdateMockLevelDto) { + return await this.prisma.level.update({ + where: { + id, + }, + data: updateMockLevelDto, + }); + } + + public async remove(id: number) { + return await this.prisma.level.delete({ + where: { + id, + }, + }); + } +} diff --git a/src/mockModules/mock-team/dto/create-mock-team.dto.ts b/src/mockModules/mock-team/dto/create-mock-team.dto.ts new file mode 100644 index 0000000..3c4a649 --- /dev/null +++ b/src/mockModules/mock-team/dto/create-mock-team.dto.ts @@ -0,0 +1,12 @@ +import { IsNotEmpty, IsOptional, IsString } from "class-validator"; + +export class CreateMockTeamDto { + @IsString() + @IsNotEmpty() + name: string; + + @IsOptional() + @IsString() + @IsNotEmpty() + description?: string; +} diff --git a/src/mockModules/mock-team/dto/response-mock-team.dto.ts b/src/mockModules/mock-team/dto/response-mock-team.dto.ts new file mode 100644 index 0000000..75bb09e --- /dev/null +++ b/src/mockModules/mock-team/dto/response-mock-team.dto.ts @@ -0,0 +1,5 @@ +export class ResponseMockTeamDto { + readonly id: number; + readonly name: string; + readonly description?: string; +} diff --git a/src/mockModules/mock-team/dto/update-mock-team.dto.ts b/src/mockModules/mock-team/dto/update-mock-team.dto.ts new file mode 100644 index 0000000..d93f93f --- /dev/null +++ b/src/mockModules/mock-team/dto/update-mock-team.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateMockTeamDto } from './create-mock-team.dto'; + +export class UpdateMockTeamDto extends PartialType(CreateMockTeamDto) {} diff --git a/src/mockModules/mock-team/entities/mock-team.entity.ts b/src/mockModules/mock-team/entities/mock-team.entity.ts new file mode 100644 index 0000000..c6d4d6a --- /dev/null +++ b/src/mockModules/mock-team/entities/mock-team.entity.ts @@ -0,0 +1 @@ +export class MockTeam {} diff --git a/src/mockModules/mock-team/mock-team.controller.spec.ts b/src/mockModules/mock-team/mock-team.controller.spec.ts new file mode 100644 index 0000000..2f4c9e0 --- /dev/null +++ b/src/mockModules/mock-team/mock-team.controller.spec.ts @@ -0,0 +1,20 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { MockTeamController } from './mock-team.controller'; +import { MockTeamService } from './mock-team.service'; + +describe('MockTeamController', () => { + let controller: MockTeamController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [MockTeamController], + providers: [MockTeamService], + }).compile(); + + controller = module.get(MockTeamController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/mockModules/mock-team/mock-team.controller.ts b/src/mockModules/mock-team/mock-team.controller.ts new file mode 100644 index 0000000..35535bb --- /dev/null +++ b/src/mockModules/mock-team/mock-team.controller.ts @@ -0,0 +1,47 @@ +import { + Controller, + Get, + Post, + Body, + Patch, + Param, + Delete, +} from "@nestjs/common"; +import { MockTeamService } from "./mock-team.service"; +import { CreateMockTeamDto } from "./dto/create-mock-team.dto"; +import { UpdateMockTeamDto } from "./dto/update-mock-team.dto"; +import { ApiTags } from "@nestjs/swagger"; + +@Controller("mockUserService/team") +@ApiTags("mockUserService/team") +export class MockTeamController { + constructor(private readonly mockTeamService: MockTeamService) {} + + @Post() + async create(@Body() createMockTeamDto: CreateMockTeamDto) { + return await this.mockTeamService.create(createMockTeamDto); + } + + @Get() + async findAll() { + return await this.mockTeamService.findAll(); + } + + @Get(":id") + async findOne(@Param("id") id: string) { + return await this.mockTeamService.findOne(+id); + } + + @Patch(":id") + async update( + @Param("id") id: string, + @Body() updateMockTeamDto: UpdateMockTeamDto + ) { + return await this.mockTeamService.update(+id, updateMockTeamDto); + } + + @Delete(":id") + async remove(@Param("id") id: string) { + return await this.mockTeamService.remove(+id); + } +} diff --git a/src/mockModules/mock-team/mock-team.module.ts b/src/mockModules/mock-team/mock-team.module.ts new file mode 100644 index 0000000..f328598 --- /dev/null +++ b/src/mockModules/mock-team/mock-team.module.ts @@ -0,0 +1,10 @@ +import { Module } from "@nestjs/common"; +import { MockTeamService } from "./mock-team.service"; +import { MockTeamController } from "./mock-team.controller"; +import { PrismaService } from "src/prisma/prisma.service"; + +@Module({ + controllers: [MockTeamController], + providers: [MockTeamService, PrismaService], +}) +export class MockTeamModule {} diff --git a/src/mockModules/mock-team/mock-team.service.spec.ts b/src/mockModules/mock-team/mock-team.service.spec.ts new file mode 100644 index 0000000..4999d7d --- /dev/null +++ b/src/mockModules/mock-team/mock-team.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { MockTeamService } from './mock-team.service'; + +describe('MockTeamService', () => { + let service: MockTeamService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [MockTeamService], + }).compile(); + + service = module.get(MockTeamService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/mockModules/mock-team/mock-team.service.ts b/src/mockModules/mock-team/mock-team.service.ts new file mode 100644 index 0000000..0960543 --- /dev/null +++ b/src/mockModules/mock-team/mock-team.service.ts @@ -0,0 +1,44 @@ +import { Injectable } from "@nestjs/common"; +import { CreateMockTeamDto } from "./dto/create-mock-team.dto"; +import { UpdateMockTeamDto } from "./dto/update-mock-team.dto"; +import { PrismaService } from "src/prisma/prisma.service"; + +@Injectable() +export class MockTeamService { + constructor(private prisma: PrismaService) {} + + public async create(createMockTeamDto: CreateMockTeamDto) { + return await this.prisma.team.create({ + data: createMockTeamDto, + }); + } + + public async findAll() { + return await this.prisma.team.findMany(); + } + + public async findOne(id: number) { + return await this.prisma.team.findUnique({ + where: { + id, + }, + }); + } + + public async update(id: number, updateMockTeamDto: UpdateMockTeamDto) { + return await this.prisma.team.update({ + where: { + id, + }, + data: updateMockTeamDto, + }); + } + + public async remove(id: number) { + return await this.prisma.team.delete({ + where: { + id, + }, + }); + } +} diff --git a/src/mockModules/mock-user/dto/create-mock-user.dto.ts b/src/mockModules/mock-user/dto/create-mock-user.dto.ts new file mode 100644 index 0000000..8bd44d3 --- /dev/null +++ b/src/mockModules/mock-user/dto/create-mock-user.dto.ts @@ -0,0 +1,52 @@ +import { UserRolesEnum } from "@prisma/client"; +import { + IsDate, + IsEmail, + IsEnum, + IsNotEmpty, + IsNumber, + IsOptional, + IsString, + IsStrongPassword, +} from "class-validator"; + +export class CreateMockUserDto { + @IsEmail() + @IsNotEmpty() + email: string; + + @IsEnum(UserRolesEnum, { each: true, always: true }) + @IsNotEmpty() + role: UserRolesEnum; + + @IsNotEmpty() + @IsString() + userName: string; + + @IsStrongPassword() + @IsString() + password: string; + + @IsOptional() + @IsNotEmpty() + @IsString() + profilePicture: string; + + @IsNotEmpty() + @IsNumber() + levelId: number; + + @IsNotEmpty() + @IsNumber() + teamId: number; + + // Optional creation date of the request. + @IsDate() + @IsOptional() + createdAt?: Date; + + // Optional update date of the request. + @IsDate() + @IsOptional() + updatedAt?: Date; +} diff --git a/src/mockModules/mock-user/dto/response-mock-user.dto.ts b/src/mockModules/mock-user/dto/response-mock-user.dto.ts new file mode 100644 index 0000000..7fe3e65 --- /dev/null +++ b/src/mockModules/mock-user/dto/response-mock-user.dto.ts @@ -0,0 +1,12 @@ +export class ResponseMockUserDto { + readonly id: number; + readonly email: string; + readonly role: string; + readonly userName: string; + readonly password: string; + readonly profilePicture?: string; + readonly createdAt: Date; + readonly updatedAt: Date; + readonly levelId: number; + readonly teamId: number; +} diff --git a/src/mockModules/mock-user/dto/update-mock-user.dto.ts b/src/mockModules/mock-user/dto/update-mock-user.dto.ts new file mode 100644 index 0000000..cb4c624 --- /dev/null +++ b/src/mockModules/mock-user/dto/update-mock-user.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateMockUserDto } from './create-mock-user.dto'; + +export class UpdateMockUserDto extends PartialType(CreateMockUserDto) {} diff --git a/src/mockModules/mock-user/mock-user.controller.spec.ts b/src/mockModules/mock-user/mock-user.controller.spec.ts new file mode 100644 index 0000000..215fcec --- /dev/null +++ b/src/mockModules/mock-user/mock-user.controller.spec.ts @@ -0,0 +1,20 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { MockUserController } from './mock-user.controller'; +import { MockUserService } from './mock-user.service'; + +describe('MockUserController', () => { + let controller: MockUserController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [MockUserController], + providers: [MockUserService], + }).compile(); + + controller = module.get(MockUserController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/mockModules/mock-user/mock-user.controller.ts b/src/mockModules/mock-user/mock-user.controller.ts new file mode 100644 index 0000000..7336d24 --- /dev/null +++ b/src/mockModules/mock-user/mock-user.controller.ts @@ -0,0 +1,63 @@ +import { + Controller, + Get, + Post, + Body, + Patch, + Param, + Delete, + HttpStatus, +} from "@nestjs/common"; +import { MockUserService } from "./mock-user.service"; +import { CreateMockUserDto } from "./dto/create-mock-user.dto"; +import { UpdateMockUserDto } from "./dto/update-mock-user.dto"; +import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { ResponseMockUserDto } from "./dto/response-mock-user.dto"; + +@Controller("mockUserService/user") +@ApiTags("mockUserService/user") +export class MockUserController { + constructor(private readonly mockUserService: MockUserService) {} + + @Post() + @ApiOperation({ summary: "create new mock user" }) + @ApiResponse({ status: HttpStatus.CREATED, type: ResponseMockUserDto }) + create(@Body() createMockUserDto: CreateMockUserDto) { + return this.mockUserService.create(createMockUserDto); + } + + @Get() + @ApiOperation({ summary: "get all mock user" }) + @ApiResponse({ + status: HttpStatus.CREATED, + type: ResponseMockUserDto, + isArray: true, + }) + findAll() { + return this.mockUserService.findAll(); + } + + @Get(":id") + @ApiOperation({ summary: "get mock user by id" }) + @ApiResponse({ status: HttpStatus.CREATED, type: ResponseMockUserDto }) + findOne(@Param("id") id: string) { + return this.mockUserService.findOne(+id); + } + + @Patch(":id") + @ApiOperation({ summary: "update mock user by id" }) + @ApiResponse({ status: HttpStatus.CREATED, type: ResponseMockUserDto }) + update( + @Param("id") id: string, + @Body() updateMockUserDto: UpdateMockUserDto + ) { + return this.mockUserService.update(+id, updateMockUserDto); + } + + @Delete(":id") + @ApiOperation({ summary: "delete mock user by id" }) + @ApiResponse({ status: HttpStatus.CREATED, type: ResponseMockUserDto }) + remove(@Param("id") id: string) { + return this.mockUserService.remove(+id); + } +} diff --git a/src/mockModules/mock-user/mock-user.module.ts b/src/mockModules/mock-user/mock-user.module.ts new file mode 100644 index 0000000..8cf03ff --- /dev/null +++ b/src/mockModules/mock-user/mock-user.module.ts @@ -0,0 +1,11 @@ +import { Module } from "@nestjs/common"; +import { MockUserService } from "./mock-user.service"; +import { MockUserController } from "./mock-user.controller"; +import { PrismaService } from "src/prisma/prisma.service"; + +@Module({ + controllers: [MockUserController], + providers: [MockUserService, PrismaService], + exports: [MockUserService], +}) +export class MockUserModule {} diff --git a/src/mockModules/mock-user/mock-user.service.spec.ts b/src/mockModules/mock-user/mock-user.service.spec.ts new file mode 100644 index 0000000..af5894c --- /dev/null +++ b/src/mockModules/mock-user/mock-user.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { MockUserService } from './mock-user.service'; + +describe('MockUserService', () => { + let service: MockUserService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [MockUserService], + }).compile(); + + service = module.get(MockUserService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/mockModules/mock-user/mock-user.service.ts b/src/mockModules/mock-user/mock-user.service.ts new file mode 100644 index 0000000..2476bb5 --- /dev/null +++ b/src/mockModules/mock-user/mock-user.service.ts @@ -0,0 +1,42 @@ +import { Injectable } from "@nestjs/common"; +import { CreateMockUserDto } from "./dto/create-mock-user.dto"; +import { UpdateMockUserDto } from "./dto/update-mock-user.dto"; +import { PrismaService } from "src/prisma/prisma.service"; + +@Injectable() +export class MockUserService { + constructor(private prisma: PrismaService) {} + + public async create(createMockUserDto: CreateMockUserDto) { + return await this.prisma.user.create({ data: createMockUserDto }); + } + + public async findAll() { + return await this.prisma.user.findMany(); + } + + public async findOne(id: number) { + return await this.prisma.user.findUnique({ + where: { + id, + }, + }); + } + + public async update(id: number, updateMockUserDto: UpdateMockUserDto) { + return await this.prisma.user.update({ + where: { + id, + }, + data: updateMockUserDto, + }); + } + + public async remove(id: number) { + return await this.prisma.user.delete({ + where: { + id, + }, + }); + } +} From 89e7d4b708fd4bfa9657ab37eff4186ef4bcd28a Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Sun, 8 Oct 2023 20:33:53 +0530 Subject: [PATCH 009/175] Added error handling --- src/main.ts | 5 +- .../mock-level/mock-level.controller.ts | 68 +++++++++++++++--- .../mock-level/mock-level.service.ts | 6 +- .../mock-team/mock-team.controller.ts | 71 +++++++++++++++--- .../mock-team/mock-team.service.ts | 6 +- .../mock-user/mock-user.controller.ts | 72 +++++++++++++++---- .../mock-user/mock-user.service.ts | 6 +- 7 files changed, 194 insertions(+), 40 deletions(-) diff --git a/src/main.ts b/src/main.ts index 91e7dcd..88b1a40 100644 --- a/src/main.ts +++ b/src/main.ts @@ -15,6 +15,9 @@ async function bootstrap() { // Create a NestJS application instance const app = await NestFactory.create(AppModule, { cors: true }); + // Enable Cross-Origin Resource Sharing (CORS) + app.enableCors(); + // Enable using the container for class-validator useContainer(app.select(AppModule), { fallbackOnErrors: true }); @@ -39,7 +42,7 @@ async function bootstrap() { app.useGlobalPipes(new ValidationPipe(validationOptions)); // Configure global serialization using class-transformer - app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector))); + // app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector))); // Configure Swagger documentation const swaggerConfig = new DocumentBuilder() diff --git a/src/mockModules/mock-level/mock-level.controller.ts b/src/mockModules/mock-level/mock-level.controller.ts index 8639c7c..ff8c326 100644 --- a/src/mockModules/mock-level/mock-level.controller.ts +++ b/src/mockModules/mock-level/mock-level.controller.ts @@ -7,6 +7,8 @@ import { Param, Delete, HttpStatus, + ParseIntPipe, + Res, } from "@nestjs/common"; import { MockLevelService } from "./mock-level.service"; import { CreateMockLevelDto } from "./dto/create-mock-level.dto"; @@ -22,8 +24,17 @@ export class MockLevelController { @Post() @ApiOperation({ summary: "create new mock level" }) @ApiResponse({ status: HttpStatus.CREATED, type: ResponseMockLevelDto }) - async create(@Body() createMockLevelDto: CreateMockLevelDto) { - return await this.mockLevelService.create(createMockLevelDto); + async create(@Res() res, @Body() createMockLevelDto: CreateMockLevelDto) { + try { + const level = await this.mockLevelService.create(createMockLevelDto); + return res + .status(HttpStatus.OK) + .json({ data: level, message: "Successfully done" }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } } @Get() @@ -33,31 +44,68 @@ export class MockLevelController { type: ResponseMockLevelDto, isArray: true, }) - async findAll() { - return await this.mockLevelService.findAll(); + async findAll(@Res() res) { + try { + const level = await this.mockLevelService.findAll(); + return res + .status(HttpStatus.OK) + .json({ data: level, message: "Successfully done" }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } } @Get(":id") @ApiOperation({ summary: "get mock level by id" }) @ApiResponse({ status: HttpStatus.OK, type: ResponseMockLevelDto }) - async findOne(@Param("id") id: string) { - return await this.mockLevelService.findOne(+id); + async findOne(@Res() res, @Param("id", ParseIntPipe) id: number) { + try { + const level = await this.mockLevelService.findOne(id); + return res + .status(HttpStatus.OK) + .json({ data: level, message: "Successfully done" }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.message }); + } } @Patch(":id") @ApiOperation({ summary: "update mock level by id" }) @ApiResponse({ status: HttpStatus.OK, type: ResponseMockLevelDto }) async update( - @Param("id") id: string, + @Res() res, + @Param("id", ParseIntPipe) id: number, @Body() updateMockLevelDto: UpdateMockLevelDto ) { - return await this.mockLevelService.update(+id, updateMockLevelDto); + try { + const level = await this.mockLevelService.update(id, updateMockLevelDto); + return res + .status(HttpStatus.OK) + .json({ data: level, message: "Successfully done" }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } } @Delete(":id") @ApiOperation({ summary: "delete mock level by id" }) @ApiResponse({ status: HttpStatus.OK, type: ResponseMockLevelDto }) - async remove(@Param("id") id: string) { - return await this.mockLevelService.remove(+id); + async remove(@Res() res, @Param("id", ParseIntPipe) id: number) { + try { + const level = await this.mockLevelService.remove(id); + return res + .status(HttpStatus.OK) + .json({ data: level, message: "Successfully done" }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } } } diff --git a/src/mockModules/mock-level/mock-level.service.ts b/src/mockModules/mock-level/mock-level.service.ts index 17f5c19..b1a84dc 100644 --- a/src/mockModules/mock-level/mock-level.service.ts +++ b/src/mockModules/mock-level/mock-level.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from "@nestjs/common"; +import { Injectable, NotFoundException } from "@nestjs/common"; import { CreateMockLevelDto } from "./dto/create-mock-level.dto"; import { UpdateMockLevelDto } from "./dto/update-mock-level.dto"; import { PrismaService } from "src/prisma/prisma.service"; @@ -17,11 +17,13 @@ export class MockLevelService { } public async findOne(id: number) { - return await this.prisma.level.findUnique({ + const level = await this.prisma.level.findUnique({ where: { id, }, }); + if (!level) throw new NotFoundException(`Level with id #${id} not found`); + return level; } public async update(id: number, updateMockLevelDto: UpdateMockLevelDto) { diff --git a/src/mockModules/mock-team/mock-team.controller.ts b/src/mockModules/mock-team/mock-team.controller.ts index 35535bb..ac06a53 100644 --- a/src/mockModules/mock-team/mock-team.controller.ts +++ b/src/mockModules/mock-team/mock-team.controller.ts @@ -6,6 +6,9 @@ import { Patch, Param, Delete, + Res, + HttpStatus, + ParseIntPipe, } from "@nestjs/common"; import { MockTeamService } from "./mock-team.service"; import { CreateMockTeamDto } from "./dto/create-mock-team.dto"; @@ -18,30 +21,76 @@ export class MockTeamController { constructor(private readonly mockTeamService: MockTeamService) {} @Post() - async create(@Body() createMockTeamDto: CreateMockTeamDto) { - return await this.mockTeamService.create(createMockTeamDto); + async create(@Body() createMockTeamDto: CreateMockTeamDto, @Res() res) { + try { + const team = await this.mockTeamService.create(createMockTeamDto); + return res + .status(HttpStatus.OK) + .json({ data: team, message: "Successfully done" }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } } @Get() - async findAll() { - return await this.mockTeamService.findAll(); + async findAll(@Res() res) { + try { + const team = await this.mockTeamService.findAll(); + return res + .status(HttpStatus.OK) + .json({ data: team, message: "Successfully done" }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } } @Get(":id") - async findOne(@Param("id") id: string) { - return await this.mockTeamService.findOne(+id); + async findOne(@Param("id", ParseIntPipe) id: number, @Res() res) { + try { + const team = await this.mockTeamService.findOne(id); + return res + .status(HttpStatus.OK) + .json({ data: team, message: "Successfully done" }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.message }); + } } @Patch(":id") async update( - @Param("id") id: string, - @Body() updateMockTeamDto: UpdateMockTeamDto + @Param("id", ParseIntPipe) id: number, + @Body() updateMockTeamDto: UpdateMockTeamDto, + @Res() res ) { - return await this.mockTeamService.update(+id, updateMockTeamDto); + try { + const team = await this.mockTeamService.update(id, updateMockTeamDto); + return res + .status(HttpStatus.OK) + .json({ data: team, message: "Successfully done" }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } } @Delete(":id") - async remove(@Param("id") id: string) { - return await this.mockTeamService.remove(+id); + async remove(@Param("id", ParseIntPipe) id: number, @Res() res) { + try { + const team = await this.mockTeamService.remove(id); + return res + .status(HttpStatus.OK) + .json({ data: team, message: "Successfully done" }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } } } diff --git a/src/mockModules/mock-team/mock-team.service.ts b/src/mockModules/mock-team/mock-team.service.ts index 0960543..e03aa63 100644 --- a/src/mockModules/mock-team/mock-team.service.ts +++ b/src/mockModules/mock-team/mock-team.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from "@nestjs/common"; +import { Injectable, NotFoundException } from "@nestjs/common"; import { CreateMockTeamDto } from "./dto/create-mock-team.dto"; import { UpdateMockTeamDto } from "./dto/update-mock-team.dto"; import { PrismaService } from "src/prisma/prisma.service"; @@ -18,11 +18,13 @@ export class MockTeamService { } public async findOne(id: number) { - return await this.prisma.team.findUnique({ + const team = await this.prisma.team.findUnique({ where: { id, }, }); + if (!team) throw new NotFoundException(`Team with id #${id} not found`); + return team; } public async update(id: number, updateMockTeamDto: UpdateMockTeamDto) { diff --git a/src/mockModules/mock-user/mock-user.controller.ts b/src/mockModules/mock-user/mock-user.controller.ts index 7336d24..7711a52 100644 --- a/src/mockModules/mock-user/mock-user.controller.ts +++ b/src/mockModules/mock-user/mock-user.controller.ts @@ -7,6 +7,8 @@ import { Param, Delete, HttpStatus, + Res, + ParseIntPipe, } from "@nestjs/common"; import { MockUserService } from "./mock-user.service"; import { CreateMockUserDto } from "./dto/create-mock-user.dto"; @@ -22,8 +24,17 @@ export class MockUserController { @Post() @ApiOperation({ summary: "create new mock user" }) @ApiResponse({ status: HttpStatus.CREATED, type: ResponseMockUserDto }) - create(@Body() createMockUserDto: CreateMockUserDto) { - return this.mockUserService.create(createMockUserDto); + async create(@Body() createMockUserDto: CreateMockUserDto, @Res() res) { + try { + const user = await this.mockUserService.create(createMockUserDto); + return res + .status(HttpStatus.OK) + .json({ data: user, message: "Successfully done" }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } } @Get() @@ -33,31 +44,68 @@ export class MockUserController { type: ResponseMockUserDto, isArray: true, }) - findAll() { - return this.mockUserService.findAll(); + async findAll(@Res() res) { + try { + const user = await this.mockUserService.findAll(); + return res + .status(HttpStatus.OK) + .json({ data: user, message: "Successfully done" }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } } @Get(":id") @ApiOperation({ summary: "get mock user by id" }) @ApiResponse({ status: HttpStatus.CREATED, type: ResponseMockUserDto }) - findOne(@Param("id") id: string) { - return this.mockUserService.findOne(+id); + async findOne(@Param("id", ParseIntPipe) id: number, @Res() res) { + try { + const user = await this.mockUserService.findOne(id); + return res + .status(HttpStatus.OK) + .json({ data: user, message: "Successfully done" }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.message }); + } } @Patch(":id") @ApiOperation({ summary: "update mock user by id" }) @ApiResponse({ status: HttpStatus.CREATED, type: ResponseMockUserDto }) - update( - @Param("id") id: string, - @Body() updateMockUserDto: UpdateMockUserDto + async update( + @Param("id", ParseIntPipe) id: number, + @Body() updateMockUserDto: UpdateMockUserDto, + @Res() res ) { - return this.mockUserService.update(+id, updateMockUserDto); + try { + const user = await this.mockUserService.update(id, updateMockUserDto); + return res + .status(HttpStatus.OK) + .json({ data: user, message: "Successfully done" }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } } @Delete(":id") @ApiOperation({ summary: "delete mock user by id" }) @ApiResponse({ status: HttpStatus.CREATED, type: ResponseMockUserDto }) - remove(@Param("id") id: string) { - return this.mockUserService.remove(+id); + async remove(@Param("id", ParseIntPipe) id: number, @Res() res) { + try { + const user = await this.mockUserService.remove(id); + return res + .status(HttpStatus.OK) + .json({ data: user, message: "Successfully done" }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } } } diff --git a/src/mockModules/mock-user/mock-user.service.ts b/src/mockModules/mock-user/mock-user.service.ts index 2476bb5..45fffac 100644 --- a/src/mockModules/mock-user/mock-user.service.ts +++ b/src/mockModules/mock-user/mock-user.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from "@nestjs/common"; +import { Injectable, NotFoundException } from "@nestjs/common"; import { CreateMockUserDto } from "./dto/create-mock-user.dto"; import { UpdateMockUserDto } from "./dto/update-mock-user.dto"; import { PrismaService } from "src/prisma/prisma.service"; @@ -16,11 +16,13 @@ export class MockUserService { } public async findOne(id: number) { - return await this.prisma.user.findUnique({ + const user = await this.prisma.user.findUnique({ where: { id, }, }); + if (!user) throw new NotFoundException(`User with id #${id} not found`); + return user; } public async update(id: number, updateMockUserDto: UpdateMockUserDto) { From 39bac286ff2030c38667ba0749736d22c7516f9e Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Sun, 8 Oct 2023 20:36:56 +0530 Subject: [PATCH 010/175] removed extra files --- .../mock-level/mock-level.controller.spec.ts | 20 ------------------- .../mock-level/mock-level.service.spec.ts | 18 ----------------- .../mock-team/entities/mock-team.entity.ts | 1 - .../mock-team/mock-team.controller.spec.ts | 20 ------------------- .../mock-team/mock-team.service.spec.ts | 18 ----------------- .../mock-user/mock-user.controller.spec.ts | 20 ------------------- .../mock-user/mock-user.service.spec.ts | 18 ----------------- 7 files changed, 115 deletions(-) delete mode 100644 src/mockModules/mock-level/mock-level.controller.spec.ts delete mode 100644 src/mockModules/mock-level/mock-level.service.spec.ts delete mode 100644 src/mockModules/mock-team/entities/mock-team.entity.ts delete mode 100644 src/mockModules/mock-team/mock-team.controller.spec.ts delete mode 100644 src/mockModules/mock-team/mock-team.service.spec.ts delete mode 100644 src/mockModules/mock-user/mock-user.controller.spec.ts delete mode 100644 src/mockModules/mock-user/mock-user.service.spec.ts diff --git a/src/mockModules/mock-level/mock-level.controller.spec.ts b/src/mockModules/mock-level/mock-level.controller.spec.ts deleted file mode 100644 index 62dcc39..0000000 --- a/src/mockModules/mock-level/mock-level.controller.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Test, TestingModule } from "@nestjs/testing"; -import { MockLevelController } from "./mock-level.controller"; -import { MockLevelService } from "./mock-level.service"; - -describe("MockLevelController", () => { - let controller: MockLevelController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [MockLevelController], - providers: [MockLevelService], - }).compile(); - - controller = module.get(MockLevelController); - }); - - it("should be defined", () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/src/mockModules/mock-level/mock-level.service.spec.ts b/src/mockModules/mock-level/mock-level.service.spec.ts deleted file mode 100644 index 9a86f9d..0000000 --- a/src/mockModules/mock-level/mock-level.service.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from "@nestjs/testing"; -import { MockLevelService } from "./mock-level.service"; - -describe("MockLevelService", () => { - let service: MockLevelService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [MockLevelService], - }).compile(); - - service = module.get(MockLevelService); - }); - - it("should be defined", () => { - expect(service).toBeDefined(); - }); -}); diff --git a/src/mockModules/mock-team/entities/mock-team.entity.ts b/src/mockModules/mock-team/entities/mock-team.entity.ts deleted file mode 100644 index c6d4d6a..0000000 --- a/src/mockModules/mock-team/entities/mock-team.entity.ts +++ /dev/null @@ -1 +0,0 @@ -export class MockTeam {} diff --git a/src/mockModules/mock-team/mock-team.controller.spec.ts b/src/mockModules/mock-team/mock-team.controller.spec.ts deleted file mode 100644 index 2f4c9e0..0000000 --- a/src/mockModules/mock-team/mock-team.controller.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { MockTeamController } from './mock-team.controller'; -import { MockTeamService } from './mock-team.service'; - -describe('MockTeamController', () => { - let controller: MockTeamController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [MockTeamController], - providers: [MockTeamService], - }).compile(); - - controller = module.get(MockTeamController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/src/mockModules/mock-team/mock-team.service.spec.ts b/src/mockModules/mock-team/mock-team.service.spec.ts deleted file mode 100644 index 4999d7d..0000000 --- a/src/mockModules/mock-team/mock-team.service.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { MockTeamService } from './mock-team.service'; - -describe('MockTeamService', () => { - let service: MockTeamService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [MockTeamService], - }).compile(); - - service = module.get(MockTeamService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); -}); diff --git a/src/mockModules/mock-user/mock-user.controller.spec.ts b/src/mockModules/mock-user/mock-user.controller.spec.ts deleted file mode 100644 index 215fcec..0000000 --- a/src/mockModules/mock-user/mock-user.controller.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { MockUserController } from './mock-user.controller'; -import { MockUserService } from './mock-user.service'; - -describe('MockUserController', () => { - let controller: MockUserController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [MockUserController], - providers: [MockUserService], - }).compile(); - - controller = module.get(MockUserController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/src/mockModules/mock-user/mock-user.service.spec.ts b/src/mockModules/mock-user/mock-user.service.spec.ts deleted file mode 100644 index af5894c..0000000 --- a/src/mockModules/mock-user/mock-user.service.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { MockUserService } from './mock-user.service'; - -describe('MockUserService', () => { - let service: MockUserService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [MockUserService], - }).compile(); - - service = module.get(MockUserService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); -}); From 5c8ea7f551f5cbf34a6046fa8270435a1b55ff85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CVamshiBatta=E2=80=9D?= <“batta.v@esmagico.in”> Date: Tue, 10 Oct 2023 11:47:46 +0530 Subject: [PATCH 011/175] Role, Competency and Competency Level APIs. --- prisma/schema.prisma | 54 ++++-- src/app.module.ts | 8 +- src/main.ts | 10 +- src/mockModules/index.ts | 0 .../dto/create-competency-level.dto.ts | 13 ++ .../mock-competency-level/dto/index.ts | 3 + .../dto/response-competency-level.dto.ts | 6 + .../dto/update-competency-level.dto.ts | 4 + .../mock-competency-level.controller.ts | 138 +++++++++++++ .../mock-competency-level.module.ts | 9 + .../mock-competency-level.service.ts | 50 +++++ .../dto/create-competency.dto.ts | 13 ++ src/mockModules/mock-competency/dto/index.ts | 3 + .../dto/response-competency.dto.ts | 6 + .../dto/update-competency.dto.ts | 4 + .../mock-competency.controller.ts | 183 ++++++++++++++++++ .../mock-competency/mock-competency.module.ts | 10 + .../mock-competency.service.ts | 98 ++++++++++ .../mock-level/mock-level.controller.ts | 2 +- .../mock-role/dto/create-mock-role.dto.ts | 13 ++ src/mockModules/mock-role/dto/index.ts | 3 + .../mock-role/dto/response-mock-role.dto.ts | 8 + .../mock-role/dto/update-mock-role.dto.ts | 4 + .../mock-role/mock-role.controller.ts | 162 ++++++++++++++++ src/mockModules/mock-role/mock-role.module.ts | 11 ++ .../mock-role/mock-role.service.ts | 94 +++++++++ .../mock-team/mock-team.controller.ts | 2 +- .../mock-user/mock-user.controller.ts | 2 +- src/mockModules/mock.module.ts | 28 +++ src/prisma/prisma.module.ts | 9 +- src/prisma/prisma.service.ts | 17 +- 31 files changed, 934 insertions(+), 33 deletions(-) delete mode 100644 src/mockModules/index.ts create mode 100644 src/mockModules/mock-competency-level/dto/create-competency-level.dto.ts create mode 100644 src/mockModules/mock-competency-level/dto/index.ts create mode 100644 src/mockModules/mock-competency-level/dto/response-competency-level.dto.ts create mode 100644 src/mockModules/mock-competency-level/dto/update-competency-level.dto.ts create mode 100644 src/mockModules/mock-competency-level/mock-competency-level.controller.ts create mode 100644 src/mockModules/mock-competency-level/mock-competency-level.module.ts create mode 100644 src/mockModules/mock-competency-level/mock-competency-level.service.ts create mode 100644 src/mockModules/mock-competency/dto/create-competency.dto.ts create mode 100644 src/mockModules/mock-competency/dto/index.ts create mode 100644 src/mockModules/mock-competency/dto/response-competency.dto.ts create mode 100644 src/mockModules/mock-competency/dto/update-competency.dto.ts create mode 100644 src/mockModules/mock-competency/mock-competency.controller.ts create mode 100644 src/mockModules/mock-competency/mock-competency.module.ts create mode 100644 src/mockModules/mock-competency/mock-competency.service.ts create mode 100644 src/mockModules/mock-role/dto/create-mock-role.dto.ts create mode 100644 src/mockModules/mock-role/dto/index.ts create mode 100644 src/mockModules/mock-role/dto/response-mock-role.dto.ts create mode 100644 src/mockModules/mock-role/dto/update-mock-role.dto.ts create mode 100644 src/mockModules/mock-role/mock-role.controller.ts create mode 100644 src/mockModules/mock-role/mock-role.module.ts create mode 100644 src/mockModules/mock-role/mock-role.service.ts create mode 100644 src/mockModules/mock.module.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index fed7f03..b4e78ac 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -141,40 +141,58 @@ model Team { } model Role { - id Int @id @default(autoincrement()) - roleName String + id Int @id @default(autoincrement()) + name String @unique description String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt users User[] - competencies Competency[] + competencies RoleToCompetency[] @@map("roles") } model Competency { - id Int @id @default(autoincrement()) - competencyName String - description String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - roles Role[] - levels CompetencyLevel[] + id Int @id @default(autoincrement()) + name String + description String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + roles RoleToCompetency[] + competencyLevels CompetencyToCompetencyLevel[] @@map("competencies") } +model RoleToCompetency { + role Role @relation(fields: [roleId], references: [id]) + roleId Int + competency Competency @relation(fields: [competencyId], references: [id]) + competencyId Int + + @@id([roleId, competencyId]) +} + model CompetencyLevel { - id Int @id @default(autoincrement()) - name String - description String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - Competency Competency[] + id Int @id @default(autoincrement()) + name String + description String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + competencies CompetencyToCompetencyLevel[] @@map("competency_levels") } +model CompetencyToCompetencyLevel { + competency Competency @relation(fields: [competencyId], references: [id]) + competencyId Int + competencyLevel CompetencyLevel @relation(fields: [competencyLevelId], references: [id]) + competencyLevelId Int + + @@id([competencyId, competencyLevelId]) +} + enum UserRolesEnum { ADMIN COMSUMER diff --git a/src/app.module.ts b/src/app.module.ts index 166f816..5f0804a 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -4,9 +4,7 @@ import { AppController } from "./app.controller"; import { AppService } from "./app.service"; import { PrismaModule } from "./prisma/prisma.module"; import { PrismaService } from "./prisma/prisma.service"; -import { MockUserModule } from "./mockModules/mock-user/mock-user.module"; -import { MockLevelModule } from "./mockModules/mock-level/mock-level.module"; -import { MockTeamModule } from "./mockModules/mock-team/mock-team.module"; +import { MockModule } from "./mockModules/mock.module"; @Module({ imports: [ @@ -14,9 +12,7 @@ import { MockTeamModule } from "./mockModules/mock-team/mock-team.module"; isGlobal: true, }), PrismaModule, - MockUserModule, - MockLevelModule, - MockTeamModule, + MockModule ], controllers: [AppController], providers: [AppService, PrismaService], diff --git a/src/main.ts b/src/main.ts index 88b1a40..429e9b6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -75,7 +75,15 @@ async function bootstrap() { }); // Start the NestJS application, listening on the specified port or defaulting to 4010 - await app.listen(configService.get("APP_PORT") || 4010); + const PORT = configService.get("APP_PORT") || 4010; + await app.listen(PORT, () => { + console.log( + ` + 🔌 REST API ready at http://localhost:${PORT}/ + 💻 Swagger UI ready at http://localhost:${PORT}/${SWAGGER_CONSTANTS.PATH} + ` + ); + }); } // Call the bootstrap function to start the application diff --git a/src/mockModules/index.ts b/src/mockModules/index.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/mockModules/mock-competency-level/dto/create-competency-level.dto.ts b/src/mockModules/mock-competency-level/dto/create-competency-level.dto.ts new file mode 100644 index 0000000..ea32163 --- /dev/null +++ b/src/mockModules/mock-competency-level/dto/create-competency-level.dto.ts @@ -0,0 +1,13 @@ + +import { IsNotEmpty, IsOptional, IsString } from "class-validator"; + +export class CreateCompetencyLevelDto { + @IsString() + @IsNotEmpty() + name: string; + + @IsOptional() + @IsString() + @IsNotEmpty() + description?: string; +} \ No newline at end of file diff --git a/src/mockModules/mock-competency-level/dto/index.ts b/src/mockModules/mock-competency-level/dto/index.ts new file mode 100644 index 0000000..a9f39a6 --- /dev/null +++ b/src/mockModules/mock-competency-level/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-competency-level.dto'; +export * from './response-competency-level.dto'; +export * from './update-competency-level.dto'; \ No newline at end of file diff --git a/src/mockModules/mock-competency-level/dto/response-competency-level.dto.ts b/src/mockModules/mock-competency-level/dto/response-competency-level.dto.ts new file mode 100644 index 0000000..595d18e --- /dev/null +++ b/src/mockModules/mock-competency-level/dto/response-competency-level.dto.ts @@ -0,0 +1,6 @@ + +export class ResponseCompetencyLevelDto { + readonly id: number; + readonly name: string; + readonly description?: string; + } \ No newline at end of file diff --git a/src/mockModules/mock-competency-level/dto/update-competency-level.dto.ts b/src/mockModules/mock-competency-level/dto/update-competency-level.dto.ts new file mode 100644 index 0000000..20c315f --- /dev/null +++ b/src/mockModules/mock-competency-level/dto/update-competency-level.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateCompetencyLevelDto } from './create-competency-level.dto'; + +export class UpdateCompetencyLevelDto extends PartialType(CreateCompetencyLevelDto) {} \ No newline at end of file diff --git a/src/mockModules/mock-competency-level/mock-competency-level.controller.ts b/src/mockModules/mock-competency-level/mock-competency-level.controller.ts new file mode 100644 index 0000000..935a9e3 --- /dev/null +++ b/src/mockModules/mock-competency-level/mock-competency-level.controller.ts @@ -0,0 +1,138 @@ +import { + Body, + Controller, + Delete, + Get, + HttpStatus, + Param, + ParseIntPipe, + Patch, + Post, + Res, +} from "@nestjs/common"; +import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { MockCompetencyLevelService } from "./mock-competency-level.service"; +import { CreateCompetencyLevelDto, UpdateCompetencyLevelDto } from "./dto"; + +@Controller("competency-level") +@ApiTags("mockUserService/competency-level") +export class MockCompetencyLevelController { + constructor(private competencyLevelService: MockCompetencyLevelService) {} + @Post() + @ApiOperation({ summary: "create new mock competency-level" }) + @ApiResponse({ status: HttpStatus.CREATED }) + async createCompetencyLevel( + @Res() res, + @Body() createCompetencyLevelDto: CreateCompetencyLevelDto + ) { + try { + const competency = + await this.competencyLevelService.createCompetencyLevel( + createCompetencyLevelDto + ); + return res + .status(HttpStatus.OK) + .json({ + data: competency, + message: "CompetencyLevel successfully created.", + }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } + + @Get() + @ApiOperation({ summary: "fetch all mock competency-levels" }) + @ApiResponse({ status: HttpStatus.OK }) + async getAllCompetencyLevels(@Res() res) { + try { + const competency = + await this.competencyLevelService.findAllCompetencyLevels(); + return res + .status(HttpStatus.OK) + .json({ + data: competency, + message: "CompetencyLevels successfully fetched.", + }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } + + @Get(":id") + @ApiOperation({ summary: "get mock competency-level by id" }) + @ApiResponse({ status: HttpStatus.OK }) + async findOneCompetencyLevel( + @Res() res, + @Param("id", ParseIntPipe) id: number + ) { + try { + const competency = + await this.competencyLevelService.findCompetencyLevelById(id); + return res + .status(HttpStatus.OK) + .json({ + data: competency, + message: "CompetencyLevel successfully fetched.", + }); + } catch (error) { + return res + .status(error.response.statusCode ?? HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.response.message ?? "Internal server error." }); + } + } + + @Patch(":id") + @ApiOperation({ summary: "update mock competency-level by id" }) + @ApiResponse({ status: HttpStatus.OK }) + async updateCompetencyLevel( + @Res() res, + @Param("id", ParseIntPipe) id: number, + @Body() updateMockCompetencyLevelDto: UpdateCompetencyLevelDto + ) { + try { + const competency = + await this.competencyLevelService.updateCompetencyLevelById( + id, + updateMockCompetencyLevelDto + ); + return res + .status(HttpStatus.OK) + .json({ + data: competency, + message: "CompetencyLevels successfully updated.", + }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } + + @Delete(":id") + @ApiOperation({ summary: "delete mock competency-level by id" }) + @ApiResponse({ status: HttpStatus.OK }) + async removeCompetencyLevel( + @Res() res, + @Param("id", ParseIntPipe) id: number + ) { + try { + const competency = + await this.competencyLevelService.removeCompetencyLevel(id); + return res + .status(HttpStatus.OK) + .json({ + data: competency, + message: "CompetencyLevels successfully deleted.", + }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } +} diff --git a/src/mockModules/mock-competency-level/mock-competency-level.module.ts b/src/mockModules/mock-competency-level/mock-competency-level.module.ts new file mode 100644 index 0000000..61fe12b --- /dev/null +++ b/src/mockModules/mock-competency-level/mock-competency-level.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { MockCompetencyLevelController } from './mock-competency-level.controller'; +import { MockCompetencyLevelService } from './mock-competency-level.service'; + +@Module({ + controllers: [MockCompetencyLevelController], + providers: [MockCompetencyLevelService] +}) +export class MockCompetencyLevelModule {} diff --git a/src/mockModules/mock-competency-level/mock-competency-level.service.ts b/src/mockModules/mock-competency-level/mock-competency-level.service.ts new file mode 100644 index 0000000..f27b8bd --- /dev/null +++ b/src/mockModules/mock-competency-level/mock-competency-level.service.ts @@ -0,0 +1,50 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { PrismaService } from 'src/prisma/prisma.service'; +import { CreateCompetencyLevelDto, UpdateCompetencyLevelDto } from './dto'; + +@Injectable() +export class MockCompetencyLevelService { + constructor(private prisma: PrismaService) {} + public async createCompetencyLevel(createCompetencyLevelDto: CreateCompetencyLevelDto) { + return this.prisma.competencyLevel.create({ + data: createCompetencyLevelDto + }); + } + + public async findAllCompetencyLevels() { + return this.prisma.competencyLevel.findMany(); + } + + public async findCompetencyLevelById(id: number) { + const competencyLevel = await this.prisma.competencyLevel.findUnique({ + where: { + id, + }, + include: { + competencies: true, + }, + }); + if (!competencyLevel) throw new NotFoundException(`CompetencyLevel with id #${id} not found`); + return competencyLevel; + } + + public async updateCompetencyLevelById( + id: number, + updateCompetencyLevelDto: UpdateCompetencyLevelDto + ) { + return this.prisma.competencyLevel.update({ + where: { + id, + }, + data: updateCompetencyLevelDto, + }); + } + + public async removeCompetencyLevel(id: number) { + return this.prisma.competencyLevel.delete({ + where: { + id, + }, + }); + } +} diff --git a/src/mockModules/mock-competency/dto/create-competency.dto.ts b/src/mockModules/mock-competency/dto/create-competency.dto.ts new file mode 100644 index 0000000..85e07c5 --- /dev/null +++ b/src/mockModules/mock-competency/dto/create-competency.dto.ts @@ -0,0 +1,13 @@ + +import { IsNotEmpty, IsOptional, IsString } from "class-validator"; + +export class CreateCompetencyDto { + @IsString() + @IsNotEmpty() + name: string; + + @IsOptional() + @IsString() + @IsNotEmpty() + description?: string; +} \ No newline at end of file diff --git a/src/mockModules/mock-competency/dto/index.ts b/src/mockModules/mock-competency/dto/index.ts new file mode 100644 index 0000000..41b680c --- /dev/null +++ b/src/mockModules/mock-competency/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-competency.dto'; +export * from './response-competency.dto'; +export * from './update-competency.dto'; \ No newline at end of file diff --git a/src/mockModules/mock-competency/dto/response-competency.dto.ts b/src/mockModules/mock-competency/dto/response-competency.dto.ts new file mode 100644 index 0000000..ad826fd --- /dev/null +++ b/src/mockModules/mock-competency/dto/response-competency.dto.ts @@ -0,0 +1,6 @@ + +export class ResponseCompetencyDto { + readonly id: number; + readonly name: string; + readonly description?: string; + } \ No newline at end of file diff --git a/src/mockModules/mock-competency/dto/update-competency.dto.ts b/src/mockModules/mock-competency/dto/update-competency.dto.ts new file mode 100644 index 0000000..06234aa --- /dev/null +++ b/src/mockModules/mock-competency/dto/update-competency.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateCompetencyDto } from './create-competency.dto'; + +export class UpdateCompetencyDto extends PartialType(CreateCompetencyDto) {} \ No newline at end of file diff --git a/src/mockModules/mock-competency/mock-competency.controller.ts b/src/mockModules/mock-competency/mock-competency.controller.ts new file mode 100644 index 0000000..d1f3a4d --- /dev/null +++ b/src/mockModules/mock-competency/mock-competency.controller.ts @@ -0,0 +1,183 @@ +import { + Body, + Controller, + Delete, + Get, + HttpStatus, + Param, + ParseIntPipe, + Patch, + Post, + Res, +} from "@nestjs/common"; +import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { CreateCompetencyDto, UpdateCompetencyDto } from "./dto"; +import { MockCompetencyService } from "./mock-competency.service"; +import { CreateCompetencyLevelDto } from "../mock-competency-level/dto"; + +@Controller("competency") +@ApiTags("mockUserService/competency") +export class MockCompetencyController { + constructor(private competencyService: MockCompetencyService) {} + + @Post() + @ApiOperation({ summary: "create new mock competency" }) + @ApiResponse({ status: HttpStatus.CREATED }) + async createCompetency( + @Res() res, + @Body() createCompetencyDto: CreateCompetencyDto + ) { + try { + const competency = await this.competencyService.createCompetency( + createCompetencyDto + ); + return res + .status(HttpStatus.OK) + .json({ + data: competency, + message: "Competency successfully created.", + }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } + + @Get() + @ApiOperation({ summary: "fetch all mock competencies" }) + @ApiResponse({ status: HttpStatus.CREATED }) + async getAllCompetencies(@Res() res) { + try { + const competency = await this.competencyService.findAllCompetencies(); + return res + .status(HttpStatus.OK) + .json({ + data: competency, + message: "Competency successfully created.", + }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } + + @Get(":id") + @ApiOperation({ summary: "get mock competency by id" }) + @ApiResponse({ status: HttpStatus.OK }) + async findOneCompetency(@Res() res, @Param("id", ParseIntPipe) id: number) { + try { + const competency = await this.competencyService.findCompetencyById(id); + return res + .status(HttpStatus.OK) + .json({ + data: competency, + message: "Competency successfully fetched.", + }); + } catch (error) { + return res + .status(error.response.statusCode ?? HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.response.message ?? "Internal server error." }); + } + } + + @Patch(":id") + @ApiOperation({ summary: "update mock competency by id" }) + @ApiResponse({ status: HttpStatus.OK }) + async updateCompetency( + @Res() res, + @Param("id", ParseIntPipe) id: number, + @Body() updateCompetencyDto: UpdateCompetencyDto + ) { + try { + const competency = await this.competencyService.updatecompetencyById( + id, + updateCompetencyDto + ); + return res + .status(HttpStatus.OK) + .json({ + data: competency, + message: "Competency successfully updated.", + }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } + + @Delete(":id") + @ApiOperation({ summary: "delete mock competency by id" }) + @ApiResponse({ status: HttpStatus.OK }) + async removeCompetency(@Res() res, @Param("id", ParseIntPipe) id: number) { + try { + const competency = await this.competencyService.removeCompetency(id); + return res + .status(HttpStatus.OK) + .json({ + data: competency, + message: "Competency successfully deleted.", + }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } + + @Post("addExistingCompetencyLevelToCompetency/:id") + @ApiOperation({ summary: "add competency level to competency" }) + @ApiResponse({ status: HttpStatus.OK }) + async addExistingCompetencyLevelToCompetency( + @Res() res, + @Param("id", ParseIntPipe) id: number, + @Body() competencyLevel: { competencyLevelId: number } + ) { + try { + const competency = + await this.competencyService.addExistingCompetencyLevelToCompetency( + id, + competencyLevel.competencyLevelId + ); + return res + .status(HttpStatus.OK) + .json({ + data: competency, + message: "Competency successfully deleted.", + }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } + + @Post("addNewCompetencyLevelToCompetency/:id") + @ApiOperation({ summary: "create and add competency level to competency" }) + @ApiResponse({ status: HttpStatus.OK }) + async addNewCompetencyLevelToCompetency( + @Res() res, + @Param("id", ParseIntPipe) id: number, + @Body() competencyLevel: CreateCompetencyLevelDto + ) { + try { + const competency = + await this.competencyService.addNewCompetencyLevelToCompetency( + id, + competencyLevel + ); + return res + .status(HttpStatus.OK) + .json({ + data: competency, + message: "Competency successfully deleted.", + }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } +} diff --git a/src/mockModules/mock-competency/mock-competency.module.ts b/src/mockModules/mock-competency/mock-competency.module.ts new file mode 100644 index 0000000..d0d9058 --- /dev/null +++ b/src/mockModules/mock-competency/mock-competency.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { MockCompetencyController } from './mock-competency.controller'; +import { MockCompetencyService } from './mock-competency.service'; +import { MockCompetencyLevelService } from '../mock-competency-level/mock-competency-level.service'; + +@Module({ + controllers: [MockCompetencyController], + providers: [MockCompetencyService, MockCompetencyLevelService] +}) +export class MockCompetencyModule {} diff --git a/src/mockModules/mock-competency/mock-competency.service.ts b/src/mockModules/mock-competency/mock-competency.service.ts new file mode 100644 index 0000000..fed8612 --- /dev/null +++ b/src/mockModules/mock-competency/mock-competency.service.ts @@ -0,0 +1,98 @@ +import { Injectable, NotFoundException } from "@nestjs/common"; +import { PrismaService } from "src/prisma/prisma.service"; +import { CreateCompetencyDto, UpdateCompetencyDto } from "./dto"; +import { MockCompetencyLevelService } from "../mock-competency-level/mock-competency-level.service"; +import { CreateCompetencyLevelDto } from "../mock-competency-level/dto"; + +@Injectable() +export class MockCompetencyService { + constructor( + private prisma: PrismaService, + private competencyLevelService: MockCompetencyLevelService + ) {} + public async createCompetency(createMockRoleDto: CreateCompetencyDto) { + return this.prisma.competency.create({ + data: createMockRoleDto, + }); + } + + public async findAllCompetencies() { + return this.prisma.competency.findMany(); + } + + public async findCompetencyById(id: number) { + const competency = await this.prisma.competency.findUnique({ + where: { + id, + }, + include: { + competencyLevels: true, + roles: true, + }, + }); + if (!competency) + throw new NotFoundException(`Competency with id #${id} not found`); + return competency; + } + + public async updatecompetencyById( + id: number, + updateCompetencyDto: UpdateCompetencyDto + ) { + return this.prisma.competency.update({ + where: { + id, + }, + data: updateCompetencyDto, + }); + } + + public async removeCompetency(id: number) { + return this.prisma.competency.delete({ + where: { + id, + }, + }); + } + + public async addExistingCompetencyLevelToCompetency( + competencyId: number, + competencyLevelId: number + ) { + const competency = await this.findCompetencyById(competencyId); + const competencyLevel = + await this.competencyLevelService.findCompetencyLevelById( + competencyLevelId + ); + const connection = await this.prisma.competencyToCompetencyLevel.create({ + data: { + competencyId: competency.id, + competencyLevelId: competencyLevel.id, + }, + include: { + competency: true, + competencyLevel: true, + }, + }); + return connection; + } + + public async addNewCompetencyLevelToCompetency( + competencyId: number, + createCompetencyLevel: CreateCompetencyLevelDto + ) { + const competency = await this.findCompetencyById(competencyId); + + const competencyLevel = + await this.competencyLevelService.createCompetencyLevel( + createCompetencyLevel + ); + + const connection = await this.addExistingCompetencyLevelToCompetency( + competency.id, + competencyLevel.id + ); + + return connection; + } +} diff --git a/src/mockModules/mock-level/mock-level.controller.ts b/src/mockModules/mock-level/mock-level.controller.ts index ff8c326..37906ee 100644 --- a/src/mockModules/mock-level/mock-level.controller.ts +++ b/src/mockModules/mock-level/mock-level.controller.ts @@ -16,7 +16,7 @@ import { UpdateMockLevelDto } from "./dto/update-mock-level.dto"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { ResponseMockLevelDto } from "./dto/response-mock-level.dto"; -@Controller("mockUserService/level") +@Controller("level") @ApiTags("mockUserService/level") export class MockLevelController { constructor(private readonly mockLevelService: MockLevelService) {} diff --git a/src/mockModules/mock-role/dto/create-mock-role.dto.ts b/src/mockModules/mock-role/dto/create-mock-role.dto.ts new file mode 100644 index 0000000..f21aab3 --- /dev/null +++ b/src/mockModules/mock-role/dto/create-mock-role.dto.ts @@ -0,0 +1,13 @@ + +import { IsNotEmpty, IsOptional, IsString } from "class-validator"; + +export class CreateMockRoleDto { + @IsString() + @IsNotEmpty() + name: string; + + @IsOptional() + @IsString() + @IsNotEmpty() + description?: string; +} \ No newline at end of file diff --git a/src/mockModules/mock-role/dto/index.ts b/src/mockModules/mock-role/dto/index.ts new file mode 100644 index 0000000..4f480ba --- /dev/null +++ b/src/mockModules/mock-role/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-mock-role.dto'; +export * from './response-mock-role.dto'; +export * from './update-mock-role.dto'; \ No newline at end of file diff --git a/src/mockModules/mock-role/dto/response-mock-role.dto.ts b/src/mockModules/mock-role/dto/response-mock-role.dto.ts new file mode 100644 index 0000000..1b50844 --- /dev/null +++ b/src/mockModules/mock-role/dto/response-mock-role.dto.ts @@ -0,0 +1,8 @@ +import { ResponseCompetencyDto } from "src/mockModules/mock-competency/dto"; + +export class ResponseMockRoleDto { + readonly id: number; + readonly name: string; + readonly description?: string; + readonly competencies?: ResponseCompetencyDto[] + } \ No newline at end of file diff --git a/src/mockModules/mock-role/dto/update-mock-role.dto.ts b/src/mockModules/mock-role/dto/update-mock-role.dto.ts new file mode 100644 index 0000000..8a75c76 --- /dev/null +++ b/src/mockModules/mock-role/dto/update-mock-role.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateMockRoleDto } from './create-mock-role.dto'; + +export class UpdateMockRoleDto extends PartialType(CreateMockRoleDto) {} \ No newline at end of file diff --git a/src/mockModules/mock-role/mock-role.controller.ts b/src/mockModules/mock-role/mock-role.controller.ts new file mode 100644 index 0000000..55693db --- /dev/null +++ b/src/mockModules/mock-role/mock-role.controller.ts @@ -0,0 +1,162 @@ +import { + Body, + Controller, + Delete, + Get, + HttpStatus, + Param, + ParseIntPipe, + Patch, + Post, + Res, +} from "@nestjs/common"; +import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { CreateMockRoleDto, ResponseMockRoleDto, UpdateMockRoleDto } from "./dto"; +import { MockRoleService } from "./mock-role.service"; +import { CreateCompetencyDto } from "../mock-competency/dto"; + +@Controller("role") +@ApiTags("mockUserService/role") +export class MockRoleController { + constructor(private roleService: MockRoleService) {} + + @Post() + @ApiOperation({ summary: "create new mock role" }) + @ApiResponse({ status: HttpStatus.CREATED, type: ResponseMockRoleDto }) + async createRole(@Res() res, @Body() createMockLevelDto: CreateMockRoleDto) { + try { + const role = await this.roleService.createRole(createMockLevelDto); + return res + .status(HttpStatus.OK) + .json({ data: role, message: "Role successfully created." }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } + + @Get() + @ApiOperation({ summary: "fetch all mock roles" }) + @ApiResponse({ + status: HttpStatus.CREATED, + type: ResponseMockRoleDto, + isArray: true, + }) + async getAllRoles(@Res() res) { + try { + const roles = await this.roleService.findAllRoles(); + return res + .status(HttpStatus.OK) + .json({ data: roles, message: "Successfully fetched all roles." }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } + + @Get(":id") + @ApiOperation({ summary: "get mock role by id" }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseMockRoleDto }) + async findOne(@Res() res, @Param("id", ParseIntPipe) id: number) { + try { + const role = await this.roleService.findRoleById(id); + return res + .status(HttpStatus.OK) + .json({ data: role, message: "Role successfully fetched." }); + } catch (error) { + return res + .status(error.response.statusCode ?? HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.response.message ?? "Internal server error." }); + } + } + + @Patch(":id") + @ApiOperation({ summary: "update mock role by id" }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseMockRoleDto }) + async update( + @Res() res, + @Param("id", ParseIntPipe) id: number, + @Body() updateMockRoleDto: UpdateMockRoleDto + ) { + try { + const roles = await this.roleService.updateRoleById( + id, + updateMockRoleDto + ); + return res + .status(HttpStatus.OK) + .json({ data: roles, message: "Role successfully updated." }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } + + @Delete(":id") + @ApiOperation({ summary: "delete mock role by id" }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseMockRoleDto }) + async remove(@Res() res, @Param("id", ParseIntPipe) id: number) { + try { + const roles = await this.roleService.removeRole(id); + return res + .status(HttpStatus.OK) + .json({ data: roles, message: "Role successfully deleted." }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } + + @Post("addExistingCompetencyToRole/:id") + @ApiOperation({ summary: "add competency to role" }) + @ApiResponse({ status: HttpStatus.CREATED, type: ResponseMockRoleDto }) + async addExistingCompetencyToRole( + @Res() res, + @Param("id", ParseIntPipe) id: number, + @Body() competency: { competencyId: number } + ) { + try { + const role = await this.roleService.addExistingCompetencyToRole( + id, + competency.competencyId + ); + return res + .status(HttpStatus.OK) + .json({ + data: role, + message: `Successfully added competency with id #${competency.competencyId} to Role with id #${id}.`, + }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.message }); + } + } + + @Post("addNewCompetencyToRole/:id") + @ApiOperation({ summary: "create and add competency to role" }) + @ApiResponse({ status: HttpStatus.CREATED }) + async addNewCompetencyToRole( + @Res() res, + @Param("id", ParseIntPipe) id: number, + @Body() competency: CreateCompetencyDto + ) { + try { + const connection = await this.roleService.addNewCompetencyToRole(id, competency); + return res + .status(HttpStatus.OK) + .json({ + data: connection, + message: `Successfully added competency with id #${connection.competency.id} to Role with id #${id}.`, + }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.message }); + } + } +} diff --git a/src/mockModules/mock-role/mock-role.module.ts b/src/mockModules/mock-role/mock-role.module.ts new file mode 100644 index 0000000..c037220 --- /dev/null +++ b/src/mockModules/mock-role/mock-role.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { MockRoleController } from './mock-role.controller'; +import { MockRoleService } from './mock-role.service'; +import { MockCompetencyService } from '../mock-competency/mock-competency.service'; +import { MockCompetencyLevelService } from '../mock-competency-level/mock-competency-level.service'; + +@Module({ + controllers: [MockRoleController], + providers: [MockRoleService, MockCompetencyService, MockCompetencyLevelService] +}) +export class MockRoleModule {} diff --git a/src/mockModules/mock-role/mock-role.service.ts b/src/mockModules/mock-role/mock-role.service.ts new file mode 100644 index 0000000..ce9fbc1 --- /dev/null +++ b/src/mockModules/mock-role/mock-role.service.ts @@ -0,0 +1,94 @@ +import { Injectable, NotFoundException } from "@nestjs/common"; +import { PrismaService } from "src/prisma/prisma.service"; +import { CreateMockRoleDto, UpdateMockRoleDto } from "./dto"; +import { MockCompetencyService } from "../mock-competency/mock-competency.service"; +import { CreateCompetencyDto } from "../mock-competency/dto"; + +@Injectable() +export class MockRoleService { + constructor( + private prisma: PrismaService, + private competency: MockCompetencyService + ) {} + public async createRole(createMockRoleDto: CreateMockRoleDto) { + return this.prisma.role.create({ + data: createMockRoleDto, + }); + } + + public async findAllRoles() { + return this.prisma.role.findMany(); + } + + public async findRoleById(id: number) { + const role = await this.prisma.role.findUnique({ + where: { + id, + }, + include: { + competencies: { + include:{ + competency: true, + role: false + } + }, + }, + }); + if (!role) throw new NotFoundException(`Role with id #${id} not found`); + return role; + } + + public async updateRoleById( + id: number, + updateMockRoleDto: UpdateMockRoleDto + ) { + return this.prisma.role.update({ + where: { + id, + }, + data: updateMockRoleDto, + }); + } + + public async removeRole(id: number) { + return this.prisma.role.delete({ + where: { + id, + }, + }); + } + + public async addExistingCompetencyToRole( + roleId: number, + competencyId: number + ) { + const role = await this.findRoleById(roleId); + + const competency = await this.competency.findCompetencyById(competencyId); + + const connection = await this.prisma.roleToCompetency.create({ + data: { + roleId: role.id, + competencyId: competency.id, + }, + include:{ + role: true, + competency: true + } + }); + return connection; + } + + public async addNewCompetencyToRole( + roleId: number, + createCompetencyDto: CreateCompetencyDto + ) { + const role = await this.findRoleById(roleId); + + const competency = await this.competency.createCompetency(createCompetencyDto); + + const connection = await this.addExistingCompetencyToRole(role.id, competency.id); + + return connection; + } +} diff --git a/src/mockModules/mock-team/mock-team.controller.ts b/src/mockModules/mock-team/mock-team.controller.ts index ac06a53..6082ec1 100644 --- a/src/mockModules/mock-team/mock-team.controller.ts +++ b/src/mockModules/mock-team/mock-team.controller.ts @@ -15,7 +15,7 @@ import { CreateMockTeamDto } from "./dto/create-mock-team.dto"; import { UpdateMockTeamDto } from "./dto/update-mock-team.dto"; import { ApiTags } from "@nestjs/swagger"; -@Controller("mockUserService/team") +@Controller("team") @ApiTags("mockUserService/team") export class MockTeamController { constructor(private readonly mockTeamService: MockTeamService) {} diff --git a/src/mockModules/mock-user/mock-user.controller.ts b/src/mockModules/mock-user/mock-user.controller.ts index 7711a52..47ad6df 100644 --- a/src/mockModules/mock-user/mock-user.controller.ts +++ b/src/mockModules/mock-user/mock-user.controller.ts @@ -16,7 +16,7 @@ import { UpdateMockUserDto } from "./dto/update-mock-user.dto"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { ResponseMockUserDto } from "./dto/response-mock-user.dto"; -@Controller("mockUserService/user") +@Controller("user") @ApiTags("mockUserService/user") export class MockUserController { constructor(private readonly mockUserService: MockUserService) {} diff --git a/src/mockModules/mock.module.ts b/src/mockModules/mock.module.ts new file mode 100644 index 0000000..5c38ffd --- /dev/null +++ b/src/mockModules/mock.module.ts @@ -0,0 +1,28 @@ +import { Module } from "@nestjs/common"; +import { MockRoleModule } from "./mock-role/mock-role.module"; +import { MockCompetencyModule } from "./mock-competency/mock-competency.module"; +import { MockCompetencyLevelModule } from "./mock-competency-level/mock-competency-level.module"; +import { RouterModule } from "@nestjs/core"; +import { MockUserModule } from "./mock-user/mock-user.module"; +import { MockLevelModule } from "./mock-level/mock-level.module"; +import { MockTeamModule } from "./mock-team/mock-team.module"; + +@Module({ + imports: [ + MockRoleModule, + MockCompetencyModule, + MockCompetencyLevelModule, + MockUserModule, + MockLevelModule, + MockTeamModule, + RouterModule.register([ + { path: "mockUserService/", module: MockRoleModule }, + { path: "mockUserService/", module: MockCompetencyModule }, + { path: "mockUserService/", module: MockCompetencyLevelModule }, + { path: "mockUserService/", module: MockUserModule }, + { path: "mockUserService/", module: MockLevelModule }, + { path: "mockUserService/", module: MockTeamModule }, + ]), + ], +}) +export class MockModule {} diff --git a/src/prisma/prisma.module.ts b/src/prisma/prisma.module.ts index e6d1e9e..1edbf95 100644 --- a/src/prisma/prisma.module.ts +++ b/src/prisma/prisma.module.ts @@ -1,4 +1,9 @@ -import { Module } from '@nestjs/common'; +import { Global, Module } from "@nestjs/common"; +import { PrismaService } from "./prisma.service"; -@Module({}) +@Global() +@Module({ + providers: [PrismaService], + exports: [PrismaService], +}) export class PrismaModule {} diff --git a/src/prisma/prisma.service.ts b/src/prisma/prisma.service.ts index 5a993ff..aa0a5d6 100644 --- a/src/prisma/prisma.service.ts +++ b/src/prisma/prisma.service.ts @@ -1,5 +1,16 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaClient } from '@prisma/client'; +import { Injectable } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { PrismaClient } from "@prisma/client"; @Injectable() -export class PrismaService extends PrismaClient{} +export class PrismaService extends PrismaClient { + constructor(private config: ConfigService) { + super({ + datasources: { + db: { + url: config.get("DATABASE_URL"), + }, + }, + }); + } +} From 7bc5aeb0e0f14a96ed40a2449e7b0b1b7fe61aa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CVamshiBatta=E2=80=9D?= <“batta.v@esmagico.in”> Date: Tue, 10 Oct 2023 17:54:59 +0530 Subject: [PATCH 012/175] Added types for swagger docs. And improved error handling. --- prisma/schema.prisma | 8 +- .../dto/response-competency-level.dto.ts | 2 + .../mock-competency-level.controller.ts | 70 ++++++------ .../mock-competency-level.service.ts | 100 ++++++++++-------- .../dto/response-competency.dto.ts | 17 ++- .../mock-competency.controller.ts | 95 ++++++++--------- .../mock-competency.service.ts | 70 ++++++++---- .../mock-role/dto/response-mock-role.dto.ts | 15 ++- .../mock-role/mock-role.controller.ts | 10 +- .../mock-role/mock-role.service.ts | 61 ++++++++--- 10 files changed, 266 insertions(+), 182 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index b4e78ac..264ff6a 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -165,9 +165,9 @@ model Competency { } model RoleToCompetency { - role Role @relation(fields: [roleId], references: [id]) + role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) roleId Int - competency Competency @relation(fields: [competencyId], references: [id]) + competency Competency @relation(fields: [competencyId], references: [id], onDelete: Cascade) competencyId Int @@id([roleId, competencyId]) @@ -185,9 +185,9 @@ model CompetencyLevel { } model CompetencyToCompetencyLevel { - competency Competency @relation(fields: [competencyId], references: [id]) + competency Competency @relation(fields: [competencyId], references: [id], onDelete: Cascade) competencyId Int - competencyLevel CompetencyLevel @relation(fields: [competencyLevelId], references: [id]) + competencyLevel CompetencyLevel @relation(fields: [competencyLevelId], references: [id], onDelete: Cascade) competencyLevelId Int @@id([competencyId, competencyLevelId]) diff --git a/src/mockModules/mock-competency-level/dto/response-competency-level.dto.ts b/src/mockModules/mock-competency-level/dto/response-competency-level.dto.ts index 595d18e..27aab6f 100644 --- a/src/mockModules/mock-competency-level/dto/response-competency-level.dto.ts +++ b/src/mockModules/mock-competency-level/dto/response-competency-level.dto.ts @@ -1,6 +1,8 @@ +import { ResponseCompetencyDto } from "src/mockModules/mock-competency/dto"; export class ResponseCompetencyLevelDto { readonly id: number; readonly name: string; readonly description?: string; + readonly competencies?: ResponseCompetencyDto[] } \ No newline at end of file diff --git a/src/mockModules/mock-competency-level/mock-competency-level.controller.ts b/src/mockModules/mock-competency-level/mock-competency-level.controller.ts index 935a9e3..0be16be 100644 --- a/src/mockModules/mock-competency-level/mock-competency-level.controller.ts +++ b/src/mockModules/mock-competency-level/mock-competency-level.controller.ts @@ -12,7 +12,11 @@ import { } from "@nestjs/common"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { MockCompetencyLevelService } from "./mock-competency-level.service"; -import { CreateCompetencyLevelDto, UpdateCompetencyLevelDto } from "./dto"; +import { + CreateCompetencyLevelDto, + ResponseCompetencyLevelDto, + UpdateCompetencyLevelDto, +} from "./dto"; @Controller("competency-level") @ApiTags("mockUserService/competency-level") @@ -20,7 +24,7 @@ export class MockCompetencyLevelController { constructor(private competencyLevelService: MockCompetencyLevelService) {} @Post() @ApiOperation({ summary: "create new mock competency-level" }) - @ApiResponse({ status: HttpStatus.CREATED }) + @ApiResponse({ status: HttpStatus.CREATED, type: ResponseCompetencyLevelDto }) async createCompetencyLevel( @Res() res, @Body() createCompetencyLevelDto: CreateCompetencyLevelDto @@ -30,12 +34,10 @@ export class MockCompetencyLevelController { await this.competencyLevelService.createCompetencyLevel( createCompetencyLevelDto ); - return res - .status(HttpStatus.OK) - .json({ - data: competency, - message: "CompetencyLevel successfully created.", - }); + return res.status(HttpStatus.OK).json({ + data: competency, + message: "CompetencyLevel successfully created.", + }); } catch (error) { return res .status(HttpStatus.INTERNAL_SERVER_ERROR) @@ -45,17 +47,19 @@ export class MockCompetencyLevelController { @Get() @ApiOperation({ summary: "fetch all mock competency-levels" }) - @ApiResponse({ status: HttpStatus.OK }) + @ApiResponse({ + status: HttpStatus.OK, + type: ResponseCompetencyLevelDto, + isArray: true, + }) async getAllCompetencyLevels(@Res() res) { try { const competency = await this.competencyLevelService.findAllCompetencyLevels(); - return res - .status(HttpStatus.OK) - .json({ - data: competency, - message: "CompetencyLevels successfully fetched.", - }); + return res.status(HttpStatus.OK).json({ + data: competency, + message: "CompetencyLevels successfully fetched.", + }); } catch (error) { return res .status(HttpStatus.INTERNAL_SERVER_ERROR) @@ -65,7 +69,7 @@ export class MockCompetencyLevelController { @Get(":id") @ApiOperation({ summary: "get mock competency-level by id" }) - @ApiResponse({ status: HttpStatus.OK }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseCompetencyLevelDto }) async findOneCompetencyLevel( @Res() res, @Param("id", ParseIntPipe) id: number @@ -73,12 +77,10 @@ export class MockCompetencyLevelController { try { const competency = await this.competencyLevelService.findCompetencyLevelById(id); - return res - .status(HttpStatus.OK) - .json({ - data: competency, - message: "CompetencyLevel successfully fetched.", - }); + return res.status(HttpStatus.OK).json({ + data: competency, + message: "CompetencyLevel successfully fetched.", + }); } catch (error) { return res .status(error.response.statusCode ?? HttpStatus.INTERNAL_SERVER_ERROR) @@ -88,7 +90,7 @@ export class MockCompetencyLevelController { @Patch(":id") @ApiOperation({ summary: "update mock competency-level by id" }) - @ApiResponse({ status: HttpStatus.OK }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseCompetencyLevelDto }) async updateCompetencyLevel( @Res() res, @Param("id", ParseIntPipe) id: number, @@ -100,12 +102,10 @@ export class MockCompetencyLevelController { id, updateMockCompetencyLevelDto ); - return res - .status(HttpStatus.OK) - .json({ - data: competency, - message: "CompetencyLevels successfully updated.", - }); + return res.status(HttpStatus.OK).json({ + data: competency, + message: "CompetencyLevels successfully updated.", + }); } catch (error) { return res .status(HttpStatus.INTERNAL_SERVER_ERROR) @@ -115,7 +115,7 @@ export class MockCompetencyLevelController { @Delete(":id") @ApiOperation({ summary: "delete mock competency-level by id" }) - @ApiResponse({ status: HttpStatus.OK }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseCompetencyLevelDto }) async removeCompetencyLevel( @Res() res, @Param("id", ParseIntPipe) id: number @@ -123,12 +123,10 @@ export class MockCompetencyLevelController { try { const competency = await this.competencyLevelService.removeCompetencyLevel(id); - return res - .status(HttpStatus.OK) - .json({ - data: competency, - message: "CompetencyLevels successfully deleted.", - }); + return res.status(HttpStatus.OK).json({ + data: competency, + message: "CompetencyLevels successfully deleted.", + }); } catch (error) { return res .status(HttpStatus.INTERNAL_SERVER_ERROR) diff --git a/src/mockModules/mock-competency-level/mock-competency-level.service.ts b/src/mockModules/mock-competency-level/mock-competency-level.service.ts index f27b8bd..cc9f2dd 100644 --- a/src/mockModules/mock-competency-level/mock-competency-level.service.ts +++ b/src/mockModules/mock-competency-level/mock-competency-level.service.ts @@ -1,50 +1,60 @@ -import { Injectable, NotFoundException } from '@nestjs/common'; -import { PrismaService } from 'src/prisma/prisma.service'; -import { CreateCompetencyLevelDto, UpdateCompetencyLevelDto } from './dto'; +import { Injectable, NotFoundException } from "@nestjs/common"; +import { PrismaService } from "src/prisma/prisma.service"; +import { CreateCompetencyLevelDto, UpdateCompetencyLevelDto } from "./dto"; @Injectable() export class MockCompetencyLevelService { - constructor(private prisma: PrismaService) {} - public async createCompetencyLevel(createCompetencyLevelDto: CreateCompetencyLevelDto) { - return this.prisma.competencyLevel.create({ - data: createCompetencyLevelDto - }); - } - - public async findAllCompetencyLevels() { - return this.prisma.competencyLevel.findMany(); - } - - public async findCompetencyLevelById(id: number) { - const competencyLevel = await this.prisma.competencyLevel.findUnique({ - where: { - id, - }, - include: { - competencies: true, - }, - }); - if (!competencyLevel) throw new NotFoundException(`CompetencyLevel with id #${id} not found`); - return competencyLevel; - } - - public async updateCompetencyLevelById( - id: number, - updateCompetencyLevelDto: UpdateCompetencyLevelDto - ) { - return this.prisma.competencyLevel.update({ - where: { - id, - }, - data: updateCompetencyLevelDto, - }); - } - - public async removeCompetencyLevel(id: number) { - return this.prisma.competencyLevel.delete({ - where: { - id, + constructor(private prisma: PrismaService) {} + public async createCompetencyLevel( + createCompetencyLevelDto: CreateCompetencyLevelDto + ) { + return this.prisma.competencyLevel.create({ + data: createCompetencyLevelDto, + }); + } + + public async findAllCompetencyLevels() { + return this.prisma.competencyLevel.findMany(); + } + + public async findCompetencyLevelById(id: number) { + const competencyLevel = await this.prisma.competencyLevel.findUnique({ + where: { + id, + }, + select: { + id: true, + name: true, + description: true, + competencies: { + select: { + competency: true, }, - }); - } + }, + }, + }); + if (!competencyLevel) + throw new NotFoundException(`CompetencyLevel with id #${id} not found`); + return competencyLevel; + } + + public async updateCompetencyLevelById( + id: number, + updateCompetencyLevelDto: UpdateCompetencyLevelDto + ) { + return this.prisma.competencyLevel.update({ + where: { + id, + }, + data: updateCompetencyLevelDto, + }); + } + + public async removeCompetencyLevel(id: number) { + return this.prisma.competencyLevel.delete({ + where: { + id, + }, + }); + } } diff --git a/src/mockModules/mock-competency/dto/response-competency.dto.ts b/src/mockModules/mock-competency/dto/response-competency.dto.ts index ad826fd..624a9d5 100644 --- a/src/mockModules/mock-competency/dto/response-competency.dto.ts +++ b/src/mockModules/mock-competency/dto/response-competency.dto.ts @@ -1,6 +1,15 @@ +import { ResponseCompetencyLevelDto } from "src/mockModules/mock-competency-level/dto"; +import { ResponseMockRoleDto } from "src/mockModules/mock-role/dto"; export class ResponseCompetencyDto { - readonly id: number; - readonly name: string; - readonly description?: string; - } \ No newline at end of file + readonly id: number; + readonly name: string; + readonly description?: string; + readonly roles?: ResponseMockRoleDto[]; + readonly competencies?: ResponseCompetencyDto[]; +} + +export class ResponseAddCompetencyLevelToCompetency { + readonly competency: ResponseCompetencyDto; + readonly competencyLevel: ResponseCompetencyLevelDto; +} \ No newline at end of file diff --git a/src/mockModules/mock-competency/mock-competency.controller.ts b/src/mockModules/mock-competency/mock-competency.controller.ts index d1f3a4d..5884a66 100644 --- a/src/mockModules/mock-competency/mock-competency.controller.ts +++ b/src/mockModules/mock-competency/mock-competency.controller.ts @@ -11,7 +11,12 @@ import { Res, } from "@nestjs/common"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; -import { CreateCompetencyDto, UpdateCompetencyDto } from "./dto"; +import { + CreateCompetencyDto, + ResponseAddCompetencyLevelToCompetency, + ResponseCompetencyDto, + UpdateCompetencyDto, +} from "./dto"; import { MockCompetencyService } from "./mock-competency.service"; import { CreateCompetencyLevelDto } from "../mock-competency-level/dto"; @@ -22,7 +27,7 @@ export class MockCompetencyController { @Post() @ApiOperation({ summary: "create new mock competency" }) - @ApiResponse({ status: HttpStatus.CREATED }) + @ApiResponse({ status: HttpStatus.CREATED, type: ResponseCompetencyDto }) async createCompetency( @Res() res, @Body() createCompetencyDto: CreateCompetencyDto @@ -31,12 +36,10 @@ export class MockCompetencyController { const competency = await this.competencyService.createCompetency( createCompetencyDto ); - return res - .status(HttpStatus.OK) - .json({ - data: competency, - message: "Competency successfully created.", - }); + return res.status(HttpStatus.OK).json({ + data: competency, + message: "Competency successfully created.", + }); } catch (error) { return res .status(HttpStatus.INTERNAL_SERVER_ERROR) @@ -46,16 +49,18 @@ export class MockCompetencyController { @Get() @ApiOperation({ summary: "fetch all mock competencies" }) - @ApiResponse({ status: HttpStatus.CREATED }) + @ApiResponse({ + status: HttpStatus.CREATED, + type: ResponseCompetencyDto, + isArray: true, + }) async getAllCompetencies(@Res() res) { try { const competency = await this.competencyService.findAllCompetencies(); - return res - .status(HttpStatus.OK) - .json({ - data: competency, - message: "Competency successfully created.", - }); + return res.status(HttpStatus.OK).json({ + data: competency, + message: "Competencies successfully fetched.", + }); } catch (error) { return res .status(HttpStatus.INTERNAL_SERVER_ERROR) @@ -65,16 +70,14 @@ export class MockCompetencyController { @Get(":id") @ApiOperation({ summary: "get mock competency by id" }) - @ApiResponse({ status: HttpStatus.OK }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseCompetencyDto }) async findOneCompetency(@Res() res, @Param("id", ParseIntPipe) id: number) { try { const competency = await this.competencyService.findCompetencyById(id); - return res - .status(HttpStatus.OK) - .json({ - data: competency, - message: "Competency successfully fetched.", - }); + return res.status(HttpStatus.OK).json({ + data: competency, + message: "Competency successfully fetched.", + }); } catch (error) { return res .status(error.response.statusCode ?? HttpStatus.INTERNAL_SERVER_ERROR) @@ -84,7 +87,7 @@ export class MockCompetencyController { @Patch(":id") @ApiOperation({ summary: "update mock competency by id" }) - @ApiResponse({ status: HttpStatus.OK }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseCompetencyDto }) async updateCompetency( @Res() res, @Param("id", ParseIntPipe) id: number, @@ -95,12 +98,10 @@ export class MockCompetencyController { id, updateCompetencyDto ); - return res - .status(HttpStatus.OK) - .json({ - data: competency, - message: "Competency successfully updated.", - }); + return res.status(HttpStatus.OK).json({ + data: competency, + message: "Competency successfully updated.", + }); } catch (error) { return res .status(HttpStatus.INTERNAL_SERVER_ERROR) @@ -110,16 +111,14 @@ export class MockCompetencyController { @Delete(":id") @ApiOperation({ summary: "delete mock competency by id" }) - @ApiResponse({ status: HttpStatus.OK }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseCompetencyDto, }) async removeCompetency(@Res() res, @Param("id", ParseIntPipe) id: number) { try { const competency = await this.competencyService.removeCompetency(id); - return res - .status(HttpStatus.OK) - .json({ - data: competency, - message: "Competency successfully deleted.", - }); + return res.status(HttpStatus.OK).json({ + data: competency, + message: "Competency successfully deleted.", + }); } catch (error) { return res .status(HttpStatus.INTERNAL_SERVER_ERROR) @@ -129,7 +128,7 @@ export class MockCompetencyController { @Post("addExistingCompetencyLevelToCompetency/:id") @ApiOperation({ summary: "add competency level to competency" }) - @ApiResponse({ status: HttpStatus.OK }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseAddCompetencyLevelToCompetency }) async addExistingCompetencyLevelToCompetency( @Res() res, @Param("id", ParseIntPipe) id: number, @@ -141,12 +140,10 @@ export class MockCompetencyController { id, competencyLevel.competencyLevelId ); - return res - .status(HttpStatus.OK) - .json({ - data: competency, - message: "Competency successfully deleted.", - }); + return res.status(HttpStatus.OK).json({ + data: competency, + message: "Competency successfully deleted.", + }); } catch (error) { return res .status(HttpStatus.INTERNAL_SERVER_ERROR) @@ -156,7 +153,7 @@ export class MockCompetencyController { @Post("addNewCompetencyLevelToCompetency/:id") @ApiOperation({ summary: "create and add competency level to competency" }) - @ApiResponse({ status: HttpStatus.OK }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseCompetencyDto }) async addNewCompetencyLevelToCompetency( @Res() res, @Param("id", ParseIntPipe) id: number, @@ -168,12 +165,10 @@ export class MockCompetencyController { id, competencyLevel ); - return res - .status(HttpStatus.OK) - .json({ - data: competency, - message: "Competency successfully deleted.", - }); + return res.status(HttpStatus.OK).json({ + data: competency, + message: "Competency successfully deleted.", + }); } catch (error) { return res .status(HttpStatus.INTERNAL_SERVER_ERROR) diff --git a/src/mockModules/mock-competency/mock-competency.service.ts b/src/mockModules/mock-competency/mock-competency.service.ts index fed8612..34caf42 100644 --- a/src/mockModules/mock-competency/mock-competency.service.ts +++ b/src/mockModules/mock-competency/mock-competency.service.ts @@ -1,8 +1,13 @@ -import { Injectable, NotFoundException } from "@nestjs/common"; +import { + ForbiddenException, + Injectable, + NotFoundException, +} from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; import { CreateCompetencyDto, UpdateCompetencyDto } from "./dto"; import { MockCompetencyLevelService } from "../mock-competency-level/mock-competency-level.service"; import { CreateCompetencyLevelDto } from "../mock-competency-level/dto"; +import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library"; @Injectable() export class MockCompetencyService { @@ -25,9 +30,20 @@ export class MockCompetencyService { where: { id, }, - include: { - competencyLevels: true, - roles: true, + select: { + id: true, + name: true, + description: true, + roles: { + select: { + role: true, + }, + }, + competencyLevels: { + select: { + competencyLevel: true, + }, + }, }, }); if (!competency) @@ -69,7 +85,7 @@ export class MockCompetencyService { competencyId: competency.id, competencyLevelId: competencyLevel.id, }, - include: { + select: { competency: true, competencyLevel: true, }, @@ -81,18 +97,36 @@ export class MockCompetencyService { competencyId: number, createCompetencyLevel: CreateCompetencyLevelDto ) { - const competency = await this.findCompetencyById(competencyId); - - const competencyLevel = - await this.competencyLevelService.createCompetencyLevel( - createCompetencyLevel - ); - - const connection = await this.addExistingCompetencyLevelToCompetency( - competency.id, - competencyLevel.id - ); - - return connection; + return this.prisma.competency.update({ + where: { + id: competencyId, + }, + select: { + id: true, + name: true, + description: true, + competencyLevels: { + orderBy: { + competencyLevel: { + createdAt: "desc", + }, + }, + select: { + competencyLevel: true, + }, + }, + }, + data: { + competencyLevels: { + create: { + competencyLevel: { + create: { + ...createCompetencyLevel, + }, + }, + }, + }, + }, + }); } } diff --git a/src/mockModules/mock-role/dto/response-mock-role.dto.ts b/src/mockModules/mock-role/dto/response-mock-role.dto.ts index 1b50844..b3f4fd3 100644 --- a/src/mockModules/mock-role/dto/response-mock-role.dto.ts +++ b/src/mockModules/mock-role/dto/response-mock-role.dto.ts @@ -1,8 +1,13 @@ import { ResponseCompetencyDto } from "src/mockModules/mock-competency/dto"; export class ResponseMockRoleDto { - readonly id: number; - readonly name: string; - readonly description?: string; - readonly competencies?: ResponseCompetencyDto[] - } \ No newline at end of file + readonly id: number; + readonly name: string; + readonly description?: string; + readonly competencies?: ResponseCompetencyDto[]; +} + +export class ResponseAddCompetencyToRoleDto { + readonly role: ResponseMockRoleDto + readonly competency: ResponseCompetencyDto +} \ No newline at end of file diff --git a/src/mockModules/mock-role/mock-role.controller.ts b/src/mockModules/mock-role/mock-role.controller.ts index 55693db..e089c59 100644 --- a/src/mockModules/mock-role/mock-role.controller.ts +++ b/src/mockModules/mock-role/mock-role.controller.ts @@ -11,7 +11,7 @@ import { Res, } from "@nestjs/common"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; -import { CreateMockRoleDto, ResponseMockRoleDto, UpdateMockRoleDto } from "./dto"; +import { CreateMockRoleDto, ResponseAddCompetencyToRoleDto, ResponseMockRoleDto, UpdateMockRoleDto } from "./dto"; import { MockRoleService } from "./mock-role.service"; import { CreateCompetencyDto } from "../mock-competency/dto"; @@ -113,7 +113,7 @@ export class MockRoleController { @Post("addExistingCompetencyToRole/:id") @ApiOperation({ summary: "add competency to role" }) - @ApiResponse({ status: HttpStatus.CREATED, type: ResponseMockRoleDto }) + @ApiResponse({ status: HttpStatus.CREATED, type: ResponseAddCompetencyToRoleDto }) async addExistingCompetencyToRole( @Res() res, @Param("id", ParseIntPipe) id: number, @@ -139,7 +139,7 @@ export class MockRoleController { @Post("addNewCompetencyToRole/:id") @ApiOperation({ summary: "create and add competency to role" }) - @ApiResponse({ status: HttpStatus.CREATED }) + @ApiResponse({ status: HttpStatus.CREATED, type: ResponseMockRoleDto }) async addNewCompetencyToRole( @Res() res, @Param("id", ParseIntPipe) id: number, @@ -151,12 +151,12 @@ export class MockRoleController { .status(HttpStatus.OK) .json({ data: connection, - message: `Successfully added competency with id #${connection.competency.id} to Role with id #${id}.`, + message: `Successfully added competency with id # to Role with id #${id}.`, }); } catch (error) { return res .status(HttpStatus.INTERNAL_SERVER_ERROR) - .json({ message: error.message }); + .json({ message: error.meta.cause }); } } } diff --git a/src/mockModules/mock-role/mock-role.service.ts b/src/mockModules/mock-role/mock-role.service.ts index ce9fbc1..9c1ce19 100644 --- a/src/mockModules/mock-role/mock-role.service.ts +++ b/src/mockModules/mock-role/mock-role.service.ts @@ -1,8 +1,13 @@ -import { Injectable, NotFoundException } from "@nestjs/common"; +import { + ForbiddenException, + Injectable, + NotFoundException, +} from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; import { CreateMockRoleDto, UpdateMockRoleDto } from "./dto"; import { MockCompetencyService } from "../mock-competency/mock-competency.service"; import { CreateCompetencyDto } from "../mock-competency/dto"; +import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library"; @Injectable() export class MockRoleService { @@ -25,12 +30,14 @@ export class MockRoleService { where: { id, }, - include: { + select: { + id: true, + name: true, + description: true, competencies: { - include:{ + select: { competency: true, - role: false - } + }, }, }, }); @@ -71,10 +78,10 @@ export class MockRoleService { roleId: role.id, competencyId: competency.id, }, - include:{ + select: { role: true, - competency: true - } + competency: true, + }, }); return connection; } @@ -83,12 +90,36 @@ export class MockRoleService { roleId: number, createCompetencyDto: CreateCompetencyDto ) { - const role = await this.findRoleById(roleId); - - const competency = await this.competency.createCompetency(createCompetencyDto); - - const connection = await this.addExistingCompetencyToRole(role.id, competency.id); - - return connection; + return this.prisma.role.update({ + where: { + id: roleId, + }, + select: { + id: true, + name: true, + description: true, + competencies: { + orderBy: { + competency: { + createdAt: "desc", + }, + }, + select: { + competency: true, + }, + }, + }, + data: { + competencies: { + create: { + competency: { + create: { + ...createCompetencyDto, + }, + }, + }, + }, + }, + }); } } From 4ff81fd09c91169b242c56b826c4b09ed9133363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CVamshiBatta=E2=80=9D?= <“batta.v@esmagico.in”> Date: Thu, 12 Oct 2023 10:42:00 +0530 Subject: [PATCH 013/175] Added APIs to assign a role to "CONSUMER" and get all FRAC data for a user. Changed the API tag and rout from mockUserService to mockFracService. --- prisma/schema.prisma | 2 +- src/app.module.ts | 4 +- .../mock-competency-level.controller.ts | 2 +- .../mock-competency.controller.ts | 2 +- .../mock-level/mock-level.controller.ts | 2 +- .../mock-role/mock-role.controller.ts | 2 +- .../mock-team/mock-team.controller.ts | 2 +- .../mock-user/dto/create-mock-user.dto.ts | 2 + .../mock-user/dto/response-mock-user.dto.ts | 16 ++- .../mock-user/mock-user.controller.ts | 38 ++++++- .../mock-user/mock-user.service.ts | 100 +++++++++++++++++- src/mockModules/mock.module.ts | 14 +-- 12 files changed, 163 insertions(+), 23 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 264ff6a..d8ad831 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -117,7 +117,7 @@ model User { profilePicture String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - roles Role[] + Roles Role[] Level Level? @relation(fields: [levelId], references: [id]) levelId Int? Team Team? @relation(fields: [teamId], references: [id]) diff --git a/src/app.module.ts b/src/app.module.ts index 5f0804a..23bdb5c 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -4,7 +4,7 @@ import { AppController } from "./app.controller"; import { AppService } from "./app.service"; import { PrismaModule } from "./prisma/prisma.module"; import { PrismaService } from "./prisma/prisma.service"; -import { MockModule } from "./mockModules/mock.module"; +import { MockFracModule } from "./mockModules/mock.module"; @Module({ imports: [ @@ -12,7 +12,7 @@ import { MockModule } from "./mockModules/mock.module"; isGlobal: true, }), PrismaModule, - MockModule + MockFracModule ], controllers: [AppController], providers: [AppService, PrismaService], diff --git a/src/mockModules/mock-competency-level/mock-competency-level.controller.ts b/src/mockModules/mock-competency-level/mock-competency-level.controller.ts index 0be16be..0be1f0f 100644 --- a/src/mockModules/mock-competency-level/mock-competency-level.controller.ts +++ b/src/mockModules/mock-competency-level/mock-competency-level.controller.ts @@ -19,7 +19,7 @@ import { } from "./dto"; @Controller("competency-level") -@ApiTags("mockUserService/competency-level") +@ApiTags("mockFracService/competency-level") export class MockCompetencyLevelController { constructor(private competencyLevelService: MockCompetencyLevelService) {} @Post() diff --git a/src/mockModules/mock-competency/mock-competency.controller.ts b/src/mockModules/mock-competency/mock-competency.controller.ts index 5884a66..6d0b06f 100644 --- a/src/mockModules/mock-competency/mock-competency.controller.ts +++ b/src/mockModules/mock-competency/mock-competency.controller.ts @@ -21,7 +21,7 @@ import { MockCompetencyService } from "./mock-competency.service"; import { CreateCompetencyLevelDto } from "../mock-competency-level/dto"; @Controller("competency") -@ApiTags("mockUserService/competency") +@ApiTags("mockFracService/competency") export class MockCompetencyController { constructor(private competencyService: MockCompetencyService) {} diff --git a/src/mockModules/mock-level/mock-level.controller.ts b/src/mockModules/mock-level/mock-level.controller.ts index 37906ee..6bbf455 100644 --- a/src/mockModules/mock-level/mock-level.controller.ts +++ b/src/mockModules/mock-level/mock-level.controller.ts @@ -17,7 +17,7 @@ import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { ResponseMockLevelDto } from "./dto/response-mock-level.dto"; @Controller("level") -@ApiTags("mockUserService/level") +@ApiTags("mockFracService/level") export class MockLevelController { constructor(private readonly mockLevelService: MockLevelService) {} diff --git a/src/mockModules/mock-role/mock-role.controller.ts b/src/mockModules/mock-role/mock-role.controller.ts index e089c59..070bbe0 100644 --- a/src/mockModules/mock-role/mock-role.controller.ts +++ b/src/mockModules/mock-role/mock-role.controller.ts @@ -16,7 +16,7 @@ import { MockRoleService } from "./mock-role.service"; import { CreateCompetencyDto } from "../mock-competency/dto"; @Controller("role") -@ApiTags("mockUserService/role") +@ApiTags("mockFracService/role") export class MockRoleController { constructor(private roleService: MockRoleService) {} diff --git a/src/mockModules/mock-team/mock-team.controller.ts b/src/mockModules/mock-team/mock-team.controller.ts index 6082ec1..66fc15e 100644 --- a/src/mockModules/mock-team/mock-team.controller.ts +++ b/src/mockModules/mock-team/mock-team.controller.ts @@ -16,7 +16,7 @@ import { UpdateMockTeamDto } from "./dto/update-mock-team.dto"; import { ApiTags } from "@nestjs/swagger"; @Controller("team") -@ApiTags("mockUserService/team") +@ApiTags("mockFracService/team") export class MockTeamController { constructor(private readonly mockTeamService: MockTeamService) {} diff --git a/src/mockModules/mock-user/dto/create-mock-user.dto.ts b/src/mockModules/mock-user/dto/create-mock-user.dto.ts index 8bd44d3..525e3a4 100644 --- a/src/mockModules/mock-user/dto/create-mock-user.dto.ts +++ b/src/mockModules/mock-user/dto/create-mock-user.dto.ts @@ -32,10 +32,12 @@ export class CreateMockUserDto { @IsString() profilePicture: string; + @IsOptional() @IsNotEmpty() @IsNumber() levelId: number; + @IsOptional() @IsNotEmpty() @IsNumber() teamId: number; diff --git a/src/mockModules/mock-user/dto/response-mock-user.dto.ts b/src/mockModules/mock-user/dto/response-mock-user.dto.ts index 7fe3e65..529afa1 100644 --- a/src/mockModules/mock-user/dto/response-mock-user.dto.ts +++ b/src/mockModules/mock-user/dto/response-mock-user.dto.ts @@ -1,12 +1,18 @@ +import { ResponseCompetencyLevelDto } from "src/mockModules/mock-competency-level/dto"; +import { ResponseMockRoleDto } from "src/mockModules/mock-role/dto"; +import { ResponseMockTeamDto } from "src/mockModules/mock-team/dto/response-mock-team.dto"; + export class ResponseMockUserDto { readonly id: number; readonly email: string; readonly role: string; readonly userName: string; - readonly password: string; readonly profilePicture?: string; - readonly createdAt: Date; - readonly updatedAt: Date; - readonly levelId: number; - readonly teamId: number; + readonly createdAt?: Date; + readonly updatedAt?: Date; + readonly levelId?: number; + readonly teamId?: number; + readonly Level?: ResponseCompetencyLevelDto; + readonly Team?: ResponseMockTeamDto; + readonly Roles?: ResponseMockRoleDto[]; } diff --git a/src/mockModules/mock-user/mock-user.controller.ts b/src/mockModules/mock-user/mock-user.controller.ts index 47ad6df..1b91641 100644 --- a/src/mockModules/mock-user/mock-user.controller.ts +++ b/src/mockModules/mock-user/mock-user.controller.ts @@ -17,7 +17,7 @@ import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { ResponseMockUserDto } from "./dto/response-mock-user.dto"; @Controller("user") -@ApiTags("mockUserService/user") +@ApiTags("mockFracService/user") export class MockUserController { constructor(private readonly mockUserService: MockUserService) {} @@ -108,4 +108,40 @@ export class MockUserController { .json({ message: error.meta.cause }); } } + + @Patch("addRoleToUser/:id") + @ApiOperation({ summary: "add role to user by id" }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseMockUserDto }) + async addRoleToUser( + @Param("id", ParseIntPipe) id: number, + @Body() { roleId }: any, + @Res() res + ) { + try { + const user = await this.mockUserService.addRoleToUser(id, roleId); + return res + .status(HttpStatus.OK) + .json({ data: user, message: "Successfully done" }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } + + @Get("getcompetencyData/:id") + @ApiOperation({ summary: "get mock user's competency data by id" }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseMockUserDto }) + async fetchAllCompetencyDataForUserById(@Param("id", ParseIntPipe) id: number, @Res() res) { + try { + const user = await this.mockUserService.fetchAllCompetencyDataForUserById(id); + return res + .status(HttpStatus.OK) + .json({ data: user, message: "Successfully done" }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.message }); + } + } } diff --git a/src/mockModules/mock-user/mock-user.service.ts b/src/mockModules/mock-user/mock-user.service.ts index 45fffac..db95464 100644 --- a/src/mockModules/mock-user/mock-user.service.ts +++ b/src/mockModules/mock-user/mock-user.service.ts @@ -8,11 +8,20 @@ export class MockUserService { constructor(private prisma: PrismaService) {} public async create(createMockUserDto: CreateMockUserDto) { - return await this.prisma.user.create({ data: createMockUserDto }); + return await this.prisma.user.create({ + data: createMockUserDto, + select:{ + ...this.userSelectObj, + } + }); } public async findAll() { - return await this.prisma.user.findMany(); + return await this.prisma.user.findMany({ + select: { + ...this.userSelectObj, + }, + }); } public async findOne(id: number) { @@ -20,6 +29,9 @@ export class MockUserService { where: { id, }, + select: { + ...this.userSelectObj, + }, }); if (!user) throw new NotFoundException(`User with id #${id} not found`); return user; @@ -30,6 +42,9 @@ export class MockUserService { where: { id, }, + select: { + ...this.userSelectObj, + }, data: updateMockUserDto, }); } @@ -39,6 +54,87 @@ export class MockUserService { where: { id, }, + select: { + ...this.userSelectObj, + }, + }); + } + + public async addRoleToUser(userId: number, roleId: number) { + return await this.prisma.user.update({ + where: { + id: userId, + }, + select: { + ...this.userSelectObj, + }, + data: { + Roles: { + connect: { id: roleId }, + }, + }, + }); + } + + public async fetchAllCompetencyDataForUserById(id: number) { + return await this.prisma.user.findUnique({ + where: { + id, + }, + select: { + id: true, + userName: true, + role: true, + Level: { + select: { + id: true, + label: true, + }, + }, + Team: { + select: { + id: true, + name: true, + }, + }, + Roles: { + select: { + id: true, + name: true, + competencies: { + select: { + competency: { + select: { + id: true, + name: true, + competencyLevels: { + select: { + competencyLevel: { + select: { + id: true, + name: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, }); } + userSelectObj = { + id: true, + email: true, + role: true, + userName: true, + profilePicture: true, + createdAt: true, + updatedAt: true, + levelId: true, + teamId: true, + }; } diff --git a/src/mockModules/mock.module.ts b/src/mockModules/mock.module.ts index 5c38ffd..6c383da 100644 --- a/src/mockModules/mock.module.ts +++ b/src/mockModules/mock.module.ts @@ -16,13 +16,13 @@ import { MockTeamModule } from "./mock-team/mock-team.module"; MockLevelModule, MockTeamModule, RouterModule.register([ - { path: "mockUserService/", module: MockRoleModule }, - { path: "mockUserService/", module: MockCompetencyModule }, - { path: "mockUserService/", module: MockCompetencyLevelModule }, - { path: "mockUserService/", module: MockUserModule }, - { path: "mockUserService/", module: MockLevelModule }, - { path: "mockUserService/", module: MockTeamModule }, + { path: "mockFracService/", module: MockRoleModule }, + { path: "mockFracService/", module: MockCompetencyModule }, + { path: "mockFracService/", module: MockCompetencyLevelModule }, + { path: "mockFracService/", module: MockUserModule }, + { path: "mockFracService/", module: MockLevelModule }, + { path: "mockFracService/", module: MockTeamModule }, ]), ], }) -export class MockModule {} +export class MockFracModule {} From c9477bfd9e089b5ada62b2d3ea3781535e54eacc Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Thu, 12 Oct 2023 15:52:53 +0530 Subject: [PATCH 014/175] Resolved review changes --- src/mockModules/mock-level/mock-level.module.ts | 2 +- src/mockModules/mock-team/mock-team.module.ts | 2 +- src/mockModules/mock-user/mock-user.module.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mockModules/mock-level/mock-level.module.ts b/src/mockModules/mock-level/mock-level.module.ts index a467147..9bcb897 100644 --- a/src/mockModules/mock-level/mock-level.module.ts +++ b/src/mockModules/mock-level/mock-level.module.ts @@ -5,6 +5,6 @@ import { PrismaService } from "src/prisma/prisma.service"; @Module({ controllers: [MockLevelController], - providers: [MockLevelService, PrismaService], + providers: [MockLevelService], }) export class MockLevelModule {} diff --git a/src/mockModules/mock-team/mock-team.module.ts b/src/mockModules/mock-team/mock-team.module.ts index f328598..3375439 100644 --- a/src/mockModules/mock-team/mock-team.module.ts +++ b/src/mockModules/mock-team/mock-team.module.ts @@ -5,6 +5,6 @@ import { PrismaService } from "src/prisma/prisma.service"; @Module({ controllers: [MockTeamController], - providers: [MockTeamService, PrismaService], + providers: [MockTeamService], }) export class MockTeamModule {} diff --git a/src/mockModules/mock-user/mock-user.module.ts b/src/mockModules/mock-user/mock-user.module.ts index 8cf03ff..d4f9c58 100644 --- a/src/mockModules/mock-user/mock-user.module.ts +++ b/src/mockModules/mock-user/mock-user.module.ts @@ -5,7 +5,7 @@ import { PrismaService } from "src/prisma/prisma.service"; @Module({ controllers: [MockUserController], - providers: [MockUserService, PrismaService], + providers: [MockUserService], exports: [MockUserService], }) export class MockUserModule {} From 37fe8b394ea981666c8542ce8d1589dd010556eb Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Tue, 10 Oct 2023 17:09:43 +0530 Subject: [PATCH 015/175] Response tracer APIs created --- src/app.module.ts | 4 +- .../dto/create-response-tracker.dto.ts | 36 ++ src/response-tracker/dto/index.ts | 5 + .../dto/response-response-tracker.dto.ts | 22 ++ .../dto/update-response-tracker.dto.ts | 4 + .../response-tracker.controller.spec.ts | 20 ++ .../response-tracker.controller.ts | 316 ++++++++++++++++++ .../response-tracker.module.ts | 11 + .../response-tracker.service.spec.ts | 18 + .../response-tracker.service.ts | 78 +++++ 10 files changed, 513 insertions(+), 1 deletion(-) create mode 100644 src/response-tracker/dto/create-response-tracker.dto.ts create mode 100644 src/response-tracker/dto/index.ts create mode 100644 src/response-tracker/dto/response-response-tracker.dto.ts create mode 100644 src/response-tracker/dto/update-response-tracker.dto.ts create mode 100644 src/response-tracker/response-tracker.controller.spec.ts create mode 100644 src/response-tracker/response-tracker.controller.ts create mode 100644 src/response-tracker/response-tracker.module.ts create mode 100644 src/response-tracker/response-tracker.service.spec.ts create mode 100644 src/response-tracker/response-tracker.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 23bdb5c..043799c 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -4,6 +4,7 @@ import { AppController } from "./app.controller"; import { AppService } from "./app.service"; import { PrismaModule } from "./prisma/prisma.module"; import { PrismaService } from "./prisma/prisma.service"; +import { ResponseTrackerModule } from "./response-tracker/response-tracker.module"; import { MockFracModule } from "./mockModules/mock.module"; @Module({ @@ -12,7 +13,8 @@ import { MockFracModule } from "./mockModules/mock.module"; isGlobal: true, }), PrismaModule, - MockFracModule + ResponseTrackerModule, + MockFracModule, ], controllers: [AppController], providers: [AppService, PrismaService], diff --git a/src/response-tracker/dto/create-response-tracker.dto.ts b/src/response-tracker/dto/create-response-tracker.dto.ts new file mode 100644 index 0000000..8f0eb17 --- /dev/null +++ b/src/response-tracker/dto/create-response-tracker.dto.ts @@ -0,0 +1,36 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { ResponseTrackerStatusEnum } from "@prisma/client"; +import { + IsInt, + IsNotEmpty, + IsOptional, + IsObject, + IsEnum, +} from "class-validator"; + +export class CreateResponseTrackerDto { + @ApiProperty({ type: "integer", example: 1 }) + @IsNotEmpty() + @IsInt() + surveyFormId: number; + + @ApiProperty({ type: "integer", example: 2 }) + @IsNotEmpty() + @IsInt() + assesseeId: number; + + @ApiProperty({ type: "integer", example: 3 }) + @IsNotEmpty() + @IsInt() + assessorId: number; + + @ApiProperty({ type: "object", example: { question: true || false } }) + @IsOptional() + @IsObject() + responseJson?: Record; + + @ApiProperty({ enum: ResponseTrackerStatusEnum, example: "Pending" }) + @IsNotEmpty() + @IsEnum(ResponseTrackerStatusEnum) + status: ResponseTrackerStatusEnum; +} diff --git a/src/response-tracker/dto/index.ts b/src/response-tracker/dto/index.ts new file mode 100644 index 0000000..0686f6c --- /dev/null +++ b/src/response-tracker/dto/index.ts @@ -0,0 +1,5 @@ +import { from } from "rxjs"; + +export * from "./create-response-tracker.dto"; +export * from "./response-response-tracker.dto"; +export * from "./update-response-tracker.dto"; diff --git a/src/response-tracker/dto/response-response-tracker.dto.ts b/src/response-tracker/dto/response-response-tracker.dto.ts new file mode 100644 index 0000000..c813949 --- /dev/null +++ b/src/response-tracker/dto/response-response-tracker.dto.ts @@ -0,0 +1,22 @@ +import { ResponseTrackerStatusEnum } from "@prisma/client"; + +export class ResponseTracker { + readonly id: number; + readonly assesseeId: number; + readonly surveyFormId: number; + readonly assessorId: number; + readonly responseJson?: Record; + readonly status: ResponseTrackerStatusEnum; +} + +export class ResponseTrackerDtoResponse { + data?: ResponseTracker; + message: string; + statusCode?: number; +} + +export class ResponseTrackerDtoMultipleResponse { + data?: ResponseTracker[]; + message: string; + statusCode?: number; +} diff --git a/src/response-tracker/dto/update-response-tracker.dto.ts b/src/response-tracker/dto/update-response-tracker.dto.ts new file mode 100644 index 0000000..b34cde1 --- /dev/null +++ b/src/response-tracker/dto/update-response-tracker.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateResponseTrackerDto } from './create-response-tracker.dto'; + +export class UpdateResponseTrackerDto extends PartialType(CreateResponseTrackerDto) {} diff --git a/src/response-tracker/response-tracker.controller.spec.ts b/src/response-tracker/response-tracker.controller.spec.ts new file mode 100644 index 0000000..9a353ef --- /dev/null +++ b/src/response-tracker/response-tracker.controller.spec.ts @@ -0,0 +1,20 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ResponseTrackerController } from './response-tracker.controller'; +import { ResponseTrackerService } from './response-tracker.service'; + +describe('ResponseTrackerController', () => { + let controller: ResponseTrackerController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [ResponseTrackerController], + providers: [ResponseTrackerService], + }).compile(); + + controller = module.get(ResponseTrackerController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/response-tracker/response-tracker.controller.ts b/src/response-tracker/response-tracker.controller.ts new file mode 100644 index 0000000..94089f2 --- /dev/null +++ b/src/response-tracker/response-tracker.controller.ts @@ -0,0 +1,316 @@ +import { + Controller, + Get, + Post, + Body, + Patch, + Param, + Delete, + Res, + HttpStatus, + Logger, + ParseIntPipe, +} from "@nestjs/common"; +import { ResponseTrackerService } from "./response-tracker.service"; +import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { + CreateResponseTrackerDto, + ResponseTrackerDtoMultipleResponse, + ResponseTrackerDtoResponse, + UpdateResponseTrackerDto, +} from "./dto"; +import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; + +@Controller("response-tracker") +@ApiTags("response-tracker") +export class ResponseTrackerController { + private readonly logger = new Logger(ResponseTrackerController.name); + constructor( + private readonly responseTrackerService: ResponseTrackerService + ) {} + + @Post() + @ApiOperation({ summary: "create new response tracer" }) + @ApiResponse({ status: HttpStatus.CREATED, type: ResponseTrackerDtoResponse }) + async create( + @Res() res, + @Body() createResponseTrackerDto: CreateResponseTrackerDto + ): Promise { + try { + this.logger.log(`Initiated creating response tracer`); + + const response = await this.responseTrackerService.create( + createResponseTrackerDto + ); + + return res.status(HttpStatus.CREATED).json({ + data: response, + message: "Successfully created response tracer", + }); + } catch (error) { + this.logger.error(`Failed to create response tracer`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed to create response tracer`, + }); + } + } + + @Get() + @ApiOperation({ summary: "get all response tracers" }) + @ApiResponse({ + status: HttpStatus.OK, + type: ResponseTrackerDtoMultipleResponse, + }) + async findAll(@Res() res): Promise { + try { + this.logger.log(`Initiated fetching all response tracers`); + + const responses = await this.responseTrackerService.findAll(); + + return res.status(HttpStatus.OK).json({ + data: responses, + message: `Successfully fetched all response tracers`, + }); + } catch (error) { + this.logger.error(`Failed to fetch all response tracers`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed to fetch all response tracers`, + }); + } + } + + @Get(":id") + @ApiOperation({ summary: "get response tracer by id" }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseTrackerDtoResponse }) + async findOne( + @Res() res, + @Param("id", ParseIntPipe) id: number + ): Promise { + try { + this.logger.log(`Initiated fetching response tracer with id#${id}`); + + const response = await this.responseTrackerService.findOne(id); + + return res.status(HttpStatus.OK).json({ + data: response, + message: `Successfully fetched response tracer for id #${id}`, + }); + } catch (error) { + this.logger.error( + `Failed to fetch response tracer with id #${id}`, + error + ); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: + errorMessage || `Failed to fetch response tracer for id #${id}`, + }); + } + } + + @Get("survey-form/:surveyFormId") + @ApiOperation({ summary: "get response tracers by surveyFormId" }) + @ApiResponse({ + status: HttpStatus.OK, + type: ResponseTrackerDtoMultipleResponse, + }) + async findBySurveyFormId( + @Res() res, + @Param("surveyFormId", ParseIntPipe) surveyFormId: number + ): Promise { + try { + this.logger.log( + `Initiated fetching response tracer with surveyFormId#${surveyFormId}` + ); + + const response = await this.responseTrackerService.findBySurveyFormId( + surveyFormId + ); + + return res.status(HttpStatus.OK).json({ + data: response, + message: `Successfully fetched response tracer for surveyFormId #${surveyFormId}`, + }); + } catch (error) { + this.logger.error( + `Failed to fetch response tracer with surveyFormId #${surveyFormId}`, + error + ); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: + errorMessage || + `Failed to fetch response tracer for surveyFormId #${surveyFormId}`, + }); + } + } + + @Get("assessor/:assessorId") + @ApiOperation({ summary: "get response tracer by assessorId" }) + @ApiResponse({ + status: HttpStatus.OK, + type: ResponseTrackerDtoMultipleResponse, + }) + async findByAssessorId( + @Res() res, + @Param("assessorId", ParseIntPipe) assessorId: number + ): Promise { + try { + this.logger.log( + `Initiated fetching response tracer with assessorId#${assessorId}` + ); + + const response = await this.responseTrackerService.findByAssessorId( + assessorId + ); + + return res.status(HttpStatus.OK).json({ + data: response, + message: `Successfully fetched response tracer for assessorId #${assessorId}`, + }); + } catch (error) { + this.logger.error( + `Failed to fetch response tracer with assessorId #${assessorId}`, + error + ); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: + errorMessage || + `Failed to fetch response tracer for assessorId #${assessorId}`, + }); + } + } + + @Get("assessee/:assesseeId") + @ApiOperation({ summary: "get response tracer by assesseeId" }) + @ApiResponse({ + status: HttpStatus.OK, + type: ResponseTrackerDtoMultipleResponse, + }) + async findByAssesseeId( + @Res() res, + @Param("assesseeId", ParseIntPipe) assesseeId: number + ): Promise { + try { + this.logger.log( + `Initiated fetching response tracer with assesseeId#${assesseeId}` + ); + + const response = await this.responseTrackerService.findByAssesseeId( + assesseeId + ); + + return res.status(HttpStatus.OK).json({ + data: response, + message: `Successfully fetched response tracer for assesseeId #${assesseeId}`, + }); + } catch (error) { + this.logger.error( + `Failed to fetch response tracer with assesseeId #${assesseeId}`, + error + ); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: + errorMessage || + `Failed to fetch response tracer for assesseeId #${assesseeId}`, + }); + } + } + + @Patch(":id") + @ApiOperation({ summary: "update response tracer by id" }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseTrackerDtoResponse }) + async update( + @Res() res, + @Param("id", ParseIntPipe) id: number, + @Body() updateResponseTrackerDto: UpdateResponseTrackerDto + ): Promise { + try { + this.logger.log(`Initiated updating response tracer with id #${id}`); + + const updatedResponse = await this.responseTrackerService.update( + id, + updateResponseTrackerDto + ); + + return res.status(HttpStatus.OK).json({ + data: updatedResponse, + message: `Successfully updated response tracer with id ${id}`, + }); + } catch (error) { + this.logger.error( + `Failed to update response tracer with id #${id}`, + error + ); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: + errorMessage || `Failed to update response tracer with id #${id}`, + }); + } + } + + @Delete(":id") + @ApiOperation({ summary: "delete response tracer by id" }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseTrackerDtoResponse }) + async remove( + @Res() res, + @Param("id", ParseIntPipe) id: number + ): Promise { + try { + this.logger.log(`Initiated deleting response tracer with id #${id}`); + + await this.responseTrackerService.remove(id); + + return res.status(HttpStatus.OK).json({ + message: `Successfully deleted response tracer with id ${id}`, + }); + } catch (error) { + this.logger.error( + `Failed to delete response tracer with id #${id}`, + error + ); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: + errorMessage || `Failed to delete response tracer with id #${id}`, + }); + } + } +} diff --git a/src/response-tracker/response-tracker.module.ts b/src/response-tracker/response-tracker.module.ts new file mode 100644 index 0000000..2e79bdf --- /dev/null +++ b/src/response-tracker/response-tracker.module.ts @@ -0,0 +1,11 @@ +import { Module } from "@nestjs/common"; +import { ResponseTrackerService } from "./response-tracker.service"; +import { ResponseTrackerController } from "./response-tracker.controller"; +import { PrismaService } from "src/prisma/prisma.service"; + +@Module({ + controllers: [ResponseTrackerController], + providers: [ResponseTrackerService, PrismaService], + exports: [ResponseTrackerService], +}) +export class ResponseTrackerModule {} diff --git a/src/response-tracker/response-tracker.service.spec.ts b/src/response-tracker/response-tracker.service.spec.ts new file mode 100644 index 0000000..bc48094 --- /dev/null +++ b/src/response-tracker/response-tracker.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ResponseTrackerService } from './response-tracker.service'; + +describe('ResponseTrackerService', () => { + let service: ResponseTrackerService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ResponseTrackerService], + }).compile(); + + service = module.get(ResponseTrackerService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/response-tracker/response-tracker.service.ts b/src/response-tracker/response-tracker.service.ts new file mode 100644 index 0000000..79f7584 --- /dev/null +++ b/src/response-tracker/response-tracker.service.ts @@ -0,0 +1,78 @@ +import { Injectable, NotFoundException } from "@nestjs/common"; +import { CreateResponseTrackerDto } from "./dto/create-response-tracker.dto"; +import { UpdateResponseTrackerDto } from "./dto/update-response-tracker.dto"; +import { PrismaService } from "src/prisma/prisma.service"; + +@Injectable() +export class ResponseTrackerService { + constructor(private prisma: PrismaService) {} + + public async create(createResponseTrackerDto: CreateResponseTrackerDto) { + return await this.prisma.responseTracker.create({ + data: createResponseTrackerDto, + }); + } + + public async findAll() { + return await this.prisma.responseTracker.findMany(); + } + + public async findOne(id: number) { + const response = await this.prisma.responseTracker.findUnique({ + where: { id }, + }); + if (!response) + throw new NotFoundException(`Response tracer with id #${id} not found`); + return response; + } + + public async findBySurveyFormId(surveyFormId: number) { + const response = await this.prisma.responseTracker.findMany({ + where: { surveyFormId }, + }); + + if (!response || response.length === 0) + throw new NotFoundException( + `Response tracer with surveyFormId #${surveyFormId} not found` + ); + return response; + } + + public async findByAssessorId(assessorId: number) { + const response = await this.prisma.responseTracker.findMany({ + where: { assessorId }, + }); + + if (!response || response.length === 0) + throw new NotFoundException( + `Response tracer with assessorId #${assessorId} not found` + ); + return response; + } + + public async findByAssesseeId(assesseeId: number) { + const response = await this.prisma.responseTracker.findMany({ + where: { assesseeId }, + }); + + if (!response || response.length === 0) + throw new NotFoundException( + `Response tracer with assesseeId #${assesseeId} not found` + ); + return response; + } + + public async update( + id: number, + updateResponseTrackerDto: UpdateResponseTrackerDto + ) { + return await this.prisma.responseTracker.update({ + where: { id }, + data: updateResponseTrackerDto, + }); + } + + public async remove(id: number) { + return await this.prisma.responseTracker.delete({ where: { id } }); + } +} From bd046a1fb11b3b7e40d50a0d41236802dbc2b3cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CVamshiBatta=E2=80=9D?= <“batta.v@esmagico.in”> Date: Wed, 11 Oct 2023 21:25:40 +0530 Subject: [PATCH 016/175] Added sorveyForm module. --- src/app.module.ts | 2 + src/survey-form/dto/create-survey-form.dto.ts | 24 +++ src/survey-form/dto/index.ts | 3 + .../dto/response-survey-form.dto.ts | 9 ++ src/survey-form/dto/update-survey-form.dto.ts | 10 ++ .../survey-form.controller.spec.ts | 18 +++ src/survey-form/survey-form.controller.ts | 150 ++++++++++++++++++ src/survey-form/survey-form.module.ts | 10 ++ src/survey-form/survey-form.service.ts | 45 ++++++ 9 files changed, 271 insertions(+) create mode 100644 src/survey-form/dto/create-survey-form.dto.ts create mode 100644 src/survey-form/dto/index.ts create mode 100644 src/survey-form/dto/response-survey-form.dto.ts create mode 100644 src/survey-form/dto/update-survey-form.dto.ts create mode 100644 src/survey-form/survey-form.controller.spec.ts create mode 100644 src/survey-form/survey-form.controller.ts create mode 100644 src/survey-form/survey-form.module.ts create mode 100644 src/survey-form/survey-form.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 043799c..4b76d76 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -4,6 +4,7 @@ import { AppController } from "./app.controller"; import { AppService } from "./app.service"; import { PrismaModule } from "./prisma/prisma.module"; import { PrismaService } from "./prisma/prisma.service"; +import { SurveyFormModule } from "./survey-form/survey-form.module"; import { ResponseTrackerModule } from "./response-tracker/response-tracker.module"; import { MockFracModule } from "./mockModules/mock.module"; @@ -13,6 +14,7 @@ import { MockFracModule } from "./mockModules/mock.module"; isGlobal: true, }), PrismaModule, + SurveyFormModule, ResponseTrackerModule, MockFracModule, ], diff --git a/src/survey-form/dto/create-survey-form.dto.ts b/src/survey-form/dto/create-survey-form.dto.ts new file mode 100644 index 0000000..0a97a16 --- /dev/null +++ b/src/survey-form/dto/create-survey-form.dto.ts @@ -0,0 +1,24 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { SurveyStatusEnum } from "@prisma/client"; +import { IsEnum, IsNotEmpty, IsNumber, IsObject } from "class-validator"; + +export class CreateSurveyFormDto{ + @ApiProperty({ type: "integer", example: 1 }) + @IsNumber() + @IsNotEmpty() + userId: number; + + @IsNumber() + @IsNotEmpty() + surveyConfigId: number; + + @ApiProperty({ enum: SurveyStatusEnum, example: "CREATED" }) + @IsEnum(SurveyStatusEnum) + @IsNotEmpty() + status: SurveyStatusEnum; + + @ApiProperty({ type: "object", example: { key: "value" } }) + @IsObject() + @IsNotEmpty() + questionsJson: Record; +} \ No newline at end of file diff --git a/src/survey-form/dto/index.ts b/src/survey-form/dto/index.ts new file mode 100644 index 0000000..5ed2c9e --- /dev/null +++ b/src/survey-form/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-survey-form.dto'; +export * from './update-survey-form.dto'; +export * from './response-survey-form.dto'; \ No newline at end of file diff --git a/src/survey-form/dto/response-survey-form.dto.ts b/src/survey-form/dto/response-survey-form.dto.ts new file mode 100644 index 0000000..00d229f --- /dev/null +++ b/src/survey-form/dto/response-survey-form.dto.ts @@ -0,0 +1,9 @@ +import { SurveyStatusEnum } from "@prisma/client"; + +export class ResponseSurveyFormDto{ + readonly id: number; + readonly userId: number; + readonly surveyConfigId: number; + readonly status: SurveyStatusEnum; + readonly questionsJson: Record; +} \ No newline at end of file diff --git a/src/survey-form/dto/update-survey-form.dto.ts b/src/survey-form/dto/update-survey-form.dto.ts new file mode 100644 index 0000000..77cf398 --- /dev/null +++ b/src/survey-form/dto/update-survey-form.dto.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { SurveyStatusEnum } from "@prisma/client"; +import { IsEnum, IsNotEmpty } from "class-validator"; + +export class UpdateSurveyFormDto { + @ApiProperty({ enum: SurveyStatusEnum, example: "CREATED" }) + @IsEnum(SurveyStatusEnum) + @IsNotEmpty() + status: SurveyStatusEnum; +} diff --git a/src/survey-form/survey-form.controller.spec.ts b/src/survey-form/survey-form.controller.spec.ts new file mode 100644 index 0000000..5f21695 --- /dev/null +++ b/src/survey-form/survey-form.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { SurveyFormController } from './survey-form.controller'; + +describe('SurveyFormController', () => { + let controller: SurveyFormController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [SurveyFormController], + }).compile(); + + controller = module.get(SurveyFormController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/survey-form/survey-form.controller.ts b/src/survey-form/survey-form.controller.ts new file mode 100644 index 0000000..aa3d028 --- /dev/null +++ b/src/survey-form/survey-form.controller.ts @@ -0,0 +1,150 @@ +import { + Controller, + Post, + Body, + HttpStatus, + Get, + Param, + Patch, + Delete, + Res, + Logger, +} from "@nestjs/common"; +import { SurveyFormService } from "./survey-form.service"; +import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { + CreateSurveyFormDto, + ResponseSurveyFormDto, + UpdateSurveyFormDto, +} from "./dto"; +import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; + +@Controller("survey-form") +@ApiTags("survey-form") +export class SurveyFormController { + private readonly logger = new Logger(SurveyFormController.name); + constructor(private readonly surveyFormService: SurveyFormService) {} + + @Post() + @ApiOperation({ summary: "Create a Survey-Form" }) + @ApiResponse({ status: HttpStatus.CREATED, type: ResponseSurveyFormDto }) + async createSurveyForm( + @Res() res, + @Body() createSurveyFormDto: CreateSurveyFormDto + ): Promise { + try { + this.logger.log(`Initiated creating new survey form`); + + const surveyForm = await this.surveyFormService.createSurveyForm( + createSurveyFormDto + ); + + this.logger.log(`Successfully created new survey form`); + + return res.status(HttpStatus.CREATED).json({ + data: surveyForm, + message: "SurveyForm created successfully", + }); + } catch (error) { + console.log({error,}); + this.logger.error(`Failed to create new survey form`, error); + // Handle and return appropriate response for errors + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + return res + .status(statusCode) + .json({ message: errorMessage || "Could not create survey form" }); + } + } + + @Get(":id") + @ApiOperation({ summary: "Fetch a Survey-Form" }) + @ApiResponse({ status: HttpStatus.FOUND, type: ResponseSurveyFormDto }) + async findSurveyFormById( + @Res() res, + @Param("id") id: number + ): Promise { + try { + this.logger.log(`Initiated fetching new survey form`); + + const surveyForm = await this.surveyFormService.findSurveyFormById(id); + + this.logger.log(`Successfully fetched the survey form`); + + return res.status(HttpStatus.OK).json({ + data: surveyForm, + message: "SurveyForm fetched successfully", + }); + } catch (error) { + this.logger.error(`Failed to fetch survey form`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + return res + .status(statusCode) + .json({ message: errorMessage || "Could not find survey form" }); + } + } + + @Patch(":id") + @ApiOperation({ summary: "Update the Survey-Form" }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseSurveyFormDto }) + async updateSurveyForm( + @Res() res, + @Param("id") id: number, + @Body() { status }: UpdateSurveyFormDto + ): Promise { + try { + this.logger.log(`Initiated updating new survey form`); + + const surveyForm = await this.surveyFormService.updateSurveyForm( + id, + status + ); + + this.logger.log(`Successfully updated the survey form`); + + return res.status(HttpStatus.OK).json({ + data: surveyForm, + message: "SurveyForm updated successfully", + }); + } catch (error) { + this.logger.error(`Failed to update survey form`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + return res + .status(statusCode) + .json({ message: errorMessage || "Could not update survey form" }); + } + } + + @Delete(":id") + @ApiOperation({ summary: "Delete a Survey-Form" }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseSurveyFormDto }) + async deleteSurveyForm( + @Res() res, + @Param("id") id: number + ): Promise { + try { + this.logger.log(`Initiated deleting new survey form`); + + const surveyForm = await this.surveyFormService.deleteSurveyForm(id); + + this.logger.log(`Successfully deleted survey form`); + + return res.status(HttpStatus.OK).json({ + data: surveyForm, + message: "SurveyForm deleted successfully", + }); + } catch (error) { + this.logger.error(`Failed to delete survey form`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + return res + .status(statusCode) + .json({ message: errorMessage || "Could not delete survey form." }); + } + } +} diff --git a/src/survey-form/survey-form.module.ts b/src/survey-form/survey-form.module.ts new file mode 100644 index 0000000..9887c0e --- /dev/null +++ b/src/survey-form/survey-form.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { SurveyFormController } from './survey-form.controller'; +import { SurveyFormService } from './survey-form.service'; +import { PrismaService } from 'src/prisma/prisma.service'; + +@Module({ + controllers: [SurveyFormController], + providers: [SurveyFormService, PrismaService] +}) +export class SurveyFormModule {} diff --git a/src/survey-form/survey-form.service.ts b/src/survey-form/survey-form.service.ts new file mode 100644 index 0000000..386efce --- /dev/null +++ b/src/survey-form/survey-form.service.ts @@ -0,0 +1,45 @@ +import { Injectable, NotFoundException } from "@nestjs/common"; +import { SurveyStatusEnum } from "@prisma/client"; +import { PrismaService } from "src/prisma/prisma.service"; +import { CreateSurveyFormDto } from "./dto"; + +@Injectable() +export class SurveyFormService { + constructor(private prisma: PrismaService) {} + + async createSurveyForm(createSurveyFormDto: CreateSurveyFormDto) { + try { + const surveyform = await this.prisma.surveyForm.create({ + data: { ...createSurveyFormDto }, + }); + return surveyform; + } catch (error) { + console.log({error,}) + throw new Error(error) + } + + } + + async findSurveyFormById(id: number) { + const surveyForm = await this.prisma.surveyForm.findUnique({ + where: { id }, + }); + if (!surveyForm) { + throw new NotFoundException("Survey Form not found"); + } + return surveyForm; + } + + async updateSurveyForm(id: number, status: SurveyStatusEnum) { + return this.prisma.surveyForm.update({ + where: { id }, + data: { status }, + }); + } + + async deleteSurveyForm(id: number) { + return this.prisma.surveyForm.delete({ + where: { id }, + }); + } +} From 6f75d598b07a594379dc908d572d9121a9f3f778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CVamshiBatta=E2=80=9D?= <“batta.v@esmagico.in”> Date: Fri, 13 Oct 2023 11:11:36 +0530 Subject: [PATCH 017/175] Updated Response DTO and removed global serializer. --- src/app.module.ts | 2 +- .../dto/response-survey-form.dto.ts | 8 +++++- src/survey-form/survey-form.controller.ts | 25 +++++++++---------- src/survey-form/survey-form.service.ts | 19 +++++--------- 4 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/app.module.ts b/src/app.module.ts index 4b76d76..517adb1 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -14,9 +14,9 @@ import { MockFracModule } from "./mockModules/mock.module"; isGlobal: true, }), PrismaModule, + MockFracModule, SurveyFormModule, ResponseTrackerModule, - MockFracModule, ], controllers: [AppController], providers: [AppService, PrismaService], diff --git a/src/survey-form/dto/response-survey-form.dto.ts b/src/survey-form/dto/response-survey-form.dto.ts index 00d229f..67ce658 100644 --- a/src/survey-form/dto/response-survey-form.dto.ts +++ b/src/survey-form/dto/response-survey-form.dto.ts @@ -1,9 +1,15 @@ import { SurveyStatusEnum } from "@prisma/client"; +import { JsonValue } from "@prisma/client/runtime/library"; export class ResponseSurveyFormDto{ readonly id: number; readonly userId: number; readonly surveyConfigId: number; readonly status: SurveyStatusEnum; - readonly questionsJson: Record; + readonly questionsJson?: JsonValue; +} + +export class SurveyFormResponse { + readonly data?: ResponseSurveyFormDto; + readonly message: string; } \ No newline at end of file diff --git a/src/survey-form/survey-form.controller.ts b/src/survey-form/survey-form.controller.ts index aa3d028..b19bf5c 100644 --- a/src/survey-form/survey-form.controller.ts +++ b/src/survey-form/survey-form.controller.ts @@ -14,7 +14,7 @@ import { SurveyFormService } from "./survey-form.service"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { CreateSurveyFormDto, - ResponseSurveyFormDto, + SurveyFormResponse, UpdateSurveyFormDto, } from "./dto"; import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; @@ -27,11 +27,11 @@ export class SurveyFormController { @Post() @ApiOperation({ summary: "Create a Survey-Form" }) - @ApiResponse({ status: HttpStatus.CREATED, type: ResponseSurveyFormDto }) + @ApiResponse({ status: HttpStatus.CREATED, type: SurveyFormResponse }) async createSurveyForm( @Res() res, @Body() createSurveyFormDto: CreateSurveyFormDto - ): Promise { + ): Promise { try { this.logger.log(`Initiated creating new survey form`); @@ -46,9 +46,8 @@ export class SurveyFormController { message: "SurveyForm created successfully", }); } catch (error) { - console.log({error,}); this.logger.error(`Failed to create new survey form`, error); - // Handle and return appropriate response for errors + const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); return res @@ -59,11 +58,11 @@ export class SurveyFormController { @Get(":id") @ApiOperation({ summary: "Fetch a Survey-Form" }) - @ApiResponse({ status: HttpStatus.FOUND, type: ResponseSurveyFormDto }) + @ApiResponse({ status: HttpStatus.FOUND, type: SurveyFormResponse }) async findSurveyFormById( @Res() res, @Param("id") id: number - ): Promise { + ): Promise { try { this.logger.log(`Initiated fetching new survey form`); @@ -71,7 +70,7 @@ export class SurveyFormController { this.logger.log(`Successfully fetched the survey form`); - return res.status(HttpStatus.OK).json({ + return res.status(HttpStatus.OK).send({ data: surveyForm, message: "SurveyForm fetched successfully", }); @@ -82,18 +81,18 @@ export class SurveyFormController { getPrismaErrorStatusAndMessage(error); return res .status(statusCode) - .json({ message: errorMessage || "Could not find survey form" }); + .send({ message: errorMessage || "Could not find survey form" }); } } @Patch(":id") @ApiOperation({ summary: "Update the Survey-Form" }) - @ApiResponse({ status: HttpStatus.OK, type: ResponseSurveyFormDto }) + @ApiResponse({ status: HttpStatus.OK, type: SurveyFormResponse }) async updateSurveyForm( @Res() res, @Param("id") id: number, @Body() { status }: UpdateSurveyFormDto - ): Promise { + ): Promise { try { this.logger.log(`Initiated updating new survey form`); @@ -121,11 +120,11 @@ export class SurveyFormController { @Delete(":id") @ApiOperation({ summary: "Delete a Survey-Form" }) - @ApiResponse({ status: HttpStatus.OK, type: ResponseSurveyFormDto }) + @ApiResponse({ status: HttpStatus.OK, type: SurveyFormResponse }) async deleteSurveyForm( @Res() res, @Param("id") id: number - ): Promise { + ): Promise { try { this.logger.log(`Initiated deleting new survey form`); diff --git a/src/survey-form/survey-form.service.ts b/src/survey-form/survey-form.service.ts index 386efce..c778fd0 100644 --- a/src/survey-form/survey-form.service.ts +++ b/src/survey-form/survey-form.service.ts @@ -1,23 +1,16 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { SurveyStatusEnum } from "@prisma/client"; import { PrismaService } from "src/prisma/prisma.service"; -import { CreateSurveyFormDto } from "./dto"; +import { CreateSurveyFormDto, ResponseSurveyFormDto } from "./dto"; @Injectable() export class SurveyFormService { constructor(private prisma: PrismaService) {} async createSurveyForm(createSurveyFormDto: CreateSurveyFormDto) { - try { - const surveyform = await this.prisma.surveyForm.create({ - data: { ...createSurveyFormDto }, - }); - return surveyform; - } catch (error) { - console.log({error,}) - throw new Error(error) - } - + return await this.prisma.surveyForm.create({ + data: { ...createSurveyFormDto }, + }); } async findSurveyFormById(id: number) { @@ -31,14 +24,14 @@ export class SurveyFormService { } async updateSurveyForm(id: number, status: SurveyStatusEnum) { - return this.prisma.surveyForm.update({ + return await this.prisma.surveyForm.update({ where: { id }, data: { status }, }); } async deleteSurveyForm(id: number) { - return this.prisma.surveyForm.delete({ + return await this.prisma.surveyForm.delete({ where: { id }, }); } From 377d78aee1a83e75e30f5b5f0030561d4a54126a Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Wed, 11 Oct 2023 14:19:20 +0530 Subject: [PATCH 018/175] Survey score APIs created --- src/app.module.ts | 2 + .../dto/create-survey-score.dto.ts | 27 ++ src/survey-score/dto/index.ts | 3 + .../dto/response-survey-score.dto.ts | 21 ++ .../dto/update-survey-score.dto.ts | 4 + .../survey-score.controller.spec.ts | 20 ++ src/survey-score/survey-score.controller.ts | 266 ++++++++++++++++++ src/survey-score/survey-score.module.ts | 10 + src/survey-score/survey-score.service.spec.ts | 18 ++ src/survey-score/survey-score.service.ts | 62 ++++ 10 files changed, 433 insertions(+) create mode 100644 src/survey-score/dto/create-survey-score.dto.ts create mode 100644 src/survey-score/dto/index.ts create mode 100644 src/survey-score/dto/response-survey-score.dto.ts create mode 100644 src/survey-score/dto/update-survey-score.dto.ts create mode 100644 src/survey-score/survey-score.controller.spec.ts create mode 100644 src/survey-score/survey-score.controller.ts create mode 100644 src/survey-score/survey-score.module.ts create mode 100644 src/survey-score/survey-score.service.spec.ts create mode 100644 src/survey-score/survey-score.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 517adb1..5a83dc1 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -7,6 +7,7 @@ import { PrismaService } from "./prisma/prisma.service"; import { SurveyFormModule } from "./survey-form/survey-form.module"; import { ResponseTrackerModule } from "./response-tracker/response-tracker.module"; import { MockFracModule } from "./mockModules/mock.module"; +import { SurveyScoreModule } from "./survey-score/survey-score.module"; @Module({ imports: [ @@ -17,6 +18,7 @@ import { MockFracModule } from "./mockModules/mock.module"; MockFracModule, SurveyFormModule, ResponseTrackerModule, + SurveyScoreModule, ], controllers: [AppController], providers: [AppService, PrismaService], diff --git a/src/survey-score/dto/create-survey-score.dto.ts b/src/survey-score/dto/create-survey-score.dto.ts new file mode 100644 index 0000000..86ad827 --- /dev/null +++ b/src/survey-score/dto/create-survey-score.dto.ts @@ -0,0 +1,27 @@ +import { IsNotEmpty, IsNumber } from "class-validator"; + +export class CreateSurveyScoreDto { + @IsNotEmpty() + @IsNumber() + userId: number; + + @IsNotEmpty() + @IsNumber() + surveyFormId: number; + + @IsNotEmpty() + @IsNumber() + roleId: number; + + @IsNotEmpty() + @IsNumber() + competencyId: number; + + @IsNotEmpty() + @IsNumber() + competencyLevelId: number; + + @IsNotEmpty() + @IsNumber() + score: number; +} diff --git a/src/survey-score/dto/index.ts b/src/survey-score/dto/index.ts new file mode 100644 index 0000000..8c1faec --- /dev/null +++ b/src/survey-score/dto/index.ts @@ -0,0 +1,3 @@ +export * from "./create-survey-score.dto"; +export * from "./response-survey-score.dto"; +export * from "./update-survey-score.dto"; diff --git a/src/survey-score/dto/response-survey-score.dto.ts b/src/survey-score/dto/response-survey-score.dto.ts new file mode 100644 index 0000000..17ecb38 --- /dev/null +++ b/src/survey-score/dto/response-survey-score.dto.ts @@ -0,0 +1,21 @@ +export class SurveyScoreResponse { + readonly id: number; + readonly userId: number; + readonly surveyFormId: number; + readonly roleId: number; + readonly competencyId: number; + readonly competencyLevelId: number; + readonly score: number; +} + +export class SurveyScoreResponseDto { + data?: SurveyScoreResponse; + message: string; + statusCode?: number; +} + +export class SurveyScoreMultipleResponseDto { + data?: SurveyScoreResponse[]; + message: string; + statusCode?: number; +} diff --git a/src/survey-score/dto/update-survey-score.dto.ts b/src/survey-score/dto/update-survey-score.dto.ts new file mode 100644 index 0000000..82dca82 --- /dev/null +++ b/src/survey-score/dto/update-survey-score.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateSurveyScoreDto } from './create-survey-score.dto'; + +export class UpdateSurveyScoreDto extends PartialType(CreateSurveyScoreDto) {} diff --git a/src/survey-score/survey-score.controller.spec.ts b/src/survey-score/survey-score.controller.spec.ts new file mode 100644 index 0000000..234643b --- /dev/null +++ b/src/survey-score/survey-score.controller.spec.ts @@ -0,0 +1,20 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { SurveyScoreController } from './survey-score.controller'; +import { SurveyScoreService } from './survey-score.service'; + +describe('SurveyScoreController', () => { + let controller: SurveyScoreController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [SurveyScoreController], + providers: [SurveyScoreService], + }).compile(); + + controller = module.get(SurveyScoreController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/survey-score/survey-score.controller.ts b/src/survey-score/survey-score.controller.ts new file mode 100644 index 0000000..bcd06c4 --- /dev/null +++ b/src/survey-score/survey-score.controller.ts @@ -0,0 +1,266 @@ +import { + Controller, + Get, + Post, + Body, + Patch, + Param, + Delete, + Res, + HttpStatus, + Logger, + ParseIntPipe, +} from "@nestjs/common"; +import { SurveyScoreService } from "./survey-score.service"; +import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { + CreateSurveyScoreDto, + SurveyScoreMultipleResponseDto, + SurveyScoreResponseDto, + UpdateSurveyScoreDto, +} from "./dto"; +import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; + +@Controller("survey-score") +@ApiTags("survey-score") +export class SurveyScoreController { + private logger = new Logger(SurveyScoreController.name); + constructor(private readonly surveyScoreService: SurveyScoreService) {} + + @Post() + @ApiOperation({ summary: "create new survey score" }) + @ApiResponse({ status: HttpStatus.CREATED, type: SurveyScoreResponseDto }) + async create( + @Res() res, + @Body() createSurveyScoreDto: CreateSurveyScoreDto + ): Promise { + try { + this.logger.log(`Initiated creating new survey score`); + + const surveyScore = await this.surveyScoreService.create( + createSurveyScoreDto + ); + + this.logger.log(`Successfully created new survey score`); + + return res.status(HttpStatus.CREATED).json({ + data: surveyScore, + message: `Successfully created survey score`, + }); + } catch (error) { + this.logger.error(`Failed to create new survey score`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed to create survey score`, + }); + } + } + + @Get() + @ApiOperation({ summary: "get all survey score" }) + @ApiResponse({ status: HttpStatus.OK, type: SurveyScoreMultipleResponseDto }) + async findAll(@Res() res): Promise { + try { + this.logger.log(`Initiated fetching all survey scores`); + + const surveyScore = await this.surveyScoreService.findAll(); + + this.logger.log(`Successfully fetched all survey scores`); + + return res.status(HttpStatus.OK).json({ + data: surveyScore, + message: `Successfully fetched all survey scores`, + }); + } catch (error) { + this.logger.error(`Failed to fetch all survey score`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed to fetch all survey score`, + }); + } + } + + @Get("survey-form/:surveyFormId") + @ApiOperation({ summary: "get survey scores by surveyFormId" }) + @ApiResponse({ status: HttpStatus.OK, type: SurveyScoreMultipleResponseDto }) + async findBySurveyFormId( + @Res() res, + @Param("surveyFormId", ParseIntPipe) surveyFormId: number + ): Promise { + try { + this.logger.log( + `Initiated fetching survey score with surveyFormId #${surveyFormId}` + ); + + const surveyScore = await this.surveyScoreService.findBySurveyFormId( + surveyFormId + ); + + this.logger.log( + `Successfully fetched survey score with surveyFormId #${surveyFormId}` + ); + + return res.status(HttpStatus.OK).json({ + data: surveyScore, + message: `Successfully fetched survey score with surveyFormId #${surveyFormId}`, + }); + } catch (error) { + this.logger.error( + `Failed to fetch survey score for surveyFormId #${surveyFormId}`, + error + ); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: + errorMessage || + `Failed to fetch survey score for surveyFormId #${surveyFormId}`, + }); + } + } + + @Get("user/:userId") + @ApiOperation({ summary: "get survey score by user Id" }) + @ApiResponse({ status: HttpStatus.OK, type: SurveyScoreMultipleResponseDto }) + async findByUserId( + @Res() res, + @Param("userId", ParseIntPipe) userId: number + ): Promise { + try { + this.logger.log(`Initiated fetching survey score with userId #${userId}`); + + const surveyScore = await this.surveyScoreService.findByUserId(userId); + + this.logger.log( + `Successfully fetched survey score with userId #${userId}` + ); + + return res.status(HttpStatus.OK).json({ + data: surveyScore, + message: `Successfully fetched survey score with userId #${userId}`, + }); + } catch (error) { + this.logger.error( + `Failed to fetch survey score for userId #${userId}`, + error + ); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: + errorMessage || `Failed to fetch survey score for userId #${userId}`, + }); + } + } + + @Get(":id") + @ApiOperation({ summary: "get survey score by id" }) + @ApiResponse({ status: HttpStatus.OK, type: SurveyScoreResponseDto }) + async findOne( + @Res() res, + @Param("id", ParseIntPipe) id: number + ): Promise { + try { + this.logger.log(`Initiated fetching survey score with id #${id}`); + + const surveyScore = await this.surveyScoreService.findOne(id); + + this.logger.log(`Successfully fetched survey score with id #${id}`); + + return res.status(HttpStatus.OK).json({ + data: surveyScore, + message: `Successfully fetched survey score with id #${id}`, + }); + } catch (error) { + this.logger.error(`Failed to fetch survey score for id #${id}`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed to fetch survey score for id #${id}`, + }); + } + } + + @Patch(":id") + @ApiOperation({ summary: "update survey score by id" }) + @ApiResponse({ status: HttpStatus.OK, type: SurveyScoreResponseDto }) + async update( + @Res() res, + @Param("id", ParseIntPipe) id: number, + @Body() updateSurveyScoreDto: UpdateSurveyScoreDto + ): Promise { + try { + this.logger.log(`Initiated updating survey score with id #${id}`); + + const surveyScore = await this.surveyScoreService.update( + id, + updateSurveyScoreDto + ); + + this.logger.log(`Successfully updated survey score with id #${id}`); + + return res.status(HttpStatus.OK).json({ + data: surveyScore, + message: `Successfully updated survey score with id #${id}`, + }); + } catch (error) { + this.logger.error(`Failed to update survey score with id #${id}`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + message: + errorMessage || `Failed to update survey score with id #${id} `, + }); + } + } + + @Delete(":id") + @ApiOperation({ summary: "delete survey score by id" }) + @ApiResponse({ status: HttpStatus.OK, type: SurveyScoreResponseDto }) + async remove( + @Res() res, + @Param("id", ParseIntPipe) id: number + ): Promise { + try { + this.logger.log(`Initiated deleting survey score with id #${id}`); + + const surveyScore = await this.surveyScoreService.remove(id); + + this.logger.log(`Successfully deleted survey score with id #${id}`); + + return res.status(HttpStatus.OK).json({ + data: surveyScore, + message: `Successfully deleted survey score with id #${id}`, + }); + } catch (error) { + this.logger.error(`Failed to delete survey score with id #${id}`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + message: + errorMessage || `Failed to delete survey score with id #${id} `, + }); + } + } +} diff --git a/src/survey-score/survey-score.module.ts b/src/survey-score/survey-score.module.ts new file mode 100644 index 0000000..4d791fb --- /dev/null +++ b/src/survey-score/survey-score.module.ts @@ -0,0 +1,10 @@ +import { Module } from "@nestjs/common"; +import { SurveyScoreService } from "./survey-score.service"; +import { SurveyScoreController } from "./survey-score.controller"; +import { PrismaService } from "src/prisma/prisma.service"; + +@Module({ + controllers: [SurveyScoreController], + providers: [SurveyScoreService, PrismaService], +}) +export class SurveyScoreModule {} diff --git a/src/survey-score/survey-score.service.spec.ts b/src/survey-score/survey-score.service.spec.ts new file mode 100644 index 0000000..8590a58 --- /dev/null +++ b/src/survey-score/survey-score.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { SurveyScoreService } from './survey-score.service'; + +describe('SurveyScoreService', () => { + let service: SurveyScoreService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [SurveyScoreService], + }).compile(); + + service = module.get(SurveyScoreService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/survey-score/survey-score.service.ts b/src/survey-score/survey-score.service.ts new file mode 100644 index 0000000..f630aa7 --- /dev/null +++ b/src/survey-score/survey-score.service.ts @@ -0,0 +1,62 @@ +import { Injectable, NotFoundException } from "@nestjs/common"; +import { CreateSurveyScoreDto } from "./dto/create-survey-score.dto"; +import { UpdateSurveyScoreDto } from "./dto/update-survey-score.dto"; +import { PrismaService } from "src/prisma/prisma.service"; + +@Injectable() +export class SurveyScoreService { + constructor(private prisma: PrismaService) {} + + public async create(createSurveyScoreDto: CreateSurveyScoreDto) { + return await this.prisma.surveyScore.create({ data: createSurveyScoreDto }); + } + + public async findAll() { + return await this.prisma.surveyScore.findMany(); + } + + public async findOne(id: number) { + const surveyScore = await this.prisma.surveyScore.findUnique({ + where: { + id, + }, + }); + + if (!surveyScore) + throw new NotFoundException(`survey score with id #${id} not found`); + return surveyScore; + } + + public async findBySurveyFormId(surveyFormId: number) { + const surveyScore = await this.prisma.surveyScore.findMany({ + where: { surveyFormId }, + }); + if (!surveyScore || surveyScore.length === 0) + throw new NotFoundException( + `Survey score for survey form id #${surveyFormId} not found` + ); + return surveyScore; + } + + public async findByUserId(userId: number) { + const surveyScore = await this.prisma.surveyScore.findMany({ + where: { userId }, + }); + if (!surveyScore || surveyScore.length === 0) + throw new NotFoundException( + `Survey score for user id #${userId} not found` + ); + return surveyScore; + } + + public async update(id: number, updateSurveyScoreDto: UpdateSurveyScoreDto) { + return await this.prisma.surveyScore.update({ + where: { id }, + data: updateSurveyScoreDto, + }); + } + + public async remove(id: number) { + return await this.prisma.surveyScore.delete({ where: { id } }); + } +} From d3b49d7917c98f112f4d1b58cb091f6f34aa2107 Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Thu, 12 Oct 2023 13:07:42 +0530 Subject: [PATCH 019/175] Created API for the question bank --- prisma/schema.prisma | 3 +- src/app.module.ts | 2 + .../dto/create-question-bank.dto.ts | 63 ++++++ .../dto/response-question-bank.dto.ts | 7 + .../dto/update-question-bank.dto.ts | 18 ++ src/question-bank/question-bank.controller.ts | 183 ++++++++++++++++++ src/question-bank/question-bank.module.ts | 20 ++ src/question-bank/question-bank.service.ts | 111 +++++++++++ 8 files changed, 405 insertions(+), 2 deletions(-) create mode 100644 src/question-bank/dto/create-question-bank.dto.ts create mode 100644 src/question-bank/dto/response-question-bank.dto.ts create mode 100644 src/question-bank/dto/update-question-bank.dto.ts create mode 100644 src/question-bank/question-bank.controller.ts create mode 100644 src/question-bank/question-bank.module.ts create mode 100644 src/question-bank/question-bank.service.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index d8ad831..54d122d 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -25,9 +25,8 @@ model QuestionBank { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt competencyId Int - competencyLevelId String + competencyLevelId Int question String - levelId Int @@unique([competencyLevelId, question]) @@map("question_bank") diff --git a/src/app.module.ts b/src/app.module.ts index 5a83dc1..8b31b96 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -8,6 +8,7 @@ import { SurveyFormModule } from "./survey-form/survey-form.module"; import { ResponseTrackerModule } from "./response-tracker/response-tracker.module"; import { MockFracModule } from "./mockModules/mock.module"; import { SurveyScoreModule } from "./survey-score/survey-score.module"; +import { QuestionBankModule } from "./question-bank/question-bank.module"; @Module({ imports: [ @@ -19,6 +20,7 @@ import { SurveyScoreModule } from "./survey-score/survey-score.module"; SurveyFormModule, ResponseTrackerModule, SurveyScoreModule, + QuestionBankModule, ], controllers: [AppController], providers: [AppService, PrismaService], diff --git a/src/question-bank/dto/create-question-bank.dto.ts b/src/question-bank/dto/create-question-bank.dto.ts new file mode 100644 index 0000000..d50861e --- /dev/null +++ b/src/question-bank/dto/create-question-bank.dto.ts @@ -0,0 +1,63 @@ +import { + IsDate, + IsInt, + IsNotEmpty, + IsOptional, + IsString, +} from "class-validator"; + +// CreateQuestionBankDto is used for creating a new question bank +export class CreateQuestionBankDto { + // Compentency id associated with the question bank + @IsNotEmpty() + @IsInt() + competencyId: number; + + // Compentency level id associated with the question bank + @IsNotEmpty() + @IsInt() + competencyLevelId: number; + + // Question for the question bank + @IsNotEmpty() + @IsString() + question: string; + + // creation date-time for the question bank + @IsOptional() + @IsDate() + createdAt?: Date; + + // update date-time for the question bank + @IsOptional() + @IsDate() + updatedAt?: Date; +} + +// Question bank filter +export class QuestionBankFilterDto { + // Optional competencyId filter, validated that it's valid compentency id or not + @IsOptional() + @IsInt() + competencyId?: number; + + // Optional competencyLevelId filter, validated that it's valid competencyLevel Id or not + @IsOptional() + @IsInt() + competencyLevelId?: number; + + // Optional limit for pagination, validate that it's an integer. + @IsOptional() + @IsInt() + limit?: number; + + // Optional offset for pagination, validate that it's an integer. + @IsOptional() + @IsInt() + offset?: number; + + // Optional field to specify the order of results, validate that it's a string. + @IsOptional() + @IsString() + orderBy?: string; +} diff --git a/src/question-bank/dto/response-question-bank.dto.ts b/src/question-bank/dto/response-question-bank.dto.ts new file mode 100644 index 0000000..0435aaa --- /dev/null +++ b/src/question-bank/dto/response-question-bank.dto.ts @@ -0,0 +1,7 @@ +export class ResponseQuestionBankDto { + readonly competencyId: number; + readonly competencyLevelId: number; + readonly question: string; + readonly createdAt: Date; + readonly updatedAt: Date; +} diff --git a/src/question-bank/dto/update-question-bank.dto.ts b/src/question-bank/dto/update-question-bank.dto.ts new file mode 100644 index 0000000..8850979 --- /dev/null +++ b/src/question-bank/dto/update-question-bank.dto.ts @@ -0,0 +1,18 @@ +import { IsDate, IsNotEmpty, IsOptional, IsString } from "class-validator"; + +export class UpdateQuestionBankDto { + // Question for the question bank + @IsNotEmpty() + @IsString() + question: string; + + // creation date-time for the question bank + @IsOptional() + @IsDate() + createdAt?: Date; + + // update date-time for the question bank + @IsOptional() + @IsDate() + updatedAt?: Date; +} diff --git a/src/question-bank/question-bank.controller.ts b/src/question-bank/question-bank.controller.ts new file mode 100644 index 0000000..d074414 --- /dev/null +++ b/src/question-bank/question-bank.controller.ts @@ -0,0 +1,183 @@ +import { + Controller, + Get, + Post, + Body, + Patch, + Param, + Delete, + Logger, + HttpStatus, + Res, + Query, + ParseIntPipe, +} from "@nestjs/common"; +import { QuestionBankService } from "./question-bank.service"; +import { + CreateQuestionBankDto, + QuestionBankFilterDto, +} from "./dto/create-question-bank.dto"; +import { UpdateQuestionBankDto } from "./dto/update-question-bank.dto"; +import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { ResponseQuestionBankDto } from "./dto/response-question-bank.dto"; +import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; + +@Controller("question-bank") +@ApiTags("question-bank") +export class QuestionBankController { + // Create a logger instance for this controller to log events and errors. + private readonly logger = new Logger(QuestionBankController.name); + + // The question bank controller class, injecting the question bank service. + constructor(private readonly questionBankService: QuestionBankService) {} + + // Api for creating a new question + @Post() + @ApiOperation({ summary: "Create a new question" }) // Api operation for the swagger + @ApiResponse({ status: HttpStatus.CREATED, type: ResponseQuestionBankDto }) // Api response for the swagger + async createQuestionByCompentencyLevel( + @Res() res, + @Body() createQuestionBankDto: CreateQuestionBankDto + ): Promise { + try { + // Log the initiation for the question creation + this.logger.log(`Initiate to create a question`); + + const createdQuestion = + await this.questionBankService.createQuestionByCompentencyLevel( + createQuestionBankDto + ); + // Log the successful creation of the question bank + this.logger.log(`Successfully created question`); + return res.status(HttpStatus.CREATED).json({ + message: "Question created sucessfully.", + data: createdQuestion, + }); + } catch (error) { + this.logger.error(`Failed to create new question.`, error); + // get error message and status code + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + // Return an error response + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed to create new question.`, + }); + } + } + + // Api for getting all questions or by filtering using competencyId or competencyLevelId + @Get() + @ApiOperation({ summary: "Get all the questions" }) // Api operation for the swagger + @ApiResponse({ + status: HttpStatus.OK, + type: ResponseQuestionBankDto, + isArray: true, + }) // Api response for the swagger + async getAllQuestions( + @Res() res, + @Query() filter: QuestionBankFilterDto + ): Promise { + try { + // Log the initiation for fetching all the questions + this.logger.log(`Initiated fetching all the questions.`); + const questions = await this.questionBankService.getAllQuestions(filter); + + // Log the successful fetching of data from db. + this.logger.log(`Successfully fetched all questions`); + + // return the successful response for fetching the data from the db. + return res.status(HttpStatus.OK).json({ + message: "Successfully fetched all questions.", + data: questions, + }); + } catch (error) { + // log the error message + this.logger.error(`Failed to get all questions.`, error); + // get error message and status code + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + // Return an error response + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed to get all questions.`, + }); + } + } + + // Api to update the question by Id + @Patch("update/:id") + @ApiOperation({ summary: "Update the question by Id" }) // Api operation for the swagger + @ApiResponse({ + status: HttpStatus.OK, + type: ResponseQuestionBankDto, + }) // Api operation for the swagger + async updateQuestionById( + @Res() res, + @Param("id", ParseIntPipe) id: number, + @Body() updateQuestionBankDto: UpdateQuestionBankDto + ): Promise { + try { + // Log the initiation for updating the question for the given id + this.logger.log(`Initiated updating the question for id #${id}`); + // Update the question for the id + const updateQuestion = await this.questionBankService.updateQuestionById( + id, + updateQuestionBankDto + ); + // Log the successful update of the question with the given id + this.logger.log(`Successfully updated question for id #${id}`); + // Return response and statuscode for the successful update of the question + return res.status(HttpStatus.OK).json({ + message: `Successfully updated question for id #${id}`, + data: updateQuestion, + }); + } catch (error) { + // Log the error + this.logger.error(`Failed to update question for id #${id}`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); // get error message and status code + + // Return the response and status code for the failed update of the question + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed to update question for id #${id}`, + }); + } + } + + // Delete the Question for the id + @Delete("/delete/:id") + @ApiOperation({ summary: "Delete the question by Id" }) // Api operation for swagger + @ApiResponse({ + status: HttpStatus.OK, + type: ResponseQuestionBankDto, + }) + async deleteQuestionById( + @Res() res, + @Param("id", ParseIntPipe) id: number + ): Promise { + try { + this.logger.log(`Initiating deleting of a question with an id ${id}`); + + const deletedQuestion = + await this.questionBankService.deleteQuestionById(id); + + return res.status(HttpStatus.OK).json({ + message: `Successfully deleted question for id #${id}`, + data: deletedQuestion, + }); + } catch (error) { + this.logger.error(`Failed to delete question for id #${id}`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); // get error message and status code + + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed to delete question for id #${id}`, + }); + } + } +} diff --git a/src/question-bank/question-bank.module.ts b/src/question-bank/question-bank.module.ts new file mode 100644 index 0000000..a4d475a --- /dev/null +++ b/src/question-bank/question-bank.module.ts @@ -0,0 +1,20 @@ +import { Module } from '@nestjs/common'; +import { QuestionBankService } from './question-bank.service'; +import { QuestionBankController } from './question-bank.controller'; +import { PrismaModule } from 'src/prisma/prisma.module'; +import { PrismaService } from 'src/prisma/prisma.service'; + +@Module({ + // Import the PrismaModule to make Prisma service available within this module + imports: [PrismaModule], + + // Declare the QuestionBankController as a controller for this module + controllers: [QuestionBankController], + + // Declare the QuestionBankService and PrismaService as providers for this module + providers: [QuestionBankService, PrismaService], + + // Export the QuestionBankService to make it available for other modules that import this module + exports: [QuestionBankService], +}) +export class QuestionBankModule {} diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts new file mode 100644 index 0000000..7028100 --- /dev/null +++ b/src/question-bank/question-bank.service.ts @@ -0,0 +1,111 @@ +import { Injectable, NotFoundException } from "@nestjs/common"; +import { + CreateQuestionBankDto, + QuestionBankFilterDto, +} from "./dto/create-question-bank.dto"; +import { UpdateQuestionBankDto } from "./dto/update-question-bank.dto"; +import { PrismaService } from "src/prisma/prisma.service"; + +@Injectable() +export class QuestionBankService { + constructor(private prisma: PrismaService) {} + + async createQuestionByCompentencyLevel( + createQuestionBankDto: CreateQuestionBankDto + ) { + // check for compentencyId exist in competency model in db + const checkCompentencyId = await this.prisma.competency.findUnique({ + where: { + id: createQuestionBankDto.competencyId, + }, + }); + + // check for competencyLevelId exist in competencyLevel model in db + const checkCompetencyLevelId = await this.prisma.competencyLevel.findUnique( + { + where: { + id: createQuestionBankDto.competencyLevelId, + }, + } + ); + // if not found throw error + if (!checkCompentencyId || !checkCompetencyLevelId) { + throw new NotFoundException( + "Either Competency or Competency Level is not Found" + ); + } + // Otherwise create a new question bank + const newQuestion = await this.prisma.questionBank.create({ + data: createQuestionBankDto, + }); + return newQuestion; + } + + async getAllQuestions(filter: QuestionBankFilterDto) { + // Get all questions and filter using compentencyId and compentencyLevelId + const { + competencyId, + competencyLevelId, + limit = 10, + offset = 0, + orderBy, + } = filter; + return this.prisma.questionBank.findMany({ + where: { + competencyId: competencyId ?? undefined, // Optional compentencyId filter + competencyLevelId: competencyLevelId ?? undefined, // Optional competencyLevelId filter + }, + orderBy: { + [orderBy || "createdAt"]: "asc", // Default sorting by createdAt + }, + skip: offset, + take: limit, + }); + } + + async updateQuestionById( + questionId: number, + updateQuestionBankDto: UpdateQuestionBankDto + ) { + // Check if there is question in db for the questionId + const findQuestion = await this.prisma.questionBank.findUnique({ + where: { + id: questionId, + }, + }); + // If not found throw error message + if (!findQuestion) { + throw new NotFoundException( + `No question exists with the id #${questionId}.` + ); + } + // Otherwise update it's details + return this.prisma.questionBank.updateMany({ + where: { + id: questionId, + }, + data: updateQuestionBankDto, + }); + } + + async deleteQuestionById(questionId : number) { + // Check the question is there in db for the given questionId and competencyLevelId + const findQuestion = await this.prisma.questionBank.findUnique({ + where: { + id: questionId, + }, + }); + // If not found throw error message + if (!findQuestion) { + throw new NotFoundException( + `No question exists with the Id #${questionId}.` + ); + } + // Otherwise delete question related to that competency id + return this.prisma.questionBank.delete({ + where: { + id: questionId, + } + }) + } +} From 29abd3c420c7ee1ae8d712b8ed1068a88045adcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CVamshiBatta=E2=80=9D?= <“batta.v@esmagico.in”> Date: Fri, 13 Oct 2023 11:29:26 +0530 Subject: [PATCH 020/175] Removed createdAt and UpdaredAt fields from create and update DTOs. --- src/question-bank/dto/create-question-bank.dto.ts | 10 ---------- src/question-bank/dto/update-question-bank.dto.ts | 10 ---------- 2 files changed, 20 deletions(-) diff --git a/src/question-bank/dto/create-question-bank.dto.ts b/src/question-bank/dto/create-question-bank.dto.ts index d50861e..263e875 100644 --- a/src/question-bank/dto/create-question-bank.dto.ts +++ b/src/question-bank/dto/create-question-bank.dto.ts @@ -22,16 +22,6 @@ export class CreateQuestionBankDto { @IsNotEmpty() @IsString() question: string; - - // creation date-time for the question bank - @IsOptional() - @IsDate() - createdAt?: Date; - - // update date-time for the question bank - @IsOptional() - @IsDate() - updatedAt?: Date; } // Question bank filter diff --git a/src/question-bank/dto/update-question-bank.dto.ts b/src/question-bank/dto/update-question-bank.dto.ts index 8850979..7865f23 100644 --- a/src/question-bank/dto/update-question-bank.dto.ts +++ b/src/question-bank/dto/update-question-bank.dto.ts @@ -5,14 +5,4 @@ export class UpdateQuestionBankDto { @IsNotEmpty() @IsString() question: string; - - // creation date-time for the question bank - @IsOptional() - @IsDate() - createdAt?: Date; - - // update date-time for the question bank - @IsOptional() - @IsDate() - updatedAt?: Date; } From d0b664d023920f26607efceff7b07605d6b4b301 Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Wed, 11 Oct 2023 19:29:46 +0530 Subject: [PATCH 021/175] API for survey config and survey parameter --- src/app.module.ts | 4 + .../dto/create-survey-config.dto.ts | 49 +++++ .../dto/response-survey-config.dto.ts | 6 + .../dto/update-survey-config.dto.ts | 4 + src/survey-config/survey-config.controller.ts | 176 ++++++++++++++++++ src/survey-config/survey-config.module.ts | 13 ++ src/survey-config/survey-config.service.ts | 72 +++++++ .../dto/create-survey-parameter.dto.ts | 20 ++ .../dto/response-survey-parameter.dto.ts | 8 + .../dto/update-survey-parameter.dto.ts | 4 + .../survey-parameter.controller.ts | 142 ++++++++++++++ .../survey-parameter.module.ts | 13 ++ .../survey-parameter.service.ts | 60 ++++++ 13 files changed, 571 insertions(+) create mode 100644 src/survey-config/dto/create-survey-config.dto.ts create mode 100644 src/survey-config/dto/response-survey-config.dto.ts create mode 100644 src/survey-config/dto/update-survey-config.dto.ts create mode 100644 src/survey-config/survey-config.controller.ts create mode 100644 src/survey-config/survey-config.module.ts create mode 100644 src/survey-config/survey-config.service.ts create mode 100644 src/survey-parameter/dto/create-survey-parameter.dto.ts create mode 100644 src/survey-parameter/dto/response-survey-parameter.dto.ts create mode 100644 src/survey-parameter/dto/update-survey-parameter.dto.ts create mode 100644 src/survey-parameter/survey-parameter.controller.ts create mode 100644 src/survey-parameter/survey-parameter.module.ts create mode 100644 src/survey-parameter/survey-parameter.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 8b31b96..27c97a3 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -9,6 +9,8 @@ import { ResponseTrackerModule } from "./response-tracker/response-tracker.modul import { MockFracModule } from "./mockModules/mock.module"; import { SurveyScoreModule } from "./survey-score/survey-score.module"; import { QuestionBankModule } from "./question-bank/question-bank.module"; +import { SurveyConfigModule } from "./survey-config/survey-config.module"; +import { SurveyParameterModule } from "./survey-parameter/survey-parameter.module"; @Module({ imports: [ @@ -21,6 +23,8 @@ import { QuestionBankModule } from "./question-bank/question-bank.module"; ResponseTrackerModule, SurveyScoreModule, QuestionBankModule, + SurveyConfigModule, + SurveyParameterModule, ], controllers: [AppController], providers: [AppService, PrismaService], diff --git a/src/survey-config/dto/create-survey-config.dto.ts b/src/survey-config/dto/create-survey-config.dto.ts new file mode 100644 index 0000000..e45ed06 --- /dev/null +++ b/src/survey-config/dto/create-survey-config.dto.ts @@ -0,0 +1,49 @@ +import { IsDate, IsInt, IsNotEmpty, IsOptional, IsString, Min } from "class-validator"; + +export class CreateSurveyConfigDto { + // maximum number of question for the survey config + @IsNotEmpty() + @IsInt() + maxQuestions: number; + + // start time for the survey config + @IsNotEmpty() + @IsDate() + startTime: Date; + + // end time for the survey config + @IsNotEmpty() + @IsDate() + endTime: Date; +} + +// SurveyConfigFilterDto is used for filtering and paginating survey config. +export class SurveyConfigFilterDto { + // Optional maxQuestions filter validate that its a number + @IsOptional() + @IsNotEmpty() + @IsInt() + maxQuestions?: number; + + //Optional startTime filter validate that its valid date-time value + @IsOptional() + @IsNotEmpty() + @IsDate() + startTime?: Date; + + //Optional endTime filter validate that its valid date-time value. + @IsOptional() + @IsNotEmpty() + @IsDate() + endTime?: Date; + + // Optional limit for pagination, validate that it's an integer. + @IsOptional() + @IsInt() + limit?: number = 10; + + // Optional offset for pagination, validate that it's an integer. + @IsOptional() + @IsInt() + offset?: number = 0; +} diff --git a/src/survey-config/dto/response-survey-config.dto.ts b/src/survey-config/dto/response-survey-config.dto.ts new file mode 100644 index 0000000..d90166f --- /dev/null +++ b/src/survey-config/dto/response-survey-config.dto.ts @@ -0,0 +1,6 @@ +export class ResponseSurveyConfigDto { + readonly id: number; + readonly maxQuestions: number; + readonly startTime: Date; + readonly endTime: Date; +} diff --git a/src/survey-config/dto/update-survey-config.dto.ts b/src/survey-config/dto/update-survey-config.dto.ts new file mode 100644 index 0000000..25c9b44 --- /dev/null +++ b/src/survey-config/dto/update-survey-config.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/swagger'; +import { CreateSurveyConfigDto } from './create-survey-config.dto'; + +export class UpdateSurveyConfigDto extends PartialType(CreateSurveyConfigDto) {} diff --git a/src/survey-config/survey-config.controller.ts b/src/survey-config/survey-config.controller.ts new file mode 100644 index 0000000..a19ff85 --- /dev/null +++ b/src/survey-config/survey-config.controller.ts @@ -0,0 +1,176 @@ +import { + Controller, + Get, + Post, + Body, + Patch, + Delete, + Logger, + HttpStatus, + Res, + Query, + Param, + ParseIntPipe, +} from "@nestjs/common"; +import { SurveyConfigService } from "./survey-config.service"; +import { + CreateSurveyConfigDto, + SurveyConfigFilterDto, +} from "./dto/create-survey-config.dto"; +import { UpdateSurveyConfigDto } from "./dto/update-survey-config.dto"; +import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { ResponseSurveyConfigDto } from "./dto/response-survey-config.dto"; +import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; + +@Controller("survey-config") +@ApiTags("survey-config") +export class SurveyConfigController { + // Create a logger instance for this controller to log events and errors. + private readonly logger = new Logger(SurveyConfigController.name); + + // The survey config controller class, injecting the survey config service. + constructor(private readonly surveyConfigService: SurveyConfigService) {} + + @Post() + @ApiOperation({ summary: "Create a survey config" }) // Describes the api operation for Swagger. + @ApiResponse({ status: HttpStatus.CREATED, type: ResponseSurveyConfigDto }) // Describes the response for Swagger. + async createSurveyConfig( + @Res() res, + @Body() createSurveyConfigDto: CreateSurveyConfigDto + ): Promise { + try { + // Log the initiation for the Survey config creation + this.logger.log(`Creating a new survey config`); + const createdSurveyConfig = + await this.surveyConfigService.createSurveyConfig( + createSurveyConfigDto + ); + + // Log the successful creation of the survey config + this.logger.log(`Successfully created the new survey config.`); + + // Return a success response with the created survey config data + return res.status(HttpStatus.CREATED).json({ + message: "Survey config created successfully", + data: createdSurveyConfig, + }); + } catch (error) { + this.logger.error(`Failed to create new survey config.`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); // get error message and status code + + // Return an error response + return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ + statusCode, + message: errorMessage || `Failed to create new survey config.`, + }); + } + } + + // Get all survey config + @Get() + @ApiOperation({ summary: "Get all Survey config" }) // Api operation for swagger + @ApiResponse({ + status: HttpStatus.OK, + type: ResponseSurveyConfigDto, + isArray: true, + }) // Api response for Swagger. + async getAllSurveyConfig( + @Res() res, + @Query() filter: SurveyConfigFilterDto + ): Promise { + try { + this.logger.log(`Initiated fetching all the survey config.`); + const surveyConfigs = await this.surveyConfigService.getAllSurveyConfig( + filter + ); + this.logger.log(`Successfully fetched survey config`); + + return res.status(HttpStatus.OK).json({ + message: "Successfully fetched all survey config.", + data: surveyConfigs, + }); + } catch (error) { + this.logger.error(`Failed to fetch survey configs`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); // get error message and status code + + return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ + statusCode, + message: errorMessage || `Failed to Get all survey config.`, + }); + } + } + + // Update the survey config + @Patch("/update/:id") + @ApiOperation({ summary: "Update the survey config by ID" }) // Api operation for swagger + @ApiResponse({ status: HttpStatus.OK, type: ResponseSurveyConfigDto }) // Api operation for swagger + async updateSurveyConfigById( + @Res() res, + @Param("id", ParseIntPipe) id: number, + @Body() updateSurveyConfigDto: UpdateSurveyConfigDto + ): Promise { + try { + this.logger.log(`Initiated updating the survey config for id #${id}`); + const updateSurveyConfig = + await this.surveyConfigService.updateSurveyConfigById( + id, + updateSurveyConfigDto + ); + this.logger.log(`Successfully updated survey config for id #${id}`); + return res.status(HttpStatus.OK).json({ + message: `Successfully updated survey config for id #${id}`, + data: updateSurveyConfig, + }); + } catch (error) { + this.logger.error(`Failed to update survey config for id #${id}`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); // get error message and status code + + return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ + statusCode, + message: errorMessage || `Failed to update survey config for id #${id}`, + }); + } + } + + // Delete the survey config + @Delete("/delete/:id") + @ApiOperation({ summary: "Delete the survey config by Id" }) // Api operation for swagger + @ApiResponse({ + status: HttpStatus.OK, + type: ResponseSurveyConfigDto, + }) + async deleteSurveyConfigById( + @Res() res, + @Param("id", ParseIntPipe) id: number + ): Promise { + try { + this.logger.log( + `Initiating deleting of a survey config with an id ${id}` + ); + + const deletedSurveyConfig = + await this.surveyConfigService.deleteSurveyConfig(id); + + return res.status(HttpStatus.OK).json({ + message: `Successfully deleted survey config for id #${id}`, + data: deletedSurveyConfig, + }); + } catch (error) { + this.logger.error(`Failed to delete survey config for id #${id}`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); // get error message and status code + + return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ + statusCode, + message: errorMessage || `Failed to delete survey config for id #${id}`, + }); + } + } +} diff --git a/src/survey-config/survey-config.module.ts b/src/survey-config/survey-config.module.ts new file mode 100644 index 0000000..8ddbdb4 --- /dev/null +++ b/src/survey-config/survey-config.module.ts @@ -0,0 +1,13 @@ +import { Module } from "@nestjs/common"; +import { SurveyConfigService } from "./survey-config.service"; +import { SurveyConfigController } from "./survey-config.controller"; +import { PrismaModule } from "../prisma/prisma.module"; +import { PrismaService } from "../prisma/prisma.service"; + +@Module({ + imports: [PrismaModule], + controllers: [SurveyConfigController], + providers: [SurveyConfigService, PrismaService], + exports: [SurveyConfigService], +}) +export class SurveyConfigModule {} diff --git a/src/survey-config/survey-config.service.ts b/src/survey-config/survey-config.service.ts new file mode 100644 index 0000000..06dd4d8 --- /dev/null +++ b/src/survey-config/survey-config.service.ts @@ -0,0 +1,72 @@ +import { Injectable, NotFoundException, Query, Res } from "@nestjs/common"; +import { + CreateSurveyConfigDto, + SurveyConfigFilterDto, +} from "./dto/create-survey-config.dto"; +import { UpdateSurveyConfigDto } from "./dto/update-survey-config.dto"; +import { PrismaService } from "src/prisma/prisma.service"; + +@Injectable() +export class SurveyConfigService { + constructor(private prisma: PrismaService) {} + async createSurveyConfig(createSurveyConfigDto: CreateSurveyConfigDto) { + // create new survey config + const newConfig = await this.prisma.surveyConfig.create({ + data: createSurveyConfigDto, + }); + return newConfig; + } + + async getAllSurveyConfig(filter: SurveyConfigFilterDto) { + const { maxQuestions, startTime, endTime, limit = 10, offset = 0 } = filter; + return this.prisma.surveyConfig.findMany({ + where: { + maxQuestions: maxQuestions ?? undefined, // Optional maxQuestions filter + startTime: startTime ?? undefined, // Optional startTime filter + endTime: endTime ?? undefined, // Optional endTime filter + }, + skip: +offset, + take: +limit, + }); + } + + async updateSurveyConfigById(surveyConfigId: number, updateSurveyConfig: UpdateSurveyConfigDto) { + // Check the document available for the given surveyId in database to update. + const findSurveyConfigId = await this.prisma.surveyConfig.findUnique({ + where: { + id: surveyConfigId, + }, + }); + if (!findSurveyConfigId) { + throw new NotFoundException( + `Survey config with ID ${findSurveyConfigId} not found.` + ); + } + // Update the survey config by the id + return this.prisma.surveyConfig.update({ + where: { + id: surveyConfigId, + }, + data: updateSurveyConfig, + }); + } + + async deleteSurveyConfig(surveyConfigId: number) { + // Find the survey config with the provided Id and check it exists or not before deleting + const isExisted = await this.prisma.surveyConfig.findFirst({ + where: { + id : surveyConfigId + } + }) + if (!isExisted) { + throw new NotFoundException(`The survey Config ID ${surveyConfigId} doesn't exist.`) + } + // Delete a single survey configuration based on its unique identifier (id). + const deletedSurveyConfig = await this.prisma.surveyConfig.delete({ + where: { + id: surveyConfigId + } + }) + return deletedSurveyConfig; + } +} diff --git a/src/survey-parameter/dto/create-survey-parameter.dto.ts b/src/survey-parameter/dto/create-survey-parameter.dto.ts new file mode 100644 index 0000000..10d31e1 --- /dev/null +++ b/src/survey-parameter/dto/create-survey-parameter.dto.ts @@ -0,0 +1,20 @@ +import { TimeUnitsEnum } from "@prisma/client"; +import { IsEnum, IsInt, IsNotEmpty, IsOptional } from "class-validator"; + +// CreateSurveyParameterDto is used to create a new survey parameter +export class CreateSurveyParameterDto { + // Onboarding time for the survey parameter + @IsNotEmpty() + @IsInt() + onboardingTime: number; + + // Onboarding time unit for the survey parameter + @IsNotEmpty() + @IsEnum(TimeUnitsEnum) + onboardingTimeUnit: TimeUnitsEnum; + + // Survey cycle value for the survey parameter + @IsNotEmpty() + @IsInt() + surveyCycle: number; +} diff --git a/src/survey-parameter/dto/response-survey-parameter.dto.ts b/src/survey-parameter/dto/response-survey-parameter.dto.ts new file mode 100644 index 0000000..a0a5729 --- /dev/null +++ b/src/survey-parameter/dto/response-survey-parameter.dto.ts @@ -0,0 +1,8 @@ +import { TimeUnitsEnum } from "@prisma/client"; + +export class ResponseSurveyParameterdto { + readonly id: number; + readonly onboardingTime: number; + readonly onboardingTimeUnit: TimeUnitsEnum; + readonly surveyCycle: number; +} diff --git a/src/survey-parameter/dto/update-survey-parameter.dto.ts b/src/survey-parameter/dto/update-survey-parameter.dto.ts new file mode 100644 index 0000000..da9262b --- /dev/null +++ b/src/survey-parameter/dto/update-survey-parameter.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/swagger'; +import { CreateSurveyParameterDto } from './create-survey-parameter.dto'; + +export class UpdateSurveyParameterDto extends PartialType(CreateSurveyParameterDto) {} diff --git a/src/survey-parameter/survey-parameter.controller.ts b/src/survey-parameter/survey-parameter.controller.ts new file mode 100644 index 0000000..34cf2b0 --- /dev/null +++ b/src/survey-parameter/survey-parameter.controller.ts @@ -0,0 +1,142 @@ +import { + Controller, + Get, + Post, + Body, + Patch, + Param, + Logger, + HttpStatus, + Res, + Query, + ParseIntPipe, +} from "@nestjs/common"; +import { SurveyParameterService } from "./survey-parameter.service"; +import { CreateSurveyParameterDto } from "./dto/create-survey-parameter.dto"; +import { UpdateSurveyParameterDto } from "./dto/update-survey-parameter.dto"; +import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { ResponseSurveyParameterdto } from "./dto/response-survey-parameter.dto"; +import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; + +@ApiTags("survey-parameter") +@Controller("survey-parameter") +export class SurveyParameterController { + // Create a logger instance for this log events and errors + private readonly logger = new Logger(SurveyParameterController.name); + + // The survey parameter controller class, injecting the survey parameter service. + constructor( + private readonly surveyParameterService: SurveyParameterService + ) {} + + // Api to create a new survey parameter + @Post() + @ApiOperation({ summary: "Create a Survey parameter" }) // Api operation for swagger + @ApiResponse({ status: HttpStatus.CREATED, type: ResponseSurveyParameterdto }) // Api response for swagger + async createSurveyParameter( + @Res() res, + @Body() createSurveyParameterDto: CreateSurveyParameterDto + ): Promise { + try { + // Log the initiation for the Survey Parameter creation + this.logger.log(`Initiate to create a Survey Parameter`); + + const createdSurveyParameter = + await this.surveyParameterService.createSurveyParameter( + createSurveyParameterDto + ); + // Log the successful creation of the survey parameter + this.logger.log(`Successfully Created Survey Parameter`); + // Return a success response with the created survey parameter. + return res.status(HttpStatus.CREATED).json({ + message: "Survey parameter created sucessfully.", + data: createdSurveyParameter, + }); + } catch (error) { + this.logger.error(`Failed to create new survey Parameter.`, error); + // get error message and status code + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + // Return an error response + return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ + statusCode, + message: errorMessage || `Failed to create new survey Parameter.`, + }); + } + } + + // Api to get all the survey parameter + @Get() + @ApiOperation({ summary: "Find All Survey parameters" }) // Api operation for swagger + @ApiResponse({ + status: HttpStatus.OK, + type: ResponseSurveyParameterdto, + isArray: true, + }) // Api response for swagger + async getAllSurveyParameter( + @Res() res + ): Promise { + try { + this.logger.log(`Initiated fetching all the survey parameter.`); + const surveyParameters = + await this.surveyParameterService.getAllSurveyParameter(); + + if (!surveyParameters.length) throw Error("No record found"); + this.logger.log(`Fetched successfully all the survey parameter.`); + + return res.status(HttpStatus.OK).json({ + message: "Successfully fetched all survey config.", + data: surveyParameters, + }); + } catch (error) { + this.logger.error(`Failed to fetch survey parameters`, error); + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); // get error message and status code + // Return an error response + return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ + statusCode, + message: errorMessage || `Failed to Get all survey parameters.`, + }); + } + } + + // Api to update the survey parameter by using the Id. + @Patch("update/:id") + @ApiOperation({ summary: "Update the survey parameter by Id." }) + @ApiResponse({ + status: HttpStatus.OK, + type: ResponseSurveyParameterdto, + }) + async updateSurveyParameterById( + @Res() res, + @Param("id", ParseIntPipe) id: number, + @Body() updateSurveyParameterDto: UpdateSurveyParameterDto + ): Promise { + try { + this.logger.log(`Initiated updating the survey parameter for id #${id}`); + let updatedSurveyParameter = + await this.surveyParameterService.updateSurveyParameterById( + id, + updateSurveyParameterDto + ); + this.logger.log(`Successfully updated survey parameter for id #${id}`); + return res.status(HttpStatus.OK).json({ + message: `Successfully updated survey parameter for id #${id}`, + data: updatedSurveyParameter, + }); + } catch (error) { + this.logger.error( + `Failed to update survey parameter for id #${id}`, + error + ); + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); // get error message and status code + // Return an error response + return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ + statusCode, + message: + errorMessage || `Failed to update survey parameter for id #${id}`, + }); + } + } +} diff --git a/src/survey-parameter/survey-parameter.module.ts b/src/survey-parameter/survey-parameter.module.ts new file mode 100644 index 0000000..04515df --- /dev/null +++ b/src/survey-parameter/survey-parameter.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { SurveyParameterService } from './survey-parameter.service'; +import { SurveyParameterController } from './survey-parameter.controller'; +import { PrismaModule } from 'src/prisma/prisma.module'; +import { PrismaService } from 'src/prisma/prisma.service'; + +@Module({ + imports: [PrismaModule], + controllers: [SurveyParameterController], + providers: [SurveyParameterService, PrismaService], + exports : [SurveyParameterService] +}) +export class SurveyParameterModule {} diff --git a/src/survey-parameter/survey-parameter.service.ts b/src/survey-parameter/survey-parameter.service.ts new file mode 100644 index 0000000..84e8ad2 --- /dev/null +++ b/src/survey-parameter/survey-parameter.service.ts @@ -0,0 +1,60 @@ +import { ConflictException, Injectable, NotFoundException } from "@nestjs/common"; +import { CreateSurveyParameterDto } from "./dto/create-survey-parameter.dto"; +import { UpdateSurveyParameterDto } from "./dto/update-survey-parameter.dto"; +import { PrismaService } from "src/prisma/prisma.service"; + +@Injectable() +export class SurveyParameterService { + constructor(private prisma: PrismaService) { } + + // To check if the survey parameter exist in database or not + async checkIfSurveyParameterExists(){ + return this.prisma.surveyParameters.findFirst(); + } + + async createSurveyParameter( + createSurveyParameterDto: CreateSurveyParameterDto + ) { + const existingSurveyParameter = await this.checkIfSurveyParameterExists(); + + if (existingSurveyParameter) { + throw new ConflictException("Survey parameter already exists."); + } + + // create a new survey parameter + const newSurveyParameter = await this.prisma.surveyParameters.create({ + data: createSurveyParameterDto, + }); + return newSurveyParameter; + } + + async getAllSurveyParameter() { + return this.prisma.surveyParameters.findMany(); + } + + async updateSurveyParameterById( + surveyParameterId: number, + updateSurveyParameter: UpdateSurveyParameterDto + ) { + // check if for the given id, data is available in db or not + const findSurveyParameterId = await this.prisma.surveyParameters.findUnique( + { + where: { + id: surveyParameterId, + }, + } + ); + if (!findSurveyParameterId) { + throw new NotFoundException( + `Survey parameter with ID ${findSurveyParameterId} not found.` + ); + } + // update an existing survey parameter by its unique identifier (id). + return this.prisma.surveyParameters.update({ + where: { + id: surveyParameterId, + }, + data: updateSurveyParameter, + }); + } +} From 60ee21a449c663e4e3b4addd5a3f9972a0d888eb Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Wed, 11 Oct 2023 20:55:40 +0530 Subject: [PATCH 022/175] Changes as per PR review --- src/survey-config/survey-config.controller.ts | 8 ++++---- src/survey-parameter/survey-parameter.controller.ts | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/survey-config/survey-config.controller.ts b/src/survey-config/survey-config.controller.ts index a19ff85..31b1abc 100644 --- a/src/survey-config/survey-config.controller.ts +++ b/src/survey-config/survey-config.controller.ts @@ -61,7 +61,7 @@ export class SurveyConfigController { getPrismaErrorStatusAndMessage(error); // get error message and status code // Return an error response - return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ + return res.status(statusCode).json({ statusCode, message: errorMessage || `Failed to create new survey config.`, }); @@ -97,7 +97,7 @@ export class SurveyConfigController { const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); // get error message and status code - return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ + return res.status(statusCode).json({ statusCode, message: errorMessage || `Failed to Get all survey config.`, }); @@ -131,7 +131,7 @@ export class SurveyConfigController { const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); // get error message and status code - return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ + return res.status(statusCode).json({ statusCode, message: errorMessage || `Failed to update survey config for id #${id}`, }); @@ -167,7 +167,7 @@ export class SurveyConfigController { const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); // get error message and status code - return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ + return res.status(statusCode).json({ statusCode, message: errorMessage || `Failed to delete survey config for id #${id}`, }); diff --git a/src/survey-parameter/survey-parameter.controller.ts b/src/survey-parameter/survey-parameter.controller.ts index 34cf2b0..7dab2ac 100644 --- a/src/survey-parameter/survey-parameter.controller.ts +++ b/src/survey-parameter/survey-parameter.controller.ts @@ -58,7 +58,7 @@ export class SurveyParameterController { const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); // Return an error response - return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ + return res.status(statusCode).json({ statusCode, message: errorMessage || `Failed to create new survey Parameter.`, }); @@ -93,7 +93,7 @@ export class SurveyParameterController { const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); // get error message and status code // Return an error response - return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ + return res.status(statusCode).json({ statusCode, message: errorMessage || `Failed to Get all survey parameters.`, }); @@ -132,7 +132,7 @@ export class SurveyParameterController { const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); // get error message and status code // Return an error response - return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ + return res.status(statusCode).json({ statusCode, message: errorMessage || `Failed to update survey parameter for id #${id}`, From 7a27385b31a733281982167eb5910be98de13544 Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Fri, 13 Oct 2023 15:48:26 +0530 Subject: [PATCH 023/175] competencyLevelId-dto added --- .../mock-competency/dto/create-competency.dto.ts | 11 ++++++++--- .../mock-competency/mock-competency.controller.ts | 10 +++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/mockModules/mock-competency/dto/create-competency.dto.ts b/src/mockModules/mock-competency/dto/create-competency.dto.ts index 85e07c5..898d1b0 100644 --- a/src/mockModules/mock-competency/dto/create-competency.dto.ts +++ b/src/mockModules/mock-competency/dto/create-competency.dto.ts @@ -1,5 +1,4 @@ - -import { IsNotEmpty, IsOptional, IsString } from "class-validator"; +import { IsNotEmpty, IsNumber, IsOptional, IsString } from "class-validator"; export class CreateCompetencyDto { @IsString() @@ -10,4 +9,10 @@ export class CreateCompetencyDto { @IsString() @IsNotEmpty() description?: string; -} \ No newline at end of file +} + +export class AddCompetencyLevelDto { + @IsNotEmpty() + @IsNumber() + competencyLevelId: number; +} diff --git a/src/mockModules/mock-competency/mock-competency.controller.ts b/src/mockModules/mock-competency/mock-competency.controller.ts index 6d0b06f..831d5f5 100644 --- a/src/mockModules/mock-competency/mock-competency.controller.ts +++ b/src/mockModules/mock-competency/mock-competency.controller.ts @@ -12,6 +12,7 @@ import { } from "@nestjs/common"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { + AddCompetencyLevelDto, CreateCompetencyDto, ResponseAddCompetencyLevelToCompetency, ResponseCompetencyDto, @@ -111,7 +112,7 @@ export class MockCompetencyController { @Delete(":id") @ApiOperation({ summary: "delete mock competency by id" }) - @ApiResponse({ status: HttpStatus.OK, type: ResponseCompetencyDto, }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseCompetencyDto }) async removeCompetency(@Res() res, @Param("id", ParseIntPipe) id: number) { try { const competency = await this.competencyService.removeCompetency(id); @@ -128,11 +129,14 @@ export class MockCompetencyController { @Post("addExistingCompetencyLevelToCompetency/:id") @ApiOperation({ summary: "add competency level to competency" }) - @ApiResponse({ status: HttpStatus.OK, type: ResponseAddCompetencyLevelToCompetency }) + @ApiResponse({ + status: HttpStatus.OK, + type: ResponseAddCompetencyLevelToCompetency, + }) async addExistingCompetencyLevelToCompetency( @Res() res, @Param("id", ParseIntPipe) id: number, - @Body() competencyLevel: { competencyLevelId: number } + @Body() competencyLevel: AddCompetencyLevelDto ) { try { const competency = From f3cb4bd66d00a1e8c6f064a375a6758b39984e71 Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Fri, 13 Oct 2023 16:18:53 +0530 Subject: [PATCH 024/175] updated required fields --- .../dto/response-competency.dto.ts | 4 +- .../mock-competency.controller.ts | 17 ++++++-- .../mock-role/dto/create-mock-role.dto.ts | 11 +++-- .../mock-role/mock-role.controller.ts | 42 +++++++++++-------- 4 files changed, 48 insertions(+), 26 deletions(-) diff --git a/src/mockModules/mock-competency/dto/response-competency.dto.ts b/src/mockModules/mock-competency/dto/response-competency.dto.ts index 624a9d5..aa69cb3 100644 --- a/src/mockModules/mock-competency/dto/response-competency.dto.ts +++ b/src/mockModules/mock-competency/dto/response-competency.dto.ts @@ -11,5 +11,5 @@ export class ResponseCompetencyDto { export class ResponseAddCompetencyLevelToCompetency { readonly competency: ResponseCompetencyDto; - readonly competencyLevel: ResponseCompetencyLevelDto; -} \ No newline at end of file + readonly competencyLevel?: ResponseCompetencyLevelDto; +} diff --git a/src/mockModules/mock-competency/mock-competency.controller.ts b/src/mockModules/mock-competency/mock-competency.controller.ts index 831d5f5..fee7791 100644 --- a/src/mockModules/mock-competency/mock-competency.controller.ts +++ b/src/mockModules/mock-competency/mock-competency.controller.ts @@ -146,12 +146,16 @@ export class MockCompetencyController { ); return res.status(HttpStatus.OK).json({ data: competency, - message: "Competency successfully deleted.", + message: "Competency added competency level to competency.", }); } catch (error) { return res .status(HttpStatus.INTERNAL_SERVER_ERROR) - .json({ message: error.meta.cause }); + .json({ + message: + error?.meta?.cause || + `Failed to add competency level to competency`, + }); } } @@ -171,12 +175,17 @@ export class MockCompetencyController { ); return res.status(HttpStatus.OK).json({ data: competency, - message: "Competency successfully deleted.", + message: + "Successfully created competency level and added to competency.", }); } catch (error) { return res .status(HttpStatus.INTERNAL_SERVER_ERROR) - .json({ message: error.meta.cause }); + .json({ + message: + error?.meta?.cause || + `Failed to create competency level and add to competency`, + }); } } } diff --git a/src/mockModules/mock-role/dto/create-mock-role.dto.ts b/src/mockModules/mock-role/dto/create-mock-role.dto.ts index f21aab3..ebe10bd 100644 --- a/src/mockModules/mock-role/dto/create-mock-role.dto.ts +++ b/src/mockModules/mock-role/dto/create-mock-role.dto.ts @@ -1,5 +1,4 @@ - -import { IsNotEmpty, IsOptional, IsString } from "class-validator"; +import { IsNotEmpty, IsNumber, IsOptional, IsString } from "class-validator"; export class CreateMockRoleDto { @IsString() @@ -10,4 +9,10 @@ export class CreateMockRoleDto { @IsString() @IsNotEmpty() description?: string; -} \ No newline at end of file +} + +export class AddCompetencyDto { + @IsNotEmpty() + @IsNumber() + competencyId: number; +} diff --git a/src/mockModules/mock-role/mock-role.controller.ts b/src/mockModules/mock-role/mock-role.controller.ts index 070bbe0..a267d15 100644 --- a/src/mockModules/mock-role/mock-role.controller.ts +++ b/src/mockModules/mock-role/mock-role.controller.ts @@ -11,7 +11,13 @@ import { Res, } from "@nestjs/common"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; -import { CreateMockRoleDto, ResponseAddCompetencyToRoleDto, ResponseMockRoleDto, UpdateMockRoleDto } from "./dto"; +import { + AddCompetencyDto, + CreateMockRoleDto, + ResponseAddCompetencyToRoleDto, + ResponseMockRoleDto, + UpdateMockRoleDto, +} from "./dto"; import { MockRoleService } from "./mock-role.service"; import { CreateCompetencyDto } from "../mock-competency/dto"; @@ -39,7 +45,7 @@ export class MockRoleController { @Get() @ApiOperation({ summary: "fetch all mock roles" }) @ApiResponse({ - status: HttpStatus.CREATED, + status: HttpStatus.OK, type: ResponseMockRoleDto, isArray: true, }) @@ -113,23 +119,24 @@ export class MockRoleController { @Post("addExistingCompetencyToRole/:id") @ApiOperation({ summary: "add competency to role" }) - @ApiResponse({ status: HttpStatus.CREATED, type: ResponseAddCompetencyToRoleDto }) + @ApiResponse({ + status: HttpStatus.CREATED, + type: ResponseAddCompetencyToRoleDto, + }) async addExistingCompetencyToRole( @Res() res, @Param("id", ParseIntPipe) id: number, - @Body() competency: { competencyId: number } + @Body() competency: AddCompetencyDto ) { try { const role = await this.roleService.addExistingCompetencyToRole( id, competency.competencyId ); - return res - .status(HttpStatus.OK) - .json({ - data: role, - message: `Successfully added competency with id #${competency.competencyId} to Role with id #${id}.`, - }); + return res.status(HttpStatus.OK).json({ + data: role, + message: `Successfully added competency with id #${competency.competencyId} to Role with id #${id}.`, + }); } catch (error) { return res .status(HttpStatus.INTERNAL_SERVER_ERROR) @@ -146,13 +153,14 @@ export class MockRoleController { @Body() competency: CreateCompetencyDto ) { try { - const connection = await this.roleService.addNewCompetencyToRole(id, competency); - return res - .status(HttpStatus.OK) - .json({ - data: connection, - message: `Successfully added competency with id # to Role with id #${id}.`, - }); + const connection = await this.roleService.addNewCompetencyToRole( + id, + competency + ); + return res.status(HttpStatus.OK).json({ + data: connection, + message: `Successfully added competency with id # to Role with id #${id}.`, + }); } catch (error) { return res .status(HttpStatus.INTERNAL_SERVER_ERROR) From 73cb507ccc353024999038212573c577cd896bd8 Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Fri, 13 Oct 2023 16:40:40 +0530 Subject: [PATCH 025/175] updated required fields --- prisma/schema.prisma | 2 +- .../mock-user/dto/create-mock-user.dto.ts | 8 ++++++++ src/mockModules/mock-user/mock-user.controller.ts | 15 ++++++++++----- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 54d122d..dafed47 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -194,5 +194,5 @@ model CompetencyToCompetencyLevel { enum UserRolesEnum { ADMIN - COMSUMER + CONSUMER } diff --git a/src/mockModules/mock-user/dto/create-mock-user.dto.ts b/src/mockModules/mock-user/dto/create-mock-user.dto.ts index 525e3a4..de477b7 100644 --- a/src/mockModules/mock-user/dto/create-mock-user.dto.ts +++ b/src/mockModules/mock-user/dto/create-mock-user.dto.ts @@ -1,3 +1,4 @@ +import { ApiProperty } from "@nestjs/swagger"; import { UserRolesEnum } from "@prisma/client"; import { IsDate, @@ -15,6 +16,7 @@ export class CreateMockUserDto { @IsNotEmpty() email: string; + @ApiProperty({ enum: UserRolesEnum, example: "CONSUMER" }) @IsEnum(UserRolesEnum, { each: true, always: true }) @IsNotEmpty() role: UserRolesEnum; @@ -52,3 +54,9 @@ export class CreateMockUserDto { @IsOptional() updatedAt?: Date; } + +export class AddRoleDto { + @IsNotEmpty() + @IsNumber() + roleId: number; +} diff --git a/src/mockModules/mock-user/mock-user.controller.ts b/src/mockModules/mock-user/mock-user.controller.ts index 1b91641..244bf66 100644 --- a/src/mockModules/mock-user/mock-user.controller.ts +++ b/src/mockModules/mock-user/mock-user.controller.ts @@ -11,7 +11,7 @@ import { ParseIntPipe, } from "@nestjs/common"; import { MockUserService } from "./mock-user.service"; -import { CreateMockUserDto } from "./dto/create-mock-user.dto"; +import { AddRoleDto, CreateMockUserDto } from "./dto/create-mock-user.dto"; import { UpdateMockUserDto } from "./dto/update-mock-user.dto"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { ResponseMockUserDto } from "./dto/response-mock-user.dto"; @@ -33,7 +33,7 @@ export class MockUserController { } catch (error) { return res .status(HttpStatus.INTERNAL_SERVER_ERROR) - .json({ message: error.meta.cause }); + .json({ message: error?.meta?.cause || `Failed to create user` }); } } @@ -114,7 +114,7 @@ export class MockUserController { @ApiResponse({ status: HttpStatus.OK, type: ResponseMockUserDto }) async addRoleToUser( @Param("id", ParseIntPipe) id: number, - @Body() { roleId }: any, + @Body() { roleId }: AddRoleDto, @Res() res ) { try { @@ -132,9 +132,14 @@ export class MockUserController { @Get("getcompetencyData/:id") @ApiOperation({ summary: "get mock user's competency data by id" }) @ApiResponse({ status: HttpStatus.OK, type: ResponseMockUserDto }) - async fetchAllCompetencyDataForUserById(@Param("id", ParseIntPipe) id: number, @Res() res) { + async fetchAllCompetencyDataForUserById( + @Param("id", ParseIntPipe) id: number, + @Res() res + ) { try { - const user = await this.mockUserService.fetchAllCompetencyDataForUserById(id); + const user = await this.mockUserService.fetchAllCompetencyDataForUserById( + id + ); return res .status(HttpStatus.OK) .json({ data: user, message: "Successfully done" }); From 71bad4b2fa75943c9e2ad6e7bd75877029243d34 Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Fri, 13 Oct 2023 16:42:26 +0530 Subject: [PATCH 026/175] updated required fields --- src/mockModules/mock-user/mock-user.controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mockModules/mock-user/mock-user.controller.ts b/src/mockModules/mock-user/mock-user.controller.ts index 244bf66..165fc99 100644 --- a/src/mockModules/mock-user/mock-user.controller.ts +++ b/src/mockModules/mock-user/mock-user.controller.ts @@ -114,11 +114,11 @@ export class MockUserController { @ApiResponse({ status: HttpStatus.OK, type: ResponseMockUserDto }) async addRoleToUser( @Param("id", ParseIntPipe) id: number, - @Body() { roleId }: AddRoleDto, + @Body() role: AddRoleDto, @Res() res ) { try { - const user = await this.mockUserService.addRoleToUser(id, roleId); + const user = await this.mockUserService.addRoleToUser(id, role.roleId); return res .status(HttpStatus.OK) .json({ data: user, message: "Successfully done" }); From 85378ad4b4452621165fec4afe67a7264af6048e Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Fri, 13 Oct 2023 19:13:38 +0530 Subject: [PATCH 027/175] Changed "Team" to "Department". Added Many to One ralation from "CompetencyLevel" to "Level". --- prisma/schema.prisma | 25 +++++----- .../dto/create-competency-level.dto.ts | 6 ++- .../dto/create-mock-department.dto.ts} | 2 +- .../dto/response-mock-department.dto.ts} | 2 +- .../dto/update-mock-department.dto.ts | 4 ++ .../mock-department.controller.ts} | 39 ++++++++-------- .../mock-department/mock-department.module.ts | 9 ++++ .../mock-department.service.ts | 46 +++++++++++++++++++ .../mock-team/dto/update-mock-team.dto.ts | 4 -- src/mockModules/mock-team/mock-team.module.ts | 10 ---- .../mock-team/mock-team.service.ts | 46 ------------------- .../mock-user/dto/create-mock-user.dto.ts | 2 +- .../mock-user/dto/response-mock-user.dto.ts | 14 +++--- .../mock-user/mock-user.service.ts | 22 ++++++--- src/mockModules/mock.module.ts | 6 +-- 15 files changed, 127 insertions(+), 110 deletions(-) rename src/mockModules/{mock-team/dto/create-mock-team.dto.ts => mock-department/dto/create-mock-department.dto.ts} (84%) rename src/mockModules/{mock-team/dto/response-mock-team.dto.ts => mock-department/dto/response-mock-department.dto.ts} (69%) create mode 100644 src/mockModules/mock-department/dto/update-mock-department.dto.ts rename src/mockModules/{mock-team/mock-team.controller.ts => mock-department/mock-department.controller.ts} (54%) create mode 100644 src/mockModules/mock-department/mock-department.module.ts create mode 100644 src/mockModules/mock-department/mock-department.service.ts delete mode 100644 src/mockModules/mock-team/dto/update-mock-team.dto.ts delete mode 100644 src/mockModules/mock-team/mock-team.module.ts delete mode 100644 src/mockModules/mock-team/mock-team.service.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index dafed47..ce70b78 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -14,7 +14,7 @@ datasource db { model UserMetadata { id Int @id @default(autoincrement()) userId Int @unique() - teamId Int + departmentId Int isNewEmployee Boolean createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@ -119,22 +119,23 @@ model User { Roles Role[] Level Level? @relation(fields: [levelId], references: [id]) levelId Int? - Team Team? @relation(fields: [teamId], references: [id]) - teamId Int? + Department Department? @relation(fields: [departmentId], references: [id]) + departmentId Int? @@map("users") } model Level { - id Int @id @default(autoincrement()) - label String - description String? - users User[] + id Int @id @default(autoincrement()) + label String + description String? + users User[] + CompetencyLevel CompetencyLevel[] } -model Team { +model Department { id Int @id @default(autoincrement()) - name String + name String @unique description String? users User[] } @@ -153,7 +154,7 @@ model Role { model Competency { id Int @id @default(autoincrement()) - name String + name String @unique description String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@ -174,8 +175,10 @@ model RoleToCompetency { model CompetencyLevel { id Int @id @default(autoincrement()) - name String + name String @unique description String? + levelId Int + Level Level @relation(fields: [levelId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt competencies CompetencyToCompetencyLevel[] diff --git a/src/mockModules/mock-competency-level/dto/create-competency-level.dto.ts b/src/mockModules/mock-competency-level/dto/create-competency-level.dto.ts index ea32163..2c34399 100644 --- a/src/mockModules/mock-competency-level/dto/create-competency-level.dto.ts +++ b/src/mockModules/mock-competency-level/dto/create-competency-level.dto.ts @@ -1,5 +1,5 @@ -import { IsNotEmpty, IsOptional, IsString } from "class-validator"; +import { IsNotEmpty, IsNumber, IsOptional, IsString } from "class-validator"; export class CreateCompetencyLevelDto { @IsString() @@ -10,4 +10,8 @@ export class CreateCompetencyLevelDto { @IsString() @IsNotEmpty() description?: string; + + @IsNumber() + @IsNotEmpty() + levelId: number; } \ No newline at end of file diff --git a/src/mockModules/mock-team/dto/create-mock-team.dto.ts b/src/mockModules/mock-department/dto/create-mock-department.dto.ts similarity index 84% rename from src/mockModules/mock-team/dto/create-mock-team.dto.ts rename to src/mockModules/mock-department/dto/create-mock-department.dto.ts index 3c4a649..45172ee 100644 --- a/src/mockModules/mock-team/dto/create-mock-team.dto.ts +++ b/src/mockModules/mock-department/dto/create-mock-department.dto.ts @@ -1,6 +1,6 @@ import { IsNotEmpty, IsOptional, IsString } from "class-validator"; -export class CreateMockTeamDto { +export class CreateDepartmentDto { @IsString() @IsNotEmpty() name: string; diff --git a/src/mockModules/mock-team/dto/response-mock-team.dto.ts b/src/mockModules/mock-department/dto/response-mock-department.dto.ts similarity index 69% rename from src/mockModules/mock-team/dto/response-mock-team.dto.ts rename to src/mockModules/mock-department/dto/response-mock-department.dto.ts index 75bb09e..9afdfcd 100644 --- a/src/mockModules/mock-team/dto/response-mock-team.dto.ts +++ b/src/mockModules/mock-department/dto/response-mock-department.dto.ts @@ -1,4 +1,4 @@ -export class ResponseMockTeamDto { +export class ResponseDepartmentDto { readonly id: number; readonly name: string; readonly description?: string; diff --git a/src/mockModules/mock-department/dto/update-mock-department.dto.ts b/src/mockModules/mock-department/dto/update-mock-department.dto.ts new file mode 100644 index 0000000..9210fec --- /dev/null +++ b/src/mockModules/mock-department/dto/update-mock-department.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateDepartmentDto } from './create-mock-department.dto'; + +export class UpdateDepartmentDto extends PartialType(CreateDepartmentDto) {} diff --git a/src/mockModules/mock-team/mock-team.controller.ts b/src/mockModules/mock-department/mock-department.controller.ts similarity index 54% rename from src/mockModules/mock-team/mock-team.controller.ts rename to src/mockModules/mock-department/mock-department.controller.ts index 66fc15e..4cb10fb 100644 --- a/src/mockModules/mock-team/mock-team.controller.ts +++ b/src/mockModules/mock-department/mock-department.controller.ts @@ -10,23 +10,23 @@ import { HttpStatus, ParseIntPipe, } from "@nestjs/common"; -import { MockTeamService } from "./mock-team.service"; -import { CreateMockTeamDto } from "./dto/create-mock-team.dto"; -import { UpdateMockTeamDto } from "./dto/update-mock-team.dto"; +import { MockDepartmentService } from "./mock-department.service"; +import { CreateDepartmentDto } from "./dto/create-mock-department.dto"; +import { UpdateDepartmentDto } from "./dto/update-mock-department.dto"; import { ApiTags } from "@nestjs/swagger"; -@Controller("team") -@ApiTags("mockFracService/team") -export class MockTeamController { - constructor(private readonly mockTeamService: MockTeamService) {} +@Controller("department") +@ApiTags("mockFracService/department") +export class MockDepartmentController { + constructor(private readonly mockDepartmentService: MockDepartmentService) {} @Post() - async create(@Body() createMockTeamDto: CreateMockTeamDto, @Res() res) { + async create(@Body() createDepartmentDto: CreateDepartmentDto, @Res() res) { try { - const team = await this.mockTeamService.create(createMockTeamDto); + const department = await this.mockDepartmentService.create(createDepartmentDto); return res .status(HttpStatus.OK) - .json({ data: team, message: "Successfully done" }); + .json({ data: department, message: "Successfully done" }); } catch (error) { return res .status(HttpStatus.INTERNAL_SERVER_ERROR) @@ -37,10 +37,10 @@ export class MockTeamController { @Get() async findAll(@Res() res) { try { - const team = await this.mockTeamService.findAll(); + const department = await this.mockDepartmentService.findAll(); return res .status(HttpStatus.OK) - .json({ data: team, message: "Successfully done" }); + .json({ data: department, message: "Successfully done" }); } catch (error) { return res .status(HttpStatus.INTERNAL_SERVER_ERROR) @@ -51,11 +51,12 @@ export class MockTeamController { @Get(":id") async findOne(@Param("id", ParseIntPipe) id: number, @Res() res) { try { - const team = await this.mockTeamService.findOne(id); + const department = await this.mockDepartmentService.findOne(id); return res .status(HttpStatus.OK) - .json({ data: team, message: "Successfully done" }); + .json({ data: department, message: "Successfully done" }); } catch (error) { + return res .status(HttpStatus.INTERNAL_SERVER_ERROR) .json({ message: error.message }); @@ -65,14 +66,14 @@ export class MockTeamController { @Patch(":id") async update( @Param("id", ParseIntPipe) id: number, - @Body() updateMockTeamDto: UpdateMockTeamDto, + @Body() updateDepartmentDto: UpdateDepartmentDto, @Res() res ) { try { - const team = await this.mockTeamService.update(id, updateMockTeamDto); + const department = await this.mockDepartmentService.update(id, updateDepartmentDto); return res .status(HttpStatus.OK) - .json({ data: team, message: "Successfully done" }); + .json({ data: department, message: "Successfully done" }); } catch (error) { return res .status(HttpStatus.INTERNAL_SERVER_ERROR) @@ -83,10 +84,10 @@ export class MockTeamController { @Delete(":id") async remove(@Param("id", ParseIntPipe) id: number, @Res() res) { try { - const team = await this.mockTeamService.remove(id); + const department = await this.mockDepartmentService.remove(id); return res .status(HttpStatus.OK) - .json({ data: team, message: "Successfully done" }); + .json({ data: department, message: "Successfully done" }); } catch (error) { return res .status(HttpStatus.INTERNAL_SERVER_ERROR) diff --git a/src/mockModules/mock-department/mock-department.module.ts b/src/mockModules/mock-department/mock-department.module.ts new file mode 100644 index 0000000..9a7d4f1 --- /dev/null +++ b/src/mockModules/mock-department/mock-department.module.ts @@ -0,0 +1,9 @@ +import { Module } from "@nestjs/common"; +import { MockDepartmentService } from "./mock-department.service"; +import { MockDepartmentController } from "./mock-department.controller"; + +@Module({ + controllers: [MockDepartmentController], + providers: [MockDepartmentService], +}) +export class MockDepartmentModule {} diff --git a/src/mockModules/mock-department/mock-department.service.ts b/src/mockModules/mock-department/mock-department.service.ts new file mode 100644 index 0000000..848ad05 --- /dev/null +++ b/src/mockModules/mock-department/mock-department.service.ts @@ -0,0 +1,46 @@ +import { Injectable, NotFoundException } from "@nestjs/common"; +import { CreateDepartmentDto } from "./dto/create-mock-department.dto"; +import { UpdateDepartmentDto } from "./dto/update-mock-department.dto"; +import { PrismaService } from "src/prisma/prisma.service"; + +@Injectable() +export class MockDepartmentService { + constructor(private prisma: PrismaService) {} + + public async create(createDepartmentDto: CreateDepartmentDto) { + return await this.prisma.department.create({ + data: createDepartmentDto, + }); + } + + public async findAll() { + return await this.prisma.department.findMany(); + } + + public async findOne(id: number) { + const department = await this.prisma.department.findUnique({ + where: { + id, + }, + }); + if (!department) throw new NotFoundException(`Department with id #${id} not found`); + return department; + } + + public async update(id: number, updateDepartmentDto: UpdateDepartmentDto) { + return await this.prisma.department.update({ + where: { + id, + }, + data: updateDepartmentDto, + }); + } + + public async remove(id: number) { + return await this.prisma.department.delete({ + where: { + id, + }, + }); + } +} diff --git a/src/mockModules/mock-team/dto/update-mock-team.dto.ts b/src/mockModules/mock-team/dto/update-mock-team.dto.ts deleted file mode 100644 index d93f93f..0000000 --- a/src/mockModules/mock-team/dto/update-mock-team.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { PartialType } from '@nestjs/mapped-types'; -import { CreateMockTeamDto } from './create-mock-team.dto'; - -export class UpdateMockTeamDto extends PartialType(CreateMockTeamDto) {} diff --git a/src/mockModules/mock-team/mock-team.module.ts b/src/mockModules/mock-team/mock-team.module.ts deleted file mode 100644 index 3375439..0000000 --- a/src/mockModules/mock-team/mock-team.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Module } from "@nestjs/common"; -import { MockTeamService } from "./mock-team.service"; -import { MockTeamController } from "./mock-team.controller"; -import { PrismaService } from "src/prisma/prisma.service"; - -@Module({ - controllers: [MockTeamController], - providers: [MockTeamService], -}) -export class MockTeamModule {} diff --git a/src/mockModules/mock-team/mock-team.service.ts b/src/mockModules/mock-team/mock-team.service.ts deleted file mode 100644 index e03aa63..0000000 --- a/src/mockModules/mock-team/mock-team.service.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Injectable, NotFoundException } from "@nestjs/common"; -import { CreateMockTeamDto } from "./dto/create-mock-team.dto"; -import { UpdateMockTeamDto } from "./dto/update-mock-team.dto"; -import { PrismaService } from "src/prisma/prisma.service"; - -@Injectable() -export class MockTeamService { - constructor(private prisma: PrismaService) {} - - public async create(createMockTeamDto: CreateMockTeamDto) { - return await this.prisma.team.create({ - data: createMockTeamDto, - }); - } - - public async findAll() { - return await this.prisma.team.findMany(); - } - - public async findOne(id: number) { - const team = await this.prisma.team.findUnique({ - where: { - id, - }, - }); - if (!team) throw new NotFoundException(`Team with id #${id} not found`); - return team; - } - - public async update(id: number, updateMockTeamDto: UpdateMockTeamDto) { - return await this.prisma.team.update({ - where: { - id, - }, - data: updateMockTeamDto, - }); - } - - public async remove(id: number) { - return await this.prisma.team.delete({ - where: { - id, - }, - }); - } -} diff --git a/src/mockModules/mock-user/dto/create-mock-user.dto.ts b/src/mockModules/mock-user/dto/create-mock-user.dto.ts index de477b7..8516806 100644 --- a/src/mockModules/mock-user/dto/create-mock-user.dto.ts +++ b/src/mockModules/mock-user/dto/create-mock-user.dto.ts @@ -42,7 +42,7 @@ export class CreateMockUserDto { @IsOptional() @IsNotEmpty() @IsNumber() - teamId: number; + departmentId: number; // Optional creation date of the request. @IsDate() diff --git a/src/mockModules/mock-user/dto/response-mock-user.dto.ts b/src/mockModules/mock-user/dto/response-mock-user.dto.ts index 529afa1..33d017f 100644 --- a/src/mockModules/mock-user/dto/response-mock-user.dto.ts +++ b/src/mockModules/mock-user/dto/response-mock-user.dto.ts @@ -1,6 +1,6 @@ -import { ResponseCompetencyLevelDto } from "src/mockModules/mock-competency-level/dto"; import { ResponseMockRoleDto } from "src/mockModules/mock-role/dto"; -import { ResponseMockTeamDto } from "src/mockModules/mock-team/dto/response-mock-team.dto"; +import { ResponseDepartmentDto } from "src/mockModules/mock-department/dto/response-mock-department.dto"; +import { ResponseMockLevelDto } from "src/mockModules/mock-level/dto/response-mock-level.dto"; export class ResponseMockUserDto { readonly id: number; @@ -8,11 +8,11 @@ export class ResponseMockUserDto { readonly role: string; readonly userName: string; readonly profilePicture?: string; - readonly createdAt?: Date; - readonly updatedAt?: Date; readonly levelId?: number; - readonly teamId?: number; - readonly Level?: ResponseCompetencyLevelDto; - readonly Team?: ResponseMockTeamDto; + readonly Level?: ResponseMockLevelDto; + readonly departmentId?: number; + readonly Department?: ResponseDepartmentDto; readonly Roles?: ResponseMockRoleDto[]; + readonly createdAt?: Date; + readonly updatedAt?: Date; } diff --git a/src/mockModules/mock-user/mock-user.service.ts b/src/mockModules/mock-user/mock-user.service.ts index db95464..d2bffbb 100644 --- a/src/mockModules/mock-user/mock-user.service.ts +++ b/src/mockModules/mock-user/mock-user.service.ts @@ -8,11 +8,11 @@ export class MockUserService { constructor(private prisma: PrismaService) {} public async create(createMockUserDto: CreateMockUserDto) { - return await this.prisma.user.create({ + return await this.prisma.user.create({ data: createMockUserDto, - select:{ + select: { ...this.userSelectObj, - } + }, }); } @@ -91,7 +91,7 @@ export class MockUserService { label: true, }, }, - Team: { + Department: { select: { id: true, name: true, @@ -132,9 +132,19 @@ export class MockUserService { role: true, userName: true, profilePicture: true, + Level: { + select: { + id: true, + label: true, + }, + }, + Department: { + select: { + id: true, + name: true, + }, + }, createdAt: true, updatedAt: true, - levelId: true, - teamId: true, }; } diff --git a/src/mockModules/mock.module.ts b/src/mockModules/mock.module.ts index 6c383da..5e8852b 100644 --- a/src/mockModules/mock.module.ts +++ b/src/mockModules/mock.module.ts @@ -5,7 +5,7 @@ import { MockCompetencyLevelModule } from "./mock-competency-level/mock-competen import { RouterModule } from "@nestjs/core"; import { MockUserModule } from "./mock-user/mock-user.module"; import { MockLevelModule } from "./mock-level/mock-level.module"; -import { MockTeamModule } from "./mock-team/mock-team.module"; +import { MockDepartmentModule } from "./mock-department/mock-department.module"; @Module({ imports: [ @@ -14,14 +14,14 @@ import { MockTeamModule } from "./mock-team/mock-team.module"; MockCompetencyLevelModule, MockUserModule, MockLevelModule, - MockTeamModule, + MockDepartmentModule, RouterModule.register([ { path: "mockFracService/", module: MockRoleModule }, { path: "mockFracService/", module: MockCompetencyModule }, { path: "mockFracService/", module: MockCompetencyLevelModule }, { path: "mockFracService/", module: MockUserModule }, { path: "mockFracService/", module: MockLevelModule }, - { path: "mockFracService/", module: MockTeamModule }, + { path: "mockFracService/", module: MockDepartmentModule }, ]), ], }) From 28565300b57fc70351a9d224933c025638fbae71 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Tue, 17 Oct 2023 16:31:00 +0530 Subject: [PATCH 028/175] Schema Changes. --- prisma/schema.prisma | 113 ++++++++++++++++++++++++++++++------------- 1 file changed, 80 insertions(+), 33 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ce70b78..9ddfa83 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -12,12 +12,21 @@ datasource db { // Here goes models model UserMetadata { - id Int @id @default(autoincrement()) - userId Int @unique() - departmentId Int - isNewEmployee Boolean - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id Int @unique() @default(autoincrement()) + userId String @unique() @db.Uuid + isNewEmployee Boolean + departmentId Int + Department AdminDepartment @relation(fields: [departmentId], references: [departmentId]) + designation String? + dateOfJoining DateTime + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + SurveyForm SurveyForm[] + isAdmin Boolean + ResponseTrackersAsAssessee ResponseTracker[] @relation("AssesseeResponseTrackers") + ResponseTrackersAsAssessor ResponseTracker[] @relation("AssessorResponseTrackers") + + @@id([id, userId]) } model QuestionBank { @@ -26,41 +35,78 @@ model QuestionBank { updatedAt DateTime @updatedAt competencyId Int competencyLevelId Int + competencyLevelNumber Int question String @@unique([competencyLevelId, question]) @@map("question_bank") } -model SurveyConfig { - id Int @id @default(autoincrement()) - maxQuestions Int - startTime DateTime - endTime DateTime - surveyForm SurveyForm? +model AdminCompetency { + id Int @default(autoincrement()) + competencyId Int + name String + description String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + competencyLevels Json[] //{competencyLevelNumber, competencyLevelName} - @@map("survey_config") + @@id([id, competencyId]) + @@unique([id, competencyId]) + @@map("admin_competencies") +} + +model AdminDepartment { + id Int @default(autoincrement()) + departmentId Int @unique + name String + UserMetadata UserMetadata[] + + @@id([id, departmentId]) + @@map("admin_department") } -model SurveyParameters { - id Int @id @default(autoincrement()) +model SurveyConfig { + id Int @id @default(autoincrement()) onboardingTime Int onboardingTimeUnit TimeUnitsEnum - surveyCycle Int + //will be replaced with surveyCycle in phase two + startTime DateTime + endTime DateTime + departmentId Int @unique + SurveyParameters SurveyCycleParameters[] + + @@map("survey_config") +} + +model SurveyCycleParameters { + id Int @id @default(autoincrement()) + startTime DateTime + endTime DateTime + surveyForm SurveyForm[] + isActive Boolean + surveyConfigId Int? + SurveyConfig SurveyConfig? @relation(fields: [surveyConfigId], references: [id]) @@map("survey_parameters") } model SurveyForm { - id Int @id @default(autoincrement()) - userId Int - questionsJson Json - surveyConfigId Int @unique() // Add this field for the one-to-one relation - status SurveyStatusEnum - surveyConfig SurveyConfig @relation(fields: [surveyConfigId], references: [id]) - ResponseTracker ResponseTracker[] - SurveyScore SurveyScore[] - + id Int @id @default(autoincrement()) + userId String @db.Uuid + UserMetadata UserMetadata @relation(fields: [userId], references: [userId]) + questionsJson Json //{questionId: question} + surveyParameterId Int + surveyParameter SurveyCycleParameters @relation(fields: [surveyParameterId], references: [id]) + status SurveyStatusEnum + overallScore Int? + sunbirdCredentialIds String? + ResponseTracker ResponseTracker[] + SurveyScore SurveyScore[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@unique([userId, surveyParameterId]) @@map("survey_form") } @@ -68,10 +114,12 @@ model ResponseTracker { id Int @id @default(autoincrement()) surveyFormId Int surveyForm SurveyForm @relation(fields: [surveyFormId], references: [id]) - assesseeId Int - assessorId Int - responseJson Json? + assesseeId String @db.Uuid + assessorId String @db.Uuid + responseJson Json[] //{questionId: answer(yes, no and don't know)} status ResponseTrackerStatusEnum + Assessee UserMetadata @relation("AssesseeResponseTrackers", fields: [assesseeId], references: [userId]) + Assessor UserMetadata @relation("AssessorResponseTrackers", fields: [assessorId], references: [userId]) @@unique([surveyFormId, assesseeId, assessorId]) @@map("response_tracker") @@ -79,14 +127,13 @@ model ResponseTracker { model SurveyScore { id Int @id @default(autoincrement()) - userId Int surveyFormId Int surveyForm SurveyForm @relation(fields: [surveyFormId], references: [id]) - roleId Int? competencyId Int competencyLevelId Int - score Int + score Float + @@unique([surveyFormId, competencyId, competencyLevelId]) @@map("survey_scores") } @@ -108,7 +155,7 @@ enum TimeUnitsEnum { //Mock Database Models - To be removed after the app is integrated with User Service and FRAC Based Competency Tagger. model User { - id Int @id @default(autoincrement()) + id String @id @default(uuid()) email String @unique role UserRolesEnum userName String @@ -178,7 +225,7 @@ model CompetencyLevel { name String @unique description String? levelId Int - Level Level @relation(fields: [levelId], references: [id]) + Level Level @relation(fields: [levelId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt competencies CompetencyToCompetencyLevel[] From d8dd64efc35970fc2843e963c66113300a9f8a03 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Tue, 17 Oct 2023 17:40:19 +0530 Subject: [PATCH 029/175] Relation name correction in SurveyForm Model. --- prisma/schema.prisma | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 9ddfa83..83bec96 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -96,8 +96,8 @@ model SurveyForm { userId String @db.Uuid UserMetadata UserMetadata @relation(fields: [userId], references: [userId]) questionsJson Json //{questionId: question} - surveyParameterId Int - surveyParameter SurveyCycleParameters @relation(fields: [surveyParameterId], references: [id]) + surveyCycleParameterId Int + surveyParameter SurveyCycleParameters @relation(fields: [surveyCycleParameterId], references: [id]) status SurveyStatusEnum overallScore Int? sunbirdCredentialIds String? @@ -106,7 +106,7 @@ model SurveyForm { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - @@unique([userId, surveyParameterId]) + @@unique([userId, surveyCycleParameterId]) @@map("survey_form") } From 8d6bb167a3c023fcb433e032214c3de7b9d31954 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 18 Oct 2023 10:48:59 +0530 Subject: [PATCH 030/175] Schema update for SurveyCycleParameter. --- prisma/schema.prisma | 48 +++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 83bec96..4e270e3 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -30,13 +30,13 @@ model UserMetadata { } model QuestionBank { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - competencyId Int - competencyLevelId Int + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + competencyId Int + competencyLevelId Int competencyLevelNumber Int - question String + question String @@unique([competencyLevelId, question]) @@map("question_bank") @@ -67,19 +67,19 @@ model AdminDepartment { } model SurveyConfig { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) onboardingTime Int onboardingTimeUnit TimeUnitsEnum //will be replaced with surveyCycle in phase two startTime DateTime endTime DateTime - departmentId Int @unique - SurveyParameters SurveyCycleParameters[] + departmentId Int @unique + SurveyParameters SurveyCycleParameter[] @@map("survey_config") } -model SurveyCycleParameters { +model SurveyCycleParameter { id Int @id @default(autoincrement()) startTime DateTime endTime DateTime @@ -87,24 +87,26 @@ model SurveyCycleParameters { isActive Boolean surveyConfigId Int? SurveyConfig SurveyConfig? @relation(fields: [surveyConfigId], references: [id]) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt @@map("survey_parameters") } model SurveyForm { - id Int @id @default(autoincrement()) - userId String @db.Uuid - UserMetadata UserMetadata @relation(fields: [userId], references: [userId]) - questionsJson Json //{questionId: question} - surveyCycleParameterId Int - surveyParameter SurveyCycleParameters @relation(fields: [surveyCycleParameterId], references: [id]) - status SurveyStatusEnum - overallScore Int? - sunbirdCredentialIds String? - ResponseTracker ResponseTracker[] - SurveyScore SurveyScore[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id Int @id @default(autoincrement()) + userId String @db.Uuid + UserMetadata UserMetadata @relation(fields: [userId], references: [userId]) + questionsJson Json //{questionId: question} + surveyCycleParameterId Int + surveyCycleParameter SurveyCycleParameter @relation(fields: [surveyCycleParameterId], references: [id]) + status SurveyStatusEnum + overallScore Int? + sunbirdCredentialIds String? + ResponseTracker ResponseTracker[] + SurveyScore SurveyScore[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt @@unique([userId, surveyCycleParameterId]) @@map("survey_form") From bdd44d31478a46c4045b72e3f85bfff44258d5cf Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 18 Oct 2023 11:37:08 +0530 Subject: [PATCH 031/175] Survey Form Module update according to the changes in schema. --- src/survey-form/dto/create-survey-form.dto.ts | 47 +++++++++++-------- .../dto/response-survey-form.dto.ts | 20 ++++---- src/survey-form/dto/update-survey-form.dto.ts | 12 ++++- 3 files changed, 49 insertions(+), 30 deletions(-) diff --git a/src/survey-form/dto/create-survey-form.dto.ts b/src/survey-form/dto/create-survey-form.dto.ts index 0a97a16..258b50e 100644 --- a/src/survey-form/dto/create-survey-form.dto.ts +++ b/src/survey-form/dto/create-survey-form.dto.ts @@ -1,24 +1,31 @@ import { ApiProperty } from "@nestjs/swagger"; import { SurveyStatusEnum } from "@prisma/client"; -import { IsEnum, IsNotEmpty, IsNumber, IsObject } from "class-validator"; +import { + IsArray, + IsEnum, + IsNotEmpty, + IsNumber, + IsOptional, + IsString, +} from "class-validator"; -export class CreateSurveyFormDto{ - @ApiProperty({ type: "integer", example: 1 }) - @IsNumber() - @IsNotEmpty() - userId: number; - - @IsNumber() - @IsNotEmpty() - surveyConfigId: number; - - @ApiProperty({ enum: SurveyStatusEnum, example: "CREATED" }) - @IsEnum(SurveyStatusEnum) - @IsNotEmpty() - status: SurveyStatusEnum; +export class CreateSurveyFormDto { + @ApiProperty({ type: "integer", example: 1 }) + @IsNumber() + @IsNotEmpty() + userId: string; - @ApiProperty({ type: "object", example: { key: "value" } }) - @IsObject() - @IsNotEmpty() - questionsJson: Record; -} \ No newline at end of file + @IsNumber() + @IsNotEmpty() + surveyCycleParameterId: number; + + @ApiProperty({ enum: SurveyStatusEnum, example: "CREATED" }) + @IsEnum(SurveyStatusEnum) + @IsNotEmpty() + status: SurveyStatusEnum; + + @ApiProperty({ type: "array", example: [{ number: "value" }] }) + @IsArray() + @IsNotEmpty() + questionsJson: Record[]; +} diff --git a/src/survey-form/dto/response-survey-form.dto.ts b/src/survey-form/dto/response-survey-form.dto.ts index 67ce658..6e7b32d 100644 --- a/src/survey-form/dto/response-survey-form.dto.ts +++ b/src/survey-form/dto/response-survey-form.dto.ts @@ -1,15 +1,17 @@ import { SurveyStatusEnum } from "@prisma/client"; import { JsonValue } from "@prisma/client/runtime/library"; -export class ResponseSurveyFormDto{ - readonly id: number; - readonly userId: number; - readonly surveyConfigId: number; - readonly status: SurveyStatusEnum; - readonly questionsJson?: JsonValue; +export class ResponseSurveyFormDto { + readonly id: number; + readonly userId: number; + readonly surveyConfigId: number; + readonly status: SurveyStatusEnum; + readonly questionsJson: JsonValue; + readonly overallScore: number; + readonly sunbirdCredentialIds: string; } export class SurveyFormResponse { - readonly data?: ResponseSurveyFormDto; - readonly message: string; -} \ No newline at end of file + readonly data?: ResponseSurveyFormDto; + readonly message: string; +} diff --git a/src/survey-form/dto/update-survey-form.dto.ts b/src/survey-form/dto/update-survey-form.dto.ts index 77cf398..8b26905 100644 --- a/src/survey-form/dto/update-survey-form.dto.ts +++ b/src/survey-form/dto/update-survey-form.dto.ts @@ -1,10 +1,20 @@ import { ApiProperty } from "@nestjs/swagger"; import { SurveyStatusEnum } from "@prisma/client"; -import { IsEnum, IsNotEmpty } from "class-validator"; +import { IsEnum, IsNotEmpty, IsNumber, IsOptional, IsString } from "class-validator"; export class UpdateSurveyFormDto { @ApiProperty({ enum: SurveyStatusEnum, example: "CREATED" }) @IsEnum(SurveyStatusEnum) @IsNotEmpty() status: SurveyStatusEnum; + + @IsNumber() + @IsOptional() + @IsNotEmpty() + overallScore: number; + + @IsString() + @IsOptional() + @IsNotEmpty() + sunbirdCredentialIds: string; } From 4e698744d27a7fd9810cd1cdd12a4c1393fdd08a Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Wed, 18 Oct 2023 11:44:04 +0530 Subject: [PATCH 032/175] fixes for the question bank module as per prisma schema changes. --- src/question-bank/dto/create-question-bank.dto.ts | 12 +++++++++++- src/question-bank/dto/response-question-bank.dto.ts | 1 + src/question-bank/question-bank.service.ts | 2 ++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/question-bank/dto/create-question-bank.dto.ts b/src/question-bank/dto/create-question-bank.dto.ts index 263e875..b2b93da 100644 --- a/src/question-bank/dto/create-question-bank.dto.ts +++ b/src/question-bank/dto/create-question-bank.dto.ts @@ -22,6 +22,11 @@ export class CreateQuestionBankDto { @IsNotEmpty() @IsString() question: string; + + // Competency Level number for the question bank + @IsNotEmpty() + @IsInt() + competencyLevelNumber: number; } // Question bank filter @@ -36,6 +41,11 @@ export class QuestionBankFilterDto { @IsInt() competencyLevelId?: number; + // Optional competencyLevelNumber filter, validate that it's valid competency Level number or not + @IsOptional() + @IsInt() + competencyLevelNumber?: number; + // Optional limit for pagination, validate that it's an integer. @IsOptional() @IsInt() @@ -45,7 +55,7 @@ export class QuestionBankFilterDto { @IsOptional() @IsInt() offset?: number; - + // Optional field to specify the order of results, validate that it's a string. @IsOptional() @IsString() diff --git a/src/question-bank/dto/response-question-bank.dto.ts b/src/question-bank/dto/response-question-bank.dto.ts index 0435aaa..45f1f39 100644 --- a/src/question-bank/dto/response-question-bank.dto.ts +++ b/src/question-bank/dto/response-question-bank.dto.ts @@ -1,6 +1,7 @@ export class ResponseQuestionBankDto { readonly competencyId: number; readonly competencyLevelId: number; + readonly competencyLevelNumber: number; readonly question: string; readonly createdAt: Date; readonly updatedAt: Date; diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index 7028100..c33d464 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -46,6 +46,7 @@ export class QuestionBankService { const { competencyId, competencyLevelId, + competencyLevelNumber, limit = 10, offset = 0, orderBy, @@ -54,6 +55,7 @@ export class QuestionBankService { where: { competencyId: competencyId ?? undefined, // Optional compentencyId filter competencyLevelId: competencyLevelId ?? undefined, // Optional competencyLevelId filter + competencyLevelNumber: competencyLevelNumber ?? undefined, // Optional competencyLevelNumber }, orderBy: { [orderBy || "createdAt"]: "asc", // Default sorting by createdAt From 2330c3d39808959bf323671e0e0b3519dec61bde Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 18 Oct 2023 13:43:50 +0530 Subject: [PATCH 033/175] Detatched Role to User relation, added designation table for mock. Made changes to MockUser accordingly. --- prisma/schema.prisma | 24 ++++--- .../mock-user/dto/create-mock-user.dto.ts | 10 ++- .../mock-user/dto/response-mock-user.dto.ts | 5 +- .../mock-user/mock-user.controller.ts | 50 ++----------- .../mock-user/mock-user.service.ts | 72 +------------------ 5 files changed, 29 insertions(+), 132 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 4e270e3..a649e32 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -165,7 +165,7 @@ model User { profilePicture String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - Roles Role[] + designation String Level Level? @relation(fields: [levelId], references: [id]) levelId Int? Department Department? @relation(fields: [departmentId], references: [id]) @@ -189,14 +189,22 @@ model Department { users User[] } +model Designation { + id Int @id @default(autoincrement()) + name String @unique + description String? + Roles Role[] +} + model Role { - id Int @id @default(autoincrement()) - name String @unique - description String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - users User[] - competencies RoleToCompetency[] + id Int @id @default(autoincrement()) + name String @unique + description String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + competencies RoleToCompetency[] + Designation Designation? @relation(fields: [designationId], references: [id]) + designationId Int? @@map("roles") } diff --git a/src/mockModules/mock-user/dto/create-mock-user.dto.ts b/src/mockModules/mock-user/dto/create-mock-user.dto.ts index 8516806..f81b22a 100644 --- a/src/mockModules/mock-user/dto/create-mock-user.dto.ts +++ b/src/mockModules/mock-user/dto/create-mock-user.dto.ts @@ -44,6 +44,10 @@ export class CreateMockUserDto { @IsNumber() departmentId: number; + @IsNotEmpty() + @IsString() + designation: string; + // Optional creation date of the request. @IsDate() @IsOptional() @@ -54,9 +58,3 @@ export class CreateMockUserDto { @IsOptional() updatedAt?: Date; } - -export class AddRoleDto { - @IsNotEmpty() - @IsNumber() - roleId: number; -} diff --git a/src/mockModules/mock-user/dto/response-mock-user.dto.ts b/src/mockModules/mock-user/dto/response-mock-user.dto.ts index 33d017f..1cfcfcd 100644 --- a/src/mockModules/mock-user/dto/response-mock-user.dto.ts +++ b/src/mockModules/mock-user/dto/response-mock-user.dto.ts @@ -1,9 +1,8 @@ -import { ResponseMockRoleDto } from "src/mockModules/mock-role/dto"; import { ResponseDepartmentDto } from "src/mockModules/mock-department/dto/response-mock-department.dto"; import { ResponseMockLevelDto } from "src/mockModules/mock-level/dto/response-mock-level.dto"; export class ResponseMockUserDto { - readonly id: number; + readonly id: string; readonly email: string; readonly role: string; readonly userName: string; @@ -12,7 +11,7 @@ export class ResponseMockUserDto { readonly Level?: ResponseMockLevelDto; readonly departmentId?: number; readonly Department?: ResponseDepartmentDto; - readonly Roles?: ResponseMockRoleDto[]; + readonly designation: String; readonly createdAt?: Date; readonly updatedAt?: Date; } diff --git a/src/mockModules/mock-user/mock-user.controller.ts b/src/mockModules/mock-user/mock-user.controller.ts index 165fc99..42af0d4 100644 --- a/src/mockModules/mock-user/mock-user.controller.ts +++ b/src/mockModules/mock-user/mock-user.controller.ts @@ -8,10 +8,9 @@ import { Delete, HttpStatus, Res, - ParseIntPipe, } from "@nestjs/common"; import { MockUserService } from "./mock-user.service"; -import { AddRoleDto, CreateMockUserDto } from "./dto/create-mock-user.dto"; +import { CreateMockUserDto } from "./dto/create-mock-user.dto"; import { UpdateMockUserDto } from "./dto/update-mock-user.dto"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { ResponseMockUserDto } from "./dto/response-mock-user.dto"; @@ -60,7 +59,7 @@ export class MockUserController { @Get(":id") @ApiOperation({ summary: "get mock user by id" }) @ApiResponse({ status: HttpStatus.CREATED, type: ResponseMockUserDto }) - async findOne(@Param("id", ParseIntPipe) id: number, @Res() res) { + async findOne(@Param("id") id: string, @Res() res) { try { const user = await this.mockUserService.findOne(id); return res @@ -77,7 +76,7 @@ export class MockUserController { @ApiOperation({ summary: "update mock user by id" }) @ApiResponse({ status: HttpStatus.CREATED, type: ResponseMockUserDto }) async update( - @Param("id", ParseIntPipe) id: number, + @Param("id") id: string, @Body() updateMockUserDto: UpdateMockUserDto, @Res() res ) { @@ -96,7 +95,7 @@ export class MockUserController { @Delete(":id") @ApiOperation({ summary: "delete mock user by id" }) @ApiResponse({ status: HttpStatus.CREATED, type: ResponseMockUserDto }) - async remove(@Param("id", ParseIntPipe) id: number, @Res() res) { + async remove(@Param("id") id: string, @Res() res) { try { const user = await this.mockUserService.remove(id); return res @@ -108,45 +107,4 @@ export class MockUserController { .json({ message: error.meta.cause }); } } - - @Patch("addRoleToUser/:id") - @ApiOperation({ summary: "add role to user by id" }) - @ApiResponse({ status: HttpStatus.OK, type: ResponseMockUserDto }) - async addRoleToUser( - @Param("id", ParseIntPipe) id: number, - @Body() role: AddRoleDto, - @Res() res - ) { - try { - const user = await this.mockUserService.addRoleToUser(id, role.roleId); - return res - .status(HttpStatus.OK) - .json({ data: user, message: "Successfully done" }); - } catch (error) { - return res - .status(HttpStatus.INTERNAL_SERVER_ERROR) - .json({ message: error.meta.cause }); - } - } - - @Get("getcompetencyData/:id") - @ApiOperation({ summary: "get mock user's competency data by id" }) - @ApiResponse({ status: HttpStatus.OK, type: ResponseMockUserDto }) - async fetchAllCompetencyDataForUserById( - @Param("id", ParseIntPipe) id: number, - @Res() res - ) { - try { - const user = await this.mockUserService.fetchAllCompetencyDataForUserById( - id - ); - return res - .status(HttpStatus.OK) - .json({ data: user, message: "Successfully done" }); - } catch (error) { - return res - .status(HttpStatus.INTERNAL_SERVER_ERROR) - .json({ message: error.message }); - } - } } diff --git a/src/mockModules/mock-user/mock-user.service.ts b/src/mockModules/mock-user/mock-user.service.ts index d2bffbb..983d530 100644 --- a/src/mockModules/mock-user/mock-user.service.ts +++ b/src/mockModules/mock-user/mock-user.service.ts @@ -24,7 +24,7 @@ export class MockUserService { }); } - public async findOne(id: number) { + public async findOne(id: string) { const user = await this.prisma.user.findUnique({ where: { id, @@ -37,7 +37,7 @@ export class MockUserService { return user; } - public async update(id: number, updateMockUserDto: UpdateMockUserDto) { + public async update(id: string, updateMockUserDto: UpdateMockUserDto) { return await this.prisma.user.update({ where: { id, @@ -49,7 +49,7 @@ export class MockUserService { }); } - public async remove(id: number) { + public async remove(id: string) { return await this.prisma.user.delete({ where: { id, @@ -60,72 +60,6 @@ export class MockUserService { }); } - public async addRoleToUser(userId: number, roleId: number) { - return await this.prisma.user.update({ - where: { - id: userId, - }, - select: { - ...this.userSelectObj, - }, - data: { - Roles: { - connect: { id: roleId }, - }, - }, - }); - } - - public async fetchAllCompetencyDataForUserById(id: number) { - return await this.prisma.user.findUnique({ - where: { - id, - }, - select: { - id: true, - userName: true, - role: true, - Level: { - select: { - id: true, - label: true, - }, - }, - Department: { - select: { - id: true, - name: true, - }, - }, - Roles: { - select: { - id: true, - name: true, - competencies: { - select: { - competency: { - select: { - id: true, - name: true, - competencyLevels: { - select: { - competencyLevel: { - select: { - id: true, - name: true, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }); - } userSelectObj = { id: true, email: true, From 8dd8e6002c7b1971d34337edb67694839aa12d03 Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Wed, 18 Oct 2023 13:56:28 +0530 Subject: [PATCH 034/175] fixes for the survey score module as per prisma schema changes. --- .../dto/create-survey-score.dto.ts | 7 ---- .../dto/response-survey-score.dto.ts | 2 -- src/survey-score/survey-score.controller.ts | 36 ------------------- src/survey-score/survey-score.service.ts | 11 ------ 4 files changed, 56 deletions(-) diff --git a/src/survey-score/dto/create-survey-score.dto.ts b/src/survey-score/dto/create-survey-score.dto.ts index 86ad827..67b10ad 100644 --- a/src/survey-score/dto/create-survey-score.dto.ts +++ b/src/survey-score/dto/create-survey-score.dto.ts @@ -1,18 +1,11 @@ import { IsNotEmpty, IsNumber } from "class-validator"; export class CreateSurveyScoreDto { - @IsNotEmpty() - @IsNumber() - userId: number; @IsNotEmpty() @IsNumber() surveyFormId: number; - @IsNotEmpty() - @IsNumber() - roleId: number; - @IsNotEmpty() @IsNumber() competencyId: number; diff --git a/src/survey-score/dto/response-survey-score.dto.ts b/src/survey-score/dto/response-survey-score.dto.ts index 17ecb38..0a47bab 100644 --- a/src/survey-score/dto/response-survey-score.dto.ts +++ b/src/survey-score/dto/response-survey-score.dto.ts @@ -1,8 +1,6 @@ export class SurveyScoreResponse { readonly id: number; - readonly userId: number; readonly surveyFormId: number; - readonly roleId: number; readonly competencyId: number; readonly competencyLevelId: number; readonly score: number; diff --git a/src/survey-score/survey-score.controller.ts b/src/survey-score/survey-score.controller.ts index bcd06c4..402030c 100644 --- a/src/survey-score/survey-score.controller.ts +++ b/src/survey-score/survey-score.controller.ts @@ -130,42 +130,6 @@ export class SurveyScoreController { } } - @Get("user/:userId") - @ApiOperation({ summary: "get survey score by user Id" }) - @ApiResponse({ status: HttpStatus.OK, type: SurveyScoreMultipleResponseDto }) - async findByUserId( - @Res() res, - @Param("userId", ParseIntPipe) userId: number - ): Promise { - try { - this.logger.log(`Initiated fetching survey score with userId #${userId}`); - - const surveyScore = await this.surveyScoreService.findByUserId(userId); - - this.logger.log( - `Successfully fetched survey score with userId #${userId}` - ); - - return res.status(HttpStatus.OK).json({ - data: surveyScore, - message: `Successfully fetched survey score with userId #${userId}`, - }); - } catch (error) { - this.logger.error( - `Failed to fetch survey score for userId #${userId}`, - error - ); - - const { errorMessage, statusCode } = - getPrismaErrorStatusAndMessage(error); - - return res.status(statusCode).json({ - statusCode, - message: - errorMessage || `Failed to fetch survey score for userId #${userId}`, - }); - } - } @Get(":id") @ApiOperation({ summary: "get survey score by id" }) diff --git a/src/survey-score/survey-score.service.ts b/src/survey-score/survey-score.service.ts index f630aa7..0f13dc6 100644 --- a/src/survey-score/survey-score.service.ts +++ b/src/survey-score/survey-score.service.ts @@ -38,17 +38,6 @@ export class SurveyScoreService { return surveyScore; } - public async findByUserId(userId: number) { - const surveyScore = await this.prisma.surveyScore.findMany({ - where: { userId }, - }); - if (!surveyScore || surveyScore.length === 0) - throw new NotFoundException( - `Survey score for user id #${userId} not found` - ); - return surveyScore; - } - public async update(id: number, updateSurveyScoreDto: UpdateSurveyScoreDto) { return await this.prisma.surveyScore.update({ where: { id }, From ed17e68d515eef8f366e8f349f9637dbd8b19650 Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Wed, 18 Oct 2023 13:58:35 +0530 Subject: [PATCH 035/175] Survey config and survey parameter fixes --- prisma/schema.prisma | 16 ++-- src/app.module.ts | 4 +- .../mock-user/mock-user.controller.ts | 40 ++++---- .../dto/create-survey-config.dto.ts | 17 +++- .../dto/response-survey-config.dto.ts | 8 +- src/survey-config/survey-config.service.ts | 27 +++--- .../dto/create-survey-cycle-parameter.dto.ts | 36 +++++++ .../response-survey-cycle-parameter.dto.ts | 11 +++ .../dto/update-survey-cycle-parameter.dto.ts | 6 ++ .../survey-cycle-parameter.controller.ts} | 95 ++++++++++--------- .../survey-cycle-parameter.module.ts | 12 +++ .../survey-cycle-parameter.service.ts} | 39 ++++---- .../dto/create-survey-parameter.dto.ts | 20 ---- .../dto/response-survey-parameter.dto.ts | 8 -- .../dto/update-survey-parameter.dto.ts | 4 - .../survey-parameter.module.ts | 13 --- 16 files changed, 203 insertions(+), 153 deletions(-) create mode 100644 src/survey-cycle-parameter/dto/create-survey-cycle-parameter.dto.ts create mode 100644 src/survey-cycle-parameter/dto/response-survey-cycle-parameter.dto.ts create mode 100644 src/survey-cycle-parameter/dto/update-survey-cycle-parameter.dto.ts rename src/{survey-parameter/survey-parameter.controller.ts => survey-cycle-parameter/survey-cycle-parameter.controller.ts} (50%) create mode 100644 src/survey-cycle-parameter/survey-cycle-parameter.module.ts rename src/{survey-parameter/survey-parameter.service.ts => survey-cycle-parameter/survey-cycle-parameter.service.ts} (50%) delete mode 100644 src/survey-parameter/dto/create-survey-parameter.dto.ts delete mode 100644 src/survey-parameter/dto/response-survey-parameter.dto.ts delete mode 100644 src/survey-parameter/dto/update-survey-parameter.dto.ts delete mode 100644 src/survey-parameter/survey-parameter.module.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index a649e32..c958504 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -67,14 +67,14 @@ model AdminDepartment { } model SurveyConfig { - id Int @id @default(autoincrement()) - onboardingTime Int - onboardingTimeUnit TimeUnitsEnum + id Int @id @default(autoincrement()) + onboardingTime Int + onboardingTimeUnit TimeUnitsEnum //will be replaced with surveyCycle in phase two - startTime DateTime - endTime DateTime - departmentId Int @unique - SurveyParameters SurveyCycleParameter[] + startTime DateTime + endTime DateTime + departmentId Int @unique + SurveyCycleParameters SurveyCycleParameter[] @@map("survey_config") } @@ -90,7 +90,7 @@ model SurveyCycleParameter { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - @@map("survey_parameters") + @@map("survey_cycle_parameters") } model SurveyForm { diff --git a/src/app.module.ts b/src/app.module.ts index 27c97a3..8ba6ef1 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -10,7 +10,7 @@ import { MockFracModule } from "./mockModules/mock.module"; import { SurveyScoreModule } from "./survey-score/survey-score.module"; import { QuestionBankModule } from "./question-bank/question-bank.module"; import { SurveyConfigModule } from "./survey-config/survey-config.module"; -import { SurveyParameterModule } from "./survey-parameter/survey-parameter.module"; +import { SurveyCycleParameterModule } from "./survey-cycle-parameter/survey-cycle-parameter.module"; @Module({ imports: [ @@ -24,7 +24,7 @@ import { SurveyParameterModule } from "./survey-parameter/survey-parameter.modul SurveyScoreModule, QuestionBankModule, SurveyConfigModule, - SurveyParameterModule, + SurveyCycleParameterModule, ], controllers: [AppController], providers: [AppService, PrismaService], diff --git a/src/mockModules/mock-user/mock-user.controller.ts b/src/mockModules/mock-user/mock-user.controller.ts index 42af0d4..3ba8c28 100644 --- a/src/mockModules/mock-user/mock-user.controller.ts +++ b/src/mockModules/mock-user/mock-user.controller.ts @@ -1,19 +1,19 @@ import { - Controller, - Get, - Post, Body, - Patch, - Param, + Controller, Delete, + Get, HttpStatus, + Param, + Patch, + Post, Res, } from "@nestjs/common"; -import { MockUserService } from "./mock-user.service"; -import { CreateMockUserDto } from "./dto/create-mock-user.dto"; -import { UpdateMockUserDto } from "./dto/update-mock-user.dto"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { CreateMockUserDto } from "./dto/create-mock-user.dto"; import { ResponseMockUserDto } from "./dto/response-mock-user.dto"; +import { UpdateMockUserDto } from "./dto/update-mock-user.dto"; +import { MockUserService } from "./mock-user.service"; @Controller("user") @ApiTags("mockFracService/user") @@ -56,12 +56,12 @@ export class MockUserController { } } - @Get(":id") - @ApiOperation({ summary: "get mock user by id" }) + @Get(":userId") + @ApiOperation({ summary: "get mock user by userId" }) @ApiResponse({ status: HttpStatus.CREATED, type: ResponseMockUserDto }) - async findOne(@Param("id") id: string, @Res() res) { + async findOne(@Param("userId") userId: string, @Res() res) { try { - const user = await this.mockUserService.findOne(id); + const user = await this.mockUserService.findOne(userId); return res .status(HttpStatus.OK) .json({ data: user, message: "Successfully done" }); @@ -72,16 +72,16 @@ export class MockUserController { } } - @Patch(":id") - @ApiOperation({ summary: "update mock user by id" }) + @Patch(":userId") + @ApiOperation({ summary: "update mock user by userId" }) @ApiResponse({ status: HttpStatus.CREATED, type: ResponseMockUserDto }) async update( - @Param("id") id: string, + @Param("userId") userId: string, @Body() updateMockUserDto: UpdateMockUserDto, @Res() res ) { try { - const user = await this.mockUserService.update(id, updateMockUserDto); + const user = await this.mockUserService.update(userId, updateMockUserDto); return res .status(HttpStatus.OK) .json({ data: user, message: "Successfully done" }); @@ -92,12 +92,12 @@ export class MockUserController { } } - @Delete(":id") - @ApiOperation({ summary: "delete mock user by id" }) + @Delete(":userId") + @ApiOperation({ summary: "delete mock user by userId" }) @ApiResponse({ status: HttpStatus.CREATED, type: ResponseMockUserDto }) - async remove(@Param("id") id: string, @Res() res) { + async remove(@Param("userId") userId: string, @Res() res) { try { - const user = await this.mockUserService.remove(id); + const user = await this.mockUserService.remove(userId); return res .status(HttpStatus.OK) .json({ data: user, message: "Successfully done" }); diff --git a/src/survey-config/dto/create-survey-config.dto.ts b/src/survey-config/dto/create-survey-config.dto.ts index e45ed06..c05410b 100644 --- a/src/survey-config/dto/create-survey-config.dto.ts +++ b/src/survey-config/dto/create-survey-config.dto.ts @@ -1,10 +1,19 @@ -import { IsDate, IsInt, IsNotEmpty, IsOptional, IsString, Min } from "class-validator"; +import { ApiProperty } from "@nestjs/swagger"; +import { TimeUnitsEnum } from "@prisma/client"; +import { IsDate, IsEnum, IsInt, IsNotEmpty, IsOptional } from "class-validator"; export class CreateSurveyConfigDto { - // maximum number of question for the survey config @IsNotEmpty() @IsInt() - maxQuestions: number; + departmentId: number; + + @IsNotEmpty() + @IsInt() + onboardingTime: number; + + @ApiProperty({ enum: TimeUnitsEnum }) + @IsEnum(TimeUnitsEnum, { each: true }) + onboardingTimeUnit: TimeUnitsEnum; // start time for the survey config @IsNotEmpty() @@ -23,7 +32,7 @@ export class SurveyConfigFilterDto { @IsOptional() @IsNotEmpty() @IsInt() - maxQuestions?: number; + departmentId?: number; //Optional startTime filter validate that its valid date-time value @IsOptional() diff --git a/src/survey-config/dto/response-survey-config.dto.ts b/src/survey-config/dto/response-survey-config.dto.ts index d90166f..f574cce 100644 --- a/src/survey-config/dto/response-survey-config.dto.ts +++ b/src/survey-config/dto/response-survey-config.dto.ts @@ -1,6 +1,12 @@ +import { TimeUnitsEnum } from "@prisma/client"; +import { ResponseSurveyCycleParameterDto } from "src/survey-cycle-parameter/dto/response-survey-cycle-parameter.dto"; + export class ResponseSurveyConfigDto { readonly id: number; - readonly maxQuestions: number; + readonly departmentId: number; + readonly onboardingTime: number; + readonly onboardingTimeUnit: TimeUnitsEnum; readonly startTime: Date; readonly endTime: Date; + readonly SurveyCycleParameters?: ResponseSurveyCycleParameterDto[]; } diff --git a/src/survey-config/survey-config.service.ts b/src/survey-config/survey-config.service.ts index 06dd4d8..37c2642 100644 --- a/src/survey-config/survey-config.service.ts +++ b/src/survey-config/survey-config.service.ts @@ -18,10 +18,10 @@ export class SurveyConfigService { } async getAllSurveyConfig(filter: SurveyConfigFilterDto) { - const { maxQuestions, startTime, endTime, limit = 10, offset = 0 } = filter; + const { departmentId, startTime, endTime, limit = 10, offset = 0 } = filter; return this.prisma.surveyConfig.findMany({ where: { - maxQuestions: maxQuestions ?? undefined, // Optional maxQuestions filter + departmentId: departmentId ?? undefined, // Optional departmentId filter startTime: startTime ?? undefined, // Optional startTime filter endTime: endTime ?? undefined, // Optional endTime filter }, @@ -30,7 +30,10 @@ export class SurveyConfigService { }); } - async updateSurveyConfigById(surveyConfigId: number, updateSurveyConfig: UpdateSurveyConfigDto) { + async updateSurveyConfigById( + surveyConfigId: number, + updateSurveyConfig: UpdateSurveyConfigDto + ) { // Check the document available for the given surveyId in database to update. const findSurveyConfigId = await this.prisma.surveyConfig.findUnique({ where: { @@ -55,18 +58,20 @@ export class SurveyConfigService { // Find the survey config with the provided Id and check it exists or not before deleting const isExisted = await this.prisma.surveyConfig.findFirst({ where: { - id : surveyConfigId - } - }) - if (!isExisted) { - throw new NotFoundException(`The survey Config ID ${surveyConfigId} doesn't exist.`) + id: surveyConfigId, + }, + }); + if (!isExisted) { + throw new NotFoundException( + `The survey Config ID ${surveyConfigId} doesn't exist.` + ); } // Delete a single survey configuration based on its unique identifier (id). const deletedSurveyConfig = await this.prisma.surveyConfig.delete({ where: { - id: surveyConfigId - } - }) + id: surveyConfigId, + }, + }); return deletedSurveyConfig; } } diff --git a/src/survey-cycle-parameter/dto/create-survey-cycle-parameter.dto.ts b/src/survey-cycle-parameter/dto/create-survey-cycle-parameter.dto.ts new file mode 100644 index 0000000..f930612 --- /dev/null +++ b/src/survey-cycle-parameter/dto/create-survey-cycle-parameter.dto.ts @@ -0,0 +1,36 @@ +import { + IsBoolean, + IsDate, + IsInt, + IsNotEmpty, + IsOptional, +} from "class-validator"; + +export class CreateSurveyCycleParameterDto { + @IsNotEmpty() + @IsDate() + startTime: Date; + + @IsNotEmpty() + @IsDate() + endTime: Date; + + @IsNotEmpty() + @IsBoolean() + isActive: boolean; + + @IsOptional() + @IsNotEmpty() + @IsInt() + surveyConfigId?: number; + + @IsOptional() + @IsNotEmpty() + @IsDate() + createdAt?: Date = new Date(); + + @IsOptional() + @IsNotEmpty() + @IsDate() + updatedAt?: Date; +} diff --git a/src/survey-cycle-parameter/dto/response-survey-cycle-parameter.dto.ts b/src/survey-cycle-parameter/dto/response-survey-cycle-parameter.dto.ts new file mode 100644 index 0000000..d1ba804 --- /dev/null +++ b/src/survey-cycle-parameter/dto/response-survey-cycle-parameter.dto.ts @@ -0,0 +1,11 @@ +import { TimeUnitsEnum } from "@prisma/client"; + +export class ResponseSurveyCycleParameterDto { + readonly id: number; + readonly startTime: number; + readonly endTime: TimeUnitsEnum; + readonly surveyConfigId: number; + readonly isActive: boolean; + readonly createdAt: Date; + readonly updatedAt: Date; +} diff --git a/src/survey-cycle-parameter/dto/update-survey-cycle-parameter.dto.ts b/src/survey-cycle-parameter/dto/update-survey-cycle-parameter.dto.ts new file mode 100644 index 0000000..a59ac75 --- /dev/null +++ b/src/survey-cycle-parameter/dto/update-survey-cycle-parameter.dto.ts @@ -0,0 +1,6 @@ +import { PartialType } from "@nestjs/swagger"; +import { CreateSurveyCycleParameterDto } from "./create-survey-cycle-parameter.dto"; + +export class UpdateSurveyCycleParameterDto extends PartialType( + CreateSurveyCycleParameterDto +) {} diff --git a/src/survey-parameter/survey-parameter.controller.ts b/src/survey-cycle-parameter/survey-cycle-parameter.controller.ts similarity index 50% rename from src/survey-parameter/survey-parameter.controller.ts rename to src/survey-cycle-parameter/survey-cycle-parameter.controller.ts index 7dab2ac..c9928fd 100644 --- a/src/survey-parameter/survey-parameter.controller.ts +++ b/src/survey-cycle-parameter/survey-cycle-parameter.controller.ts @@ -1,132 +1,138 @@ import { + Body, Controller, Get, - Post, - Body, - Patch, - Param, - Logger, HttpStatus, - Res, - Query, + Logger, + Param, ParseIntPipe, + Patch, + Post, + Res, } from "@nestjs/common"; -import { SurveyParameterService } from "./survey-parameter.service"; -import { CreateSurveyParameterDto } from "./dto/create-survey-parameter.dto"; -import { UpdateSurveyParameterDto } from "./dto/update-survey-parameter.dto"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; -import { ResponseSurveyParameterdto } from "./dto/response-survey-parameter.dto"; import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; +import { ResponseSurveyCycleParameterDto } from "./dto/response-survey-cycle-parameter.dto"; +import { CreateSurveyCycleParameterDto } from "./dto/create-survey-cycle-parameter.dto"; +import { UpdateSurveyCycleParameterDto } from "./dto/update-survey-cycle-parameter.dto"; +import { SurveyCycleParameterService } from "./survey-cycle-parameter.service"; -@ApiTags("survey-parameter") -@Controller("survey-parameter") -export class SurveyParameterController { +@ApiTags("survey-cycle-parameter") +@Controller("survey-cycle-parameter") +export class SurveyCycleParameterController { // Create a logger instance for this log events and errors - private readonly logger = new Logger(SurveyParameterController.name); + private readonly logger = new Logger(SurveyCycleParameterController.name); // The survey parameter controller class, injecting the survey parameter service. constructor( - private readonly surveyParameterService: SurveyParameterService + private readonly surveyParameterService: SurveyCycleParameterService ) {} // Api to create a new survey parameter @Post() - @ApiOperation({ summary: "Create a Survey parameter" }) // Api operation for swagger - @ApiResponse({ status: HttpStatus.CREATED, type: ResponseSurveyParameterdto }) // Api response for swagger + @ApiOperation({ summary: "Create a Survey cycle parameter" }) // Api operation for swagger + @ApiResponse({ + status: HttpStatus.CREATED, + type: ResponseSurveyCycleParameterDto, + }) // Api response for swagger async createSurveyParameter( @Res() res, - @Body() createSurveyParameterDto: CreateSurveyParameterDto - ): Promise { + @Body() createSurveyCycleParameterDto: CreateSurveyCycleParameterDto + ): Promise { try { // Log the initiation for the Survey Parameter creation - this.logger.log(`Initiate to create a Survey Parameter`); + this.logger.log(`Initiate to create a Survey cycle Parameter`); const createdSurveyParameter = await this.surveyParameterService.createSurveyParameter( - createSurveyParameterDto + createSurveyCycleParameterDto ); // Log the successful creation of the survey parameter - this.logger.log(`Successfully Created Survey Parameter`); + this.logger.log(`Successfully Created Survey cycle Parameter`); // Return a success response with the created survey parameter. return res.status(HttpStatus.CREATED).json({ - message: "Survey parameter created sucessfully.", + message: "Survey cycle parameter created successfully.", data: createdSurveyParameter, }); } catch (error) { - this.logger.error(`Failed to create new survey Parameter.`, error); + this.logger.error(`Failed to create new survey cycle Parameter.`, error); // get error message and status code const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); // Return an error response return res.status(statusCode).json({ statusCode, - message: errorMessage || `Failed to create new survey Parameter.`, + message: errorMessage || `Failed to create new survey cycle Parameter.`, }); } } // Api to get all the survey parameter @Get() - @ApiOperation({ summary: "Find All Survey parameters" }) // Api operation for swagger + @ApiOperation({ summary: "Find All Survey cycle parameters" }) // Api operation for swagger @ApiResponse({ status: HttpStatus.OK, - type: ResponseSurveyParameterdto, + type: ResponseSurveyCycleParameterDto, isArray: true, }) // Api response for swagger async getAllSurveyParameter( @Res() res - ): Promise { + ): Promise { try { - this.logger.log(`Initiated fetching all the survey parameter.`); + this.logger.log(`Initiated fetching all the survey cycle parameter.`); const surveyParameters = await this.surveyParameterService.getAllSurveyParameter(); if (!surveyParameters.length) throw Error("No record found"); - this.logger.log(`Fetched successfully all the survey parameter.`); + this.logger.log(`Fetched successfully all the survey cycle parameter.`); return res.status(HttpStatus.OK).json({ - message: "Successfully fetched all survey config.", + message: "Successfully fetched all survey cycle parameter.", data: surveyParameters, }); } catch (error) { - this.logger.error(`Failed to fetch survey parameters`, error); + this.logger.error(`Failed to fetch survey cycle parameters`, error); const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); // get error message and status code // Return an error response return res.status(statusCode).json({ statusCode, - message: errorMessage || `Failed to Get all survey parameters.`, + message: errorMessage || `Failed to Get all survey cycle parameters.`, }); } } // Api to update the survey parameter by using the Id. @Patch("update/:id") - @ApiOperation({ summary: "Update the survey parameter by Id." }) + @ApiOperation({ summary: "Update the survey cycle parameter by Id." }) @ApiResponse({ status: HttpStatus.OK, - type: ResponseSurveyParameterdto, + type: ResponseSurveyCycleParameterDto, }) async updateSurveyParameterById( @Res() res, @Param("id", ParseIntPipe) id: number, - @Body() updateSurveyParameterDto: UpdateSurveyParameterDto - ): Promise { + @Body() updateSurveyCycleParameterDto: UpdateSurveyCycleParameterDto + ): Promise { try { - this.logger.log(`Initiated updating the survey parameter for id #${id}`); + this.logger.log( + `Initiated updating the survey cycle parameter for id #${id}` + ); let updatedSurveyParameter = await this.surveyParameterService.updateSurveyParameterById( id, - updateSurveyParameterDto + updateSurveyCycleParameterDto ); - this.logger.log(`Successfully updated survey parameter for id #${id}`); + this.logger.log( + `Successfully updated survey cycle parameter for id #${id}` + ); return res.status(HttpStatus.OK).json({ - message: `Successfully updated survey parameter for id #${id}`, + message: `Successfully updated survey cycle parameter for id #${id}`, data: updatedSurveyParameter, }); } catch (error) { this.logger.error( - `Failed to update survey parameter for id #${id}`, + `Failed to update survey cycle parameter for id #${id}`, error ); const { errorMessage, statusCode } = @@ -135,7 +141,8 @@ export class SurveyParameterController { return res.status(statusCode).json({ statusCode, message: - errorMessage || `Failed to update survey parameter for id #${id}`, + errorMessage || + `Failed to update survey cycle parameter for id #${id}`, }); } } diff --git a/src/survey-cycle-parameter/survey-cycle-parameter.module.ts b/src/survey-cycle-parameter/survey-cycle-parameter.module.ts new file mode 100644 index 0000000..7edcac6 --- /dev/null +++ b/src/survey-cycle-parameter/survey-cycle-parameter.module.ts @@ -0,0 +1,12 @@ +import { Module } from "@nestjs/common"; +import { PrismaModule } from "src/prisma/prisma.module"; +import { SurveyCycleParameterController } from "./survey-cycle-parameter.controller"; +import { SurveyCycleParameterService } from "./survey-cycle-parameter.service"; + +@Module({ + imports: [PrismaModule], + controllers: [SurveyCycleParameterController], + providers: [SurveyCycleParameterService], + exports: [SurveyCycleParameterService], +}) +export class SurveyCycleParameterModule {} diff --git a/src/survey-parameter/survey-parameter.service.ts b/src/survey-cycle-parameter/survey-cycle-parameter.service.ts similarity index 50% rename from src/survey-parameter/survey-parameter.service.ts rename to src/survey-cycle-parameter/survey-cycle-parameter.service.ts index 84e8ad2..9ea2d85 100644 --- a/src/survey-parameter/survey-parameter.service.ts +++ b/src/survey-cycle-parameter/survey-cycle-parameter.service.ts @@ -1,19 +1,23 @@ -import { ConflictException, Injectable, NotFoundException } from "@nestjs/common"; -import { CreateSurveyParameterDto } from "./dto/create-survey-parameter.dto"; -import { UpdateSurveyParameterDto } from "./dto/update-survey-parameter.dto"; +import { + ConflictException, + Injectable, + NotFoundException, +} from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; +import { CreateSurveyCycleParameterDto } from "./dto/create-survey-cycle-parameter.dto"; +import { UpdateSurveyCycleParameterDto } from "./dto/update-survey-cycle-parameter.dto"; @Injectable() -export class SurveyParameterService { - constructor(private prisma: PrismaService) { } +export class SurveyCycleParameterService { + constructor(private prisma: PrismaService) {} // To check if the survey parameter exist in database or not - async checkIfSurveyParameterExists(){ - return this.prisma.surveyParameters.findFirst(); + async checkIfSurveyParameterExists() { + return this.prisma.surveyCycleParameter.findFirst(); } async createSurveyParameter( - createSurveyParameterDto: CreateSurveyParameterDto + createSurveyCycleParameterDto: CreateSurveyCycleParameterDto ) { const existingSurveyParameter = await this.checkIfSurveyParameterExists(); @@ -22,39 +26,38 @@ export class SurveyParameterService { } // create a new survey parameter - const newSurveyParameter = await this.prisma.surveyParameters.create({ - data: createSurveyParameterDto, + const newSurveyParameter = await this.prisma.surveyCycleParameter.create({ + data: createSurveyCycleParameterDto, }); return newSurveyParameter; } async getAllSurveyParameter() { - return this.prisma.surveyParameters.findMany(); + return this.prisma.surveyCycleParameter.findMany(); } async updateSurveyParameterById( surveyParameterId: number, - updateSurveyParameter: UpdateSurveyParameterDto + updateSurveyCycleParameter: UpdateSurveyCycleParameterDto ) { // check if for the given id, data is available in db or not - const findSurveyParameterId = await this.prisma.surveyParameters.findUnique( - { + const findSurveyParameterId = + await this.prisma.surveyCycleParameter.findUnique({ where: { id: surveyParameterId, }, - } - ); + }); if (!findSurveyParameterId) { throw new NotFoundException( `Survey parameter with ID ${findSurveyParameterId} not found.` ); } // update an existing survey parameter by its unique identifier (id). - return this.prisma.surveyParameters.update({ + return this.prisma.surveyCycleParameter.update({ where: { id: surveyParameterId, }, - data: updateSurveyParameter, + data: updateSurveyCycleParameter, }); } } diff --git a/src/survey-parameter/dto/create-survey-parameter.dto.ts b/src/survey-parameter/dto/create-survey-parameter.dto.ts deleted file mode 100644 index 10d31e1..0000000 --- a/src/survey-parameter/dto/create-survey-parameter.dto.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { TimeUnitsEnum } from "@prisma/client"; -import { IsEnum, IsInt, IsNotEmpty, IsOptional } from "class-validator"; - -// CreateSurveyParameterDto is used to create a new survey parameter -export class CreateSurveyParameterDto { - // Onboarding time for the survey parameter - @IsNotEmpty() - @IsInt() - onboardingTime: number; - - // Onboarding time unit for the survey parameter - @IsNotEmpty() - @IsEnum(TimeUnitsEnum) - onboardingTimeUnit: TimeUnitsEnum; - - // Survey cycle value for the survey parameter - @IsNotEmpty() - @IsInt() - surveyCycle: number; -} diff --git a/src/survey-parameter/dto/response-survey-parameter.dto.ts b/src/survey-parameter/dto/response-survey-parameter.dto.ts deleted file mode 100644 index a0a5729..0000000 --- a/src/survey-parameter/dto/response-survey-parameter.dto.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { TimeUnitsEnum } from "@prisma/client"; - -export class ResponseSurveyParameterdto { - readonly id: number; - readonly onboardingTime: number; - readonly onboardingTimeUnit: TimeUnitsEnum; - readonly surveyCycle: number; -} diff --git a/src/survey-parameter/dto/update-survey-parameter.dto.ts b/src/survey-parameter/dto/update-survey-parameter.dto.ts deleted file mode 100644 index da9262b..0000000 --- a/src/survey-parameter/dto/update-survey-parameter.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { PartialType } from '@nestjs/swagger'; -import { CreateSurveyParameterDto } from './create-survey-parameter.dto'; - -export class UpdateSurveyParameterDto extends PartialType(CreateSurveyParameterDto) {} diff --git a/src/survey-parameter/survey-parameter.module.ts b/src/survey-parameter/survey-parameter.module.ts deleted file mode 100644 index 04515df..0000000 --- a/src/survey-parameter/survey-parameter.module.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Module } from '@nestjs/common'; -import { SurveyParameterService } from './survey-parameter.service'; -import { SurveyParameterController } from './survey-parameter.controller'; -import { PrismaModule } from 'src/prisma/prisma.module'; -import { PrismaService } from 'src/prisma/prisma.service'; - -@Module({ - imports: [PrismaModule], - controllers: [SurveyParameterController], - providers: [SurveyParameterService, PrismaService], - exports : [SurveyParameterService] -}) -export class SurveyParameterModule {} From 69d3964e8115451951ffab9d50e2dc1664894756 Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Wed, 18 Oct 2023 14:48:54 +0530 Subject: [PATCH 036/175] fixes for the response tracker module as per prisma schema changes. --- .../dto/create-response-tracker.dto.ts | 20 +++++++++++-------- .../dto/response-response-tracker.dto.ts | 6 +++--- .../response-tracker.controller.ts | 4 ++-- .../response-tracker.service.ts | 4 ++-- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/response-tracker/dto/create-response-tracker.dto.ts b/src/response-tracker/dto/create-response-tracker.dto.ts index 8f0eb17..b50ac72 100644 --- a/src/response-tracker/dto/create-response-tracker.dto.ts +++ b/src/response-tracker/dto/create-response-tracker.dto.ts @@ -4,8 +4,9 @@ import { IsInt, IsNotEmpty, IsOptional, - IsObject, IsEnum, + IsArray, + IsString, } from "class-validator"; export class CreateResponseTrackerDto { @@ -16,18 +17,21 @@ export class CreateResponseTrackerDto { @ApiProperty({ type: "integer", example: 2 }) @IsNotEmpty() - @IsInt() - assesseeId: number; + @IsString() + assesseeId: string; @ApiProperty({ type: "integer", example: 3 }) @IsNotEmpty() - @IsInt() - assessorId: number; + @IsString() + assessorId: string; - @ApiProperty({ type: "object", example: { question: true || false } }) + @ApiProperty({ + type: "array", + example: [{ question: true || false || "don't know" }], + }) @IsOptional() - @IsObject() - responseJson?: Record; + @IsArray() + responseJson?: Record[]; @ApiProperty({ enum: ResponseTrackerStatusEnum, example: "Pending" }) @IsNotEmpty() diff --git a/src/response-tracker/dto/response-response-tracker.dto.ts b/src/response-tracker/dto/response-response-tracker.dto.ts index c813949..abbd527 100644 --- a/src/response-tracker/dto/response-response-tracker.dto.ts +++ b/src/response-tracker/dto/response-response-tracker.dto.ts @@ -2,10 +2,10 @@ import { ResponseTrackerStatusEnum } from "@prisma/client"; export class ResponseTracker { readonly id: number; - readonly assesseeId: number; + readonly assesseeId: string; readonly surveyFormId: number; - readonly assessorId: number; - readonly responseJson?: Record; + readonly assessorId: string; + readonly responseJson?: Record[]; readonly status: ResponseTrackerStatusEnum; } diff --git a/src/response-tracker/response-tracker.controller.ts b/src/response-tracker/response-tracker.controller.ts index 94089f2..3fe1336 100644 --- a/src/response-tracker/response-tracker.controller.ts +++ b/src/response-tracker/response-tracker.controller.ts @@ -171,7 +171,7 @@ export class ResponseTrackerController { }) async findByAssessorId( @Res() res, - @Param("assessorId", ParseIntPipe) assessorId: number + @Param("assessorId") assessorId: string ): Promise { try { this.logger.log( @@ -212,7 +212,7 @@ export class ResponseTrackerController { }) async findByAssesseeId( @Res() res, - @Param("assesseeId", ParseIntPipe) assesseeId: number + @Param("assesseeId") assesseeId: string ): Promise { try { this.logger.log( diff --git a/src/response-tracker/response-tracker.service.ts b/src/response-tracker/response-tracker.service.ts index 79f7584..05fd727 100644 --- a/src/response-tracker/response-tracker.service.ts +++ b/src/response-tracker/response-tracker.service.ts @@ -38,7 +38,7 @@ export class ResponseTrackerService { return response; } - public async findByAssessorId(assessorId: number) { + public async findByAssessorId(assessorId: string) { const response = await this.prisma.responseTracker.findMany({ where: { assessorId }, }); @@ -50,7 +50,7 @@ export class ResponseTrackerService { return response; } - public async findByAssesseeId(assesseeId: number) { + public async findByAssesseeId(assesseeId: string) { const response = await this.prisma.responseTracker.findMany({ where: { assesseeId }, }); From e80f52aea4d58c39de739cc5a9a0dcd454ffac33 Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Wed, 18 Oct 2023 15:17:26 +0530 Subject: [PATCH 037/175] chages as per PR review --- src/response-tracker/dto/create-response-tracker.dto.ts | 8 ++++---- src/response-tracker/response-tracker.controller.ts | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/response-tracker/dto/create-response-tracker.dto.ts b/src/response-tracker/dto/create-response-tracker.dto.ts index b50ac72..cb25d3e 100644 --- a/src/response-tracker/dto/create-response-tracker.dto.ts +++ b/src/response-tracker/dto/create-response-tracker.dto.ts @@ -6,7 +6,7 @@ import { IsOptional, IsEnum, IsArray, - IsString, + IsUUID, } from "class-validator"; export class CreateResponseTrackerDto { @@ -17,12 +17,12 @@ export class CreateResponseTrackerDto { @ApiProperty({ type: "integer", example: 2 }) @IsNotEmpty() - @IsString() + @IsUUID() assesseeId: string; @ApiProperty({ type: "integer", example: 3 }) @IsNotEmpty() - @IsString() + @IsUUID() assessorId: string; @ApiProperty({ @@ -31,7 +31,7 @@ export class CreateResponseTrackerDto { }) @IsOptional() @IsArray() - responseJson?: Record[]; + responseJson?: Record[]; @ApiProperty({ enum: ResponseTrackerStatusEnum, example: "Pending" }) @IsNotEmpty() diff --git a/src/response-tracker/response-tracker.controller.ts b/src/response-tracker/response-tracker.controller.ts index 3fe1336..9f87b56 100644 --- a/src/response-tracker/response-tracker.controller.ts +++ b/src/response-tracker/response-tracker.controller.ts @@ -10,6 +10,7 @@ import { HttpStatus, Logger, ParseIntPipe, + ParseUUIDPipe, } from "@nestjs/common"; import { ResponseTrackerService } from "./response-tracker.service"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; @@ -171,7 +172,7 @@ export class ResponseTrackerController { }) async findByAssessorId( @Res() res, - @Param("assessorId") assessorId: string + @Param("assessorId", ParseUUIDPipe) assessorId: string ): Promise { try { this.logger.log( @@ -212,7 +213,7 @@ export class ResponseTrackerController { }) async findByAssesseeId( @Res() res, - @Param("assesseeId") assesseeId: string + @Param("assesseeId", ParseUUIDPipe) assesseeId: string ): Promise { try { this.logger.log( From dfcf0894f1b07bda7fffbe817a60c99e804a60da Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Wed, 18 Oct 2023 16:18:53 +0530 Subject: [PATCH 038/175] changes as per PR review. --- .../dto/create-response-tracker.dto.ts | 25 ++++++++++++++++--- .../dto/response-response-tracker.dto.ts | 3 ++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/response-tracker/dto/create-response-tracker.dto.ts b/src/response-tracker/dto/create-response-tracker.dto.ts index cb25d3e..75d7eef 100644 --- a/src/response-tracker/dto/create-response-tracker.dto.ts +++ b/src/response-tracker/dto/create-response-tracker.dto.ts @@ -7,6 +7,8 @@ import { IsEnum, IsArray, IsUUID, + IsNumber, + IsString, } from "class-validator"; export class CreateResponseTrackerDto { @@ -27,14 +29,31 @@ export class CreateResponseTrackerDto { @ApiProperty({ type: "array", - example: [{ question: true || false || "don't know" }], + example: [{ questionId: "integer", answer: "Yes" || "No" || "DoNotKnow" }], }) @IsOptional() @IsArray() - responseJson?: Record[]; - + responses?: Array<{ questionId: number; answer: AnswerEnum }>; + @ApiProperty({ enum: ResponseTrackerStatusEnum, example: "Pending" }) @IsNotEmpty() @IsEnum(ResponseTrackerStatusEnum) status: ResponseTrackerStatusEnum; } + +enum AnswerEnum { + Yes, + No, + DoNotKnow, +} + +export class responseObject { + @IsNumber() + @IsNotEmpty() + questionId: number; + + @ApiProperty({ enum: AnswerEnum, example: "Yes" }) + @IsString() + @IsNotEmpty() + answer: AnswerEnum; +} diff --git a/src/response-tracker/dto/response-response-tracker.dto.ts b/src/response-tracker/dto/response-response-tracker.dto.ts index abbd527..0f51274 100644 --- a/src/response-tracker/dto/response-response-tracker.dto.ts +++ b/src/response-tracker/dto/response-response-tracker.dto.ts @@ -1,11 +1,12 @@ import { ResponseTrackerStatusEnum } from "@prisma/client"; +import { responseObject } from "./create-response-tracker.dto"; export class ResponseTracker { readonly id: number; readonly assesseeId: string; readonly surveyFormId: number; readonly assessorId: string; - readonly responseJson?: Record[]; + readonly responseJson?:responseObject[]; readonly status: ResponseTrackerStatusEnum; } From 4cae517d3dbf980a3cd4e12307482a9ce8039ebc Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Wed, 18 Oct 2023 16:24:45 +0530 Subject: [PATCH 039/175] Changes as per PR review. --- prisma/schema.prisma | 2 +- src/response-tracker/dto/create-response-tracker.dto.ts | 5 +++-- src/response-tracker/dto/response-response-tracker.dto.ts | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index a649e32..6a414ab 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -118,7 +118,7 @@ model ResponseTracker { surveyForm SurveyForm @relation(fields: [surveyFormId], references: [id]) assesseeId String @db.Uuid assessorId String @db.Uuid - responseJson Json[] //{questionId: answer(yes, no and don't know)} + responseJson Json[] @default([]) //{questionId: answer(yes, no and don't know)} status ResponseTrackerStatusEnum Assessee UserMetadata @relation("AssesseeResponseTrackers", fields: [assesseeId], references: [userId]) Assessor UserMetadata @relation("AssessorResponseTrackers", fields: [assessorId], references: [userId]) diff --git a/src/response-tracker/dto/create-response-tracker.dto.ts b/src/response-tracker/dto/create-response-tracker.dto.ts index 75d7eef..84eb00e 100644 --- a/src/response-tracker/dto/create-response-tracker.dto.ts +++ b/src/response-tracker/dto/create-response-tracker.dto.ts @@ -31,10 +31,11 @@ export class CreateResponseTrackerDto { type: "array", example: [{ questionId: "integer", answer: "Yes" || "No" || "DoNotKnow" }], }) + @IsOptional() @IsArray() - responses?: Array<{ questionId: number; answer: AnswerEnum }>; - + responseJson?: Array<{ questionId: number; answer: AnswerEnum }>; + @ApiProperty({ enum: ResponseTrackerStatusEnum, example: "Pending" }) @IsNotEmpty() @IsEnum(ResponseTrackerStatusEnum) diff --git a/src/response-tracker/dto/response-response-tracker.dto.ts b/src/response-tracker/dto/response-response-tracker.dto.ts index 0f51274..7c86d5b 100644 --- a/src/response-tracker/dto/response-response-tracker.dto.ts +++ b/src/response-tracker/dto/response-response-tracker.dto.ts @@ -6,7 +6,7 @@ export class ResponseTracker { readonly assesseeId: string; readonly surveyFormId: number; readonly assessorId: string; - readonly responseJson?:responseObject[]; + readonly responseJson?: responseObject[]; readonly status: ResponseTrackerStatusEnum; } From b6d8eda2330fcc821b869c0c89744e7d05ddfc6b Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Wed, 18 Oct 2023 18:06:23 +0530 Subject: [PATCH 040/175] Created the crud APIs for the admin department --- .../admin-department.controller.ts | 145 ++++++++++++++++++ .../admin-department.module.ts | 20 +++ .../admin-department.service.ts | 60 ++++++++ .../dto/create-admin-department.dto.ts | 36 +++++ .../dto/response-admin-department.dto.ts | 5 + .../dto/update-admin-department.dto.ts | 4 + src/app.module.ts | 2 + 7 files changed, 272 insertions(+) create mode 100644 src/admin-department/admin-department.controller.ts create mode 100644 src/admin-department/admin-department.module.ts create mode 100644 src/admin-department/admin-department.service.ts create mode 100644 src/admin-department/dto/create-admin-department.dto.ts create mode 100644 src/admin-department/dto/response-admin-department.dto.ts create mode 100644 src/admin-department/dto/update-admin-department.dto.ts diff --git a/src/admin-department/admin-department.controller.ts b/src/admin-department/admin-department.controller.ts new file mode 100644 index 0000000..d895f53 --- /dev/null +++ b/src/admin-department/admin-department.controller.ts @@ -0,0 +1,145 @@ +import { + Controller, + Get, + Post, + Body, + Patch, + Param, + Logger, + HttpStatus, + Res, + Query, + ParseIntPipe, +} from "@nestjs/common"; +import { AdminDepartmentService } from "./admin-department.service"; +import { CreateAdminDepartmentDto, FilterAdminDepartmentsDto } from "./dto/create-admin-department.dto"; +import { UpdateAdminDepartmentDto } from "./dto/update-admin-department.dto"; +import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { ResponseAdminDepartmentDto } from "./dto/response-admin-department.dto"; +import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; + +@Controller("admin-department") +@ApiTags("admin-department") +export class AdminDepartmentController { + // Create a logger instance for this controller to log events and errors. + private readonly logger = new Logger(AdminDepartmentController.name); + + // The Admin department controller class, injecting the Admin department service. + constructor( + private readonly adminDepartmentService: AdminDepartmentService + ) {} + + // Api for creating a new admin department + @Post() + @ApiOperation({ summary: "Create a new admin department" }) // Api operation for swagger + @ApiResponse({ status: HttpStatus.CREATED, type: ResponseAdminDepartmentDto }) // Api response for the swagger + async createAdminDepartment( + @Res() res, + @Body() createAdminDepartmentDto: CreateAdminDepartmentDto + ): Promise { + try { + // Log the initiation for the admin department creation + this.logger.log(`Initiate to create a admin department`); + + const createdAdminDepartment = + await this.adminDepartmentService.createAdminDepartment( + createAdminDepartmentDto + ); + // Log the successful creation of the admin department + this.logger.log(`Successfully created admin department.`); + return res.status(HttpStatus.CREATED).json({ + message: "Admin department created sucessfully.", + data: createdAdminDepartment, + }); + } catch (error) { + this.logger.error(`Failed to create new admin department.`, error); + // get error message and status code + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + // Return an error response + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed to create new admin department.`, + }); + } + } + + // Api for getting all admin department + @Get() + @ApiOperation({ summary: "Fetch all admin departments." }) + @ApiResponse({ + status: HttpStatus.OK, + type: ResponseAdminDepartmentDto, + isArray: true, + }) + async getAllAdminDepartment( + @Res() res, + @Query() filter: FilterAdminDepartmentsDto + ): Promise { + try { + // Log the initiation for fetching all the admin department + this.logger.log(`Initiated fetching all the admin department.`); + const adminDepartments = + await this.adminDepartmentService.getAllAdminDepartment(filter); + + // Log the successful fetching of data from db. + this.logger.log(`Successfully fetched all admin department.`); + + // return the successful response for fetching the data from the db. + return res.status(HttpStatus.OK).json({ + message: "Successfully fetched all admin department.", + data: adminDepartments, + }); + } catch (error) { + // log the error message + this.logger.error(`Failed to get all admin department.`, error); + // get error message and status code + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + // Return an error response + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed to get all admin department.`, + }); + } + } + + // Api for updating the admin department + @Patch("update/:id") + @ApiOperation({ summary: "Update the admin department by Id." }) // Api operation for the swagger + @ApiResponse({ status: HttpStatus.OK, type: ResponseAdminDepartmentDto }) // Api response for the swagger + async updateAdminDepartmentById( + @Res() res, + @Param("id", ParseIntPipe) id: number, + @Body() updateAdminDepartmentDto: UpdateAdminDepartmentDto + ): Promise { + try { + // Log the initiation for updating the admin department for the given id + this.logger.log(`Initiated updating the admin department for id #${id}`); + // Update the admin department for the id + const updateAdminDepartment = await this.adminDepartmentService.updateAdminDepartmentById( + id, + updateAdminDepartmentDto + ); + // Log the successful update of the admin department with the given id + this.logger.log(`Successfully updated admin department for id #${id}`); + // Return response and statuscode for the successful update of the question + return res.status(HttpStatus.OK).json({ + message: `Successfully updated question for id #${id}`, + data: updateAdminDepartment, + }); + } catch (error) { + // Log the error + this.logger.error(`Failed to update admin department for id #${id}`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); // get error message and status code + + // Return the response and status code for the failed update of the admin department + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed to update admin department for id #${id}`, + }); + } + } +} diff --git a/src/admin-department/admin-department.module.ts b/src/admin-department/admin-department.module.ts new file mode 100644 index 0000000..b33a134 --- /dev/null +++ b/src/admin-department/admin-department.module.ts @@ -0,0 +1,20 @@ +import { Module } from '@nestjs/common'; +import { AdminDepartmentService } from './admin-department.service'; +import { AdminDepartmentController } from './admin-department.controller'; +import { PrismaService } from 'src/prisma/prisma.service'; +import { PrismaModule } from 'src/prisma/prisma.module'; + +@Module({ + // Import the PrismaModule to make Prisma service available within this module + imports: [PrismaModule], + + // Declare the AdminDepartmentController as a controller for this module + controllers: [AdminDepartmentController], + + // Declare the AdminDepartmentService and PrismaService as providers (singleton) for this module + providers: [AdminDepartmentService, PrismaService], + + // Export the AdminDepartmentService to make it available for other modules that import this module + exports: [AdminDepartmentService] +}) +export class AdminDepartmentModule {} diff --git a/src/admin-department/admin-department.service.ts b/src/admin-department/admin-department.service.ts new file mode 100644 index 0000000..ed8ba49 --- /dev/null +++ b/src/admin-department/admin-department.service.ts @@ -0,0 +1,60 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { CreateAdminDepartmentDto, FilterAdminDepartmentsDto } from './dto/create-admin-department.dto'; +import { UpdateAdminDepartmentDto } from './dto/update-admin-department.dto'; +import { PrismaService } from 'src/prisma/prisma.service'; + +@Injectable() +export class AdminDepartmentService { + constructor(private prisma: PrismaService) {} + + public async createAdminDepartment( + createAdminDepartmentDto: CreateAdminDepartmentDto + ) { + // Create a new Admin department + const createNewDepartment = await this.prisma.adminDepartment.create({ + data: createAdminDepartmentDto + }) + return createNewDepartment; + } + + public async getAllAdminDepartment(filter : FilterAdminDepartmentsDto) { + const { departmentId, name, limit = 10, offset = 0 } = filter; + // Get all the admin department and filter using department Id and name. + return this.prisma.adminDepartment.findMany({ + where: { + departmentId: departmentId ?? undefined, // Optional departmentId filter + name: name ?? undefined, // Optional filter by Name + }, + skip: offset, + take: limit + }); + } + + public async updateAdminDepartmentById( + adminDepartmentId: number, + updateAdminDepartmentDto: UpdateAdminDepartmentDto + ) { + // Check if there is Admin department in the db for the id + let checkExistingDepartment = await this.prisma.adminDepartment.findFirst({ + where: { + id: adminDepartmentId, + }, + }); + // If not found throw error message + if (!checkExistingDepartment) { + throw new NotFoundException( + `Admin Department with ID "${adminDepartmentId}" not found.` + ); + } + // Otherwise update existing Admin department + const updatedAdminDepartment = await this.prisma.adminDepartment.updateMany( + { + where: { + id: adminDepartmentId, + }, + data: updateAdminDepartmentDto, + } + ); + return updatedAdminDepartment; + } +} diff --git a/src/admin-department/dto/create-admin-department.dto.ts b/src/admin-department/dto/create-admin-department.dto.ts new file mode 100644 index 0000000..c99fb05 --- /dev/null +++ b/src/admin-department/dto/create-admin-department.dto.ts @@ -0,0 +1,36 @@ +import { IsInt, IsNotEmpty, IsOptional, IsString } from "class-validator"; + +export class CreateAdminDepartmentDto { + // Department Id associated with admin department + @IsNotEmpty() + @IsInt() + departmentId: number; + + // Name field associated with the admin department + @IsNotEmpty() + @IsString() + name: string; +} + +// Admin department filter dto +export class FilterAdminDepartmentsDto { + // Optional Department id to be filtered for admin departments + @IsOptional() + @IsInt() + departmentId?: number; + + // Optional Name of the admin department to be searched + @IsOptional() + @IsString() + name?: string; + + // Optional limit for pagination, validate that it's an integer. + @IsOptional() + @IsInt() + limit?: number = 10; + + // Optional offset for pagination, validate that it's an integer. + @IsOptional() + @IsInt() + offset?: number = 0; +} diff --git a/src/admin-department/dto/response-admin-department.dto.ts b/src/admin-department/dto/response-admin-department.dto.ts new file mode 100644 index 0000000..714a2cf --- /dev/null +++ b/src/admin-department/dto/response-admin-department.dto.ts @@ -0,0 +1,5 @@ +export class ResponseAdminDepartmentDto { + readonly id: number; + readonly name: string; + readonly departmentId: number; +} diff --git a/src/admin-department/dto/update-admin-department.dto.ts b/src/admin-department/dto/update-admin-department.dto.ts new file mode 100644 index 0000000..9227d2a --- /dev/null +++ b/src/admin-department/dto/update-admin-department.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/swagger'; +import { CreateAdminDepartmentDto } from './create-admin-department.dto'; + +export class UpdateAdminDepartmentDto extends PartialType(CreateAdminDepartmentDto) {} diff --git a/src/app.module.ts b/src/app.module.ts index 8ba6ef1..aac463c 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -11,6 +11,7 @@ import { SurveyScoreModule } from "./survey-score/survey-score.module"; import { QuestionBankModule } from "./question-bank/question-bank.module"; import { SurveyConfigModule } from "./survey-config/survey-config.module"; import { SurveyCycleParameterModule } from "./survey-cycle-parameter/survey-cycle-parameter.module"; +import { AdminDepartmentModule } from './admin-department/admin-department.module'; @Module({ imports: [ @@ -25,6 +26,7 @@ import { SurveyCycleParameterModule } from "./survey-cycle-parameter/survey-cycl QuestionBankModule, SurveyConfigModule, SurveyCycleParameterModule, + AdminDepartmentModule, ], controllers: [AppController], providers: [AppService, PrismaService], From be2e7d6adcd93feffa90acc244f8cbd228405b06 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 18 Oct 2023 18:15:10 +0530 Subject: [PATCH 041/175] Mock Designation APIs. --- .../dto/create-designation.dto.ts | 12 ++ src/mockModules/mock-designation/dto/index.ts | 3 + .../dto/response-designation.dto.ts | 5 + .../dto/update-designation.dto.ts | 13 +++ .../mock-designation.controller.ts | 108 ++++++++++++++++++ .../mock-designation.module.ts | 9 ++ .../mock-designation.service.ts | 46 ++++++++ src/mockModules/mock.module.ts | 3 + 8 files changed, 199 insertions(+) create mode 100644 src/mockModules/mock-designation/dto/create-designation.dto.ts create mode 100644 src/mockModules/mock-designation/dto/index.ts create mode 100644 src/mockModules/mock-designation/dto/response-designation.dto.ts create mode 100644 src/mockModules/mock-designation/dto/update-designation.dto.ts create mode 100644 src/mockModules/mock-designation/mock-designation.controller.ts create mode 100644 src/mockModules/mock-designation/mock-designation.module.ts create mode 100644 src/mockModules/mock-designation/mock-designation.service.ts diff --git a/src/mockModules/mock-designation/dto/create-designation.dto.ts b/src/mockModules/mock-designation/dto/create-designation.dto.ts new file mode 100644 index 0000000..a6dc501 --- /dev/null +++ b/src/mockModules/mock-designation/dto/create-designation.dto.ts @@ -0,0 +1,12 @@ +import { IsNotEmpty, IsOptional, IsString } from "class-validator"; + +export class CreateDesignationDto { + @IsString() + @IsNotEmpty() + name: string; + + @IsString() + @IsOptional() + @IsNotEmpty() + description?: string; +} diff --git a/src/mockModules/mock-designation/dto/index.ts b/src/mockModules/mock-designation/dto/index.ts new file mode 100644 index 0000000..f3d730c --- /dev/null +++ b/src/mockModules/mock-designation/dto/index.ts @@ -0,0 +1,3 @@ +export * from './create-designation.dto'; +export * from './update-designation.dto'; +export * from './response-designation.dto'; \ No newline at end of file diff --git a/src/mockModules/mock-designation/dto/response-designation.dto.ts b/src/mockModules/mock-designation/dto/response-designation.dto.ts new file mode 100644 index 0000000..e96348b --- /dev/null +++ b/src/mockModules/mock-designation/dto/response-designation.dto.ts @@ -0,0 +1,5 @@ +export class ResponseDesignationDto { + readonly id: number; + readonly name: string; + readonly description?: string | null; +} diff --git a/src/mockModules/mock-designation/dto/update-designation.dto.ts b/src/mockModules/mock-designation/dto/update-designation.dto.ts new file mode 100644 index 0000000..9d52fdc --- /dev/null +++ b/src/mockModules/mock-designation/dto/update-designation.dto.ts @@ -0,0 +1,13 @@ +import { IsNotEmpty, IsOptional, IsString } from "class-validator"; + +export class UpdateDesignationDto { + @IsString() + @IsOptional() + @IsNotEmpty() + name?: string; + + @IsString() + @IsOptional() + @IsNotEmpty() + description?: string; +} diff --git a/src/mockModules/mock-designation/mock-designation.controller.ts b/src/mockModules/mock-designation/mock-designation.controller.ts new file mode 100644 index 0000000..4f8badd --- /dev/null +++ b/src/mockModules/mock-designation/mock-designation.controller.ts @@ -0,0 +1,108 @@ +import { + Body, + Controller, + Delete, + Get, + HttpStatus, + Param, + Post, + Put, + Res, +} from "@nestjs/common"; +import { MockDesignationService } from "./mock-designation.service"; +import { + CreateDesignationDto, + ResponseDesignationDto, + UpdateDesignationDto, +} from "./dto"; +import { ApiTags } from "@nestjs/swagger"; + +@Controller("designation") +@ApiTags("mockFracService/designation") +export class MockDesignationController { + constructor(private readonly designationService: MockDesignationService) {} + + @Post() + async create(@Body() createDto: CreateDesignationDto, @Res() res) { + try { + const designation = await this.designationService.createDesignation( + createDto + ); + return res.status(HttpStatus.CREATED).json({ + data: designation, + message: "Successfully created the Designation", + }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } + + @Get() + async findAll(@Res() res): Promise { + try { + const designation = await this.designationService.findAllDesignations(); + return res.status(HttpStatus.FOUND).json({ + data: designation, + message: "Successfully Fetched all the designations.", + }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } + + @Get(":id") + async findOne(@Param("id") id: number, @Res() res) { + try { + const designation = await this.designationService.findDesignationById(id); + return res.status(HttpStatus.FOUND).json({ + data: designation, + message: "Successfully Fetched the designation.", + }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } + + @Put(":id") + async update( + @Param("id") id: number, + @Body() updateDto: UpdateDesignationDto, + @Res() res + ) { + try { + const designation = await this.designationService.updateDesignation( + id, + updateDto + ); + return res.status(HttpStatus.OK).json({ + data: designation, + message: "Successfully updated the designation.", + }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } + + @Delete(":id") + async remove(@Param("id") id: number, @Res() res) { + try { + const designation = await this.designationService.removeDesignation(id); + return res.status(HttpStatus.OK).json({ + data: designation, + message: "Successfully deleted the designation.", + }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } +} diff --git a/src/mockModules/mock-designation/mock-designation.module.ts b/src/mockModules/mock-designation/mock-designation.module.ts new file mode 100644 index 0000000..255bfda --- /dev/null +++ b/src/mockModules/mock-designation/mock-designation.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { MockDesignationService } from './mock-designation.service'; +import { MockDesignationController } from './mock-designation.controller'; + +@Module({ + providers: [MockDesignationService], + controllers: [MockDesignationController] +}) +export class MockDesignationModule {} diff --git a/src/mockModules/mock-designation/mock-designation.service.ts b/src/mockModules/mock-designation/mock-designation.service.ts new file mode 100644 index 0000000..a291b8c --- /dev/null +++ b/src/mockModules/mock-designation/mock-designation.service.ts @@ -0,0 +1,46 @@ +import { Injectable } from "@nestjs/common"; +import { PrismaService } from "src/prisma/prisma.service"; +import { CreateDesignationDto, UpdateDesignationDto } from "./dto"; + +@Injectable() +export class MockDesignationService { + constructor(private prisma: PrismaService) {} + + async createDesignation(createDto: CreateDesignationDto) { + return await this.prisma.designation.create({ + data: createDto, + }); + } + + async findAllDesignations() { + return await this.prisma.designation.findMany(); + } + + async findDesignationById(id: number) { + return await this.prisma.designation.findUnique({ + where: { id }, + }); + } + + async findUsersWithDesignationId(id: number) { + const designation = await this.findDesignationById(id); + return await this.prisma.userMetadata.findMany({ + where: { + designation: designation?.name, + }, + }); + } + + async updateDesignation(id: number, updateDto: UpdateDesignationDto) { + return await this.prisma.designation.update({ + where: { id }, + data: updateDto, + }); + } + + async removeDesignation(id: number) { + return await this.prisma.designation.delete({ + where: { id }, + }); + } +} diff --git a/src/mockModules/mock.module.ts b/src/mockModules/mock.module.ts index 5e8852b..f63164c 100644 --- a/src/mockModules/mock.module.ts +++ b/src/mockModules/mock.module.ts @@ -6,6 +6,7 @@ import { RouterModule } from "@nestjs/core"; import { MockUserModule } from "./mock-user/mock-user.module"; import { MockLevelModule } from "./mock-level/mock-level.module"; import { MockDepartmentModule } from "./mock-department/mock-department.module"; +import { MockDesignationModule } from './mock-designation/mock-designation.module'; @Module({ imports: [ @@ -15,6 +16,7 @@ import { MockDepartmentModule } from "./mock-department/mock-department.module"; MockUserModule, MockLevelModule, MockDepartmentModule, + MockDesignationModule, RouterModule.register([ { path: "mockFracService/", module: MockRoleModule }, { path: "mockFracService/", module: MockCompetencyModule }, @@ -22,6 +24,7 @@ import { MockDepartmentModule } from "./mock-department/mock-department.module"; { path: "mockFracService/", module: MockUserModule }, { path: "mockFracService/", module: MockLevelModule }, { path: "mockFracService/", module: MockDepartmentModule }, + { path: "mockFracService/", module: MockDesignationModule }, ]), ], }) From 959cd92a171b9796199b44084ca1944dbbf06f36 Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Wed, 18 Oct 2023 19:07:39 +0530 Subject: [PATCH 042/175] refactor --- src/admin-department/admin-department.controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/admin-department/admin-department.controller.ts b/src/admin-department/admin-department.controller.ts index d895f53..22352aa 100644 --- a/src/admin-department/admin-department.controller.ts +++ b/src/admin-department/admin-department.controller.ts @@ -123,9 +123,9 @@ export class AdminDepartmentController { ); // Log the successful update of the admin department with the given id this.logger.log(`Successfully updated admin department for id #${id}`); - // Return response and statuscode for the successful update of the question + // Return response and statuscode for the successful update of the admin department return res.status(HttpStatus.OK).json({ - message: `Successfully updated question for id #${id}`, + message: `Successfully updated admin department for id #${id}`, data: updateAdminDepartment, }); } catch (error) { From 829f264f60b9ed44c90016a316a439e4b88bd5a0 Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Wed, 18 Oct 2023 19:16:07 +0530 Subject: [PATCH 043/175] Added the optional field description in the Admin department prisma model. --- prisma/schema.prisma | 1 + src/admin-department/dto/create-admin-department.dto.ts | 5 +++++ src/admin-department/dto/response-admin-department.dto.ts | 1 + 3 files changed, 7 insertions(+) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 5cc78c2..1c7656f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -60,6 +60,7 @@ model AdminDepartment { id Int @default(autoincrement()) departmentId Int @unique name String + description String? UserMetadata UserMetadata[] @@id([id, departmentId]) diff --git a/src/admin-department/dto/create-admin-department.dto.ts b/src/admin-department/dto/create-admin-department.dto.ts index c99fb05..4ca9437 100644 --- a/src/admin-department/dto/create-admin-department.dto.ts +++ b/src/admin-department/dto/create-admin-department.dto.ts @@ -10,6 +10,11 @@ export class CreateAdminDepartmentDto { @IsNotEmpty() @IsString() name: string; + + // Optional description field associated with the admin department + @IsOptional() + @IsString() + description?: string; } // Admin department filter dto diff --git a/src/admin-department/dto/response-admin-department.dto.ts b/src/admin-department/dto/response-admin-department.dto.ts index 714a2cf..9ad5997 100644 --- a/src/admin-department/dto/response-admin-department.dto.ts +++ b/src/admin-department/dto/response-admin-department.dto.ts @@ -2,4 +2,5 @@ export class ResponseAdminDepartmentDto { readonly id: number; readonly name: string; readonly departmentId: number; + readonly description?: string; } From 37cc2d189b267881b2ad8a2f2275cd9fb16c874d Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Wed, 18 Oct 2023 19:53:52 +0530 Subject: [PATCH 044/175] Admin competency module and APIs --- .../admin-competency.controller.spec.ts | 20 ++ .../admin-competency.controller.ts | 227 ++++++++++++++++++ .../admin-competency.module.ts | 9 + .../admin-competency.service.spec.ts | 18 ++ .../admin-competency.service.ts | 66 +++++ .../dto/create-admin-competency.dto.ts | 45 ++++ src/admin-competency/dto/index.ts | 3 + .../dto/response-admin-competency.dto.ts | 26 ++ .../dto/update-admin-competency.dto.ts | 4 + src/app.module.ts | 2 + src/utils/utils.ts | 37 +-- 11 files changed, 443 insertions(+), 14 deletions(-) create mode 100644 src/admin-competency/admin-competency.controller.spec.ts create mode 100644 src/admin-competency/admin-competency.controller.ts create mode 100644 src/admin-competency/admin-competency.module.ts create mode 100644 src/admin-competency/admin-competency.service.spec.ts create mode 100644 src/admin-competency/admin-competency.service.ts create mode 100644 src/admin-competency/dto/create-admin-competency.dto.ts create mode 100644 src/admin-competency/dto/index.ts create mode 100644 src/admin-competency/dto/response-admin-competency.dto.ts create mode 100644 src/admin-competency/dto/update-admin-competency.dto.ts diff --git a/src/admin-competency/admin-competency.controller.spec.ts b/src/admin-competency/admin-competency.controller.spec.ts new file mode 100644 index 0000000..b84496d --- /dev/null +++ b/src/admin-competency/admin-competency.controller.spec.ts @@ -0,0 +1,20 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AdminCompetencyController } from './admin-competency.controller'; +import { AdminCompetencyService } from './admin-competency.service'; + +describe('AdminCompetencyController', () => { + let controller: AdminCompetencyController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [AdminCompetencyController], + providers: [AdminCompetencyService], + }).compile(); + + controller = module.get(AdminCompetencyController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/admin-competency/admin-competency.controller.ts b/src/admin-competency/admin-competency.controller.ts new file mode 100644 index 0000000..3193698 --- /dev/null +++ b/src/admin-competency/admin-competency.controller.ts @@ -0,0 +1,227 @@ +import { + Body, + Controller, + Delete, + Get, + HttpStatus, + Logger, + Param, + ParseIntPipe, + Patch, + Post, + Res, +} from "@nestjs/common"; +import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; +import { AdminCompetencyService } from "./admin-competency.service"; +import { + AdminCompetencyArrayResponseDto, + AdminCompetencyResponseDto, + CreateAdminCompetencyDto, + UpdateAdminCompetencyDto, +} from "./dto"; + +@Controller("admin-competency") +@ApiTags("admin-competency") +export class AdminCompetencyController { + private readonly logger = new Logger(AdminCompetencyController.name); + + constructor( + private readonly adminCompetencyService: AdminCompetencyService + ) {} + + @Post() + @ApiOperation({ summary: "create new admin-competency" }) + @ApiResponse({ status: HttpStatus.CREATED, type: AdminCompetencyResponseDto }) + async create( + @Res() res, + @Body() createAdminCompetencyDto: CreateAdminCompetencyDto + ): Promise { + try { + this.logger.log(`Initiated creating new admin-competency`); + + const adminCompetency = await this.adminCompetencyService.create( + createAdminCompetencyDto + ); + + this.logger.log(`Successfully created new admin-competency`); + + return res.status(HttpStatus.CREATED).send({ + message: `Successfully created new admin-competency`, + data: adminCompetency, + }); + } catch (error) { + this.logger.error("Failed to create new admin-competency", error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: errorMessage || "Failed to create new admin-competency", + }); + } + } + + @Get() + @ApiOperation({ summary: "get all admin-competency" }) + @ApiResponse({ status: HttpStatus.OK, type: AdminCompetencyArrayResponseDto }) + async findAll(@Res() res): Promise { + try { + this.logger.log(`Initiated fetching all admin-competency`); + + const adminCompetency = await this.adminCompetencyService.findAll(); + + this.logger.log(`Successfully fetched all admin-competency`); + + return res.status(HttpStatus.OK).send({ + message: `Successfully fetched all admin-competency`, + data: adminCompetency, + }); + } catch (error) { + this.logger.error("Failed to fetch all admin-competency", error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: errorMessage || "Failed to fetch all admin-competency", + }); + } + } + + @Get(":id/:competencyId") + @ApiOperation({ summary: "get admin-competency by id and competencyId" }) + @ApiResponse({ status: HttpStatus.OK, type: AdminCompetencyResponseDto }) + async findOne( + @Res() res, + @Param("id", ParseIntPipe) id: number, + @Param("competencyId", ParseIntPipe) competencyId: number + ): Promise { + try { + this.logger.log( + `Initiated fetching admin-competency with id #${id} and competency id #${competencyId}` + ); + + const adminCompetency = await this.adminCompetencyService.findOne( + id, + competencyId + ); + + this.logger.log( + `Successfully fetched admin-competency with id #${id} and competency id #${competencyId}` + ); + + return res.status(HttpStatus.OK).send({ + message: `Successfully fetched admin-competency with id #${id} and competency id #${competencyId}`, + data: adminCompetency, + }); + } catch (error) { + this.logger.error( + `Failed to fetch admin-competency with id #${id} and competency id #${competencyId}`, + error + ); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: + errorMessage || + `Failed to fetch admin-competency with id #${id} and competency id #${competencyId}`, + }); + } + } + + @Patch(":id/:competencyId") + @ApiOperation({ summary: "update admin-competency" }) + @ApiResponse({ status: HttpStatus.OK, type: AdminCompetencyResponseDto }) + async update( + @Res() res, + @Param("id", ParseIntPipe) id: number, + @Param("competencyId", ParseIntPipe) competencyId: number, + @Body() updateAdminCompetencyDto: UpdateAdminCompetencyDto + ): Promise { + try { + this.logger.log( + `Initiated updating admin-competency with id #${id} and competency id #${competencyId}` + ); + + const adminCompetency = await this.adminCompetencyService.update( + id, + competencyId, + updateAdminCompetencyDto + ); + + this.logger.log( + `Successfully updated admin-competency with id #${id} and competency id #${competencyId}` + ); + + return res.status(HttpStatus.OK).send({ + message: `Successfully updated admin-competency with id #${id} and competency id #${competencyId}`, + data: adminCompetency, + }); + } catch (error) { + this.logger.error( + `Failed to update admin-competency with id #${id} and competency id #${competencyId}`, + error + ); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: + errorMessage || + `Failed to update admin-competency with id #${id} and competency id #${competencyId}`, + }); + } + } + + @Delete(":id/:competencyId") + @ApiOperation({ summary: "delete admin-competency" }) + @ApiResponse({ status: HttpStatus.OK, type: AdminCompetencyResponseDto }) + async remove( + @Res() res, + @Param("id", ParseIntPipe) id: number, + @Param("competencyId", ParseIntPipe) competencyId: number + ): Promise { + try { + this.logger.log( + `Initiated deleting admin-competency with id #${id} and competency id #${competencyId}` + ); + + const adminCompetency = await this.adminCompetencyService.remove( + id, + competencyId + ); + + this.logger.log( + `Successfully deleted admin-competency with id #${id} and competency id #${competencyId}` + ); + + return res.status(HttpStatus.OK).send({ + message: `Successfully deleted admin-competency with id #${id} and competency id #${competencyId}`, + data: adminCompetency, + }); + } catch (error) { + this.logger.error( + `Failed to delete admin-competency with id #${id} and competency id #${competencyId}`, + error + ); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: + errorMessage || + `Failed to delete admin-competency with id #${id} and competency id #${competencyId}`, + }); + } + } +} diff --git a/src/admin-competency/admin-competency.module.ts b/src/admin-competency/admin-competency.module.ts new file mode 100644 index 0000000..cd07b97 --- /dev/null +++ b/src/admin-competency/admin-competency.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { AdminCompetencyService } from './admin-competency.service'; +import { AdminCompetencyController } from './admin-competency.controller'; + +@Module({ + controllers: [AdminCompetencyController], + providers: [AdminCompetencyService] +}) +export class AdminCompetencyModule {} diff --git a/src/admin-competency/admin-competency.service.spec.ts b/src/admin-competency/admin-competency.service.spec.ts new file mode 100644 index 0000000..47c246c --- /dev/null +++ b/src/admin-competency/admin-competency.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AdminCompetencyService } from './admin-competency.service'; + +describe('AdminCompetencyService', () => { + let service: AdminCompetencyService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [AdminCompetencyService], + }).compile(); + + service = module.get(AdminCompetencyService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/admin-competency/admin-competency.service.ts b/src/admin-competency/admin-competency.service.ts new file mode 100644 index 0000000..5ef725d --- /dev/null +++ b/src/admin-competency/admin-competency.service.ts @@ -0,0 +1,66 @@ +import { Injectable, NotFoundException } from "@nestjs/common"; +import { PrismaService } from "src/prisma/prisma.service"; +import { CreateAdminCompetencyDto, UpdateAdminCompetencyDto } from "./dto"; + +@Injectable() +export class AdminCompetencyService { + constructor(private prisma: PrismaService) {} + + public async create(createAdminCompetencyDto: CreateAdminCompetencyDto) { + console.log("createAdminCompetencyDto:", createAdminCompetencyDto); + const adminCompetency = await this.prisma.adminCompetency.create({ + data: createAdminCompetencyDto, + }); + return adminCompetency; + } + + public async findAll() { + const adminCompetency = await this.prisma.adminCompetency.findMany(); + return adminCompetency; + } + + public async findOne(id: number, competencyId: number) { + const adminCompetency = await this.prisma.adminCompetency.findUnique({ + where: { + id_competencyId: { + id, + competencyId, + }, + }, + }); + if (!adminCompetency) + throw new NotFoundException( + `Admin competency with id #${id} and competency id #${competencyId} not found ` + ); + return adminCompetency; + } + + public async update( + id: number, + competencyId: number, + updateAdminCompetencyDto: UpdateAdminCompetencyDto + ) { + const updatedAdminCompetency = await this.prisma.adminCompetency.update({ + where: { + id_competencyId: { + id, + competencyId, + }, + }, + data: updateAdminCompetencyDto, + }); + return updatedAdminCompetency; + } + + public async remove(id: number, competencyId: number) { + const adminCompetency = await this.prisma.adminCompetency.delete({ + where: { + id_competencyId: { + id, + competencyId, + }, + }, + }); + return adminCompetency; + } +} diff --git a/src/admin-competency/dto/create-admin-competency.dto.ts b/src/admin-competency/dto/create-admin-competency.dto.ts new file mode 100644 index 0000000..eedd262 --- /dev/null +++ b/src/admin-competency/dto/create-admin-competency.dto.ts @@ -0,0 +1,45 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { + IsArray, + IsDate, + IsInt, + IsNotEmpty, + IsOptional, + IsString, +} from "class-validator"; + +export class CreateAdminCompetencyDto { + @IsNotEmpty() + @IsInt() + competencyId: number; + + @IsNotEmpty() + @IsString() + name: string; + + @IsNotEmpty() + @IsString() + @IsOptional() + description?: string; + + @ApiProperty({ + type: "array", + example: [{ competencyLevelNumber: 1, competencyLevelName: "Level-1" }], + }) + @IsNotEmpty() + @IsArray() + competencyLevels: Array<{ + competencyLevelNumber: number; + competencyLevelName: string; + }>; + + @IsNotEmpty() + @IsDate() + @IsOptional() + createdAt?: Date; + + @IsNotEmpty() + @IsDate() + @IsOptional() + updatedAt?: Date; +} diff --git a/src/admin-competency/dto/index.ts b/src/admin-competency/dto/index.ts new file mode 100644 index 0000000..3e53146 --- /dev/null +++ b/src/admin-competency/dto/index.ts @@ -0,0 +1,3 @@ +export * from "./create-admin-competency.dto"; +export * from "./response-admin-competency.dto"; +export * from "./update-admin-competency.dto"; diff --git a/src/admin-competency/dto/response-admin-competency.dto.ts b/src/admin-competency/dto/response-admin-competency.dto.ts new file mode 100644 index 0000000..47db330 --- /dev/null +++ b/src/admin-competency/dto/response-admin-competency.dto.ts @@ -0,0 +1,26 @@ +export class AdminCompetencyResponse { + id: number; + competencyId: number; + name: string; + description?: string; + competencyLevels: CompetencyLevels[]; + createdAt?: Date; + updatedAt?: Date; +} + +class CompetencyLevels { + competencyLevelNumber: number; + competencyLevelName: string; +} + +export class AdminCompetencyResponseDto { + data?: AdminCompetencyResponse; + message: string; + statusCode?: number; +} + +export class AdminCompetencyArrayResponseDto { + data?: AdminCompetencyResponse[]; + message: string; + statusCode?: number; +} diff --git a/src/admin-competency/dto/update-admin-competency.dto.ts b/src/admin-competency/dto/update-admin-competency.dto.ts new file mode 100644 index 0000000..97fa4ac --- /dev/null +++ b/src/admin-competency/dto/update-admin-competency.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateAdminCompetencyDto } from './create-admin-competency.dto'; + +export class UpdateAdminCompetencyDto extends PartialType(CreateAdminCompetencyDto) {} diff --git a/src/app.module.ts b/src/app.module.ts index 8ba6ef1..a8e353e 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -11,6 +11,7 @@ import { SurveyScoreModule } from "./survey-score/survey-score.module"; import { QuestionBankModule } from "./question-bank/question-bank.module"; import { SurveyConfigModule } from "./survey-config/survey-config.module"; import { SurveyCycleParameterModule } from "./survey-cycle-parameter/survey-cycle-parameter.module"; +import { AdminCompetencyModule } from "./admin-competency/admin-competency.module"; @Module({ imports: [ @@ -25,6 +26,7 @@ import { SurveyCycleParameterModule } from "./survey-cycle-parameter/survey-cycl QuestionBankModule, SurveyConfigModule, SurveyCycleParameterModule, + AdminCompetencyModule, ], controllers: [AppController], providers: [AppService, PrismaService], diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 2a1e8e3..b70c1a5 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,4 +1,5 @@ import { HttpStatus } from "@nestjs/common"; +import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library"; export const validationOptions = { whitelist: true, @@ -13,21 +14,29 @@ export function getPrismaErrorStatusAndMessage(error: any): { errorMessage: string | undefined; statusCode: number; } { - const errorCode = error?.code || "DEFAULT_ERROR_CODE"; + if (error instanceof PrismaClientKnownRequestError) { + const errorCode = error?.code || "DEFAULT_ERROR_CODE"; - const errorCodeMap: Record = { - P2000: HttpStatus.BAD_REQUEST, - P2002: HttpStatus.CONFLICT, - P2003: HttpStatus.CONFLICT, - P2025: HttpStatus.NOT_FOUND, - DEFAULT_ERROR_CODE: - error?.status || - error?.response?.status || - HttpStatus.INTERNAL_SERVER_ERROR, - }; + const errorCodeMap: Record = { + P2000: HttpStatus.BAD_REQUEST, + P2002: HttpStatus.CONFLICT, + P2003: HttpStatus.CONFLICT, + P2025: HttpStatus.NOT_FOUND, + }; + + const statusCode = + errorCodeMap[errorCode] || HttpStatus.INTERNAL_SERVER_ERROR; + const errorMessage = error.message.split("\n").pop(); - const statusCode = errorCodeMap[errorCode]; - const errorMessage = error.message.split("\n").pop(); + return { statusCode, errorMessage }; + } - return { statusCode, errorMessage }; + const statusCode = + error?.status || + error?.response?.status || + HttpStatus.INTERNAL_SERVER_ERROR; + return { + statusCode, + errorMessage: error.message, + }; } From b9ce3387e84f4b01c7c12372144f78e249c5346a Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Thu, 19 Oct 2023 14:21:43 +0530 Subject: [PATCH 045/175] Response tracker JSON data fix --- .../dto/create-response-tracker.dto.ts | 62 +++++++++++-------- .../enums/response-tracker.enums.ts | 5 ++ .../response-tracker.service.ts | 18 +++++- 3 files changed, 56 insertions(+), 29 deletions(-) create mode 100644 src/response-tracker/enums/response-tracker.enums.ts diff --git a/src/response-tracker/dto/create-response-tracker.dto.ts b/src/response-tracker/dto/create-response-tracker.dto.ts index 84eb00e..fa33edf 100644 --- a/src/response-tracker/dto/create-response-tracker.dto.ts +++ b/src/response-tracker/dto/create-response-tracker.dto.ts @@ -1,15 +1,31 @@ import { ApiProperty } from "@nestjs/swagger"; import { ResponseTrackerStatusEnum } from "@prisma/client"; +import { Type } from "class-transformer"; import { + IsArray, + IsEnum, IsInt, IsNotEmpty, + IsNumber, IsOptional, - IsEnum, - IsArray, IsUUID, - IsNumber, - IsString, + ValidateNested, } from "class-validator"; +import { AnswerEnum } from "../enums/response-tracker.enums"; + +export class responseObject { + @IsNumber() + @IsNotEmpty() + questionId: number; + + @ApiProperty({ + enum: AnswerEnum, + example: `${AnswerEnum.YES} || ${AnswerEnum.NO} || ${AnswerEnum.DO_NOT_KNOW}`, + }) + @IsEnum(AnswerEnum) + @IsNotEmpty() + answer: AnswerEnum; +} export class CreateResponseTrackerDto { @ApiProperty({ type: "integer", example: 1 }) @@ -28,33 +44,27 @@ export class CreateResponseTrackerDto { assessorId: string; @ApiProperty({ - type: "array", - example: [{ questionId: "integer", answer: "Yes" || "No" || "DoNotKnow" }], + type: responseObject, + isArray: true, + example: [ + { + questionId: 1, + answer: `${AnswerEnum.YES} || ${AnswerEnum.NO} || ${AnswerEnum.DO_NOT_KNOW}`, + }, + ], }) - @IsOptional() @IsArray() - responseJson?: Array<{ questionId: number; answer: AnswerEnum }>; + @ValidateNested({ each: true }) + @Type(() => responseObject) + responseJson?: responseObject[] = []; - @ApiProperty({ enum: ResponseTrackerStatusEnum, example: "Pending" }) + @ApiProperty({ + enum: ResponseTrackerStatusEnum, + example: + ResponseTrackerStatusEnum.PENDING || ResponseTrackerStatusEnum.COMPLETED, + }) @IsNotEmpty() @IsEnum(ResponseTrackerStatusEnum) status: ResponseTrackerStatusEnum; } - -enum AnswerEnum { - Yes, - No, - DoNotKnow, -} - -export class responseObject { - @IsNumber() - @IsNotEmpty() - questionId: number; - - @ApiProperty({ enum: AnswerEnum, example: "Yes" }) - @IsString() - @IsNotEmpty() - answer: AnswerEnum; -} diff --git a/src/response-tracker/enums/response-tracker.enums.ts b/src/response-tracker/enums/response-tracker.enums.ts new file mode 100644 index 0000000..8ff0435 --- /dev/null +++ b/src/response-tracker/enums/response-tracker.enums.ts @@ -0,0 +1,5 @@ +export enum AnswerEnum { + YES = "Yes", + NO = "No", + DO_NOT_KNOW = "DoNotKnow", +} diff --git a/src/response-tracker/response-tracker.service.ts b/src/response-tracker/response-tracker.service.ts index 05fd727..573228b 100644 --- a/src/response-tracker/response-tracker.service.ts +++ b/src/response-tracker/response-tracker.service.ts @@ -1,15 +1,21 @@ import { Injectable, NotFoundException } from "@nestjs/common"; +import { PrismaService } from "src/prisma/prisma.service"; import { CreateResponseTrackerDto } from "./dto/create-response-tracker.dto"; import { UpdateResponseTrackerDto } from "./dto/update-response-tracker.dto"; -import { PrismaService } from "src/prisma/prisma.service"; @Injectable() export class ResponseTrackerService { constructor(private prisma: PrismaService) {} public async create(createResponseTrackerDto: CreateResponseTrackerDto) { + const responseJson = JSON.stringify(createResponseTrackerDto.responseJson); + const payload = { + ...createResponseTrackerDto, + responseJson: JSON.parse(responseJson), + }; + return await this.prisma.responseTracker.create({ - data: createResponseTrackerDto, + data: payload, }); } @@ -66,9 +72,15 @@ export class ResponseTrackerService { id: number, updateResponseTrackerDto: UpdateResponseTrackerDto ) { + const responseJson = JSON.stringify(updateResponseTrackerDto.responseJson); + const payload = { + ...updateResponseTrackerDto, + responseJson: JSON.parse(responseJson), + }; + return await this.prisma.responseTracker.update({ where: { id }, - data: updateResponseTrackerDto, + data: payload, }); } From d08d5a375cd0ca827c9204eadff8c40bd01ae19f Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Thu, 19 Oct 2023 16:04:44 +0530 Subject: [PATCH 046/175] Added UserMetaData APIs. --- src/app.module.ts | 2 + .../mock-user/mock-user.service.ts | 1 + src/survey-config/survey-config.service.ts | 4 +- src/user-metadata/dto/index.ts | 1 + .../dto/user-metadata-filter.dto.ts | 37 ++++ .../dto/user-metadata-response.dto.ts | 13 ++ .../user-metadata.controller.spec.ts | 18 ++ src/user-metadata/user-metadata.controller.ts | 167 ++++++++++++++++ src/user-metadata/user-metadata.module.ts | 9 + src/user-metadata/user-metadata.service.ts | 184 ++++++++++++++++++ 10 files changed, 434 insertions(+), 2 deletions(-) create mode 100644 src/user-metadata/dto/index.ts create mode 100644 src/user-metadata/dto/user-metadata-filter.dto.ts create mode 100644 src/user-metadata/dto/user-metadata-response.dto.ts create mode 100644 src/user-metadata/user-metadata.controller.spec.ts create mode 100644 src/user-metadata/user-metadata.controller.ts create mode 100644 src/user-metadata/user-metadata.module.ts create mode 100644 src/user-metadata/user-metadata.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 8ba6ef1..905905f 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -11,6 +11,7 @@ import { SurveyScoreModule } from "./survey-score/survey-score.module"; import { QuestionBankModule } from "./question-bank/question-bank.module"; import { SurveyConfigModule } from "./survey-config/survey-config.module"; import { SurveyCycleParameterModule } from "./survey-cycle-parameter/survey-cycle-parameter.module"; +import { UserMetadataModule } from './user-metadata/user-metadata.module'; @Module({ imports: [ @@ -25,6 +26,7 @@ import { SurveyCycleParameterModule } from "./survey-cycle-parameter/survey-cycl QuestionBankModule, SurveyConfigModule, SurveyCycleParameterModule, + UserMetadataModule, ], controllers: [AppController], providers: [AppService, PrismaService], diff --git a/src/mockModules/mock-user/mock-user.service.ts b/src/mockModules/mock-user/mock-user.service.ts index 983d530..9bb5539 100644 --- a/src/mockModules/mock-user/mock-user.service.ts +++ b/src/mockModules/mock-user/mock-user.service.ts @@ -66,6 +66,7 @@ export class MockUserService { role: true, userName: true, profilePicture: true, + designation:true, Level: { select: { id: true, diff --git a/src/survey-config/survey-config.service.ts b/src/survey-config/survey-config.service.ts index 37c2642..c6ad3db 100644 --- a/src/survey-config/survey-config.service.ts +++ b/src/survey-config/survey-config.service.ts @@ -19,7 +19,7 @@ export class SurveyConfigService { async getAllSurveyConfig(filter: SurveyConfigFilterDto) { const { departmentId, startTime, endTime, limit = 10, offset = 0 } = filter; - return this.prisma.surveyConfig.findMany({ + return await this.prisma.surveyConfig.findMany({ where: { departmentId: departmentId ?? undefined, // Optional departmentId filter startTime: startTime ?? undefined, // Optional startTime filter @@ -46,7 +46,7 @@ export class SurveyConfigService { ); } // Update the survey config by the id - return this.prisma.surveyConfig.update({ + return await this.prisma.surveyConfig.update({ where: { id: surveyConfigId, }, diff --git a/src/user-metadata/dto/index.ts b/src/user-metadata/dto/index.ts new file mode 100644 index 0000000..235cefb --- /dev/null +++ b/src/user-metadata/dto/index.ts @@ -0,0 +1 @@ +export * from './user-metadata-filter.dto' \ No newline at end of file diff --git a/src/user-metadata/dto/user-metadata-filter.dto.ts b/src/user-metadata/dto/user-metadata-filter.dto.ts new file mode 100644 index 0000000..14e89b9 --- /dev/null +++ b/src/user-metadata/dto/user-metadata-filter.dto.ts @@ -0,0 +1,37 @@ +import { + IsBoolean, + IsInt, + IsNotEmpty, + IsOptional, + IsString, +} from "class-validator"; + +export class UserMetadataFilterDto { + @IsBoolean() + @IsOptional() + @IsNotEmpty() + isNewEmployee: boolean; + + @IsInt() + @IsOptional() + @IsNotEmpty() + departmentId: number; + + @IsString() + @IsOptional() + @IsNotEmpty() + designation: string; + + @IsBoolean() + @IsOptional() + @IsNotEmpty() + isAdmin: boolean; + + @IsOptional() + @IsInt() + limit?: number; + + @IsOptional() + @IsInt() + offset?: number; +} diff --git a/src/user-metadata/dto/user-metadata-response.dto.ts b/src/user-metadata/dto/user-metadata-response.dto.ts new file mode 100644 index 0000000..bc1905c --- /dev/null +++ b/src/user-metadata/dto/user-metadata-response.dto.ts @@ -0,0 +1,13 @@ +import { IsBoolean, IsDate, IsInt, IsJSON, IsNotEmpty, IsOptional, IsString, IsUUID } from "class-validator"; +import { ResponseSurveyFormDto } from "src/survey-form/dto"; + +export class ResponseUserMetadataDto { + + readonly userId: string; + readonly designation?: string | null; + readonly departmentId: number; + readonly isAdmin: boolean; + readonly isNewEmployee: boolean; + readonly dateOfJoining: Date; + readonly SurveyForms?: ResponseSurveyFormDto[] +} \ No newline at end of file diff --git a/src/user-metadata/user-metadata.controller.spec.ts b/src/user-metadata/user-metadata.controller.spec.ts new file mode 100644 index 0000000..5cc58f2 --- /dev/null +++ b/src/user-metadata/user-metadata.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { UserMetadataController } from './user-metadata.controller'; + +describe('UserMetadataController', () => { + let controller: UserMetadataController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [UserMetadataController], + }).compile(); + + controller = module.get(UserMetadataController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/user-metadata/user-metadata.controller.ts b/src/user-metadata/user-metadata.controller.ts new file mode 100644 index 0000000..a953fae --- /dev/null +++ b/src/user-metadata/user-metadata.controller.ts @@ -0,0 +1,167 @@ +import { + Body, + Controller, + Delete, + Get, + HttpStatus, + Logger, + Param, + ParseUUIDPipe, + Post, + Res, +} from "@nestjs/common"; +import { UserMetadataService } from "./user-metadata.service"; +import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { ResponseUserMetadataDto } from "./dto/user-metadata-response.dto"; +import { UserMetadataFilterDto } from "./dto"; +import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; + +@Controller("user-metadata") +@ApiTags("user-metadata") +export class UserMetadataController { + constructor(private readonly userMetadataService: UserMetadataService) {} + private readonly logger = new Logger(UserMetadataController.name); + + @Post(":userId") + @ApiOperation({ summary: "Create or Update User Metadata" }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseUserMetadataDto }) + async createOrUpdateUserMetadata( + @Res() res, + @Param("userId", ParseUUIDPipe) userId: string + ): Promise { + try { + this.logger.log(`Creating or Updating User Metadata`); + + const userMetadata = + await this.userMetadataService.createOrUpdateUserMetadata(userId); + + this.logger.log(`Successfully created the new survey config.`); + + return res.status(HttpStatus.OK).json({ + message: "UserMetadata created or updated successfully", + data: userMetadata, + }); + } catch (error) { + this.logger.error(`Failed to Create or Update User Metadata.`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed to Create or Update User Metadata.`, + }); + } + } + + @Get(":userId") + @ApiOperation({ summary: "Fetch User Metadata by userId" }) + @ApiResponse({ status: HttpStatus.FOUND, type: ResponseUserMetadataDto }) + async findUserMetadata( + @Res() res, + @Param("userId", ParseUUIDPipe) userId: string + ): Promise { + try { + this.logger.log(`Fetching User Metadata`); + + const userMetadata = await this.userMetadataService.findUserMetadataById( + userId + ); + + this.logger.log(`Successfully fetched UserMetadata for id #${userId}.`); + + return res.status(HttpStatus.OK).json({ + message: "UserMetadata fetched successfully", + data: userMetadata, + }); + } catch (error) { + this.logger.error( + `Failed to fetch UserMetadata for id #${userId}.`, + error + ); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: + errorMessage || `Failed to fetch UserMetadata for id #${userId}.`, + }); + } + } + + @Get() + @ApiOperation({ + summary: "Fetch User Metadata accordind to the filter specified.", + }) + @ApiResponse({ + status: HttpStatus.FOUND, + type: ResponseUserMetadataDto, + isArray: true, + }) + async findManyUserMetadata( + @Res() res, + @Body() filter: UserMetadataFilterDto + ): Promise { + try { + this.logger.log(`Fetching UserMetadata(s)`); + + const userMetadata = await this.userMetadataService.findManyUserMetadata( + filter + ); + + this.logger.log(`Successfully fetched UserMetadata(s).`); + + return res.status(HttpStatus.OK).json({ + message: "UserMetadata(s) fetched successfully", + data: userMetadata, + }); + } catch (error) { + this.logger.error(`Failed to fetch UserMetadats(s).`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed to fetch UserMetadata(s).`, + }); + } + } + + @Delete(":userId") + @ApiOperation({ summary: "Delete User Metadata." }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseUserMetadataDto }) + async deleteUserMetadata( + @Res() res, + @Param("userId", ParseUUIDPipe) userId: string + ): Promise { + try { + this.logger.log(`Deleting UserMetadata.`); + + const userMetadata = await this.userMetadataService.deleteUserMetadata( + userId + ); + + this.logger.log( + `Successfully deleted UserMetadata with userId ${userId}.` + ); + + return res.status(HttpStatus.OK).json({ + message: "UserMetadata deleted successfully", + data: userMetadata, + }); + } catch (error) { + this.logger.error(`Failed to delete UserMetadata.`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed to delete UserMetadata.`, + }); + } + } +} diff --git a/src/user-metadata/user-metadata.module.ts b/src/user-metadata/user-metadata.module.ts new file mode 100644 index 0000000..3e9b748 --- /dev/null +++ b/src/user-metadata/user-metadata.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { UserMetadataController } from './user-metadata.controller'; +import { UserMetadataService } from './user-metadata.service'; + +@Module({ + controllers: [UserMetadataController], + providers: [UserMetadataService] +}) +export class UserMetadataModule {} diff --git a/src/user-metadata/user-metadata.service.ts b/src/user-metadata/user-metadata.service.ts new file mode 100644 index 0000000..6c6112a --- /dev/null +++ b/src/user-metadata/user-metadata.service.ts @@ -0,0 +1,184 @@ +import { NotFoundException } from "@nestjs/common"; +import { TimeUnitsEnum, UserRolesEnum } from "@prisma/client"; +import { MockUserService } from "src/mockModules/mock-user/mock-user.service"; +import { PrismaService } from "src/prisma/prisma.service"; +import { SurveyConfigService } from "src/survey-config/survey-config.service"; +import { UserMetadataFilterDto } from "./dto"; + +export class UserMetadataService { + constructor( + private prisma: PrismaService, + private mockUser: MockUserService, + private surveyConfig: SurveyConfigService + ) {} + + async createOrUpdateUserMetadata(userId: string) { + let userMetadata = await this.prisma.userMetadata.findUnique({ + where: { userId }, + select: this.metadataSelect, + }); + const user = await this.mockUser.findOne(userId); + const isNewEmployee = await this.isEmployeeNew( + user.createdAt, + user.Department!.id + ); + const userObj = { + userId: user.id, + userName: user.userName, + isNewEmployee: isNewEmployee, + dateOfJoining: user.createdAt, + isAdmin: user.role == UserRolesEnum.ADMIN ? true : false, + departmentId: user.Department!.id, + designation: user.designation, + }; + + if (!userMetadata) { + userMetadata = await this.prisma.userMetadata.create({ + data: userObj, + select: this.metadataSelect, + }); + } else { + if (userMetadata !== userObj) { + userMetadata = await this.prisma.userMetadata.update({ + where: { userId }, + data: { + isNewEmployee: isNewEmployee, + dateOfJoining: user.createdAt, + isAdmin: user.role == UserRolesEnum.ADMIN ? true : false, + departmentId: user.Department!.id, + designation: user.designation, + }, + select: this.metadataSelect, + }); + } + } + return userMetadata; + } + + async findUserMetadataById(userId: string) { + const userMetadata = await this.prisma.userMetadata.findUnique({ + where: { userId }, + select: this.metadataSelect, + }); + if (!userMetadata) + throw new NotFoundException( + `UserMetadata for user with id #${userId} not found` + ); + return userMetadata; + } + + async findManyUserMetadata(filter: UserMetadataFilterDto) { + const { + isAdmin, + isNewEmployee, + departmentId, + designation, + limit = 10, + offset = 0, + } = filter; + return await this.prisma.userMetadata.findMany({ + where: { + isAdmin: isAdmin ?? undefined, + isNewEmployee: isNewEmployee ?? undefined, + departmentId: departmentId ?? undefined, + designation: designation ?? undefined, + }, + skip: +offset, + take: +limit, + select: this.metadataSelect, + }); + } + + async deleteUserMetadata(userId: string) { + return await this.prisma.userMetadata.delete({ + where: { userId }, + select: this.metadataSelect, + }); + } + + async isEmployeeNew( + dateOfJoining: Date, + departmentId: number + ): Promise { + const surveyConfig = await this.surveyConfig.getAllSurveyConfig({ + departmentId, + }); + + if (surveyConfig.length == 0) + throw new NotFoundException( + `Survey Config for the department with Id #${departmentId} not found.` + ); + + let dateToBeCompared = new Date(dateOfJoining); + const today = new Date(); + const { onboardingTime, onboardingTimeUnit } = surveyConfig[0]; + + if (onboardingTimeUnit === TimeUnitsEnum.DAY) { + dateToBeCompared.setDate(dateToBeCompared.getDate() + onboardingTime); + } else if (onboardingTimeUnit === TimeUnitsEnum.MONTH) { + dateToBeCompared.setMonth(dateToBeCompared.getMonth() + onboardingTime); + } else { + dateToBeCompared.setFullYear( + dateToBeCompared.getFullYear() + onboardingTime + ); + } + + return dateToBeCompared <= today; + } + + async getSurveyFormsByUserId(userId: string) { + const userMetadata = await this.prisma.userMetadata.findUnique({ + where: { + userId, + }, + select: { + userId: true, + dateOfJoining: true, + departmentId: true, + designation: true, + isNewEmployee: true, + SurveyForm: { + orderBy: { + createdAt: "asc" + }, + select: { + id: true, + surveyCycleParameterId: true, + questionsJson: true, + status: true, + overallScore: true, + sunbirdCredentialIds: true, + ResponseTracker: { + select: { + id: true, + Assessor: { + select:{ + userId: true, + userName: true, + } + }, + responseJson: true, + status: true, + }, + }, + }, + }, + }, + }); + if (!userMetadata) + throw new NotFoundException( + `UserMetadata for user with id #${userId} not found` + ); + return userMetadata; + } + + metadataSelect = { + userId: true, + userName: true, + dateOfJoining: true, + departmentId: true, + isAdmin: true, + designation: true, + isNewEmployee: true, + }; +} From c7ce31ab59f89f52c410cf2b637260929ff99c2b Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Thu, 19 Oct 2023 16:05:48 +0530 Subject: [PATCH 047/175] UserMetadata schema update. --- prisma/schema.prisma | 1 + 1 file changed, 1 insertion(+) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 5cc78c2..4d30a17 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -14,6 +14,7 @@ datasource db { model UserMetadata { id Int @unique() @default(autoincrement()) userId String @unique() @db.Uuid + userName String @unique() isNewEmployee Boolean departmentId Int Department AdminDepartment @relation(fields: [departmentId], references: [departmentId]) From 8e243290df9b584d684aa7cead143aca7e4b2f70 Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Thu, 19 Oct 2023 16:13:08 +0530 Subject: [PATCH 048/175] Updated DTO and APIs for JSON data --- .../admin-competency.service.ts | 33 +++++++++++++++---- .../dto/create-admin-competency.dto.ts | 22 ++++++++++--- .../dto/response-admin-competency.dto.ts | 7 ++-- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/src/admin-competency/admin-competency.service.ts b/src/admin-competency/admin-competency.service.ts index 5ef725d..ded7cc4 100644 --- a/src/admin-competency/admin-competency.service.ts +++ b/src/admin-competency/admin-competency.service.ts @@ -7,11 +7,18 @@ export class AdminCompetencyService { constructor(private prisma: PrismaService) {} public async create(createAdminCompetencyDto: CreateAdminCompetencyDto) { - console.log("createAdminCompetencyDto:", createAdminCompetencyDto); - const adminCompetency = await this.prisma.adminCompetency.create({ - data: createAdminCompetencyDto, + const competencyLevels = JSON.stringify( + createAdminCompetencyDto.competencyLevels + ); + + const payload = { + ...createAdminCompetencyDto, + competencyLevels: JSON.parse(competencyLevels), + }; + + return await this.prisma.adminCompetency.create({ + data: payload, }); - return adminCompetency; } public async findAll() { @@ -40,16 +47,28 @@ export class AdminCompetencyService { competencyId: number, updateAdminCompetencyDto: UpdateAdminCompetencyDto ) { - const updatedAdminCompetency = await this.prisma.adminCompetency.update({ + const competencyLevels = JSON.stringify( + updateAdminCompetencyDto?.competencyLevels + ); + + let payload = { + ...updateAdminCompetencyDto, + competencyLevels: JSON.parse(competencyLevels || "[]"), + }; + + if (!competencyLevels) { + delete payload.competencyLevels; + } + + return await this.prisma.adminCompetency.update({ where: { id_competencyId: { id, competencyId, }, }, - data: updateAdminCompetencyDto, + data: payload, }); - return updatedAdminCompetency; } public async remove(id: number, competencyId: number) { diff --git a/src/admin-competency/dto/create-admin-competency.dto.ts b/src/admin-competency/dto/create-admin-competency.dto.ts index eedd262..ff475bd 100644 --- a/src/admin-competency/dto/create-admin-competency.dto.ts +++ b/src/admin-competency/dto/create-admin-competency.dto.ts @@ -1,4 +1,5 @@ import { ApiProperty } from "@nestjs/swagger"; +import { Type } from "class-transformer"; import { IsArray, IsDate, @@ -6,8 +7,19 @@ import { IsNotEmpty, IsOptional, IsString, + ValidateNested, } from "class-validator"; +export class CompetencyLevels { + @IsInt() + @IsNotEmpty() + competencyLevelNumber: number; + + @IsString() + @IsNotEmpty() + competencyLevelName: string; +} + export class CreateAdminCompetencyDto { @IsNotEmpty() @IsInt() @@ -23,15 +35,15 @@ export class CreateAdminCompetencyDto { description?: string; @ApiProperty({ - type: "array", + type: CompetencyLevels, + isArray: true, example: [{ competencyLevelNumber: 1, competencyLevelName: "Level-1" }], }) @IsNotEmpty() @IsArray() - competencyLevels: Array<{ - competencyLevelNumber: number; - competencyLevelName: string; - }>; + @ValidateNested({ each: true }) + @Type(() => CompetencyLevels) + competencyLevels: CompetencyLevels[]; @IsNotEmpty() @IsDate() diff --git a/src/admin-competency/dto/response-admin-competency.dto.ts b/src/admin-competency/dto/response-admin-competency.dto.ts index 47db330..7661623 100644 --- a/src/admin-competency/dto/response-admin-competency.dto.ts +++ b/src/admin-competency/dto/response-admin-competency.dto.ts @@ -1,3 +1,5 @@ +import { CompetencyLevels } from "./create-admin-competency.dto"; + export class AdminCompetencyResponse { id: number; competencyId: number; @@ -8,11 +10,6 @@ export class AdminCompetencyResponse { updatedAt?: Date; } -class CompetencyLevels { - competencyLevelNumber: number; - competencyLevelName: string; -} - export class AdminCompetencyResponseDto { data?: AdminCompetencyResponse; message: string; From 46688818b98a9823f33ebd3297c4f86491cbc9cc Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Thu, 19 Oct 2023 16:19:45 +0530 Subject: [PATCH 049/175] fixed Patch API --- src/response-tracker/response-tracker.service.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/response-tracker/response-tracker.service.ts b/src/response-tracker/response-tracker.service.ts index 573228b..8e70ca0 100644 --- a/src/response-tracker/response-tracker.service.ts +++ b/src/response-tracker/response-tracker.service.ts @@ -73,11 +73,14 @@ export class ResponseTrackerService { updateResponseTrackerDto: UpdateResponseTrackerDto ) { const responseJson = JSON.stringify(updateResponseTrackerDto.responseJson); + const payload = { ...updateResponseTrackerDto, - responseJson: JSON.parse(responseJson), + responseJson: JSON.parse(responseJson || "[]"), }; + if (!responseJson) delete payload.responseJson; + return await this.prisma.responseTracker.update({ where: { id }, data: payload, From 22a730675018dff32e496f2ffdc93a70aa17f541 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Thu, 19 Oct 2023 16:28:20 +0530 Subject: [PATCH 050/175] Changed update API as a post request. Added API to connect roles to designation. --- .../dto/response-designation.dto.ts | 3 ++ .../mock-designation.controller.ts | 19 +++++++++++-- .../mock-designation.service.ts | 28 +++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/mockModules/mock-designation/dto/response-designation.dto.ts b/src/mockModules/mock-designation/dto/response-designation.dto.ts index e96348b..44e81e9 100644 --- a/src/mockModules/mock-designation/dto/response-designation.dto.ts +++ b/src/mockModules/mock-designation/dto/response-designation.dto.ts @@ -1,5 +1,8 @@ +import { ResponseMockRoleDto } from "src/mockModules/mock-role/dto"; + export class ResponseDesignationDto { readonly id: number; readonly name: string; readonly description?: string | null; + readonly Role: ResponseMockRoleDto[] } diff --git a/src/mockModules/mock-designation/mock-designation.controller.ts b/src/mockModules/mock-designation/mock-designation.controller.ts index 4f8badd..3f3d34f 100644 --- a/src/mockModules/mock-designation/mock-designation.controller.ts +++ b/src/mockModules/mock-designation/mock-designation.controller.ts @@ -5,8 +5,8 @@ import { Get, HttpStatus, Param, + Patch, Post, - Put, Res, } from "@nestjs/common"; import { MockDesignationService } from "./mock-designation.service"; @@ -69,7 +69,7 @@ export class MockDesignationController { } } - @Put(":id") + @Patch(":id") async update( @Param("id") id: number, @Body() updateDto: UpdateDesignationDto, @@ -105,4 +105,19 @@ export class MockDesignationController { .json({ message: error.meta.cause }); } } + + @Post("addRoleToDesignation/:id") + async addRoleToDesignation(@Param("id") id: number, @Res() res, @Body() {roleId}: any) { + try { + const designation = await this.designationService.addRoleToDesignation(id, roleId); + return res.status(HttpStatus.OK).json({ + data: designation, + message: `Successfully added role with id #${roleId} to the designation with id #${id}.`, + }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } } diff --git a/src/mockModules/mock-designation/mock-designation.service.ts b/src/mockModules/mock-designation/mock-designation.service.ts index a291b8c..0c35540 100644 --- a/src/mockModules/mock-designation/mock-designation.service.ts +++ b/src/mockModules/mock-designation/mock-designation.service.ts @@ -43,4 +43,32 @@ export class MockDesignationService { where: { id }, }); } + + async addRoleToDesignation(id: number, roleId: number) { + return await this.prisma.designation.update({ + where: { + id, + }, + data: { + Roles: { + connect: [{ id: roleId }], + }, + }, + select: { + id: true, + name: true, + description: true, + Roles: { + orderBy: { + createdAt: "asc", + }, + select: { + id: true, + name: true, + description: true, + }, + }, + }, + }); + } } From 0af6b4bac45c0ea4c9b86b069d67b2443cf24449 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Thu, 19 Oct 2023 16:35:16 +0530 Subject: [PATCH 051/175] Added Swagger decorators. --- .../mock-designation.controller.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/mockModules/mock-designation/mock-designation.controller.ts b/src/mockModules/mock-designation/mock-designation.controller.ts index 3f3d34f..96aee06 100644 --- a/src/mockModules/mock-designation/mock-designation.controller.ts +++ b/src/mockModules/mock-designation/mock-designation.controller.ts @@ -15,7 +15,7 @@ import { ResponseDesignationDto, UpdateDesignationDto, } from "./dto"; -import { ApiTags } from "@nestjs/swagger"; +import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; @Controller("designation") @ApiTags("mockFracService/designation") @@ -23,6 +23,8 @@ export class MockDesignationController { constructor(private readonly designationService: MockDesignationService) {} @Post() + @ApiOperation({ summary: "Create mock designation." }) + @ApiResponse({ status: HttpStatus.CREATED, type: ResponseDesignationDto }) async create(@Body() createDto: CreateDesignationDto, @Res() res) { try { const designation = await this.designationService.createDesignation( @@ -40,6 +42,8 @@ export class MockDesignationController { } @Get() + @ApiOperation({ summary: "Fetch all mock designations." }) + @ApiResponse({ status: HttpStatus.FOUND, type: ResponseDesignationDto, isArray: true }) async findAll(@Res() res): Promise { try { const designation = await this.designationService.findAllDesignations(); @@ -55,6 +59,8 @@ export class MockDesignationController { } @Get(":id") + @ApiOperation({ summary: "Fetch mock designation by id." }) + @ApiResponse({ status: HttpStatus.FOUND, type: ResponseDesignationDto}) async findOne(@Param("id") id: number, @Res() res) { try { const designation = await this.designationService.findDesignationById(id); @@ -70,6 +76,8 @@ export class MockDesignationController { } @Patch(":id") + @ApiOperation({ summary: "Update mock designation by id." }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseDesignationDto}) async update( @Param("id") id: number, @Body() updateDto: UpdateDesignationDto, @@ -92,6 +100,8 @@ export class MockDesignationController { } @Delete(":id") + @ApiOperation({ summary: "Delete mock designation by id." }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseDesignationDto}) async remove(@Param("id") id: number, @Res() res) { try { const designation = await this.designationService.removeDesignation(id); @@ -107,6 +117,8 @@ export class MockDesignationController { } @Post("addRoleToDesignation/:id") + @ApiOperation({ summary: "Add a Role to mock designation." }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseDesignationDto}) async addRoleToDesignation(@Param("id") id: number, @Res() res, @Body() {roleId}: any) { try { const designation = await this.designationService.addRoleToDesignation(id, roleId); From 4d13a407100d2eb26473cab33365b55776312bc1 Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Thu, 19 Oct 2023 17:28:58 +0530 Subject: [PATCH 052/175] Added the department service relation with adminDepartment. --- .../admin-department.controller.ts | 29 +++++----- .../admin-department.module.ts | 3 +- .../admin-department.service.ts | 58 ++++++++++++++----- .../dto/create-admin-department.dto.ts | 19 +----- .../dto/update-admin-department.dto.ts | 14 ++++- 5 files changed, 75 insertions(+), 48 deletions(-) diff --git a/src/admin-department/admin-department.controller.ts b/src/admin-department/admin-department.controller.ts index 22352aa..a1a2516 100644 --- a/src/admin-department/admin-department.controller.ts +++ b/src/admin-department/admin-department.controller.ts @@ -12,7 +12,7 @@ import { ParseIntPipe, } from "@nestjs/common"; import { AdminDepartmentService } from "./admin-department.service"; -import { CreateAdminDepartmentDto, FilterAdminDepartmentsDto } from "./dto/create-admin-department.dto"; +import { FilterAdminDepartmentsDto } from "./dto/create-admin-department.dto"; import { UpdateAdminDepartmentDto } from "./dto/update-admin-department.dto"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { ResponseAdminDepartmentDto } from "./dto/response-admin-department.dto"; @@ -29,22 +29,20 @@ export class AdminDepartmentController { private readonly adminDepartmentService: AdminDepartmentService ) {} - // Api for creating a new admin department - @Post() + // Api for creating a new admin department using departmentId + @Post(":departmentId") @ApiOperation({ summary: "Create a new admin department" }) // Api operation for swagger @ApiResponse({ status: HttpStatus.CREATED, type: ResponseAdminDepartmentDto }) // Api response for the swagger async createAdminDepartment( @Res() res, - @Body() createAdminDepartmentDto: CreateAdminDepartmentDto + @Param("departmentId", ParseIntPipe) departmentId: number ): Promise { try { // Log the initiation for the admin department creation this.logger.log(`Initiate to create a admin department`); const createdAdminDepartment = - await this.adminDepartmentService.createAdminDepartment( - createAdminDepartmentDto - ); + await this.adminDepartmentService.createAdminDepartment(departmentId); // Log the successful creation of the admin department this.logger.log(`Successfully created admin department.`); return res.status(HttpStatus.CREATED).json({ @@ -117,10 +115,11 @@ export class AdminDepartmentController { // Log the initiation for updating the admin department for the given id this.logger.log(`Initiated updating the admin department for id #${id}`); // Update the admin department for the id - const updateAdminDepartment = await this.adminDepartmentService.updateAdminDepartmentById( - id, - updateAdminDepartmentDto - ); + const updateAdminDepartment = + await this.adminDepartmentService.updateAdminDepartmentById( + id, + updateAdminDepartmentDto + ); // Log the successful update of the admin department with the given id this.logger.log(`Successfully updated admin department for id #${id}`); // Return response and statuscode for the successful update of the admin department @@ -130,7 +129,10 @@ export class AdminDepartmentController { }); } catch (error) { // Log the error - this.logger.error(`Failed to update admin department for id #${id}`, error); + this.logger.error( + `Failed to update admin department for id #${id}`, + error + ); const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); // get error message and status code @@ -138,7 +140,8 @@ export class AdminDepartmentController { // Return the response and status code for the failed update of the admin department return res.status(statusCode).json({ statusCode, - message: errorMessage || `Failed to update admin department for id #${id}`, + message: + errorMessage || `Failed to update admin department for id #${id}`, }); } } diff --git a/src/admin-department/admin-department.module.ts b/src/admin-department/admin-department.module.ts index b33a134..d888017 100644 --- a/src/admin-department/admin-department.module.ts +++ b/src/admin-department/admin-department.module.ts @@ -3,6 +3,7 @@ import { AdminDepartmentService } from './admin-department.service'; import { AdminDepartmentController } from './admin-department.controller'; import { PrismaService } from 'src/prisma/prisma.service'; import { PrismaModule } from 'src/prisma/prisma.module'; +import { MockDepartmentService } from 'src/mockModules/mock-department/mock-department.service'; @Module({ // Import the PrismaModule to make Prisma service available within this module @@ -12,7 +13,7 @@ import { PrismaModule } from 'src/prisma/prisma.module'; controllers: [AdminDepartmentController], // Declare the AdminDepartmentService and PrismaService as providers (singleton) for this module - providers: [AdminDepartmentService, PrismaService], + providers: [AdminDepartmentService, PrismaService,MockDepartmentService], // Export the AdminDepartmentService to make it available for other modules that import this module exports: [AdminDepartmentService] diff --git a/src/admin-department/admin-department.service.ts b/src/admin-department/admin-department.service.ts index ed8ba49..cea60fe 100644 --- a/src/admin-department/admin-department.service.ts +++ b/src/admin-department/admin-department.service.ts @@ -1,23 +1,55 @@ -import { Injectable, NotFoundException } from '@nestjs/common'; -import { CreateAdminDepartmentDto, FilterAdminDepartmentsDto } from './dto/create-admin-department.dto'; -import { UpdateAdminDepartmentDto } from './dto/update-admin-department.dto'; -import { PrismaService } from 'src/prisma/prisma.service'; +import { + ForbiddenException, + Injectable, + NotFoundException, +} from "@nestjs/common"; +import { + FilterAdminDepartmentsDto, +} from "./dto/create-admin-department.dto"; +import { UpdateAdminDepartmentDto } from "./dto/update-admin-department.dto"; +import { PrismaService } from "src/prisma/prisma.service"; +import { MockDepartmentService } from "src/mockModules/mock-department/mock-department.service"; @Injectable() export class AdminDepartmentService { - constructor(private prisma: PrismaService) {} + constructor( + private prisma: PrismaService, + private departmentService: MockDepartmentService + ) {} public async createAdminDepartment( - createAdminDepartmentDto: CreateAdminDepartmentDto + id : number, ) { - // Create a new Admin department - const createNewDepartment = await this.prisma.adminDepartment.create({ - data: createAdminDepartmentDto - }) - return createNewDepartment; + // Check the admin department exist for admin department db or not + const checkExistingAdminDepartment = + await this.prisma.adminDepartment.findUnique({ + where: { + departmentId: id, + }, + }); + // If admin department exist in db then throw an error + if (checkExistingAdminDepartment) { + throw new ForbiddenException("Admin Department already exists."); + } + + // Check if the department exist in mock department module or not + const adminDepartment = await this.departmentService.findOne( + id + ); + // If department does not exist in the department module then throw an error + if (!adminDepartment) { + throw new NotFoundException(`The Department for the #${id} not found.`); + } + return this.prisma.adminDepartment.create({ + data: { + departmentId: adminDepartment.id, + name: adminDepartment.name, + description: adminDepartment.description + }, + }); } - public async getAllAdminDepartment(filter : FilterAdminDepartmentsDto) { + public async getAllAdminDepartment(filter: FilterAdminDepartmentsDto) { const { departmentId, name, limit = 10, offset = 0 } = filter; // Get all the admin department and filter using department Id and name. return this.prisma.adminDepartment.findMany({ @@ -26,7 +58,7 @@ export class AdminDepartmentService { name: name ?? undefined, // Optional filter by Name }, skip: offset, - take: limit + take: limit, }); } diff --git a/src/admin-department/dto/create-admin-department.dto.ts b/src/admin-department/dto/create-admin-department.dto.ts index 4ca9437..d18c330 100644 --- a/src/admin-department/dto/create-admin-department.dto.ts +++ b/src/admin-department/dto/create-admin-department.dto.ts @@ -1,21 +1,4 @@ -import { IsInt, IsNotEmpty, IsOptional, IsString } from "class-validator"; - -export class CreateAdminDepartmentDto { - // Department Id associated with admin department - @IsNotEmpty() - @IsInt() - departmentId: number; - - // Name field associated with the admin department - @IsNotEmpty() - @IsString() - name: string; - - // Optional description field associated with the admin department - @IsOptional() - @IsString() - description?: string; -} +import { IsInt, IsOptional, IsString } from "class-validator"; // Admin department filter dto export class FilterAdminDepartmentsDto { diff --git a/src/admin-department/dto/update-admin-department.dto.ts b/src/admin-department/dto/update-admin-department.dto.ts index 9227d2a..a245f1e 100644 --- a/src/admin-department/dto/update-admin-department.dto.ts +++ b/src/admin-department/dto/update-admin-department.dto.ts @@ -1,4 +1,12 @@ -import { PartialType } from '@nestjs/swagger'; -import { CreateAdminDepartmentDto } from './create-admin-department.dto'; +import { IsNotEmpty, IsOptional, IsString } from "class-validator"; -export class UpdateAdminDepartmentDto extends PartialType(CreateAdminDepartmentDto) {} +export class UpdateAdminDepartmentDto { + // Optional Name field to update the name of the admin department + @IsNotEmpty() + @IsString() + name: string; + + @IsNotEmpty() + @IsString() + description: string; +} From d07b034481a57d83f6564930466114b3af8dd959 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Oct 2023 13:38:47 +0000 Subject: [PATCH 053/175] Bump @babel/traverse from 7.23.0 to 7.23.2 Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.23.0 to 7.23.2. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse) --- updated-dependencies: - dependency-name: "@babel/traverse" dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/package-lock.json b/package-lock.json index a2a62da..7e240c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -734,9 +734,9 @@ } }, "node_modules/@babel/traverse": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.0.tgz", - "integrity": "sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.22.13", @@ -4663,27 +4663,6 @@ "node": ">=8.0.0" } }, - "node_modules/eslint-scope/node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint-scope/node_modules/esrecurse/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/eslint-visitor-keys": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", From 20b1e355b5d89923005f43d7801c8a9009f35ce2 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 25 Oct 2023 11:20:29 +0530 Subject: [PATCH 054/175] UserMetadata Fix. --- src/user-metadata/dto/user-metadata-filter.dto.ts | 12 ++++++------ src/user-metadata/user-metadata.controller.ts | 6 ++++-- src/user-metadata/user-metadata.module.ts | 4 +++- src/user-metadata/user-metadata.service.ts | 3 ++- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/user-metadata/dto/user-metadata-filter.dto.ts b/src/user-metadata/dto/user-metadata-filter.dto.ts index 14e89b9..78e2270 100644 --- a/src/user-metadata/dto/user-metadata-filter.dto.ts +++ b/src/user-metadata/dto/user-metadata-filter.dto.ts @@ -10,28 +10,28 @@ export class UserMetadataFilterDto { @IsBoolean() @IsOptional() @IsNotEmpty() - isNewEmployee: boolean; + isNewEmployee?: boolean; @IsInt() @IsOptional() @IsNotEmpty() - departmentId: number; + departmentId?: number; @IsString() @IsOptional() @IsNotEmpty() - designation: string; + designation?: string; @IsBoolean() @IsOptional() @IsNotEmpty() - isAdmin: boolean; + isAdmin?: boolean; - @IsOptional() @IsInt() + @IsOptional() limit?: number; - @IsOptional() @IsInt() + @IsOptional() offset?: number; } diff --git a/src/user-metadata/user-metadata.controller.ts b/src/user-metadata/user-metadata.controller.ts index a953fae..f7c0251 100644 --- a/src/user-metadata/user-metadata.controller.ts +++ b/src/user-metadata/user-metadata.controller.ts @@ -8,6 +8,7 @@ import { Param, ParseUUIDPipe, Post, + Query, Res, } from "@nestjs/common"; import { UserMetadataService } from "./user-metadata.service"; @@ -102,7 +103,7 @@ export class UserMetadataController { }) async findManyUserMetadata( @Res() res, - @Body() filter: UserMetadataFilterDto + @Query() filter: UserMetadataFilterDto, ): Promise { try { this.logger.log(`Fetching UserMetadata(s)`); @@ -118,7 +119,8 @@ export class UserMetadataController { data: userMetadata, }); } catch (error) { - this.logger.error(`Failed to fetch UserMetadats(s).`, error); + this.logger.error(`Failed to fetch UserMetadata(s).`, error); + console.log({error,}) const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); diff --git a/src/user-metadata/user-metadata.module.ts b/src/user-metadata/user-metadata.module.ts index 3e9b748..4bde1ee 100644 --- a/src/user-metadata/user-metadata.module.ts +++ b/src/user-metadata/user-metadata.module.ts @@ -1,9 +1,11 @@ import { Module } from '@nestjs/common'; import { UserMetadataController } from './user-metadata.controller'; import { UserMetadataService } from './user-metadata.service'; +import { SurveyConfigService } from 'src/survey-config/survey-config.service'; +import { MockUserService } from 'src/mockModules/mock-user/mock-user.service'; @Module({ controllers: [UserMetadataController], - providers: [UserMetadataService] + providers: [UserMetadataService, SurveyConfigService, MockUserService] }) export class UserMetadataModule {} diff --git a/src/user-metadata/user-metadata.service.ts b/src/user-metadata/user-metadata.service.ts index 6c6112a..63bdb0c 100644 --- a/src/user-metadata/user-metadata.service.ts +++ b/src/user-metadata/user-metadata.service.ts @@ -1,10 +1,11 @@ -import { NotFoundException } from "@nestjs/common"; +import { Injectable, NotFoundException } from "@nestjs/common"; import { TimeUnitsEnum, UserRolesEnum } from "@prisma/client"; import { MockUserService } from "src/mockModules/mock-user/mock-user.service"; import { PrismaService } from "src/prisma/prisma.service"; import { SurveyConfigService } from "src/survey-config/survey-config.service"; import { UserMetadataFilterDto } from "./dto"; +@Injectable() export class UserMetadataService { constructor( private prisma: PrismaService, From 6707c66139345fcf91167a1d92fcc2e58b678601 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 25 Oct 2023 11:24:03 +0530 Subject: [PATCH 055/175] SurveyForm APIs fix. --- src/survey-form/dto/create-survey-form.dto.ts | 13 +++++++++++-- src/survey-form/dto/response-survey-form.dto.ts | 8 ++++---- src/survey-form/survey-form.service.ts | 6 +++++- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/survey-form/dto/create-survey-form.dto.ts b/src/survey-form/dto/create-survey-form.dto.ts index 258b50e..d5029e1 100644 --- a/src/survey-form/dto/create-survey-form.dto.ts +++ b/src/survey-form/dto/create-survey-form.dto.ts @@ -9,6 +9,15 @@ import { IsString, } from "class-validator"; +export class responseObject { + @IsNumber() + @IsNotEmpty() + questionId: number; + + @IsString() + @IsNotEmpty() + question: string; +} export class CreateSurveyFormDto { @ApiProperty({ type: "integer", example: 1 }) @IsNumber() @@ -24,8 +33,8 @@ export class CreateSurveyFormDto { @IsNotEmpty() status: SurveyStatusEnum; - @ApiProperty({ type: "array", example: [{ number: "value" }] }) + @ApiProperty({ type: "array", example: [{ questionId: 1, question: "Is this a dummy question?" }] }) @IsArray() @IsNotEmpty() - questionsJson: Record[]; + questionsJson: responseObject[]; } diff --git a/src/survey-form/dto/response-survey-form.dto.ts b/src/survey-form/dto/response-survey-form.dto.ts index 6e7b32d..53211f8 100644 --- a/src/survey-form/dto/response-survey-form.dto.ts +++ b/src/survey-form/dto/response-survey-form.dto.ts @@ -3,12 +3,12 @@ import { JsonValue } from "@prisma/client/runtime/library"; export class ResponseSurveyFormDto { readonly id: number; - readonly userId: number; - readonly surveyConfigId: number; + readonly userId: string; + readonly surveyCycleParameterId: number; readonly status: SurveyStatusEnum; readonly questionsJson: JsonValue; - readonly overallScore: number; - readonly sunbirdCredentialIds: string; + readonly overallScore: number | null; + readonly sunbirdCredentialIds: string | null; } export class SurveyFormResponse { diff --git a/src/survey-form/survey-form.service.ts b/src/survey-form/survey-form.service.ts index c778fd0..c0fbcc6 100644 --- a/src/survey-form/survey-form.service.ts +++ b/src/survey-form/survey-form.service.ts @@ -8,8 +8,12 @@ export class SurveyFormService { constructor(private prisma: PrismaService) {} async createSurveyForm(createSurveyFormDto: CreateSurveyFormDto) { + const surveyFormDto = { + ...createSurveyFormDto, + questionsJson: JSON.parse(JSON.stringify(createSurveyFormDto.questionsJson)) + } return await this.prisma.surveyForm.create({ - data: { ...createSurveyFormDto }, + data: { ...surveyFormDto }, }); } From b6a20d8cede369c25cfb83ec5dcdafbae2f5db34 Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Fri, 20 Oct 2023 19:01:53 +0530 Subject: [PATCH 056/175] Api created to calculate survey score --- prisma/schema.prisma | 4 +- src/question-bank/question-bank.service.ts | 16 +- .../response-tracker.service.ts | 35 ++++ src/survey-form/survey-form.controller.ts | 4 +- src/survey-form/survey-form.service.ts | 11 +- .../dto/create-survey-score.dto.ts | 15 +- .../interfaces/survey-score.interfaces.ts | 11 ++ src/survey-score/survey-score.controller.ts | 45 ++++- src/survey-score/survey-score.module.ts | 13 +- src/survey-score/survey-score.service.ts | 172 +++++++++++++++++- 10 files changed, 306 insertions(+), 20 deletions(-) create mode 100644 src/survey-score/interfaces/survey-score.interfaces.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 4d30a17..8a69a16 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -102,7 +102,7 @@ model SurveyForm { surveyCycleParameterId Int surveyCycleParameter SurveyCycleParameter @relation(fields: [surveyCycleParameterId], references: [id]) status SurveyStatusEnum - overallScore Int? + overallScore Float? sunbirdCredentialIds String? ResponseTracker ResponseTracker[] SurveyScore SurveyScore[] @@ -119,7 +119,7 @@ model ResponseTracker { surveyForm SurveyForm @relation(fields: [surveyFormId], references: [id]) assesseeId String @db.Uuid assessorId String @db.Uuid - responseJson Json[] @default([]) //{questionId: answer(yes, no and don't know)} + responseJson Json[] @default([]) //{questionId: number, answer: (Yes, No and DoNotKnow)} status ResponseTrackerStatusEnum Assessee UserMetadata @relation("AssesseeResponseTrackers", fields: [assesseeId], references: [userId]) Assessor UserMetadata @relation("AssessorResponseTrackers", fields: [assessorId], references: [userId]) diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index c33d464..00d1d1a 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -90,7 +90,7 @@ export class QuestionBankService { }); } - async deleteQuestionById(questionId : number) { + async deleteQuestionById(questionId: number) { // Check the question is there in db for the given questionId and competencyLevelId const findQuestion = await this.prisma.questionBank.findUnique({ where: { @@ -107,7 +107,17 @@ export class QuestionBankService { return this.prisma.questionBank.delete({ where: { id: questionId, - } - }) + }, + }); + } + + public async getQuestionById(id: number) { + const question = await this.prisma.questionBank.findUnique({ + where: { id }, + }); + + if (!question) + throw new NotFoundException(`question with id #${id} not found`); + return question; } } diff --git a/src/response-tracker/response-tracker.service.ts b/src/response-tracker/response-tracker.service.ts index 8e70ca0..10ebbe4 100644 --- a/src/response-tracker/response-tracker.service.ts +++ b/src/response-tracker/response-tracker.service.ts @@ -90,4 +90,39 @@ export class ResponseTrackerService { public async remove(id: number) { return await this.prisma.responseTracker.delete({ where: { id } }); } + + async getAllResponseJsonBySurveyFormId( + surveyFormId: number, + status?: string + ) { + const whereClause: any = { + surveyFormId: surveyFormId, + }; + + if (status) { + whereClause.status = status; // Filter by status if provided + } + + const responseTrackers = await this.prisma.responseTracker.findMany({ + where: whereClause, + select: { + responseJson: true, + status: true, + assessorId: true, + assesseeId: true, + }, + }); + + const result = responseTrackers.map((response) => { + const { assesseeId, assessorId, responseJson, status } = response; + return { + assesseeId, + assessorId, + status, + responseJson: JSON.parse(JSON.stringify(responseJson) || "[]"), + }; + }); + + return result; + } } diff --git a/src/survey-form/survey-form.controller.ts b/src/survey-form/survey-form.controller.ts index b19bf5c..05b573f 100644 --- a/src/survey-form/survey-form.controller.ts +++ b/src/survey-form/survey-form.controller.ts @@ -88,7 +88,7 @@ export class SurveyFormController { @Patch(":id") @ApiOperation({ summary: "Update the Survey-Form" }) @ApiResponse({ status: HttpStatus.OK, type: SurveyFormResponse }) - async updateSurveyForm( + async updateSurveyFormStatus( @Res() res, @Param("id") id: number, @Body() { status }: UpdateSurveyFormDto @@ -96,7 +96,7 @@ export class SurveyFormController { try { this.logger.log(`Initiated updating new survey form`); - const surveyForm = await this.surveyFormService.updateSurveyForm( + const surveyForm = await this.surveyFormService.updateSurveyFormStatus( id, status ); diff --git a/src/survey-form/survey-form.service.ts b/src/survey-form/survey-form.service.ts index c0fbcc6..e25c0e7 100644 --- a/src/survey-form/survey-form.service.ts +++ b/src/survey-form/survey-form.service.ts @@ -1,7 +1,7 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { SurveyStatusEnum } from "@prisma/client"; import { PrismaService } from "src/prisma/prisma.service"; -import { CreateSurveyFormDto, ResponseSurveyFormDto } from "./dto"; +import { CreateSurveyFormDto } from "./dto"; @Injectable() export class SurveyFormService { @@ -27,13 +27,20 @@ export class SurveyFormService { return surveyForm; } - async updateSurveyForm(id: number, status: SurveyStatusEnum) { + async updateSurveyFormStatus(id: number, status: SurveyStatusEnum) { return await this.prisma.surveyForm.update({ where: { id }, data: { status }, }); } + async updateSurveyFormScore(id: number, overallScore: number) { + return await this.prisma.surveyForm.update({ + where: { id }, + data: { overallScore }, + }); + } + async deleteSurveyForm(id: number) { return await this.prisma.surveyForm.delete({ where: { id }, diff --git a/src/survey-score/dto/create-survey-score.dto.ts b/src/survey-score/dto/create-survey-score.dto.ts index 67b10ad..8a7cdcb 100644 --- a/src/survey-score/dto/create-survey-score.dto.ts +++ b/src/survey-score/dto/create-survey-score.dto.ts @@ -1,20 +1,25 @@ -import { IsNotEmpty, IsNumber } from "class-validator"; +import { IsInt, IsNotEmpty, IsNumber } from "class-validator"; export class CreateSurveyScoreDto { - @IsNotEmpty() - @IsNumber() + @IsInt() surveyFormId: number; @IsNotEmpty() - @IsNumber() + @IsInt() competencyId: number; @IsNotEmpty() - @IsNumber() + @IsInt() competencyLevelId: number; @IsNotEmpty() @IsNumber() score: number; } + +export class GenerateSurveyScoreDto { + @IsNotEmpty() + @IsInt() + surveyFormId: number; +} diff --git a/src/survey-score/interfaces/survey-score.interfaces.ts b/src/survey-score/interfaces/survey-score.interfaces.ts new file mode 100644 index 0000000..3437e40 --- /dev/null +++ b/src/survey-score/interfaces/survey-score.interfaces.ts @@ -0,0 +1,11 @@ +export interface IAnswerScore { + score: number; + totalQuestions: number; +} + +export interface IGroupScoreData extends IAnswerScore { + competencyId: number; + competencyLevelId: number; + competencyLevelNumber: number; + scorePercentage: number; +} diff --git a/src/survey-score/survey-score.controller.ts b/src/survey-score/survey-score.controller.ts index 402030c..3d00ff4 100644 --- a/src/survey-score/survey-score.controller.ts +++ b/src/survey-score/survey-score.controller.ts @@ -15,6 +15,7 @@ import { SurveyScoreService } from "./survey-score.service"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { CreateSurveyScoreDto, + GenerateSurveyScoreDto, SurveyScoreMultipleResponseDto, SurveyScoreResponseDto, UpdateSurveyScoreDto, @@ -130,7 +131,6 @@ export class SurveyScoreController { } } - @Get(":id") @ApiOperation({ summary: "get survey score by id" }) @ApiResponse({ status: HttpStatus.OK, type: SurveyScoreResponseDto }) @@ -227,4 +227,47 @@ export class SurveyScoreController { }); } } + + @Post("calculate-score") + @ApiOperation({ summary: "calculate survey score by survey form id" }) + @ApiResponse({ status: HttpStatus.OK }) + async generateSurveyScore( + @Res() res, + @Body() generateSurveyScoreDto: GenerateSurveyScoreDto + ): Promise { + const { surveyFormId } = generateSurveyScoreDto; + try { + this.logger.log( + `Initiated calculating survey score for surveyFormId #${surveyFormId}` + ); + + const response = + await this.surveyScoreService.calculateSurveyScoreBySurveyFormId( + surveyFormId + ); + + this.logger.log( + `Successfully calculated survey score for id #${surveyFormId}` + ); + + return res.status(HttpStatus.OK).json({ + data: response, + message: `Successfully calculated survey score for surveyFormId #${surveyFormId}`, + }); + } catch (error) { + this.logger.error( + `Failed to calculate survey score for surveyFormId #${surveyFormId}`, + error + ); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + message: + errorMessage || + `Failed to calculate survey score for surveyFormId #${surveyFormId} `, + }); + } + } } diff --git a/src/survey-score/survey-score.module.ts b/src/survey-score/survey-score.module.ts index 4d791fb..9e11288 100644 --- a/src/survey-score/survey-score.module.ts +++ b/src/survey-score/survey-score.module.ts @@ -1,10 +1,17 @@ import { Module } from "@nestjs/common"; -import { SurveyScoreService } from "./survey-score.service"; +import { QuestionBankService } from "src/question-bank/question-bank.service"; +import { ResponseTrackerService } from "src/response-tracker/response-tracker.service"; +import { SurveyFormService } from "src/survey-form/survey-form.service"; import { SurveyScoreController } from "./survey-score.controller"; -import { PrismaService } from "src/prisma/prisma.service"; +import { SurveyScoreService } from "./survey-score.service"; @Module({ controllers: [SurveyScoreController], - providers: [SurveyScoreService, PrismaService], + providers: [ + SurveyScoreService, + ResponseTrackerService, + QuestionBankService, + SurveyFormService, + ], }) export class SurveyScoreModule {} diff --git a/src/survey-score/survey-score.service.ts b/src/survey-score/survey-score.service.ts index 0f13dc6..ccc4634 100644 --- a/src/survey-score/survey-score.service.ts +++ b/src/survey-score/survey-score.service.ts @@ -1,11 +1,26 @@ import { Injectable, NotFoundException } from "@nestjs/common"; +import { ResponseTrackerStatusEnum } from "@prisma/client"; +import { PrismaService } from "src/prisma/prisma.service"; +import { QuestionBankService } from "src/question-bank/question-bank.service"; +import { ResponseTrackerService } from "src/response-tracker/response-tracker.service"; +import { SurveyFormService } from "src/survey-form/survey-form.service"; import { CreateSurveyScoreDto } from "./dto/create-survey-score.dto"; import { UpdateSurveyScoreDto } from "./dto/update-survey-score.dto"; -import { PrismaService } from "src/prisma/prisma.service"; +import { + IAnswerScore, + IGroupScoreData, +} from "./interfaces/survey-score.interfaces"; +import { AnswerEnum } from "src/response-tracker/enums/response-tracker.enums"; +import { responseObject } from "src/response-tracker/dto"; @Injectable() export class SurveyScoreService { - constructor(private prisma: PrismaService) {} + constructor( + private prisma: PrismaService, + private responseTrackerService: ResponseTrackerService, + private questionBankService: QuestionBankService, + private surveyFormService: SurveyFormService + ) {} public async create(createSurveyScoreDto: CreateSurveyScoreDto) { return await this.prisma.surveyScore.create({ data: createSurveyScoreDto }); @@ -48,4 +63,157 @@ export class SurveyScoreService { public async remove(id: number) { return await this.prisma.surveyScore.delete({ where: { id } }); } + + public async calculateSurveyScoreBySurveyFormId(surveyFormId: number) { + const completedSurveyResponses = + await this.responseTrackerService.getAllResponseJsonBySurveyFormId( + surveyFormId, + ResponseTrackerStatusEnum.COMPLETED + ); + + const surveyResponses = completedSurveyResponses + .map((response) => response.responseJson) + .flat(); + + const answerScore = this.calculateAnswerScore(surveyResponses); + + const answerScoreAndCompetencyGroupData: { + [key: string]: IGroupScoreData; + } = this.getAnswerScoreAndCompetencyGroupData(answerScore); + + const groupedScoreDataByCompetency = this.groupScoreDataByCompetency( + answerScoreAndCompetencyGroupData + ); + + const finalGroupedData = Object.values(groupedScoreDataByCompetency).map( + (group: IGroupScoreData) => ({ + ...group, + scorePercentage: Number( + ((group.score / group.totalQuestions) * 100).toFixed(2) + ), + }) + ); + + const overallScore = + this.calculateOverAllScoreFromFinalGropedData(finalGroupedData); + + const updateSurveyFormScore = + await this.surveyFormService.updateSurveyFormScore( + surveyFormId, + overallScore + ); + + const scorePayload = this.formatScorePayloadFromFinalGroupDataAndFormId( + surveyFormId, + finalGroupedData + ); + + const surveyScores = await this.prisma.surveyScore.createMany({ + data: scorePayload, + }); + + return { surveyScores, updateSurveyFormScore }; + } + + public calculateAnswerScore(surveyResponses: responseObject[]): { + [key: string]: IAnswerScore; + } { + return surveyResponses.reduce((acc, item) => { + const { questionId, answer } = item; + + if (!acc[questionId]) { + acc[questionId] = { score: 0, totalQuestions: 0 }; + } + + if (answer === AnswerEnum.YES) { + acc[questionId].score += 1; + acc[questionId].totalQuestions += 1; + } else if (answer === AnswerEnum.NO) { + acc[questionId].score += 0; + acc[questionId].totalQuestions += 1; + } + + return acc; + }, {}); + } + + public getAnswerScoreAndCompetencyGroupData(answerScore: { + [key: string]: IAnswerScore; + }): { [key: string]: IGroupScoreData } { + const groupedData: { [key: string]: IGroupScoreData } = {}; + + Object.keys(answerScore).forEach(async (questionId) => { + const question = await this.questionBankService.getQuestionById( + +questionId + ); + const { competencyId, competencyLevelId, competencyLevelNumber } = + question; + const scoreData = answerScore[questionId]; + + groupedData[questionId] = { + score: scoreData.score, + totalQuestions: scoreData.totalQuestions, + competencyId, + competencyLevelId, + competencyLevelNumber, + scorePercentage: 0, + }; + }); + return groupedData; + } + + public groupScoreDataByCompetency(groupedData: { + [key: string]: IGroupScoreData; + }): { [key: string]: IGroupScoreData } { + return Object.values(groupedData).reduce((acc, item) => { + const key = `${item.competencyId}-${item.competencyLevelId}-${item.competencyLevelNumber}`; + + if (!acc[key]) { + acc[key] = { + score: 0, + totalQuestions: 0, + competencyId: item.competencyId, + competencyLevelId: item.competencyLevelId, + competencyLevelNumber: item.competencyLevelNumber, + }; + } + + acc[key].score += item.score; + acc[key].totalQuestions += item.totalQuestions; + + return acc; + }, {}); + } + + public calculateOverAllScoreFromFinalGropedData( + finalGroupedData: IGroupScoreData[] + ): number { + const overallScore = finalGroupedData.reduce( + (acc, group) => { + acc.score += group.score; + acc.totalQuestions += group.totalQuestions; + return acc; + }, + { score: 0, totalQuestions: 0 } + ); + + return Number( + ((overallScore.score / overallScore.totalQuestions) * 100).toFixed(2) + ); + } + + public formatScorePayloadFromFinalGroupDataAndFormId( + surveyFormId: number, + finalGroupedData: IGroupScoreData[] + ) { + return finalGroupedData.map((data: IGroupScoreData) => { + const { competencyId, competencyLevelId, scorePercentage } = data; + return { + surveyFormId, + competencyId, + competencyLevelId, + score: scorePercentage, + }; + }); + } } From 3ff6ade7a27b0ea938342f6ad7449e918a2e1ed8 Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Wed, 25 Oct 2023 11:44:00 +0530 Subject: [PATCH 057/175] updated score calculation API --- src/survey-form/dto/create-survey-form.dto.ts | 13 ++- .../dto/response-survey-score.dto.ts | 9 +- src/survey-score/survey-score.controller.ts | 13 ++- src/survey-score/survey-score.service.ts | 94 +++++++++++-------- 4 files changed, 73 insertions(+), 56 deletions(-) diff --git a/src/survey-form/dto/create-survey-form.dto.ts b/src/survey-form/dto/create-survey-form.dto.ts index d5029e1..85e1d22 100644 --- a/src/survey-form/dto/create-survey-form.dto.ts +++ b/src/survey-form/dto/create-survey-form.dto.ts @@ -5,8 +5,8 @@ import { IsEnum, IsNotEmpty, IsNumber, - IsOptional, IsString, + IsUUID, } from "class-validator"; export class responseObject { @@ -20,20 +20,23 @@ export class responseObject { } export class CreateSurveyFormDto { @ApiProperty({ type: "integer", example: 1 }) - @IsNumber() + @IsUUID() @IsNotEmpty() userId: string; @IsNumber() @IsNotEmpty() surveyCycleParameterId: number; - + @ApiProperty({ enum: SurveyStatusEnum, example: "CREATED" }) @IsEnum(SurveyStatusEnum) @IsNotEmpty() status: SurveyStatusEnum; - - @ApiProperty({ type: "array", example: [{ questionId: 1, question: "Is this a dummy question?" }] }) + + @ApiProperty({ + type: "array", + example: [{ questionId: 1, question: "Is this a dummy question?" }], + }) @IsArray() @IsNotEmpty() questionsJson: responseObject[]; diff --git a/src/survey-score/dto/response-survey-score.dto.ts b/src/survey-score/dto/response-survey-score.dto.ts index 0a47bab..dfa2112 100644 --- a/src/survey-score/dto/response-survey-score.dto.ts +++ b/src/survey-score/dto/response-survey-score.dto.ts @@ -6,10 +6,13 @@ export class SurveyScoreResponse { readonly score: number; } -export class SurveyScoreResponseDto { - data?: SurveyScoreResponse; - message: string; +export class ResponseMessageDto { statusCode?: number; + message: string; +} + +export class SurveyScoreResponseDto extends ResponseMessageDto { + data?: SurveyScoreResponse; } export class SurveyScoreMultipleResponseDto { diff --git a/src/survey-score/survey-score.controller.ts b/src/survey-score/survey-score.controller.ts index 3d00ff4..d976278 100644 --- a/src/survey-score/survey-score.controller.ts +++ b/src/survey-score/survey-score.controller.ts @@ -16,6 +16,7 @@ import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { CreateSurveyScoreDto, GenerateSurveyScoreDto, + ResponseMessageDto, SurveyScoreMultipleResponseDto, SurveyScoreResponseDto, UpdateSurveyScoreDto, @@ -234,24 +235,22 @@ export class SurveyScoreController { async generateSurveyScore( @Res() res, @Body() generateSurveyScoreDto: GenerateSurveyScoreDto - ): Promise { + ): Promise { const { surveyFormId } = generateSurveyScoreDto; try { this.logger.log( `Initiated calculating survey score for surveyFormId #${surveyFormId}` ); - const response = - await this.surveyScoreService.calculateSurveyScoreBySurveyFormId( - surveyFormId - ); + await this.surveyScoreService.calculateSurveyScoreBySurveyFormId( + surveyFormId + ); this.logger.log( - `Successfully calculated survey score for id #${surveyFormId}` + `Successfully calculated survey score for surveyFormId #${surveyFormId}` ); return res.status(HttpStatus.OK).json({ - data: response, message: `Successfully calculated survey score for surveyFormId #${surveyFormId}`, }); } catch (error) { diff --git a/src/survey-score/survey-score.service.ts b/src/survey-score/survey-score.service.ts index ccc4634..d1da600 100644 --- a/src/survey-score/survey-score.service.ts +++ b/src/survey-score/survey-score.service.ts @@ -64,55 +64,60 @@ export class SurveyScoreService { return await this.prisma.surveyScore.delete({ where: { id } }); } - public async calculateSurveyScoreBySurveyFormId(surveyFormId: number) { - const completedSurveyResponses = - await this.responseTrackerService.getAllResponseJsonBySurveyFormId( - surveyFormId, - ResponseTrackerStatusEnum.COMPLETED + public async calculateSurveyScoreBySurveyFormId( + surveyFormId: number + ): Promise { + try { + const completedSurveyResponses = + await this.responseTrackerService.getAllResponseJsonBySurveyFormId( + surveyFormId, + ResponseTrackerStatusEnum.COMPLETED + ); + + const surveyResponses = completedSurveyResponses + .map((response) => response.responseJson) + .flat(); + + const answerScore = this.calculateAnswerScore(surveyResponses); + + const answerScoreAndCompetencyGroupData: { + [key: string]: IGroupScoreData; + } = await this.getAnswerScoreAndCompetencyGroupData(answerScore); + + const groupedScoreDataByCompetency = this.groupScoreDataByCompetency( + answerScoreAndCompetencyGroupData ); - const surveyResponses = completedSurveyResponses - .map((response) => response.responseJson) - .flat(); - - const answerScore = this.calculateAnswerScore(surveyResponses); - - const answerScoreAndCompetencyGroupData: { - [key: string]: IGroupScoreData; - } = this.getAnswerScoreAndCompetencyGroupData(answerScore); - - const groupedScoreDataByCompetency = this.groupScoreDataByCompetency( - answerScoreAndCompetencyGroupData - ); - - const finalGroupedData = Object.values(groupedScoreDataByCompetency).map( - (group: IGroupScoreData) => ({ - ...group, - scorePercentage: Number( - ((group.score / group.totalQuestions) * 100).toFixed(2) - ), - }) - ); + const finalGroupedData = Object.values(groupedScoreDataByCompetency).map( + (group: IGroupScoreData) => ({ + ...group, + scorePercentage: Number( + ((group.score / group.totalQuestions || 0) * 100).toFixed(2) + ), + }) + ); - const overallScore = - this.calculateOverAllScoreFromFinalGropedData(finalGroupedData); + const overallScore = + this.calculateOverAllScoreFromFinalGropedData(finalGroupedData); - const updateSurveyFormScore = await this.surveyFormService.updateSurveyFormScore( surveyFormId, overallScore ); - const scorePayload = this.formatScorePayloadFromFinalGroupDataAndFormId( - surveyFormId, - finalGroupedData - ); + const scorePayload = this.formatScorePayloadFromFinalGroupDataAndFormId( + surveyFormId, + finalGroupedData + ); - const surveyScores = await this.prisma.surveyScore.createMany({ - data: scorePayload, - }); + await this.prisma.surveyScore.createMany({ + data: scorePayload, + }); - return { surveyScores, updateSurveyFormScore }; + return `Successfully calculated score for survey form id #${surveyFormId}`; + } catch (error) { + throw error; + } } public calculateAnswerScore(surveyResponses: responseObject[]): { @@ -137,17 +142,20 @@ export class SurveyScoreService { }, {}); } - public getAnswerScoreAndCompetencyGroupData(answerScore: { + public async getAnswerScoreAndCompetencyGroupData(answerScore: { [key: string]: IAnswerScore; - }): { [key: string]: IGroupScoreData } { + }): Promise<{ [key: string]: IGroupScoreData }> { const groupedData: { [key: string]: IGroupScoreData } = {}; + const questionIds = Object.keys(answerScore); - Object.keys(answerScore).forEach(async (questionId) => { + // Create an array of promises for each iteration + const promises = questionIds.map(async (questionId) => { const question = await this.questionBankService.getQuestionById( +questionId ); const { competencyId, competencyLevelId, competencyLevelNumber } = question; + const scoreData = answerScore[questionId]; groupedData[questionId] = { @@ -159,6 +167,10 @@ export class SurveyScoreService { scorePercentage: 0, }; }); + + // Wait for all promises to resolve + await Promise.all(promises); + return groupedData; } From fb5d577d93e05e9f8e9031a42050e77a5f4d1250 Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Wed, 25 Oct 2023 15:17:45 +0530 Subject: [PATCH 058/175] APIs to fetch user survey scores --- prisma/schema.prisma | 1 + src/survey-form/dto/create-survey-form.dto.ts | 4 + .../dto/response-survey-form.dto.ts | 14 +++ src/survey-form/survey-form.service.ts | 71 ++++++++++- .../dto/response-survey-score.dto.ts | 23 ++++ src/survey-score/survey-score.controller.ts | 114 ++++++++++++++++-- src/survey-score/survey-score.module.ts | 4 +- src/survey-score/survey-score.service.ts | 38 +++++- src/user-metadata/user-metadata.controller.ts | 10 +- src/user-metadata/user-metadata.module.ts | 13 +- 10 files changed, 261 insertions(+), 31 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 8a69a16..2df7177 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -143,6 +143,7 @@ model SurveyScore { enum SurveyStatusEnum { CREATED PUBLISHED + CLOSED } enum ResponseTrackerStatusEnum { diff --git a/src/survey-form/dto/create-survey-form.dto.ts b/src/survey-form/dto/create-survey-form.dto.ts index 85e1d22..43ca5cb 100644 --- a/src/survey-form/dto/create-survey-form.dto.ts +++ b/src/survey-form/dto/create-survey-form.dto.ts @@ -1,5 +1,6 @@ import { ApiProperty } from "@nestjs/swagger"; import { SurveyStatusEnum } from "@prisma/client"; +import { Type } from "class-transformer"; import { IsArray, IsEnum, @@ -7,6 +8,7 @@ import { IsNumber, IsString, IsUUID, + ValidateNested, } from "class-validator"; export class responseObject { @@ -39,5 +41,7 @@ export class CreateSurveyFormDto { }) @IsArray() @IsNotEmpty() + @ValidateNested({ each: true }) + @Type(() => responseObject) questionsJson: responseObject[]; } diff --git a/src/survey-form/dto/response-survey-form.dto.ts b/src/survey-form/dto/response-survey-form.dto.ts index 53211f8..e602b42 100644 --- a/src/survey-form/dto/response-survey-form.dto.ts +++ b/src/survey-form/dto/response-survey-form.dto.ts @@ -1,5 +1,7 @@ +import { ApiProperty } from "@nestjs/swagger"; import { SurveyStatusEnum } from "@prisma/client"; import { JsonValue } from "@prisma/client/runtime/library"; +import { SurveyScoreResponse } from "src/survey-score/dto"; export class ResponseSurveyFormDto { readonly id: number; @@ -15,3 +17,15 @@ export class SurveyFormResponse { readonly data?: ResponseSurveyFormDto; readonly message: string; } + +export class SurveyScoresForUser { + id: number; + @ApiProperty({ enum: SurveyStatusEnum, example: SurveyStatusEnum.CLOSED }) + status: SurveyStatusEnum; + userId: string; + overallScore: number | null; + sunbirdCredentialIds: string | null; + SurveyScore: SurveyScoreResponse[]; + createdAt: Date; + updatedAt: Date; +} diff --git a/src/survey-form/survey-form.service.ts b/src/survey-form/survey-form.service.ts index e25c0e7..dae3e0b 100644 --- a/src/survey-form/survey-form.service.ts +++ b/src/survey-form/survey-form.service.ts @@ -1,7 +1,7 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { SurveyStatusEnum } from "@prisma/client"; import { PrismaService } from "src/prisma/prisma.service"; -import { CreateSurveyFormDto } from "./dto"; +import { CreateSurveyFormDto, SurveyScoresForUser } from "./dto"; @Injectable() export class SurveyFormService { @@ -10,8 +10,10 @@ export class SurveyFormService { async createSurveyForm(createSurveyFormDto: CreateSurveyFormDto) { const surveyFormDto = { ...createSurveyFormDto, - questionsJson: JSON.parse(JSON.stringify(createSurveyFormDto.questionsJson)) - } + questionsJson: JSON.parse( + JSON.stringify(createSurveyFormDto.questionsJson) + ), + }; return await this.prisma.surveyForm.create({ data: { ...surveyFormDto }, }); @@ -46,4 +48,67 @@ export class SurveyFormService { where: { id }, }); } + + public async getAllSurveyFormScoresByUserId( + userId: string + ): Promise { + const userMetadata = await this.prisma.userMetadata.findUnique({ + where: { userId }, + }); + + if (!userMetadata) + throw new NotFoundException(`User with id #${userId} not found`); + + const response = await this.prisma.surveyForm.findMany({ + where: { + userId, + }, + select: { + id: true, + userId: true, + status: true, + overallScore: true, + sunbirdCredentialIds: true, + SurveyScore: true, + createdAt: true, + updatedAt: true, + }, + }); + + return response; + } + + public async getLatestSurveyFormScoresByUserId( + userId: string + ): Promise { + const userMetadata = await this.prisma.userMetadata.findUnique({ + where: { userId }, + }); + + if (!userMetadata) + throw new NotFoundException(`User with id #${userId} not found`); + + const latestSurveyForm = await this.prisma.surveyForm.findMany({ + where: { + userId, + status: SurveyStatusEnum.CLOSED, // Get score for only closed survey form + }, + orderBy: { + createdAt: "desc", // Order by createdAt in descending order to get the latest survey form + }, + select: { + id: true, + status: true, + userId: true, + overallScore: true, + sunbirdCredentialIds: true, + SurveyScore: true, + createdAt: true, + updatedAt: true, + }, + take: 1, // Limit the query to return only one result + }); + + return latestSurveyForm[0]; + } } diff --git a/src/survey-score/dto/response-survey-score.dto.ts b/src/survey-score/dto/response-survey-score.dto.ts index dfa2112..96af24f 100644 --- a/src/survey-score/dto/response-survey-score.dto.ts +++ b/src/survey-score/dto/response-survey-score.dto.ts @@ -1,3 +1,6 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { SurveyStatusEnum } from "@prisma/client"; + export class SurveyScoreResponse { readonly id: number; readonly surveyFormId: number; @@ -20,3 +23,23 @@ export class SurveyScoreMultipleResponseDto { message: string; statusCode?: number; } + +export class SurveyScoresForUser { + surveyFormId: number; + @ApiProperty({ enum: SurveyStatusEnum, example: SurveyStatusEnum.CLOSED }) + status: SurveyStatusEnum; + userId: string; + overallScore: number; + sunbirdCredentialIds: string; + SurveyScore: SurveyScoreResponse[]; + createdAt: Date; + updatedAt: Date; +} + +export class SurveyScoresForUserDto extends ResponseMessageDto { + data?: SurveyScoresForUser; +} + +export class AllSurveyScoresForUserDto extends ResponseMessageDto { + data?: SurveyScoresForUser[]; +} diff --git a/src/survey-score/survey-score.controller.ts b/src/survey-score/survey-score.controller.ts index d976278..7bc7070 100644 --- a/src/survey-score/survey-score.controller.ts +++ b/src/survey-score/survey-score.controller.ts @@ -1,27 +1,29 @@ import { - Controller, - Get, - Post, Body, - Patch, - Param, + Controller, Delete, - Res, + Get, HttpStatus, Logger, + Param, ParseIntPipe, + Patch, + Post, + Res, } from "@nestjs/common"; -import { SurveyScoreService } from "./survey-score.service"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; import { + AllSurveyScoresForUserDto, CreateSurveyScoreDto, GenerateSurveyScoreDto, ResponseMessageDto, SurveyScoreMultipleResponseDto, SurveyScoreResponseDto, + SurveyScoresForUserDto, UpdateSurveyScoreDto, } from "./dto"; -import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; +import { SurveyScoreService } from "./survey-score.service"; @Controller("survey-score") @ApiTags("survey-score") @@ -191,10 +193,13 @@ export class SurveyScoreController { const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); - return res.status(statusCode).json({ - message: - errorMessage || `Failed to update survey score with id #${id} `, - }); + return res + .status(statusCode) + .json({ + statusCode, + message: + errorMessage || `Failed to update survey score with id #${id} `, + }); } } @@ -223,6 +228,7 @@ export class SurveyScoreController { getPrismaErrorStatusAndMessage(error); return res.status(statusCode).json({ + statusCode, message: errorMessage || `Failed to delete survey score with id #${id} `, }); @@ -263,10 +269,94 @@ export class SurveyScoreController { getPrismaErrorStatusAndMessage(error); return res.status(statusCode).json({ + statusCode, message: errorMessage || `Failed to calculate survey score for surveyFormId #${surveyFormId} `, }); } } + + @Get("latest-survey-score/:userId") + @ApiOperation({ summary: "get latest survey score for user by user id" }) + @ApiResponse({ status: HttpStatus.OK, type: SurveyScoresForUserDto }) + async getLatestSurveyScoreByUserId( + @Res() res, + @Param("userId") userId: string + ): Promise { + try { + this.logger.log( + `Initiated fetching latest survey score for userId #${userId}` + ); + + const response = + await this.surveyScoreService.getLatestSurveyScoreByUserId(userId); + + this.logger.log( + `Successfully fetched latest survey score for userId #${userId}` + ); + + return res.status(HttpStatus.OK).json({ + data: response, + message: `Successfully fetched latest survey score for userId #${userId}`, + }); + } catch (error) { + this.logger.error( + `Failed to fetch latest survey score for userId #${userId}`, + error + ); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: + errorMessage || + `Failed to fetch latest survey score for userId #${userId} `, + }); + } + } + + @Get("all-survey-score/:userId") + @ApiOperation({ summary: "get all surveys score for user by user id" }) + @ApiResponse({ status: HttpStatus.OK, type: AllSurveyScoresForUserDto }) + async getAllSurveyScoreByUserId( + @Res() res, + @Param("userId") userId: string + ): Promise { + try { + this.logger.log( + `Initiated fetching all survey score for userId #${userId}` + ); + + const response = await this.surveyScoreService.getAllSurveyScoreByUserId( + userId + ); + + this.logger.log( + `Successfully fetched all survey score for userId #${userId}` + ); + + return res.status(HttpStatus.OK).json({ + data: response, + message: `Successfully fetched all survey score for userId #${userId}`, + }); + } catch (error) { + this.logger.error( + `Failed to fetch all survey score for userId #${userId}`, + error + ); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: + errorMessage || + `Failed to fetch all survey score for userId #${userId} `, + }); + } + } } diff --git a/src/survey-score/survey-score.module.ts b/src/survey-score/survey-score.module.ts index 9e11288..ac421df 100644 --- a/src/survey-score/survey-score.module.ts +++ b/src/survey-score/survey-score.module.ts @@ -1,11 +1,13 @@ -import { Module } from "@nestjs/common"; +import { Module, forwardRef } from "@nestjs/common"; import { QuestionBankService } from "src/question-bank/question-bank.service"; import { ResponseTrackerService } from "src/response-tracker/response-tracker.service"; import { SurveyFormService } from "src/survey-form/survey-form.service"; +import { UserMetadataModule } from "src/user-metadata/user-metadata.module"; import { SurveyScoreController } from "./survey-score.controller"; import { SurveyScoreService } from "./survey-score.service"; @Module({ + imports: [forwardRef(() => UserMetadataModule)], controllers: [SurveyScoreController], providers: [ SurveyScoreService, diff --git a/src/survey-score/survey-score.service.ts b/src/survey-score/survey-score.service.ts index d1da600..43b5510 100644 --- a/src/survey-score/survey-score.service.ts +++ b/src/survey-score/survey-score.service.ts @@ -2,16 +2,17 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { ResponseTrackerStatusEnum } from "@prisma/client"; import { PrismaService } from "src/prisma/prisma.service"; import { QuestionBankService } from "src/question-bank/question-bank.service"; +import { responseObject } from "src/response-tracker/dto"; +import { AnswerEnum } from "src/response-tracker/enums/response-tracker.enums"; import { ResponseTrackerService } from "src/response-tracker/response-tracker.service"; import { SurveyFormService } from "src/survey-form/survey-form.service"; +import { UserMetadataService } from "src/user-metadata/user-metadata.service"; import { CreateSurveyScoreDto } from "./dto/create-survey-score.dto"; import { UpdateSurveyScoreDto } from "./dto/update-survey-score.dto"; import { IAnswerScore, IGroupScoreData, } from "./interfaces/survey-score.interfaces"; -import { AnswerEnum } from "src/response-tracker/enums/response-tracker.enums"; -import { responseObject } from "src/response-tracker/dto"; @Injectable() export class SurveyScoreService { @@ -19,7 +20,8 @@ export class SurveyScoreService { private prisma: PrismaService, private responseTrackerService: ResponseTrackerService, private questionBankService: QuestionBankService, - private surveyFormService: SurveyFormService + private surveyFormService: SurveyFormService, + private userMetadataService: UserMetadataService ) {} public async create(createSurveyScoreDto: CreateSurveyScoreDto) { @@ -228,4 +230,34 @@ export class SurveyScoreService { }; }); } + + public async getAllSurveyScoreByUserId(userId: string) { + const surveyScoreResponse = + await this.surveyFormService.getAllSurveyFormScoresByUserId(userId); + + if (!surveyScoreResponse) + throw new NotFoundException( + `Survey scores not found for user id #${userId}` + ); + + const updatedSurveyScore = surveyScoreResponse.map((data) => { + const { id, ...rest } = data; + return { surveyFormId: id, ...rest }; + }); + + return updatedSurveyScore; + } + + public async getLatestSurveyScoreByUserId(userId: string) { + const latestSurveyScore = + await this.surveyFormService.getLatestSurveyFormScoresByUserId(userId); + + if (!latestSurveyScore) + throw new NotFoundException( + `Survey score not found for user id #${userId}` + ); + + const { id, ...rest } = latestSurveyScore; + return { surveyFormId: id, ...rest }; + } } diff --git a/src/user-metadata/user-metadata.controller.ts b/src/user-metadata/user-metadata.controller.ts index f7c0251..a70dbc1 100644 --- a/src/user-metadata/user-metadata.controller.ts +++ b/src/user-metadata/user-metadata.controller.ts @@ -1,5 +1,4 @@ import { - Body, Controller, Delete, Get, @@ -11,11 +10,11 @@ import { Query, Res, } from "@nestjs/common"; -import { UserMetadataService } from "./user-metadata.service"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; -import { ResponseUserMetadataDto } from "./dto/user-metadata-response.dto"; -import { UserMetadataFilterDto } from "./dto"; import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; +import { UserMetadataFilterDto } from "./dto"; +import { ResponseUserMetadataDto } from "./dto/user-metadata-response.dto"; +import { UserMetadataService } from "./user-metadata.service"; @Controller("user-metadata") @ApiTags("user-metadata") @@ -103,7 +102,7 @@ export class UserMetadataController { }) async findManyUserMetadata( @Res() res, - @Query() filter: UserMetadataFilterDto, + @Query() filter: UserMetadataFilterDto ): Promise { try { this.logger.log(`Fetching UserMetadata(s)`); @@ -120,7 +119,6 @@ export class UserMetadataController { }); } catch (error) { this.logger.error(`Failed to fetch UserMetadata(s).`, error); - console.log({error,}) const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); diff --git a/src/user-metadata/user-metadata.module.ts b/src/user-metadata/user-metadata.module.ts index 4bde1ee..2a83e80 100644 --- a/src/user-metadata/user-metadata.module.ts +++ b/src/user-metadata/user-metadata.module.ts @@ -1,11 +1,12 @@ -import { Module } from '@nestjs/common'; -import { UserMetadataController } from './user-metadata.controller'; -import { UserMetadataService } from './user-metadata.service'; -import { SurveyConfigService } from 'src/survey-config/survey-config.service'; -import { MockUserService } from 'src/mockModules/mock-user/mock-user.service'; +import { Module } from "@nestjs/common"; +import { MockUserService } from "src/mockModules/mock-user/mock-user.service"; +import { SurveyConfigService } from "src/survey-config/survey-config.service"; +import { UserMetadataController } from "./user-metadata.controller"; +import { UserMetadataService } from "./user-metadata.service"; @Module({ controllers: [UserMetadataController], - providers: [UserMetadataService, SurveyConfigService, MockUserService] + providers: [UserMetadataService, SurveyConfigService, MockUserService], + exports: [UserMetadataService], }) export class UserMetadataModule {} From 3b655b85227e118ff877dd7926c9bc7cdc8cc96d Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Wed, 25 Oct 2023 20:50:00 +0530 Subject: [PATCH 059/175] schema change for question bank and changes in question bank API --- prisma/schema.prisma | 3 +-- src/question-bank/dto/create-question-bank.dto.ts | 10 ---------- src/question-bank/question-bank.controller.ts | 2 +- src/question-bank/question-bank.service.ts | 14 ++------------ 4 files changed, 4 insertions(+), 25 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 4d30a17..b4a0bbf 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -35,11 +35,10 @@ model QuestionBank { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt competencyId Int - competencyLevelId Int competencyLevelNumber Int question String - @@unique([competencyLevelId, question]) + @@unique([competencyId, competencyLevelNumber, question]) @@map("question_bank") } diff --git a/src/question-bank/dto/create-question-bank.dto.ts b/src/question-bank/dto/create-question-bank.dto.ts index b2b93da..ac4eb47 100644 --- a/src/question-bank/dto/create-question-bank.dto.ts +++ b/src/question-bank/dto/create-question-bank.dto.ts @@ -13,11 +13,6 @@ export class CreateQuestionBankDto { @IsInt() competencyId: number; - // Compentency level id associated with the question bank - @IsNotEmpty() - @IsInt() - competencyLevelId: number; - // Question for the question bank @IsNotEmpty() @IsString() @@ -36,11 +31,6 @@ export class QuestionBankFilterDto { @IsInt() competencyId?: number; - // Optional competencyLevelId filter, validated that it's valid competencyLevel Id or not - @IsOptional() - @IsInt() - competencyLevelId?: number; - // Optional competencyLevelNumber filter, validate that it's valid competency Level number or not @IsOptional() @IsInt() diff --git a/src/question-bank/question-bank.controller.ts b/src/question-bank/question-bank.controller.ts index d074414..764fc65 100644 --- a/src/question-bank/question-bank.controller.ts +++ b/src/question-bank/question-bank.controller.ts @@ -44,7 +44,7 @@ export class QuestionBankController { this.logger.log(`Initiate to create a question`); const createdQuestion = - await this.questionBankService.createQuestionByCompentencyLevel( + await this.questionBankService.createQuestionByCompentencyId( createQuestionBankDto ); // Log the successful creation of the question bank diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index c33d464..9b5bdcd 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -10,7 +10,7 @@ import { PrismaService } from "src/prisma/prisma.service"; export class QuestionBankService { constructor(private prisma: PrismaService) {} - async createQuestionByCompentencyLevel( + async createQuestionByCompentencyId( createQuestionBankDto: CreateQuestionBankDto ) { // check for compentencyId exist in competency model in db @@ -20,16 +20,8 @@ export class QuestionBankService { }, }); - // check for competencyLevelId exist in competencyLevel model in db - const checkCompetencyLevelId = await this.prisma.competencyLevel.findUnique( - { - where: { - id: createQuestionBankDto.competencyLevelId, - }, - } - ); // if not found throw error - if (!checkCompentencyId || !checkCompetencyLevelId) { + if (!checkCompentencyId) { throw new NotFoundException( "Either Competency or Competency Level is not Found" ); @@ -45,7 +37,6 @@ export class QuestionBankService { // Get all questions and filter using compentencyId and compentencyLevelId const { competencyId, - competencyLevelId, competencyLevelNumber, limit = 10, offset = 0, @@ -54,7 +45,6 @@ export class QuestionBankService { return this.prisma.questionBank.findMany({ where: { competencyId: competencyId ?? undefined, // Optional compentencyId filter - competencyLevelId: competencyLevelId ?? undefined, // Optional competencyLevelId filter competencyLevelNumber: competencyLevelNumber ?? undefined, // Optional competencyLevelNumber }, orderBy: { From ed11e47e4793773e218d711502e470a04161c632 Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Wed, 25 Oct 2023 20:51:10 +0530 Subject: [PATCH 060/175] Csv file upload API. --- package.json | 3 + src/app.module.ts | 2 + src/config/multer-options.config.ts | 16 ++ src/file-upload/dto/create-file-upload.dto.ts | 18 +++ src/file-upload/dto/update-file-upload.dto.ts | 4 + src/file-upload/file-upload.controller.ts | 74 +++++++++ src/file-upload/file-upload.module.ts | 13 ++ src/file-upload/file-upload.service.ts | 140 ++++++++++++++++++ src/user-metadata/user-metadata.service.ts | 2 +- 9 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 src/config/multer-options.config.ts create mode 100644 src/file-upload/dto/create-file-upload.dto.ts create mode 100644 src/file-upload/dto/update-file-upload.dto.ts create mode 100644 src/file-upload/file-upload.controller.ts create mode 100644 src/file-upload/file-upload.module.ts create mode 100644 src/file-upload/file-upload.service.ts diff --git a/package.json b/package.json index 6b10e50..81210fc 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,8 @@ "@prisma/client": "^5.4.2", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", + "csv-parser": "^3.0.0", + "multer": "^1.4.5-lts.1", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.2.0" @@ -45,6 +47,7 @@ "@nestjs/testing": "^9.0.0", "@types/express": "^4.17.13", "@types/jest": "28.1.8", + "@types/multer": "^1.4.9", "@types/node": "^16.0.0", "@types/supertest": "^2.0.11", "@typescript-eslint/eslint-plugin": "^5.0.0", diff --git a/src/app.module.ts b/src/app.module.ts index 8518ebb..99d2984 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -13,6 +13,7 @@ import { SurveyConfigModule } from "./survey-config/survey-config.module"; import { SurveyCycleParameterModule } from "./survey-cycle-parameter/survey-cycle-parameter.module"; import { UserMetadataModule } from './user-metadata/user-metadata.module'; import { AdminCompetencyModule } from "./admin-competency/admin-competency.module"; +import { FileUploadModule } from './file-upload/file-upload.module'; @Module({ imports: [ @@ -29,6 +30,7 @@ import { AdminCompetencyModule } from "./admin-competency/admin-competency.modul SurveyCycleParameterModule, UserMetadataModule, AdminCompetencyModule, + FileUploadModule, ], controllers: [AppController], providers: [AppService, PrismaService], diff --git a/src/config/multer-options.config.ts b/src/config/multer-options.config.ts new file mode 100644 index 0000000..1eaf12d --- /dev/null +++ b/src/config/multer-options.config.ts @@ -0,0 +1,16 @@ +import { MulterOptions } from "@nestjs/platform-express/multer/interfaces/multer-options.interface"; +import { diskStorage } from "multer"; + +export const multerOptions: MulterOptions = { + dest: "../uploads", // this is the destination of the uploaded files on disk + limits: { + fileSize: 1024 * 1024 * 5, // max size 5MB + }, + storage: diskStorage({ + destination: "../uploads", + filename: (req, file, cb) => { + const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9); + cb(null, `${file.fieldname}-${uniqueSuffix}${file.originalname}`); + }, + }), +}; diff --git a/src/file-upload/dto/create-file-upload.dto.ts b/src/file-upload/dto/create-file-upload.dto.ts new file mode 100644 index 0000000..1078a7e --- /dev/null +++ b/src/file-upload/dto/create-file-upload.dto.ts @@ -0,0 +1,18 @@ +import { IsInt, IsNotEmpty, IsString } from "class-validator"; + +export class CreateFileUploadDto { + // Competency associated with Question bank csv file + @IsNotEmpty() + @IsString() + competency: string; + + // competencyLevel number of the uploaded question bank csv file + @IsNotEmpty() + @IsInt() + competencyLevelNumber: number; + + // Questions of the uploaded question bank csv file + @IsNotEmpty() + @IsString() + question: string; +} diff --git a/src/file-upload/dto/update-file-upload.dto.ts b/src/file-upload/dto/update-file-upload.dto.ts new file mode 100644 index 0000000..095d832 --- /dev/null +++ b/src/file-upload/dto/update-file-upload.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/swagger'; +import { CreateFileUploadDto } from './create-file-upload.dto'; + +export class UpdateFileUploadDto extends PartialType(CreateFileUploadDto) {} diff --git a/src/file-upload/file-upload.controller.ts b/src/file-upload/file-upload.controller.ts new file mode 100644 index 0000000..2dfba59 --- /dev/null +++ b/src/file-upload/file-upload.controller.ts @@ -0,0 +1,74 @@ +import { + Controller, + Post, + Logger, + HttpStatus, + UseInterceptors, + UploadedFile, + Res, +} from "@nestjs/common"; +import { FileUploadService } from "./file-upload.service"; +import { CreateFileUploadDto } from "./dto/create-file-upload.dto"; +import { UpdateFileUploadDto } from "./dto/update-file-upload.dto"; +import { + ApiBody, + ApiConsumes, + ApiOperation, + ApiResponse, + ApiTags, +} from "@nestjs/swagger"; +import { FileInterceptor } from "@nestjs/platform-express"; +import { multerOptions } from "src/config/multer-options.config"; +import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; + +@Controller("file-upload") +@ApiTags("file-upload") +export class FileUploadController { + // Create a logger instance for this controller to log events and errors. + private readonly logger = new Logger(FileUploadController.name); + + // The fileupload controller injecting the FileUpload service into it + constructor(private readonly fileUploadService: FileUploadService) {} + + @Post() + @UseInterceptors(FileInterceptor("file", multerOptions)) + @ApiConsumes("multipart/form-data") // Specify content type as multipart/form-data + @ApiBody({ + schema: { + type: 'object', + properties: { + file: { + type: 'string' || 'number', + format : 'binary' + } + } + } + }) + async uploadQuestions(@UploadedFile() file, @Res() res) { + // Log the initiaton for csv file upload + this.logger.log(`Initiate to upload a csv file.`); + try { + // Parsed the uploaded data + const parsedData = await this.fileUploadService.parseCSV(file.path); + // Store the parsedData in the db + await this.fileUploadService.storeData(parsedData); + // Clean up after the sucessful upload of csv data + await this.fileUploadService.deleteUploadedFile(file.path); + // Log the successful upload of the question bank. + this.logger.log(`Successfully uploaded question bank.`); + return res.status(HttpStatus.CREATED).json({ + message: "Question bank uploaded sucessfully.", + }); + } catch (error) { + this.logger.error(`Failed to upload question bank.`, error); + // get error message and status code + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + // Return an error response + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed to upload question bank.`, + }); + } + } +} diff --git a/src/file-upload/file-upload.module.ts b/src/file-upload/file-upload.module.ts new file mode 100644 index 0000000..56fb7a4 --- /dev/null +++ b/src/file-upload/file-upload.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { FileUploadService } from './file-upload.service'; +import { FileUploadController } from './file-upload.controller'; +import { PrismaModule } from 'src/prisma/prisma.module'; +import { PrismaService } from 'src/prisma/prisma.service'; + +@Module({ + imports : [PrismaModule], + controllers: [FileUploadController], + providers: [FileUploadService, PrismaService], + exports : [FileUploadService] +}) +export class FileUploadModule {} diff --git a/src/file-upload/file-upload.service.ts b/src/file-upload/file-upload.service.ts new file mode 100644 index 0000000..51568ea --- /dev/null +++ b/src/file-upload/file-upload.service.ts @@ -0,0 +1,140 @@ +import { Injectable } from "@nestjs/common"; +import { CreateFileUploadDto } from "./dto/create-file-upload.dto"; +import { PrismaService } from "src/prisma/prisma.service"; +import * as fs from "fs"; +import csvParser = require("csv-parser"); + +@Injectable() +export class FileUploadService { + constructor(private prisma: PrismaService) {} + + public async storeData(data: CreateFileUploadDto[]) { + // Check if the competency Existed or not the given csv data if not then filter and throw error + const allCompetencies = await this.prisma.competency.findMany(); + + // Extract an array of competency names from the fetched competencies + const competencyNames = allCompetencies.map( + (competency) => competency.name + ); + + // check for the data that is not in the competency model + const dataNotInCompetencyModel = data.filter( + (item) => !competencyNames.includes(item["competency"]) + ); + + // If there is data not in the competency model, you can throw an error or handle it as needed. + if (dataNotInCompetencyModel.length > 0) { + throw new Error( + `Some data is not in the competency model #${dataNotInCompetencyModel + .map((item) => item.competency) + .join(",")}` + ); + } + + // Check for the non-numeric competencyLevelNumber which is not a number in the csv data + const dataWithNonNumericCompetencyLevelNumber = data.filter((item) => { + const competencyLevelNumber = Number(item.competencyLevelNumber); + return ( + isNaN(competencyLevelNumber) || + competencyLevelNumber < 1 || + competencyLevelNumber > 7 + ); // Check if it's not a number and value should b/w 1 to 7 + }); + // Throw an error for the dataWithNonNumericCompetencyLevelNumber + if (dataWithNonNumericCompetencyLevelNumber.length > 0) { + throw new Error( + `The following competency level numbers are not numeric : ${dataWithNonNumericCompetencyLevelNumber + .map( + (item) => + `Competency ${item?.competency} with competency value #${item?.competencyLevelNumber || "Empty"}.` + ) + .join(",")}` + ); + } + + // Filter for the empty question in the csv data + const dataEmptyQuestion = data.filter((item) => !item.question); + // Throw an error for the dataEmptyQuestion + if (dataEmptyQuestion.length > 0) { + throw new Error( + `The following questions are empty: ${dataEmptyQuestion + .map( + (item) => + `Competency ${item?.competency} with competency value#${item?.competencyLevelNumber}.` + ) + .join(",")}` + ); + } + + const createManyDataPromises: Promise[] = data.map(async (item) => { + const getCompetencyId = await this.prisma.competency.findUnique({ + where: { + name: item["competency"], + }, + }); + if (getCompetencyId && getCompetencyId.id) { + // Check if a question with the same competencyId exists in the database + const existingQuestion = await this.prisma.questionBank.findFirst({ + where: { + competencyId: getCompetencyId.id, + competencyLevelNumber: Number(item["competencyLevelNumber"]), + }, + }); + if (existingQuestion) { + // If it exists, update the existing question + await this.prisma.questionBank.update({ + where: { + id: existingQuestion.id, + }, + data: { + question: item["question"], + competencyLevelNumber: Number(item["competencyLevelNumber"]), + }, + }); + } else { + // If it doesn't exist, create a new entry + await this.prisma.questionBank.create({ + data: { + question: item["question"], + competencyId: getCompetencyId.id, + competencyLevelNumber: Number(item["competencyLevelNumber"]), + }, + }); + } + } else { + throw new Error("Competency Id not found"); + } + }); + try { + await Promise.all(createManyDataPromises); + } catch (error) { + console.error("Error storing data in the database:", error); + throw new Error("Failed to store data in the database"); + } + } + + public async parseCSV(filePath: string): Promise { + const results: any[] = []; + + return new Promise((resolve, reject) => { + fs.createReadStream(filePath) + .pipe(csvParser()) + .on("data", (row) => { + results.push(row); + }) + .on("end", () => { + resolve(results); + }) + .on("error", (error) => { + reject(error); + }); + }); + } + public async deleteUploadedFile(filePath: string): Promise { + try { + await fs.promises.unlink(filePath); + } catch (error) { + console.error("Error deleting the file:", error); + } + } +} diff --git a/src/user-metadata/user-metadata.service.ts b/src/user-metadata/user-metadata.service.ts index 6c6112a..26652ed 100644 --- a/src/user-metadata/user-metadata.service.ts +++ b/src/user-metadata/user-metadata.service.ts @@ -154,7 +154,7 @@ export class UserMetadataService { Assessor: { select:{ userId: true, - userName: true, + // userName: true, } }, responseJson: true, From 6690ccde43dad66b9de53fdc67b97842b1aecb0c Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Wed, 25 Oct 2023 21:04:29 +0530 Subject: [PATCH 061/175] Added package-lock.json --- package-lock.json | 81 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index a2a62da..8deef58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,8 @@ "@prisma/client": "^5.4.2", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", + "csv-parser": "^3.0.0", + "multer": "^1.4.5-lts.1", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.2.0" @@ -27,6 +29,7 @@ "@nestjs/testing": "^9.0.0", "@types/express": "^4.17.13", "@types/jest": "28.1.8", + "@types/multer": "^1.4.9", "@types/node": "^16.0.0", "@types/supertest": "^2.0.11", "@typescript-eslint/eslint-plugin": "^5.0.0", @@ -2212,17 +2215,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/@nestjs/platform-express/node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, "node_modules/@nestjs/platform-express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -2267,14 +2259,6 @@ "node": ">= 0.8" } }, - "node_modules/@nestjs/platform-express/node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/@nestjs/platform-express/node_modules/tslib": { "version": "2.5.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", @@ -2741,6 +2725,15 @@ "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", "dev": true }, + "node_modules/@types/multer": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.9.tgz", + "integrity": "sha512-9NSvPJ2E8bNTc8XtJq1Cimx2Wrn2Ah48F15B2Du/hM8a8CHLhVbJMlF3ZCqhvMdht7Sa+YdP0aKP7N4fxDcrrg==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/node": { "version": "16.18.57", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.57.tgz", @@ -3896,6 +3889,17 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -4328,6 +4332,20 @@ "node": ">= 8" } }, + "node_modules/csv-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/csv-parser/-/csv-parser-3.0.0.tgz", + "integrity": "sha512-s6OYSXAK3IdKqYO33y09jhypG/bSDHPuyCme/IdEHfWpLf/jKcpitVFyOC6UemgGk8v7Q5u2XE0vvwmanxhGlQ==", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "csv-parser": "bin/csv-parser" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -8213,6 +8231,23 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/multer": { + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -9375,6 +9410,14 @@ "node": ">= 0.8" } }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", From 1347821ed6a16c97d2fbffe061c468cb3541843a Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Thu, 26 Oct 2023 12:08:44 +0530 Subject: [PATCH 062/175] competency level id removed --- prisma/schema.prisma | 14 +++++++------- src/survey-score/dto/create-survey-score.dto.ts | 2 +- src/survey-score/dto/response-survey-score.dto.ts | 2 +- .../interfaces/survey-score.interfaces.ts | 1 - src/survey-score/survey-score.service.ts | 11 ++++------- 5 files changed, 13 insertions(+), 17 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 2df7177..d961344 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -129,14 +129,14 @@ model ResponseTracker { } model SurveyScore { - id Int @id @default(autoincrement()) - surveyFormId Int - surveyForm SurveyForm @relation(fields: [surveyFormId], references: [id]) - competencyId Int - competencyLevelId Int - score Float + id Int @id @default(autoincrement()) + surveyFormId Int + surveyForm SurveyForm @relation(fields: [surveyFormId], references: [id]) + competencyId Int + competencyLevelNumber Int + score Float - @@unique([surveyFormId, competencyId, competencyLevelId]) + @@unique([surveyFormId, competencyId, competencyLevelNumber]) @@map("survey_scores") } diff --git a/src/survey-score/dto/create-survey-score.dto.ts b/src/survey-score/dto/create-survey-score.dto.ts index 8a7cdcb..2399849 100644 --- a/src/survey-score/dto/create-survey-score.dto.ts +++ b/src/survey-score/dto/create-survey-score.dto.ts @@ -11,7 +11,7 @@ export class CreateSurveyScoreDto { @IsNotEmpty() @IsInt() - competencyLevelId: number; + competencyLevelNumber: number; @IsNotEmpty() @IsNumber() diff --git a/src/survey-score/dto/response-survey-score.dto.ts b/src/survey-score/dto/response-survey-score.dto.ts index 96af24f..06a7cd3 100644 --- a/src/survey-score/dto/response-survey-score.dto.ts +++ b/src/survey-score/dto/response-survey-score.dto.ts @@ -5,7 +5,7 @@ export class SurveyScoreResponse { readonly id: number; readonly surveyFormId: number; readonly competencyId: number; - readonly competencyLevelId: number; + readonly competencyLevelNumber: number; readonly score: number; } diff --git a/src/survey-score/interfaces/survey-score.interfaces.ts b/src/survey-score/interfaces/survey-score.interfaces.ts index 3437e40..e6aa917 100644 --- a/src/survey-score/interfaces/survey-score.interfaces.ts +++ b/src/survey-score/interfaces/survey-score.interfaces.ts @@ -5,7 +5,6 @@ export interface IAnswerScore { export interface IGroupScoreData extends IAnswerScore { competencyId: number; - competencyLevelId: number; competencyLevelNumber: number; scorePercentage: number; } diff --git a/src/survey-score/survey-score.service.ts b/src/survey-score/survey-score.service.ts index 43b5510..b5878aa 100644 --- a/src/survey-score/survey-score.service.ts +++ b/src/survey-score/survey-score.service.ts @@ -155,8 +155,7 @@ export class SurveyScoreService { const question = await this.questionBankService.getQuestionById( +questionId ); - const { competencyId, competencyLevelId, competencyLevelNumber } = - question; + const { competencyId, competencyLevelNumber } = question; const scoreData = answerScore[questionId]; @@ -164,7 +163,6 @@ export class SurveyScoreService { score: scoreData.score, totalQuestions: scoreData.totalQuestions, competencyId, - competencyLevelId, competencyLevelNumber, scorePercentage: 0, }; @@ -180,14 +178,13 @@ export class SurveyScoreService { [key: string]: IGroupScoreData; }): { [key: string]: IGroupScoreData } { return Object.values(groupedData).reduce((acc, item) => { - const key = `${item.competencyId}-${item.competencyLevelId}-${item.competencyLevelNumber}`; + const key = `${item.competencyId}-${item.competencyLevelNumber}`; if (!acc[key]) { acc[key] = { score: 0, totalQuestions: 0, competencyId: item.competencyId, - competencyLevelId: item.competencyLevelId, competencyLevelNumber: item.competencyLevelNumber, }; } @@ -221,11 +218,11 @@ export class SurveyScoreService { finalGroupedData: IGroupScoreData[] ) { return finalGroupedData.map((data: IGroupScoreData) => { - const { competencyId, competencyLevelId, scorePercentage } = data; + const { competencyId, competencyLevelNumber, scorePercentage } = data; return { surveyFormId, competencyId, - competencyLevelId, + competencyLevelNumber, score: scorePercentage, }; }); From fc3299f2ac86c28d98814d00836e7003230a634e Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Thu, 26 Oct 2023 14:28:13 +0530 Subject: [PATCH 063/175] Changes as per PR Review --- .../admin-department.controller.ts | 12 +-- .../admin-department.service.ts | 78 ++++++++----------- 2 files changed, 41 insertions(+), 49 deletions(-) diff --git a/src/admin-department/admin-department.controller.ts b/src/admin-department/admin-department.controller.ts index a1a2516..c432f7d 100644 --- a/src/admin-department/admin-department.controller.ts +++ b/src/admin-department/admin-department.controller.ts @@ -42,7 +42,10 @@ export class AdminDepartmentController { this.logger.log(`Initiate to create a admin department`); const createdAdminDepartment = - await this.adminDepartmentService.createAdminDepartment(departmentId); + await this.adminDepartmentService.createOrUpdateAdminDepartment( + departmentId, + "post" + ); // Log the successful creation of the admin department this.logger.log(`Successfully created admin department.`); return res.status(HttpStatus.CREATED).json({ @@ -108,17 +111,16 @@ export class AdminDepartmentController { @ApiResponse({ status: HttpStatus.OK, type: ResponseAdminDepartmentDto }) // Api response for the swagger async updateAdminDepartmentById( @Res() res, - @Param("id", ParseIntPipe) id: number, - @Body() updateAdminDepartmentDto: UpdateAdminDepartmentDto + @Param("id", ParseIntPipe) id: number ): Promise { try { // Log the initiation for updating the admin department for the given id this.logger.log(`Initiated updating the admin department for id #${id}`); // Update the admin department for the id const updateAdminDepartment = - await this.adminDepartmentService.updateAdminDepartmentById( + await this.adminDepartmentService.createOrUpdateAdminDepartment( id, - updateAdminDepartmentDto + "patch" ); // Log the successful update of the admin department with the given id this.logger.log(`Successfully updated admin department for id #${id}`); diff --git a/src/admin-department/admin-department.service.ts b/src/admin-department/admin-department.service.ts index cea60fe..fac4379 100644 --- a/src/admin-department/admin-department.service.ts +++ b/src/admin-department/admin-department.service.ts @@ -3,9 +3,7 @@ import { Injectable, NotFoundException, } from "@nestjs/common"; -import { - FilterAdminDepartmentsDto, -} from "./dto/create-admin-department.dto"; +import { FilterAdminDepartmentsDto } from "./dto/create-admin-department.dto"; import { UpdateAdminDepartmentDto } from "./dto/update-admin-department.dto"; import { PrismaService } from "src/prisma/prisma.service"; import { MockDepartmentService } from "src/mockModules/mock-department/mock-department.service"; @@ -17,9 +15,7 @@ export class AdminDepartmentService { private departmentService: MockDepartmentService ) {} - public async createAdminDepartment( - id : number, - ) { + public async createOrUpdateAdminDepartment(id: number, requestType: string) { // Check the admin department exist for admin department db or not const checkExistingAdminDepartment = await this.prisma.adminDepartment.findUnique({ @@ -27,24 +23,46 @@ export class AdminDepartmentService { departmentId: id, }, }); - // If admin department exist in db then throw an error - if (checkExistingAdminDepartment) { - throw new ForbiddenException("Admin Department already exists."); - } - // Check if the department exist in mock department module or not - const adminDepartment = await this.departmentService.findOne( - id - ); + const adminDepartment = await this.departmentService.findOne(id); // If department does not exist in the department module then throw an error if (!adminDepartment) { - throw new NotFoundException(`The Department for the #${id} not found.`); + throw new NotFoundException( + `The Mock Admin Department for the #${id} not found.` + ); + } + // If admin department exist in db then update the existing admin department + if (!checkExistingAdminDepartment && requestType === "patch") { + throw new NotFoundException( + `The Admin Department for the #${id} not found.` + ); + } + if ( + checkExistingAdminDepartment && + adminDepartment && + requestType === "patch" + ) { + const updatedAdminDepartment = + await this.prisma.adminDepartment.updateMany({ + where: { + departmentId: id, + }, + data: { + name: adminDepartment.name, + description: adminDepartment.description, + }, + }); + return updatedAdminDepartment; + } + // If department exist and we are trying to create a new admin department. + if (checkExistingAdminDepartment && requestType === "post") { + throw new ForbiddenException("Admin Department already exists."); } return this.prisma.adminDepartment.create({ data: { departmentId: adminDepartment.id, name: adminDepartment.name, - description: adminDepartment.description + description: adminDepartment.description, }, }); } @@ -61,32 +79,4 @@ export class AdminDepartmentService { take: limit, }); } - - public async updateAdminDepartmentById( - adminDepartmentId: number, - updateAdminDepartmentDto: UpdateAdminDepartmentDto - ) { - // Check if there is Admin department in the db for the id - let checkExistingDepartment = await this.prisma.adminDepartment.findFirst({ - where: { - id: adminDepartmentId, - }, - }); - // If not found throw error message - if (!checkExistingDepartment) { - throw new NotFoundException( - `Admin Department with ID "${adminDepartmentId}" not found.` - ); - } - // Otherwise update existing Admin department - const updatedAdminDepartment = await this.prisma.adminDepartment.updateMany( - { - where: { - id: adminDepartmentId, - }, - data: updateAdminDepartmentDto, - } - ); - return updatedAdminDepartment; - } } From 0b43ac5943807cf96d5b207ca50d6c17378ace52 Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Thu, 26 Oct 2023 15:42:51 +0530 Subject: [PATCH 064/175] removed the API for updating and creating and updating admin departmeent using single api --- .../admin-department.controller.ts | 62 +++---------------- .../admin-department.service.ts | 38 ++++-------- 2 files changed, 21 insertions(+), 79 deletions(-) diff --git a/src/admin-department/admin-department.controller.ts b/src/admin-department/admin-department.controller.ts index c432f7d..5d168bb 100644 --- a/src/admin-department/admin-department.controller.ts +++ b/src/admin-department/admin-department.controller.ts @@ -29,11 +29,11 @@ export class AdminDepartmentController { private readonly adminDepartmentService: AdminDepartmentService ) {} - // Api for creating a new admin department using departmentId + // Api for creating or updating a admin department using departmentId @Post(":departmentId") - @ApiOperation({ summary: "Create a new admin department" }) // Api operation for swagger + @ApiOperation({ summary: "Create or update admin department" }) // Api operation for swagger @ApiResponse({ status: HttpStatus.CREATED, type: ResponseAdminDepartmentDto }) // Api response for the swagger - async createAdminDepartment( + async createOrUpdateAdminDepartment( @Res() res, @Param("departmentId", ParseIntPipe) departmentId: number ): Promise { @@ -41,26 +41,25 @@ export class AdminDepartmentController { // Log the initiation for the admin department creation this.logger.log(`Initiate to create a admin department`); - const createdAdminDepartment = + const createdOrUpdatedAdminDepartment = await this.adminDepartmentService.createOrUpdateAdminDepartment( - departmentId, - "post" + departmentId ); // Log the successful creation of the admin department this.logger.log(`Successfully created admin department.`); return res.status(HttpStatus.CREATED).json({ - message: "Admin department created sucessfully.", - data: createdAdminDepartment, + message: "Admin department created or updated sucessfully.", + data: createdOrUpdatedAdminDepartment, }); } catch (error) { - this.logger.error(`Failed to create new admin department.`, error); + this.logger.error(`Failed to create or update admin department.`, error); // get error message and status code const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); // Return an error response return res.status(statusCode).json({ statusCode, - message: errorMessage || `Failed to create new admin department.`, + message: errorMessage || `Failed to create or update admin department.`, }); } } @@ -104,47 +103,4 @@ export class AdminDepartmentController { }); } } - - // Api for updating the admin department - @Patch("update/:id") - @ApiOperation({ summary: "Update the admin department by Id." }) // Api operation for the swagger - @ApiResponse({ status: HttpStatus.OK, type: ResponseAdminDepartmentDto }) // Api response for the swagger - async updateAdminDepartmentById( - @Res() res, - @Param("id", ParseIntPipe) id: number - ): Promise { - try { - // Log the initiation for updating the admin department for the given id - this.logger.log(`Initiated updating the admin department for id #${id}`); - // Update the admin department for the id - const updateAdminDepartment = - await this.adminDepartmentService.createOrUpdateAdminDepartment( - id, - "patch" - ); - // Log the successful update of the admin department with the given id - this.logger.log(`Successfully updated admin department for id #${id}`); - // Return response and statuscode for the successful update of the admin department - return res.status(HttpStatus.OK).json({ - message: `Successfully updated admin department for id #${id}`, - data: updateAdminDepartment, - }); - } catch (error) { - // Log the error - this.logger.error( - `Failed to update admin department for id #${id}`, - error - ); - - const { errorMessage, statusCode } = - getPrismaErrorStatusAndMessage(error); // get error message and status code - - // Return the response and status code for the failed update of the admin department - return res.status(statusCode).json({ - statusCode, - message: - errorMessage || `Failed to update admin department for id #${id}`, - }); - } - } } diff --git a/src/admin-department/admin-department.service.ts b/src/admin-department/admin-department.service.ts index fac4379..2b6ac97 100644 --- a/src/admin-department/admin-department.service.ts +++ b/src/admin-department/admin-department.service.ts @@ -15,7 +15,7 @@ export class AdminDepartmentService { private departmentService: MockDepartmentService ) {} - public async createOrUpdateAdminDepartment(id: number, requestType: string) { + public async createOrUpdateAdminDepartment(id: number) { // Check the admin department exist for admin department db or not const checkExistingAdminDepartment = await this.prisma.adminDepartment.findUnique({ @@ -31,33 +31,19 @@ export class AdminDepartmentService { `The Mock Admin Department for the #${id} not found.` ); } - // If admin department exist in db then update the existing admin department - if (!checkExistingAdminDepartment && requestType === "patch") { - throw new NotFoundException( - `The Admin Department for the #${id} not found.` - ); - } - if ( - checkExistingAdminDepartment && - adminDepartment && - requestType === "patch" - ) { - const updatedAdminDepartment = - await this.prisma.adminDepartment.updateMany({ - where: { - departmentId: id, - }, - data: { - name: adminDepartment.name, - description: adminDepartment.description, - }, - }); + // If it is existing admin department then update the admin department + if (checkExistingAdminDepartment) { + const updatedAdminDepartment = await this.prisma.adminDepartment.update({ + where: { + departmentId: id, + }, + data: { + name: adminDepartment.name, + description: adminDepartment.description, + }, + }); return updatedAdminDepartment; } - // If department exist and we are trying to create a new admin department. - if (checkExistingAdminDepartment && requestType === "post") { - throw new ForbiddenException("Admin Department already exists."); - } return this.prisma.adminDepartment.create({ data: { departmentId: adminDepartment.id, From 66d8808c4394eff145f225f168c2eb375e8be038 Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Fri, 27 Oct 2023 13:57:42 +0530 Subject: [PATCH 065/175] Schema changes and changes in related module --- prisma/schema.prisma | 11 +++++------ .../dto/create-competency-level.dto.ts | 2 +- .../mock-level/dto/create-mock-level.dto.ts | 6 +++--- .../mock-level/dto/response-mock-level.dto.ts | 3 +-- src/mockModules/mock-level/mock-level.service.ts | 6 +++--- src/mockModules/mock-user/dto/create-mock-user.dto.ts | 3 +-- .../mock-user/dto/response-mock-user.dto.ts | 4 ++-- src/mockModules/mock-user/mock-user.service.ts | 3 +-- 8 files changed, 17 insertions(+), 21 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 4d30a17..1ed74af 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -167,8 +167,8 @@ model User { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt designation String - Level Level? @relation(fields: [levelId], references: [id]) - levelId Int? + Level Level? @relation(fields: [levelNumber], references: [levelNumber]) + levelNumber Int? Department Department? @relation(fields: [departmentId], references: [id]) departmentId Int? @@ -176,8 +176,7 @@ model User { } model Level { - id Int @id @default(autoincrement()) - label String + levelNumber Int @id @unique description String? users User[] CompetencyLevel CompetencyLevel[] @@ -235,8 +234,8 @@ model CompetencyLevel { id Int @id @default(autoincrement()) name String @unique description String? - levelId Int - Level Level @relation(fields: [levelId], references: [id]) + levelNumber Int + Level Level @relation(fields: [levelNumber], references: [levelNumber]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt competencies CompetencyToCompetencyLevel[] diff --git a/src/mockModules/mock-competency-level/dto/create-competency-level.dto.ts b/src/mockModules/mock-competency-level/dto/create-competency-level.dto.ts index 2c34399..3dbc6bb 100644 --- a/src/mockModules/mock-competency-level/dto/create-competency-level.dto.ts +++ b/src/mockModules/mock-competency-level/dto/create-competency-level.dto.ts @@ -13,5 +13,5 @@ export class CreateCompetencyLevelDto { @IsNumber() @IsNotEmpty() - levelId: number; + levelNumber: number; } \ No newline at end of file diff --git a/src/mockModules/mock-level/dto/create-mock-level.dto.ts b/src/mockModules/mock-level/dto/create-mock-level.dto.ts index 4d7efe6..4111250 100644 --- a/src/mockModules/mock-level/dto/create-mock-level.dto.ts +++ b/src/mockModules/mock-level/dto/create-mock-level.dto.ts @@ -1,9 +1,9 @@ -import { IsNotEmpty, IsOptional, IsString } from "class-validator"; +import { IsInt, IsNotEmpty, IsOptional, IsString } from "class-validator"; export class CreateMockLevelDto { - @IsString() + @IsInt() @IsNotEmpty() - label: string; + levelNumber: number; @IsOptional() @IsString() diff --git a/src/mockModules/mock-level/dto/response-mock-level.dto.ts b/src/mockModules/mock-level/dto/response-mock-level.dto.ts index 82c8ab7..e2a9a8e 100644 --- a/src/mockModules/mock-level/dto/response-mock-level.dto.ts +++ b/src/mockModules/mock-level/dto/response-mock-level.dto.ts @@ -1,5 +1,4 @@ export class ResponseMockLevelDto { - readonly id: number; - readonly label: string; + readonly levelNumber: number; readonly description?: string; } diff --git a/src/mockModules/mock-level/mock-level.service.ts b/src/mockModules/mock-level/mock-level.service.ts index b1a84dc..fe5a5a3 100644 --- a/src/mockModules/mock-level/mock-level.service.ts +++ b/src/mockModules/mock-level/mock-level.service.ts @@ -19,7 +19,7 @@ export class MockLevelService { public async findOne(id: number) { const level = await this.prisma.level.findUnique({ where: { - id, + levelNumber : id, }, }); if (!level) throw new NotFoundException(`Level with id #${id} not found`); @@ -29,7 +29,7 @@ export class MockLevelService { public async update(id: number, updateMockLevelDto: UpdateMockLevelDto) { return await this.prisma.level.update({ where: { - id, + levelNumber: id, }, data: updateMockLevelDto, }); @@ -38,7 +38,7 @@ export class MockLevelService { public async remove(id: number) { return await this.prisma.level.delete({ where: { - id, + levelNumber: id, }, }); } diff --git a/src/mockModules/mock-user/dto/create-mock-user.dto.ts b/src/mockModules/mock-user/dto/create-mock-user.dto.ts index f81b22a..8dab42d 100644 --- a/src/mockModules/mock-user/dto/create-mock-user.dto.ts +++ b/src/mockModules/mock-user/dto/create-mock-user.dto.ts @@ -34,10 +34,9 @@ export class CreateMockUserDto { @IsString() profilePicture: string; - @IsOptional() @IsNotEmpty() @IsNumber() - levelId: number; + levelNumber: number; @IsOptional() @IsNotEmpty() diff --git a/src/mockModules/mock-user/dto/response-mock-user.dto.ts b/src/mockModules/mock-user/dto/response-mock-user.dto.ts index 1cfcfcd..f338731 100644 --- a/src/mockModules/mock-user/dto/response-mock-user.dto.ts +++ b/src/mockModules/mock-user/dto/response-mock-user.dto.ts @@ -7,8 +7,8 @@ export class ResponseMockUserDto { readonly role: string; readonly userName: string; readonly profilePicture?: string; - readonly levelId?: number; - readonly Level?: ResponseMockLevelDto; + readonly levelNumber: number; + readonly Level: ResponseMockLevelDto; readonly departmentId?: number; readonly Department?: ResponseDepartmentDto; readonly designation: String; diff --git a/src/mockModules/mock-user/mock-user.service.ts b/src/mockModules/mock-user/mock-user.service.ts index 9bb5539..8c6bc0b 100644 --- a/src/mockModules/mock-user/mock-user.service.ts +++ b/src/mockModules/mock-user/mock-user.service.ts @@ -69,8 +69,7 @@ export class MockUserService { designation:true, Level: { select: { - id: true, - label: true, + levelNumber : true }, }, Department: { From 73705690cc97ef57244b04cfe8ccb5bb463f65f4 Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Fri, 27 Oct 2023 15:02:08 +0530 Subject: [PATCH 066/175] API for user to map with questions using competencyId and competencyLevelNumber --- .../mock-designation.service.ts | 12 ++++ .../mock-role/mock-role.service.ts | 1 + src/question-bank/question-bank.controller.ts | 44 +++++++++++- src/question-bank/question-bank.module.ts | 15 +++- src/question-bank/question-bank.service.ts | 71 ++++++++++++++++++- 5 files changed, 137 insertions(+), 6 deletions(-) diff --git a/src/mockModules/mock-designation/mock-designation.service.ts b/src/mockModules/mock-designation/mock-designation.service.ts index 0c35540..01c2022 100644 --- a/src/mockModules/mock-designation/mock-designation.service.ts +++ b/src/mockModules/mock-designation/mock-designation.service.ts @@ -71,4 +71,16 @@ export class MockDesignationService { }, }); } + + async findAllRolesForDesignation(designation: string) { + return this.prisma.designation.findUnique({ + where: { + name: designation, + }, + select: { + Roles: true, + }, + }); + + } } diff --git a/src/mockModules/mock-role/mock-role.service.ts b/src/mockModules/mock-role/mock-role.service.ts index 9c1ce19..68341cd 100644 --- a/src/mockModules/mock-role/mock-role.service.ts +++ b/src/mockModules/mock-role/mock-role.service.ts @@ -37,6 +37,7 @@ export class MockRoleService { competencies: { select: { competency: true, + competencyId: true }, }, }, diff --git a/src/question-bank/question-bank.controller.ts b/src/question-bank/question-bank.controller.ts index d074414..69c70f8 100644 --- a/src/question-bank/question-bank.controller.ts +++ b/src/question-bank/question-bank.controller.ts @@ -161,8 +161,9 @@ export class QuestionBankController { try { this.logger.log(`Initiating deleting of a question with an id ${id}`); - const deletedQuestion = - await this.questionBankService.deleteQuestionById(id); + const deletedQuestion = await this.questionBankService.deleteQuestionById( + id + ); return res.status(HttpStatus.OK).json({ message: `Successfully deleted question for id #${id}`, @@ -180,4 +181,43 @@ export class QuestionBankController { }); } } + + // Get all the mapped questions for the user + @Get("user/:id") + @ApiOperation({ + summary: "Get all the questions for a user in the survey form.", + }) + @ApiResponse({ + status: HttpStatus.OK, + type: ResponseQuestionBankDto, + isArray: true, + }) + async getAllQuestionsForUser( + @Res() res, + @Param("id") id: string + ): Promise { + try { + this.logger.log( + `Initiating the fetching of all the question for user with id#${id}` + ); + + const getAllQuestionsForUser = + await this.questionBankService.getAllQuestionsForUser(id); + + return res.status(HttpStatus.OK).json({ + message: `Successfully get all the question for id #${id}`, + data: getAllQuestionsForUser, + }); + } catch (error) { + this.logger.error(`Failed to get all questions for id #${id}`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); // get error message and status code + + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed to get all questions for id #${id}`, + }); + } + } } diff --git a/src/question-bank/question-bank.module.ts b/src/question-bank/question-bank.module.ts index a4d475a..815aa5d 100644 --- a/src/question-bank/question-bank.module.ts +++ b/src/question-bank/question-bank.module.ts @@ -3,6 +3,11 @@ import { QuestionBankService } from './question-bank.service'; import { QuestionBankController } from './question-bank.controller'; import { PrismaModule } from 'src/prisma/prisma.module'; import { PrismaService } from 'src/prisma/prisma.service'; +import { MockUserService } from 'src/mockModules/mock-user/mock-user.service'; +import { MockDesignationService } from 'src/mockModules/mock-designation/mock-designation.service'; +import { MockRoleService } from 'src/mockModules/mock-role/mock-role.service'; +import { MockCompetencyService } from 'src/mockModules/mock-competency/mock-competency.service'; +import { MockCompetencyLevelService } from 'src/mockModules/mock-competency-level/mock-competency-level.service'; @Module({ // Import the PrismaModule to make Prisma service available within this module @@ -12,7 +17,15 @@ import { PrismaService } from 'src/prisma/prisma.service'; controllers: [QuestionBankController], // Declare the QuestionBankService and PrismaService as providers for this module - providers: [QuestionBankService, PrismaService], + providers: [ + QuestionBankService, + MockCompetencyService, + MockCompetencyLevelService, + PrismaService, + MockUserService, + MockDesignationService, + MockRoleService, + ], // Export the QuestionBankService to make it available for other modules that import this module exports: [QuestionBankService], diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index c33d464..db50214 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -5,10 +5,18 @@ import { } from "./dto/create-question-bank.dto"; import { UpdateQuestionBankDto } from "./dto/update-question-bank.dto"; import { PrismaService } from "src/prisma/prisma.service"; +import { MockUserService } from "src/mockModules/mock-user/mock-user.service"; +import { MockRoleService } from "./../mockModules/mock-role/mock-role.service"; +import { MockDesignationService } from "src/mockModules/mock-designation/mock-designation.service"; @Injectable() export class QuestionBankService { - constructor(private prisma: PrismaService) {} + constructor( + private prisma: PrismaService, + private mockUserService: MockUserService, + private mockDesignationService: MockDesignationService, + private mockRoleService: MockRoleService + ) {} async createQuestionByCompentencyLevel( createQuestionBankDto: CreateQuestionBankDto @@ -90,7 +98,7 @@ export class QuestionBankService { }); } - async deleteQuestionById(questionId : number) { + async deleteQuestionById(questionId: number) { // Check the question is there in db for the given questionId and competencyLevelId const findQuestion = await this.prisma.questionBank.findUnique({ where: { @@ -107,7 +115,64 @@ export class QuestionBankService { return this.prisma.questionBank.delete({ where: { id: questionId, + }, + }); + } + + async getAllQuestionsForUser(userId: string) { + const user = await this.mockUserService.findOne(userId); + if (!user) { + // Handle the case when the user is not found. + throw new Error("User not found"); + } + + // Get the user's designation + const designation = user.designation; + + // Get all the roles for a designation + const userRoles = + await this.mockDesignationService.findAllRolesForDesignation(designation); + + // Get roleId of each role of the user + const roleIds = userRoles?.Roles.map((item) => { + return item.id; + }); + + // unique competencyId associated with the roles of the user + const competencyIds: any = []; + if (roleIds?.length) { + for (let i = 0; i < roleIds.length; i++) { + const competency = (await this.mockRoleService.findRoleById(roleIds[i])) + .competencies; + competencyIds.push( + ...competency.filter((item) => { + if (!competencyIds.includes(item.competencyId)) { + competencyIds.push(item.competencyId); + } + }) + ); + } + } + + // Get competencyLevel for an user + let competencyLevelNumber = user.Level?.levelNumber; + + const questionLists: any = []; + if (competencyIds?.length && competencyLevelNumber) { + for (let i = 0; i < competencyIds.length; i++) { + while (competencyLevelNumber) { + let question = await this.getAllQuestions({ + competencyId: competencyIds[i], + competencyLevelNumber: competencyLevelNumber, + }); + questionLists.push(...question); + competencyLevelNumber--; + } } - }) + } + if (!questionLists.length) { + throw new NotFoundException(`No Question Found for user with #${userId}`); + } + return questionLists; } } From f4d5b82501e02b4516b9a9c2649ff18130d1388c Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Mon, 30 Oct 2023 15:00:40 +0530 Subject: [PATCH 067/175] Changes as per PR review and refactor. --- src/config/multer-options.config.ts | 6 +-- src/file-upload/file-upload.controller.ts | 8 +-- src/file-upload/file-upload.module.ts | 25 +++++++--- src/file-upload/file-upload.service.ts | 49 ++++++++++++------- .../mock-competency.service.ts | 8 +++ .../dto/create-question-bank.dto.ts | 1 - .../dto/response-question-bank.dto.ts | 1 - .../dto/update-question-bank.dto.ts | 2 +- src/user-metadata/user-metadata.service.ts | 2 +- 9 files changed, 62 insertions(+), 40 deletions(-) diff --git a/src/config/multer-options.config.ts b/src/config/multer-options.config.ts index 1eaf12d..93d5fb6 100644 --- a/src/config/multer-options.config.ts +++ b/src/config/multer-options.config.ts @@ -2,13 +2,13 @@ import { MulterOptions } from "@nestjs/platform-express/multer/interfaces/multer import { diskStorage } from "multer"; export const multerOptions: MulterOptions = { - dest: "../uploads", // this is the destination of the uploaded files on disk + dest: "src/uploads", // this is the destination of the uploaded files on disk limits: { fileSize: 1024 * 1024 * 5, // max size 5MB }, storage: diskStorage({ - destination: "../uploads", - filename: (req, file, cb) => { + destination: "src/uploads", + filename: (_req, file, cb) => { const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9); cb(null, `${file.fieldname}-${uniqueSuffix}${file.originalname}`); }, diff --git a/src/file-upload/file-upload.controller.ts b/src/file-upload/file-upload.controller.ts index 2dfba59..b2b98d2 100644 --- a/src/file-upload/file-upload.controller.ts +++ b/src/file-upload/file-upload.controller.ts @@ -48,13 +48,7 @@ export class FileUploadController { // Log the initiaton for csv file upload this.logger.log(`Initiate to upload a csv file.`); try { - // Parsed the uploaded data - const parsedData = await this.fileUploadService.parseCSV(file.path); - // Store the parsedData in the db - await this.fileUploadService.storeData(parsedData); - // Clean up after the sucessful upload of csv data - await this.fileUploadService.deleteUploadedFile(file.path); - // Log the successful upload of the question bank. + this.fileUploadService.uploadCsvFile(file.path); this.logger.log(`Successfully uploaded question bank.`); return res.status(HttpStatus.CREATED).json({ message: "Question bank uploaded sucessfully.", diff --git a/src/file-upload/file-upload.module.ts b/src/file-upload/file-upload.module.ts index 56fb7a4..4c48f61 100644 --- a/src/file-upload/file-upload.module.ts +++ b/src/file-upload/file-upload.module.ts @@ -1,13 +1,22 @@ -import { Module } from '@nestjs/common'; -import { FileUploadService } from './file-upload.service'; -import { FileUploadController } from './file-upload.controller'; -import { PrismaModule } from 'src/prisma/prisma.module'; -import { PrismaService } from 'src/prisma/prisma.service'; +import { Module } from "@nestjs/common"; +import { FileUploadService } from "./file-upload.service"; +import { FileUploadController } from "./file-upload.controller"; +import { PrismaModule } from "src/prisma/prisma.module"; +import { PrismaService } from "src/prisma/prisma.service"; +import { MockCompetencyService } from "src/mockModules/mock-competency/mock-competency.service"; +import { MockCompetencyLevelService } from "src/mockModules/mock-competency-level/mock-competency-level.service"; +import { QuestionBankService } from "src/question-bank/question-bank.service"; @Module({ - imports : [PrismaModule], + imports: [PrismaModule], controllers: [FileUploadController], - providers: [FileUploadService, PrismaService], - exports : [FileUploadService] + providers: [ + FileUploadService, + PrismaService, + MockCompetencyService, + MockCompetencyLevelService, + QuestionBankService, + ], + exports: [FileUploadService], }) export class FileUploadModule {} diff --git a/src/file-upload/file-upload.service.ts b/src/file-upload/file-upload.service.ts index 51568ea..b854acb 100644 --- a/src/file-upload/file-upload.service.ts +++ b/src/file-upload/file-upload.service.ts @@ -3,14 +3,29 @@ import { CreateFileUploadDto } from "./dto/create-file-upload.dto"; import { PrismaService } from "src/prisma/prisma.service"; import * as fs from "fs"; import csvParser = require("csv-parser"); +import { MockCompetencyService } from "src/mockModules/mock-competency/mock-competency.service"; +import { QuestionBankService } from "src/question-bank/question-bank.service"; +import path = require("path"); @Injectable() export class FileUploadService { - constructor(private prisma: PrismaService) {} + constructor( + private prisma: PrismaService, + private competencyService: MockCompetencyService, + private questionBankService: QuestionBankService + ) {} + public async uploadCsvFile(filepath) { + // Parsed the uploaded data + const parsedData = await this.parseCSV(filepath); + // Store the parsedData in the db + await this.storeData(parsedData); + // Clean up after the sucessful upload of csv data + await this.deleteUploadedFile(filepath); + } public async storeData(data: CreateFileUploadDto[]) { // Check if the competency Existed or not the given csv data if not then filter and throw error - const allCompetencies = await this.prisma.competency.findMany(); + const allCompetencies = await this.competencyService.findAllCompetencies(); // Extract an array of competency names from the fetched competencies const competencyNames = allCompetencies.map( @@ -46,7 +61,9 @@ export class FileUploadService { `The following competency level numbers are not numeric : ${dataWithNonNumericCompetencyLevelNumber .map( (item) => - `Competency ${item?.competency} with competency value #${item?.competencyLevelNumber || "Empty"}.` + `Competency ${item?.competency} with competency value #${ + item?.competencyLevelNumber || "Empty" + }.` ) .join(",")}` ); @@ -67,16 +84,14 @@ export class FileUploadService { } const createManyDataPromises: Promise[] = data.map(async (item) => { - const getCompetencyId = await this.prisma.competency.findUnique({ - where: { - name: item["competency"], - }, - }); - if (getCompetencyId && getCompetencyId.id) { + const getCompetency = await this.competencyService.findCompetencyByName( + item.competency + ); + if (getCompetency && getCompetency.id) { // Check if a question with the same competencyId exists in the database const existingQuestion = await this.prisma.questionBank.findFirst({ where: { - competencyId: getCompetencyId.id, + competencyId: getCompetency.id, competencyLevelNumber: Number(item["competencyLevelNumber"]), }, }); @@ -93,12 +108,10 @@ export class FileUploadService { }); } else { // If it doesn't exist, create a new entry - await this.prisma.questionBank.create({ - data: { - question: item["question"], - competencyId: getCompetencyId.id, - competencyLevelNumber: Number(item["competencyLevelNumber"]), - }, + await this.questionBankService.createQuestionByCompentencyId({ + question: item["question"], + competencyId: getCompetency.id, + competencyLevelNumber: Number(item["competencyLevelNumber"]), }); } } else { @@ -132,9 +145,9 @@ export class FileUploadService { } public async deleteUploadedFile(filePath: string): Promise { try { - await fs.promises.unlink(filePath); + fs.unlinkSync(filePath) } catch (error) { - console.error("Error deleting the file:", error); + throw new Error("Error unlinking the file.",); } } } diff --git a/src/mockModules/mock-competency/mock-competency.service.ts b/src/mockModules/mock-competency/mock-competency.service.ts index 34caf42..e98f36c 100644 --- a/src/mockModules/mock-competency/mock-competency.service.ts +++ b/src/mockModules/mock-competency/mock-competency.service.ts @@ -129,4 +129,12 @@ export class MockCompetencyService { }, }); } + + public async findCompetencyByName(competencyName: string) { + return this.prisma.competency.findUnique({ + where: { + name: competencyName + } + }) + } } diff --git a/src/question-bank/dto/create-question-bank.dto.ts b/src/question-bank/dto/create-question-bank.dto.ts index ac4eb47..ddd82b1 100644 --- a/src/question-bank/dto/create-question-bank.dto.ts +++ b/src/question-bank/dto/create-question-bank.dto.ts @@ -1,5 +1,4 @@ import { - IsDate, IsInt, IsNotEmpty, IsOptional, diff --git a/src/question-bank/dto/response-question-bank.dto.ts b/src/question-bank/dto/response-question-bank.dto.ts index 45f1f39..fcf52cf 100644 --- a/src/question-bank/dto/response-question-bank.dto.ts +++ b/src/question-bank/dto/response-question-bank.dto.ts @@ -1,6 +1,5 @@ export class ResponseQuestionBankDto { readonly competencyId: number; - readonly competencyLevelId: number; readonly competencyLevelNumber: number; readonly question: string; readonly createdAt: Date; diff --git a/src/question-bank/dto/update-question-bank.dto.ts b/src/question-bank/dto/update-question-bank.dto.ts index 7865f23..a748473 100644 --- a/src/question-bank/dto/update-question-bank.dto.ts +++ b/src/question-bank/dto/update-question-bank.dto.ts @@ -1,4 +1,4 @@ -import { IsDate, IsNotEmpty, IsOptional, IsString } from "class-validator"; +import { IsNotEmpty, IsString } from "class-validator"; export class UpdateQuestionBankDto { // Question for the question bank diff --git a/src/user-metadata/user-metadata.service.ts b/src/user-metadata/user-metadata.service.ts index 26652ed..6c6112a 100644 --- a/src/user-metadata/user-metadata.service.ts +++ b/src/user-metadata/user-metadata.service.ts @@ -154,7 +154,7 @@ export class UserMetadataService { Assessor: { select:{ userId: true, - // userName: true, + userName: true, } }, responseJson: true, From 3a37d14cb11576c0701d5760a702b7c93219eee6 Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Mon, 30 Oct 2023 15:18:36 +0530 Subject: [PATCH 068/175] Changes as per PR review and refactor. --- src/file-upload/file-upload.service.ts | 106 +------------------ src/question-bank/question-bank.module.ts | 4 +- src/question-bank/question-bank.service.ts | 114 ++++++++++++++++++++- 3 files changed, 116 insertions(+), 108 deletions(-) diff --git a/src/file-upload/file-upload.service.ts b/src/file-upload/file-upload.service.ts index b854acb..772b207 100644 --- a/src/file-upload/file-upload.service.ts +++ b/src/file-upload/file-upload.service.ts @@ -19,113 +19,11 @@ export class FileUploadService { // Parsed the uploaded data const parsedData = await this.parseCSV(filepath); // Store the parsedData in the db - await this.storeData(parsedData); + await this.questionBankService.bulkUploadQuestions(parsedData); // Clean up after the sucessful upload of csv data await this.deleteUploadedFile(filepath); } - public async storeData(data: CreateFileUploadDto[]) { - // Check if the competency Existed or not the given csv data if not then filter and throw error - const allCompetencies = await this.competencyService.findAllCompetencies(); - - // Extract an array of competency names from the fetched competencies - const competencyNames = allCompetencies.map( - (competency) => competency.name - ); - - // check for the data that is not in the competency model - const dataNotInCompetencyModel = data.filter( - (item) => !competencyNames.includes(item["competency"]) - ); - - // If there is data not in the competency model, you can throw an error or handle it as needed. - if (dataNotInCompetencyModel.length > 0) { - throw new Error( - `Some data is not in the competency model #${dataNotInCompetencyModel - .map((item) => item.competency) - .join(",")}` - ); - } - - // Check for the non-numeric competencyLevelNumber which is not a number in the csv data - const dataWithNonNumericCompetencyLevelNumber = data.filter((item) => { - const competencyLevelNumber = Number(item.competencyLevelNumber); - return ( - isNaN(competencyLevelNumber) || - competencyLevelNumber < 1 || - competencyLevelNumber > 7 - ); // Check if it's not a number and value should b/w 1 to 7 - }); - // Throw an error for the dataWithNonNumericCompetencyLevelNumber - if (dataWithNonNumericCompetencyLevelNumber.length > 0) { - throw new Error( - `The following competency level numbers are not numeric : ${dataWithNonNumericCompetencyLevelNumber - .map( - (item) => - `Competency ${item?.competency} with competency value #${ - item?.competencyLevelNumber || "Empty" - }.` - ) - .join(",")}` - ); - } - - // Filter for the empty question in the csv data - const dataEmptyQuestion = data.filter((item) => !item.question); - // Throw an error for the dataEmptyQuestion - if (dataEmptyQuestion.length > 0) { - throw new Error( - `The following questions are empty: ${dataEmptyQuestion - .map( - (item) => - `Competency ${item?.competency} with competency value#${item?.competencyLevelNumber}.` - ) - .join(",")}` - ); - } - - const createManyDataPromises: Promise[] = data.map(async (item) => { - const getCompetency = await this.competencyService.findCompetencyByName( - item.competency - ); - if (getCompetency && getCompetency.id) { - // Check if a question with the same competencyId exists in the database - const existingQuestion = await this.prisma.questionBank.findFirst({ - where: { - competencyId: getCompetency.id, - competencyLevelNumber: Number(item["competencyLevelNumber"]), - }, - }); - if (existingQuestion) { - // If it exists, update the existing question - await this.prisma.questionBank.update({ - where: { - id: existingQuestion.id, - }, - data: { - question: item["question"], - competencyLevelNumber: Number(item["competencyLevelNumber"]), - }, - }); - } else { - // If it doesn't exist, create a new entry - await this.questionBankService.createQuestionByCompentencyId({ - question: item["question"], - competencyId: getCompetency.id, - competencyLevelNumber: Number(item["competencyLevelNumber"]), - }); - } - } else { - throw new Error("Competency Id not found"); - } - }); - try { - await Promise.all(createManyDataPromises); - } catch (error) { - console.error("Error storing data in the database:", error); - throw new Error("Failed to store data in the database"); - } - } - + public async parseCSV(filePath: string): Promise { const results: any[] = []; diff --git a/src/question-bank/question-bank.module.ts b/src/question-bank/question-bank.module.ts index a4d475a..cbdac78 100644 --- a/src/question-bank/question-bank.module.ts +++ b/src/question-bank/question-bank.module.ts @@ -3,6 +3,8 @@ import { QuestionBankService } from './question-bank.service'; import { QuestionBankController } from './question-bank.controller'; import { PrismaModule } from 'src/prisma/prisma.module'; import { PrismaService } from 'src/prisma/prisma.service'; +import { MockCompetencyService } from 'src/mockModules/mock-competency/mock-competency.service'; +import { MockCompetencyLevelService } from 'src/mockModules/mock-competency-level/mock-competency-level.service'; @Module({ // Import the PrismaModule to make Prisma service available within this module @@ -12,7 +14,7 @@ import { PrismaService } from 'src/prisma/prisma.service'; controllers: [QuestionBankController], // Declare the QuestionBankService and PrismaService as providers for this module - providers: [QuestionBankService, PrismaService], + providers: [QuestionBankService, PrismaService, MockCompetencyService, MockCompetencyLevelService], // Export the QuestionBankService to make it available for other modules that import this module exports: [QuestionBankService], diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index 9b5bdcd..f562818 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -5,10 +5,15 @@ import { } from "./dto/create-question-bank.dto"; import { UpdateQuestionBankDto } from "./dto/update-question-bank.dto"; import { PrismaService } from "src/prisma/prisma.service"; +import { CreateFileUploadDto } from "src/file-upload/dto/create-file-upload.dto"; +import { MockCompetencyService } from "src/mockModules/mock-competency/mock-competency.service"; @Injectable() export class QuestionBankService { - constructor(private prisma: PrismaService) {} + constructor( + private prisma: PrismaService, + private competencyService: MockCompetencyService + ) {} async createQuestionByCompentencyId( createQuestionBankDto: CreateQuestionBankDto @@ -80,7 +85,7 @@ export class QuestionBankService { }); } - async deleteQuestionById(questionId : number) { + async deleteQuestionById(questionId: number) { // Check the question is there in db for the given questionId and competencyLevelId const findQuestion = await this.prisma.questionBank.findUnique({ where: { @@ -97,7 +102,110 @@ export class QuestionBankService { return this.prisma.questionBank.delete({ where: { id: questionId, + }, + }); + } + + public async bulkUploadQuestions(data: CreateFileUploadDto[]) { + // Check if the competency Existed or not the given csv data if not then filter and throw error + const allCompetencies = await this.competencyService.findAllCompetencies(); + + // Extract an array of competency names from the fetched competencies + const competencyNames = allCompetencies.map( + (competency) => competency.name + ); + + // check for the data that is not in the competency model + const dataNotInCompetencyModel = data.filter( + (item) => !competencyNames.includes(item["competency"]) + ); + + // If there is data not in the competency model, you can throw an error or handle it as needed. + if (dataNotInCompetencyModel.length > 0) { + throw new Error( + `Some data is not in the competency model #${dataNotInCompetencyModel + .map((item) => item.competency) + .join(",")}` + ); + } + + // Check for the non-numeric competencyLevelNumber which is not a number in the csv data + const dataWithNonNumericCompetencyLevelNumber = data.filter((item) => { + const competencyLevelNumber = Number(item.competencyLevelNumber); + return ( + isNaN(competencyLevelNumber) || + competencyLevelNumber < 1 || + competencyLevelNumber > 7 + ); // Check if it's not a number and value should b/w 1 to 7 + }); + // Throw an error for the dataWithNonNumericCompetencyLevelNumber + if (dataWithNonNumericCompetencyLevelNumber.length > 0) { + throw new Error( + `The following competency level numbers are not numeric : ${dataWithNonNumericCompetencyLevelNumber + .map( + (item) => + `Competency ${item?.competency} with competency value #${ + item?.competencyLevelNumber || "Empty" + }.` + ) + .join(",")}` + ); + } + + // Filter for the empty question in the csv data + const dataEmptyQuestion = data.filter((item) => !item.question); + // Throw an error for the dataEmptyQuestion + if (dataEmptyQuestion.length > 0) { + throw new Error( + `The following questions are empty: ${dataEmptyQuestion + .map( + (item) => + `Competency ${item?.competency} with competency value#${item?.competencyLevelNumber}.` + ) + .join(",")}` + ); + } + + const createManyDataPromises: Promise[] = data.map(async (item) => { + const getCompetency = await this.competencyService.findCompetencyByName( + item.competency + ); + if (getCompetency && getCompetency.id) { + // Check if a question with the same competencyId exists in the database + const existingQuestion = await this.prisma.questionBank.findFirst({ + where: { + competencyId: getCompetency.id, + competencyLevelNumber: Number(item["competencyLevelNumber"]), + }, + }); + if (existingQuestion) { + // If it exists, update the existing question + await this.prisma.questionBank.update({ + where: { + id: existingQuestion.id, + }, + data: { + question: item["question"], + competencyLevelNumber: Number(item["competencyLevelNumber"]), + }, + }); + } else { + // If it doesn't exist, create a new entry + await this.createQuestionByCompentencyId({ + question: item["question"], + competencyId: getCompetency.id, + competencyLevelNumber: Number(item["competencyLevelNumber"]), + }); + } + } else { + throw new Error("Competency Id not found"); } - }) + }); + try { + await Promise.all(createManyDataPromises); + } catch (error) { + console.error("Error storing data in the database:", error); + throw new Error("Failed to store data in the database"); + } } } From 4ec7eccb1975dd2e579b1f6dd7722481f90704a9 Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Mon, 30 Oct 2023 15:40:32 +0530 Subject: [PATCH 069/175] Refactor as per PR review --- src/file-upload/dto/create-file-upload.dto.ts | 18 ----- src/file-upload/dto/update-file-upload.dto.ts | 4 -- src/file-upload/file-upload.controller.ts | 68 ------------------- src/file-upload/file-upload.module.ts | 22 ------ src/file-upload/file-upload.service.ts | 51 -------------- .../dto/create-question-bank.dto.ts | 16 +++++ src/question-bank/question-bank.controller.ts | 46 ++++++++++++- src/question-bank/question-bank.service.ts | 39 ++++++++++- 8 files changed, 96 insertions(+), 168 deletions(-) delete mode 100644 src/file-upload/dto/create-file-upload.dto.ts delete mode 100644 src/file-upload/dto/update-file-upload.dto.ts delete mode 100644 src/file-upload/file-upload.controller.ts delete mode 100644 src/file-upload/file-upload.module.ts delete mode 100644 src/file-upload/file-upload.service.ts diff --git a/src/file-upload/dto/create-file-upload.dto.ts b/src/file-upload/dto/create-file-upload.dto.ts deleted file mode 100644 index 1078a7e..0000000 --- a/src/file-upload/dto/create-file-upload.dto.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { IsInt, IsNotEmpty, IsString } from "class-validator"; - -export class CreateFileUploadDto { - // Competency associated with Question bank csv file - @IsNotEmpty() - @IsString() - competency: string; - - // competencyLevel number of the uploaded question bank csv file - @IsNotEmpty() - @IsInt() - competencyLevelNumber: number; - - // Questions of the uploaded question bank csv file - @IsNotEmpty() - @IsString() - question: string; -} diff --git a/src/file-upload/dto/update-file-upload.dto.ts b/src/file-upload/dto/update-file-upload.dto.ts deleted file mode 100644 index 095d832..0000000 --- a/src/file-upload/dto/update-file-upload.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { PartialType } from '@nestjs/swagger'; -import { CreateFileUploadDto } from './create-file-upload.dto'; - -export class UpdateFileUploadDto extends PartialType(CreateFileUploadDto) {} diff --git a/src/file-upload/file-upload.controller.ts b/src/file-upload/file-upload.controller.ts deleted file mode 100644 index b2b98d2..0000000 --- a/src/file-upload/file-upload.controller.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { - Controller, - Post, - Logger, - HttpStatus, - UseInterceptors, - UploadedFile, - Res, -} from "@nestjs/common"; -import { FileUploadService } from "./file-upload.service"; -import { CreateFileUploadDto } from "./dto/create-file-upload.dto"; -import { UpdateFileUploadDto } from "./dto/update-file-upload.dto"; -import { - ApiBody, - ApiConsumes, - ApiOperation, - ApiResponse, - ApiTags, -} from "@nestjs/swagger"; -import { FileInterceptor } from "@nestjs/platform-express"; -import { multerOptions } from "src/config/multer-options.config"; -import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; - -@Controller("file-upload") -@ApiTags("file-upload") -export class FileUploadController { - // Create a logger instance for this controller to log events and errors. - private readonly logger = new Logger(FileUploadController.name); - - // The fileupload controller injecting the FileUpload service into it - constructor(private readonly fileUploadService: FileUploadService) {} - - @Post() - @UseInterceptors(FileInterceptor("file", multerOptions)) - @ApiConsumes("multipart/form-data") // Specify content type as multipart/form-data - @ApiBody({ - schema: { - type: 'object', - properties: { - file: { - type: 'string' || 'number', - format : 'binary' - } - } - } - }) - async uploadQuestions(@UploadedFile() file, @Res() res) { - // Log the initiaton for csv file upload - this.logger.log(`Initiate to upload a csv file.`); - try { - this.fileUploadService.uploadCsvFile(file.path); - this.logger.log(`Successfully uploaded question bank.`); - return res.status(HttpStatus.CREATED).json({ - message: "Question bank uploaded sucessfully.", - }); - } catch (error) { - this.logger.error(`Failed to upload question bank.`, error); - // get error message and status code - const { errorMessage, statusCode } = - getPrismaErrorStatusAndMessage(error); - // Return an error response - return res.status(statusCode).json({ - statusCode, - message: errorMessage || `Failed to upload question bank.`, - }); - } - } -} diff --git a/src/file-upload/file-upload.module.ts b/src/file-upload/file-upload.module.ts deleted file mode 100644 index 4c48f61..0000000 --- a/src/file-upload/file-upload.module.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Module } from "@nestjs/common"; -import { FileUploadService } from "./file-upload.service"; -import { FileUploadController } from "./file-upload.controller"; -import { PrismaModule } from "src/prisma/prisma.module"; -import { PrismaService } from "src/prisma/prisma.service"; -import { MockCompetencyService } from "src/mockModules/mock-competency/mock-competency.service"; -import { MockCompetencyLevelService } from "src/mockModules/mock-competency-level/mock-competency-level.service"; -import { QuestionBankService } from "src/question-bank/question-bank.service"; - -@Module({ - imports: [PrismaModule], - controllers: [FileUploadController], - providers: [ - FileUploadService, - PrismaService, - MockCompetencyService, - MockCompetencyLevelService, - QuestionBankService, - ], - exports: [FileUploadService], -}) -export class FileUploadModule {} diff --git a/src/file-upload/file-upload.service.ts b/src/file-upload/file-upload.service.ts deleted file mode 100644 index 772b207..0000000 --- a/src/file-upload/file-upload.service.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { CreateFileUploadDto } from "./dto/create-file-upload.dto"; -import { PrismaService } from "src/prisma/prisma.service"; -import * as fs from "fs"; -import csvParser = require("csv-parser"); -import { MockCompetencyService } from "src/mockModules/mock-competency/mock-competency.service"; -import { QuestionBankService } from "src/question-bank/question-bank.service"; -import path = require("path"); - -@Injectable() -export class FileUploadService { - constructor( - private prisma: PrismaService, - private competencyService: MockCompetencyService, - private questionBankService: QuestionBankService - ) {} - - public async uploadCsvFile(filepath) { - // Parsed the uploaded data - const parsedData = await this.parseCSV(filepath); - // Store the parsedData in the db - await this.questionBankService.bulkUploadQuestions(parsedData); - // Clean up after the sucessful upload of csv data - await this.deleteUploadedFile(filepath); - } - - public async parseCSV(filePath: string): Promise { - const results: any[] = []; - - return new Promise((resolve, reject) => { - fs.createReadStream(filePath) - .pipe(csvParser()) - .on("data", (row) => { - results.push(row); - }) - .on("end", () => { - resolve(results); - }) - .on("error", (error) => { - reject(error); - }); - }); - } - public async deleteUploadedFile(filePath: string): Promise { - try { - fs.unlinkSync(filePath) - } catch (error) { - throw new Error("Error unlinking the file.",); - } - } -} diff --git a/src/question-bank/dto/create-question-bank.dto.ts b/src/question-bank/dto/create-question-bank.dto.ts index ddd82b1..91a7c04 100644 --- a/src/question-bank/dto/create-question-bank.dto.ts +++ b/src/question-bank/dto/create-question-bank.dto.ts @@ -22,6 +22,22 @@ export class CreateQuestionBankDto { @IsInt() competencyLevelNumber: number; } +export class CreateFileUploadDto { + // Competency associated with Question bank csv file + @IsNotEmpty() + @IsString() + competency: string; + + // competencyLevel number of the uploaded question bank csv file + @IsNotEmpty() + @IsInt() + competencyLevelNumber: number; + + // Questions of the uploaded question bank csv file + @IsNotEmpty() + @IsString() + question: string; +} // Question bank filter export class QuestionBankFilterDto { diff --git a/src/question-bank/question-bank.controller.ts b/src/question-bank/question-bank.controller.ts index 764fc65..2f55e17 100644 --- a/src/question-bank/question-bank.controller.ts +++ b/src/question-bank/question-bank.controller.ts @@ -11,6 +11,8 @@ import { Res, Query, ParseIntPipe, + UseInterceptors, + UploadedFile, } from "@nestjs/common"; import { QuestionBankService } from "./question-bank.service"; import { @@ -18,9 +20,11 @@ import { QuestionBankFilterDto, } from "./dto/create-question-bank.dto"; import { UpdateQuestionBankDto } from "./dto/update-question-bank.dto"; -import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { ApiBody, ApiConsumes, ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { ResponseQuestionBankDto } from "./dto/response-question-bank.dto"; import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; +import { FileInterceptor } from "@nestjs/platform-express"; +import { multerOptions } from "src/config/multer-options.config"; @Controller("question-bank") @ApiTags("question-bank") @@ -161,8 +165,9 @@ export class QuestionBankController { try { this.logger.log(`Initiating deleting of a question with an id ${id}`); - const deletedQuestion = - await this.questionBankService.deleteQuestionById(id); + const deletedQuestion = await this.questionBankService.deleteQuestionById( + id + ); return res.status(HttpStatus.OK).json({ message: `Successfully deleted question for id #${id}`, @@ -180,4 +185,39 @@ export class QuestionBankController { }); } } + @Post("upload") + @UseInterceptors(FileInterceptor("file", multerOptions)) + @ApiConsumes("multipart/form-data") // Specify content type as multipart/form-data + @ApiBody({ + schema: { + type: "object", + properties: { + file: { + type: "string" || "number", + format: "binary", + }, + }, + }, + }) + async uploadQuestions(@UploadedFile() file, @Res() res) { + // Log the initiaton for csv file upload + this.logger.log(`Initiate to upload a csv file.`); + try { + this.questionBankService.uploadCsvFile(file.path); + this.logger.log(`Successfully uploaded question bank.`); + return res.status(HttpStatus.CREATED).json({ + message: "Question bank uploaded sucessfully.", + }); + } catch (error) { + this.logger.error(`Failed to upload question bank.`, error); + // get error message and status code + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + // Return an error response + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed to upload question bank.`, + }); + } + } } diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index f562818..8011d7a 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -1,13 +1,14 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { + CreateFileUploadDto, CreateQuestionBankDto, QuestionBankFilterDto, } from "./dto/create-question-bank.dto"; import { UpdateQuestionBankDto } from "./dto/update-question-bank.dto"; import { PrismaService } from "src/prisma/prisma.service"; -import { CreateFileUploadDto } from "src/file-upload/dto/create-file-upload.dto"; import { MockCompetencyService } from "src/mockModules/mock-competency/mock-competency.service"; - +import * as fs from "fs"; +import csvParser = require("csv-parser"); @Injectable() export class QuestionBankService { constructor( @@ -106,6 +107,15 @@ export class QuestionBankService { }); } + public async uploadCsvFile(filepath) { + // Parsed the uploaded data + const parsedData = await this.parseCSV(filepath); + // Store the parsedData in the db + await this.bulkUploadQuestions(parsedData); + // Clean up after the sucessful upload of csv data + await this.deleteUploadedFile(filepath); + } + public async bulkUploadQuestions(data: CreateFileUploadDto[]) { // Check if the competency Existed or not the given csv data if not then filter and throw error const allCompetencies = await this.competencyService.findAllCompetencies(); @@ -208,4 +218,29 @@ export class QuestionBankService { throw new Error("Failed to store data in the database"); } } + + public async parseCSV(filePath: string): Promise { + const results: any[] = []; + + return new Promise((resolve, reject) => { + fs.createReadStream(filePath) + .pipe(csvParser()) + .on("data", (row) => { + results.push(row); + }) + .on("end", () => { + resolve(results); + }) + .on("error", (error) => { + reject(error); + }); + }); + } + public async deleteUploadedFile(filePath: string): Promise { + try { + fs.unlinkSync(filePath); + } catch (error) { + throw new Error("Error unlinking the file."); + } + } } From a06ef68ff131b86950a42c258ede66fa7b0edfc8 Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Tue, 31 Oct 2023 10:42:49 +0530 Subject: [PATCH 070/175] Revert and added back the file upload module --- src/file-upload/dto/create-file-upload.dto.ts | 1 + src/file-upload/dto/update-file-upload.dto.ts | 4 ++++ src/file-upload/file-upload.controller.ts | 10 ++++++++++ src/file-upload/file-upload.module.ts | 9 +++++++++ src/file-upload/file-upload.service.ts | 8 ++++++++ 5 files changed, 32 insertions(+) create mode 100644 src/file-upload/dto/create-file-upload.dto.ts create mode 100644 src/file-upload/dto/update-file-upload.dto.ts create mode 100644 src/file-upload/file-upload.controller.ts create mode 100644 src/file-upload/file-upload.module.ts create mode 100644 src/file-upload/file-upload.service.ts diff --git a/src/file-upload/dto/create-file-upload.dto.ts b/src/file-upload/dto/create-file-upload.dto.ts new file mode 100644 index 0000000..54acc2e --- /dev/null +++ b/src/file-upload/dto/create-file-upload.dto.ts @@ -0,0 +1 @@ +export class CreateFileUploadDto {} diff --git a/src/file-upload/dto/update-file-upload.dto.ts b/src/file-upload/dto/update-file-upload.dto.ts new file mode 100644 index 0000000..095d832 --- /dev/null +++ b/src/file-upload/dto/update-file-upload.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/swagger'; +import { CreateFileUploadDto } from './create-file-upload.dto'; + +export class UpdateFileUploadDto extends PartialType(CreateFileUploadDto) {} diff --git a/src/file-upload/file-upload.controller.ts b/src/file-upload/file-upload.controller.ts new file mode 100644 index 0000000..7452562 --- /dev/null +++ b/src/file-upload/file-upload.controller.ts @@ -0,0 +1,10 @@ +import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common'; +import { FileUploadService } from './file-upload.service'; +import { CreateFileUploadDto } from './dto/create-file-upload.dto'; +import { UpdateFileUploadDto } from './dto/update-file-upload.dto'; + +@Controller('file-upload') +export class FileUploadController { + constructor(private readonly fileUploadService: FileUploadService) {} + +} diff --git a/src/file-upload/file-upload.module.ts b/src/file-upload/file-upload.module.ts new file mode 100644 index 0000000..05f67f7 --- /dev/null +++ b/src/file-upload/file-upload.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { FileUploadService } from './file-upload.service'; +import { FileUploadController } from './file-upload.controller'; + +@Module({ + controllers: [FileUploadController], + providers: [FileUploadService] +}) +export class FileUploadModule {} diff --git a/src/file-upload/file-upload.service.ts b/src/file-upload/file-upload.service.ts new file mode 100644 index 0000000..5280391 --- /dev/null +++ b/src/file-upload/file-upload.service.ts @@ -0,0 +1,8 @@ +import { Injectable } from '@nestjs/common'; +import { CreateFileUploadDto } from './dto/create-file-upload.dto'; +import { UpdateFileUploadDto } from './dto/update-file-upload.dto'; + +@Injectable() +export class FileUploadService { + +} From 820d10fe347a452206e5ea38bad1ad7350e1e6a4 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Tue, 31 Oct 2023 10:44:41 +0530 Subject: [PATCH 071/175] API updates needed for the CRON Service. --- prisma/schema.prisma | 2 + .../admin-department.service.ts | 22 ++++++++++- .../dto/create-admin-department.dto.ts | 6 ++- .../dto/response-admin-department.dto.ts | 3 ++ src/question-bank/question-bank.service.ts | 4 +- .../interfaces/response-tracker.interface.ts | 11 ++++++ .../response-tracker.service.ts | 38 +++++++++++++++++++ .../dto/response-survey-config.dto.ts | 1 + src/survey-config/survey-config.controller.ts | 2 +- src/survey-config/survey-config.service.ts | 2 +- .../dto/create-survey-cycle-parameter.dto.ts | 18 +++++++++ .../survey-cycle-parameter.controller.ts | 8 ++-- .../survey-cycle-parameter.service.ts | 20 ++++++++-- src/survey-form/survey-form.service.ts | 12 ++++++ src/survey-score/survey-score.module.ts | 10 +++++ 15 files changed, 147 insertions(+), 12 deletions(-) create mode 100644 src/response-tracker/interfaces/response-tracker.interface.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 0202e73..f2d218a 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -63,6 +63,7 @@ model AdminDepartment { name String description String? UserMetadata UserMetadata[] + SurveyConfig SurveyConfig? @@id([id, departmentId]) @@map("admin_department") @@ -76,6 +77,7 @@ model SurveyConfig { startTime DateTime endTime DateTime departmentId Int @unique + AdminDepartment AdminDepartment @relation(fields: [departmentId], references: [departmentId]) SurveyCycleParameters SurveyCycleParameter[] @@map("survey_config") diff --git a/src/admin-department/admin-department.service.ts b/src/admin-department/admin-department.service.ts index 2b6ac97..48510e7 100644 --- a/src/admin-department/admin-department.service.ts +++ b/src/admin-department/admin-department.service.ts @@ -54,15 +54,33 @@ export class AdminDepartmentService { } public async getAllAdminDepartment(filter: FilterAdminDepartmentsDto) { - const { departmentId, name, limit = 10, offset = 0 } = filter; + const { departmentId, name, limit = 10, offset = 0, surveyConfig } = filter; // Get all the admin department and filter using department Id and name. - return this.prisma.adminDepartment.findMany({ + return await this.prisma.adminDepartment.findMany({ where: { departmentId: departmentId ?? undefined, // Optional departmentId filter name: name ?? undefined, // Optional filter by Name }, skip: offset, take: limit, + select: { + id: true, + departmentId: true, + name: true, + description: true, + SurveyConfig: + surveyConfig == true + ? { + select: { + id: true, + startTime: true, + endTime: true, + onboardingTime: true, + onboardingTimeUnit: true, + }, + } + : {}, + }, }); } } diff --git a/src/admin-department/dto/create-admin-department.dto.ts b/src/admin-department/dto/create-admin-department.dto.ts index d18c330..aec4383 100644 --- a/src/admin-department/dto/create-admin-department.dto.ts +++ b/src/admin-department/dto/create-admin-department.dto.ts @@ -1,4 +1,4 @@ -import { IsInt, IsOptional, IsString } from "class-validator"; +import { IsBoolean, IsInt, IsOptional, IsString } from "class-validator"; // Admin department filter dto export class FilterAdminDepartmentsDto { @@ -21,4 +21,8 @@ export class FilterAdminDepartmentsDto { @IsOptional() @IsInt() offset?: number = 0; + + @IsOptional() + @IsBoolean() + surveyConfig?: boolean } diff --git a/src/admin-department/dto/response-admin-department.dto.ts b/src/admin-department/dto/response-admin-department.dto.ts index 9ad5997..38ecadd 100644 --- a/src/admin-department/dto/response-admin-department.dto.ts +++ b/src/admin-department/dto/response-admin-department.dto.ts @@ -1,6 +1,9 @@ +import { ResponseSurveyConfigDto } from "../../survey-config/dto/response-survey-config.dto"; + export class ResponseAdminDepartmentDto { readonly id: number; readonly name: string; readonly departmentId: number; readonly description?: string; + readonly surveyConfig?: ResponseSurveyConfigDto } diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index 0fe020a..7598e23 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -174,7 +174,9 @@ export class QuestionBankService { throw new NotFoundException(`No Question Found for user with #${userId}`); } return questionLists; - public async getQuestionById(id: number) { + } + + public async getQuestionById(id: number) { const question = await this.prisma.questionBank.findUnique({ where: { id }, }); diff --git a/src/response-tracker/interfaces/response-tracker.interface.ts b/src/response-tracker/interfaces/response-tracker.interface.ts new file mode 100644 index 0000000..f85cb9a --- /dev/null +++ b/src/response-tracker/interfaces/response-tracker.interface.ts @@ -0,0 +1,11 @@ +import { ResponseTrackerStatusEnum } from "@prisma/client"; + +export interface IResponseTracker { + id: number; + status: ResponseTrackerStatusEnum; + Assessor: { + userId: string; + userName: string; + }; +} + diff --git a/src/response-tracker/response-tracker.service.ts b/src/response-tracker/response-tracker.service.ts index 10ebbe4..2bf6c61 100644 --- a/src/response-tracker/response-tracker.service.ts +++ b/src/response-tracker/response-tracker.service.ts @@ -2,6 +2,7 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; import { CreateResponseTrackerDto } from "./dto/create-response-tracker.dto"; import { UpdateResponseTrackerDto } from "./dto/update-response-tracker.dto"; +import { IResponseTracker } from "./interfaces/response-tracker.interface"; @Injectable() export class ResponseTrackerService { @@ -68,6 +69,43 @@ export class ResponseTrackerService { return response; } + public async getResponsesActiveSurveyByUserId(userId: string) { + const surveyForms = await this.prisma.surveyForm.findMany({ + where: { + surveyCycleParameter: { + isActive: true, + }, + userId, + ResponseTracker:{ + every:{ + assesseeId: userId + } + } + }, + orderBy: { + createdAt: "desc", + }, + select: { + ResponseTracker:{ + select:{ + id: true, + status: true, + Assessor:{ + select:{ + userId: true, + userName: true + } + }, + }, + }, + }, + }); + const responses: IResponseTracker[] = surveyForms.flatMap( + (form) => form.ResponseTracker + ); + return responses; + } + public async update( id: number, updateResponseTrackerDto: UpdateResponseTrackerDto diff --git a/src/survey-config/dto/response-survey-config.dto.ts b/src/survey-config/dto/response-survey-config.dto.ts index f574cce..b5c0aed 100644 --- a/src/survey-config/dto/response-survey-config.dto.ts +++ b/src/survey-config/dto/response-survey-config.dto.ts @@ -8,5 +8,6 @@ export class ResponseSurveyConfigDto { readonly onboardingTimeUnit: TimeUnitsEnum; readonly startTime: Date; readonly endTime: Date; + readonly isActive: boolean; readonly SurveyCycleParameters?: ResponseSurveyCycleParameterDto[]; } diff --git a/src/survey-config/survey-config.controller.ts b/src/survey-config/survey-config.controller.ts index 31b1abc..f66bbc3 100644 --- a/src/survey-config/survey-config.controller.ts +++ b/src/survey-config/survey-config.controller.ts @@ -20,7 +20,7 @@ import { import { UpdateSurveyConfigDto } from "./dto/update-survey-config.dto"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { ResponseSurveyConfigDto } from "./dto/response-survey-config.dto"; -import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; +import { getPrismaErrorStatusAndMessage } from "../utils/utils"; @Controller("survey-config") @ApiTags("survey-config") diff --git a/src/survey-config/survey-config.service.ts b/src/survey-config/survey-config.service.ts index c6ad3db..3bb350e 100644 --- a/src/survey-config/survey-config.service.ts +++ b/src/survey-config/survey-config.service.ts @@ -4,7 +4,7 @@ import { SurveyConfigFilterDto, } from "./dto/create-survey-config.dto"; import { UpdateSurveyConfigDto } from "./dto/update-survey-config.dto"; -import { PrismaService } from "src/prisma/prisma.service"; +import { PrismaService } from "../prisma/prisma.service"; @Injectable() export class SurveyConfigService { diff --git a/src/survey-cycle-parameter/dto/create-survey-cycle-parameter.dto.ts b/src/survey-cycle-parameter/dto/create-survey-cycle-parameter.dto.ts index f930612..67d858a 100644 --- a/src/survey-cycle-parameter/dto/create-survey-cycle-parameter.dto.ts +++ b/src/survey-cycle-parameter/dto/create-survey-cycle-parameter.dto.ts @@ -3,6 +3,7 @@ import { IsDate, IsInt, IsNotEmpty, + IsNumber, IsOptional, } from "class-validator"; @@ -34,3 +35,20 @@ export class CreateSurveyCycleParameterDto { @IsDate() updatedAt?: Date; } + +export class SurveyCycleParameterFilterDto { + @IsNumber() + @IsOptional() + @IsNotEmpty() + departmentId?: number; + + @IsNumber() + @IsOptional() + @IsNotEmpty() + surveyConfigId?: number; + + @IsBoolean() + @IsOptional() + @IsNotEmpty() + isActive? : boolean; +} diff --git a/src/survey-cycle-parameter/survey-cycle-parameter.controller.ts b/src/survey-cycle-parameter/survey-cycle-parameter.controller.ts index c9928fd..b3ca9c6 100644 --- a/src/survey-cycle-parameter/survey-cycle-parameter.controller.ts +++ b/src/survey-cycle-parameter/survey-cycle-parameter.controller.ts @@ -8,12 +8,13 @@ import { ParseIntPipe, Patch, Post, + Query, Res, } from "@nestjs/common"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; import { ResponseSurveyCycleParameterDto } from "./dto/response-survey-cycle-parameter.dto"; -import { CreateSurveyCycleParameterDto } from "./dto/create-survey-cycle-parameter.dto"; +import { CreateSurveyCycleParameterDto, SurveyCycleParameterFilterDto } from "./dto/create-survey-cycle-parameter.dto"; import { UpdateSurveyCycleParameterDto } from "./dto/update-survey-cycle-parameter.dto"; import { SurveyCycleParameterService } from "./survey-cycle-parameter.service"; @@ -76,12 +77,13 @@ export class SurveyCycleParameterController { isArray: true, }) // Api response for swagger async getAllSurveyParameter( - @Res() res + @Res() res, + @Query() filter: SurveyCycleParameterFilterDto ): Promise { try { this.logger.log(`Initiated fetching all the survey cycle parameter.`); const surveyParameters = - await this.surveyParameterService.getAllSurveyParameter(); + await this.surveyParameterService.getAllSurveyParameter(filter); if (!surveyParameters.length) throw Error("No record found"); this.logger.log(`Fetched successfully all the survey cycle parameter.`); diff --git a/src/survey-cycle-parameter/survey-cycle-parameter.service.ts b/src/survey-cycle-parameter/survey-cycle-parameter.service.ts index 9ea2d85..333f4ac 100644 --- a/src/survey-cycle-parameter/survey-cycle-parameter.service.ts +++ b/src/survey-cycle-parameter/survey-cycle-parameter.service.ts @@ -4,7 +4,7 @@ import { NotFoundException, } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; -import { CreateSurveyCycleParameterDto } from "./dto/create-survey-cycle-parameter.dto"; +import { CreateSurveyCycleParameterDto, SurveyCycleParameterFilterDto } from "./dto/create-survey-cycle-parameter.dto"; import { UpdateSurveyCycleParameterDto } from "./dto/update-survey-cycle-parameter.dto"; @Injectable() @@ -32,8 +32,22 @@ export class SurveyCycleParameterService { return newSurveyParameter; } - async getAllSurveyParameter() { - return this.prisma.surveyCycleParameter.findMany(); + async getAllSurveyParameter(filter: SurveyCycleParameterFilterDto) { + const {departmentId, surveyConfigId, isActive} = filter; + return this.prisma.surveyCycleParameter.findMany({ + where:{ + isActive: isActive ?? undefined, + SurveyConfig:{ + id: surveyConfigId ?? undefined, + AdminDepartment:{ + id: departmentId ??undefined + } + } + }, + orderBy: { + createdAt: "desc", + } + }); } async updateSurveyParameterById( diff --git a/src/survey-form/survey-form.service.ts b/src/survey-form/survey-form.service.ts index dae3e0b..640db86 100644 --- a/src/survey-form/survey-form.service.ts +++ b/src/survey-form/survey-form.service.ts @@ -36,6 +36,18 @@ export class SurveyFormService { }); } + async findSurveyFormBySurveyCycleParameterId(paramId: number){ + const surveyForms = await this.prisma.surveyForm.findMany({ + where:{ + surveyCycleParameterId: paramId + } + }); + if(!surveyForms || surveyForms.length==0){ + throw new NotFoundException(`Survey Forms not found for SurveyCycleParameter with id #${paramId}`); + } + return surveyForms; + } + async updateSurveyFormScore(id: number, overallScore: number) { return await this.prisma.surveyForm.update({ where: { id }, diff --git a/src/survey-score/survey-score.module.ts b/src/survey-score/survey-score.module.ts index ac421df..95d55be 100644 --- a/src/survey-score/survey-score.module.ts +++ b/src/survey-score/survey-score.module.ts @@ -5,6 +5,11 @@ import { SurveyFormService } from "src/survey-form/survey-form.service"; import { UserMetadataModule } from "src/user-metadata/user-metadata.module"; import { SurveyScoreController } from "./survey-score.controller"; import { SurveyScoreService } from "./survey-score.service"; +import { MockUserService } from "../mockModules/mock-user/mock-user.service"; +import { MockDesignationService } from "../mockModules/mock-designation/mock-designation.service"; +import { MockRoleService } from "../mockModules/mock-role/mock-role.service"; +import { MockCompetencyService } from "../mockModules/mock-competency/mock-competency.service"; +import { MockCompetencyLevelService } from "../mockModules/mock-competency-level/mock-competency-level.service"; @Module({ imports: [forwardRef(() => UserMetadataModule)], @@ -14,6 +19,11 @@ import { SurveyScoreService } from "./survey-score.service"; ResponseTrackerService, QuestionBankService, SurveyFormService, + MockUserService, + MockDesignationService, + MockRoleService, + MockCompetencyService, + MockCompetencyLevelService ], }) export class SurveyScoreModule {} From eb3b0bb562058d79a60f351c7ed1076bbc027440 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Tue, 31 Oct 2023 10:45:17 +0530 Subject: [PATCH 072/175] Core Survey APIs and CRON Service. --- package-lock.json | 55 +++++- package.json | 1 + src/app.module.ts | 6 + src/scheduled-tasks/scheduled-tasks.module.ts | 39 +++++ .../scheduled-tasks.service.ts | 164 ++++++++++++++++++ src/survey/survey.controller.spec.ts | 18 ++ src/survey/survey.controller.ts | 66 +++++++ src/survey/survey.module.ts | 25 +++ src/survey/survey.service.spec.ts | 18 ++ src/survey/survey.service.ts | 123 +++++++++++++ 10 files changed, 512 insertions(+), 3 deletions(-) create mode 100644 src/scheduled-tasks/scheduled-tasks.module.ts create mode 100644 src/scheduled-tasks/scheduled-tasks.service.ts create mode 100644 src/survey/survey.controller.spec.ts create mode 100644 src/survey/survey.controller.ts create mode 100644 src/survey/survey.module.ts create mode 100644 src/survey/survey.service.spec.ts create mode 100644 src/survey/survey.service.ts diff --git a/package-lock.json b/package-lock.json index a2a62da..59959bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@nestjs/config": "^3.1.1", "@nestjs/core": "^9.0.0", "@nestjs/platform-express": "^9.0.0", + "@nestjs/schedule": "^3.0.4", "@nestjs/swagger": "^7.1.11", "@prisma/client": "^5.4.2", "class-transformer": "^0.5.1", @@ -734,9 +735,9 @@ } }, "node_modules/@babel/traverse": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.0.tgz", - "integrity": "sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.22.13", @@ -2280,6 +2281,20 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" }, + "node_modules/@nestjs/schedule": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-3.0.4.tgz", + "integrity": "sha512-uFJpuZsXfpvgx2y7/KrIZW9e1L68TLiwRodZ6+Gc8xqQiHSUzAVn+9F4YMxWFlHITZvvkjWziUFgRNCitDcTZQ==", + "dependencies": { + "cron": "2.4.3", + "uuid": "9.0.1" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0", + "reflect-metadata": "^0.1.12" + } + }, "node_modules/@nestjs/schematics": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.2.0.tgz", @@ -2735,6 +2750,11 @@ "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, + "node_modules/@types/luxon": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.3.tgz", + "integrity": "sha512-/BJF3NT0pRMuxrenr42emRUF67sXwcZCd+S1ksG/Fcf9O7C3kKCY4uJSbKBE4KDUIYr3WMsvfmWD8hRjXExBJQ==" + }, "node_modules/@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", @@ -4314,6 +4334,15 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "node_modules/cron": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/cron/-/cron-2.4.3.tgz", + "integrity": "sha512-YBvExkQYF7w0PxyeFLRyr817YVDhGxaCi5/uRRMqa4aWD3IFKRd+uNbpW1VWMdqQy8PZ7CElc+accXJcauPKzQ==", + "dependencies": { + "@types/luxon": "~3.3.0", + "luxon": "~3.3.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -8018,6 +8047,14 @@ "yallist": "^3.0.2" } }, + "node_modules/luxon": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz", + "integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==", + "engines": { + "node": ">=12" + } + }, "node_modules/macos-release": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", @@ -10245,6 +10282,18 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", diff --git a/package.json b/package.json index 6b10e50..c3235fc 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "@nestjs/config": "^3.1.1", "@nestjs/core": "^9.0.0", "@nestjs/platform-express": "^9.0.0", + "@nestjs/schedule": "^3.0.4", "@nestjs/swagger": "^7.1.11", "@prisma/client": "^5.4.2", "class-transformer": "^0.5.1", diff --git a/src/app.module.ts b/src/app.module.ts index 13071f3..785b25c 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -14,6 +14,9 @@ import { SurveyCycleParameterModule } from "./survey-cycle-parameter/survey-cycl import { AdminDepartmentModule } from './admin-department/admin-department.module'; import { UserMetadataModule } from './user-metadata/user-metadata.module'; import { AdminCompetencyModule } from "./admin-competency/admin-competency.module"; +import { ScheduledTasksModule } from './scheduled-tasks/scheduled-tasks.module'; +import { ScheduleModule } from "@nestjs/schedule"; +import { SurveyModule } from './survey/survey.module'; @Module({ imports: [ @@ -31,6 +34,9 @@ import { AdminCompetencyModule } from "./admin-competency/admin-competency.modul AdminDepartmentModule, UserMetadataModule, AdminCompetencyModule, + ScheduledTasksModule, + ScheduleModule.forRoot(), + SurveyModule ], controllers: [AppController], providers: [AppService, PrismaService], diff --git a/src/scheduled-tasks/scheduled-tasks.module.ts b/src/scheduled-tasks/scheduled-tasks.module.ts new file mode 100644 index 0000000..fa769cd --- /dev/null +++ b/src/scheduled-tasks/scheduled-tasks.module.ts @@ -0,0 +1,39 @@ +import { Module } from "@nestjs/common"; +import { ScheduledTasksService } from "./scheduled-tasks.service"; +import { SurveyService } from "../survey/survey.service"; +import { UserMetadataService } from "../user-metadata/user-metadata.service"; +import { SurveyConfigService } from "../survey-config/survey-config.service"; +import { SurveyFormService } from "../survey-form/survey-form.service"; +import { MockUserService } from "../mockModules/mock-user/mock-user.service"; +import { SurveyCycleParameterService } from "../survey-cycle-parameter/survey-cycle-parameter.service"; +import { SurveyScoreService } from "../survey-score/survey-score.service"; +import { ResponseTrackerService } from "../response-tracker/response-tracker.service"; +import { QuestionBankService } from "../question-bank/question-bank.service"; +import { MockDesignationService } from "../mockModules/mock-designation/mock-designation.service"; +import { MockRoleService } from "../mockModules/mock-role/mock-role.service"; +import { MockCompetencyService } from "../mockModules/mock-competency/mock-competency.service"; +import { MockCompetencyLevelService } from "../mockModules/mock-competency-level/mock-competency-level.service"; +import { AdminDepartmentService } from "../admin-department/admin-department.service"; +import { MockDepartmentService } from "../mockModules/mock-department/mock-department.service"; + +@Module({ + providers: [ + ScheduledTasksService, + SurveyFormService, + MockUserService, + SurveyConfigService, + AdminDepartmentService, + SurveyService, + UserMetadataService, + SurveyCycleParameterService, + SurveyScoreService, + ResponseTrackerService, + QuestionBankService, + MockDepartmentService, + MockDesignationService, + MockRoleService, + MockCompetencyService, + MockCompetencyLevelService + ], +}) +export class ScheduledTasksModule {} diff --git a/src/scheduled-tasks/scheduled-tasks.service.ts b/src/scheduled-tasks/scheduled-tasks.service.ts new file mode 100644 index 0000000..257d9f9 --- /dev/null +++ b/src/scheduled-tasks/scheduled-tasks.service.ts @@ -0,0 +1,164 @@ +import { Injectable, Logger, NotFoundException } from "@nestjs/common"; +import { Cron, CronExpression } from "@nestjs/schedule"; +import { + AdminDepartment, + SurveyConfig, + SurveyStatusEnum, +} from "@prisma/client"; +import { SurveyConfigService } from "../survey-config/survey-config.service"; +import { SurveyCycleParameterService } from "../survey-cycle-parameter/survey-cycle-parameter.service"; +import { SurveyFormService } from "../survey-form/survey-form.service"; +import { SurveyScoreService } from "../survey-score/survey-score.service"; +import { SurveyService } from "../survey/survey.service"; +import { UserMetadataService } from "../user-metadata/user-metadata.service"; +import { AdminDepartmentService } from "../admin-department/admin-department.service"; + +@Injectable() +export class ScheduledTasksService { + constructor( + private adminDepartmentService: AdminDepartmentService, + private surveyConfigService: SurveyConfigService, + private surveyParamsService: SurveyCycleParameterService, + private surveyService: SurveyService, + private userMetadataService: UserMetadataService, + private surveyFormService: SurveyFormService, + private surveyScoreService: SurveyScoreService + ) {} + private readonly logger = new Logger(ScheduledTasksService.name); + + @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT) + async surveyCron() { + this.logger.log( + `Start CRON Job for Surveys. Fetching data of all departments.` + ); + + const departments = await this.adminDepartmentService.getAllAdminDepartment({}); + + this.logger.log( + `Fetched data of all departments. Cycling through every department to start or end surveys.` + ); + + for (const department of departments) { + const surveyConfig = await this.surveyConfigService.getAllSurveyConfig({ + departmentId: department.id, + }); + + const surveyConfigParam = await this.surveyParamsService.getAllSurveyParameter({ + surveyConfigId: surveyConfig[0].id, + }); + + this.logger.log( + `Checking if a survey is Active for the department "${department.name}" with id: "${department.departmentId}".` + ); + + if (surveyConfigParam && surveyConfigParam[0].isActive == true) { + + this.logger.log(`A survey is ACTIVE for department "${department.name}"`); + + if (this.isDayBeforeToday(surveyConfigParam[0].endTime)) { + this.logger.log( + `Survey will end tomorrow. Sending remainder notification to all survey participants.` + ); + //send notifications + } else if (this.isToday(surveyConfigParam[0].endTime)) { + + this.logger.log(`Fetching all SurveyForms related to the survey.`); + const surveyForms = + await this.surveyFormService.findSurveyFormBySurveyCycleParameterId( + surveyConfigParam[0].id + ); + + this.logger.log(`Calculation scores for all "${surveyForms.length}" SurveyForms related to the survey.`); + for (const surveyForm of surveyForms) { + //calculate score for each form + this.logger.log(`Calculation score for SurveyForms with id: "${surveyForm.id}" for user with id: "${surveyForm.userId}".`); + await this.surveyScoreService.calculateSurveyScoreBySurveyFormId( + surveyForm.id + ); + + //update the survey form as "CLOSED" + this.logger.log(`Calculated the score and updating the surveyFrom's status to "CLOSED."`); + await this.surveyFormService.updateSurveyFormStatus( + surveyForm.id, + SurveyStatusEnum.CLOSED + ); + this.logger.log(`Updated the surveyFrom's status to "CLOSED."`); + } + + this.logger.log(`Deactivating the Survey as it will end today.`); + await this.surveyParamsService.updateSurveyParameterById( + surveyConfigParam[0].id, + { isActive: false } + ); + this.logger.log(`Deactivated the Survey with for the department "${department.name}" with id: "${department.departmentId}.`); + + } + } else { + await this.startSurvey(department, surveyConfig[0]); + } + } + } + + async startSurvey(department: AdminDepartment, surveyConfig: SurveyConfig) { + this.logger.log(`No survey is ACTIVE for department "${department.name}"`); + if (this.isDayBeforeToday(surveyConfig.startTime)) { + //update department data + this.logger.log(`The survey starts tomorrow. Updating department data.`); + const updateDepartment = + await this.adminDepartmentService.createOrUpdateAdminDepartment( + department.id + ); + this.logger.log(`Updated department's data.`); + + //update user data + this.logger.log(`Fetching users that belong to "${department.name}" department.`); + const updateUsers = await this.userMetadataService.findManyUserMetadata({ + departmentId: department.id, + }); + this.logger.log(`Updating all "${updateUsers.length}" user's data that belongs to the department "${department.name}".`); + for (const user of updateUsers) { + await this.userMetadataService.createOrUpdateUserMetadata(user.userId); + } + this.logger.log(`Updated all "${updateUsers.length}" user's data that belongs to the department "${department.name}".`); + } else if (this.isToday(surveyConfig.startTime)) { + this.logger.log(`The survey starts today.`); + const surveyConfigParam = + await this.surveyParamsService.getAllSurveyParameter({ + surveyConfigId: surveyConfig.id, + }); + + this.logger.log(`Activating the Survey with for the department "${department.name}" with id: "${department.departmentId}.`); + if (surveyConfigParam) { + await this.surveyParamsService.updateSurveyParameterById( + surveyConfigParam[0].id, + { isActive: false } + ); + } + + this.logger.log(`Generating SurveyForms for every user that belongs to the department "${department.name}".`); + await this.surveyService.generateSurveyFormsForDepartment(department.id); + + // send notifications for all survey participant + this.logger.log(`Sending notification to every user that belongs to the department "${department.name}".`); + } + } + + isDayBeforeToday(date) { + const today = new Date(); + today.setHours(0, 0, 0, 0); + + const yesterday = new Date(date); + yesterday.setDate(yesterday.getDate() - 1); + + return yesterday.getTime() === today.getTime(); + } + + isToday(date) { + const today = new Date(); + today.setHours(0, 0, 0, 0); + + const givenDate = new Date(date); + + return givenDate.getTime() === today.getTime(); + } +} diff --git a/src/survey/survey.controller.spec.ts b/src/survey/survey.controller.spec.ts new file mode 100644 index 0000000..f8fea70 --- /dev/null +++ b/src/survey/survey.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { SurveyController } from './survey.controller'; + +describe('SurveyController', () => { + let controller: SurveyController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [SurveyController], + }).compile(); + + controller = module.get(SurveyController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/survey/survey.controller.ts b/src/survey/survey.controller.ts new file mode 100644 index 0000000..56e8380 --- /dev/null +++ b/src/survey/survey.controller.ts @@ -0,0 +1,66 @@ +import { Body, Controller, Get, HttpStatus, Logger, Param, ParseUUIDPipe, Post, Res } from '@nestjs/common'; +import { SurveyService } from './survey.service'; +import { ApiOperation, ApiResponse } from '@nestjs/swagger'; +import { getPrismaErrorStatusAndMessage } from 'src/utils/utils'; + +@Controller('survey') +export class SurveyController { + private readonly logger = new Logger(SurveyController.name); + constructor(private readonly surveyService: SurveyService) {} + + @Post() + @ApiOperation({ summary: "Create Survey-Forms for a department." }) + @ApiResponse({ status: HttpStatus.CREATED }) + async generateSurveyFormsForDepartment( + @Res() res, + @Param() departmentId: number, + ){ + try { + this.logger.log(`Initiated creating new survey forms for a department.`); + + const surveyForms = await this.surveyService.generateSurveyFormsForDepartment(departmentId); + + this.logger.log(`Successfully created new survey forms for a department.`); + + return res.status(HttpStatus.CREATED).json({ + data: surveyForms, + message: `SurveyForms created successfully for depatrment with id #${departmentId}`, + }); + } catch (error) { + this.logger.error(`Failed to create new survey forms for depatrment with id #${departmentId}`, error); + + const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); + return res + .status(statusCode) + .json({ message: errorMessage || `Could not create survey forms for depatrment with id #${departmentId}` }); + } + } + + @Get() + @ApiOperation({ summary: "Get number of surveys to be filled by User." }) + @ApiResponse({ status: HttpStatus.CREATED, type: "number" }) + async getSurveysToBeFilledByUser( + @Res() res, + @Param("userId", ParseUUIDPipe) userId: string + ){ + try { + this.logger.log(`Getting number of surveys to be filled by user with id #${userId}.`); + + const surveyForms = await this.surveyService.getSurveysToBeFilledByUser(userId); + + this.logger.log(`Successfully fetched the number of surveys to be filled by user with id #${userId}.`); + + return res.status(HttpStatus.CREATED).json({ + data: surveyForms, + message: `Successfully fetched the number of surveys to be filled by user with id #${userId}.`, + }); + } catch (error) { + this.logger.error(`Failed to fetch the number of surveys to be filled by user with id #${userId}.`, error); + + const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); + return res + .status(statusCode) + .json({ message: errorMessage || `Could not fetch the number of surveys to be filled by user with id #${userId}.` }); + } + } +} diff --git a/src/survey/survey.module.ts b/src/survey/survey.module.ts new file mode 100644 index 0000000..0450dce --- /dev/null +++ b/src/survey/survey.module.ts @@ -0,0 +1,25 @@ +import { Module } from "@nestjs/common"; +import { SurveyService } from "./survey.service"; +import { SurveyController } from "./survey.controller"; +import { SurveyFormService } from "src/survey-form/survey-form.service"; +import { QuestionBankService } from "../question-bank/question-bank.service"; +import { MockUserService } from "../mockModules/mock-user/mock-user.service"; +import { MockDesignationService } from "../mockModules/mock-designation/mock-designation.service"; +import { MockRoleService } from "../mockModules/mock-role/mock-role.service"; +import { MockCompetencyService } from "../mockModules/mock-competency/mock-competency.service"; +import { MockCompetencyLevelService } from "../mockModules/mock-competency-level/mock-competency-level.service"; + +@Module({ + providers: [ + SurveyService, + SurveyFormService, + QuestionBankService, + MockUserService, + MockDesignationService, + MockRoleService, + MockCompetencyService, + MockCompetencyLevelService + ], + controllers: [SurveyController], +}) +export class SurveyModule {} diff --git a/src/survey/survey.service.spec.ts b/src/survey/survey.service.spec.ts new file mode 100644 index 0000000..9cd098f --- /dev/null +++ b/src/survey/survey.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { SurveyService } from './survey.service'; + +describe('SurveyService', () => { + let service: SurveyService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [SurveyService], + }).compile(); + + service = module.get(SurveyService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/survey/survey.service.ts b/src/survey/survey.service.ts new file mode 100644 index 0000000..26f2948 --- /dev/null +++ b/src/survey/survey.service.ts @@ -0,0 +1,123 @@ +import { Injectable, NotFoundException } from "@nestjs/common"; +import { ResponseTrackerStatusEnum, SurveyStatusEnum } from "@prisma/client"; +import { PrismaService } from "src/prisma/prisma.service"; +import { + CreateSurveyFormDto, + ResponseSurveyFormDto, +} from "src/survey-form/dto"; +import { SurveyFormService } from "src/survey-form/survey-form.service"; +import { QuestionBankService } from "../question-bank/question-bank.service"; + +@Injectable() +export class SurveyService { + constructor( + private prisma: PrismaService, + private surveyForm: SurveyFormService, + private questionBank: QuestionBankService + ) {} + + async getSurveysToBeFilledByUser(userId: string) { + const responseCount = await this.prisma.surveyForm.findFirst({ + where: { + userId, + surveyCycleParameter: { + isActive: true, + }, + ResponseTracker: { + every: { + assessorId: userId, + status: ResponseTrackerStatusEnum.PENDING, + }, + }, + }, + orderBy: { + createdAt: "desc", + }, + select: { + ResponseTracker: { + select: { + id: true, + status: true, + }, + }, + }, + }); + if (!responseCount) { + return 0; + } + return responseCount.ResponseTracker.length; + } + + async getLatestSurveyResponsesForUserId(userId: string) { + const responses = await this.prisma.surveyForm.findFirst({ + where: { + userId, + surveyCycleParameter: { + isActive: true, + }, + }, + orderBy: { + createdAt: "desc", + }, + select: { + ResponseTracker: { + select: { + id: true, + surveyFormId: true, + assesseeId: true, + assessorId: true, + responseJson: true, + status: true, + }, + }, + }, + }); + if (!responses) { + throw new NotFoundException( + `For the user with id #${userId} either no responses found or no survey is active for their department.` + ); + } + return responses; + } + + async generateSurveyFormsForDepartment(departmentId: number) { + let surveyForms: ResponseSurveyFormDto[] = []; + const users = await this.prisma.userMetadata.findMany({ + where: { + departmentId, + }, + }); + for (const user of users) { + //get questions for the user according to their designation + const questions = await this.questionBank.getAllQuestionsForUser(user.userId); + //create the CreateSurveyFormDto for the user + const surveyFormDto: CreateSurveyFormDto = { + userId: user.userId, + surveyCycleParameterId: 1, + status: SurveyStatusEnum.PUBLISHED, + questionsJson: questions, + }; + const surveyForm = await this.surveyForm.createSurveyForm(surveyFormDto); + surveyForms.push(surveyForm); + for (const assessor of users) { + if (user.userId !== assessor.userId) { + await this.prisma.responseTracker.create({ + data: { + surveyFormId: surveyForm.id, + assesseeId: user.userId, + assessorId: assessor.userId, + status: ResponseTrackerStatusEnum.PENDING, + }, + }); + } + } + } + return surveyForms; + } +} + + + +// ScheduledTasksModule, +// ScheduleModule.forRoot(), +// SurveyModule \ No newline at end of file From 725dc6db2ae3f9e6c521f8dd222286735f03c6ae Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Tue, 31 Oct 2023 11:20:19 +0530 Subject: [PATCH 073/175] Optimizing SurveyScore module. --- src/survey-score/survey-score.module.ts | 20 ++++---------------- src/survey-score/survey-score.service.ts | 1 - 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/survey-score/survey-score.module.ts b/src/survey-score/survey-score.module.ts index 95d55be..dd185a6 100644 --- a/src/survey-score/survey-score.module.ts +++ b/src/survey-score/survey-score.module.ts @@ -1,29 +1,17 @@ -import { Module, forwardRef } from "@nestjs/common"; -import { QuestionBankService } from "src/question-bank/question-bank.service"; +import { Module } from "@nestjs/common"; import { ResponseTrackerService } from "src/response-tracker/response-tracker.service"; import { SurveyFormService } from "src/survey-form/survey-form.service"; -import { UserMetadataModule } from "src/user-metadata/user-metadata.module"; +import { QuestionBankModule } from "../question-bank/question-bank.module"; import { SurveyScoreController } from "./survey-score.controller"; import { SurveyScoreService } from "./survey-score.service"; -import { MockUserService } from "../mockModules/mock-user/mock-user.service"; -import { MockDesignationService } from "../mockModules/mock-designation/mock-designation.service"; -import { MockRoleService } from "../mockModules/mock-role/mock-role.service"; -import { MockCompetencyService } from "../mockModules/mock-competency/mock-competency.service"; -import { MockCompetencyLevelService } from "../mockModules/mock-competency-level/mock-competency-level.service"; @Module({ - imports: [forwardRef(() => UserMetadataModule)], + imports: [QuestionBankModule], controllers: [SurveyScoreController], providers: [ SurveyScoreService, ResponseTrackerService, - QuestionBankService, - SurveyFormService, - MockUserService, - MockDesignationService, - MockRoleService, - MockCompetencyService, - MockCompetencyLevelService + SurveyFormService ], }) export class SurveyScoreModule {} diff --git a/src/survey-score/survey-score.service.ts b/src/survey-score/survey-score.service.ts index b5878aa..7aeb5c5 100644 --- a/src/survey-score/survey-score.service.ts +++ b/src/survey-score/survey-score.service.ts @@ -21,7 +21,6 @@ export class SurveyScoreService { private responseTrackerService: ResponseTrackerService, private questionBankService: QuestionBankService, private surveyFormService: SurveyFormService, - private userMetadataService: UserMetadataService ) {} public async create(createSurveyScoreDto: CreateSurveyScoreDto) { From d88761cd2473f40e0d20fc9a2adec5e62d07809d Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Tue, 31 Oct 2023 11:26:23 +0530 Subject: [PATCH 074/175] Removeing unnecessary comments. --- src/survey/survey.service.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/survey/survey.service.ts b/src/survey/survey.service.ts index 26f2948..d1c9923 100644 --- a/src/survey/survey.service.ts +++ b/src/survey/survey.service.ts @@ -115,9 +115,3 @@ export class SurveyService { return surveyForms; } } - - - -// ScheduledTasksModule, -// ScheduleModule.forRoot(), -// SurveyModule \ No newline at end of file From 26b90f156d532223862b91343e96e247661296ec Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Tue, 31 Oct 2023 11:38:59 +0530 Subject: [PATCH 075/175] Refactor and imported services from file upload module --- src/file-upload/file-upload.service.ts | 30 ++++++++++++++++-- src/question-bank/question-bank.module.ts | 2 ++ src/question-bank/question-bank.service.ts | 37 ++++------------------ 3 files changed, 36 insertions(+), 33 deletions(-) diff --git a/src/file-upload/file-upload.service.ts b/src/file-upload/file-upload.service.ts index 5280391..502e38c 100644 --- a/src/file-upload/file-upload.service.ts +++ b/src/file-upload/file-upload.service.ts @@ -1,8 +1,32 @@ import { Injectable } from '@nestjs/common'; -import { CreateFileUploadDto } from './dto/create-file-upload.dto'; -import { UpdateFileUploadDto } from './dto/update-file-upload.dto'; +import * as fs from "fs"; +import csvParser = require("csv-parser"); @Injectable() export class FileUploadService { - + public async parseCSV(filePath: string): Promise { + const results: any[] = []; + + return new Promise((resolve, reject) => { + fs.createReadStream(filePath) + .pipe(csvParser()) + .on("data", (row) => { + results.push(row); + }) + .on("end", () => { + resolve(results); + }) + .on("error", (error) => { + reject(error); + }); + }); + } + + public async deleteUploadedFile(filePath: string): Promise { + try { + fs.unlinkSync(filePath); + } catch (error) { + throw new Error("Error unlinking the file."); + } + } } diff --git a/src/question-bank/question-bank.module.ts b/src/question-bank/question-bank.module.ts index 815aa5d..e12479e 100644 --- a/src/question-bank/question-bank.module.ts +++ b/src/question-bank/question-bank.module.ts @@ -8,6 +8,7 @@ import { MockDesignationService } from 'src/mockModules/mock-designation/mock-de import { MockRoleService } from 'src/mockModules/mock-role/mock-role.service'; import { MockCompetencyService } from 'src/mockModules/mock-competency/mock-competency.service'; import { MockCompetencyLevelService } from 'src/mockModules/mock-competency-level/mock-competency-level.service'; +import { FileUploadService } from 'src/file-upload/file-upload.service'; @Module({ // Import the PrismaModule to make Prisma service available within this module @@ -25,6 +26,7 @@ import { MockCompetencyLevelService } from 'src/mockModules/mock-competency-leve MockUserService, MockDesignationService, MockRoleService, + FileUploadService ], // Export the QuestionBankService to make it available for other modules that import this module diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index b288594..4cdf41d 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -7,20 +7,20 @@ import { import { UpdateQuestionBankDto } from "./dto/update-question-bank.dto"; import { PrismaService } from "src/prisma/prisma.service"; import { MockCompetencyService } from "src/mockModules/mock-competency/mock-competency.service"; -import * as fs from "fs"; -import csvParser = require("csv-parser"); import { MockUserService } from "src/mockModules/mock-user/mock-user.service"; import { MockRoleService } from "./../mockModules/mock-role/mock-role.service"; import { MockDesignationService } from "src/mockModules/mock-designation/mock-designation.service"; +import { FileUploadService } from "src/file-upload/file-upload.service"; @Injectable() export class QuestionBankService { constructor( private prisma: PrismaService, - private competencyService: MockCompetencyService + private competencyService: MockCompetencyService, private mockUserService: MockUserService, private mockDesignationService: MockDesignationService, - private mockRoleService: MockRoleService + private mockRoleService: MockRoleService, + private fileUploadService: FileUploadService ) {} async createQuestionByCompentencyId( @@ -116,11 +116,11 @@ export class QuestionBankService { public async uploadCsvFile(filepath) { // Parsed the uploaded data - const parsedData = await this.parseCSV(filepath); + const parsedData = await this.fileUploadService.parseCSV(filepath); // Store the parsedData in the db await this.bulkUploadQuestions(parsedData); // Clean up after the sucessful upload of csv data - await this.deleteUploadedFile(filepath); + await this.fileUploadService.deleteUploadedFile(filepath); } public async bulkUploadQuestions(data: CreateFileUploadDto[]) { @@ -226,30 +226,7 @@ export class QuestionBankService { } } - public async parseCSV(filePath: string): Promise { - const results: any[] = []; - - return new Promise((resolve, reject) => { - fs.createReadStream(filePath) - .pipe(csvParser()) - .on("data", (row) => { - results.push(row); - }) - .on("end", () => { - resolve(results); - }) - .on("error", (error) => { - reject(error); - }); - }); - } - public async deleteUploadedFile(filePath: string): Promise { - try { - fs.unlinkSync(filePath); - } catch (error) { - throw new Error("Error unlinking the file."); - } - } + async getAllQuestionsForUser(userId: string) { const user = await this.mockUserService.findOne(userId); From ef4434c605587a972644ffca6511c983ac4cd499 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Tue, 31 Oct 2023 14:58:35 +0530 Subject: [PATCH 076/175] Fix for dependancy errors after resolving merge conflicts. --- src/scheduled-tasks/scheduled-tasks.module.ts | 30 +++++-------------- src/survey/survey.module.ts | 19 ++++-------- 2 files changed, 13 insertions(+), 36 deletions(-) diff --git a/src/scheduled-tasks/scheduled-tasks.module.ts b/src/scheduled-tasks/scheduled-tasks.module.ts index fa769cd..71cc008 100644 --- a/src/scheduled-tasks/scheduled-tasks.module.ts +++ b/src/scheduled-tasks/scheduled-tasks.module.ts @@ -1,39 +1,25 @@ import { Module } from "@nestjs/common"; -import { ScheduledTasksService } from "./scheduled-tasks.service"; -import { SurveyService } from "../survey/survey.service"; -import { UserMetadataService } from "../user-metadata/user-metadata.service"; +import { AdminDepartmentModule } from "../admin-department/admin-department.module"; +import { QuestionBankModule } from "../question-bank/question-bank.module"; +import { ResponseTrackerService } from "../response-tracker/response-tracker.service"; import { SurveyConfigService } from "../survey-config/survey-config.service"; -import { SurveyFormService } from "../survey-form/survey-form.service"; -import { MockUserService } from "../mockModules/mock-user/mock-user.service"; import { SurveyCycleParameterService } from "../survey-cycle-parameter/survey-cycle-parameter.service"; +import { SurveyFormService } from "../survey-form/survey-form.service"; import { SurveyScoreService } from "../survey-score/survey-score.service"; -import { ResponseTrackerService } from "../response-tracker/response-tracker.service"; -import { QuestionBankService } from "../question-bank/question-bank.service"; -import { MockDesignationService } from "../mockModules/mock-designation/mock-designation.service"; -import { MockRoleService } from "../mockModules/mock-role/mock-role.service"; -import { MockCompetencyService } from "../mockModules/mock-competency/mock-competency.service"; -import { MockCompetencyLevelService } from "../mockModules/mock-competency-level/mock-competency-level.service"; -import { AdminDepartmentService } from "../admin-department/admin-department.service"; -import { MockDepartmentService } from "../mockModules/mock-department/mock-department.service"; +import { SurveyService } from "../survey/survey.service"; +import { UserMetadataModule } from "../user-metadata/user-metadata.module"; +import { ScheduledTasksService } from "./scheduled-tasks.service"; @Module({ + imports: [QuestionBankModule, UserMetadataModule, AdminDepartmentModule], providers: [ ScheduledTasksService, SurveyFormService, - MockUserService, SurveyConfigService, - AdminDepartmentService, SurveyService, - UserMetadataService, SurveyCycleParameterService, SurveyScoreService, ResponseTrackerService, - QuestionBankService, - MockDepartmentService, - MockDesignationService, - MockRoleService, - MockCompetencyService, - MockCompetencyLevelService ], }) export class ScheduledTasksModule {} diff --git a/src/survey/survey.module.ts b/src/survey/survey.module.ts index 0450dce..c24e579 100644 --- a/src/survey/survey.module.ts +++ b/src/survey/survey.module.ts @@ -1,24 +1,15 @@ import { Module } from "@nestjs/common"; -import { SurveyService } from "./survey.service"; -import { SurveyController } from "./survey.controller"; import { SurveyFormService } from "src/survey-form/survey-form.service"; -import { QuestionBankService } from "../question-bank/question-bank.service"; -import { MockUserService } from "../mockModules/mock-user/mock-user.service"; -import { MockDesignationService } from "../mockModules/mock-designation/mock-designation.service"; -import { MockRoleService } from "../mockModules/mock-role/mock-role.service"; -import { MockCompetencyService } from "../mockModules/mock-competency/mock-competency.service"; -import { MockCompetencyLevelService } from "../mockModules/mock-competency-level/mock-competency-level.service"; +import { QuestionBankModule } from "../question-bank/question-bank.module"; +import { SurveyFormModule } from "../survey-form/survey-form.module"; +import { SurveyController } from "./survey.controller"; +import { SurveyService } from "./survey.service"; @Module({ + imports:[QuestionBankModule, SurveyFormModule], providers: [ SurveyService, SurveyFormService, - QuestionBankService, - MockUserService, - MockDesignationService, - MockRoleService, - MockCompetencyService, - MockCompetencyLevelService ], controllers: [SurveyController], }) From 62a6a11ffe451267fc2d44665c39a9b17a2f2938 Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Thu, 2 Nov 2023 11:24:57 +0530 Subject: [PATCH 077/175] updated --- .../response-tracker.controller.ts | 80 +++++++++---------- .../response-tracker.service.ts | 32 ++++---- src/survey-form/dto/update-survey-form.dto.ts | 15 +++- src/survey-form/survey-form.controller.ts | 7 +- src/survey-score/survey-score.service.ts | 20 ++++- 5 files changed, 91 insertions(+), 63 deletions(-) diff --git a/src/response-tracker/response-tracker.controller.ts b/src/response-tracker/response-tracker.controller.ts index 9f87b56..32db1c4 100644 --- a/src/response-tracker/response-tracker.controller.ts +++ b/src/response-tracker/response-tracker.controller.ts @@ -31,14 +31,14 @@ export class ResponseTrackerController { ) {} @Post() - @ApiOperation({ summary: "create new response tracer" }) + @ApiOperation({ summary: "create new response tracker" }) @ApiResponse({ status: HttpStatus.CREATED, type: ResponseTrackerDtoResponse }) async create( @Res() res, @Body() createResponseTrackerDto: CreateResponseTrackerDto ): Promise { try { - this.logger.log(`Initiated creating response tracer`); + this.logger.log(`Initiated creating response tracker`); const response = await this.responseTrackerService.create( createResponseTrackerDto @@ -46,69 +46,69 @@ export class ResponseTrackerController { return res.status(HttpStatus.CREATED).json({ data: response, - message: "Successfully created response tracer", + message: "Successfully created response tracker", }); } catch (error) { - this.logger.error(`Failed to create response tracer`, error); + this.logger.error(`Failed to create response tracker`, error); const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); return res.status(statusCode).json({ statusCode, - message: errorMessage || `Failed to create response tracer`, + message: errorMessage || `Failed to create response tracker`, }); } } @Get() - @ApiOperation({ summary: "get all response tracers" }) + @ApiOperation({ summary: "get all response trackers" }) @ApiResponse({ status: HttpStatus.OK, type: ResponseTrackerDtoMultipleResponse, }) async findAll(@Res() res): Promise { try { - this.logger.log(`Initiated fetching all response tracers`); + this.logger.log(`Initiated fetching all response trackers`); const responses = await this.responseTrackerService.findAll(); return res.status(HttpStatus.OK).json({ data: responses, - message: `Successfully fetched all response tracers`, + message: `Successfully fetched all response trackers`, }); } catch (error) { - this.logger.error(`Failed to fetch all response tracers`, error); + this.logger.error(`Failed to fetch all response trackers`, error); const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); return res.status(statusCode).json({ statusCode, - message: errorMessage || `Failed to fetch all response tracers`, + message: errorMessage || `Failed to fetch all response trackers`, }); } } @Get(":id") - @ApiOperation({ summary: "get response tracer by id" }) + @ApiOperation({ summary: "get response tracker by id" }) @ApiResponse({ status: HttpStatus.OK, type: ResponseTrackerDtoResponse }) async findOne( @Res() res, @Param("id", ParseIntPipe) id: number ): Promise { try { - this.logger.log(`Initiated fetching response tracer with id#${id}`); + this.logger.log(`Initiated fetching response tracker with id#${id}`); const response = await this.responseTrackerService.findOne(id); return res.status(HttpStatus.OK).json({ data: response, - message: `Successfully fetched response tracer for id #${id}`, + message: `Successfully fetched response tracker for id #${id}`, }); } catch (error) { this.logger.error( - `Failed to fetch response tracer with id #${id}`, + `Failed to fetch response tracker with id #${id}`, error ); @@ -118,13 +118,13 @@ export class ResponseTrackerController { return res.status(statusCode).json({ statusCode, message: - errorMessage || `Failed to fetch response tracer for id #${id}`, + errorMessage || `Failed to fetch response tracker for id #${id}`, }); } } @Get("survey-form/:surveyFormId") - @ApiOperation({ summary: "get response tracers by surveyFormId" }) + @ApiOperation({ summary: "get response trackers by surveyFormId" }) @ApiResponse({ status: HttpStatus.OK, type: ResponseTrackerDtoMultipleResponse, @@ -135,7 +135,7 @@ export class ResponseTrackerController { ): Promise { try { this.logger.log( - `Initiated fetching response tracer with surveyFormId#${surveyFormId}` + `Initiated fetching response tracker with surveyFormId#${surveyFormId}` ); const response = await this.responseTrackerService.findBySurveyFormId( @@ -144,11 +144,11 @@ export class ResponseTrackerController { return res.status(HttpStatus.OK).json({ data: response, - message: `Successfully fetched response tracer for surveyFormId #${surveyFormId}`, + message: `Successfully fetched response tracker for surveyFormId #${surveyFormId}`, }); } catch (error) { this.logger.error( - `Failed to fetch response tracer with surveyFormId #${surveyFormId}`, + `Failed to fetch response tracker with surveyFormId #${surveyFormId}`, error ); @@ -159,13 +159,13 @@ export class ResponseTrackerController { statusCode, message: errorMessage || - `Failed to fetch response tracer for surveyFormId #${surveyFormId}`, + `Failed to fetch response tracker for surveyFormId #${surveyFormId}`, }); } } @Get("assessor/:assessorId") - @ApiOperation({ summary: "get response tracer by assessorId" }) + @ApiOperation({ summary: "get response tracker by assessorId" }) @ApiResponse({ status: HttpStatus.OK, type: ResponseTrackerDtoMultipleResponse, @@ -176,7 +176,7 @@ export class ResponseTrackerController { ): Promise { try { this.logger.log( - `Initiated fetching response tracer with assessorId#${assessorId}` + `Initiated fetching response tracker with assessorId#${assessorId}` ); const response = await this.responseTrackerService.findByAssessorId( @@ -185,11 +185,11 @@ export class ResponseTrackerController { return res.status(HttpStatus.OK).json({ data: response, - message: `Successfully fetched response tracer for assessorId #${assessorId}`, + message: `Successfully fetched response tracker for assessorId #${assessorId}`, }); } catch (error) { this.logger.error( - `Failed to fetch response tracer with assessorId #${assessorId}`, + `Failed to fetch response tracker with assessorId #${assessorId}`, error ); @@ -200,13 +200,13 @@ export class ResponseTrackerController { statusCode, message: errorMessage || - `Failed to fetch response tracer for assessorId #${assessorId}`, + `Failed to fetch response tracker for assessorId #${assessorId}`, }); } } @Get("assessee/:assesseeId") - @ApiOperation({ summary: "get response tracer by assesseeId" }) + @ApiOperation({ summary: "get response tracker by assesseeId" }) @ApiResponse({ status: HttpStatus.OK, type: ResponseTrackerDtoMultipleResponse, @@ -217,7 +217,7 @@ export class ResponseTrackerController { ): Promise { try { this.logger.log( - `Initiated fetching response tracer with assesseeId#${assesseeId}` + `Initiated fetching response tracker with assesseeId#${assesseeId}` ); const response = await this.responseTrackerService.findByAssesseeId( @@ -226,11 +226,11 @@ export class ResponseTrackerController { return res.status(HttpStatus.OK).json({ data: response, - message: `Successfully fetched response tracer for assesseeId #${assesseeId}`, + message: `Successfully fetched response tracker for assesseeId #${assesseeId}`, }); } catch (error) { this.logger.error( - `Failed to fetch response tracer with assesseeId #${assesseeId}`, + `Failed to fetch response tracker with assesseeId #${assesseeId}`, error ); @@ -241,13 +241,13 @@ export class ResponseTrackerController { statusCode, message: errorMessage || - `Failed to fetch response tracer for assesseeId #${assesseeId}`, + `Failed to fetch response tracker for assesseeId #${assesseeId}`, }); } } @Patch(":id") - @ApiOperation({ summary: "update response tracer by id" }) + @ApiOperation({ summary: "update response tracker by id" }) @ApiResponse({ status: HttpStatus.OK, type: ResponseTrackerDtoResponse }) async update( @Res() res, @@ -255,7 +255,7 @@ export class ResponseTrackerController { @Body() updateResponseTrackerDto: UpdateResponseTrackerDto ): Promise { try { - this.logger.log(`Initiated updating response tracer with id #${id}`); + this.logger.log(`Initiated updating response tracker with id #${id}`); const updatedResponse = await this.responseTrackerService.update( id, @@ -264,11 +264,11 @@ export class ResponseTrackerController { return res.status(HttpStatus.OK).json({ data: updatedResponse, - message: `Successfully updated response tracer with id ${id}`, + message: `Successfully updated response tracker with id ${id}`, }); } catch (error) { this.logger.error( - `Failed to update response tracer with id #${id}`, + `Failed to update response tracker with id #${id}`, error ); @@ -278,29 +278,29 @@ export class ResponseTrackerController { return res.status(statusCode).json({ statusCode, message: - errorMessage || `Failed to update response tracer with id #${id}`, + errorMessage || `Failed to update response tracker with id #${id}`, }); } } @Delete(":id") - @ApiOperation({ summary: "delete response tracer by id" }) + @ApiOperation({ summary: "delete response tracker by id" }) @ApiResponse({ status: HttpStatus.OK, type: ResponseTrackerDtoResponse }) async remove( @Res() res, @Param("id", ParseIntPipe) id: number ): Promise { try { - this.logger.log(`Initiated deleting response tracer with id #${id}`); + this.logger.log(`Initiated deleting response tracker with id #${id}`); await this.responseTrackerService.remove(id); return res.status(HttpStatus.OK).json({ - message: `Successfully deleted response tracer with id ${id}`, + message: `Successfully deleted response tracker with id ${id}`, }); } catch (error) { this.logger.error( - `Failed to delete response tracer with id #${id}`, + `Failed to delete response tracker with id #${id}`, error ); @@ -310,7 +310,7 @@ export class ResponseTrackerController { return res.status(statusCode).json({ statusCode, message: - errorMessage || `Failed to delete response tracer with id #${id}`, + errorMessage || `Failed to delete response tracker with id #${id}`, }); } } diff --git a/src/response-tracker/response-tracker.service.ts b/src/response-tracker/response-tracker.service.ts index 2bf6c61..962d7da 100644 --- a/src/response-tracker/response-tracker.service.ts +++ b/src/response-tracker/response-tracker.service.ts @@ -29,7 +29,7 @@ export class ResponseTrackerService { where: { id }, }); if (!response) - throw new NotFoundException(`Response tracer with id #${id} not found`); + throw new NotFoundException(`Response tracker with id #${id} not found`); return response; } @@ -40,7 +40,7 @@ export class ResponseTrackerService { if (!response || response.length === 0) throw new NotFoundException( - `Response tracer with surveyFormId #${surveyFormId} not found` + `Response tracker with surveyFormId #${surveyFormId} not found` ); return response; } @@ -52,7 +52,7 @@ export class ResponseTrackerService { if (!response || response.length === 0) throw new NotFoundException( - `Response tracer with assessorId #${assessorId} not found` + `Response tracker with assessorId #${assessorId} not found` ); return response; } @@ -64,7 +64,7 @@ export class ResponseTrackerService { if (!response || response.length === 0) throw new NotFoundException( - `Response tracer with assesseeId #${assesseeId} not found` + `Response tracker with assesseeId #${assesseeId} not found` ); return response; } @@ -76,25 +76,25 @@ export class ResponseTrackerService { isActive: true, }, userId, - ResponseTracker:{ - every:{ - assesseeId: userId - } - } + ResponseTracker: { + every: { + assesseeId: userId, + }, + }, }, orderBy: { createdAt: "desc", }, select: { - ResponseTracker:{ - select:{ + ResponseTracker: { + select: { id: true, status: true, - Assessor:{ - select:{ + Assessor: { + select: { userId: true, - userName: true - } + userName: true, + }, }, }, }, @@ -103,7 +103,7 @@ export class ResponseTrackerService { const responses: IResponseTracker[] = surveyForms.flatMap( (form) => form.ResponseTracker ); - return responses; + return responses; } public async update( diff --git a/src/survey-form/dto/update-survey-form.dto.ts b/src/survey-form/dto/update-survey-form.dto.ts index 8b26905..7ed34d5 100644 --- a/src/survey-form/dto/update-survey-form.dto.ts +++ b/src/survey-form/dto/update-survey-form.dto.ts @@ -1,6 +1,12 @@ import { ApiProperty } from "@nestjs/swagger"; import { SurveyStatusEnum } from "@prisma/client"; -import { IsEnum, IsNotEmpty, IsNumber, IsOptional, IsString } from "class-validator"; +import { + IsEnum, + IsNotEmpty, + IsNumber, + IsOptional, + IsString, +} from "class-validator"; export class UpdateSurveyFormDto { @ApiProperty({ enum: SurveyStatusEnum, example: "CREATED" }) @@ -18,3 +24,10 @@ export class UpdateSurveyFormDto { @IsNotEmpty() sunbirdCredentialIds: string; } + +export class UpdateSurveyFormStatusDto { + @ApiProperty({ enum: SurveyStatusEnum, example: "CREATED" }) + @IsEnum(SurveyStatusEnum) + @IsNotEmpty() + status: SurveyStatusEnum; +} diff --git a/src/survey-form/survey-form.controller.ts b/src/survey-form/survey-form.controller.ts index 05b573f..559dcac 100644 --- a/src/survey-form/survey-form.controller.ts +++ b/src/survey-form/survey-form.controller.ts @@ -16,6 +16,7 @@ import { CreateSurveyFormDto, SurveyFormResponse, UpdateSurveyFormDto, + UpdateSurveyFormStatusDto, } from "./dto"; import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; @@ -85,20 +86,20 @@ export class SurveyFormController { } } - @Patch(":id") + @Patch("status/:id") @ApiOperation({ summary: "Update the Survey-Form" }) @ApiResponse({ status: HttpStatus.OK, type: SurveyFormResponse }) async updateSurveyFormStatus( @Res() res, @Param("id") id: number, - @Body() { status }: UpdateSurveyFormDto + @Body() updateSurveyFormDto: UpdateSurveyFormStatusDto ): Promise { try { this.logger.log(`Initiated updating new survey form`); const surveyForm = await this.surveyFormService.updateSurveyFormStatus( id, - status + updateSurveyFormDto.status ); this.logger.log(`Successfully updated the survey form`); diff --git a/src/survey-score/survey-score.service.ts b/src/survey-score/survey-score.service.ts index 7aeb5c5..301fffd 100644 --- a/src/survey-score/survey-score.service.ts +++ b/src/survey-score/survey-score.service.ts @@ -1,5 +1,9 @@ -import { Injectable, NotFoundException } from "@nestjs/common"; -import { ResponseTrackerStatusEnum } from "@prisma/client"; +import { + BadRequestException, + Injectable, + NotFoundException, +} from "@nestjs/common"; +import { ResponseTrackerStatusEnum, SurveyStatusEnum } from "@prisma/client"; import { PrismaService } from "src/prisma/prisma.service"; import { QuestionBankService } from "src/question-bank/question-bank.service"; import { responseObject } from "src/response-tracker/dto"; @@ -20,7 +24,7 @@ export class SurveyScoreService { private prisma: PrismaService, private responseTrackerService: ResponseTrackerService, private questionBankService: QuestionBankService, - private surveyFormService: SurveyFormService, + private surveyFormService: SurveyFormService ) {} public async create(createSurveyScoreDto: CreateSurveyScoreDto) { @@ -69,6 +73,16 @@ export class SurveyScoreService { surveyFormId: number ): Promise { try { + const surveyForm = await this.surveyFormService.findSurveyFormById( + surveyFormId + ); + + if (surveyForm && surveyForm.status !== SurveyStatusEnum.CLOSED) { + throw new BadRequestException( + `Status for Survey form with id #${surveyFormId} is not ${SurveyStatusEnum.CLOSED}.` + ); + } + const completedSurveyResponses = await this.responseTrackerService.getAllResponseJsonBySurveyFormId( surveyFormId, From 06228477f959f4614ceda9555a68fe9e667c90a1 Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Thu, 2 Nov 2023 17:02:15 +0530 Subject: [PATCH 078/175] User metadata, admin department and admin competency data sync APIs --- package-lock.json | 8 +++ package.json | 2 + prisma/schema.prisma | 2 +- .../admin-competency.controller.ts | 24 +++---- .../admin-competency.module.ts | 11 ++-- .../admin-competency.service.ts | 65 +++++++++++++++---- .../admin-department.controller.ts | 34 ++++++++++ .../admin-department.module.ts | 17 +++-- .../admin-department.service.ts | 36 +++++++--- .../mock-competency/mock-competency.module.ts | 11 ++-- .../mock-competency.service.ts | 29 +++++---- .../dto/user-metadata-response.dto.ts | 22 ++++--- src/user-metadata/user-metadata.controller.ts | 33 +++++++++- src/user-metadata/user-metadata.module.ts | 2 + src/user-metadata/user-metadata.service.ts | 64 ++++++++++++++---- src/utils/utils.ts | 17 +++-- 16 files changed, 284 insertions(+), 93 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6f4e568..15726c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "csv-parser": "^3.0.0", + "lodash": "^4.17.21", "multer": "^1.4.5-lts.1", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", @@ -30,6 +31,7 @@ "@nestjs/testing": "^9.0.0", "@types/express": "^4.17.13", "@types/jest": "28.1.8", + "@types/lodash": "^4.14.200", "@types/multer": "^1.4.9", "@types/node": "^16.0.0", "@types/supertest": "^2.0.11", @@ -2734,6 +2736,12 @@ "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, + "node_modules/@types/lodash": { + "version": "4.14.200", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.200.tgz", + "integrity": "sha512-YI/M/4HRImtNf3pJgbF+W6FrXovqj+T+/HpENLTooK9PnkacBsDpeP3IpHab40CClUfhNmdM2WTNP2sa2dni5Q==", + "dev": true + }, "node_modules/@types/luxon": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.3.tgz", diff --git a/package.json b/package.json index e72c2e8..be4ca74 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "csv-parser": "^3.0.0", + "lodash": "^4.17.21", "multer": "^1.4.5-lts.1", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", @@ -48,6 +49,7 @@ "@nestjs/testing": "^9.0.0", "@types/express": "^4.17.13", "@types/jest": "28.1.8", + "@types/lodash": "^4.14.200", "@types/multer": "^1.4.9", "@types/node": "^16.0.0", "@types/supertest": "^2.0.11", diff --git a/prisma/schema.prisma b/prisma/schema.prisma index e7c773d..4e06c0e 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -52,7 +52,7 @@ model AdminCompetency { competencyLevels Json[] //{competencyLevelNumber, competencyLevelName} @@id([id, competencyId]) - @@unique([id, competencyId]) + @@unique([competencyId]) @@map("admin_competencies") } diff --git a/src/admin-competency/admin-competency.controller.ts b/src/admin-competency/admin-competency.controller.ts index 3193698..6bcefcc 100644 --- a/src/admin-competency/admin-competency.controller.ts +++ b/src/admin-competency/admin-competency.controller.ts @@ -30,35 +30,31 @@ export class AdminCompetencyController { private readonly adminCompetencyService: AdminCompetencyService ) {} - @Post() - @ApiOperation({ summary: "create new admin-competency" }) + @Post("sync-data") + @ApiOperation({ summary: "sync competency data" }) @ApiResponse({ status: HttpStatus.CREATED, type: AdminCompetencyResponseDto }) - async create( - @Res() res, - @Body() createAdminCompetencyDto: CreateAdminCompetencyDto - ): Promise { + async syncCompetencyData(@Res() res): Promise { try { - this.logger.log(`Initiated creating new admin-competency`); + this.logger.log(`Initiated sync competency data`); - const adminCompetency = await this.adminCompetencyService.create( - createAdminCompetencyDto - ); + const adminCompetency = + await this.adminCompetencyService.syncCompetencyData(); - this.logger.log(`Successfully created new admin-competency`); + this.logger.log(`Successfully sync competency data`); return res.status(HttpStatus.CREATED).send({ - message: `Successfully created new admin-competency`, + message: `Successfully sync competency data`, data: adminCompetency, }); } catch (error) { - this.logger.error("Failed to create new admin-competency", error); + this.logger.error("Failed to sync competency data", error); const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); return res.status(statusCode).json({ statusCode, - message: errorMessage || "Failed to create new admin-competency", + message: errorMessage || "Failed to sync competency data", }); } } diff --git a/src/admin-competency/admin-competency.module.ts b/src/admin-competency/admin-competency.module.ts index cd07b97..6df37a5 100644 --- a/src/admin-competency/admin-competency.module.ts +++ b/src/admin-competency/admin-competency.module.ts @@ -1,9 +1,12 @@ -import { Module } from '@nestjs/common'; -import { AdminCompetencyService } from './admin-competency.service'; -import { AdminCompetencyController } from './admin-competency.controller'; +import { Module } from "@nestjs/common"; +import { MockCompetencyModule } from "src/mockModules/mock-competency/mock-competency.module"; +import { AdminCompetencyController } from "./admin-competency.controller"; +import { AdminCompetencyService } from "./admin-competency.service"; @Module({ + imports: [MockCompetencyModule], controllers: [AdminCompetencyController], - providers: [AdminCompetencyService] + providers: [AdminCompetencyService], + exports: [AdminCompetencyService], }) export class AdminCompetencyModule {} diff --git a/src/admin-competency/admin-competency.service.ts b/src/admin-competency/admin-competency.service.ts index ded7cc4..6dc6e8a 100644 --- a/src/admin-competency/admin-competency.service.ts +++ b/src/admin-competency/admin-competency.service.ts @@ -1,24 +1,61 @@ -import { Injectable, NotFoundException } from "@nestjs/common"; +import { + Inject, + Injectable, + NotFoundException, + forwardRef, +} from "@nestjs/common"; +import _ from "lodash"; import { PrismaService } from "src/prisma/prisma.service"; -import { CreateAdminCompetencyDto, UpdateAdminCompetencyDto } from "./dto"; +import { MockCompetencyService } from "../mockModules/mock-competency/mock-competency.service"; +import { UpdateAdminCompetencyDto } from "./dto"; @Injectable() export class AdminCompetencyService { - constructor(private prisma: PrismaService) {} + constructor( + private prisma: PrismaService, + @Inject(forwardRef(() => MockCompetencyService)) + private mockCompetencyService: MockCompetencyService + ) {} - public async create(createAdminCompetencyDto: CreateAdminCompetencyDto) { - const competencyLevels = JSON.stringify( - createAdminCompetencyDto.competencyLevels - ); + public async syncCompetencyData() { + const competenciesData = + await this.mockCompetencyService.findAllCompetenciesWithLevelNames(); - const payload = { - ...createAdminCompetencyDto, - competencyLevels: JSON.parse(competencyLevels), - }; + const response = await Promise.all( + _.map(competenciesData, async (data) => { + const { competencyLevels, description, id, name } = data; + const competencyLevelData = _.map( + competencyLevels, + (competencyLevelData) => { + const { levelNumber, name } = + competencyLevelData?.competencyLevel || {}; + return { + competencyLevelNumber: levelNumber, + competencyLevelName: name, + }; + } + ); + const adminCompetencyPayload = { + id, + competencyLevels: JSON.parse(JSON.stringify(competencyLevelData)), + competencyId: id, + name, + description, + }; - return await this.prisma.adminCompetency.create({ - data: payload, - }); + return await this.prisma.adminCompetency.upsert({ + where: { + id_competencyId: { + id: adminCompetencyPayload.id, + competencyId: adminCompetencyPayload.competencyId, + }, + }, + update: adminCompetencyPayload, + create: adminCompetencyPayload, + }); + }) + ); + return response; } public async findAll() { diff --git a/src/admin-department/admin-department.controller.ts b/src/admin-department/admin-department.controller.ts index 5d168bb..0be4ff4 100644 --- a/src/admin-department/admin-department.controller.ts +++ b/src/admin-department/admin-department.controller.ts @@ -29,6 +29,40 @@ export class AdminDepartmentController { private readonly adminDepartmentService: AdminDepartmentService ) {} + // Api to sync department with user org + @Post("sync-data") + @ApiOperation({ summary: "sync department with user org" }) // Api operation for swagger + @ApiResponse({ status: HttpStatus.CREATED, type: ResponseAdminDepartmentDto }) // Api response for the swagger + async syncDepartmentData(@Res() res): Promise { + try { + // Log the initiation for the admin department creation + this.logger.log(`Initiate to sync department with user org`); + + const createdOrUpdatedAdminDepartment = + await this.adminDepartmentService.syncDepartmentData(); + // Log the successful creation of the admin department + this.logger.log(`Successfully created admin department.`); + return res.status(HttpStatus.CREATED).json({ + message: "Successfully created admin department.", + data: createdOrUpdatedAdminDepartment, + }); + } catch (error) { + this.logger.error( + `Failed to Successfully created admin department.`, + error + ); + // get error message and status code + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + // Return an error response + return res.status(statusCode).json({ + statusCode, + message: + errorMessage || `Failed to Successfully created admin department.`, + }); + } + } + // Api for creating or updating a admin department using departmentId @Post(":departmentId") @ApiOperation({ summary: "Create or update admin department" }) // Api operation for swagger diff --git a/src/admin-department/admin-department.module.ts b/src/admin-department/admin-department.module.ts index d888017..f5ae089 100644 --- a/src/admin-department/admin-department.module.ts +++ b/src/admin-department/admin-department.module.ts @@ -1,9 +1,8 @@ -import { Module } from '@nestjs/common'; -import { AdminDepartmentService } from './admin-department.service'; -import { AdminDepartmentController } from './admin-department.controller'; -import { PrismaService } from 'src/prisma/prisma.service'; -import { PrismaModule } from 'src/prisma/prisma.module'; -import { MockDepartmentService } from 'src/mockModules/mock-department/mock-department.service'; +import { Module } from "@nestjs/common"; +import { MockDepartmentService } from "src/mockModules/mock-department/mock-department.service"; +import { PrismaModule } from "src/prisma/prisma.module"; +import { AdminDepartmentController } from "./admin-department.controller"; +import { AdminDepartmentService } from "./admin-department.service"; @Module({ // Import the PrismaModule to make Prisma service available within this module @@ -12,10 +11,10 @@ import { MockDepartmentService } from 'src/mockModules/mock-department/mock-depa // Declare the AdminDepartmentController as a controller for this module controllers: [AdminDepartmentController], - // Declare the AdminDepartmentService and PrismaService as providers (singleton) for this module - providers: [AdminDepartmentService, PrismaService,MockDepartmentService], + // Declare the AdminDepartmentService as providers (singleton) for this module + providers: [AdminDepartmentService, MockDepartmentService], // Export the AdminDepartmentService to make it available for other modules that import this module - exports: [AdminDepartmentService] + exports: [AdminDepartmentService], }) export class AdminDepartmentModule {} diff --git a/src/admin-department/admin-department.service.ts b/src/admin-department/admin-department.service.ts index 48510e7..91e7c5d 100644 --- a/src/admin-department/admin-department.service.ts +++ b/src/admin-department/admin-department.service.ts @@ -1,12 +1,8 @@ -import { - ForbiddenException, - Injectable, - NotFoundException, -} from "@nestjs/common"; -import { FilterAdminDepartmentsDto } from "./dto/create-admin-department.dto"; -import { UpdateAdminDepartmentDto } from "./dto/update-admin-department.dto"; -import { PrismaService } from "src/prisma/prisma.service"; +import { Injectable, NotFoundException } from "@nestjs/common"; +import _ from "lodash"; import { MockDepartmentService } from "src/mockModules/mock-department/mock-department.service"; +import { PrismaService } from "src/prisma/prisma.service"; +import { FilterAdminDepartmentsDto } from "./dto/create-admin-department.dto"; @Injectable() export class AdminDepartmentService { @@ -15,6 +11,30 @@ export class AdminDepartmentService { private departmentService: MockDepartmentService ) {} + public async syncDepartmentData() { + const departments = await this.departmentService.findAll(); + + const response = await Promise.all( + _.map(departments, async (department) => { + const { description, id, name } = department; + const adminDepartmentPayload = { + id, + departmentId: id, + name, + description, + }; + + return await this.prisma.adminDepartment.upsert({ + where: { departmentId: adminDepartmentPayload.departmentId }, + update: adminDepartmentPayload, + create: adminDepartmentPayload, + }); + }) + ); + + return response; + } + public async createOrUpdateAdminDepartment(id: number) { // Check the admin department exist for admin department db or not const checkExistingAdminDepartment = diff --git a/src/mockModules/mock-competency/mock-competency.module.ts b/src/mockModules/mock-competency/mock-competency.module.ts index d0d9058..6d315e2 100644 --- a/src/mockModules/mock-competency/mock-competency.module.ts +++ b/src/mockModules/mock-competency/mock-competency.module.ts @@ -1,10 +1,11 @@ -import { Module } from '@nestjs/common'; -import { MockCompetencyController } from './mock-competency.controller'; -import { MockCompetencyService } from './mock-competency.service'; -import { MockCompetencyLevelService } from '../mock-competency-level/mock-competency-level.service'; +import { Module } from "@nestjs/common"; +import { MockCompetencyLevelService } from "../mock-competency-level/mock-competency-level.service"; +import { MockCompetencyController } from "./mock-competency.controller"; +import { MockCompetencyService } from "./mock-competency.service"; @Module({ controllers: [MockCompetencyController], - providers: [MockCompetencyService, MockCompetencyLevelService] + providers: [MockCompetencyService, MockCompetencyLevelService], + exports: [MockCompetencyService], }) export class MockCompetencyModule {} diff --git a/src/mockModules/mock-competency/mock-competency.service.ts b/src/mockModules/mock-competency/mock-competency.service.ts index e98f36c..35b3709 100644 --- a/src/mockModules/mock-competency/mock-competency.service.ts +++ b/src/mockModules/mock-competency/mock-competency.service.ts @@ -1,13 +1,8 @@ -import { - ForbiddenException, - Injectable, - NotFoundException, -} from "@nestjs/common"; +import { Injectable, NotFoundException } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; -import { CreateCompetencyDto, UpdateCompetencyDto } from "./dto"; -import { MockCompetencyLevelService } from "../mock-competency-level/mock-competency-level.service"; import { CreateCompetencyLevelDto } from "../mock-competency-level/dto"; -import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library"; +import { MockCompetencyLevelService } from "../mock-competency-level/mock-competency-level.service"; +import { CreateCompetencyDto, UpdateCompetencyDto } from "./dto"; @Injectable() export class MockCompetencyService { @@ -133,8 +128,20 @@ export class MockCompetencyService { public async findCompetencyByName(competencyName: string) { return this.prisma.competency.findUnique({ where: { - name: competencyName - } - }) + name: competencyName, + }, + }); + } + + public async findAllCompetenciesWithLevelNames() { + return this.prisma.competency.findMany({ + include: { + competencyLevels: { + include: { + competencyLevel: true, + }, + }, + }, + }); } } diff --git a/src/user-metadata/dto/user-metadata-response.dto.ts b/src/user-metadata/dto/user-metadata-response.dto.ts index bc1905c..71013da 100644 --- a/src/user-metadata/dto/user-metadata-response.dto.ts +++ b/src/user-metadata/dto/user-metadata-response.dto.ts @@ -1,13 +1,15 @@ -import { IsBoolean, IsDate, IsInt, IsJSON, IsNotEmpty, IsOptional, IsString, IsUUID } from "class-validator"; import { ResponseSurveyFormDto } from "src/survey-form/dto"; export class ResponseUserMetadataDto { - - readonly userId: string; - readonly designation?: string | null; - readonly departmentId: number; - readonly isAdmin: boolean; - readonly isNewEmployee: boolean; - readonly dateOfJoining: Date; - readonly SurveyForms?: ResponseSurveyFormDto[] -} \ No newline at end of file + readonly userId: string; + readonly designation?: string | null; + readonly departmentId: number; + readonly isAdmin: boolean; + readonly isNewEmployee: boolean; + readonly dateOfJoining: Date; + readonly SurveyForms?: ResponseSurveyFormDto[]; +} + +export class UserResponseMessage { + message: string; +} diff --git a/src/user-metadata/user-metadata.controller.ts b/src/user-metadata/user-metadata.controller.ts index a70dbc1..a6eaf3d 100644 --- a/src/user-metadata/user-metadata.controller.ts +++ b/src/user-metadata/user-metadata.controller.ts @@ -13,7 +13,10 @@ import { import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; import { UserMetadataFilterDto } from "./dto"; -import { ResponseUserMetadataDto } from "./dto/user-metadata-response.dto"; +import { + ResponseUserMetadataDto, + UserResponseMessage, +} from "./dto/user-metadata-response.dto"; import { UserMetadataService } from "./user-metadata.service"; @Controller("user-metadata") @@ -22,6 +25,34 @@ export class UserMetadataController { constructor(private readonly userMetadataService: UserMetadataService) {} private readonly logger = new Logger(UserMetadataController.name); + @Post("sync-data") + @ApiOperation({ summary: "Sync userMetadata with user org" }) + @ApiResponse({ status: HttpStatus.OK, type: UserResponseMessage }) + async SyncUserDataWithFrac(@Res() res): Promise { + try { + this.logger.log(`Initiated sync of userMetadata with user org`); + + const userMetadata = + await this.userMetadataService.syncUserDataWithFrac(); + + this.logger.log(`Successfully sync userMetadata with user org.`); + + return res.status(HttpStatus.OK).json({ + message: "Successfully sync userMetadata with user org.", + }); + } catch (error) { + this.logger.error(`Failed to sync userMetadata with user org.`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed sync userMetadata with user org.`, + }); + } + } + @Post(":userId") @ApiOperation({ summary: "Create or Update User Metadata" }) @ApiResponse({ status: HttpStatus.OK, type: ResponseUserMetadataDto }) diff --git a/src/user-metadata/user-metadata.module.ts b/src/user-metadata/user-metadata.module.ts index 2a83e80..3af3b60 100644 --- a/src/user-metadata/user-metadata.module.ts +++ b/src/user-metadata/user-metadata.module.ts @@ -1,10 +1,12 @@ import { Module } from "@nestjs/common"; +import { AdminDepartmentModule } from "src/admin-department/admin-department.module"; import { MockUserService } from "src/mockModules/mock-user/mock-user.service"; import { SurveyConfigService } from "src/survey-config/survey-config.service"; import { UserMetadataController } from "./user-metadata.controller"; import { UserMetadataService } from "./user-metadata.service"; @Module({ + imports: [AdminDepartmentModule], controllers: [UserMetadataController], providers: [UserMetadataService, SurveyConfigService, MockUserService], exports: [UserMetadataService], diff --git a/src/user-metadata/user-metadata.service.ts b/src/user-metadata/user-metadata.service.ts index 63bdb0c..cc00c8a 100644 --- a/src/user-metadata/user-metadata.service.ts +++ b/src/user-metadata/user-metadata.service.ts @@ -1,18 +1,60 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { TimeUnitsEnum, UserRolesEnum } from "@prisma/client"; +import _ from "lodash"; import { MockUserService } from "src/mockModules/mock-user/mock-user.service"; import { PrismaService } from "src/prisma/prisma.service"; import { SurveyConfigService } from "src/survey-config/survey-config.service"; import { UserMetadataFilterDto } from "./dto"; +import { AdminCompetencyService } from "src/admin-competency/admin-competency.service"; +import { AdminDepartmentService } from "src/admin-department/admin-department.service"; @Injectable() export class UserMetadataService { constructor( private prisma: PrismaService, private mockUser: MockUserService, - private surveyConfig: SurveyConfigService + private surveyConfig: SurveyConfigService, + private adminDepartmentService: AdminDepartmentService ) {} + public async syncUserDataWithFrac() { + // sync department data first before users because of table relation + await this.adminDepartmentService.syncDepartmentData(); + + // fetch all users + const users = await this.mockUser.findAll(); + + // update or create data in user metadata table + const usersMetaDataPayload = await Promise.all( + users.map(async (user) => { + const { Department, createdAt, designation, id, role, userName } = user; + + const isNewEmployee = await this.isEmployeeNew( + user.createdAt, + user.Department!.id + ); + + const userMetadataPayload = { + userId: id, + userName: `${userName}_${id}`, + departmentId: _.get(Department, "id", 0), + designation, + isNewEmployee, + dateOfJoining: createdAt, + isAdmin: role == _.get(UserRolesEnum, "ADMIN", "") ? true : false, + }; + + return await this.prisma.userMetadata.upsert({ + where: { userId: userMetadataPayload.userId }, + create: userMetadataPayload, + update: userMetadataPayload, + }); + }) + ); + + return usersMetaDataPayload; + } + async createOrUpdateUserMetadata(userId: string) { let userMetadata = await this.prisma.userMetadata.findUnique({ where: { userId }, @@ -97,7 +139,7 @@ export class UserMetadataService { }); } - async isEmployeeNew( + public async isEmployeeNew( dateOfJoining: Date, departmentId: number ): Promise { @@ -105,10 +147,10 @@ export class UserMetadataService { departmentId, }); - if (surveyConfig.length == 0) - throw new NotFoundException( - `Survey Config for the department with Id #${departmentId} not found.` - ); + if (surveyConfig.length == 0) return true; + // throw new NotFoundException( + // `Survey Config for the department with Id #${departmentId} not found.` + // ); let dateToBeCompared = new Date(dateOfJoining); const today = new Date(); @@ -140,7 +182,7 @@ export class UserMetadataService { isNewEmployee: true, SurveyForm: { orderBy: { - createdAt: "asc" + createdAt: "asc", }, select: { id: true, @@ -153,10 +195,10 @@ export class UserMetadataService { select: { id: true, Assessor: { - select:{ - userId: true, - userName: true, - } + select: { + userId: true, + userName: true, + }, }, responseJson: true, status: true, diff --git a/src/utils/utils.ts b/src/utils/utils.ts index b70c1a5..3e5f7d6 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,5 +1,9 @@ import { HttpStatus } from "@nestjs/common"; -import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library"; +import { + PrismaClientKnownRequestError, + PrismaClientValidationError, +} from "@prisma/client/runtime/library"; +import _ from "lodash"; export const validationOptions = { whitelist: true, @@ -14,18 +18,21 @@ export function getPrismaErrorStatusAndMessage(error: any): { errorMessage: string | undefined; statusCode: number; } { - if (error instanceof PrismaClientKnownRequestError) { - const errorCode = error?.code || "DEFAULT_ERROR_CODE"; + if ( + error instanceof PrismaClientKnownRequestError || + error instanceof PrismaClientValidationError + ) { + const errorCode = _.get(error, "code", "DEFAULT_ERROR_CODE"); const errorCodeMap: Record = { P2000: HttpStatus.BAD_REQUEST, P2002: HttpStatus.CONFLICT, P2003: HttpStatus.CONFLICT, P2025: HttpStatus.NOT_FOUND, + DEFAULT_ERROR_CODE: HttpStatus.INTERNAL_SERVER_ERROR, }; - const statusCode = - errorCodeMap[errorCode] || HttpStatus.INTERNAL_SERVER_ERROR; + const statusCode = errorCodeMap[errorCode]; const errorMessage = error.message.split("\n").pop(); return { statusCode, errorMessage }; From a33ad0ddd15dd765281cf1e96972fab4d1d7de10 Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Thu, 2 Nov 2023 17:21:16 +0530 Subject: [PATCH 079/175] updated --- prisma/schema.prisma | 3 +-- src/admin-competency/admin-competency.controller.ts | 2 +- src/admin-competency/admin-competency.service.ts | 9 +++++---- src/admin-department/admin-department.controller.ts | 2 +- src/user-metadata/user-metadata.controller.ts | 2 +- src/user-metadata/user-metadata.service.ts | 3 --- 6 files changed, 9 insertions(+), 12 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 4e06c0e..dbfd164 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -44,7 +44,7 @@ model QuestionBank { model AdminCompetency { id Int @default(autoincrement()) - competencyId Int + competencyId Int @unique name String description String? createdAt DateTime @default(now()) @@ -52,7 +52,6 @@ model AdminCompetency { competencyLevels Json[] //{competencyLevelNumber, competencyLevelName} @@id([id, competencyId]) - @@unique([competencyId]) @@map("admin_competencies") } diff --git a/src/admin-competency/admin-competency.controller.ts b/src/admin-competency/admin-competency.controller.ts index 6bcefcc..5a8e346 100644 --- a/src/admin-competency/admin-competency.controller.ts +++ b/src/admin-competency/admin-competency.controller.ts @@ -30,7 +30,7 @@ export class AdminCompetencyController { private readonly adminCompetencyService: AdminCompetencyService ) {} - @Post("sync-data") + @Post("sync-competency-data") @ApiOperation({ summary: "sync competency data" }) @ApiResponse({ status: HttpStatus.CREATED, type: AdminCompetencyResponseDto }) async syncCompetencyData(@Res() res): Promise { diff --git a/src/admin-competency/admin-competency.service.ts b/src/admin-competency/admin-competency.service.ts index 6dc6e8a..e5cd7db 100644 --- a/src/admin-competency/admin-competency.service.ts +++ b/src/admin-competency/admin-competency.service.ts @@ -24,17 +24,20 @@ export class AdminCompetencyService { const response = await Promise.all( _.map(competenciesData, async (data) => { const { competencyLevels, description, id, name } = data; + const competencyLevelData = _.map( competencyLevels, (competencyLevelData) => { const { levelNumber, name } = competencyLevelData?.competencyLevel || {}; + return { competencyLevelNumber: levelNumber, competencyLevelName: name, }; } ); + const adminCompetencyPayload = { id, competencyLevels: JSON.parse(JSON.stringify(competencyLevelData)), @@ -45,16 +48,14 @@ export class AdminCompetencyService { return await this.prisma.adminCompetency.upsert({ where: { - id_competencyId: { - id: adminCompetencyPayload.id, - competencyId: adminCompetencyPayload.competencyId, - }, + competencyId: adminCompetencyPayload.competencyId, }, update: adminCompetencyPayload, create: adminCompetencyPayload, }); }) ); + return response; } diff --git a/src/admin-department/admin-department.controller.ts b/src/admin-department/admin-department.controller.ts index 0be4ff4..f38c830 100644 --- a/src/admin-department/admin-department.controller.ts +++ b/src/admin-department/admin-department.controller.ts @@ -30,7 +30,7 @@ export class AdminDepartmentController { ) {} // Api to sync department with user org - @Post("sync-data") + @Post("sync-department-data") @ApiOperation({ summary: "sync department with user org" }) // Api operation for swagger @ApiResponse({ status: HttpStatus.CREATED, type: ResponseAdminDepartmentDto }) // Api response for the swagger async syncDepartmentData(@Res() res): Promise { diff --git a/src/user-metadata/user-metadata.controller.ts b/src/user-metadata/user-metadata.controller.ts index a6eaf3d..396ed20 100644 --- a/src/user-metadata/user-metadata.controller.ts +++ b/src/user-metadata/user-metadata.controller.ts @@ -25,7 +25,7 @@ export class UserMetadataController { constructor(private readonly userMetadataService: UserMetadataService) {} private readonly logger = new Logger(UserMetadataController.name); - @Post("sync-data") + @Post("sync-user-metadata") @ApiOperation({ summary: "Sync userMetadata with user org" }) @ApiResponse({ status: HttpStatus.OK, type: UserResponseMessage }) async SyncUserDataWithFrac(@Res() res): Promise { diff --git a/src/user-metadata/user-metadata.service.ts b/src/user-metadata/user-metadata.service.ts index cc00c8a..311e32f 100644 --- a/src/user-metadata/user-metadata.service.ts +++ b/src/user-metadata/user-metadata.service.ts @@ -148,9 +148,6 @@ export class UserMetadataService { }); if (surveyConfig.length == 0) return true; - // throw new NotFoundException( - // `Survey Config for the department with Id #${departmentId} not found.` - // ); let dateToBeCompared = new Date(dateOfJoining); const today = new Date(); From 4e8daa395b128969256a758d4e7a67834815018e Mon Sep 17 00:00:00 2001 From: Prashant Kumar Date: Fri, 27 Oct 2023 13:09:23 +0530 Subject: [PATCH 080/175] added the unit test-cases --- .../mock-level/mock-level.controller.spec.ts | 148 ++++++++++++++++++ .../mock-level/mock-level.service.spec.ts | 128 +++++++++++++++ .../mock-level/mock-level.service.ts | 2 +- 3 files changed, 277 insertions(+), 1 deletion(-) create mode 100644 src/mockModules/mock-level/mock-level.controller.spec.ts create mode 100644 src/mockModules/mock-level/mock-level.service.spec.ts diff --git a/src/mockModules/mock-level/mock-level.controller.spec.ts b/src/mockModules/mock-level/mock-level.controller.spec.ts new file mode 100644 index 0000000..54e7f6d --- /dev/null +++ b/src/mockModules/mock-level/mock-level.controller.spec.ts @@ -0,0 +1,148 @@ +import { Test, TestingModule } from "@nestjs/testing"; +import { MockLevelController } from "./mock-level.controller"; +import { MockLevelService } from "./mock-level.service"; +import { CreateMockLevelDto } from "./dto/create-mock-level.dto"; +import { UpdateMockLevelDto } from "./dto/update-mock-level.dto"; +import { NotFoundException } from "@nestjs/common"; + +describe("MockLevelController", () => { + let controller: MockLevelController; + let mockLevelService: MockLevelService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [MockLevelController], + providers: [MockLevelService], + }).compile(); + + controller = module.get(MockLevelController); + mockLevelService = module.get(MockLevelService); + }); + + it("should be defined", () => { + expect(controller).toBeDefined(); + }); + + describe("create", () => { + it("should create a new mock level", async () => { + const createMockLevelDto: CreateMockLevelDto = { + label: "Test Level", + description: "Test Description", + }; + + const mockLevel = { + id: 1, + label: "Label 1", + description: "Test description", + }; // Define a mock response from the service + + // Create mock request objects + const req = {} as Request; + + jest.spyOn(mockLevelService, "create").mockResolvedValue(mockLevel); + + const result = await controller.create(req, createMockLevelDto); + + expect(result).toEqual({ data: mockLevel, message: "Successfully done" }); + }); + }); + + describe("findAll", () => { + it("should return an array of mock levels", async () => { + const mockLevels = [ + /* Define an array of mock levels */ + ]; + + jest.spyOn(mockLevelService, "findAll").mockResolvedValue(mockLevels); + + // Create mock request objects + const req = {} as Request; + + const result = await controller.findAll(req); + + expect(result).toEqual({ + data: mockLevels, + message: "Successfully done", + }); + }); + }); + + describe("findOne", () => { + it("should return a single mock level by ID", async () => { + const mockLevelId = 1; + const mockLevel = { + /* Define one or more properties for this object*/ + id: 111, + label: "Label 1", + description: "this is test description", + }; // Define a mock response from the service + + jest.spyOn(mockLevelService, "findOne").mockResolvedValue(mockLevel); + + // Create mock request objects + const req = {} as Request; + + const result = await controller.findOne(req,mockLevelId); + + expect(result).toEqual({ data: mockLevel, message: "Successfully done" }); + }); + + it("should handle NotFoundException for non-existent ID", async () => { + const mockLevelId = 999; // Assuming this ID does not exist + jest + .spyOn(mockLevelService, "findOne") + .mockRejectedValue(new NotFoundException()); + + // Create mock request objects + const req = {} as Request; + + const result = await controller.findOne(req,mockLevelId); + + expect(result).toEqual({ message: "Level with id #999 not found" }); + }); + }); + + describe("update", () => { + it("should update a mock level by ID", async () => { + const mockLevelId = 1; + const updateMockLevelDto: UpdateMockLevelDto = { + label: "Updated Label", + description: "Updated Description", + }; + + const mockLevel = { + id: mockLevelId, + label: "Label 1", + description: "this is test description", + }; // Define a mock response from the service + + jest.spyOn(mockLevelService, "update").mockResolvedValue(mockLevel); + // Create mock request objects + const req = {} as Request; + const result = await controller.update( + req, + mockLevelId, + updateMockLevelDto + ); + + expect(result).toEqual({ data: mockLevel, message: "Successfully done" }); + }); + }); + + describe("remove", () => { + it("should delete a mock level by ID", async () => { + const mockLevelId = 1; + const mockLevel = { + id: mockLevelId, + label:"test",description:'test' + }; // Define a mock response from the service + + jest.spyOn(mockLevelService, "remove").mockResolvedValue(mockLevel); + // Create mock request objects + const req = {} as Request; + const result = await controller.remove(req,mockLevelId); + + expect(result).toEqual({ data: mockLevel, message: "Successfully done" }); + }); + }); +}); diff --git a/src/mockModules/mock-level/mock-level.service.spec.ts b/src/mockModules/mock-level/mock-level.service.spec.ts new file mode 100644 index 0000000..267bf8f --- /dev/null +++ b/src/mockModules/mock-level/mock-level.service.spec.ts @@ -0,0 +1,128 @@ +import { Test, TestingModule } from "@nestjs/testing"; +import { MockLevelService } from "./mock-level.service"; +import { PrismaService } from "../../prisma/prisma.service"; +import { CreateMockLevelDto } from "./dto/create-mock-level.dto"; +import { UpdateMockLevelDto } from "./dto/update-mock-level.dto"; + +import { NotFoundException } from "@nestjs/common"; + +describe("MockLevelService", () => { + let service: MockLevelService; + let prismaService: PrismaService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [MockLevelService, PrismaService], + }).compile(); + + service = module.get(MockLevelService); + prismaService = module.get(PrismaService); + }); + + it("should be defined", () => { + expect(service).toBeDefined(); + }); + + describe("create", () => { + it("should create a new mock level", async () => { + const createMockLevelDto: CreateMockLevelDto = { + label: "Test Level", + description: "Test Description", + }; + + const mockLevel = { + id: 1024567893, + label: 'Test Level', + description: 'Test Description' + }; // Define a mock response from the database + + jest.spyOn(prismaService.level, "create").mockResolvedValue(mockLevel); + + const result = await service.create(createMockLevelDto); + + expect(result).toEqual(mockLevel); + }); + }); + + describe("findAll", () => { + it("should return an array of mock levels", async () => { + const mockLevels = [ + { id: 1, label: "test", description: "test" }, + { id: 2, label: "test2", description: "test2" }, + ]; + + jest.spyOn(prismaService.level, "findMany").mockResolvedValue(mockLevels); + + const result = await service.findAll(); + + expect(result).toEqual(mockLevels); + }); + }); + + describe("findOne", () => { + it("should return a single mock level by ID", async () => { + const mockLevelId = 1; + const mockLevel = { + id: 1, + label: "test", + description: "test" + }; // Define a mock response from the database + + jest + .spyOn(prismaService.level, "findUnique") + .mockResolvedValue(mockLevel); + + const result = await service.findOne(mockLevelId); + + expect(result).toEqual(mockLevel); + }); + + it("should throw NotFoundException for non-existent ID", async () => { + const mockLevelId = 999; // Assuming this ID does not exist + jest.spyOn(prismaService.level, "findUnique").mockResolvedValue(null); + + await expect(service.findOne(mockLevelId)).rejects.toThrowError( + NotFoundException + ); + }); + }); + + describe("update", () => { + it("should update a mock level by ID", async () => { + const mockLevelId = 1; + const updateMockLevelDto: UpdateMockLevelDto = { + label: "Updated Label", + description: "Updated Description", + }; + + const mockLevel = { + id: 1, + label: 'Updated Label', + description: 'Updated Description' + }; // Define a mock response from the database + + jest.spyOn(prismaService.level, "update").mockResolvedValue(mockLevel); + + const result = await service.update(mockLevelId, updateMockLevelDto); + + expect(result).toEqual(mockLevel); + }); + }); + + describe("remove", () => { + it("should delete a mock level by ID", async () => { + const mockLevelId = 1; + const mockLevel = { + id: 1, + label: '', + description: '' + }; // Define a mock response from the database + + jest.spyOn(prismaService.level, "delete").mockResolvedValue(mockLevel); + + const result = await service.remove(mockLevelId); + + expect(result).toEqual(mockLevel); + }); + }); +}); diff --git a/src/mockModules/mock-level/mock-level.service.ts b/src/mockModules/mock-level/mock-level.service.ts index fe5a5a3..f598d47 100644 --- a/src/mockModules/mock-level/mock-level.service.ts +++ b/src/mockModules/mock-level/mock-level.service.ts @@ -1,7 +1,7 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { CreateMockLevelDto } from "./dto/create-mock-level.dto"; import { UpdateMockLevelDto } from "./dto/update-mock-level.dto"; -import { PrismaService } from "src/prisma/prisma.service"; +import { PrismaService } from "../../prisma/prisma.service"; @Injectable() export class MockLevelService { From 8214837e1d638a45cc682405ca381b8a7be20c34 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Tue, 31 Oct 2023 11:44:41 +0530 Subject: [PATCH 081/175] Prisma Module updated for testcases. And test cases for SurveyConfigModule. --- package-lock.json | 164 ++++++++++++++++++ package.json | 1 + src/prisma/prisma.module.ts | 3 +- .../survey-config.controller.spec.ts | 104 +++++++++++ src/survey-config/survey-config.module.ts | 2 +- 5 files changed, 272 insertions(+), 2 deletions(-) create mode 100644 src/survey-config/survey-config.controller.spec.ts diff --git a/package-lock.json b/package-lock.json index 15726c0..7ae691d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,6 +41,7 @@ "eslint-config-prettier": "^8.3.0", "eslint-plugin-prettier": "^4.0.0", "jest": "28.1.3", + "pactum": "^3.5.1", "prettier": "^2.3.2", "prisma": "^5.4.2", "source-map-support": "^0.5.20", @@ -258,6 +259,15 @@ "node": ">=8" } }, + "node_modules/@arr/every": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@arr/every/-/every-1.0.1.tgz", + "integrity": "sha512-UQFQ6SgyJ6LX42W8rHCs8KVc0JS0tzVL9ct4XYedJukskYVWTo49tNiMEK9C2HTyarbNiT/RVIRSY82vH+6sTg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/code-frame": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", @@ -931,6 +941,12 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@exodus/schemasafe": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.3.0.tgz", + "integrity": "sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==", + "dev": true + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", @@ -2492,6 +2508,12 @@ "node": ">=8" } }, + "node_modules/@polka/url": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-0.5.0.tgz", + "integrity": "sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw==", + "dev": true + }, "node_modules/@prisma/client": { "version": "5.4.2", "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.4.2.tgz", @@ -3986,6 +4008,12 @@ } ] }, + "node_modules/centra": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/centra/-/centra-2.6.0.tgz", + "integrity": "sha512-dgh+YleemrT8u85QL11Z6tYhegAs3MMxsaWAq/oXeAmYJ7VxL3SI9TZtnfaEvNDMAPolj25FXIb3S+HCI4wQaQ==", + "dev": true + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -4395,6 +4423,12 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "node_modules/deep-override": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/deep-override/-/deep-override-1.0.2.tgz", + "integrity": "sha512-+bAuLuYqaVVUWPaq8rmU8NLTX85p4I5k5/cVdhBioEfH7k+5NlGdv4NoJVQcJRByqzzTWWzTpih+pU1wBTmMow==", + "dev": true + }, "node_modules/deepmerge": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", @@ -5432,6 +5466,17 @@ "node": ">=8" } }, + "node_modules/form-data-lite": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/form-data-lite/-/form-data-lite-1.0.3.tgz", + "integrity": "sha512-P7xPqAiOPKzC9Q9aywAZJCQq4QOE5WokPb3HrcWRh7C57RKytueJzoORZAVgHBNvK/lL7E+FxjQjd4X/zbecEQ==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-lite": "^1.0.3" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -7853,6 +7898,15 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, + "node_modules/json-query": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/json-query/-/json-query-2.2.2.tgz", + "integrity": "sha512-y+IcVZSdqNmS4fO8t1uZF6RMMs0xh3SrTjJr9bp1X3+v0Q13+7Cyv12dSmKwDswp/H427BVtpkLWhGxYu3ZWRA==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -7904,6 +7958,15 @@ "node": ">=6" } }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -7931,6 +7994,12 @@ "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.15.tgz", "integrity": "sha512-sLeVLmWX17VCKKulc+aDIRHS95TxoTsKMRJi5s5gJdwlqNzMWcBCtSHHruVyXjqfi67daXM2SnLf2juSrdx5Sg==" }, + "node_modules/lightcookie": { + "version": "1.0.25", + "resolved": "https://registry.npmjs.org/lightcookie/-/lightcookie-1.0.25.tgz", + "integrity": "sha512-SrY/+eBPaKAMnsn7mCsoOMZzoQyCyHHHZlFCu2fjo28XxSyCLjlooKiTxyrXTg8NPaHp1YzWi0lcGG1gDi6KHw==", + "dev": true + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -8135,6 +8204,18 @@ "tmpl": "1.0.5" } }, + "node_modules/matchit": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/matchit/-/matchit-1.1.0.tgz", + "integrity": "sha512-+nGYoOlfHmxe5BW5tE0EMJppXEwdSf8uBA1GTZC7Q77kbT35+VKLYJMzVNWCHSsga1ps1tPYFtFyvxvKzWVmMA==", + "dev": true, + "dependencies": { + "@arr/every": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -8215,6 +8296,12 @@ "node": ">= 0.6" } }, + "node_modules/mime-lite": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/mime-lite/-/mime-lite-1.0.3.tgz", + "integrity": "sha512-V85l97zJSTG8FEvmdTlmNYb0UMrVBwvRjw7bWTf/aT6KjFwtz3iTz8D2tuFIp7lwiaO2C5ecnrEmSkkMRCrqVw==", + "dev": true + }, "node_modules/mime-types": { "version": "2.1.27", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", @@ -8461,6 +8548,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openapi-fuzzer-core": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/openapi-fuzzer-core/-/openapi-fuzzer-core-1.0.6.tgz", + "integrity": "sha512-FJNJIfgUFuv4NmVGq9MYdoKra2GrkDy2uhIjE2YGlw30UA1glf4SXLMhI4UwdcJ8jisKdIxi7lXrfej8GvNW5w==", + "dev": true, + "dependencies": { + "klona": "^2.0.4" + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -8635,6 +8731,34 @@ "node": ">=6" } }, + "node_modules/pactum": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/pactum/-/pactum-3.5.1.tgz", + "integrity": "sha512-7DcbzGFZkvpdETtyxyB5inhmxFhXh+D/AZwF8fQaxywums4zf1nZO/vNDueogN8UWV1YldUEkuGo+1XlGvz+iQ==", + "dev": true, + "dependencies": { + "@exodus/schemasafe": "^1.2.3", + "deep-override": "^1.0.2", + "form-data-lite": "^1.0.3", + "json-query": "^2.2.2", + "klona": "^2.0.6", + "lightcookie": "^1.0.25", + "openapi-fuzzer-core": "^1.0.6", + "pactum-matchers": "^1.1.6", + "parse-graphql": "^1.0.0", + "phin": "^3.7.0", + "polka": "^0.5.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pactum-matchers": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/pactum-matchers/-/pactum-matchers-1.1.6.tgz", + "integrity": "sha512-55io32NeOKbLpHKKPzYDOr+N2dseTzMbj1Gj1y+zvOkKK6NDf5BT5pxglfqLN/ra3ig5zvbrKFUqZIWjAWboog==", + "dev": true + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -8647,6 +8771,12 @@ "node": ">=6" } }, + "node_modules/parse-graphql": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-graphql/-/parse-graphql-1.0.0.tgz", + "integrity": "sha512-NjvQHHaiPCxPZrhm/kKnorxOv7r/eA+tE0VW5E8iJMH9wTqFA1V0YK/7nbpxVu3JdXUxyWTKMez9lsHUtAwa0w==", + "dev": true + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -8753,6 +8883,18 @@ "node": ">=8" } }, + "node_modules/phin": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/phin/-/phin-3.7.0.tgz", + "integrity": "sha512-DqnVNrpYhKGBZppNKprD+UJylMeEKOZxHgPB+ZP6mGzf3uA2uox4Ep9tUm+rUc8WLIdHT3HcAE4X8fhwQA9JKg==", + "dev": true, + "dependencies": { + "centra": "^2.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -8853,6 +8995,16 @@ "node": ">=4" } }, + "node_modules/polka": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/polka/-/polka-0.5.2.tgz", + "integrity": "sha512-FVg3vDmCqP80tOrs+OeNlgXYmFppTXdjD5E7I4ET1NjvtNmQrb1/mJibybKkb/d4NA7YWAr1ojxuhpL3FHqdlw==", + "dev": true, + "dependencies": { + "@polka/url": "^0.5.0", + "trouter": "^2.0.1" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -9877,6 +10029,18 @@ "tree-kill": "cli.js" } }, + "node_modules/trouter": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/trouter/-/trouter-2.0.1.tgz", + "integrity": "sha512-kr8SKKw94OI+xTGOkfsvwZQ8mWoikZDd2n8XZHjJVZUARZT+4/VV6cacRS6CLsH9bNm+HFIPU1Zx4CnNnb4qlQ==", + "dev": true, + "dependencies": { + "matchit": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/ts-jest": { "version": "28.0.8", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-28.0.8.tgz", diff --git a/package.json b/package.json index be4ca74..98510b7 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "eslint-config-prettier": "^8.3.0", "eslint-plugin-prettier": "^4.0.0", "jest": "28.1.3", + "pactum": "^3.5.1", "prettier": "^2.3.2", "prisma": "^5.4.2", "source-map-support": "^0.5.20", diff --git a/src/prisma/prisma.module.ts b/src/prisma/prisma.module.ts index 1edbf95..e03c438 100644 --- a/src/prisma/prisma.module.ts +++ b/src/prisma/prisma.module.ts @@ -1,9 +1,10 @@ import { Global, Module } from "@nestjs/common"; import { PrismaService } from "./prisma.service"; +import { ConfigService } from "@nestjs/config"; @Global() @Module({ - providers: [PrismaService], + providers: [PrismaService, ConfigService], exports: [PrismaService], }) export class PrismaModule {} diff --git a/src/survey-config/survey-config.controller.spec.ts b/src/survey-config/survey-config.controller.spec.ts new file mode 100644 index 0000000..19baefd --- /dev/null +++ b/src/survey-config/survey-config.controller.spec.ts @@ -0,0 +1,104 @@ +import * as pactum from "pactum"; +import { INestApplication, ValidationPipe } from "@nestjs/common"; +import { PrismaService } from "../prisma/prisma.service"; +import { Test } from "@nestjs/testing"; +import { SurveyConfigModule } from "./survey-config.module"; + +describe("SurveyConfig e2e", () => { + let app: INestApplication; + let prisma: PrismaService; + + beforeAll(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [SurveyConfigModule], + }).compile(); + app = moduleRef.createNestApplication(); + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + }), + ); + await app.init(); + await app.listen(3000); + + prisma = app.get(PrismaService); + pactum.request.setBaseUrl('http://localhost:3000'); + }); + + afterAll(async () => { + app.close(); + }); + + describe("SurveyConfigController createSurveyConfig()", () => { + it("should create a new survey config", async () => { + const createSurveyConfigDto = { + name: "Test Survey Config", + departmentId: 1, + startTime: new Date(), + endTime: new Date(), + }; + + const response = await pactum + .spec() + .post("/survey-config") + .withBody(createSurveyConfigDto) + .expectStatus(201); + + const createdSurveyConfig = JSON.parse(response.body); + + expect(createdSurveyConfig.name).toBe("Test Survey Config"); + expect(createdSurveyConfig.departmentId).toBe(1); + expect(createdSurveyConfig.startTime).toBeDefined(); + expect(createdSurveyConfig.endTime).toBeDefined(); + }); + }); + + describe("SurveyConfigController getAllSurveyConfig()", () => { + it("should get all survey configs", async () => { + const response = await pactum + .spec() + .get("/survey-config") + .expectStatus(200); + + const surveyConfigs = JSON.parse(response.body); + + expect(surveyConfigs.length).toBeGreaterThanOrEqual(0); + }); + }); + + describe("SurveyConfigController updateSurveyConfigById()", () => { + it("should update an existing survey config", async () => { + const updateSurveyConfigDto = { + name: "Updated Test Survey Config", + departmentId: 2, + }; + + const response = await pactum + .spec() + .put("/survey-config/1") + .withBody(updateSurveyConfigDto) + .expectStatus(200); + + const updatedSurveyConfig = JSON.parse(response.body); + + expect(updatedSurveyConfig.name).toBe("Updated Test Survey Config"); + expect(updatedSurveyConfig.departmentId).toBe(2); + }); + }); + + describe("SurveyConfigController deleteSurveyConfigById()", () => { + it("should delete an existing survey config", async () => { + const response = await pactum + .spec() + .delete("/survey-config/1") + .expectStatus(200); + + const deletedSurveyConfig = JSON.parse(response.body); + + expect(deletedSurveyConfig.name).toBeDefined(); + expect(deletedSurveyConfig.departmentId).toBeDefined(); + expect(deletedSurveyConfig.startTime).toBeDefined(); + expect(deletedSurveyConfig.endTime).toBeDefined(); + }); + }); +}); diff --git a/src/survey-config/survey-config.module.ts b/src/survey-config/survey-config.module.ts index 8ddbdb4..64e4506 100644 --- a/src/survey-config/survey-config.module.ts +++ b/src/survey-config/survey-config.module.ts @@ -7,7 +7,7 @@ import { PrismaService } from "../prisma/prisma.service"; @Module({ imports: [PrismaModule], controllers: [SurveyConfigController], - providers: [SurveyConfigService, PrismaService], + providers: [SurveyConfigService], exports: [SurveyConfigService], }) export class SurveyConfigModule {} From 076055c0c2afd2becf81707c43d90df2c95f09de Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Tue, 31 Oct 2023 15:23:59 +0530 Subject: [PATCH 082/175] Survey Form unit test cases --- .../mock-level/mock-level.controller.spec.ts | 21 +-- .../survey-config.controller.spec.ts | 39 +++-- .../survey-form.controller.spec.ts | 142 ++++++++++++++++-- src/survey-form/survey-form.controller.ts | 2 +- src/survey-form/survey-form.module.ts | 11 +- src/survey-form/survey-form.service.ts | 16 +- 6 files changed, 183 insertions(+), 48 deletions(-) diff --git a/src/mockModules/mock-level/mock-level.controller.spec.ts b/src/mockModules/mock-level/mock-level.controller.spec.ts index 54e7f6d..8b2e9bb 100644 --- a/src/mockModules/mock-level/mock-level.controller.spec.ts +++ b/src/mockModules/mock-level/mock-level.controller.spec.ts @@ -26,13 +26,13 @@ describe("MockLevelController", () => { describe("create", () => { it("should create a new mock level", async () => { const createMockLevelDto: CreateMockLevelDto = { - label: "Test Level", + levelNumber: 1, description: "Test Description", }; const mockLevel = { id: 1, - label: "Label 1", + levelNumber: 1, description: "Test description", }; // Define a mock response from the service @@ -72,8 +72,8 @@ describe("MockLevelController", () => { const mockLevelId = 1; const mockLevel = { /* Define one or more properties for this object*/ - id: 111, - label: "Label 1", + id: 1, + levelNumber: 1, description: "this is test description", }; // Define a mock response from the service @@ -82,7 +82,7 @@ describe("MockLevelController", () => { // Create mock request objects const req = {} as Request; - const result = await controller.findOne(req,mockLevelId); + const result = await controller.findOne(req, mockLevelId); expect(result).toEqual({ data: mockLevel, message: "Successfully done" }); }); @@ -96,7 +96,7 @@ describe("MockLevelController", () => { // Create mock request objects const req = {} as Request; - const result = await controller.findOne(req,mockLevelId); + const result = await controller.findOne(req, mockLevelId); expect(result).toEqual({ message: "Level with id #999 not found" }); }); @@ -106,13 +106,13 @@ describe("MockLevelController", () => { it("should update a mock level by ID", async () => { const mockLevelId = 1; const updateMockLevelDto: UpdateMockLevelDto = { - label: "Updated Label", + levelNumber: 1, description: "Updated Description", }; const mockLevel = { id: mockLevelId, - label: "Label 1", + levelNumber: 1, description: "this is test description", }; // Define a mock response from the service @@ -134,13 +134,14 @@ describe("MockLevelController", () => { const mockLevelId = 1; const mockLevel = { id: mockLevelId, - label:"test",description:'test' + levelNumber: 1, + description: "test", }; // Define a mock response from the service jest.spyOn(mockLevelService, "remove").mockResolvedValue(mockLevel); // Create mock request objects const req = {} as Request; - const result = await controller.remove(req,mockLevelId); + const result = await controller.remove(req, mockLevelId); expect(result).toEqual({ data: mockLevel, message: "Successfully done" }); }); diff --git a/src/survey-config/survey-config.controller.spec.ts b/src/survey-config/survey-config.controller.spec.ts index 19baefd..34b2d9a 100644 --- a/src/survey-config/survey-config.controller.spec.ts +++ b/src/survey-config/survey-config.controller.spec.ts @@ -1,28 +1,37 @@ import * as pactum from "pactum"; import { INestApplication, ValidationPipe } from "@nestjs/common"; import { PrismaService } from "../prisma/prisma.service"; -import { Test } from "@nestjs/testing"; +import { Test, TestingModule } from "@nestjs/testing"; import { SurveyConfigModule } from "./survey-config.module"; +import { ConfigService } from "@nestjs/config"; describe("SurveyConfig e2e", () => { let app: INestApplication; let prisma: PrismaService; + let module: TestingModule; beforeAll(async () => { - const moduleRef = await Test.createTestingModule({ - imports: [SurveyConfigModule], - }).compile(); - app = moduleRef.createNestApplication(); - app.useGlobalPipes( - new ValidationPipe({ - whitelist: true, - }), - ); - await app.init(); - await app.listen(3000); - - prisma = app.get(PrismaService); - pactum.request.setBaseUrl('http://localhost:3000'); + module = await Test.createTestingModule({ + imports: [SurveyConfigModule], + }).compile(); + + app = module.createNestApplication(); + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + }) + ); + + const configService = app.get(ConfigService); + const apiPrefix = configService.get("API_PREFIX") || "api"; + const PORT = configService.get("APP_PORT") || 4010; + app.setGlobalPrefix(apiPrefix); + + await app.init(); + await app.listen(PORT); + + prisma = app.get(PrismaService); + pactum.request.setBaseUrl(`http://localhost:${PORT}/${apiPrefix}`); }); afterAll(async () => { diff --git a/src/survey-form/survey-form.controller.spec.ts b/src/survey-form/survey-form.controller.spec.ts index 5f21695..31c3f52 100644 --- a/src/survey-form/survey-form.controller.spec.ts +++ b/src/survey-form/survey-form.controller.spec.ts @@ -1,18 +1,140 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { SurveyFormController } from './survey-form.controller'; +import * as pactum from "pactum"; +import { INestApplication, ValidationPipe } from "@nestjs/common"; +import { PrismaService } from "../prisma/prisma.service"; +import { Test, TestingModule } from "@nestjs/testing"; +import { ConfigService } from "@nestjs/config"; +import { SurveyFormModule } from "./survey-form.module"; +import { CreateSurveyFormDto } from "./dto"; +import { SurveyStatusEnum } from "@prisma/client"; -describe('SurveyFormController', () => { - let controller: SurveyFormController; +describe("SurveyFormController e2e", () => { + let app: INestApplication; + let prisma: PrismaService; + let module: TestingModule; - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [SurveyFormController], + beforeAll(async () => { + module = await Test.createTestingModule({ + imports: [SurveyFormModule], }).compile(); - controller = module.get(SurveyFormController); + app = module.createNestApplication(); + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + }) + ); + + const configService = app.get(ConfigService); + const apiPrefix = configService.get("API_PREFIX") || "api"; + const PORT = configService.get("APP_PORT") || 4010; + app.setGlobalPrefix(apiPrefix); + + await app.init(); + await app.listen(PORT); + + prisma = app.get(PrismaService); + pactum.request.setBaseUrl(`http://localhost:${PORT}/${apiPrefix}`); }); - it('should be defined', () => { - expect(controller).toBeDefined(); + afterAll(async () => { + await app.close(); + }); + + // beforeEach(async () => { + // // You may want to reset or seed the database before each test. + // // This is not shown in the provided code but is important for reliable testing. + // }); + + describe("createSurveyForm", () => { + it("should create a new survey form", async () => { + const createSurveyFormDto: CreateSurveyFormDto = { + userId: "a95f43a6-4bc6-4c5d-8833-8ef2958f9d98", + surveyCycleParameterId: 1, + status: SurveyStatusEnum.CREATED, + questionsJson: [ + { questionId: 1, question: "Is this a dummy question?" }, + ], + }; + + const response = await pactum + .spec() + .post("/survey-form") + .withHeaders("Content-Type", "application/json") + .withBody(createSurveyFormDto) + .expectStatus(201) + .expectJsonLike({ + message: "SurveyForm created successfully", + data: { + id: 1, + userId: "a95f43a6-4bc6-4c5d-8833-8ef2958f9d98", + surveyCycleParameterId: 1, + status: SurveyStatusEnum.CREATED, + questionsJson: [ + { questionId: 1, question: "Is this a dummy question?" }, + ], + }, + }); + + expect(response.json().data.id).toBeGreaterThan(0); + }); + }); + + describe("getSurveyFormById", () => { + it("should get a survey form by ID", async () => { + // Create a survey form in the database (you can use Prisma for this) + // const createdSurveyForm = await createSurveyFormInDatabase(); + + const response = await pactum + .spec() + .get(`/survey-form/1`) + .expectStatus(200); + + const surveyForm = response.body; + + expect(surveyForm.message).toBe("SurveyForm fetched successfully"); + expect(surveyForm.data.id).toBe(1); + }); + }); + + describe("updateSurveyFormStatus", () => { + it("should update the status of a survey form", async () => { + // Create a survey form in the database (you can use Prisma for this) + // const createdSurveyForm = await createSurveyFormInDatabase(); + + const updateData = { status: SurveyStatusEnum.PUBLISHED }; + + const response = await pactum + .spec() + .patch(`/survey-form/1`) + .withJson(updateData) + .expectStatus(200); + + const updatedSurveyForm = response.body; + + expect(updatedSurveyForm.message).toBe("SurveyForm updated successfully"); + expect(updatedSurveyForm.data.status).toBe(SurveyStatusEnum.PUBLISHED); + }); + }); + + describe("deleteSurveyForm", () => { + it("should delete a survey form by ID", async () => { + // Create a survey form in the database (you can use Prisma for this) + // const createdSurveyForm = await createSurveyFormInDatabase(); + + const response = await pactum + .spec() + .delete(`/survey-form/1`) + .expectStatus(200); + + const deletedSurveyForm = response.body; + + expect(deletedSurveyForm.message).toBe("SurveyForm deleted successfully"); + expect(deletedSurveyForm.data.id).toBe(1); + }); }); }); + +// async function createSurveyFormInDatabase() { +// // Create a survey form in the database (you can use Prisma for this) +// // Return the created survey form for testing purposes +// } diff --git a/src/survey-form/survey-form.controller.ts b/src/survey-form/survey-form.controller.ts index 559dcac..2fc53cc 100644 --- a/src/survey-form/survey-form.controller.ts +++ b/src/survey-form/survey-form.controller.ts @@ -18,7 +18,7 @@ import { UpdateSurveyFormDto, UpdateSurveyFormStatusDto, } from "./dto"; -import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; +import { getPrismaErrorStatusAndMessage } from "../utils/utils"; @Controller("survey-form") @ApiTags("survey-form") diff --git a/src/survey-form/survey-form.module.ts b/src/survey-form/survey-form.module.ts index 9887c0e..0e640b2 100644 --- a/src/survey-form/survey-form.module.ts +++ b/src/survey-form/survey-form.module.ts @@ -1,10 +1,11 @@ -import { Module } from '@nestjs/common'; -import { SurveyFormController } from './survey-form.controller'; -import { SurveyFormService } from './survey-form.service'; -import { PrismaService } from 'src/prisma/prisma.service'; +import { Module } from "@nestjs/common"; +import { SurveyFormController } from "./survey-form.controller"; +import { SurveyFormService } from "./survey-form.service"; +import { PrismaService } from "../prisma/prisma.service"; +import { ConfigService } from "@nestjs/config"; @Module({ controllers: [SurveyFormController], - providers: [SurveyFormService, PrismaService] + providers: [SurveyFormService, PrismaService, ConfigService], }) export class SurveyFormModule {} diff --git a/src/survey-form/survey-form.service.ts b/src/survey-form/survey-form.service.ts index 640db86..1c49817 100644 --- a/src/survey-form/survey-form.service.ts +++ b/src/survey-form/survey-form.service.ts @@ -1,6 +1,6 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { SurveyStatusEnum } from "@prisma/client"; -import { PrismaService } from "src/prisma/prisma.service"; +import { PrismaService } from "../prisma/prisma.service"; import { CreateSurveyFormDto, SurveyScoresForUser } from "./dto"; @Injectable() @@ -36,14 +36,16 @@ export class SurveyFormService { }); } - async findSurveyFormBySurveyCycleParameterId(paramId: number){ + async findSurveyFormBySurveyCycleParameterId(paramId: number) { const surveyForms = await this.prisma.surveyForm.findMany({ - where:{ - surveyCycleParameterId: paramId - } + where: { + surveyCycleParameterId: paramId, + }, }); - if(!surveyForms || surveyForms.length==0){ - throw new NotFoundException(`Survey Forms not found for SurveyCycleParameter with id #${paramId}`); + if (!surveyForms || surveyForms.length == 0) { + throw new NotFoundException( + `Survey Forms not found for SurveyCycleParameter with id #${paramId}` + ); } return surveyForms; } From 9671ca20d3982024515fde802115e61ec36edfdc Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Tue, 31 Oct 2023 17:59:26 +0530 Subject: [PATCH 083/175] Response tracker and survey score unit test cases --- .../response-tracker.controller.spec.ts | 220 +++++++++++++++++- .../response-tracker.controller.ts | 2 +- .../response-tracker.module.ts | 2 +- .../response-tracker.service.ts | 2 +- .../survey-score.controller.spec.ts | 134 ++++++++++- src/survey-score/survey-score.controller.ts | 14 +- src/survey-score/survey-score.module.ts | 10 +- src/survey-score/survey-score.service.ts | 13 +- 8 files changed, 348 insertions(+), 49 deletions(-) diff --git a/src/response-tracker/response-tracker.controller.spec.ts b/src/response-tracker/response-tracker.controller.spec.ts index 9a353ef..cdb9163 100644 --- a/src/response-tracker/response-tracker.controller.spec.ts +++ b/src/response-tracker/response-tracker.controller.spec.ts @@ -1,20 +1,216 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { ResponseTrackerController } from './response-tracker.controller'; -import { ResponseTrackerService } from './response-tracker.service'; +import { INestApplication, ValidationPipe } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { Test, TestingModule } from "@nestjs/testing"; +import * as pactum from "pactum"; +import { PrismaService } from "../prisma/prisma.service"; +import { ResponseTrackerModule } from "./response-tracker.module"; +import { CreateResponseTrackerDto } from "./dto"; +import { AnswerEnum } from "./enums/response-tracker.enums"; +import { ResponseTrackerStatusEnum } from "@prisma/client"; -describe('ResponseTrackerController', () => { - let controller: ResponseTrackerController; +describe("SurveyFormController e2e", () => { + let app: INestApplication; + let prisma: PrismaService; + let module: TestingModule; - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [ResponseTrackerController], - providers: [ResponseTrackerService], + beforeAll(async () => { + module = await Test.createTestingModule({ + imports: [ResponseTrackerModule], }).compile(); - controller = module.get(ResponseTrackerController); + app = module.createNestApplication(); + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + }) + ); + + const configService = app.get(ConfigService); + const apiPrefix = configService.get("API_PREFIX") || "api"; + const PORT = configService.get("APP_PORT") || 4010; + app.setGlobalPrefix(apiPrefix); + + await app.init(); + await app.listen(PORT); + + prisma = app.get(PrismaService); + pactum.request.setBaseUrl(`http://localhost:${PORT}/${apiPrefix}`); + }); + + afterAll(async () => { + await app.close(); + }); + + // beforeEach(async () => { + // // You may want to reset or seed the database before each test. + // // This is not shown in the provided code but is important for reliable testing. + // }); + + describe("Create ResponseTrackerController", () => { + it("should create a new ResponseTracker", async () => { + const createResponseTrackerDto: CreateResponseTrackerDto = { + surveyFormId: 1, + assessorId: "4d62c39b-8102-4c9c-83a9-90b18b02d405", + assesseeId: "a7e57477-9e62-4aa1-8ed6-6d58a74a394f", + responseJson: JSON.parse( + JSON.stringify([ + { + questionId: 1, + answer: `${AnswerEnum.YES}`, + }, + ]) + ), + status: ResponseTrackerStatusEnum.COMPLETED, + }; + + await pactum + .spec() + .post("/response-tracker") + .withBody(createResponseTrackerDto) + .expectStatus(201) + .expectJsonMatch({ + message: "Successfully created response tracer", + data: { + ...createResponseTrackerDto, + id: 1, + }, + }); + }); + + it("should return a 400 error if the request body is invalid", async () => { + const invalidCreateResponseTrackerDto = { + surveyFormId: "invalid", + assessorId: "invalid", + assesseeId: "invalid", + response: "invalid", + }; + + await pactum + .spec() + .post("/response-tracker") + .withBody(invalidCreateResponseTrackerDto) + .expectStatus(400); + }); }); - it('should be defined', () => { - expect(controller).toBeDefined(); + describe("GetAllResponseTrackersController", () => { + it("should get all ResponseTrackers", async () => { + const responseResponseTrackerDto = { + id: 1, + surveyFormId: 1, + assessorId: "4d62c39b-8102-4c9c-83a9-90b18b02d405", + assesseeId: "a7e57477-9e62-4aa1-8ed6-6d58a74a394f", + responseJson: JSON.parse( + JSON.stringify([ + { + questionId: 1, + answer: `${AnswerEnum.YES}`, + }, + ]) + ), + status: ResponseTrackerStatusEnum.COMPLETED, + }; + + await pactum + .spec() + .get("/response-tracker") + .expectStatus(200) + .expectJsonMatch({ + message: "Successfully fetched all response tracers", + data: [responseResponseTrackerDto], + }); + }); + }); + + describe("GetResponseTrackerByIdController", () => { + it("should get a ResponseTracker by ID", async () => { + const responseResponseTrackerDto = { + id: 1, + surveyFormId: 1, + assessorId: "4d62c39b-8102-4c9c-83a9-90b18b02d405", + assesseeId: "a7e57477-9e62-4aa1-8ed6-6d58a74a394f", + responseJson: JSON.parse( + JSON.stringify([ + { + questionId: 1, + answer: `${AnswerEnum.YES}`, + }, + ]) + ), + status: ResponseTrackerStatusEnum.COMPLETED, + }; + + await pactum + .spec() + .get("/response-tracker/1") + .expectStatus(200) + .expectJsonMatch({ + message: "Successfully fetched response tracer for id #1", + data: responseResponseTrackerDto, + }); + }); + + it("should return a 404 error if the ResponseTracker does not exist", async () => { + await pactum.spec().get("/response-tracker/999").expectStatus(404); + }); + }); + + describe("GetResponseTrackersBySurveyFormIdController", () => { + it("should get ResponseTrackers by surveyFormId", async () => { + const responseResponseTrackerDto = { + id: 1, + surveyFormId: 1, + assessorId: "4d62c39b-8102-4c9c-83a9-90b18b02d405", + assesseeId: "a7e57477-9e62-4aa1-8ed6-6d58a74a394f", + responseJson: JSON.parse( + JSON.stringify([ + { + questionId: 1, + answer: `${AnswerEnum.YES}`, + }, + ]) + ), + status: ResponseTrackerStatusEnum.COMPLETED, + }; + + await pactum + .spec() + .get("/response-tracker/survey-form/1") + .expectStatus(200) + .expectJsonMatch({ + message: "Successfully fetched response tracer for surveyFormId #1", + data: [responseResponseTrackerDto], + }); + }); + }); + + describe("GetResponseTrackersByAssessorIdController", () => { + it("should get ResponseTrackers by assessorId", async () => { + const responseResponseTrackerDto = { + id: 1, + surveyFormId: 1, + assessorId: "4d62c39b-8102-4c9c-83a9-90b18b02d405", + assesseeId: "a7e57477-9e62-4aa1-8ed6-6d58a74a394f", + responseJson: JSON.parse( + JSON.stringify([ + { + questionId: 1, + answer: `${AnswerEnum.YES}`, + }, + ]) + ), + status: ResponseTrackerStatusEnum.COMPLETED, + }; + + await pactum + .spec() + .get("/response-tracker/assessor/4d62c39b-8102-4c9c-83a9-90b18b02d405") + .expectStatus(200) + .expectJsonMatch({ + message: + "Successfully fetched response tracer for assessorId #4d62c39b-8102-4c9c-83a9-90b18b02d405", + data: [responseResponseTrackerDto], + }); + }); }); }); diff --git a/src/response-tracker/response-tracker.controller.ts b/src/response-tracker/response-tracker.controller.ts index 32db1c4..d7cefd8 100644 --- a/src/response-tracker/response-tracker.controller.ts +++ b/src/response-tracker/response-tracker.controller.ts @@ -20,7 +20,7 @@ import { ResponseTrackerDtoResponse, UpdateResponseTrackerDto, } from "./dto"; -import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; +import { getPrismaErrorStatusAndMessage } from "..//utils/utils"; @Controller("response-tracker") @ApiTags("response-tracker") diff --git a/src/response-tracker/response-tracker.module.ts b/src/response-tracker/response-tracker.module.ts index 2e79bdf..54eca4b 100644 --- a/src/response-tracker/response-tracker.module.ts +++ b/src/response-tracker/response-tracker.module.ts @@ -1,7 +1,7 @@ import { Module } from "@nestjs/common"; import { ResponseTrackerService } from "./response-tracker.service"; import { ResponseTrackerController } from "./response-tracker.controller"; -import { PrismaService } from "src/prisma/prisma.service"; +import { PrismaService } from "..//prisma/prisma.service"; @Module({ controllers: [ResponseTrackerController], diff --git a/src/response-tracker/response-tracker.service.ts b/src/response-tracker/response-tracker.service.ts index 962d7da..c97e8c7 100644 --- a/src/response-tracker/response-tracker.service.ts +++ b/src/response-tracker/response-tracker.service.ts @@ -1,5 +1,5 @@ import { Injectable, NotFoundException } from "@nestjs/common"; -import { PrismaService } from "src/prisma/prisma.service"; +import { PrismaService } from "..//prisma/prisma.service"; import { CreateResponseTrackerDto } from "./dto/create-response-tracker.dto"; import { UpdateResponseTrackerDto } from "./dto/update-response-tracker.dto"; import { IResponseTracker } from "./interfaces/response-tracker.interface"; diff --git a/src/survey-score/survey-score.controller.spec.ts b/src/survey-score/survey-score.controller.spec.ts index 234643b..81c641c 100644 --- a/src/survey-score/survey-score.controller.spec.ts +++ b/src/survey-score/survey-score.controller.spec.ts @@ -1,20 +1,130 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { SurveyScoreController } from './survey-score.controller'; -import { SurveyScoreService } from './survey-score.service'; +import { INestApplication, ValidationPipe } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { Test, TestingModule } from "@nestjs/testing"; +import * as pactum from "pactum"; +import { PrismaService } from "../prisma/prisma.service"; +import { SurveyScoreModule } from "./survey-score.module"; +import { CreateSurveyScoreDto } from "./dto"; -describe('SurveyScoreController', () => { - let controller: SurveyScoreController; +describe("SurveyFormController e2e", () => { + let app: INestApplication; + let prisma: PrismaService; + let module: TestingModule; - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [SurveyScoreController], - providers: [SurveyScoreService], + beforeAll(async () => { + module = await Test.createTestingModule({ + imports: [SurveyScoreModule], }).compile(); - controller = module.get(SurveyScoreController); + app = module.createNestApplication(); + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + }) + ); + + const configService = app.get(ConfigService); + const apiPrefix = configService.get("API_PREFIX") || "api"; + const PORT = configService.get("APP_PORT") || 4010; + app.setGlobalPrefix(apiPrefix); + + await app.init(); + await app.listen(PORT); + + prisma = app.get(PrismaService); + pactum.request.setBaseUrl(`http://localhost:${PORT}/${apiPrefix}`); + }); + + afterAll(async () => { + await app.close(); }); - it('should be defined', () => { - expect(controller).toBeDefined(); + describe("create()", () => { + it("should create a new survey score", async () => { + const createSurveyScoreDto: CreateSurveyScoreDto = { + competencyId: 1, + competencyLevelNumber: 1, + score: 30, + surveyFormId: 1, + }; + + const response = await pactum + .spec() + .post("/survey-score") + .withBody(createSurveyScoreDto) + .expectStatus(201); + + const surveyScore = JSON.parse(response.body); + + expect(surveyScore).toBeDefined(); + expect(surveyScore.surveyFormId).toEqual( + createSurveyScoreDto.surveyFormId + ); + expect(surveyScore.score).toEqual(createSurveyScoreDto.score); + expect(surveyScore.surveyFormId).toEqual( + createSurveyScoreDto.surveyFormId + ); + }); + + it("should return a 400 error if the request body is invalid", async () => { + const createSurveyScoreDto = { + surveyFormId: null, + userId: "", + answers: {}, + }; + + const response = await pactum + .spec() + .post("/survey-score") + .withBody(createSurveyScoreDto) + .expectStatus(400); + + const errorMessage = response.json().message; + + expect(errorMessage).toContain("Invalid request body"); + }); + }); +}); + +describe("findAll()", () => { + it("should get all survey scores", async () => { + const response = await pactum.spec().get("/survey-score").expectStatus(200); + + const surveyScores = JSON.parse(response.body); + + expect(surveyScores).toBeDefined(); + expect(surveyScores.data).toHaveLength(0); + }); +}); + +describe("findBySurveyFormId()", () => { + it("should get survey scores by survey form ID", async () => { + const surveyFormId = 1; + + const response = await pactum + .spec() + .get(`/survey-score/survey-form/${surveyFormId}`) + .expectStatus(200); + + const surveyScores = JSON.parse(response.body); + + expect(surveyScores).toBeDefined(); + expect(surveyScores.data).toHaveLength(0); + }); +}); + +describe("findOne()", () => { + it("should get a survey score by ID", async () => { + const id = 1; + + const response = await pactum + .spec() + .get(`/survey-score/${id}`) + .expectStatus(200); + + const surveyScore = JSON.parse(response.body); + + expect(surveyScore).toBeDefined(); + expect(surveyScore.id).toEqual(id); }); }); diff --git a/src/survey-score/survey-score.controller.ts b/src/survey-score/survey-score.controller.ts index 7bc7070..60282f5 100644 --- a/src/survey-score/survey-score.controller.ts +++ b/src/survey-score/survey-score.controller.ts @@ -12,7 +12,7 @@ import { Res, } from "@nestjs/common"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; -import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; +import { getPrismaErrorStatusAndMessage } from "../utils/utils"; import { AllSurveyScoresForUserDto, CreateSurveyScoreDto, @@ -193,13 +193,11 @@ export class SurveyScoreController { const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); - return res - .status(statusCode) - .json({ - statusCode, - message: - errorMessage || `Failed to update survey score with id #${id} `, - }); + return res.status(statusCode).json({ + statusCode, + message: + errorMessage || `Failed to update survey score with id #${id} `, + }); } } diff --git a/src/survey-score/survey-score.module.ts b/src/survey-score/survey-score.module.ts index dd185a6..9db82a8 100644 --- a/src/survey-score/survey-score.module.ts +++ b/src/survey-score/survey-score.module.ts @@ -1,6 +1,6 @@ import { Module } from "@nestjs/common"; -import { ResponseTrackerService } from "src/response-tracker/response-tracker.service"; -import { SurveyFormService } from "src/survey-form/survey-form.service"; +import { ResponseTrackerService } from "../response-tracker/response-tracker.service"; +import { SurveyFormService } from "../survey-form/survey-form.service"; import { QuestionBankModule } from "../question-bank/question-bank.module"; import { SurveyScoreController } from "./survey-score.controller"; import { SurveyScoreService } from "./survey-score.service"; @@ -8,10 +8,6 @@ import { SurveyScoreService } from "./survey-score.service"; @Module({ imports: [QuestionBankModule], controllers: [SurveyScoreController], - providers: [ - SurveyScoreService, - ResponseTrackerService, - SurveyFormService - ], + providers: [SurveyScoreService, ResponseTrackerService, SurveyFormService], }) export class SurveyScoreModule {} diff --git a/src/survey-score/survey-score.service.ts b/src/survey-score/survey-score.service.ts index 301fffd..0a1bee6 100644 --- a/src/survey-score/survey-score.service.ts +++ b/src/survey-score/survey-score.service.ts @@ -4,13 +4,12 @@ import { NotFoundException, } from "@nestjs/common"; import { ResponseTrackerStatusEnum, SurveyStatusEnum } from "@prisma/client"; -import { PrismaService } from "src/prisma/prisma.service"; -import { QuestionBankService } from "src/question-bank/question-bank.service"; -import { responseObject } from "src/response-tracker/dto"; -import { AnswerEnum } from "src/response-tracker/enums/response-tracker.enums"; -import { ResponseTrackerService } from "src/response-tracker/response-tracker.service"; -import { SurveyFormService } from "src/survey-form/survey-form.service"; -import { UserMetadataService } from "src/user-metadata/user-metadata.service"; +import { PrismaService } from "../prisma/prisma.service"; +import { QuestionBankService } from "../question-bank/question-bank.service"; +import { responseObject } from "../response-tracker/dto"; +import { AnswerEnum } from "../response-tracker/enums/response-tracker.enums"; +import { ResponseTrackerService } from "../response-tracker/response-tracker.service"; +import { SurveyFormService } from "../survey-form/survey-form.service"; import { CreateSurveyScoreDto } from "./dto/create-survey-score.dto"; import { UpdateSurveyScoreDto } from "./dto/update-survey-score.dto"; import { From e35911c706c7dcf593c1a549533869f2c42ae8bb Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 1 Nov 2023 11:42:30 +0530 Subject: [PATCH 084/175] Seeds for Mock: User, Designation, Department and Roles. --- package.json | 3 + prisma/seeds/data/mock-departments.data.ts | 27 ++++ prisma/seeds/data/mock-designation.data.ts | 47 ++++++ prisma/seeds/data/mock-roles.data.ts | 165 +++++++++++++++++++++ prisma/seeds/data/mock-users.data.ts | 144 ++++++++++++++++++ prisma/seeds/seed.ts | 36 +++++ 6 files changed, 422 insertions(+) create mode 100644 prisma/seeds/data/mock-departments.data.ts create mode 100644 prisma/seeds/data/mock-designation.data.ts create mode 100644 prisma/seeds/data/mock-roles.data.ts create mode 100644 prisma/seeds/data/mock-users.data.ts create mode 100644 prisma/seeds/seed.ts diff --git a/package.json b/package.json index 98510b7..1e5ea6f 100644 --- a/package.json +++ b/package.json @@ -86,5 +86,8 @@ ], "coverageDirectory": "../coverage", "testEnvironment": "node" + }, + "prisma": { + "seed": "ts-node ./prisma/seeds/seed.ts" } } diff --git a/prisma/seeds/data/mock-departments.data.ts b/prisma/seeds/data/mock-departments.data.ts new file mode 100644 index 0000000..c0f2673 --- /dev/null +++ b/prisma/seeds/data/mock-departments.data.ts @@ -0,0 +1,27 @@ +export const departments = [ + { + id: 1, + name: "Software Development", + description: "Responsible for software development and coding.", + }, + { + id: 2, + name: "IT Operations", + description: "Manages IT infrastructure and operations.", + }, + { + id: 3, + name: "Quality Assurance", + description: "Ensures the quality of software products.", + }, + { + id: 4, + name: "Network and Security", + description: "Handles network administration and cybersecurity.", + }, + { + id: 5, + name: "Data Analytics", + description: "Analyzes and extracts insights from data.", + }, +]; diff --git a/prisma/seeds/data/mock-designation.data.ts b/prisma/seeds/data/mock-designation.data.ts new file mode 100644 index 0000000..e4e71e2 --- /dev/null +++ b/prisma/seeds/data/mock-designation.data.ts @@ -0,0 +1,47 @@ +export const designations = [ + { + id: 1, + name: "CEO", + description: "Chief Executive Officer", + }, + { + id: 2, + name: "CTO", + description: "Chief Technology Officer", + }, + { + id: 3, + name: "CIO", + description: "Chief Information Officer", + }, + { + id: 4, + name: "VP of Engineering", + description: "Vice President of Engineering", + }, + { + id: 5, + name: "Software Architect", + description: "Senior Software Architect", + }, + { + id: 6, + name: "Software Engineer", + description: "Software Development Engineer", + }, + { + id: 7, + name: "QA Engineer", + description: "Quality Assurance Engineer", + }, + { + id: 8, + name: "Systems Administrator", + description: "IT Systems Administrator", + }, + { + id: 9, + name: "Network Administrator", + description: "Network Systems Administrator", + } +]; diff --git a/prisma/seeds/data/mock-roles.data.ts b/prisma/seeds/data/mock-roles.data.ts new file mode 100644 index 0000000..004f32c --- /dev/null +++ b/prisma/seeds/data/mock-roles.data.ts @@ -0,0 +1,165 @@ +export const roles = [ + //For CEO + { + name: "Corporate Strategy", + description: + "Developing and overseeing the organization's strategic plan to achieve its objectives.", + }, + { + name: "Financial Management", + description: + "Managing the company's finances, including budgeting, financial planning, and financial risk management.", + }, + { + name: "Talent Management", + description: + "Attracting, retaining, and developing top talent within the organization.", + }, + + //For CTO + { + name: "Technology Visionary", + description: + "The CTO defines the organization's long-term technology strategy, ensuring alignment with business goals and emerging industry trends.", + }, + { + name: "Innovation Leader", + description: + "Responsible for fostering innovation, the CTO drives research and development efforts to create cutting-edge solutions and stay competitive.", + }, + { + name: "Technical Advisor", + description: + "Provides technical expertise to guide decision-making, assess risks, and choose the right technologies for projects.", + }, + { + name: "Team Builder", + description: + "Recruits and manages top technical talent, fostering a culture of excellence and professional growth within the organization.", + }, + + //CIO + { + name: "Cybersecurity Oversight", + description: + "The CIO plays a critical role in safeguarding the organization's digital assets, implementing security measures, and mitigating cybersecurity threats.", + }, + { + name: "IT Governance", + description: + "The CIO establishes IT governance frameworks, policies, and procedures to ensure efficient and compliant IT operations within the organization.", + }, + { + name: "Resource Management", + description: + "CIOs manage IT budgets, resources, and teams, optimizing technology investments and ensuring IT projects are delivered on time and within budget.", + }, + + //VP of Engineering + { + name: "Team Leadership", + description: "Managing and mentoring engineering teams.", + }, + { + name: "Project Management", + description: "Overseeing project execution and delivery.", + }, + { + name: "Resource Allocation", + description: "Efficiently allocating resources and budgets.", + }, + { + name: "Innovation", + description: + "Promoting innovation and staying current with technology trends.", + }, + + //Software Architect + { + name: "System Design", + description: "Creating high-level system architecture and design.", + }, + { + name: "Technology Selection", + description: "Choosing the right tech stack for projects.", + }, + { + name: "Quality Assurance", + description: "Ensuring software meets quality standards.", + }, + { + name: "Performance Optimization", + description: "Improving system performance.", + }, + { + name: "Team Guidance", + description: "Providing technical leadership and mentoring.", + }, + + //Software Engineer + { + name: "Software Development", + description: "Writing, testing, and maintaining software applications.", + }, + { + name: "Problem Solving", + description: "Identifying and resolving technical challenges.", + }, + { + name: "Collaboration", + description: "Working with cross-functional teams to deliver projects.", + }, + + //QA Engineer + { + name: "Test Planning and Strategy", + description: + "Creating test plans and strategies to ensure comprehensive testing of software.", + }, + { + name: "Test Execution", + description: + "Performing testing, identifying defects, and ensuring software quality.", + }, + { + name: "Automation Testing", + description: + "Developing and maintaining automated test scripts to improve efficiency and coverage.", + }, + + //Systems Administrator + { + name: "System Maintenance", + description: + "Managing and maintaining IT infrastructure to ensure it runs smoothly.", + }, + { + name: "System Security Management", + description: + "Protecting systems and data from threats and vulnerabilities.", + }, + { + name: "User Support", + description: + "Providing technical assistance and troubleshooting for employees.", + }, + + //Network Administrator + { + name: "Network Configuration and Maintenance", + description: "Setting up and maintaining network infrastructure", + }, + { + name: "Security Management", + description: + "Protecting the network from cyber threats and vulnerabilities", + }, +// { +// name: "User Support", +// description: "Assisting users with network-related issues", +// }, + { + name: "Network Performance Optimization", + description: "Ensuring network efficiency and reliability", + }, +]; diff --git a/prisma/seeds/data/mock-users.data.ts b/prisma/seeds/data/mock-users.data.ts new file mode 100644 index 0000000..e1ae69f --- /dev/null +++ b/prisma/seeds/data/mock-users.data.ts @@ -0,0 +1,144 @@ +import { UserRolesEnum } from "@prisma/client"; + +export const users = [ + { + email: "amit.sharma@example.com", + role: UserRolesEnum.ADMIN, + userName: "Amit Sharma", + password: "password1", + designation: "Manager", + }, + { + email: "neha.patil@example.com", + role: UserRolesEnum.CONSUMER, + userName: "Neha Patil", + password: "password2", + designation: "Employee", + }, + { + email: "rajesh.kumar@example.com", + role: UserRolesEnum.CONSUMER, + userName: "Rajesh Kumar", + password: "password3", + designation: "Employee", + }, + { + email: "priya.jain@example.com", + role: UserRolesEnum.CONSUMER, + userName: "Priya Jain", + password: "password4", + designation: "Employee", + }, + { + email: "arjun.singh@example.com", + role: UserRolesEnum.CONSUMER, + userName: "Arjun Singh", + password: "password5", + designation: "Employee", + }, + { + email: "deepa.mishra@example.com", + role: UserRolesEnum.CONSUMER, + userName: "Deepa Mishra", + password: "password6", + designation: "Employee", + }, + { + email: "rahul.verma@example.com", + role: UserRolesEnum.CONSUMER, + userName: "Rahul Verma", + password: "password7", + designation: "Employee", + }, + { + email: "anita.sen@example.com", + role: UserRolesEnum.CONSUMER, + userName: "Anita Sen", + password: "password8", + designation: "Employee", + }, + { + email: "mohit.gupta@example.com", + role: UserRolesEnum.CONSUMER, + userName: "Mohit Gupta", + password: "password9", + designation: "Employee", + }, + { + email: "pooja.rawat@example.com", + role: UserRolesEnum.CONSUMER, + userName: "Pooja Rawat", + password: "password10", + designation: "Employee", + }, + { + email: "anurag.sharma@example.com", + role: UserRolesEnum.CONSUMER, + userName: "Anurag Sharma", + password: "password11", + designation: "Employee", + }, + { + email: "meera.rani@example.com", + role: UserRolesEnum.CONSUMER, + userName: "Meera Rani", + password: "password12", + designation: "Employee", + }, + { + email: "vikas.patel@example.com", + role: UserRolesEnum.CONSUMER, + userName: "Vikas Patel", + password: "password13", + designation: "Employee", + }, + { + email: "divya.malik@example.com", + role: UserRolesEnum.CONSUMER, + userName: "Divya Malik", + password: "password14", + designation: "Employee", + }, + { + email: "rajiv.khanna@example.com", + role: UserRolesEnum.CONSUMER, + userName: "Rajiv Khanna", + password: "password15", + designation: "Employee", + }, + { + email: "swati.shah@example.com", + role: UserRolesEnum.CONSUMER, + userName: "Swati Shah", + password: "password16", + designation: "Employee", + }, + { + email: "manish.agrawal@example.com", + role: UserRolesEnum.CONSUMER, + userName: "Manish Agrawal", + password: "password17", + designation: "Employee", + }, + { + email: "pallavi.reddy@example.com", + role: UserRolesEnum.CONSUMER, + userName: "Pallavi Reddy", + password: "password18", + designation: "Employee", + }, + { + email: "deepak.mishra@example.com", + role: UserRolesEnum.CONSUMER, + userName: "Deepak Mishra", + password: "password19", + designation: "Employee", + }, + { + email: "komal.choudhary@example.com", + role: UserRolesEnum.CONSUMER, + userName: "Komal Choudhary", + password: "password20", + designation: "Employee", + }, +]; diff --git a/prisma/seeds/seed.ts b/prisma/seeds/seed.ts new file mode 100644 index 0000000..153a79f --- /dev/null +++ b/prisma/seeds/seed.ts @@ -0,0 +1,36 @@ +import { PrismaClient } from "@prisma/client"; +import { users } from "./data/mock-users.data"; +import { roles } from "./data/mock-roles.data"; +import { departments } from "./data/mock-departments.data"; +import { designations } from "./data/mock-designation.data"; + +const prisma = new PrismaClient(); + +async function seed() { + await prisma.$transaction([ + prisma.user.createMany({ + data: users, + }), + + prisma.role.createMany({ + data: roles, + }), + + prisma.department.createMany({ + data: departments, + }), + + prisma.designation.createMany({ + data: designations, + }), + ]); +} + +seed() + .catch((e) => { + console.log(e); + process.exit(1); + }) + .finally(() => { + prisma.$disconnect(); + }); From aa848208488a7ee728c490514c50a3d908a481d0 Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Wed, 1 Nov 2023 13:10:49 +0530 Subject: [PATCH 085/175] Seeding for Levels, CompetencyLevels, Competencies, Roles to competencies and Competencies to competency levels --- prisma/seeds/data/mock-competencies.data.ts | 22 +++++++ ...ock-competenciesToCompetencyLevels.data.ts | 58 +++++++++++++++++++ .../seeds/data/mock-competencyLevels.data.ts | 27 +++++++++ prisma/seeds/data/mock-levels.data.ts | 22 +++++++ .../data/mock-rolesToCompetencies.data.ts | 18 ++++++ prisma/seeds/seed.ts | 51 +++++++++++++--- 6 files changed, 190 insertions(+), 8 deletions(-) create mode 100644 prisma/seeds/data/mock-competencies.data.ts create mode 100644 prisma/seeds/data/mock-competenciesToCompetencyLevels.data.ts create mode 100644 prisma/seeds/data/mock-competencyLevels.data.ts create mode 100644 prisma/seeds/data/mock-levels.data.ts create mode 100644 prisma/seeds/data/mock-rolesToCompetencies.data.ts diff --git a/prisma/seeds/data/mock-competencies.data.ts b/prisma/seeds/data/mock-competencies.data.ts new file mode 100644 index 0000000..6b98620 --- /dev/null +++ b/prisma/seeds/data/mock-competencies.data.ts @@ -0,0 +1,22 @@ +export const competencies = [ + { + name: "Strategic Planning", + description: + "Ability to formulate and execute long-term plans aligned with the organization's goals.", + }, + { + name: "Data Analysis", + description: + "Proficiency in analyzing data and market trends to inform strategic decisions.", + }, + { + name: "Innovation Management", + description: + "Skill in fostering innovation and adapting to technology advancements.", + }, + { + name: "Change Leadership", + description: + "Capacity to drive and manage organizational change in a dynamic IT environment.", + }, +]; diff --git a/prisma/seeds/data/mock-competenciesToCompetencyLevels.data.ts b/prisma/seeds/data/mock-competenciesToCompetencyLevels.data.ts new file mode 100644 index 0000000..c187418 --- /dev/null +++ b/prisma/seeds/data/mock-competenciesToCompetencyLevels.data.ts @@ -0,0 +1,58 @@ +export const competenciesToCompetencyLevels = [ + { + competencyId: 1, // Associated with "Strategic Planning" + competencyLevelId: 1, // Associated with "Basic Proficiency" + }, + { + competencyId: 1, // Associated with "Strategic Planning" + competencyLevelId: 2, // Associated with "Intermediate Skill" + }, + { + competencyId: 1, // Associated with "Strategic Planning" + competencyLevelId: 3, // Associated with "Advanced Expertise" + }, + { + competencyId: 1, // Associated with "Strategic Planning" + competencyLevelId: 4, // Associated with "Strategic Leadership" + }, + { + competencyId: 1, // Associated with "Strategic Planning" + competencyLevelId: 5, // Associated with "Industry Thought Leader" + }, + { + competencyId: 2, // Associated with "Data Analysis" + competencyLevelId: 1, // Associated with "Basic Proficiency" + }, + { + competencyId: 2, // Associated with "Data Analysis" + competencyLevelId: 2, // Associated with "Intermediate Skill" + }, + { + competencyId: 2, // Associated with "Data Analysis" + competencyLevelId: 3, // Associated with "Advanced Expertise" + }, + { + competencyId: 3, // Associated with "Innovation Management" + competencyLevelId: 1, // Associated with "Basic Proficiency" + }, + { + competencyId: 3, // Associated with "Innovation Management" + competencyLevelId: 2, // Associated with "Intermediate Skill" + }, + { + competencyId: 4, // Associated with "Change Leadership" + competencyLevelId: 1, // Associated with "Basic Proficiency" + }, + { + competencyId: 4, // Associated with "Change Leadership" + competencyLevelId: 2, // Associated with "Intermediate Skill" + }, + { + competencyId: 4, // Associated with "Change Leadership" + competencyLevelId: 3, // Associated with "Advanced Expertise" + }, + { + competencyId: 4, // Associated with "Change Leadership" + competencyLevelId: 4, // Associated with "Strategic Leadership" + }, +]; diff --git a/prisma/seeds/data/mock-competencyLevels.data.ts b/prisma/seeds/data/mock-competencyLevels.data.ts new file mode 100644 index 0000000..d56bb01 --- /dev/null +++ b/prisma/seeds/data/mock-competencyLevels.data.ts @@ -0,0 +1,27 @@ +export const competencyLevels = [ + { + name: "Basic Proficiency", + description: "Competency Level 1", + levelNumber: 1, // Associated with Level 1 + }, + { + name: "Intermediate Skill", + description: "Competency Level 2", + levelNumber: 2, // Associated with Level 2 + }, + { + name: "Advanced Expertise", + description: "Competency Level 3", + levelNumber: 3, // Associated with Level 3 + }, + { + name: "Strategic Leadership", + description: "Competency Level 4", + levelNumber: 4, // Associated with Level 4 + }, + { + name: "Industry Thought Leader", + description: "Competency Level 5", + levelNumber: 5, // Associated with Level 5 + }, +]; diff --git a/prisma/seeds/data/mock-levels.data.ts b/prisma/seeds/data/mock-levels.data.ts new file mode 100644 index 0000000..2cc092d --- /dev/null +++ b/prisma/seeds/data/mock-levels.data.ts @@ -0,0 +1,22 @@ +export const levels = [ + { + levelNumber: 1, + description: "Basic Proficiency", + }, + { + levelNumber: 2, + description: "Intermediate Skill", + }, + { + levelNumber: 3, + description: "Advanced Expertise", + }, + { + levelNumber: 4, + description: "Strategic Leadership", + }, + { + levelNumber: 5, + description: "Industry Thought Leader", + }, +]; diff --git a/prisma/seeds/data/mock-rolesToCompetencies.data.ts b/prisma/seeds/data/mock-rolesToCompetencies.data.ts new file mode 100644 index 0000000..9a44237 --- /dev/null +++ b/prisma/seeds/data/mock-rolesToCompetencies.data.ts @@ -0,0 +1,18 @@ +export const rolesToCompetencies = [ + { + roleId: 1, // Linking to the first role (Network Configuration and Maintenance) + competencyId: 1, // Linking to the first competency (Strategic Planning) + }, + { + roleId: 2, // Linking to the second role (Security Management) + competencyId: 2, // Linking to the second competency (Data Analysis) + }, + { + roleId: 3, // Linking to the third role (User Support) + competencyId: 3, // Linking to the third competency (Innovation Management) + }, + { + roleId: 4, // Linking to the fourth role (Performance Optimization) + competencyId: 4, // Linking to the fourth competency (Change Leadership) + }, +]; diff --git a/prisma/seeds/seed.ts b/prisma/seeds/seed.ts index 153a79f..d70e14c 100644 --- a/prisma/seeds/seed.ts +++ b/prisma/seeds/seed.ts @@ -1,27 +1,62 @@ import { PrismaClient } from "@prisma/client"; -import { users } from "./data/mock-users.data"; -import { roles } from "./data/mock-roles.data"; +import { competencies } from "./data/mock-competencies.data"; +import { competencyLevels } from "./data/mock-competencyLevels.data"; import { departments } from "./data/mock-departments.data"; import { designations } from "./data/mock-designation.data"; +import { levels } from "./data/mock-levels.data"; +import { roles } from "./data/mock-roles.data"; +import { users } from "./data/mock-users.data"; +import { competenciesToCompetencyLevels } from "./data/mock-competenciesToCompetencyLevels.data"; +import { rolesToCompetencies } from "./data/mock-rolesToCompetencies.data"; const prisma = new PrismaClient(); +// 1- Department ------ +// 2- Designation -------- +// 3- Role --------- +// 4- Level ========= +// 5- Competency Level ========= +// 6- Competency ======== +// 7- Competency to Competency Level ========= +// 8- RoleToCompetency ============ +// 9- User ------ + async function seed() { await prisma.$transaction([ - prisma.user.createMany({ - data: users, + prisma.department.createMany({ + data: departments, + }), + + prisma.designation.createMany({ + data: designations, }), prisma.role.createMany({ data: roles, }), - prisma.department.createMany({ - data: departments, + prisma.level.createMany({ + data: levels, }), - prisma.designation.createMany({ - data: designations, + prisma.competencyLevel.createMany({ + data: competencyLevels, + }), + + prisma.competency.createMany({ + data: competencies, + }), + + prisma.competencyToCompetencyLevel.createMany({ + data: competenciesToCompetencyLevels, + }), + + prisma.roleToCompetency.createMany({ + data: rolesToCompetencies, + }), + + prisma.user.createMany({ + data: users, }), ]); } From a52160eff575551a523a732c4d585dd36f55bcd5 Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Wed, 1 Nov 2023 14:34:46 +0530 Subject: [PATCH 086/175] Updated seed data --- prisma/seeds/data/mock-competencies.data.ts | 40 +++ ...ock-competenciesToCompetencyLevels.data.ts | 107 +++++++ prisma/seeds/data/mock-roles.data.ts | 84 ++++- .../data/mock-rolesToCompetencies.data.ts | 300 +++++++++++++++++- prisma/seeds/data/mock-users.data.ts | 140 +++++--- prisma/seeds/seed.ts | 19 +- 6 files changed, 615 insertions(+), 75 deletions(-) diff --git a/prisma/seeds/data/mock-competencies.data.ts b/prisma/seeds/data/mock-competencies.data.ts index 6b98620..4599204 100644 --- a/prisma/seeds/data/mock-competencies.data.ts +++ b/prisma/seeds/data/mock-competencies.data.ts @@ -1,22 +1,62 @@ export const competencies = [ { + id: 1, name: "Strategic Planning", description: "Ability to formulate and execute long-term plans aligned with the organization's goals.", }, { + id: 2, name: "Data Analysis", description: "Proficiency in analyzing data and market trends to inform strategic decisions.", }, { + id: 3, name: "Innovation Management", description: "Skill in fostering innovation and adapting to technology advancements.", }, { + id: 4, name: "Change Leadership", description: "Capacity to drive and manage organizational change in a dynamic IT environment.", }, + { + id: 5, + name: "Project Management", + description: + "Effective planning, execution, and supervision of projects to achieve desired outcomes.", + }, + { + id: 6, + name: "Team Collaboration", + description: + "Collaborative work with cross-functional teams to deliver projects efficiently.", + }, + { + id: 7, + name: "Technical Expertise", + description: + "In-depth technical knowledge and proficiency in relevant tools and technologies.", + }, + { + id: 8, + name: "Quality Assurance", + description: + "Ensuring the quality of software products through comprehensive testing and validation.", + }, + { + id: 9, + name: "Problem Solving", + description: + "Identifying and resolving complex technical challenges and issues.", + }, + { + id: 10, + name: "Strategic Leadership", + description: + "Leading and directing organizational strategy to achieve long-term goals and success.", + }, ]; diff --git a/prisma/seeds/data/mock-competenciesToCompetencyLevels.data.ts b/prisma/seeds/data/mock-competenciesToCompetencyLevels.data.ts index c187418..1b5f974 100644 --- a/prisma/seeds/data/mock-competenciesToCompetencyLevels.data.ts +++ b/prisma/seeds/data/mock-competenciesToCompetencyLevels.data.ts @@ -1,4 +1,5 @@ export const competenciesToCompetencyLevels = [ + // Strategic Planning { competencyId: 1, // Associated with "Strategic Planning" competencyLevelId: 1, // Associated with "Basic Proficiency" @@ -19,6 +20,8 @@ export const competenciesToCompetencyLevels = [ competencyId: 1, // Associated with "Strategic Planning" competencyLevelId: 5, // Associated with "Industry Thought Leader" }, + + // Data Analysis { competencyId: 2, // Associated with "Data Analysis" competencyLevelId: 1, // Associated with "Basic Proficiency" @@ -31,6 +34,8 @@ export const competenciesToCompetencyLevels = [ competencyId: 2, // Associated with "Data Analysis" competencyLevelId: 3, // Associated with "Advanced Expertise" }, + + // Innovation Management { competencyId: 3, // Associated with "Innovation Management" competencyLevelId: 1, // Associated with "Basic Proficiency" @@ -39,6 +44,8 @@ export const competenciesToCompetencyLevels = [ competencyId: 3, // Associated with "Innovation Management" competencyLevelId: 2, // Associated with "Intermediate Skill" }, + + // Change Leadership { competencyId: 4, // Associated with "Change Leadership" competencyLevelId: 1, // Associated with "Basic Proficiency" @@ -55,4 +62,104 @@ export const competenciesToCompetencyLevels = [ competencyId: 4, // Associated with "Change Leadership" competencyLevelId: 4, // Associated with "Strategic Leadership" }, + + // Project Management + { + competencyId: 5, // Associated with "Project Management" + competencyLevelId: 1, // Associated with "Basic Proficiency" + }, + { + competencyId: 5, // Associated with "Project Management" + competencyLevelId: 2, // Associated with "Intermediate Skill" + }, + { + competencyId: 5, // Associated with "Project Management" + competencyLevelId: 3, // Associated with "Advanced Expertise" + }, + { + competencyId: 5, // Associated with "Project Management" + competencyLevelId: 4, // Associated with "Strategic Leadership" + }, + { + competencyId: 5, // Associated with "Project Management" + competencyLevelId: 5, // Associated with "Industry Thought Leader" + }, + + // Team Collaboration + { + competencyId: 6, // Associated with "Team Collaboration" + competencyLevelId: 1, // Associated with "Basic Proficiency" + }, + { + competencyId: 6, // Associated with "Team Collaboration" + competencyLevelId: 2, // Associated with "Intermediate Skill" + }, + { + competencyId: 6, // Associated with "Team Collaboration" + competencyLevelId: 3, // Associated with "Advanced Expertise" + }, + + // Technical Expertise + { + competencyId: 7, // Associated with "Technical Expertise" + competencyLevelId: 1, // Associated with "Basic Proficiency" + }, + { + competencyId: 7, // Associated with "Technical Expertise" + competencyLevelId: 2, // Associated with "Intermediate Skill" + }, + { + competencyId: 7, // Associated with "Technical Expertise" + competencyLevelId: 3, // Associated with "Advanced Expertise" + }, + + // Quality Assurance + { + competencyId: 8, // Associated with "Quality Assurance" + competencyLevelId: 1, // Associated with "Basic Proficiency" + }, + { + competencyId: 8, // Associated with "Quality Assurance" + competencyLevelId: 2, // Associated with "Intermediate Skill" + }, + { + competencyId: 8, // Associated with "Quality Assurance" + competencyLevelId: 3, // Associated with "Advanced Expertise" + }, + + // Problem Solving + { + competencyId: 9, // Associated with "Problem Solving" + competencyLevelId: 1, // Associated with "Basic Proficiency" + }, + { + competencyId: 9, // Associated with "Problem Solving" + competencyLevelId: 2, // Associated with "Intermediate Skill" + }, + { + competencyId: 9, // Associated with "Problem Solving" + competencyLevelId: 3, // Associated with "Advanced Expertise" + }, + + // Strategic Leadership + { + competencyId: 10, // Associated with "Strategic Leadership" + competencyLevelId: 1, // Associated with "Basic Proficiency" + }, + { + competencyId: 10, // Associated with "Strategic Leadership" + competencyLevelId: 2, // Associated with "Intermediate Skill" + }, + { + competencyId: 10, // Associated with "Strategic Leadership" + competencyLevelId: 3, // Associated with "Advanced Expertise" + }, + { + competencyId: 10, // Associated with "Strategic Leadership" + competencyLevelId: 4, // Associated with "Strategic Leadership" + }, + { + competencyId: 10, // Associated with "Strategic Leadership" + competencyLevelId: 5, // Associated with "Industry Thought Leader" + }, ]; diff --git a/prisma/seeds/data/mock-roles.data.ts b/prisma/seeds/data/mock-roles.data.ts index 004f32c..6d16513 100644 --- a/prisma/seeds/data/mock-roles.data.ts +++ b/prisma/seeds/data/mock-roles.data.ts @@ -1,165 +1,223 @@ export const roles = [ - //For CEO + // For CEO { + id: 1, name: "Corporate Strategy", description: "Developing and overseeing the organization's strategic plan to achieve its objectives.", + designationId: 1, // CEO }, { + id: 2, name: "Financial Management", description: "Managing the company's finances, including budgeting, financial planning, and financial risk management.", + designationId: 1, // CEO }, { + id: 3, name: "Talent Management", description: "Attracting, retaining, and developing top talent within the organization.", + designationId: 1, // CEO }, - //For CTO + // For CTO { + id: 4, name: "Technology Visionary", description: "The CTO defines the organization's long-term technology strategy, ensuring alignment with business goals and emerging industry trends.", + designationId: 2, // CTO }, { + id: 5, name: "Innovation Leader", description: "Responsible for fostering innovation, the CTO drives research and development efforts to create cutting-edge solutions and stay competitive.", + designationId: 2, // CTO }, { + id: 6, name: "Technical Advisor", description: "Provides technical expertise to guide decision-making, assess risks, and choose the right technologies for projects.", + designationId: 2, // CTO }, { + id: 7, name: "Team Builder", description: "Recruits and manages top technical talent, fostering a culture of excellence and professional growth within the organization.", + designationId: 2, // CTO }, - //CIO + // CIO { + id: 8, name: "Cybersecurity Oversight", description: "The CIO plays a critical role in safeguarding the organization's digital assets, implementing security measures, and mitigating cybersecurity threats.", + designationId: 3, // CIO }, { + id: 9, name: "IT Governance", description: "The CIO establishes IT governance frameworks, policies, and procedures to ensure efficient and compliant IT operations within the organization.", + designationId: 3, // CIO }, { + id: 10, name: "Resource Management", description: "CIOs manage IT budgets, resources, and teams, optimizing technology investments and ensuring IT projects are delivered on time and within budget.", + designationId: 3, // CIO }, - //VP of Engineering + // VP of Engineering { + id: 11, name: "Team Leadership", description: "Managing and mentoring engineering teams.", + designationId: 4, // VP of Engineering }, { + id: 12, name: "Project Management", description: "Overseeing project execution and delivery.", + designationId: 4, // VP of Engineering }, { + id: 13, name: "Resource Allocation", description: "Efficiently allocating resources and budgets.", + designationId: 4, // VP of Engineering }, { + id: 14, name: "Innovation", description: "Promoting innovation and staying current with technology trends.", + designationId: 4, // VP of Engineering }, - //Software Architect + // Software Architect { + id: 15, name: "System Design", description: "Creating high-level system architecture and design.", + designationId: 5, // Software Architect }, { + id: 16, name: "Technology Selection", description: "Choosing the right tech stack for projects.", + designationId: 5, // Software Architect }, { + id: 17, name: "Quality Assurance", description: "Ensuring software meets quality standards.", + designationId: 5, // Software Architect }, { + id: 18, name: "Performance Optimization", description: "Improving system performance.", + designationId: 5, // Software Architect }, { + id: 19, name: "Team Guidance", description: "Providing technical leadership and mentoring.", + designationId: 5, // Software Architect }, - //Software Engineer + // Software Engineer { + id: 20, name: "Software Development", description: "Writing, testing, and maintaining software applications.", + designationId: 6, // Software Engineer }, { + id: 21, name: "Problem Solving", description: "Identifying and resolving technical challenges.", + designationId: 6, // Software Engineer }, { + id: 22, name: "Collaboration", description: "Working with cross-functional teams to deliver projects.", + designationId: 6, // Software Engineer }, - //QA Engineer + // QA Engineer { + id: 23, name: "Test Planning and Strategy", description: "Creating test plans and strategies to ensure comprehensive testing of software.", + designationId: 7, // QA Engineer }, { + id: 24, name: "Test Execution", description: "Performing testing, identifying defects, and ensuring software quality.", + designationId: 7, // QA Engineer }, { + id: 25, name: "Automation Testing", description: "Developing and maintaining automated test scripts to improve efficiency and coverage.", + designationId: 7, // QA Engineer }, - //Systems Administrator + // Systems Administrator { + id: 26, name: "System Maintenance", description: "Managing and maintaining IT infrastructure to ensure it runs smoothly.", + designationId: 8, // Systems Administrator }, { + id: 27, name: "System Security Management", description: "Protecting systems and data from threats and vulnerabilities.", + designationId: 8, // Systems Administrator }, { + id: 28, name: "User Support", description: "Providing technical assistance and troubleshooting for employees.", + designationId: 8, // Systems Administrator }, - //Network Administrator + // Network Administrator { + id: 29, name: "Network Configuration and Maintenance", description: "Setting up and maintaining network infrastructure", + designationId: 9, // Network Administrator }, { + id: 30, name: "Security Management", description: "Protecting the network from cyber threats and vulnerabilities", + designationId: 9, // Network Administrator }, -// { -// name: "User Support", -// description: "Assisting users with network-related issues", -// }, { + id: 31, name: "Network Performance Optimization", description: "Ensuring network efficiency and reliability", + designationId: 9, // Network Administrator }, ]; diff --git a/prisma/seeds/data/mock-rolesToCompetencies.data.ts b/prisma/seeds/data/mock-rolesToCompetencies.data.ts index 9a44237..1393d82 100644 --- a/prisma/seeds/data/mock-rolesToCompetencies.data.ts +++ b/prisma/seeds/data/mock-rolesToCompetencies.data.ts @@ -1,18 +1,302 @@ export const rolesToCompetencies = [ + // For CEO - Corporate Strategy { - roleId: 1, // Linking to the first role (Network Configuration and Maintenance) - competencyId: 1, // Linking to the first competency (Strategic Planning) + roleId: 1, + competencyId: 1, // Strategic Planning }, { - roleId: 2, // Linking to the second role (Security Management) - competencyId: 2, // Linking to the second competency (Data Analysis) + roleId: 1, + competencyId: 3, // Innovation Management }, + // For CEO - Financial Management { - roleId: 3, // Linking to the third role (User Support) - competencyId: 3, // Linking to the third competency (Innovation Management) + roleId: 2, + competencyId: 10, // Strategic Leadership }, { - roleId: 4, // Linking to the fourth role (Performance Optimization) - competencyId: 4, // Linking to the fourth competency (Change Leadership) + roleId: 2, + competencyId: 3, // Innovation Management + }, + // For CEO - Talent Management + { + roleId: 3, + competencyId: 6, // Team Collaboration + }, + { + roleId: 3, + competencyId: 10, // Strategic Leadership + }, + + // For CTO - Technology Visionary + { + roleId: 4, + competencyId: 1, // Strategic Planning + }, + { + roleId: 4, + competencyId: 3, // Innovation Management + }, + { + roleId: 4, + competencyId: 7, // Technical Expertise + }, + { + roleId: 4, + competencyId: 6, // Team Collaboration + }, + // For CTO - Innovation Leader + { + roleId: 5, + competencyId: 1, // Strategic Planning + }, + { + roleId: 5, + competencyId: 3, // Innovation Management + }, + { + roleId: 5, + competencyId: 7, // Technical Expertise + }, + { + roleId: 5, + competencyId: 6, // Team Collaboration + }, + // For CTO - Technical Advisor + { + roleId: 6, + competencyId: 1, // Strategic Planning + }, + { + roleId: 6, + competencyId: 7, // Technical Expertise + }, + { + roleId: 6, + competencyId: 9, // Problem Solving + }, + // For CTO - Team Builder + { + roleId: 7, + competencyId: 6, // Team Collaboration + }, + { + roleId: 7, + competencyId: 10, // Strategic Leadership + }, + // For CIO - Cybersecurity Oversight + { + roleId: 8, + competencyId: 4, // Change Leadership + }, + { + roleId: 8, + competencyId: 2, // Data Analysis + }, + // For CIO - IT Governance + { + roleId: 9, + competencyId: 1, // Strategic Planning + }, + { + roleId: 9, + competencyId: 7, // Technical Expertise + }, + // For CIO - Resource Management + { + roleId: 10, + competencyId: 10, // Strategic Leadership + }, + { + roleId: 10, + competencyId: 7, // Technical Expertise + }, + // For VP of Engineering - Team Leadership + { + roleId: 11, + competencyId: 6, // Team Collaboration + }, + { + roleId: 11, + competencyId: 10, // Strategic Leadership + }, + // For VP of Engineering - Project Management + { + roleId: 12, + competencyId: 10, // Strategic Leadership + }, + { + roleId: 12, + competencyId: 6, // Team Collaboration + }, + // For VP of Engineering - Resource Allocation + { + roleId: 13, + competencyId: 10, // Strategic Leadership + }, + { + roleId: 13, + competencyId: 7, // Technical Expertise + }, + // For VP of Engineering - Innovation + { + roleId: 14, + competencyId: 3, // Innovation Management + }, + { + roleId: 14, + competencyId: 6, // Team Collaboration + }, + // For Software Architect - System Design + { + roleId: 15, + competencyId: 10, // Strategic Leadership + }, + { + roleId: 15, + competencyId: 7, // Technical Expertise + }, + // For Software Architect - Technology Selection + { + roleId: 16, + competencyId: 7, // Technical Expertise + }, + { + roleId: 16, + competencyId: 6, // Team Collaboration + }, + // For Software Architect - Quality Assurance + { + roleId: 17, + competencyId: 8, // Quality Assurance + }, + { + roleId: 17, + competencyId: 9, // Problem Solving + }, + // For Software Architect - Performance Optimization + { + roleId: 18, + competencyId: 4, // Change Leadership + }, + { + roleId: 18, + competencyId: 7, // Technical Expertise + }, + // For Software Architect - Team Guidance + { + roleId: 19, + competencyId: 6, // Team Collaboration + }, + { + roleId: 19, + competencyId: 7, // Technical Expertise + }, + // For Software Engineer - Software Development + { + roleId: 20, + competencyId: 6, // Team Collaboration + }, + { + roleId: 20, + competencyId: 9, // Problem Solving + }, + // For Software Engineer - Problem Solving + { + roleId: 21, + competencyId: 9, // Problem Solving + }, + { + roleId: 21, + competencyId: 6, // Team Collaboration + }, + // For Software Engineer - Collaboration + { + roleId: 22, + competencyId: 6, // Team Collaboration + }, + { + roleId: 22, + competencyId: 10, // Strategic Leadership + }, + // For QA Engineer - Test Planning and Strategy + { + roleId: 23, + competencyId: 1, // Strategic Planning + }, + { + roleId: 23, + competencyId: 8, // Quality Assurance + }, + // For QA Engineer - Test Execution + { + roleId: 24, + competencyId: 8, // Quality Assurance + }, + { + roleId: 24, + competencyId: 9, // Problem Solving + }, + // For QA Engineer - Automation Testing + { + roleId: 25, + competencyId: 8, // Quality Assurance + }, + { + roleId: 25, + competencyId: 9, // Problem Solving + }, + // For Systems Administrator - System Maintenance + { + roleId: 26, + competencyId: 9, // Problem Solving + }, + { + roleId: 26, + competencyId: 10, // Strategic Leadership + }, + // For Systems Administrator - System Security Management + { + roleId: 27, + competencyId: 4, // Change Leadership + }, + { + roleId: 27, + competencyId: 8, // Quality Assurance + }, + // For Systems Administrator - User Support + { + roleId: 28, + competencyId: 9, // Problem Solving + }, + { + roleId: 28, + competencyId: 6, // Team Collaboration + }, + // For Network Administrator - Network Configuration and Maintenance + { + roleId: 29, + competencyId: 1, // Strategic Planning + }, + { + roleId: 29, + competencyId: 7, // Technical Expertise + }, + // For Network Administrator - Security Management + { + roleId: 30, + competencyId: 4, // Change Leadership + }, + { + roleId: 30, + competencyId: 8, // Quality Assurance + }, + // For Network Administrator - Network Performance Optimization + { + roleId: 31, + competencyId: 4, // Change Leadership + }, + { + roleId: 31, + competencyId: 7, // Technical Expertise }, ]; diff --git a/prisma/seeds/data/mock-users.data.ts b/prisma/seeds/data/mock-users.data.ts index e1ae69f..8215e7d 100644 --- a/prisma/seeds/data/mock-users.data.ts +++ b/prisma/seeds/data/mock-users.data.ts @@ -2,143 +2,203 @@ import { UserRolesEnum } from "@prisma/client"; export const users = [ { + id: "d71f8744-52b0-4ec3-9e7e-1f1266059821", // Unique UUID for Amit Sharma email: "amit.sharma@example.com", role: UserRolesEnum.ADMIN, userName: "Amit Sharma", - password: "password1", - designation: "Manager", + password: "P@ssw0rd1", + designation: "CEO", + departmentId: 1, + levelNumber: 4, // Assuming Amit Sharma has a competency level of "Strategic Leadership" }, { + id: "4d45a9e9-4a4d-4c92-aaea-7b5abbd6ff98", // Unique UUID for Neha Patil email: "neha.patil@example.com", role: UserRolesEnum.CONSUMER, userName: "Neha Patil", - password: "password2", - designation: "Employee", + password: "P@ssw0rd2", + designation: "Software Engineer", + departmentId: 1, + levelNumber: 2, // competency level for Neha Patil }, { + id: "abaa7220-5d2e-4e05-842a-95b2c4ce1876", // Unique UUID for Rajesh Kumar email: "rajesh.kumar@example.com", role: UserRolesEnum.CONSUMER, userName: "Rajesh Kumar", - password: "password3", - designation: "Employee", + password: "P@ssw0rd3", + designation: "Software Engineer", + departmentId: 1, + levelNumber: 3, // competency level for Rajesh Kumar }, { + id: "0f5d0b13-8d72-46c9-a7c4-c1f7e5aa1f17", // Unique UUID for Priya Jain email: "priya.jain@example.com", role: UserRolesEnum.CONSUMER, userName: "Priya Jain", - password: "password4", - designation: "Employee", + password: "P@ssw0rd4", + designation: "QA Engineer", + departmentId: 3, + levelNumber: 2, // competency level for Priya Jain }, { + id: "bbf1f7cf-4216-458e-8d98-0d9204ae57ef", // Unique UUID for Arjun Singh email: "arjun.singh@example.com", role: UserRolesEnum.CONSUMER, userName: "Arjun Singh", - password: "password5", - designation: "Employee", + password: "P@ssw0rd5", + designation: "QA Engineer", + departmentId: 3, + levelNumber: 3, // competency level for Arjun Singh }, { + id: "a3bf8f0e-22a9-4a5e-8c76-e80b67a1a6e9", // Unique UUID for Deepa Mishra email: "deepa.mishra@example.com", role: UserRolesEnum.CONSUMER, userName: "Deepa Mishra", - password: "password6", - designation: "Employee", + password: "P@ssw0rd6", + designation: "Software Architect", + departmentId: 1, + levelNumber: 4, // competency level for Deepa Mishra }, { + id: "ac6b9e42-efea-401a-bd67-9cf9110ac3c9", // Unique UUID for Rahul Verma email: "rahul.verma@example.com", role: UserRolesEnum.CONSUMER, userName: "Rahul Verma", - password: "password7", - designation: "Employee", + password: "P@ssw0rd7", + designation: "Software Architect", + departmentId: 1, + levelNumber: 3, // competency level for Rahul Verma }, { + id: "0f82ac61-9801-488f-babb-3c2d23a413d0", // Unique UUID for Anita Sen email: "anita.sen@example.com", role: UserRolesEnum.CONSUMER, userName: "Anita Sen", - password: "password8", - designation: "Employee", + password: "P@ssw0rd8", + designation: "Software Engineer", + departmentId: 1, + levelNumber: 2, // competency level for Anita Sen }, { + id: "69d7871b-3b09-4b5b-9ea0-0ca27c2ac9e3", // Unique UUID for Mohit Gupta email: "mohit.gupta@example.com", role: UserRolesEnum.CONSUMER, userName: "Mohit Gupta", - password: "password9", - designation: "Employee", + password: "P@ssw0rd9", + designation: "Software Engineer", + departmentId: 1, + levelNumber: 3, // competency level for Mohit Gupta }, { + id: "7b8005e4-e3d5-4a0d-81d6-d2965a9d5c2c", // Unique UUID for Pooja Rawat email: "pooja.rawat@example.com", role: UserRolesEnum.CONSUMER, userName: "Pooja Rawat", - password: "password10", - designation: "Employee", + password: "P@ssw0rd10", + designation: "Software Engineer", + departmentId: 1, + levelNumber: 3, // competency level for Pooja Rawat }, { + id: "1536c7ad-234e-44d3-b6d4-8ca655b46e2a", // Unique UUID for Anurag Sharma email: "anurag.sharma@example.com", role: UserRolesEnum.CONSUMER, userName: "Anurag Sharma", - password: "password11", - designation: "Employee", + password: "P@ssw0rd11", + designation: "Software Engineer", + departmentId: 1, + levelNumber: 2, // competency level for Anurag Sharma }, { + id: "9a1f8f5a-0d9e-40e2-b7ea-df493a303db7", // Unique UUID for Meera Rani email: "meera.rani@example.com", role: UserRolesEnum.CONSUMER, userName: "Meera Rani", - password: "password12", - designation: "Employee", + password: "P@ssw0rd12", + designation: "Software Engineer", + departmentId: 1, + levelNumber: 2, // competency level for Meera Rani }, { + id: "fbd6d6b9-4c4f-48c1-9147-3006a369e190", // Unique UUID for Vikas Patel email: "vikas.patel@example.com", role: UserRolesEnum.CONSUMER, userName: "Vikas Patel", - password: "password13", - designation: "Employee", + password: "P@ssw0rd13", + designation: "Software Engineer", + departmentId: 1, + levelNumber: 3, // competency level for Vikas Patel }, { + id: "4f67ae5a-c2a2-4652-81cc-4d1471e1a158", // Unique UUID for Divya Malik email: "divya.malik@example.com", role: UserRolesEnum.CONSUMER, userName: "Divya Malik", - password: "password14", - designation: "Employee", + password: "P@ssw0rd14", + designation: "Software Engineer", + departmentId: 1, + levelNumber: 2, // competency level for Divya Malik }, { + id: "a6a862ac-f5f7-4d6b-a297-c1497e5652e5", // Unique UUID for Rajiv Khanna email: "rajiv.khanna@example.com", role: UserRolesEnum.CONSUMER, userName: "Rajiv Khanna", - password: "password15", - designation: "Employee", + password: "P@ssw0rd15", + designation: "Software Engineer", + departmentId: 1, + levelNumber: 4, // competency level for Rajiv Khanna }, { + id: "72e9272e-3a4e-45f6-877c-6567f3f9785a", // Unique UUID for Swati Shah email: "swati.shah@example.com", role: UserRolesEnum.CONSUMER, userName: "Swati Shah", - password: "password16", - designation: "Employee", + password: "P@ssw0rd16", + designation: "QA Engineer", + departmentId: 3, + levelNumber: 3, // competency level for Swati Shah }, { + id: "5d0b5fb7-d350-4315-988e-df878e96287d", // Unique UUID for Manish Agrawal email: "manish.agrawal@example.com", role: UserRolesEnum.CONSUMER, userName: "Manish Agrawal", - password: "password17", - designation: "Employee", + password: "P@ssw0rd17", + designation: "QA Engineer", + departmentId: 3, + levelNumber: 4, // competency level for Manish Agrawal }, { + id: "a1f3e09a-369f-4e7b-997b-891274f1020e", // Unique UUID for Pallavi Reddy email: "pallavi.reddy@example.com", role: UserRolesEnum.CONSUMER, userName: "Pallavi Reddy", - password: "password18", - designation: "Employee", + password: "P@ssw0rd18", + designation: "QA Engineer", + departmentId: 3, + levelNumber: 2, // competency level for Pallavi Reddy }, { + id: "d35d1e80-d2df-4b6a-9a67-8ef5f2877a57", // Unique UUID for Deepak Mishra email: "deepak.mishra@example.com", role: UserRolesEnum.CONSUMER, userName: "Deepak Mishra", - password: "password19", - designation: "Employee", + password: "P@ssw0rd19", + designation: "QA Engineer", + departmentId: 3, + levelNumber: 4, // competency level for Deepak Mishra }, { + id: "7646a865-513f-4529-8ecf-170220e2d6b8", // Unique UUID for Komal Choudhary email: "komal.choudhary@example.com", role: UserRolesEnum.CONSUMER, userName: "Komal Choudhary", - password: "password20", - designation: "Employee", + password: "P@ssw0rd20", + designation: "QA Engineer", + departmentId: 3, + levelNumber: 2, // competency level for Komal Choudhary }, ]; diff --git a/prisma/seeds/seed.ts b/prisma/seeds/seed.ts index d70e14c..b261cfc 100644 --- a/prisma/seeds/seed.ts +++ b/prisma/seeds/seed.ts @@ -1,26 +1,16 @@ import { PrismaClient } from "@prisma/client"; import { competencies } from "./data/mock-competencies.data"; +import { competenciesToCompetencyLevels } from "./data/mock-competenciesToCompetencyLevels.data"; import { competencyLevels } from "./data/mock-competencyLevels.data"; import { departments } from "./data/mock-departments.data"; import { designations } from "./data/mock-designation.data"; import { levels } from "./data/mock-levels.data"; import { roles } from "./data/mock-roles.data"; -import { users } from "./data/mock-users.data"; -import { competenciesToCompetencyLevels } from "./data/mock-competenciesToCompetencyLevels.data"; import { rolesToCompetencies } from "./data/mock-rolesToCompetencies.data"; +import { users } from "./data/mock-users.data"; const prisma = new PrismaClient(); -// 1- Department ------ -// 2- Designation -------- -// 3- Role --------- -// 4- Level ========= -// 5- Competency Level ========= -// 6- Competency ======== -// 7- Competency to Competency Level ========= -// 8- RoleToCompetency ============ -// 9- User ------ - async function seed() { await prisma.$transaction([ prisma.department.createMany({ @@ -62,10 +52,11 @@ async function seed() { } seed() - .catch((e) => { - console.log(e); + .catch((error) => { + console.log("\n\n Seeding error:--------", error); process.exit(1); }) .finally(() => { + console.log("\n\n Seeding stopped/completed"); prisma.$disconnect(); }); From 9043e8406f00cc6815f3bd2a2b32d7629d927358 Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Wed, 1 Nov 2023 23:49:14 +0530 Subject: [PATCH 087/175] Added testcases for the survey cycle parameter --- .../survey-cycle-parameter.controller.spec.ts | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 src/survey-cycle-parameter/survey-cycle-parameter.controller.spec.ts diff --git a/src/survey-cycle-parameter/survey-cycle-parameter.controller.spec.ts b/src/survey-cycle-parameter/survey-cycle-parameter.controller.spec.ts new file mode 100644 index 0000000..dddbbdd --- /dev/null +++ b/src/survey-cycle-parameter/survey-cycle-parameter.controller.spec.ts @@ -0,0 +1,116 @@ +import * as pactum from "pactum"; +import { Test, TestingModule } from "@nestjs/testing"; +import { INestApplication, ValidationPipe } from "@nestjs/common"; +import { PrismaService } from "../prisma/prisma.service"; +import { ConfigService } from "@nestjs/config"; +import { SurveyConfigModule } from "src/survey-config/survey-config.module"; +import { CreateSurveyCycleParameterDto } from "./dto/create-survey-cycle-parameter.dto"; +import { ResponseSurveyCycleParameterDto } from "./dto/response-survey-cycle-parameter.dto"; +import { UpdateSurveyCycleParameterDto } from "./dto/update-survey-cycle-parameter.dto"; + +describe("SurveyCycleParameterController e2e", () => { + let app: INestApplication; + let prisma: PrismaService; + let module: TestingModule; + beforeAll(async () => { + module = await Test.createTestingModule({ + imports: [SurveyConfigModule], + }).compile(); + + app = module.createNestApplication(); + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + }) + ); + + const configService = app.get(ConfigService); + const apiPrefix = configService.get("API_PREFIX") || "api"; + const PORT = configService.get("APP_PORT") || 4010; + app.setGlobalPrefix(apiPrefix); + + await app.init(); + await app.listen(PORT); + + prisma = app.get(PrismaService); + pactum.request.setBaseUrl(`http://localhost:${PORT}/${apiPrefix}`); + }); + afterAll(async () => { + app.close(); + }); + describe("SurveyCycleParameterController createSurveyParameter()", () => { + it("should return created survey cycle parameter with id and name", async function () { + const createSurveyCycleParameterDto: CreateSurveyCycleParameterDto = { + startTime: new Date("2023-10-31"), + endTime: new Date("2024-01-31"), + surveyConfigId: 1, + isActive: true, + }; + + const response = await pactum + .spec() + .post("/survey-cycle-parameter") + .withBody(createSurveyCycleParameterDto) + .expectStatus(201); + + const createdSurveyCycleParameter = JSON.parse(response); + expect(createdSurveyCycleParameter).toHaveProperty("id"); + expect(createdSurveyCycleParameter.startTime).toEqual( + createSurveyCycleParameterDto.startTime + ); + expect(createdSurveyCycleParameter.endTime).toEqual( + createSurveyCycleParameterDto.endTime + ); + expect(createdSurveyCycleParameter.isActive).toBeTruthy(); + expect(createdSurveyCycleParameter.surveyConfigId).toEqual( + createSurveyCycleParameterDto.surveyConfigId + ); + expect(createdSurveyCycleParameter?.createdAt).toBeDefined(); + expect(createdSurveyCycleParameter?.updatedAt).toBeDefined(); + }); + }); + + describe("SurveyCycleParameterController getAllSurveyParameter()", () => { + it("should get all survey cycle parameter", async () => { + const response = await pactum + .spec() + .get("/survey-cycle-parameters") + .expectStatus(200); + + const getAllSurveyCycleParameter = JSON.parse(response); + expect(Array.isArray(getAllSurveyCycleParameter)).toBeTruthy(); + }); + }); + + describe("SurveyCycleParameterController updateSurveyParameterById()", () => { + it("should update survey cycle parameter by id", async () => { + const testData = { + id: 1, + }; + const updateSurveyCycleParameterDto: UpdateSurveyCycleParameterDto = { + startTime: new Date("2023-10-31"), + endTime: new Date("2024-01-31"), + isActive: false, + }; + const response = await pactum + .spec() + .patch(`/survey-cycle-parameter/${testData.id}`) + .withPathParams({ + id: testData.id, + }) + .withBody(updateSurveyCycleParameterDto) + .expectStatus(200); + const updatedSurveyCycleParameter = JSON.parse(response); + expect(updatedSurveyCycleParameter).toHaveProperty("id"); + expect(updatedSurveyCycleParameter.startTime).toEqual( + updateSurveyCycleParameterDto.startTime + ); + expect(updatedSurveyCycleParameter.endTime).toEqual( + updateSurveyCycleParameterDto.endTime + ); + expect(updatedSurveyCycleParameter.isActive).toEqual(false); + expect(updatedSurveyCycleParameter?.createdAt).toBeDefined(); + expect(updatedSurveyCycleParameter?.updatedAt).toBeDefined(); + }); + }); +}); From e8a65b4188540af41294fc6a75cfa4f68d980c0f Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Wed, 1 Nov 2023 17:10:35 +0530 Subject: [PATCH 088/175] Added testcases for admin --- .../admin-competency.controller.spec.ts | 146 ++++++++++++++++-- .../admin-department.controller.spec.ts | 58 +++++++ 2 files changed, 191 insertions(+), 13 deletions(-) create mode 100644 src/admin-department/admin-department.controller.spec.ts diff --git a/src/admin-competency/admin-competency.controller.spec.ts b/src/admin-competency/admin-competency.controller.spec.ts index b84496d..e49e5e6 100644 --- a/src/admin-competency/admin-competency.controller.spec.ts +++ b/src/admin-competency/admin-competency.controller.spec.ts @@ -1,20 +1,140 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { AdminCompetencyController } from './admin-competency.controller'; -import { AdminCompetencyService } from './admin-competency.service'; +import * as pactum from "pactum"; +import { Test, TestingModule } from "@nestjs/testing"; +import { INestApplication, ValidationPipe } from "@nestjs/common"; +import { PrismaService } from "../prisma/prisma.service"; +import { ConfigService } from "@nestjs/config"; +import { CreateAdminCompetencyDto } from "./dto/create-admin-competency.dto"; +import { UpdateAdminCompetencyDto } from "./dto"; +import { AdminCompetencyModule } from "./admin-competency.module"; -describe('AdminCompetencyController', () => { - let controller: AdminCompetencyController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [AdminCompetencyController], - providers: [AdminCompetencyService], +describe("AdminCompetencyController e2e", () => { + let app: INestApplication; + let prisma: PrismaService; + let module: TestingModule; + beforeAll(async () => { + module = await Test.createTestingModule({ + imports: [AdminCompetencyModule], }).compile(); - controller = module.get(AdminCompetencyController); + app = module.createNestApplication(); + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + }) + ); + + const configService = app.get(ConfigService); + const apiPrefix = configService.get("API_PREFIX") || "api"; + const PORT = configService.get("APP_PORT") || 4010; + app.setGlobalPrefix(apiPrefix); + + await app.init(); + await app.listen(PORT); + + prisma = app.get(PrismaService); + pactum.request.setBaseUrl(`http://localhost:${PORT}/${apiPrefix}`); + }); + afterAll(async () => { + app.close(); + }); + + describe("AdminCompetencyController create()", () => { + it("should return created admin competency on success", async function () { + const createAdminCompetencyDto: CreateAdminCompetencyDto = { + competencyId: 1, + name: "Test create admin competency", + competencyLevels: [ + { competencyLevelNumber: 1, competencyLevelName: "Test-1" }, + ], + description: "Testing description", + }; + + const response = await pactum + .spec() + .post("/admin-competency") + .withBody(createAdminCompetencyDto) + .expectStatus(201) + .stores("createdAt", "updatedAt"); + + const createdAdminCompetency = JSON.parse(response.body); + expect(createdAdminCompetency).not.toBeNull(); + expect(createdAdminCompetency.name).toEqual( + createAdminCompetencyDto.name + ); + expect(createdAdminCompetency.description).toEqual( + createAdminCompetencyDto.description + ); + expect( + createdAdminCompetency.competencyLevels[0].competencyLevelName + ).toEqual( + createAdminCompetencyDto.competencyLevels[0].competencyLevelName + ); + expect( + createdAdminCompetency.competencyLevels[0].competencyLevelNumber + ).toEqual( + createAdminCompetencyDto.competencyLevels[0].competencyLevelNumber + ); + expect(createdAdminCompetency.createdAt).toBeDefined(); + expect(createdAdminCompetency.updatedAt).toBeDefined(); + }); + }); + + describe("AdminCompetencyController, findAll()", () => { + it("should get all admin competency", async () => { + const response = await pactum + .spec() + .get("/admin-competency") + .expectStatus(200); + const adminCompetencies = JSON.parse(response.body); + expect(Array.isArray(adminCompetencies)).toBeTruthy(); + }); + }); + + describe("AdminCompetencyController update()", () => { + it("should get admin competency by competencyId", async () => { + const updatedAdminCompetencyDto: UpdateAdminCompetencyDto = { + name: "Updated Test Competency Name", + description: "Updated Test Description", + }; + const response = await pactum + .spec() + .patch(`/admin-competency/{id}/{competencyId}`) + .withPathParams({ + id: 1, + competencyId: 1, + }) + .withBody(updatedAdminCompetencyDto) + .expectStatus(200); + + const updatedAdminCompetency = JSON.parse(response.body); + expect(updatedAdminCompetency).not.toBeNull(); + expect(updatedAdminCompetency.name).toEqual( + updatedAdminCompetencyDto.name + ); + expect(updatedAdminCompetency.description).toEqual( + updatedAdminCompetencyDto.description + ); + }); }); - it('should be defined', () => { - expect(controller).toBeDefined(); + describe("AdminCompetencyController remove()", () => { + it("should delete an admin competency by id", async () => { + const response = await pactum + .spec() + .delete("/admin-competency//{id}/{competencyId}") + .withPathParams({ + id: 1, + competencyId: 1, + }) + .expectStatus(200); + const deletedAdminCompetency = JSON.parse(response.body); + expect(deletedAdminCompetency).not.toBeNull(); + expect(deletedAdminCompetency.id).toEqual(3); + expect(deletedAdminCompetency.name).toBeDefined(); + expect(deletedAdminCompetency.competencyLevels).toBeDefined(); + expect(deletedAdminCompetency?.description).toBeDefined(); + expect(deletedAdminCompetency?.createdAt).toBeDefined(); + expect(deletedAdminCompetency?.updatedAt).toBeDefined(); + }); }); }); diff --git a/src/admin-department/admin-department.controller.spec.ts b/src/admin-department/admin-department.controller.spec.ts new file mode 100644 index 0000000..37b4faa --- /dev/null +++ b/src/admin-department/admin-department.controller.spec.ts @@ -0,0 +1,58 @@ +import * as pactum from "pactum"; +import { PrismaService } from "../prisma/prisma.service"; +import { Test, TestingModule } from "@nestjs/testing"; +import { ConfigService } from "@nestjs/config"; +import { INestApplication, ValidationPipe } from "@nestjs/common"; +import { AdminDepartmentModule } from "./admin-department.module"; +import { ResponseAdminDepartmentDto } from "./dto/response-admin-department.dto"; + +describe("AdminDepartment e2e", () => { + let app: INestApplication; + let prisma: PrismaService; + let module: TestingModule; + beforeAll(async () => { + module = await Test.createTestingModule({ + imports: [AdminDepartmentModule], + }).compile(); + app = module.createNestApplication(); + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + }) + ); + const configService = module.get(ConfigService); + const apiPrefix = configService.get("API_PREFIX") || "api"; + const PORT = configService.get("APP_PORT") || 4010; + app.setGlobalPrefix(apiPrefix); + + await app.init(); + await app.listen(PORT); + + prisma = app.get(PrismaService); + pactum.request.setBaseUrl(`http://localhost:${PORT}/${apiPrefix}`); + }); + + afterAll(async () => { + app.close(); + }); + + describe("AdminDepartmentController createOrUpdateAdminDepartment()", () => { + it("should return created or updated admin department", async function () { + const response = await pactum + .spec() + .post("/admin-department/{departmentId}") + .withPathParams({ departmentId: 1 }) + .expectStatus(201); + const createdAdminDepartment = JSON.parse(response); + expect(createdAdminDepartment).toMatchObject(ResponseAdminDepartmentDto); + }); + }); + + describe("AdminDepartmentController getAllAdminDepartment()", () => { + it("should return all admin departments", async function () { + const response = await pactum.spec().get("/admin-departments"); + const allAdminDepartments = JSON.parse(response); + expect(allAdminDepartments[0]).toMatchObject(ResponseAdminDepartmentDto); + }); + }); +}); From 532a8b376a9952c7e2f2da523ee0ddd7c2a8ed37 Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Wed, 1 Nov 2023 23:51:37 +0530 Subject: [PATCH 089/175] Refactor --- .../admin-competency.controller.spec.ts | 26 ++++++++++++------- .../admin-department.controller.spec.ts | 7 +++-- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/admin-competency/admin-competency.controller.spec.ts b/src/admin-competency/admin-competency.controller.spec.ts index e49e5e6..facb1af 100644 --- a/src/admin-competency/admin-competency.controller.spec.ts +++ b/src/admin-competency/admin-competency.controller.spec.ts @@ -53,8 +53,7 @@ describe("AdminCompetencyController e2e", () => { .spec() .post("/admin-competency") .withBody(createAdminCompetencyDto) - .expectStatus(201) - .stores("createdAt", "updatedAt"); + .expectStatus(201); const createdAdminCompetency = JSON.parse(response.body); expect(createdAdminCompetency).not.toBeNull(); @@ -96,12 +95,16 @@ describe("AdminCompetencyController e2e", () => { name: "Updated Test Competency Name", description: "Updated Test Description", }; + const testData = { + id: 1, + competencyId: 55, + }; const response = await pactum .spec() - .patch(`/admin-competency/{id}/{competencyId}`) + .patch(`/admin-competency/${testData.id}/${testData.competencyId}`) .withPathParams({ - id: 1, - competencyId: 1, + id: testData.id, + competencyId: testData.competencyId, }) .withBody(updatedAdminCompetencyDto) .expectStatus(200); @@ -119,17 +122,22 @@ describe("AdminCompetencyController e2e", () => { describe("AdminCompetencyController remove()", () => { it("should delete an admin competency by id", async () => { + const testData = { + id: 1, + competencyId: 55, + }; const response = await pactum .spec() - .delete("/admin-competency//{id}/{competencyId}") + .delete(`/admin-competency/${testData.id}/${testData.competencyId}`) .withPathParams({ - id: 1, - competencyId: 1, + id: testData.id, + competencyId: testData.competencyId, }) .expectStatus(200); const deletedAdminCompetency = JSON.parse(response.body); expect(deletedAdminCompetency).not.toBeNull(); - expect(deletedAdminCompetency.id).toEqual(3); + expect(deletedAdminCompetency.id).toEqual(1); + expect(deletedAdminCompetency.competencyId).toEqual(55); expect(deletedAdminCompetency.name).toBeDefined(); expect(deletedAdminCompetency.competencyLevels).toBeDefined(); expect(deletedAdminCompetency?.description).toBeDefined(); diff --git a/src/admin-department/admin-department.controller.spec.ts b/src/admin-department/admin-department.controller.spec.ts index 37b4faa..24b70ae 100644 --- a/src/admin-department/admin-department.controller.spec.ts +++ b/src/admin-department/admin-department.controller.spec.ts @@ -38,10 +38,13 @@ describe("AdminDepartment e2e", () => { describe("AdminDepartmentController createOrUpdateAdminDepartment()", () => { it("should return created or updated admin department", async function () { + const testData = { + departmentId: 1, + }; const response = await pactum .spec() - .post("/admin-department/{departmentId}") - .withPathParams({ departmentId: 1 }) + .post(`/admin-department/${testData.departmentId}`) + .withPathParams({ departmentId: testData.departmentId }) .expectStatus(201); const createdAdminDepartment = JSON.parse(response); expect(createdAdminDepartment).toMatchObject(ResponseAdminDepartmentDto); From 75e75c320a84c6b9675db8d175549d0ed96ef538 Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Thu, 2 Nov 2023 17:54:04 +0530 Subject: [PATCH 090/175] Latest Survey, response and survey form for user APIs --- .../dto/update-response-tracker.dto.ts | 47 ++++++- .../response-tracker.controller.ts | 28 ++-- .../response-tracker.service.ts | 26 ++++ src/survey-form/survey-form.controller.ts | 39 ++++++ src/survey-form/survey-form.service.ts | 36 +++++- src/survey/survey.controller.ts | 120 ++++++++++++++---- src/survey/survey.service.ts | 18 ++- 7 files changed, 266 insertions(+), 48 deletions(-) diff --git a/src/response-tracker/dto/update-response-tracker.dto.ts b/src/response-tracker/dto/update-response-tracker.dto.ts index b34cde1..da7127c 100644 --- a/src/response-tracker/dto/update-response-tracker.dto.ts +++ b/src/response-tracker/dto/update-response-tracker.dto.ts @@ -1,4 +1,45 @@ -import { PartialType } from '@nestjs/mapped-types'; -import { CreateResponseTrackerDto } from './create-response-tracker.dto'; +import { ApiProperty } from "@nestjs/swagger"; +import { Type } from "class-transformer"; +import { + ArrayNotEmpty, + IsArray, + IsInt, + IsNotEmpty, + IsUUID, + ValidateNested, +} from "class-validator"; +import { AnswerEnum } from "../enums/response-tracker.enums"; +import { responseObject } from "./create-response-tracker.dto"; -export class UpdateResponseTrackerDto extends PartialType(CreateResponseTrackerDto) {} +export class UpdateResponseTrackerDto { + @ApiProperty({ type: "integer", example: 1 }) + @IsNotEmpty() + @IsInt() + surveyFormId: number; + + @ApiProperty({ type: "integer", example: 2 }) + @IsNotEmpty() + @IsUUID() + assesseeId: string; + + @ApiProperty({ type: "integer", example: 3 }) + @IsNotEmpty() + @IsUUID() + assessorId: string; + + @ApiProperty({ + type: responseObject, + isArray: true, + example: [ + { + questionId: 1, + answer: `${AnswerEnum.YES} || ${AnswerEnum.NO} || ${AnswerEnum.DO_NOT_KNOW}`, + }, + ], + }) + @IsArray() + @ArrayNotEmpty() + @ValidateNested({ each: true }) + @Type(() => responseObject) + responseJson: responseObject[] = []; +} diff --git a/src/response-tracker/response-tracker.controller.ts b/src/response-tracker/response-tracker.controller.ts index 32db1c4..5b3b734 100644 --- a/src/response-tracker/response-tracker.controller.ts +++ b/src/response-tracker/response-tracker.controller.ts @@ -246,29 +246,34 @@ export class ResponseTrackerController { } } - @Patch(":id") - @ApiOperation({ summary: "update response tracker by id" }) + @Patch() + @ApiOperation({ + summary: + "update response tracker by surveyFormId, assesseeId and assessorId", + }) @ApiResponse({ status: HttpStatus.OK, type: ResponseTrackerDtoResponse }) async update( @Res() res, - @Param("id", ParseIntPipe) id: number, @Body() updateResponseTrackerDto: UpdateResponseTrackerDto ): Promise { + const { surveyFormId, assesseeId, assessorId } = updateResponseTrackerDto; try { - this.logger.log(`Initiated updating response tracker with id #${id}`); - - const updatedResponse = await this.responseTrackerService.update( - id, - updateResponseTrackerDto + this.logger.log( + `Initiated updating response tracker with surveyFormId #${surveyFormId}, assesseeId #${assesseeId} and assessorId #${assessorId}` ); + const updatedResponse = + await this.responseTrackerService.updateBySurveyFormId( + updateResponseTrackerDto + ); + return res.status(HttpStatus.OK).json({ data: updatedResponse, - message: `Successfully updated response tracker with id ${id}`, + message: `Successfully updated response tracker with surveyFormId ${surveyFormId}, assesseeId #${assesseeId} and assessorId #${assessorId}`, }); } catch (error) { this.logger.error( - `Failed to update response tracker with id #${id}`, + `Failed to update response tracker with surveyFormId #${surveyFormId}, assesseeId #${assesseeId} and assessorId #${assessorId}`, error ); @@ -278,7 +283,8 @@ export class ResponseTrackerController { return res.status(statusCode).json({ statusCode, message: - errorMessage || `Failed to update response tracker with id #${id}`, + errorMessage || + `Failed to update response tracker with surveyFormId #${surveyFormId}, assesseeId #${assesseeId} and assessorId #${assessorId}`, }); } } diff --git a/src/response-tracker/response-tracker.service.ts b/src/response-tracker/response-tracker.service.ts index 962d7da..063a936 100644 --- a/src/response-tracker/response-tracker.service.ts +++ b/src/response-tracker/response-tracker.service.ts @@ -3,6 +3,7 @@ import { PrismaService } from "src/prisma/prisma.service"; import { CreateResponseTrackerDto } from "./dto/create-response-tracker.dto"; import { UpdateResponseTrackerDto } from "./dto/update-response-tracker.dto"; import { IResponseTracker } from "./interfaces/response-tracker.interface"; +import { ResponseTrackerStatusEnum } from "@prisma/client"; @Injectable() export class ResponseTrackerService { @@ -163,4 +164,29 @@ export class ResponseTrackerService { return result; } + + public async updateBySurveyFormId( + updateResponseTrackerDto: UpdateResponseTrackerDto + ) { + const { surveyFormId, assesseeId, assessorId } = updateResponseTrackerDto; + const responseJson = JSON.stringify(updateResponseTrackerDto.responseJson); + + const payload = { + responseJson: JSON.parse(responseJson || "[]"), + status: ResponseTrackerStatusEnum.COMPLETED, + }; + + if (!responseJson) delete payload.responseJson; + + return await this.prisma.responseTracker.update({ + where: { + surveyFormId_assesseeId_assessorId: { + surveyFormId, + assesseeId, + assessorId, + }, + }, + data: payload, + }); + } } diff --git a/src/survey-form/survey-form.controller.ts b/src/survey-form/survey-form.controller.ts index 559dcac..ddef44c 100644 --- a/src/survey-form/survey-form.controller.ts +++ b/src/survey-form/survey-form.controller.ts @@ -147,4 +147,43 @@ export class SurveyFormController { .json({ message: errorMessage || "Could not delete survey form." }); } } + + @Get("latest-survey-form/:userId") + @ApiOperation({ summary: "Fetch latest Survey-Form by userId" }) + @ApiResponse({ status: HttpStatus.FOUND, type: SurveyFormResponse }) + async fetchLatestSurveyFormByUserId( + @Res() res, + @Param("userId") userId: string + ): Promise { + try { + this.logger.log( + `Initiated fetching latest Survey-Form by userId #${userId}` + ); + + const surveyForm = + await this.surveyFormService.fetchLatestSurveyFormByUserId(userId); + + this.logger.log( + `Successfully fetched latest Survey-Form by userId #${userId}` + ); + + return res.status(HttpStatus.OK).send({ + data: surveyForm, + message: `Successfully fetched latest Survey-Form by userId #${userId}`, + }); + } catch (error) { + this.logger.error( + `Failed to fetch latest Survey-Form by userId #${userId}`, + error + ); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + return res.status(statusCode).send({ + message: + errorMessage || + `Could not find latest Survey-Form by userId #${userId}`, + }); + } + } } diff --git a/src/survey-form/survey-form.service.ts b/src/survey-form/survey-form.service.ts index 640db86..f57e150 100644 --- a/src/survey-form/survey-form.service.ts +++ b/src/survey-form/survey-form.service.ts @@ -36,14 +36,16 @@ export class SurveyFormService { }); } - async findSurveyFormBySurveyCycleParameterId(paramId: number){ + async findSurveyFormBySurveyCycleParameterId(paramId: number) { const surveyForms = await this.prisma.surveyForm.findMany({ - where:{ - surveyCycleParameterId: paramId - } + where: { + surveyCycleParameterId: paramId, + }, }); - if(!surveyForms || surveyForms.length==0){ - throw new NotFoundException(`Survey Forms not found for SurveyCycleParameter with id #${paramId}`); + if (!surveyForms || surveyForms.length == 0) { + throw new NotFoundException( + `Survey Forms not found for SurveyCycleParameter with id #${paramId}` + ); } return surveyForms; } @@ -123,4 +125,26 @@ export class SurveyFormService { return latestSurveyForm[0]; } + + async fetchLatestSurveyFormByUserId(userId: string) { + return await this.prisma.surveyForm.findFirst({ + where: { + userId, + status: SurveyStatusEnum.PUBLISHED, + surveyCycleParameter: { + isActive: true, + }, + }, + select: { + id: true, + questionsJson: true, + UserMetadata: { + select: { + userId: true, + designation: true, + }, + }, + }, + }); + } } diff --git a/src/survey/survey.controller.ts b/src/survey/survey.controller.ts index 56e8380..b1d8ffc 100644 --- a/src/survey/survey.controller.ts +++ b/src/survey/survey.controller.ts @@ -1,11 +1,21 @@ -import { Body, Controller, Get, HttpStatus, Logger, Param, ParseUUIDPipe, Post, Res } from '@nestjs/common'; -import { SurveyService } from './survey.service'; -import { ApiOperation, ApiResponse } from '@nestjs/swagger'; -import { getPrismaErrorStatusAndMessage } from 'src/utils/utils'; +import { + Body, + Controller, + Get, + HttpStatus, + Logger, + Param, + ParseUUIDPipe, + Post, + Res, +} from "@nestjs/common"; +import { SurveyService } from "./survey.service"; +import { ApiOperation, ApiResponse } from "@nestjs/swagger"; +import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; -@Controller('survey') +@Controller("survey") export class SurveyController { - private readonly logger = new Logger(SurveyController.name); + private readonly logger = new Logger(SurveyController.name); constructor(private readonly surveyService: SurveyService) {} @Post() @@ -13,26 +23,35 @@ export class SurveyController { @ApiResponse({ status: HttpStatus.CREATED }) async generateSurveyFormsForDepartment( @Res() res, - @Param() departmentId: number, - ){ + @Param() departmentId: number + ) { try { this.logger.log(`Initiated creating new survey forms for a department.`); - const surveyForms = await this.surveyService.generateSurveyFormsForDepartment(departmentId); + const surveyForms = + await this.surveyService.generateSurveyFormsForDepartment(departmentId); - this.logger.log(`Successfully created new survey forms for a department.`); + this.logger.log( + `Successfully created new survey forms for a department.` + ); return res.status(HttpStatus.CREATED).json({ data: surveyForms, - message: `SurveyForms created successfully for depatrment with id #${departmentId}`, + message: `SurveyForms created successfully for department with id #${departmentId}`, }); } catch (error) { - this.logger.error(`Failed to create new survey forms for depatrment with id #${departmentId}`, error); + this.logger.error( + `Failed to create new survey forms for department with id #${departmentId}`, + error + ); - const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); - return res - .status(statusCode) - .json({ message: errorMessage || `Could not create survey forms for depatrment with id #${departmentId}` }); + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + return res.status(statusCode).json({ + message: + errorMessage || + `Could not create survey forms for department with id #${departmentId}`, + }); } } @@ -42,25 +61,76 @@ export class SurveyController { async getSurveysToBeFilledByUser( @Res() res, @Param("userId", ParseUUIDPipe) userId: string - ){ + ) { try { - this.logger.log(`Getting number of surveys to be filled by user with id #${userId}.`); + this.logger.log( + `Getting number of surveys to be filled by user with id #${userId}.` + ); - const surveyForms = await this.surveyService.getSurveysToBeFilledByUser(userId); + const surveyForms = await this.surveyService.getSurveysToBeFilledByUser( + userId + ); - this.logger.log(`Successfully fetched the number of surveys to be filled by user with id #${userId}.`); + this.logger.log( + `Successfully fetched the number of surveys to be filled by user with id #${userId}.` + ); return res.status(HttpStatus.CREATED).json({ data: surveyForms, message: `Successfully fetched the number of surveys to be filled by user with id #${userId}.`, }); } catch (error) { - this.logger.error(`Failed to fetch the number of surveys to be filled by user with id #${userId}.`, error); + this.logger.error( + `Failed to fetch the number of surveys to be filled by user with id #${userId}.`, + error + ); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + return res.status(statusCode).json({ + message: + errorMessage || + `Could not fetch the number of surveys to be filled by user with id #${userId}.`, + }); + } + } + + @Get("latest-survey-response/:userId") + @ApiOperation({ summary: "Fetch latest survey response by userId" }) + @ApiResponse({ status: HttpStatus.OK }) + async getLatestSurveyResponsesForUserId( + @Res() res, + @Param("userId") userId: string + ) { + try { + this.logger.log( + `Initiated fetching latest survey response by userId #${userId}` + ); + + const surveyForm = + await this.surveyService.getLatestSurveyResponsesForUserId(userId); + + this.logger.log( + `Successfully fetched latest survey response by userId #${userId}` + ); + + return res.status(HttpStatus.OK).send({ + data: surveyForm, + message: `Successfully fetched latest survey response by userId #${userId}`, + }); + } catch (error) { + this.logger.error( + `Failed to fetch latest survey response by userId #${userId}`, + error + ); - const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); - return res - .status(statusCode) - .json({ message: errorMessage || `Could not fetch the number of surveys to be filled by user with id #${userId}.` }); + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + return res.status(statusCode).send({ + message: + errorMessage || + `Could not find latest survey response by userId #${userId}`, + }); } } } diff --git a/src/survey/survey.service.ts b/src/survey/survey.service.ts index d1c9923..6c36a28 100644 --- a/src/survey/survey.service.ts +++ b/src/survey/survey.service.ts @@ -55,6 +55,11 @@ export class SurveyService { surveyCycleParameter: { isActive: true, }, + ResponseTracker: { + every: { + assesseeId: userId, + }, + }, }, orderBy: { createdAt: "desc", @@ -65,8 +70,13 @@ export class SurveyService { id: true, surveyFormId: true, assesseeId: true, - assessorId: true, - responseJson: true, + Assessor: { + select: { + userId: true, + userName: true, + designation: true, + }, + }, status: true, }, }, @@ -89,7 +99,9 @@ export class SurveyService { }); for (const user of users) { //get questions for the user according to their designation - const questions = await this.questionBank.getAllQuestionsForUser(user.userId); + const questions = await this.questionBank.getAllQuestionsForUser( + user.userId + ); //create the CreateSurveyFormDto for the user const surveyFormDto: CreateSurveyFormDto = { userId: user.userId, From 9238291333770eb4b84ec0323d907435b98d1bd7 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Thu, 2 Nov 2023 18:27:25 +0530 Subject: [PATCH 091/175] User Metadata unit test cases and needed path changes. --- .../admin-department.controller.ts | 17 ++- .../admin-department.module.ts | 4 +- .../admin-department.service.ts | 4 +- .../mock-department.service.ts | 2 +- .../mock-user/mock-user.service.ts | 2 +- .../user-metadata.controller.spec.ts | 113 ++++++++++++++++-- src/user-metadata/user-metadata.controller.ts | 2 +- src/user-metadata/user-metadata.module.ts | 9 +- src/user-metadata/user-metadata.service.ts | 9 +- 9 files changed, 126 insertions(+), 36 deletions(-) diff --git a/src/admin-department/admin-department.controller.ts b/src/admin-department/admin-department.controller.ts index f38c830..0f6cb1d 100644 --- a/src/admin-department/admin-department.controller.ts +++ b/src/admin-department/admin-department.controller.ts @@ -1,22 +1,19 @@ import { Controller, Get, - Post, - Body, - Patch, - Param, - Logger, HttpStatus, - Res, - Query, + Logger, + Param, ParseIntPipe, + Post, + Query, + Res } from "@nestjs/common"; +import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { getPrismaErrorStatusAndMessage } from "../utils/utils"; import { AdminDepartmentService } from "./admin-department.service"; import { FilterAdminDepartmentsDto } from "./dto/create-admin-department.dto"; -import { UpdateAdminDepartmentDto } from "./dto/update-admin-department.dto"; -import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { ResponseAdminDepartmentDto } from "./dto/response-admin-department.dto"; -import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; @Controller("admin-department") @ApiTags("admin-department") diff --git a/src/admin-department/admin-department.module.ts b/src/admin-department/admin-department.module.ts index f5ae089..b8c7f86 100644 --- a/src/admin-department/admin-department.module.ts +++ b/src/admin-department/admin-department.module.ts @@ -1,6 +1,6 @@ import { Module } from "@nestjs/common"; -import { MockDepartmentService } from "src/mockModules/mock-department/mock-department.service"; -import { PrismaModule } from "src/prisma/prisma.module"; +import { MockDepartmentService } from "../mockModules/mock-department/mock-department.service"; +import { PrismaModule } from "../prisma/prisma.module"; import { AdminDepartmentController } from "./admin-department.controller"; import { AdminDepartmentService } from "./admin-department.service"; diff --git a/src/admin-department/admin-department.service.ts b/src/admin-department/admin-department.service.ts index 91e7c5d..5da7e7b 100644 --- a/src/admin-department/admin-department.service.ts +++ b/src/admin-department/admin-department.service.ts @@ -1,7 +1,7 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import _ from "lodash"; -import { MockDepartmentService } from "src/mockModules/mock-department/mock-department.service"; -import { PrismaService } from "src/prisma/prisma.service"; +import { MockDepartmentService } from "../mockModules/mock-department/mock-department.service"; +import { PrismaService } from "../prisma/prisma.service"; import { FilterAdminDepartmentsDto } from "./dto/create-admin-department.dto"; @Injectable() diff --git a/src/mockModules/mock-department/mock-department.service.ts b/src/mockModules/mock-department/mock-department.service.ts index 848ad05..b935e88 100644 --- a/src/mockModules/mock-department/mock-department.service.ts +++ b/src/mockModules/mock-department/mock-department.service.ts @@ -1,7 +1,7 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { CreateDepartmentDto } from "./dto/create-mock-department.dto"; import { UpdateDepartmentDto } from "./dto/update-mock-department.dto"; -import { PrismaService } from "src/prisma/prisma.service"; +import { PrismaService } from "../../prisma/prisma.service"; @Injectable() export class MockDepartmentService { diff --git a/src/mockModules/mock-user/mock-user.service.ts b/src/mockModules/mock-user/mock-user.service.ts index 8c6bc0b..a3a0f73 100644 --- a/src/mockModules/mock-user/mock-user.service.ts +++ b/src/mockModules/mock-user/mock-user.service.ts @@ -1,7 +1,7 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { CreateMockUserDto } from "./dto/create-mock-user.dto"; import { UpdateMockUserDto } from "./dto/update-mock-user.dto"; -import { PrismaService } from "src/prisma/prisma.service"; +import { PrismaService } from "../../prisma/prisma.service"; @Injectable() export class MockUserService { diff --git a/src/user-metadata/user-metadata.controller.spec.ts b/src/user-metadata/user-metadata.controller.spec.ts index 5cc58f2..a2c27ba 100644 --- a/src/user-metadata/user-metadata.controller.spec.ts +++ b/src/user-metadata/user-metadata.controller.spec.ts @@ -1,18 +1,111 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { UserMetadataController } from './user-metadata.controller'; +import * as pactum from "pactum"; +import { Test, TestingModule } from "@nestjs/testing"; +import { INestApplication, ValidationPipe } from "@nestjs/common"; +import { PrismaService } from "../prisma/prisma.service"; +import { ConfigService } from "@nestjs/config"; +import { UserMetadataModule } from "./user-metadata.module"; -describe('UserMetadataController', () => { - let controller: UserMetadataController; +describe("UserMetadataController", () => { + let app: INestApplication; + let prisma: PrismaService; + let module: TestingModule; - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [UserMetadataController], + beforeAll(async () => { + module = await Test.createTestingModule({ + imports: [UserMetadataModule], }).compile(); - controller = module.get(UserMetadataController); + app = module.createNestApplication(); + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + }) + ); + + const configService = app.get(ConfigService); + const apiPrefix = configService.get("API_PREFIX") || "api"; + const PORT = configService.get("APP_PORT") || 4010; + app.setGlobalPrefix(apiPrefix); + + await app.init(); + await app.listen(PORT); + + prisma = app.get(PrismaService); + pactum.request.setBaseUrl(`http://localhost:${PORT}/${apiPrefix}`); + }); + + afterAll(async () => { + app.close(); + }); + + const userId1 = "0f5d0b13-8d72-46c9-a7c4-c1f7e5aa1f17"; + const userId2 = "0f82ac61-9801-488f-babb-3c2d23a413d0"; + describe("UserMetadataController createOrUpdateUserMetadata()", () => { + it("should create or update user1 metadata", async () => { + + const response = await pactum + .spec() + .post("/user-metadata/" + userId1) + .expectStatus(200) + .expectBodyContains(userId1); + }); + }); + + describe("UserMetadataController createOrUpdateUserMetadata()", () => { + it("should create or update user2 metadata", async () => { + + const response = await pactum + .spec() + .post("/user-metadata/" + userId2) + .expectStatus(200) + .expectBodyContains(userId2); + }); }); - it('should be defined', () => { - expect(controller).toBeDefined(); + describe("UserMetadataController findUserMetadata()", () => { + it("should get user1 metadata by ID", async () => { + + const response = await pactum + .spec() + .get("/user-metadata/" + userId1) + .expectStatus(200) + .expectBodyContains(userId1); + }); + }); + + describe("UserMetadataController findManyUserMetadata()", () => { + it("should get all user metadata", async () => { + const response = await pactum + .spec() + .get("/user-metadata") + .expectStatus(200) + .expectBodyContains(userId1) + .expectBodyContains(userId2) + + const userMetadata = JSON.parse(JSON.stringify(response.body)); + + }); + }); + + describe("UserMetadataController deleteUserMetadata()", () => { + it("should delete user1 metadata by ID", async () => { + + const response = await pactum + .spec() + .delete("/user-metadata/" + userId1) + .expectStatus(200) + .expectBodyContains(userId1); + }); + }); + + describe("UserMetadataController deleteUserMetadata()", () => { + it("should delete user2 metadata by ID", async () => { + + const response = await pactum + .spec() + .delete("/user-metadata/" + userId2) + .expectStatus(200) + .expectBodyContains(userId2); + }); }); }); diff --git a/src/user-metadata/user-metadata.controller.ts b/src/user-metadata/user-metadata.controller.ts index 396ed20..e343ffa 100644 --- a/src/user-metadata/user-metadata.controller.ts +++ b/src/user-metadata/user-metadata.controller.ts @@ -11,7 +11,7 @@ import { Res, } from "@nestjs/common"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; -import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; +import { getPrismaErrorStatusAndMessage } from "../utils/utils"; import { UserMetadataFilterDto } from "./dto"; import { ResponseUserMetadataDto, diff --git a/src/user-metadata/user-metadata.module.ts b/src/user-metadata/user-metadata.module.ts index 3af3b60..c88d10a 100644 --- a/src/user-metadata/user-metadata.module.ts +++ b/src/user-metadata/user-metadata.module.ts @@ -1,12 +1,13 @@ import { Module } from "@nestjs/common"; -import { AdminDepartmentModule } from "src/admin-department/admin-department.module"; -import { MockUserService } from "src/mockModules/mock-user/mock-user.service"; -import { SurveyConfigService } from "src/survey-config/survey-config.service"; import { UserMetadataController } from "./user-metadata.controller"; import { UserMetadataService } from "./user-metadata.service"; +import { SurveyConfigService } from "../survey-config/survey-config.service"; +import { MockUserService } from "../mockModules/mock-user/mock-user.service"; +import { PrismaModule } from "../prisma/prisma.module"; +import { AdminDepartmentModule } from "../admin-department/admin-department.module"; @Module({ - imports: [AdminDepartmentModule], + imports: [AdminDepartmentModule,PrismaModule], controllers: [UserMetadataController], providers: [UserMetadataService, SurveyConfigService, MockUserService], exports: [UserMetadataService], diff --git a/src/user-metadata/user-metadata.service.ts b/src/user-metadata/user-metadata.service.ts index 311e32f..c161c0a 100644 --- a/src/user-metadata/user-metadata.service.ts +++ b/src/user-metadata/user-metadata.service.ts @@ -1,12 +1,11 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { TimeUnitsEnum, UserRolesEnum } from "@prisma/client"; import _ from "lodash"; -import { MockUserService } from "src/mockModules/mock-user/mock-user.service"; -import { PrismaService } from "src/prisma/prisma.service"; -import { SurveyConfigService } from "src/survey-config/survey-config.service"; +import { MockUserService } from "../mockModules/mock-user/mock-user.service"; +import { PrismaService } from "../prisma/prisma.service"; +import { SurveyConfigService } from "../survey-config/survey-config.service"; import { UserMetadataFilterDto } from "./dto"; -import { AdminCompetencyService } from "src/admin-competency/admin-competency.service"; -import { AdminDepartmentService } from "src/admin-department/admin-department.service"; +import { AdminDepartmentService } from "../admin-department/admin-department.service"; @Injectable() export class UserMetadataService { From 7a726a8bab5c9daf9d97d8d827a778ac428761f6 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Fri, 3 Nov 2023 12:04:53 +0530 Subject: [PATCH 092/175] Updated user metadata unit test cases. --- src/user-metadata/user-metadata.controller.spec.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/user-metadata/user-metadata.controller.spec.ts b/src/user-metadata/user-metadata.controller.spec.ts index a2c27ba..f118daa 100644 --- a/src/user-metadata/user-metadata.controller.spec.ts +++ b/src/user-metadata/user-metadata.controller.spec.ts @@ -108,4 +108,18 @@ describe("UserMetadataController", () => { .expectBodyContains(userId2); }); }); + + describe("UserMetadataController findManyUserMetadata()", () => { + it("should get all user metadata", async () => { + const response = await pactum + .spec() + .get("/user-metadata") + .expectStatus(200) + + const userMetadata = JSON.parse(JSON.stringify(response.body)); + + expect(userMetadata).toStrictEqual({"data": [], "message": "UserMetadata(s) fetched successfully"}); + + }); + }); }); From e71cf808eba54b2e25df88f77e5c937cab33f71c Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Fri, 3 Nov 2023 11:31:23 +0530 Subject: [PATCH 093/175] updated --- .../admin-competency.controller.ts | 53 +++---- .../admin-competency.service.ts | 135 ++++++++++-------- src/survey/survey.controller.ts | 12 +- src/user-metadata/user-metadata.controller.ts | 5 +- src/user-metadata/user-metadata.service.ts | 2 +- 5 files changed, 103 insertions(+), 104 deletions(-) diff --git a/src/admin-competency/admin-competency.controller.ts b/src/admin-competency/admin-competency.controller.ts index 5a8e346..78bddab 100644 --- a/src/admin-competency/admin-competency.controller.ts +++ b/src/admin-competency/admin-competency.controller.ts @@ -1,5 +1,4 @@ import { - Body, Controller, Delete, Get, @@ -17,8 +16,6 @@ import { AdminCompetencyService } from "./admin-competency.service"; import { AdminCompetencyArrayResponseDto, AdminCompetencyResponseDto, - CreateAdminCompetencyDto, - UpdateAdminCompetencyDto, } from "./dto"; @Controller("admin-competency") @@ -87,35 +84,33 @@ export class AdminCompetencyController { } } - @Get(":id/:competencyId") - @ApiOperation({ summary: "get admin-competency by id and competencyId" }) + @Get(":competencyId") + @ApiOperation({ summary: "get admin-competency by competencyId" }) @ApiResponse({ status: HttpStatus.OK, type: AdminCompetencyResponseDto }) async findOne( @Res() res, - @Param("id", ParseIntPipe) id: number, @Param("competencyId", ParseIntPipe) competencyId: number ): Promise { try { this.logger.log( - `Initiated fetching admin-competency with id #${id} and competency id #${competencyId}` + `Initiated fetching admin-competency with competency id #${competencyId}` ); const adminCompetency = await this.adminCompetencyService.findOne( - id, competencyId ); this.logger.log( - `Successfully fetched admin-competency with id #${id} and competency id #${competencyId}` + `Successfully fetched admin-competency with competency id #${competencyId}` ); return res.status(HttpStatus.OK).send({ - message: `Successfully fetched admin-competency with id #${id} and competency id #${competencyId}`, + message: `Successfully fetched admin-competency with competency id #${competencyId}`, data: adminCompetency, }); } catch (error) { this.logger.error( - `Failed to fetch admin-competency with id #${id} and competency id #${competencyId}`, + `Failed to fetch admin-competency with competency id #${competencyId}`, error ); @@ -126,42 +121,38 @@ export class AdminCompetencyController { statusCode, message: errorMessage || - `Failed to fetch admin-competency with id #${id} and competency id #${competencyId}`, + `Failed to fetch admin-competency with competency id #${competencyId}`, }); } } - @Patch(":id/:competencyId") + @Patch(":competencyId") @ApiOperation({ summary: "update admin-competency" }) @ApiResponse({ status: HttpStatus.OK, type: AdminCompetencyResponseDto }) async update( @Res() res, - @Param("id", ParseIntPipe) id: number, - @Param("competencyId", ParseIntPipe) competencyId: number, - @Body() updateAdminCompetencyDto: UpdateAdminCompetencyDto + @Param("competencyId", ParseIntPipe) competencyId: number ): Promise { try { this.logger.log( - `Initiated updating admin-competency with id #${id} and competency id #${competencyId}` + `Initiated updating admin-competency competency id #${competencyId}` ); const adminCompetency = await this.adminCompetencyService.update( - id, - competencyId, - updateAdminCompetencyDto + competencyId ); this.logger.log( - `Successfully updated admin-competency with id #${id} and competency id #${competencyId}` + `Successfully updated admin-competency competency id #${competencyId}` ); return res.status(HttpStatus.OK).send({ - message: `Successfully updated admin-competency with id #${id} and competency id #${competencyId}`, + message: `Successfully updated admin-competency competency id #${competencyId}`, data: adminCompetency, }); } catch (error) { this.logger.error( - `Failed to update admin-competency with id #${id} and competency id #${competencyId}`, + `Failed to update admin-competency competency id #${competencyId}`, error ); @@ -172,40 +163,38 @@ export class AdminCompetencyController { statusCode, message: errorMessage || - `Failed to update admin-competency with id #${id} and competency id #${competencyId}`, + `Failed to update admin-competency competency id #${competencyId}`, }); } } - @Delete(":id/:competencyId") + @Delete(":competencyId") @ApiOperation({ summary: "delete admin-competency" }) @ApiResponse({ status: HttpStatus.OK, type: AdminCompetencyResponseDto }) async remove( @Res() res, - @Param("id", ParseIntPipe) id: number, @Param("competencyId", ParseIntPipe) competencyId: number ): Promise { try { this.logger.log( - `Initiated deleting admin-competency with id #${id} and competency id #${competencyId}` + `Initiated deleting admin-competency competency id #${competencyId}` ); const adminCompetency = await this.adminCompetencyService.remove( - id, competencyId ); this.logger.log( - `Successfully deleted admin-competency with id #${id} and competency id #${competencyId}` + `Successfully deleted admin-competency competency id #${competencyId}` ); return res.status(HttpStatus.OK).send({ - message: `Successfully deleted admin-competency with id #${id} and competency id #${competencyId}`, + message: `Successfully deleted admin-competency competency id #${competencyId}`, data: adminCompetency, }); } catch (error) { this.logger.error( - `Failed to delete admin-competency with id #${id} and competency id #${competencyId}`, + `Failed to delete admin-competency competency id #${competencyId}`, error ); @@ -216,7 +205,7 @@ export class AdminCompetencyController { statusCode, message: errorMessage || - `Failed to delete admin-competency with id #${id} and competency id #${competencyId}`, + `Failed to delete admin-competency competency id #${competencyId}`, }); } } diff --git a/src/admin-competency/admin-competency.service.ts b/src/admin-competency/admin-competency.service.ts index e5cd7db..b8e26ae 100644 --- a/src/admin-competency/admin-competency.service.ts +++ b/src/admin-competency/admin-competency.service.ts @@ -23,99 +23,108 @@ export class AdminCompetencyService { const response = await Promise.all( _.map(competenciesData, async (data) => { - const { competencyLevels, description, id, name } = data; - - const competencyLevelData = _.map( - competencyLevels, - (competencyLevelData) => { - const { levelNumber, name } = - competencyLevelData?.competencyLevel || {}; - - return { - competencyLevelNumber: levelNumber, - competencyLevelName: name, - }; - } - ); - - const adminCompetencyPayload = { - id, - competencyLevels: JSON.parse(JSON.stringify(competencyLevelData)), - competencyId: id, - name, - description, - }; + const adminCompetencyPayload = this.createAdminCompetencyPayload(data); - return await this.prisma.adminCompetency.upsert({ - where: { - competencyId: adminCompetencyPayload.competencyId, - }, - update: adminCompetencyPayload, - create: adminCompetencyPayload, - }); + return await this.createOrUpdateQuery(adminCompetencyPayload); }) ); return response; } + public createAdminCompetencyPayload(competency) { + const { competencyLevels, description, id, name } = competency; + + const competencyLevelData = _.map( + competencyLevels, + (competencyLevelData) => { + const { levelNumber, name } = + competencyLevelData?.competencyLevel || {}; + + return { + competencyLevelNumber: levelNumber, + competencyLevelName: name, + }; + } + ); + + return { + id, + competencyLevels: JSON.parse(JSON.stringify(competencyLevelData)), + competencyId: id, + name, + description, + }; + } + + public async createOrUpdateQuery(adminCompetencyPayload) { + return this.prisma.adminCompetency.upsert({ + where: { + competencyId: adminCompetencyPayload.competencyId, + }, + update: adminCompetencyPayload, + create: adminCompetencyPayload, + }); + } + public async findAll() { const adminCompetency = await this.prisma.adminCompetency.findMany(); return adminCompetency; } - public async findOne(id: number, competencyId: number) { + public async findOne(competencyId: number) { const adminCompetency = await this.prisma.adminCompetency.findUnique({ where: { - id_competencyId: { - id, - competencyId, - }, + competencyId, }, }); + if (!adminCompetency) throw new NotFoundException( - `Admin competency with id #${id} and competency id #${competencyId} not found ` + `Admin competency with competency id #${competencyId} not found ` ); return adminCompetency; } - public async update( - id: number, - competencyId: number, - updateAdminCompetencyDto: UpdateAdminCompetencyDto - ) { - const competencyLevels = JSON.stringify( - updateAdminCompetencyDto?.competencyLevels - ); - - let payload = { - ...updateAdminCompetencyDto, - competencyLevels: JSON.parse(competencyLevels || "[]"), - }; - - if (!competencyLevels) { - delete payload.competencyLevels; - } + public async update(competencyId: number) { + const adminCompetencyData = await this.prisma.adminCompetency.findUnique({ + where: { competencyId }, + }); - return await this.prisma.adminCompetency.update({ - where: { - id_competencyId: { - id, - competencyId, + const competencyData = await this.prisma.competency.findUnique({ + where: { id: competencyId }, + include: { + competencyLevels: { + include: { + competencyLevel: true, + }, }, }, - data: payload, }); + + if (!adminCompetencyData && !competencyData) { + throw new Error( + `Competency not found with competency Id #${competencyId}` + ); + } + + if (adminCompetencyData && !competencyData) { + await this.prisma.adminCompetency.delete({ where: { competencyId } }); + return; + } + + if (competencyData) { + const adminCompetencyPayload = + this.createAdminCompetencyPayload(competencyData); + + return await this.createOrUpdateQuery(adminCompetencyPayload); + } } - public async remove(id: number, competencyId: number) { + public async remove(competencyId: number) { const adminCompetency = await this.prisma.adminCompetency.delete({ where: { - id_competencyId: { - id, - competencyId, - }, + competencyId, }, }); return adminCompetency; diff --git a/src/survey/survey.controller.ts b/src/survey/survey.controller.ts index b1d8ffc..b5e2843 100644 --- a/src/survey/survey.controller.ts +++ b/src/survey/survey.controller.ts @@ -1,5 +1,4 @@ import { - Body, Controller, Get, HttpStatus, @@ -9,11 +8,12 @@ import { Post, Res, } from "@nestjs/common"; -import { SurveyService } from "./survey.service"; -import { ApiOperation, ApiResponse } from "@nestjs/swagger"; +import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; +import { SurveyService } from "./survey.service"; @Controller("survey") +@ApiTags("survey") export class SurveyController { private readonly logger = new Logger(SurveyController.name); constructor(private readonly surveyService: SurveyService) {} @@ -55,9 +55,9 @@ export class SurveyController { } } - @Get() + @Get("user-filled-survey/:userId") @ApiOperation({ summary: "Get number of surveys to be filled by User." }) - @ApiResponse({ status: HttpStatus.CREATED, type: "number" }) + @ApiResponse({ status: HttpStatus.OK, type: "number" }) async getSurveysToBeFilledByUser( @Res() res, @Param("userId", ParseUUIDPipe) userId: string @@ -75,7 +75,7 @@ export class SurveyController { `Successfully fetched the number of surveys to be filled by user with id #${userId}.` ); - return res.status(HttpStatus.CREATED).json({ + return res.status(HttpStatus.OK).json({ data: surveyForms, message: `Successfully fetched the number of surveys to be filled by user with id #${userId}.`, }); diff --git a/src/user-metadata/user-metadata.controller.ts b/src/user-metadata/user-metadata.controller.ts index 396ed20..2c99a20 100644 --- a/src/user-metadata/user-metadata.controller.ts +++ b/src/user-metadata/user-metadata.controller.ts @@ -27,7 +27,7 @@ export class UserMetadataController { @Post("sync-user-metadata") @ApiOperation({ summary: "Sync userMetadata with user org" }) - @ApiResponse({ status: HttpStatus.OK, type: UserResponseMessage }) + @ApiResponse({ status: HttpStatus.CREATED, type: UserResponseMessage }) async SyncUserDataWithFrac(@Res() res): Promise { try { this.logger.log(`Initiated sync of userMetadata with user org`); @@ -37,8 +37,9 @@ export class UserMetadataController { this.logger.log(`Successfully sync userMetadata with user org.`); - return res.status(HttpStatus.OK).json({ + return res.status(HttpStatus.CREATED).json({ message: "Successfully sync userMetadata with user org.", + data: userMetadata, }); } catch (error) { this.logger.error(`Failed to sync userMetadata with user org.`, error); diff --git a/src/user-metadata/user-metadata.service.ts b/src/user-metadata/user-metadata.service.ts index 311e32f..a469638 100644 --- a/src/user-metadata/user-metadata.service.ts +++ b/src/user-metadata/user-metadata.service.ts @@ -36,7 +36,7 @@ export class UserMetadataService { const userMetadataPayload = { userId: id, - userName: `${userName}_${id}`, + userName, departmentId: _.get(Department, "id", 0), designation, isNewEmployee, From 1dd987d8278aba990ce33e0abeeafa7fe2132533 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Fri, 3 Nov 2023 15:19:44 +0530 Subject: [PATCH 094/175] User seed data update. --- prisma/seeds/data/mock-users.data.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/prisma/seeds/data/mock-users.data.ts b/prisma/seeds/data/mock-users.data.ts index 8215e7d..4e38fca 100644 --- a/prisma/seeds/data/mock-users.data.ts +++ b/prisma/seeds/data/mock-users.data.ts @@ -58,7 +58,7 @@ export const users = [ userName: "Deepa Mishra", password: "P@ssw0rd6", designation: "Software Architect", - departmentId: 1, + departmentId: 2, levelNumber: 4, // competency level for Deepa Mishra }, { @@ -68,7 +68,7 @@ export const users = [ userName: "Rahul Verma", password: "P@ssw0rd7", designation: "Software Architect", - departmentId: 1, + departmentId: 2, levelNumber: 3, // competency level for Rahul Verma }, { @@ -78,7 +78,7 @@ export const users = [ userName: "Anita Sen", password: "P@ssw0rd8", designation: "Software Engineer", - departmentId: 1, + departmentId: 2, levelNumber: 2, // competency level for Anita Sen }, { @@ -88,7 +88,7 @@ export const users = [ userName: "Mohit Gupta", password: "P@ssw0rd9", designation: "Software Engineer", - departmentId: 1, + departmentId: 2, levelNumber: 3, // competency level for Mohit Gupta }, { @@ -98,7 +98,7 @@ export const users = [ userName: "Pooja Rawat", password: "P@ssw0rd10", designation: "Software Engineer", - departmentId: 1, + departmentId: 3, levelNumber: 3, // competency level for Pooja Rawat }, { @@ -108,7 +108,7 @@ export const users = [ userName: "Anurag Sharma", password: "P@ssw0rd11", designation: "Software Engineer", - departmentId: 1, + departmentId: 3, levelNumber: 2, // competency level for Anurag Sharma }, { @@ -118,7 +118,7 @@ export const users = [ userName: "Meera Rani", password: "P@ssw0rd12", designation: "Software Engineer", - departmentId: 1, + departmentId: 3, levelNumber: 2, // competency level for Meera Rani }, { @@ -128,7 +128,7 @@ export const users = [ userName: "Vikas Patel", password: "P@ssw0rd13", designation: "Software Engineer", - departmentId: 1, + departmentId: 4, levelNumber: 3, // competency level for Vikas Patel }, { @@ -138,7 +138,7 @@ export const users = [ userName: "Divya Malik", password: "P@ssw0rd14", designation: "Software Engineer", - departmentId: 1, + departmentId: 5, levelNumber: 2, // competency level for Divya Malik }, { @@ -148,7 +148,7 @@ export const users = [ userName: "Rajiv Khanna", password: "P@ssw0rd15", designation: "Software Engineer", - departmentId: 1, + departmentId: 3, levelNumber: 4, // competency level for Rajiv Khanna }, { From 9cf3f0b9ec50fe4ed4612ea5c06e20a2f69ffe9f Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Fri, 3 Nov 2023 15:23:12 +0530 Subject: [PATCH 095/175] bugs fixed --- src/question-bank/question-bank.service.ts | 41 +++++++++++++------ .../survey-cycle-parameter.controller.ts | 5 ++- .../survey-cycle-parameter.service.ts | 32 ++++++++------- src/survey-form/survey-form.service.ts | 3 +- src/survey/survey.controller.ts | 4 +- src/survey/survey.service.ts | 38 ++++++++++++++++- 6 files changed, 89 insertions(+), 34 deletions(-) diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index 2c56bab..71561cc 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -1,16 +1,17 @@ import { Injectable, NotFoundException } from "@nestjs/common"; +import _ from "lodash"; +import { FileUploadService } from "src/file-upload/file-upload.service"; +import { MockCompetencyService } from "src/mockModules/mock-competency/mock-competency.service"; +import { MockDesignationService } from "src/mockModules/mock-designation/mock-designation.service"; +import { MockUserService } from "src/mockModules/mock-user/mock-user.service"; +import { PrismaService } from "src/prisma/prisma.service"; +import { MockRoleService } from "./../mockModules/mock-role/mock-role.service"; import { CreateFileUploadDto, CreateQuestionBankDto, QuestionBankFilterDto, } from "./dto/create-question-bank.dto"; import { UpdateQuestionBankDto } from "./dto/update-question-bank.dto"; -import { PrismaService } from "src/prisma/prisma.service"; -import { MockCompetencyService } from "src/mockModules/mock-competency/mock-competency.service"; -import { MockUserService } from "src/mockModules/mock-user/mock-user.service"; -import { MockRoleService } from "./../mockModules/mock-role/mock-role.service"; -import { MockDesignationService } from "src/mockModules/mock-designation/mock-designation.service"; -import { FileUploadService } from "src/file-upload/file-upload.service"; @Injectable() export class QuestionBankService { @@ -85,7 +86,7 @@ export class QuestionBankService { ); } // Otherwise update it's details - return this.prisma.questionBank.updateMany({ + return this.prisma.questionBank.update({ where: { id: questionId, }, @@ -226,8 +227,6 @@ export class QuestionBankService { } } - - async getAllQuestionsForUser(userId: string) { const user = await this.mockUserService.findOne(userId); if (!user) { @@ -270,15 +269,31 @@ export class QuestionBankService { if (competencyIds?.length && competencyLevelNumber) { for (let i = 0; i < competencyIds.length; i++) { while (competencyLevelNumber) { - let question = await this.getAllQuestions({ - competencyId: competencyIds[i], - competencyLevelNumber: competencyLevelNumber, + const questions = await this.prisma.questionBank.findMany({ + where: { + competencyId: competencyIds[i], + competencyLevelNumber, + }, + select: { + id: true, + question: true, + }, }); - questionLists.push(...question); + + const mappedQuestions = _.map(questions, (data) => { + const { id, question } = data; + return { + question, + questionId: id, + }; + }); + + questionLists.push(...mappedQuestions); competencyLevelNumber--; } } } + if (!questionLists.length) { throw new NotFoundException(`No Question Found for user with #${userId}`); } diff --git a/src/survey-cycle-parameter/survey-cycle-parameter.controller.ts b/src/survey-cycle-parameter/survey-cycle-parameter.controller.ts index b3ca9c6..0e7dd43 100644 --- a/src/survey-cycle-parameter/survey-cycle-parameter.controller.ts +++ b/src/survey-cycle-parameter/survey-cycle-parameter.controller.ts @@ -13,8 +13,11 @@ import { } from "@nestjs/common"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; +import { + CreateSurveyCycleParameterDto, + SurveyCycleParameterFilterDto, +} from "./dto/create-survey-cycle-parameter.dto"; import { ResponseSurveyCycleParameterDto } from "./dto/response-survey-cycle-parameter.dto"; -import { CreateSurveyCycleParameterDto, SurveyCycleParameterFilterDto } from "./dto/create-survey-cycle-parameter.dto"; import { UpdateSurveyCycleParameterDto } from "./dto/update-survey-cycle-parameter.dto"; import { SurveyCycleParameterService } from "./survey-cycle-parameter.service"; diff --git a/src/survey-cycle-parameter/survey-cycle-parameter.service.ts b/src/survey-cycle-parameter/survey-cycle-parameter.service.ts index 333f4ac..5a14b7a 100644 --- a/src/survey-cycle-parameter/survey-cycle-parameter.service.ts +++ b/src/survey-cycle-parameter/survey-cycle-parameter.service.ts @@ -4,7 +4,10 @@ import { NotFoundException, } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; -import { CreateSurveyCycleParameterDto, SurveyCycleParameterFilterDto } from "./dto/create-survey-cycle-parameter.dto"; +import { + CreateSurveyCycleParameterDto, + SurveyCycleParameterFilterDto, +} from "./dto/create-survey-cycle-parameter.dto"; import { UpdateSurveyCycleParameterDto } from "./dto/update-survey-cycle-parameter.dto"; @Injectable() @@ -33,43 +36,42 @@ export class SurveyCycleParameterService { } async getAllSurveyParameter(filter: SurveyCycleParameterFilterDto) { - const {departmentId, surveyConfigId, isActive} = filter; + const { departmentId, surveyConfigId, isActive } = filter; return this.prisma.surveyCycleParameter.findMany({ - where:{ + where: { isActive: isActive ?? undefined, - SurveyConfig:{ + SurveyConfig: { id: surveyConfigId ?? undefined, - AdminDepartment:{ - id: departmentId ??undefined - } - } + AdminDepartment: { + id: departmentId ?? undefined, + }, + }, }, orderBy: { createdAt: "desc", - } + }, }); } async updateSurveyParameterById( - surveyParameterId: number, + id: number, updateSurveyCycleParameter: UpdateSurveyCycleParameterDto ) { // check if for the given id, data is available in db or not const findSurveyParameterId = await this.prisma.surveyCycleParameter.findUnique({ where: { - id: surveyParameterId, + id, }, }); + if (!findSurveyParameterId) { - throw new NotFoundException( - `Survey parameter with ID ${findSurveyParameterId} not found.` - ); + throw new NotFoundException(`Survey parameter with ID ${id} not found.`); } // update an existing survey parameter by its unique identifier (id). return this.prisma.surveyCycleParameter.update({ where: { - id: surveyParameterId, + id, }, data: updateSurveyCycleParameter, }); diff --git a/src/survey-form/survey-form.service.ts b/src/survey-form/survey-form.service.ts index f57e150..d3eecae 100644 --- a/src/survey-form/survey-form.service.ts +++ b/src/survey-form/survey-form.service.ts @@ -14,8 +14,9 @@ export class SurveyFormService { JSON.stringify(createSurveyFormDto.questionsJson) ), }; + return await this.prisma.surveyForm.create({ - data: { ...surveyFormDto }, + data: surveyFormDto, }); } diff --git a/src/survey/survey.controller.ts b/src/survey/survey.controller.ts index b5e2843..f6b77f5 100644 --- a/src/survey/survey.controller.ts +++ b/src/survey/survey.controller.ts @@ -18,12 +18,12 @@ export class SurveyController { private readonly logger = new Logger(SurveyController.name); constructor(private readonly surveyService: SurveyService) {} - @Post() + @Post(":departmentId") @ApiOperation({ summary: "Create Survey-Forms for a department." }) @ApiResponse({ status: HttpStatus.CREATED }) async generateSurveyFormsForDepartment( @Res() res, - @Param() departmentId: number + @Param("departmentId") departmentId: number ) { try { this.logger.log(`Initiated creating new survey forms for a department.`); diff --git a/src/survey/survey.service.ts b/src/survey/survey.service.ts index 6c36a28..c132f31 100644 --- a/src/survey/survey.service.ts +++ b/src/survey/survey.service.ts @@ -1,5 +1,10 @@ -import { Injectable, NotFoundException } from "@nestjs/common"; +import { + BadRequestException, + Injectable, + NotFoundException, +} from "@nestjs/common"; import { ResponseTrackerStatusEnum, SurveyStatusEnum } from "@prisma/client"; +import _ from "lodash"; import { PrismaService } from "src/prisma/prisma.service"; import { CreateSurveyFormDto, @@ -97,6 +102,35 @@ export class SurveyService { departmentId, }, }); + + const surveyCycleParameter = await this.prisma.surveyConfig.findFirst({ + where: { + departmentId, + SurveyCycleParameters: { + every: { isActive: true }, + }, + }, + select: { + SurveyCycleParameters: { + select: { + id: true, + }, + }, + }, + }); + + const surveyCycleParameterId = _.get( + surveyCycleParameter, + "SurveyCycleParameters.[0].id", + 0 + ); + + if (!surveyCycleParameterId || _.isEqual(surveyCycleParameterId, 0)) { + throw new BadRequestException( + `No any active survey cycle found for department id #${departmentId}` + ); + } + for (const user of users) { //get questions for the user according to their designation const questions = await this.questionBank.getAllQuestionsForUser( @@ -105,7 +139,7 @@ export class SurveyService { //create the CreateSurveyFormDto for the user const surveyFormDto: CreateSurveyFormDto = { userId: user.userId, - surveyCycleParameterId: 1, + surveyCycleParameterId, status: SurveyStatusEnum.PUBLISHED, questionsJson: questions, }; From 8bdb7a77303879c2a8101da3d31c6317a1060fbf Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Thu, 2 Nov 2023 17:54:04 +0530 Subject: [PATCH 096/175] Latest Survey, response and survey form for user APIs --- .../dto/update-response-tracker.dto.ts | 47 ++++++- .../response-tracker.controller.ts | 28 ++-- .../response-tracker.service.ts | 26 ++++ src/survey-form/survey-form.controller.ts | 39 ++++++ src/survey-form/survey-form.service.ts | 22 ++++ src/survey/survey.controller.ts | 120 ++++++++++++++---- src/survey/survey.service.ts | 18 ++- 7 files changed, 258 insertions(+), 42 deletions(-) diff --git a/src/response-tracker/dto/update-response-tracker.dto.ts b/src/response-tracker/dto/update-response-tracker.dto.ts index b34cde1..da7127c 100644 --- a/src/response-tracker/dto/update-response-tracker.dto.ts +++ b/src/response-tracker/dto/update-response-tracker.dto.ts @@ -1,4 +1,45 @@ -import { PartialType } from '@nestjs/mapped-types'; -import { CreateResponseTrackerDto } from './create-response-tracker.dto'; +import { ApiProperty } from "@nestjs/swagger"; +import { Type } from "class-transformer"; +import { + ArrayNotEmpty, + IsArray, + IsInt, + IsNotEmpty, + IsUUID, + ValidateNested, +} from "class-validator"; +import { AnswerEnum } from "../enums/response-tracker.enums"; +import { responseObject } from "./create-response-tracker.dto"; -export class UpdateResponseTrackerDto extends PartialType(CreateResponseTrackerDto) {} +export class UpdateResponseTrackerDto { + @ApiProperty({ type: "integer", example: 1 }) + @IsNotEmpty() + @IsInt() + surveyFormId: number; + + @ApiProperty({ type: "integer", example: 2 }) + @IsNotEmpty() + @IsUUID() + assesseeId: string; + + @ApiProperty({ type: "integer", example: 3 }) + @IsNotEmpty() + @IsUUID() + assessorId: string; + + @ApiProperty({ + type: responseObject, + isArray: true, + example: [ + { + questionId: 1, + answer: `${AnswerEnum.YES} || ${AnswerEnum.NO} || ${AnswerEnum.DO_NOT_KNOW}`, + }, + ], + }) + @IsArray() + @ArrayNotEmpty() + @ValidateNested({ each: true }) + @Type(() => responseObject) + responseJson: responseObject[] = []; +} diff --git a/src/response-tracker/response-tracker.controller.ts b/src/response-tracker/response-tracker.controller.ts index d7cefd8..237afeb 100644 --- a/src/response-tracker/response-tracker.controller.ts +++ b/src/response-tracker/response-tracker.controller.ts @@ -246,29 +246,34 @@ export class ResponseTrackerController { } } - @Patch(":id") - @ApiOperation({ summary: "update response tracker by id" }) + @Patch() + @ApiOperation({ + summary: + "update response tracker by surveyFormId, assesseeId and assessorId", + }) @ApiResponse({ status: HttpStatus.OK, type: ResponseTrackerDtoResponse }) async update( @Res() res, - @Param("id", ParseIntPipe) id: number, @Body() updateResponseTrackerDto: UpdateResponseTrackerDto ): Promise { + const { surveyFormId, assesseeId, assessorId } = updateResponseTrackerDto; try { - this.logger.log(`Initiated updating response tracker with id #${id}`); - - const updatedResponse = await this.responseTrackerService.update( - id, - updateResponseTrackerDto + this.logger.log( + `Initiated updating response tracker with surveyFormId #${surveyFormId}, assesseeId #${assesseeId} and assessorId #${assessorId}` ); + const updatedResponse = + await this.responseTrackerService.updateBySurveyFormId( + updateResponseTrackerDto + ); + return res.status(HttpStatus.OK).json({ data: updatedResponse, - message: `Successfully updated response tracker with id ${id}`, + message: `Successfully updated response tracker with surveyFormId ${surveyFormId}, assesseeId #${assesseeId} and assessorId #${assessorId}`, }); } catch (error) { this.logger.error( - `Failed to update response tracker with id #${id}`, + `Failed to update response tracker with surveyFormId #${surveyFormId}, assesseeId #${assesseeId} and assessorId #${assessorId}`, error ); @@ -278,7 +283,8 @@ export class ResponseTrackerController { return res.status(statusCode).json({ statusCode, message: - errorMessage || `Failed to update response tracker with id #${id}`, + errorMessage || + `Failed to update response tracker with surveyFormId #${surveyFormId}, assesseeId #${assesseeId} and assessorId #${assessorId}`, }); } } diff --git a/src/response-tracker/response-tracker.service.ts b/src/response-tracker/response-tracker.service.ts index c97e8c7..19abc9e 100644 --- a/src/response-tracker/response-tracker.service.ts +++ b/src/response-tracker/response-tracker.service.ts @@ -3,6 +3,7 @@ import { PrismaService } from "..//prisma/prisma.service"; import { CreateResponseTrackerDto } from "./dto/create-response-tracker.dto"; import { UpdateResponseTrackerDto } from "./dto/update-response-tracker.dto"; import { IResponseTracker } from "./interfaces/response-tracker.interface"; +import { ResponseTrackerStatusEnum } from "@prisma/client"; @Injectable() export class ResponseTrackerService { @@ -163,4 +164,29 @@ export class ResponseTrackerService { return result; } + + public async updateBySurveyFormId( + updateResponseTrackerDto: UpdateResponseTrackerDto + ) { + const { surveyFormId, assesseeId, assessorId } = updateResponseTrackerDto; + const responseJson = JSON.stringify(updateResponseTrackerDto.responseJson); + + const payload = { + responseJson: JSON.parse(responseJson || "[]"), + status: ResponseTrackerStatusEnum.COMPLETED, + }; + + if (!responseJson) delete payload.responseJson; + + return await this.prisma.responseTracker.update({ + where: { + surveyFormId_assesseeId_assessorId: { + surveyFormId, + assesseeId, + assessorId, + }, + }, + data: payload, + }); + } } diff --git a/src/survey-form/survey-form.controller.ts b/src/survey-form/survey-form.controller.ts index 2fc53cc..6902608 100644 --- a/src/survey-form/survey-form.controller.ts +++ b/src/survey-form/survey-form.controller.ts @@ -147,4 +147,43 @@ export class SurveyFormController { .json({ message: errorMessage || "Could not delete survey form." }); } } + + @Get("latest-survey-form/:userId") + @ApiOperation({ summary: "Fetch latest Survey-Form by userId" }) + @ApiResponse({ status: HttpStatus.FOUND, type: SurveyFormResponse }) + async fetchLatestSurveyFormByUserId( + @Res() res, + @Param("userId") userId: string + ): Promise { + try { + this.logger.log( + `Initiated fetching latest Survey-Form by userId #${userId}` + ); + + const surveyForm = + await this.surveyFormService.fetchLatestSurveyFormByUserId(userId); + + this.logger.log( + `Successfully fetched latest Survey-Form by userId #${userId}` + ); + + return res.status(HttpStatus.OK).send({ + data: surveyForm, + message: `Successfully fetched latest Survey-Form by userId #${userId}`, + }); + } catch (error) { + this.logger.error( + `Failed to fetch latest Survey-Form by userId #${userId}`, + error + ); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + return res.status(statusCode).send({ + message: + errorMessage || + `Could not find latest Survey-Form by userId #${userId}`, + }); + } + } } diff --git a/src/survey-form/survey-form.service.ts b/src/survey-form/survey-form.service.ts index 1c49817..f73edf8 100644 --- a/src/survey-form/survey-form.service.ts +++ b/src/survey-form/survey-form.service.ts @@ -125,4 +125,26 @@ export class SurveyFormService { return latestSurveyForm[0]; } + + async fetchLatestSurveyFormByUserId(userId: string) { + return await this.prisma.surveyForm.findFirst({ + where: { + userId, + status: SurveyStatusEnum.PUBLISHED, + surveyCycleParameter: { + isActive: true, + }, + }, + select: { + id: true, + questionsJson: true, + UserMetadata: { + select: { + userId: true, + designation: true, + }, + }, + }, + }); + } } diff --git a/src/survey/survey.controller.ts b/src/survey/survey.controller.ts index 56e8380..b1d8ffc 100644 --- a/src/survey/survey.controller.ts +++ b/src/survey/survey.controller.ts @@ -1,11 +1,21 @@ -import { Body, Controller, Get, HttpStatus, Logger, Param, ParseUUIDPipe, Post, Res } from '@nestjs/common'; -import { SurveyService } from './survey.service'; -import { ApiOperation, ApiResponse } from '@nestjs/swagger'; -import { getPrismaErrorStatusAndMessage } from 'src/utils/utils'; +import { + Body, + Controller, + Get, + HttpStatus, + Logger, + Param, + ParseUUIDPipe, + Post, + Res, +} from "@nestjs/common"; +import { SurveyService } from "./survey.service"; +import { ApiOperation, ApiResponse } from "@nestjs/swagger"; +import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; -@Controller('survey') +@Controller("survey") export class SurveyController { - private readonly logger = new Logger(SurveyController.name); + private readonly logger = new Logger(SurveyController.name); constructor(private readonly surveyService: SurveyService) {} @Post() @@ -13,26 +23,35 @@ export class SurveyController { @ApiResponse({ status: HttpStatus.CREATED }) async generateSurveyFormsForDepartment( @Res() res, - @Param() departmentId: number, - ){ + @Param() departmentId: number + ) { try { this.logger.log(`Initiated creating new survey forms for a department.`); - const surveyForms = await this.surveyService.generateSurveyFormsForDepartment(departmentId); + const surveyForms = + await this.surveyService.generateSurveyFormsForDepartment(departmentId); - this.logger.log(`Successfully created new survey forms for a department.`); + this.logger.log( + `Successfully created new survey forms for a department.` + ); return res.status(HttpStatus.CREATED).json({ data: surveyForms, - message: `SurveyForms created successfully for depatrment with id #${departmentId}`, + message: `SurveyForms created successfully for department with id #${departmentId}`, }); } catch (error) { - this.logger.error(`Failed to create new survey forms for depatrment with id #${departmentId}`, error); + this.logger.error( + `Failed to create new survey forms for department with id #${departmentId}`, + error + ); - const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); - return res - .status(statusCode) - .json({ message: errorMessage || `Could not create survey forms for depatrment with id #${departmentId}` }); + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + return res.status(statusCode).json({ + message: + errorMessage || + `Could not create survey forms for department with id #${departmentId}`, + }); } } @@ -42,25 +61,76 @@ export class SurveyController { async getSurveysToBeFilledByUser( @Res() res, @Param("userId", ParseUUIDPipe) userId: string - ){ + ) { try { - this.logger.log(`Getting number of surveys to be filled by user with id #${userId}.`); + this.logger.log( + `Getting number of surveys to be filled by user with id #${userId}.` + ); - const surveyForms = await this.surveyService.getSurveysToBeFilledByUser(userId); + const surveyForms = await this.surveyService.getSurveysToBeFilledByUser( + userId + ); - this.logger.log(`Successfully fetched the number of surveys to be filled by user with id #${userId}.`); + this.logger.log( + `Successfully fetched the number of surveys to be filled by user with id #${userId}.` + ); return res.status(HttpStatus.CREATED).json({ data: surveyForms, message: `Successfully fetched the number of surveys to be filled by user with id #${userId}.`, }); } catch (error) { - this.logger.error(`Failed to fetch the number of surveys to be filled by user with id #${userId}.`, error); + this.logger.error( + `Failed to fetch the number of surveys to be filled by user with id #${userId}.`, + error + ); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + return res.status(statusCode).json({ + message: + errorMessage || + `Could not fetch the number of surveys to be filled by user with id #${userId}.`, + }); + } + } + + @Get("latest-survey-response/:userId") + @ApiOperation({ summary: "Fetch latest survey response by userId" }) + @ApiResponse({ status: HttpStatus.OK }) + async getLatestSurveyResponsesForUserId( + @Res() res, + @Param("userId") userId: string + ) { + try { + this.logger.log( + `Initiated fetching latest survey response by userId #${userId}` + ); + + const surveyForm = + await this.surveyService.getLatestSurveyResponsesForUserId(userId); + + this.logger.log( + `Successfully fetched latest survey response by userId #${userId}` + ); + + return res.status(HttpStatus.OK).send({ + data: surveyForm, + message: `Successfully fetched latest survey response by userId #${userId}`, + }); + } catch (error) { + this.logger.error( + `Failed to fetch latest survey response by userId #${userId}`, + error + ); - const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); - return res - .status(statusCode) - .json({ message: errorMessage || `Could not fetch the number of surveys to be filled by user with id #${userId}.` }); + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + return res.status(statusCode).send({ + message: + errorMessage || + `Could not find latest survey response by userId #${userId}`, + }); } } } diff --git a/src/survey/survey.service.ts b/src/survey/survey.service.ts index d1c9923..6c36a28 100644 --- a/src/survey/survey.service.ts +++ b/src/survey/survey.service.ts @@ -55,6 +55,11 @@ export class SurveyService { surveyCycleParameter: { isActive: true, }, + ResponseTracker: { + every: { + assesseeId: userId, + }, + }, }, orderBy: { createdAt: "desc", @@ -65,8 +70,13 @@ export class SurveyService { id: true, surveyFormId: true, assesseeId: true, - assessorId: true, - responseJson: true, + Assessor: { + select: { + userId: true, + userName: true, + designation: true, + }, + }, status: true, }, }, @@ -89,7 +99,9 @@ export class SurveyService { }); for (const user of users) { //get questions for the user according to their designation - const questions = await this.questionBank.getAllQuestionsForUser(user.userId); + const questions = await this.questionBank.getAllQuestionsForUser( + user.userId + ); //create the CreateSurveyFormDto for the user const surveyFormDto: CreateSurveyFormDto = { userId: user.userId, From e9da377c9e29ee66e951854d503960a375e513d9 Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Fri, 3 Nov 2023 11:31:23 +0530 Subject: [PATCH 097/175] updated --- .../admin-competency.controller.ts | 53 +++---- .../admin-competency.service.ts | 135 ++++++++++-------- src/survey/survey.controller.ts | 12 +- src/user-metadata/user-metadata.controller.ts | 5 +- src/user-metadata/user-metadata.service.ts | 2 +- 5 files changed, 103 insertions(+), 104 deletions(-) diff --git a/src/admin-competency/admin-competency.controller.ts b/src/admin-competency/admin-competency.controller.ts index 5a8e346..78bddab 100644 --- a/src/admin-competency/admin-competency.controller.ts +++ b/src/admin-competency/admin-competency.controller.ts @@ -1,5 +1,4 @@ import { - Body, Controller, Delete, Get, @@ -17,8 +16,6 @@ import { AdminCompetencyService } from "./admin-competency.service"; import { AdminCompetencyArrayResponseDto, AdminCompetencyResponseDto, - CreateAdminCompetencyDto, - UpdateAdminCompetencyDto, } from "./dto"; @Controller("admin-competency") @@ -87,35 +84,33 @@ export class AdminCompetencyController { } } - @Get(":id/:competencyId") - @ApiOperation({ summary: "get admin-competency by id and competencyId" }) + @Get(":competencyId") + @ApiOperation({ summary: "get admin-competency by competencyId" }) @ApiResponse({ status: HttpStatus.OK, type: AdminCompetencyResponseDto }) async findOne( @Res() res, - @Param("id", ParseIntPipe) id: number, @Param("competencyId", ParseIntPipe) competencyId: number ): Promise { try { this.logger.log( - `Initiated fetching admin-competency with id #${id} and competency id #${competencyId}` + `Initiated fetching admin-competency with competency id #${competencyId}` ); const adminCompetency = await this.adminCompetencyService.findOne( - id, competencyId ); this.logger.log( - `Successfully fetched admin-competency with id #${id} and competency id #${competencyId}` + `Successfully fetched admin-competency with competency id #${competencyId}` ); return res.status(HttpStatus.OK).send({ - message: `Successfully fetched admin-competency with id #${id} and competency id #${competencyId}`, + message: `Successfully fetched admin-competency with competency id #${competencyId}`, data: adminCompetency, }); } catch (error) { this.logger.error( - `Failed to fetch admin-competency with id #${id} and competency id #${competencyId}`, + `Failed to fetch admin-competency with competency id #${competencyId}`, error ); @@ -126,42 +121,38 @@ export class AdminCompetencyController { statusCode, message: errorMessage || - `Failed to fetch admin-competency with id #${id} and competency id #${competencyId}`, + `Failed to fetch admin-competency with competency id #${competencyId}`, }); } } - @Patch(":id/:competencyId") + @Patch(":competencyId") @ApiOperation({ summary: "update admin-competency" }) @ApiResponse({ status: HttpStatus.OK, type: AdminCompetencyResponseDto }) async update( @Res() res, - @Param("id", ParseIntPipe) id: number, - @Param("competencyId", ParseIntPipe) competencyId: number, - @Body() updateAdminCompetencyDto: UpdateAdminCompetencyDto + @Param("competencyId", ParseIntPipe) competencyId: number ): Promise { try { this.logger.log( - `Initiated updating admin-competency with id #${id} and competency id #${competencyId}` + `Initiated updating admin-competency competency id #${competencyId}` ); const adminCompetency = await this.adminCompetencyService.update( - id, - competencyId, - updateAdminCompetencyDto + competencyId ); this.logger.log( - `Successfully updated admin-competency with id #${id} and competency id #${competencyId}` + `Successfully updated admin-competency competency id #${competencyId}` ); return res.status(HttpStatus.OK).send({ - message: `Successfully updated admin-competency with id #${id} and competency id #${competencyId}`, + message: `Successfully updated admin-competency competency id #${competencyId}`, data: adminCompetency, }); } catch (error) { this.logger.error( - `Failed to update admin-competency with id #${id} and competency id #${competencyId}`, + `Failed to update admin-competency competency id #${competencyId}`, error ); @@ -172,40 +163,38 @@ export class AdminCompetencyController { statusCode, message: errorMessage || - `Failed to update admin-competency with id #${id} and competency id #${competencyId}`, + `Failed to update admin-competency competency id #${competencyId}`, }); } } - @Delete(":id/:competencyId") + @Delete(":competencyId") @ApiOperation({ summary: "delete admin-competency" }) @ApiResponse({ status: HttpStatus.OK, type: AdminCompetencyResponseDto }) async remove( @Res() res, - @Param("id", ParseIntPipe) id: number, @Param("competencyId", ParseIntPipe) competencyId: number ): Promise { try { this.logger.log( - `Initiated deleting admin-competency with id #${id} and competency id #${competencyId}` + `Initiated deleting admin-competency competency id #${competencyId}` ); const adminCompetency = await this.adminCompetencyService.remove( - id, competencyId ); this.logger.log( - `Successfully deleted admin-competency with id #${id} and competency id #${competencyId}` + `Successfully deleted admin-competency competency id #${competencyId}` ); return res.status(HttpStatus.OK).send({ - message: `Successfully deleted admin-competency with id #${id} and competency id #${competencyId}`, + message: `Successfully deleted admin-competency competency id #${competencyId}`, data: adminCompetency, }); } catch (error) { this.logger.error( - `Failed to delete admin-competency with id #${id} and competency id #${competencyId}`, + `Failed to delete admin-competency competency id #${competencyId}`, error ); @@ -216,7 +205,7 @@ export class AdminCompetencyController { statusCode, message: errorMessage || - `Failed to delete admin-competency with id #${id} and competency id #${competencyId}`, + `Failed to delete admin-competency competency id #${competencyId}`, }); } } diff --git a/src/admin-competency/admin-competency.service.ts b/src/admin-competency/admin-competency.service.ts index e5cd7db..b8e26ae 100644 --- a/src/admin-competency/admin-competency.service.ts +++ b/src/admin-competency/admin-competency.service.ts @@ -23,99 +23,108 @@ export class AdminCompetencyService { const response = await Promise.all( _.map(competenciesData, async (data) => { - const { competencyLevels, description, id, name } = data; - - const competencyLevelData = _.map( - competencyLevels, - (competencyLevelData) => { - const { levelNumber, name } = - competencyLevelData?.competencyLevel || {}; - - return { - competencyLevelNumber: levelNumber, - competencyLevelName: name, - }; - } - ); - - const adminCompetencyPayload = { - id, - competencyLevels: JSON.parse(JSON.stringify(competencyLevelData)), - competencyId: id, - name, - description, - }; + const adminCompetencyPayload = this.createAdminCompetencyPayload(data); - return await this.prisma.adminCompetency.upsert({ - where: { - competencyId: adminCompetencyPayload.competencyId, - }, - update: adminCompetencyPayload, - create: adminCompetencyPayload, - }); + return await this.createOrUpdateQuery(adminCompetencyPayload); }) ); return response; } + public createAdminCompetencyPayload(competency) { + const { competencyLevels, description, id, name } = competency; + + const competencyLevelData = _.map( + competencyLevels, + (competencyLevelData) => { + const { levelNumber, name } = + competencyLevelData?.competencyLevel || {}; + + return { + competencyLevelNumber: levelNumber, + competencyLevelName: name, + }; + } + ); + + return { + id, + competencyLevels: JSON.parse(JSON.stringify(competencyLevelData)), + competencyId: id, + name, + description, + }; + } + + public async createOrUpdateQuery(adminCompetencyPayload) { + return this.prisma.adminCompetency.upsert({ + where: { + competencyId: adminCompetencyPayload.competencyId, + }, + update: adminCompetencyPayload, + create: adminCompetencyPayload, + }); + } + public async findAll() { const adminCompetency = await this.prisma.adminCompetency.findMany(); return adminCompetency; } - public async findOne(id: number, competencyId: number) { + public async findOne(competencyId: number) { const adminCompetency = await this.prisma.adminCompetency.findUnique({ where: { - id_competencyId: { - id, - competencyId, - }, + competencyId, }, }); + if (!adminCompetency) throw new NotFoundException( - `Admin competency with id #${id} and competency id #${competencyId} not found ` + `Admin competency with competency id #${competencyId} not found ` ); return adminCompetency; } - public async update( - id: number, - competencyId: number, - updateAdminCompetencyDto: UpdateAdminCompetencyDto - ) { - const competencyLevels = JSON.stringify( - updateAdminCompetencyDto?.competencyLevels - ); - - let payload = { - ...updateAdminCompetencyDto, - competencyLevels: JSON.parse(competencyLevels || "[]"), - }; - - if (!competencyLevels) { - delete payload.competencyLevels; - } + public async update(competencyId: number) { + const adminCompetencyData = await this.prisma.adminCompetency.findUnique({ + where: { competencyId }, + }); - return await this.prisma.adminCompetency.update({ - where: { - id_competencyId: { - id, - competencyId, + const competencyData = await this.prisma.competency.findUnique({ + where: { id: competencyId }, + include: { + competencyLevels: { + include: { + competencyLevel: true, + }, }, }, - data: payload, }); + + if (!adminCompetencyData && !competencyData) { + throw new Error( + `Competency not found with competency Id #${competencyId}` + ); + } + + if (adminCompetencyData && !competencyData) { + await this.prisma.adminCompetency.delete({ where: { competencyId } }); + return; + } + + if (competencyData) { + const adminCompetencyPayload = + this.createAdminCompetencyPayload(competencyData); + + return await this.createOrUpdateQuery(adminCompetencyPayload); + } } - public async remove(id: number, competencyId: number) { + public async remove(competencyId: number) { const adminCompetency = await this.prisma.adminCompetency.delete({ where: { - id_competencyId: { - id, - competencyId, - }, + competencyId, }, }); return adminCompetency; diff --git a/src/survey/survey.controller.ts b/src/survey/survey.controller.ts index b1d8ffc..b5e2843 100644 --- a/src/survey/survey.controller.ts +++ b/src/survey/survey.controller.ts @@ -1,5 +1,4 @@ import { - Body, Controller, Get, HttpStatus, @@ -9,11 +8,12 @@ import { Post, Res, } from "@nestjs/common"; -import { SurveyService } from "./survey.service"; -import { ApiOperation, ApiResponse } from "@nestjs/swagger"; +import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; +import { SurveyService } from "./survey.service"; @Controller("survey") +@ApiTags("survey") export class SurveyController { private readonly logger = new Logger(SurveyController.name); constructor(private readonly surveyService: SurveyService) {} @@ -55,9 +55,9 @@ export class SurveyController { } } - @Get() + @Get("user-filled-survey/:userId") @ApiOperation({ summary: "Get number of surveys to be filled by User." }) - @ApiResponse({ status: HttpStatus.CREATED, type: "number" }) + @ApiResponse({ status: HttpStatus.OK, type: "number" }) async getSurveysToBeFilledByUser( @Res() res, @Param("userId", ParseUUIDPipe) userId: string @@ -75,7 +75,7 @@ export class SurveyController { `Successfully fetched the number of surveys to be filled by user with id #${userId}.` ); - return res.status(HttpStatus.CREATED).json({ + return res.status(HttpStatus.OK).json({ data: surveyForms, message: `Successfully fetched the number of surveys to be filled by user with id #${userId}.`, }); diff --git a/src/user-metadata/user-metadata.controller.ts b/src/user-metadata/user-metadata.controller.ts index e343ffa..fdcc051 100644 --- a/src/user-metadata/user-metadata.controller.ts +++ b/src/user-metadata/user-metadata.controller.ts @@ -27,7 +27,7 @@ export class UserMetadataController { @Post("sync-user-metadata") @ApiOperation({ summary: "Sync userMetadata with user org" }) - @ApiResponse({ status: HttpStatus.OK, type: UserResponseMessage }) + @ApiResponse({ status: HttpStatus.CREATED, type: UserResponseMessage }) async SyncUserDataWithFrac(@Res() res): Promise { try { this.logger.log(`Initiated sync of userMetadata with user org`); @@ -37,8 +37,9 @@ export class UserMetadataController { this.logger.log(`Successfully sync userMetadata with user org.`); - return res.status(HttpStatus.OK).json({ + return res.status(HttpStatus.CREATED).json({ message: "Successfully sync userMetadata with user org.", + data: userMetadata, }); } catch (error) { this.logger.error(`Failed to sync userMetadata with user org.`, error); diff --git a/src/user-metadata/user-metadata.service.ts b/src/user-metadata/user-metadata.service.ts index c161c0a..c63f22d 100644 --- a/src/user-metadata/user-metadata.service.ts +++ b/src/user-metadata/user-metadata.service.ts @@ -35,7 +35,7 @@ export class UserMetadataService { const userMetadataPayload = { userId: id, - userName: `${userName}_${id}`, + userName, departmentId: _.get(Department, "id", 0), designation, isNewEmployee, From c94278f472f2e7f27efb131fbd45488824281208 Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Fri, 3 Nov 2023 15:23:12 +0530 Subject: [PATCH 098/175] bugs fixed --- src/question-bank/question-bank.service.ts | 41 +++++++++++++------ .../survey-cycle-parameter.controller.ts | 5 ++- .../survey-cycle-parameter.service.ts | 32 ++++++++------- src/survey-form/survey-form.service.ts | 3 +- src/survey/survey.controller.ts | 4 +- src/survey/survey.service.ts | 38 ++++++++++++++++- 6 files changed, 89 insertions(+), 34 deletions(-) diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index 2c56bab..71561cc 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -1,16 +1,17 @@ import { Injectable, NotFoundException } from "@nestjs/common"; +import _ from "lodash"; +import { FileUploadService } from "src/file-upload/file-upload.service"; +import { MockCompetencyService } from "src/mockModules/mock-competency/mock-competency.service"; +import { MockDesignationService } from "src/mockModules/mock-designation/mock-designation.service"; +import { MockUserService } from "src/mockModules/mock-user/mock-user.service"; +import { PrismaService } from "src/prisma/prisma.service"; +import { MockRoleService } from "./../mockModules/mock-role/mock-role.service"; import { CreateFileUploadDto, CreateQuestionBankDto, QuestionBankFilterDto, } from "./dto/create-question-bank.dto"; import { UpdateQuestionBankDto } from "./dto/update-question-bank.dto"; -import { PrismaService } from "src/prisma/prisma.service"; -import { MockCompetencyService } from "src/mockModules/mock-competency/mock-competency.service"; -import { MockUserService } from "src/mockModules/mock-user/mock-user.service"; -import { MockRoleService } from "./../mockModules/mock-role/mock-role.service"; -import { MockDesignationService } from "src/mockModules/mock-designation/mock-designation.service"; -import { FileUploadService } from "src/file-upload/file-upload.service"; @Injectable() export class QuestionBankService { @@ -85,7 +86,7 @@ export class QuestionBankService { ); } // Otherwise update it's details - return this.prisma.questionBank.updateMany({ + return this.prisma.questionBank.update({ where: { id: questionId, }, @@ -226,8 +227,6 @@ export class QuestionBankService { } } - - async getAllQuestionsForUser(userId: string) { const user = await this.mockUserService.findOne(userId); if (!user) { @@ -270,15 +269,31 @@ export class QuestionBankService { if (competencyIds?.length && competencyLevelNumber) { for (let i = 0; i < competencyIds.length; i++) { while (competencyLevelNumber) { - let question = await this.getAllQuestions({ - competencyId: competencyIds[i], - competencyLevelNumber: competencyLevelNumber, + const questions = await this.prisma.questionBank.findMany({ + where: { + competencyId: competencyIds[i], + competencyLevelNumber, + }, + select: { + id: true, + question: true, + }, }); - questionLists.push(...question); + + const mappedQuestions = _.map(questions, (data) => { + const { id, question } = data; + return { + question, + questionId: id, + }; + }); + + questionLists.push(...mappedQuestions); competencyLevelNumber--; } } } + if (!questionLists.length) { throw new NotFoundException(`No Question Found for user with #${userId}`); } diff --git a/src/survey-cycle-parameter/survey-cycle-parameter.controller.ts b/src/survey-cycle-parameter/survey-cycle-parameter.controller.ts index b3ca9c6..0e7dd43 100644 --- a/src/survey-cycle-parameter/survey-cycle-parameter.controller.ts +++ b/src/survey-cycle-parameter/survey-cycle-parameter.controller.ts @@ -13,8 +13,11 @@ import { } from "@nestjs/common"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; +import { + CreateSurveyCycleParameterDto, + SurveyCycleParameterFilterDto, +} from "./dto/create-survey-cycle-parameter.dto"; import { ResponseSurveyCycleParameterDto } from "./dto/response-survey-cycle-parameter.dto"; -import { CreateSurveyCycleParameterDto, SurveyCycleParameterFilterDto } from "./dto/create-survey-cycle-parameter.dto"; import { UpdateSurveyCycleParameterDto } from "./dto/update-survey-cycle-parameter.dto"; import { SurveyCycleParameterService } from "./survey-cycle-parameter.service"; diff --git a/src/survey-cycle-parameter/survey-cycle-parameter.service.ts b/src/survey-cycle-parameter/survey-cycle-parameter.service.ts index 333f4ac..5a14b7a 100644 --- a/src/survey-cycle-parameter/survey-cycle-parameter.service.ts +++ b/src/survey-cycle-parameter/survey-cycle-parameter.service.ts @@ -4,7 +4,10 @@ import { NotFoundException, } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; -import { CreateSurveyCycleParameterDto, SurveyCycleParameterFilterDto } from "./dto/create-survey-cycle-parameter.dto"; +import { + CreateSurveyCycleParameterDto, + SurveyCycleParameterFilterDto, +} from "./dto/create-survey-cycle-parameter.dto"; import { UpdateSurveyCycleParameterDto } from "./dto/update-survey-cycle-parameter.dto"; @Injectable() @@ -33,43 +36,42 @@ export class SurveyCycleParameterService { } async getAllSurveyParameter(filter: SurveyCycleParameterFilterDto) { - const {departmentId, surveyConfigId, isActive} = filter; + const { departmentId, surveyConfigId, isActive } = filter; return this.prisma.surveyCycleParameter.findMany({ - where:{ + where: { isActive: isActive ?? undefined, - SurveyConfig:{ + SurveyConfig: { id: surveyConfigId ?? undefined, - AdminDepartment:{ - id: departmentId ??undefined - } - } + AdminDepartment: { + id: departmentId ?? undefined, + }, + }, }, orderBy: { createdAt: "desc", - } + }, }); } async updateSurveyParameterById( - surveyParameterId: number, + id: number, updateSurveyCycleParameter: UpdateSurveyCycleParameterDto ) { // check if for the given id, data is available in db or not const findSurveyParameterId = await this.prisma.surveyCycleParameter.findUnique({ where: { - id: surveyParameterId, + id, }, }); + if (!findSurveyParameterId) { - throw new NotFoundException( - `Survey parameter with ID ${findSurveyParameterId} not found.` - ); + throw new NotFoundException(`Survey parameter with ID ${id} not found.`); } // update an existing survey parameter by its unique identifier (id). return this.prisma.surveyCycleParameter.update({ where: { - id: surveyParameterId, + id, }, data: updateSurveyCycleParameter, }); diff --git a/src/survey-form/survey-form.service.ts b/src/survey-form/survey-form.service.ts index f73edf8..888a7b8 100644 --- a/src/survey-form/survey-form.service.ts +++ b/src/survey-form/survey-form.service.ts @@ -14,8 +14,9 @@ export class SurveyFormService { JSON.stringify(createSurveyFormDto.questionsJson) ), }; + return await this.prisma.surveyForm.create({ - data: { ...surveyFormDto }, + data: surveyFormDto, }); } diff --git a/src/survey/survey.controller.ts b/src/survey/survey.controller.ts index b5e2843..f6b77f5 100644 --- a/src/survey/survey.controller.ts +++ b/src/survey/survey.controller.ts @@ -18,12 +18,12 @@ export class SurveyController { private readonly logger = new Logger(SurveyController.name); constructor(private readonly surveyService: SurveyService) {} - @Post() + @Post(":departmentId") @ApiOperation({ summary: "Create Survey-Forms for a department." }) @ApiResponse({ status: HttpStatus.CREATED }) async generateSurveyFormsForDepartment( @Res() res, - @Param() departmentId: number + @Param("departmentId") departmentId: number ) { try { this.logger.log(`Initiated creating new survey forms for a department.`); diff --git a/src/survey/survey.service.ts b/src/survey/survey.service.ts index 6c36a28..c132f31 100644 --- a/src/survey/survey.service.ts +++ b/src/survey/survey.service.ts @@ -1,5 +1,10 @@ -import { Injectable, NotFoundException } from "@nestjs/common"; +import { + BadRequestException, + Injectable, + NotFoundException, +} from "@nestjs/common"; import { ResponseTrackerStatusEnum, SurveyStatusEnum } from "@prisma/client"; +import _ from "lodash"; import { PrismaService } from "src/prisma/prisma.service"; import { CreateSurveyFormDto, @@ -97,6 +102,35 @@ export class SurveyService { departmentId, }, }); + + const surveyCycleParameter = await this.prisma.surveyConfig.findFirst({ + where: { + departmentId, + SurveyCycleParameters: { + every: { isActive: true }, + }, + }, + select: { + SurveyCycleParameters: { + select: { + id: true, + }, + }, + }, + }); + + const surveyCycleParameterId = _.get( + surveyCycleParameter, + "SurveyCycleParameters.[0].id", + 0 + ); + + if (!surveyCycleParameterId || _.isEqual(surveyCycleParameterId, 0)) { + throw new BadRequestException( + `No any active survey cycle found for department id #${departmentId}` + ); + } + for (const user of users) { //get questions for the user according to their designation const questions = await this.questionBank.getAllQuestionsForUser( @@ -105,7 +139,7 @@ export class SurveyService { //create the CreateSurveyFormDto for the user const surveyFormDto: CreateSurveyFormDto = { userId: user.userId, - surveyCycleParameterId: 1, + surveyCycleParameterId, status: SurveyStatusEnum.PUBLISHED, questionsJson: questions, }; From 0d1796d7c7b6dbaa838a27c155293ac7a8dcfa46 Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Mon, 6 Nov 2023 13:59:01 +0530 Subject: [PATCH 099/175] Added the working testcases --- .../admin-competency.controller.spec.ts | 63 +++++--- .../admin-competency.controller.ts | 2 +- .../admin-competency.module.ts | 12 +- .../admin-competency.service.ts | 2 +- .../admin-department.controller.spec.ts | 20 +-- .../mock-competency-level.module.ts | 4 +- .../mock-competency-level.service.ts | 2 +- .../mock-competency/mock-competency.module.ts | 2 + .../mock-competency.service.ts | 2 +- .../mock-designation.service.ts | 2 +- .../mock-role/mock-role.service.ts | 2 +- .../question-bank.controller.spec.ts | 152 ++++++++++++++++++ src/question-bank/question-bank.controller.ts | 4 +- src/question-bank/question-bank.module.ts | 16 +- src/question-bank/question-bank.service.ts | 12 +- 15 files changed, 237 insertions(+), 60 deletions(-) create mode 100644 src/question-bank/question-bank.controller.spec.ts diff --git a/src/admin-competency/admin-competency.controller.spec.ts b/src/admin-competency/admin-competency.controller.spec.ts index facb1af..ba2252e 100644 --- a/src/admin-competency/admin-competency.controller.spec.ts +++ b/src/admin-competency/admin-competency.controller.spec.ts @@ -41,21 +41,26 @@ describe("AdminCompetencyController e2e", () => { describe("AdminCompetencyController create()", () => { it("should return created admin competency on success", async function () { const createAdminCompetencyDto: CreateAdminCompetencyDto = { - competencyId: 1, + competencyId: 10, name: "Test create admin competency", competencyLevels: [ - { competencyLevelNumber: 1, competencyLevelName: "Test-1" }, + { + competencyLevelNumber: 1, + competencyLevelName: "Basic Proficiency", + }, ], - description: "Testing description", + description: + "Ability to formulate and execute long-term plans aligned with the organization's goals.", }; const response = await pactum .spec() - .post("/admin-competency") + .post("/admin-competency/sync-competency-data") .withBody(createAdminCompetencyDto) .expectStatus(201); - const createdAdminCompetency = JSON.parse(response.body); + const createdAdminCompetency = JSON.parse(JSON.stringify(response.body)); + expect(createdAdminCompetency).not.toBeNull(); expect(createdAdminCompetency.name).toEqual( createAdminCompetencyDto.name @@ -83,9 +88,11 @@ describe("AdminCompetencyController e2e", () => { const response = await pactum .spec() .get("/admin-competency") - .expectStatus(200); - const adminCompetencies = JSON.parse(response.body); - expect(Array.isArray(adminCompetencies)).toBeTruthy(); + .expectStatus(200) + + const adminCompetencies = JSON.parse(JSON.stringify(response.body)); + expect(adminCompetencies.message).toEqual('Successfully fetched all admin-competency'); + expect(adminCompetencies.data.length).toBeGreaterThanOrEqual(0); }); }); @@ -96,8 +103,8 @@ describe("AdminCompetencyController e2e", () => { description: "Updated Test Description", }; const testData = { - id: 1, - competencyId: 55, + id: 5, + competencyId: 5, }; const response = await pactum .spec() @@ -109,12 +116,15 @@ describe("AdminCompetencyController e2e", () => { .withBody(updatedAdminCompetencyDto) .expectStatus(200); - const updatedAdminCompetency = JSON.parse(response.body); + const updatedAdminCompetency = JSON.parse(JSON.stringify(response.body)); expect(updatedAdminCompetency).not.toBeNull(); - expect(updatedAdminCompetency.name).toEqual( + expect(updatedAdminCompetency.message).toEqual( + `Successfully updated admin-competency with id #${testData.id} and competency id #${testData.competencyId}` + ); + expect(updatedAdminCompetency.data.name).toEqual( updatedAdminCompetencyDto.name ); - expect(updatedAdminCompetency.description).toEqual( + expect(updatedAdminCompetency.data.description).toEqual( updatedAdminCompetencyDto.description ); }); @@ -123,8 +133,8 @@ describe("AdminCompetencyController e2e", () => { describe("AdminCompetencyController remove()", () => { it("should delete an admin competency by id", async () => { const testData = { - id: 1, - competencyId: 55, + id: 8, + competencyId: 8, }; const response = await pactum .spec() @@ -134,15 +144,22 @@ describe("AdminCompetencyController e2e", () => { competencyId: testData.competencyId, }) .expectStatus(200); - const deletedAdminCompetency = JSON.parse(response.body); + const deletedAdminCompetency = JSON.parse(JSON.stringify(response.body)); + console.log("deletedAdminCompetency", deletedAdminCompetency); + expect(deletedAdminCompetency).not.toBeNull(); - expect(deletedAdminCompetency.id).toEqual(1); - expect(deletedAdminCompetency.competencyId).toEqual(55); - expect(deletedAdminCompetency.name).toBeDefined(); - expect(deletedAdminCompetency.competencyLevels).toBeDefined(); - expect(deletedAdminCompetency?.description).toBeDefined(); - expect(deletedAdminCompetency?.createdAt).toBeDefined(); - expect(deletedAdminCompetency?.updatedAt).toBeDefined(); + expect(deletedAdminCompetency.message).toEqual( + `Successfully deleted admin-competency with id #${testData.id} and competency id #${testData.competencyId}` + ); + expect(deletedAdminCompetency.data.id).toEqual(testData.id); + expect(deletedAdminCompetency.data.competencyId).toEqual( + testData.competencyId + ); + expect(deletedAdminCompetency.data.name).toBeDefined(); + expect(deletedAdminCompetency.data.competencyLevels).toBeDefined(); + expect(deletedAdminCompetency?.data.description).toBeDefined(); + expect(deletedAdminCompetency?.data.createdAt).toBeDefined(); + expect(deletedAdminCompetency?.data.updatedAt).toBeDefined(); }); }); }); diff --git a/src/admin-competency/admin-competency.controller.ts b/src/admin-competency/admin-competency.controller.ts index 5a8e346..861b458 100644 --- a/src/admin-competency/admin-competency.controller.ts +++ b/src/admin-competency/admin-competency.controller.ts @@ -12,7 +12,7 @@ import { Res, } from "@nestjs/common"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; -import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; +import { getPrismaErrorStatusAndMessage } from "../utils/utils"; import { AdminCompetencyService } from "./admin-competency.service"; import { AdminCompetencyArrayResponseDto, diff --git a/src/admin-competency/admin-competency.module.ts b/src/admin-competency/admin-competency.module.ts index 6df37a5..02b392f 100644 --- a/src/admin-competency/admin-competency.module.ts +++ b/src/admin-competency/admin-competency.module.ts @@ -1,12 +1,18 @@ import { Module } from "@nestjs/common"; -import { MockCompetencyModule } from "src/mockModules/mock-competency/mock-competency.module"; import { AdminCompetencyController } from "./admin-competency.controller"; import { AdminCompetencyService } from "./admin-competency.service"; +import { PrismaModule } from "../prisma/prisma.module"; +import { MockCompetencyService } from "../mockModules/mock-competency/mock-competency.service"; +import { MockCompetencyLevelService } from "../mockModules/mock-competency-level/mock-competency-level.service"; @Module({ - imports: [MockCompetencyModule], + imports: [PrismaModule], controllers: [AdminCompetencyController], - providers: [AdminCompetencyService], + providers: [ + AdminCompetencyService, + MockCompetencyService, + MockCompetencyLevelService, + ], exports: [AdminCompetencyService], }) export class AdminCompetencyModule {} diff --git a/src/admin-competency/admin-competency.service.ts b/src/admin-competency/admin-competency.service.ts index e5cd7db..560d6f5 100644 --- a/src/admin-competency/admin-competency.service.ts +++ b/src/admin-competency/admin-competency.service.ts @@ -5,7 +5,7 @@ import { forwardRef, } from "@nestjs/common"; import _ from "lodash"; -import { PrismaService } from "src/prisma/prisma.service"; +import { PrismaService } from "../prisma/prisma.service"; import { MockCompetencyService } from "../mockModules/mock-competency/mock-competency.service"; import { UpdateAdminCompetencyDto } from "./dto"; diff --git a/src/admin-department/admin-department.controller.spec.ts b/src/admin-department/admin-department.controller.spec.ts index 24b70ae..aee2182 100644 --- a/src/admin-department/admin-department.controller.spec.ts +++ b/src/admin-department/admin-department.controller.spec.ts @@ -4,7 +4,6 @@ import { Test, TestingModule } from "@nestjs/testing"; import { ConfigService } from "@nestjs/config"; import { INestApplication, ValidationPipe } from "@nestjs/common"; import { AdminDepartmentModule } from "./admin-department.module"; -import { ResponseAdminDepartmentDto } from "./dto/response-admin-department.dto"; describe("AdminDepartment e2e", () => { let app: INestApplication; @@ -36,26 +35,27 @@ describe("AdminDepartment e2e", () => { app.close(); }); + const testData = { + departmentId : 1 + } + describe("AdminDepartmentController createOrUpdateAdminDepartment()", () => { it("should return created or updated admin department", async function () { - const testData = { - departmentId: 1, - }; const response = await pactum .spec() .post(`/admin-department/${testData.departmentId}`) .withPathParams({ departmentId: testData.departmentId }) - .expectStatus(201); - const createdAdminDepartment = JSON.parse(response); - expect(createdAdminDepartment).toMatchObject(ResponseAdminDepartmentDto); + .expectBodyContains(testData.departmentId); }); }); describe("AdminDepartmentController getAllAdminDepartment()", () => { it("should return all admin departments", async function () { - const response = await pactum.spec().get("/admin-departments"); - const allAdminDepartments = JSON.parse(response); - expect(allAdminDepartments[0]).toMatchObject(ResponseAdminDepartmentDto); + const response = await pactum + .spec() + .get("/admin-department/") + .expectStatus(200) + .expectBodyContains(testData.departmentId); }); }); }); diff --git a/src/mockModules/mock-competency-level/mock-competency-level.module.ts b/src/mockModules/mock-competency-level/mock-competency-level.module.ts index 61fe12b..79f585f 100644 --- a/src/mockModules/mock-competency-level/mock-competency-level.module.ts +++ b/src/mockModules/mock-competency-level/mock-competency-level.module.ts @@ -1,9 +1,11 @@ import { Module } from '@nestjs/common'; import { MockCompetencyLevelController } from './mock-competency-level.controller'; import { MockCompetencyLevelService } from './mock-competency-level.service'; +import { PrismaService } from "../../prisma/prisma.service"; @Module({ + imports: [PrismaService], controllers: [MockCompetencyLevelController], - providers: [MockCompetencyLevelService] + providers: [MockCompetencyLevelService], }) export class MockCompetencyLevelModule {} diff --git a/src/mockModules/mock-competency-level/mock-competency-level.service.ts b/src/mockModules/mock-competency-level/mock-competency-level.service.ts index cc9f2dd..0318705 100644 --- a/src/mockModules/mock-competency-level/mock-competency-level.service.ts +++ b/src/mockModules/mock-competency-level/mock-competency-level.service.ts @@ -1,5 +1,5 @@ import { Injectable, NotFoundException } from "@nestjs/common"; -import { PrismaService } from "src/prisma/prisma.service"; +import { PrismaService } from "../../prisma/prisma.service"; import { CreateCompetencyLevelDto, UpdateCompetencyLevelDto } from "./dto"; @Injectable() diff --git a/src/mockModules/mock-competency/mock-competency.module.ts b/src/mockModules/mock-competency/mock-competency.module.ts index 6d315e2..a44842e 100644 --- a/src/mockModules/mock-competency/mock-competency.module.ts +++ b/src/mockModules/mock-competency/mock-competency.module.ts @@ -2,8 +2,10 @@ import { Module } from "@nestjs/common"; import { MockCompetencyLevelService } from "../mock-competency-level/mock-competency-level.service"; import { MockCompetencyController } from "./mock-competency.controller"; import { MockCompetencyService } from "./mock-competency.service"; +import { PrismaService } from "../../prisma/prisma.service"; @Module({ + imports: [PrismaService], controllers: [MockCompetencyController], providers: [MockCompetencyService, MockCompetencyLevelService], exports: [MockCompetencyService], diff --git a/src/mockModules/mock-competency/mock-competency.service.ts b/src/mockModules/mock-competency/mock-competency.service.ts index 35b3709..bf2d7f9 100644 --- a/src/mockModules/mock-competency/mock-competency.service.ts +++ b/src/mockModules/mock-competency/mock-competency.service.ts @@ -1,5 +1,5 @@ import { Injectable, NotFoundException } from "@nestjs/common"; -import { PrismaService } from "src/prisma/prisma.service"; +import { PrismaService } from "../../prisma/prisma.service"; import { CreateCompetencyLevelDto } from "../mock-competency-level/dto"; import { MockCompetencyLevelService } from "../mock-competency-level/mock-competency-level.service"; import { CreateCompetencyDto, UpdateCompetencyDto } from "./dto"; diff --git a/src/mockModules/mock-designation/mock-designation.service.ts b/src/mockModules/mock-designation/mock-designation.service.ts index 01c2022..5e10fc2 100644 --- a/src/mockModules/mock-designation/mock-designation.service.ts +++ b/src/mockModules/mock-designation/mock-designation.service.ts @@ -1,5 +1,5 @@ import { Injectable } from "@nestjs/common"; -import { PrismaService } from "src/prisma/prisma.service"; +import { PrismaService } from "../../prisma/prisma.service"; import { CreateDesignationDto, UpdateDesignationDto } from "./dto"; @Injectable() diff --git a/src/mockModules/mock-role/mock-role.service.ts b/src/mockModules/mock-role/mock-role.service.ts index 68341cd..be8f237 100644 --- a/src/mockModules/mock-role/mock-role.service.ts +++ b/src/mockModules/mock-role/mock-role.service.ts @@ -3,7 +3,7 @@ import { Injectable, NotFoundException, } from "@nestjs/common"; -import { PrismaService } from "src/prisma/prisma.service"; +import { PrismaService } from "../../prisma/prisma.service"; import { CreateMockRoleDto, UpdateMockRoleDto } from "./dto"; import { MockCompetencyService } from "../mock-competency/mock-competency.service"; import { CreateCompetencyDto } from "../mock-competency/dto"; diff --git a/src/question-bank/question-bank.controller.spec.ts b/src/question-bank/question-bank.controller.spec.ts new file mode 100644 index 0000000..69b4385 --- /dev/null +++ b/src/question-bank/question-bank.controller.spec.ts @@ -0,0 +1,152 @@ +import * as pactum from "pactum"; +import { Test, TestingModule } from "@nestjs/testing"; +import { INestApplication, ValidationPipe } from "@nestjs/common"; +import { PrismaService } from "../prisma/prisma.service"; +import { ConfigService } from "@nestjs/config"; +import { QuestionBankModule } from "./question-bank.module"; +import { CreateQuestionBankDto } from "./dto/create-question-bank.dto"; +import { UpdateQuestionBankDto } from "./dto/update-question-bank.dto"; + +describe("QuestionBankController", () => { + let app: INestApplication; + let prisma: PrismaService; + let module: TestingModule; + beforeAll(async () => { + module = await Test.createTestingModule({ + imports: [QuestionBankModule], + }).compile(); + + app = module.createNestApplication(); + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + }) + ); + + const configService = app.get(ConfigService); + const apiPrefix = configService.get("API_PREFIX") || "api"; + const PORT = configService.get("APP_PORT") || 4010; + app.setGlobalPrefix(apiPrefix); + + await app.init(); + await app.listen(PORT); + + prisma = app.get(PrismaService); + pactum.request.setBaseUrl(`http://localhost:${PORT}/${apiPrefix}`); + }); + afterAll(async () => { + app.close(); + }); + + describe("QuestionBankController createQuestionByCompentencyLevel()", () => { + it("should return created question by compentency level", async () => { + const createQuestionDto: CreateQuestionBankDto = { + competencyId: 6, + question: "This is test question.", + competencyLevelNumber: 4, + }; + const response = await pactum + .spec() + .post("/question-bank") + .withBody(createQuestionDto) + .expectStatus(201); + const createdQuestion = JSON.parse(JSON.stringify(response.body)); + console.log(createdQuestion); + + expect(createdQuestion.message).toEqual("Question created sucessfully."); + expect(createdQuestion).not.toBeNull(); + expect(createdQuestion.data.competencyId).toEqual( + createQuestionDto.competencyId + ); + expect(createdQuestion.data.question).toEqual(createQuestionDto.question); + expect(createdQuestion.data.competencyLevelNumber).toEqual( + createQuestionDto.competencyLevelNumber + ); + }); + }); + + describe("QuestionBankController getAllQuestions()", () => { + it("should return all questions", async () => { + const response = await pactum + .spec() + .get("/question-bank") + .expectStatus(200); + + const questions = JSON.parse(JSON.stringify(response.body)); + expect(questions.message).toEqual("Successfully fetched all questions."); + expect(questions.data.length).toBeGreaterThanOrEqual(0); + }); + }); + + describe("QuestionBankController updateQuestionById()", () => { + const testData = { + id: 1, + }; + it("should update the question by id", async () => { + const updatedQuestionDto: UpdateQuestionBankDto = { + question: "Updated Test Question", + }; + const response = await pactum + .spec() + .patch(`/question-bank/update/${testData.id}`) + .withPathParams({ + id: testData.id, + }) + .withBody(updatedQuestionDto) + .expectStatus(200); + const updatedQuestion = JSON.parse(JSON.stringify(response.body)); + + expect(updatedQuestion.message).toEqual( + `Successfully updated question for id #${testData.id}` + ); + expect(updatedQuestion.data.question).toEqual( + updatedQuestionDto.question + ); + expect(updatedQuestion.data.competencyId).toBeDefined(); + expect(updatedQuestion.data.competencyLevelNumber).toBeDefined(); + }); + }); + + describe("QuestionBankController deleteQuestionById()", () => { + const testData = { + id: 1, + }; + it("should delete a question with given id", async () => { + const response = await pactum + .spec() + .delete(`/question-bank/delete/${testData.id}`) + .withPathParams({ + id: testData.id, + }) + .expectStatus(200); + const deletedQuestion = JSON.parse(JSON.stringify(response.body)); + expect(deletedQuestion.message).toEqual( + `Successfully deleted question for id #${testData.id}` + ); + expect(deletedQuestion.data.question).toBeDefined(); + expect(deletedQuestion.data.competencyId).toBeDefined(); + expect(deletedQuestion.data.competencyLevelNumber).toBeDefined(); + }); + }); + + describe("QuestionBankController getAllQuestionsForUser()", () => { + const testData = { + userId: "0f5d0b13-8d72-46c9-a7c4-c1f7e5aa1f17", + }; + it("should get all the mapped question for a user with given userId.", async () => { + const response = await pactum + .spec() + .get(`/question-bank/user/${testData.userId}`) + .withPathParams({ + userId: testData.userId, + }) + .expectStatus(200); + const questionsForUser = JSON.parse(JSON.stringify(response.body)); + console.log("questionsByUser", questionsForUser); + expect(questionsForUser.message).toEqual( + `Successfully get all the question for id #${testData.userId}` + ); + expect(questionsForUser.data.length).toBeGreaterThanOrEqual(1); + }); + }); +}); diff --git a/src/question-bank/question-bank.controller.ts b/src/question-bank/question-bank.controller.ts index 38d1097..45f5121 100644 --- a/src/question-bank/question-bank.controller.ts +++ b/src/question-bank/question-bank.controller.ts @@ -22,9 +22,9 @@ import { import { UpdateQuestionBankDto } from "./dto/update-question-bank.dto"; import { ApiBody, ApiConsumes, ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { ResponseQuestionBankDto } from "./dto/response-question-bank.dto"; -import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; +import { getPrismaErrorStatusAndMessage } from "../utils/utils"; import { FileInterceptor } from "@nestjs/platform-express"; -import { multerOptions } from "src/config/multer-options.config"; +import { multerOptions } from "../config/multer-options.config"; @Controller("question-bank") @ApiTags("question-bank") diff --git a/src/question-bank/question-bank.module.ts b/src/question-bank/question-bank.module.ts index e12479e..9b28ddb 100644 --- a/src/question-bank/question-bank.module.ts +++ b/src/question-bank/question-bank.module.ts @@ -1,14 +1,13 @@ import { Module } from '@nestjs/common'; import { QuestionBankService } from './question-bank.service'; import { QuestionBankController } from './question-bank.controller'; -import { PrismaModule } from 'src/prisma/prisma.module'; -import { PrismaService } from 'src/prisma/prisma.service'; -import { MockUserService } from 'src/mockModules/mock-user/mock-user.service'; -import { MockDesignationService } from 'src/mockModules/mock-designation/mock-designation.service'; -import { MockRoleService } from 'src/mockModules/mock-role/mock-role.service'; -import { MockCompetencyService } from 'src/mockModules/mock-competency/mock-competency.service'; -import { MockCompetencyLevelService } from 'src/mockModules/mock-competency-level/mock-competency-level.service'; -import { FileUploadService } from 'src/file-upload/file-upload.service'; +import { PrismaModule } from '../prisma/prisma.module'; +import { MockUserService } from '../mockModules/mock-user/mock-user.service'; +import { MockDesignationService } from '../mockModules/mock-designation/mock-designation.service'; +import { MockRoleService } from '../mockModules/mock-role/mock-role.service'; +import { MockCompetencyService } from '../mockModules/mock-competency/mock-competency.service'; +import { MockCompetencyLevelService } from '../mockModules/mock-competency-level/mock-competency-level.service'; +import { FileUploadService } from '../file-upload/file-upload.service'; @Module({ // Import the PrismaModule to make Prisma service available within this module @@ -22,7 +21,6 @@ import { FileUploadService } from 'src/file-upload/file-upload.service'; QuestionBankService, MockCompetencyService, MockCompetencyLevelService, - PrismaService, MockUserService, MockDesignationService, MockRoleService, diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index 2c56bab..a858951 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -5,12 +5,12 @@ import { QuestionBankFilterDto, } from "./dto/create-question-bank.dto"; import { UpdateQuestionBankDto } from "./dto/update-question-bank.dto"; -import { PrismaService } from "src/prisma/prisma.service"; -import { MockCompetencyService } from "src/mockModules/mock-competency/mock-competency.service"; -import { MockUserService } from "src/mockModules/mock-user/mock-user.service"; +import { PrismaService } from "../prisma/prisma.service"; +import { MockCompetencyService } from "../mockModules/mock-competency/mock-competency.service"; +import { MockUserService } from "../mockModules/mock-user/mock-user.service"; import { MockRoleService } from "./../mockModules/mock-role/mock-role.service"; -import { MockDesignationService } from "src/mockModules/mock-designation/mock-designation.service"; -import { FileUploadService } from "src/file-upload/file-upload.service"; +import { MockDesignationService } from "../mockModules/mock-designation/mock-designation.service"; +import { FileUploadService } from "../file-upload/file-upload.service"; @Injectable() export class QuestionBankService { @@ -85,7 +85,7 @@ export class QuestionBankService { ); } // Otherwise update it's details - return this.prisma.questionBank.updateMany({ + return this.prisma.questionBank.update({ where: { id: questionId, }, From add2af4c6463043f5a9061883c41359d11c5fc22 Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Mon, 6 Nov 2023 14:02:26 +0530 Subject: [PATCH 100/175] removed unwanted test api --- .../admin-competency.controller.spec.ts | 45 ------------------- 1 file changed, 45 deletions(-) diff --git a/src/admin-competency/admin-competency.controller.spec.ts b/src/admin-competency/admin-competency.controller.spec.ts index ba2252e..186a113 100644 --- a/src/admin-competency/admin-competency.controller.spec.ts +++ b/src/admin-competency/admin-competency.controller.spec.ts @@ -38,51 +38,6 @@ describe("AdminCompetencyController e2e", () => { app.close(); }); - describe("AdminCompetencyController create()", () => { - it("should return created admin competency on success", async function () { - const createAdminCompetencyDto: CreateAdminCompetencyDto = { - competencyId: 10, - name: "Test create admin competency", - competencyLevels: [ - { - competencyLevelNumber: 1, - competencyLevelName: "Basic Proficiency", - }, - ], - description: - "Ability to formulate and execute long-term plans aligned with the organization's goals.", - }; - - const response = await pactum - .spec() - .post("/admin-competency/sync-competency-data") - .withBody(createAdminCompetencyDto) - .expectStatus(201); - - const createdAdminCompetency = JSON.parse(JSON.stringify(response.body)); - - expect(createdAdminCompetency).not.toBeNull(); - expect(createdAdminCompetency.name).toEqual( - createAdminCompetencyDto.name - ); - expect(createdAdminCompetency.description).toEqual( - createAdminCompetencyDto.description - ); - expect( - createdAdminCompetency.competencyLevels[0].competencyLevelName - ).toEqual( - createAdminCompetencyDto.competencyLevels[0].competencyLevelName - ); - expect( - createdAdminCompetency.competencyLevels[0].competencyLevelNumber - ).toEqual( - createAdminCompetencyDto.competencyLevels[0].competencyLevelNumber - ); - expect(createdAdminCompetency.createdAt).toBeDefined(); - expect(createdAdminCompetency.updatedAt).toBeDefined(); - }); - }); - describe("AdminCompetencyController, findAll()", () => { it("should get all admin competency", async () => { const response = await pactum From 5c5b165a0894bbae94f627bfaa6f27567a3f1aa4 Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Mon, 6 Nov 2023 18:15:48 +0530 Subject: [PATCH 101/175] Response tracker & survey score APIs modified --- .../response-tracker.controller.ts | 66 ++++--- .../response-tracker.service.ts | 134 ++++++++++++-- src/survey-score/survey-score.service.ts | 165 ++++++++++++------ 3 files changed, 273 insertions(+), 92 deletions(-) diff --git a/src/response-tracker/response-tracker.controller.ts b/src/response-tracker/response-tracker.controller.ts index 5b3b734..a1fe0ba 100644 --- a/src/response-tracker/response-tracker.controller.ts +++ b/src/response-tracker/response-tracker.controller.ts @@ -1,26 +1,26 @@ import { - Controller, - Get, - Post, Body, - Patch, - Param, + Controller, Delete, - Res, + Get, HttpStatus, Logger, + Param, ParseIntPipe, ParseUUIDPipe, + Patch, + Post, + Res, } from "@nestjs/common"; -import { ResponseTrackerService } from "./response-tracker.service"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; import { CreateResponseTrackerDto, ResponseTrackerDtoMultipleResponse, ResponseTrackerDtoResponse, UpdateResponseTrackerDto, } from "./dto"; -import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; +import { ResponseTrackerService } from "./response-tracker.service"; @Controller("response-tracker") @ApiTags("response-tracker") @@ -164,32 +164,37 @@ export class ResponseTrackerController { } } - @Get("assessor/:assessorId") - @ApiOperation({ summary: "get response tracker by assessorId" }) + @Get("assessor/:assessorId/:surveyFormId") + @ApiOperation({ + summary: "get response tracker by assessorId & surveyFormId", + }) @ApiResponse({ status: HttpStatus.OK, type: ResponseTrackerDtoMultipleResponse, }) async findByAssessorId( @Res() res, - @Param("assessorId", ParseUUIDPipe) assessorId: string + @Param("assessorId", ParseUUIDPipe) assessorId: string, + @Param("surveyFormId", ParseIntPipe) surveyFormId: number ): Promise { try { this.logger.log( - `Initiated fetching response tracker with assessorId#${assessorId}` + `Initiated fetching response tracker with assessorId#${assessorId} and surveyFormId #${surveyFormId}` ); - const response = await this.responseTrackerService.findByAssessorId( - assessorId - ); + const response = + await this.responseTrackerService.findByAssessorIdAndSurveyFormId( + assessorId, + surveyFormId + ); return res.status(HttpStatus.OK).json({ data: response, - message: `Successfully fetched response tracker for assessorId #${assessorId}`, + message: `Successfully fetched response tracker for assessorId #${assessorId} and surveyFormId #${surveyFormId}`, }); } catch (error) { this.logger.error( - `Failed to fetch response tracker with assessorId #${assessorId}`, + `Failed to fetch response tracker with assessorId #${assessorId} and surveyFormId #${surveyFormId}`, error ); @@ -200,37 +205,42 @@ export class ResponseTrackerController { statusCode, message: errorMessage || - `Failed to fetch response tracker for assessorId #${assessorId}`, + `Failed to fetch response tracker for assessorId #${assessorId} and surveyFormId #${surveyFormId}`, }); } } - @Get("assessee/:assesseeId") - @ApiOperation({ summary: "get response tracker by assesseeId" }) + @Get("assessee/:assesseeId/:surveyFormId") + @ApiOperation({ + summary: "get response tracker by assesseeId & surveyFormId", + }) @ApiResponse({ status: HttpStatus.OK, type: ResponseTrackerDtoMultipleResponse, }) async findByAssesseeId( @Res() res, - @Param("assesseeId", ParseUUIDPipe) assesseeId: string + @Param("assesseeId", ParseUUIDPipe) assesseeId: string, + @Param("surveyFormId", ParseIntPipe) surveyFormId: number ): Promise { try { this.logger.log( - `Initiated fetching response tracker with assesseeId#${assesseeId}` + `Initiated fetching response tracker with assesseeId#${assesseeId} and surveyFormId #${surveyFormId}` ); - const response = await this.responseTrackerService.findByAssesseeId( - assesseeId - ); + const response = + await this.responseTrackerService.findByAssesseeIdAndSurveyFormId( + assesseeId, + surveyFormId + ); return res.status(HttpStatus.OK).json({ data: response, - message: `Successfully fetched response tracker for assesseeId #${assesseeId}`, + message: `Successfully fetched response tracker for assesseeId #${assesseeId} and surveyFormId ${surveyFormId}`, }); } catch (error) { this.logger.error( - `Failed to fetch response tracker with assesseeId #${assesseeId}`, + `Failed to fetch response tracker with assesseeId #${assesseeId} and surveyFormId #${surveyFormId}`, error ); @@ -241,7 +251,7 @@ export class ResponseTrackerController { statusCode, message: errorMessage || - `Failed to fetch response tracker for assesseeId #${assesseeId}`, + `Failed to fetch response tracker for assesseeId #${assesseeId} and surveyFormId #${surveyFormId}`, }); } } diff --git a/src/response-tracker/response-tracker.service.ts b/src/response-tracker/response-tracker.service.ts index 063a936..6f00a33 100644 --- a/src/response-tracker/response-tracker.service.ts +++ b/src/response-tracker/response-tracker.service.ts @@ -1,9 +1,17 @@ -import { Injectable, NotFoundException } from "@nestjs/common"; +import { + Injectable, + NotAcceptableException, + NotFoundException, +} from "@nestjs/common"; +import { ResponseTrackerStatusEnum } from "@prisma/client"; +import _ from "lodash"; import { PrismaService } from "src/prisma/prisma.service"; -import { CreateResponseTrackerDto } from "./dto/create-response-tracker.dto"; +import { + CreateResponseTrackerDto, + responseObject, +} from "./dto/create-response-tracker.dto"; import { UpdateResponseTrackerDto } from "./dto/update-response-tracker.dto"; import { IResponseTracker } from "./interfaces/response-tracker.interface"; -import { ResponseTrackerStatusEnum } from "@prisma/client"; @Injectable() export class ResponseTrackerService { @@ -46,26 +54,66 @@ export class ResponseTrackerService { return response; } - public async findByAssessorId(assessorId: string) { + public async findByAssessorIdAndSurveyFormId( + assessorId: string, + surveyFormId: number + ) { const response = await this.prisma.responseTracker.findMany({ - where: { assessorId }, + where: { assessorId, surveyFormId }, + include: { + Assessee: { + select: { + designation: true, + userName: true, + }, + }, + surveyForm: { + select: { + surveyCycleParameter: { + select: { + endTime: true, + }, + }, + }, + }, + }, }); if (!response || response.length === 0) throw new NotFoundException( - `Response tracker with assessorId #${assessorId} not found` + `Response tracker with assessorId #${assessorId} and surveyFormId #${surveyFormId} not found` ); return response; } - public async findByAssesseeId(assesseeId: string) { + public async findByAssesseeIdAndSurveyFormId( + assesseeId: string, + surveyFormId: number + ) { const response = await this.prisma.responseTracker.findMany({ - where: { assesseeId }, + where: { assesseeId, surveyFormId }, + include: { + Assessor: { + select: { + designation: true, + userName: true, // add profile picture when added + }, + }, + surveyForm: { + select: { + surveyCycleParameter: { + select: { + endTime: true, + }, + }, + }, + }, + }, }); if (!response || response.length === 0) throw new NotFoundException( - `Response tracker with assesseeId #${assesseeId} not found` + `Response tracker with assesseeId #${assesseeId} and surveyFormId #${surveyFormId} not found` ); return response; } @@ -111,6 +159,16 @@ export class ResponseTrackerService { id: number, updateResponseTrackerDto: UpdateResponseTrackerDto ) { + const isQuestionsValid = await this.validateQuestions( + updateResponseTrackerDto.responseJson, + updateResponseTrackerDto.surveyFormId + ); + + if (!isQuestionsValid) { + throw new NotAcceptableException( + `Question not matching with the questions in the survey form with id #${updateResponseTrackerDto.surveyFormId}` + ); + } const responseJson = JSON.stringify(updateResponseTrackerDto.responseJson); const payload = { @@ -168,15 +226,29 @@ export class ResponseTrackerService { public async updateBySurveyFormId( updateResponseTrackerDto: UpdateResponseTrackerDto ) { - const { surveyFormId, assesseeId, assessorId } = updateResponseTrackerDto; - const responseJson = JSON.stringify(updateResponseTrackerDto.responseJson); + const { surveyFormId, assesseeId, assessorId, responseJson } = + updateResponseTrackerDto; + const isQuestionsValid = await this.validateQuestions( + responseJson, + surveyFormId + ); + + if (!isQuestionsValid) { + throw new NotAcceptableException( + `Question not matching with the questions in the survey form with id #${surveyFormId}` + ); + } + + const responseJsonData = JSON.stringify(responseJson); const payload = { - responseJson: JSON.parse(responseJson || "[]"), + responseJson: JSON.parse(responseJsonData || "[]"), status: ResponseTrackerStatusEnum.COMPLETED, }; - if (!responseJson) delete payload.responseJson; + if (!responseJsonData) delete payload.responseJson; + + const today = new Date().toISOString(); return await this.prisma.responseTracker.update({ where: { @@ -185,8 +257,44 @@ export class ResponseTrackerService { assesseeId, assessorId, }, + surveyForm: { + surveyCycleParameter: { + endTime: { + gte: today, + }, + }, + }, }, data: payload, }); } + + public async validateQuestions( + questionData: responseObject[], + surveyFormId: number + ): Promise { + const surveyForm = await this.prisma.surveyForm.findUniqueOrThrow({ + where: { + id: surveyFormId, + }, + }); + + const surveyFormQuestions = JSON.parse( + JSON.stringify(_.get(surveyForm, "questionsJson", [])) + ); + let surveyFormQuestionIds = _.compact( + _.map(surveyFormQuestions, (question) => + _.get(question, "questionId", null) + ) + ); + + surveyFormQuestionIds = _.sortBy(surveyFormQuestionIds); + + let questionIds = _.compact( + _.map(questionData, (question) => _.get(question, "questionId", null)) + ); + + questionIds = _.sortBy(questionIds); + return _.isEqual(questionIds, surveyFormQuestionIds); + } } diff --git a/src/survey-score/survey-score.service.ts b/src/survey-score/survey-score.service.ts index 301fffd..31600a2 100644 --- a/src/survey-score/survey-score.service.ts +++ b/src/survey-score/survey-score.service.ts @@ -4,13 +4,13 @@ import { NotFoundException, } from "@nestjs/common"; import { ResponseTrackerStatusEnum, SurveyStatusEnum } from "@prisma/client"; +import _ from "lodash"; import { PrismaService } from "src/prisma/prisma.service"; import { QuestionBankService } from "src/question-bank/question-bank.service"; import { responseObject } from "src/response-tracker/dto"; import { AnswerEnum } from "src/response-tracker/enums/response-tracker.enums"; import { ResponseTrackerService } from "src/response-tracker/response-tracker.service"; import { SurveyFormService } from "src/survey-form/survey-form.service"; -import { UserMetadataService } from "src/user-metadata/user-metadata.service"; import { CreateSurveyScoreDto } from "./dto/create-survey-score.dto"; import { UpdateSurveyScoreDto } from "./dto/update-survey-score.dto"; import { @@ -89,11 +89,34 @@ export class SurveyScoreService { ResponseTrackerStatusEnum.COMPLETED ); - const surveyResponses = completedSurveyResponses - .map((response) => response.responseJson) - .flat(); + const surveyResponses = _.compact( + _.flatten( + _.map(completedSurveyResponses, (response) => { + const result = _.get(response, "responseJson", []); + return !_.isEmpty(result) ? result : null; + }) + ) + ); + + if (_.isEmpty(surveyResponses)) { + const surveyQuestionIds = _.map( + JSON.parse(JSON.stringify(surveyForm.questionsJson)), + (question) => _.get(question, "questionId") + ); + + const emptyResponseScorePayload = await this.fetchEmptyResponseScores( + surveyQuestionIds, + surveyFormId + ); + + await this.prisma.surveyScore.createMany({ + data: emptyResponseScorePayload, + }); - const answerScore = this.calculateAnswerScore(surveyResponses); + return `Successfully calculated score for survey form id #${surveyFormId}`; + } + + const answerScore = this.calculateAnswerScore(_.compact(surveyResponses)); const answerScoreAndCompetencyGroupData: { [key: string]: IGroupScoreData; @@ -115,10 +138,12 @@ export class SurveyScoreService { const overallScore = this.calculateOverAllScoreFromFinalGropedData(finalGroupedData); - await this.surveyFormService.updateSurveyFormScore( - surveyFormId, - overallScore - ); + if (overallScore) { + await this.surveyFormService.updateSurveyFormScore( + surveyFormId, + overallScore + ); + } const scorePayload = this.formatScorePayloadFromFinalGroupDataAndFormId( surveyFormId, @@ -135,36 +160,61 @@ export class SurveyScoreService { } } + public async fetchEmptyResponseScores( + questionIds: number[], + surveyFormId: number + ) { + return Promise.all( + questionIds.map(async (surveyQuestionId) => { + const question = await this.questionBankService.getQuestionById( + surveyQuestionId + ); + const { competencyId, competencyLevelNumber } = question; + return { + surveyFormId, + competencyId, + competencyLevelNumber, + score: -1, + }; + }) + ); + } + public calculateAnswerScore(surveyResponses: responseObject[]): { [key: string]: IAnswerScore; } { - return surveyResponses.reduce((acc, item) => { - const { questionId, answer } = item; + if (_.isEmpty(surveyResponses)) { + return {}; // Return an empty object when surveyResponses is empty + } - if (!acc[questionId]) { - acc[questionId] = { score: 0, totalQuestions: 0 }; - } + const groupedResponses = _.groupBy(surveyResponses, "questionId"); - if (answer === AnswerEnum.YES) { - acc[questionId].score += 1; - acc[questionId].totalQuestions += 1; - } else if (answer === AnswerEnum.NO) { - acc[questionId].score += 0; - acc[questionId].totalQuestions += 1; - } + const answerScores = _.mapValues(groupedResponses, (responses) => { + const totalQuestions = _.sumBy(responses, (response) => + response.answer === AnswerEnum.DO_NOT_KNOW ? 0 : 1 + ); - return acc; - }, {}); + const score = _.sumBy(responses, (response) => + response.answer === AnswerEnum.YES ? 1 : 0 + ); + + return { score, totalQuestions }; + }); + + return answerScores; } public async getAnswerScoreAndCompetencyGroupData(answerScore: { [key: string]: IAnswerScore; }): Promise<{ [key: string]: IGroupScoreData }> { - const groupedData: { [key: string]: IGroupScoreData } = {}; + if (_.isEmpty(answerScore)) { + return {}; // Return an empty object when answerScore is empty + } + const questionIds = Object.keys(answerScore); - // Create an array of promises for each iteration - const promises = questionIds.map(async (questionId) => { + // Use _.map to create an array of promises for each iteration + const promises = _.map(questionIds, async (questionId) => { const question = await this.questionBankService.getQuestionById( +questionId ); @@ -172,35 +222,41 @@ export class SurveyScoreService { const scoreData = answerScore[questionId]; - groupedData[questionId] = { - score: scoreData.score, - totalQuestions: scoreData.totalQuestions, - competencyId, - competencyLevelNumber, - scorePercentage: 0, - }; + return [ + questionId, + { + score: scoreData.score, + totalQuestions: scoreData.totalQuestions, + competencyId, + competencyLevelNumber, + scorePercentage: 0, + }, + ]; }); - // Wait for all promises to resolve - await Promise.all(promises); + // Wait for all promises to resolve using Promise.all + const results = await Promise.all(promises); - return groupedData; + // Use _.fromPairs to transform the results array into an object + return _.fromPairs(results); } public groupScoreDataByCompetency(groupedData: { [key: string]: IGroupScoreData; }): { [key: string]: IGroupScoreData } { - return Object.values(groupedData).reduce((acc, item) => { + if (_.isEmpty(groupedData)) { + return {}; // Return an empty object when groupedData is empty + } + + return _.values(groupedData).reduce((acc, item) => { const key = `${item.competencyId}-${item.competencyLevelNumber}`; - if (!acc[key]) { - acc[key] = { - score: 0, - totalQuestions: 0, - competencyId: item.competencyId, - competencyLevelNumber: item.competencyLevelNumber, - }; - } + acc[key] = acc[key] || { + score: 0, + totalQuestions: 0, + competencyId: item.competencyId, + competencyLevelNumber: item.competencyLevelNumber, + }; acc[key].score += item.score; acc[key].totalQuestions += item.totalQuestions; @@ -211,8 +267,13 @@ export class SurveyScoreService { public calculateOverAllScoreFromFinalGropedData( finalGroupedData: IGroupScoreData[] - ): number { - const overallScore = finalGroupedData.reduce( + ): number | null { + if (_.isEmpty(finalGroupedData)) { + return null; // Return null default value for an empty payload + } + + const overallScore = _.reduce( + finalGroupedData, (acc, group) => { acc.score += group.score; acc.totalQuestions += group.totalQuestions; @@ -230,7 +291,11 @@ export class SurveyScoreService { surveyFormId: number, finalGroupedData: IGroupScoreData[] ) { - return finalGroupedData.map((data: IGroupScoreData) => { + if (_.isEmpty(finalGroupedData)) { + return []; // Return an empty array or any other appropriate default value for an empty payload + } + + return _.map(finalGroupedData, (data) => { const { competencyId, competencyLevelNumber, scorePercentage } = data; return { surveyFormId, @@ -250,12 +315,10 @@ export class SurveyScoreService { `Survey scores not found for user id #${userId}` ); - const updatedSurveyScore = surveyScoreResponse.map((data) => { + return _.map(surveyScoreResponse, (data) => { const { id, ...rest } = data; return { surveyFormId: id, ...rest }; }); - - return updatedSurveyScore; } public async getLatestSurveyScoreByUserId(userId: string) { From dfd4da66e8d602914a7b508e4c8559e7d757fcb3 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Tue, 7 Nov 2023 11:13:59 +0530 Subject: [PATCH 102/175] Resolving Import errors. --- .../mock-competency-level/mock-competency-level.module.ts | 4 ++-- src/mockModules/mock-competency/mock-competency.module.ts | 4 ++-- src/question-bank/question-bank.service.ts | 6 ------ 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/mockModules/mock-competency-level/mock-competency-level.module.ts b/src/mockModules/mock-competency-level/mock-competency-level.module.ts index 79f585f..1a1d266 100644 --- a/src/mockModules/mock-competency-level/mock-competency-level.module.ts +++ b/src/mockModules/mock-competency-level/mock-competency-level.module.ts @@ -1,10 +1,10 @@ import { Module } from '@nestjs/common'; import { MockCompetencyLevelController } from './mock-competency-level.controller'; import { MockCompetencyLevelService } from './mock-competency-level.service'; -import { PrismaService } from "../../prisma/prisma.service"; +import { PrismaModule } from '../../prisma/prisma.module'; @Module({ - imports: [PrismaService], + imports: [PrismaModule], controllers: [MockCompetencyLevelController], providers: [MockCompetencyLevelService], }) diff --git a/src/mockModules/mock-competency/mock-competency.module.ts b/src/mockModules/mock-competency/mock-competency.module.ts index a44842e..8904c06 100644 --- a/src/mockModules/mock-competency/mock-competency.module.ts +++ b/src/mockModules/mock-competency/mock-competency.module.ts @@ -2,10 +2,10 @@ import { Module } from "@nestjs/common"; import { MockCompetencyLevelService } from "../mock-competency-level/mock-competency-level.service"; import { MockCompetencyController } from "./mock-competency.controller"; import { MockCompetencyService } from "./mock-competency.service"; -import { PrismaService } from "../../prisma/prisma.service"; +import { PrismaModule } from "../../prisma/prisma.module"; @Module({ - imports: [PrismaService], + imports: [PrismaModule], controllers: [MockCompetencyController], providers: [MockCompetencyService, MockCompetencyLevelService], exports: [MockCompetencyService], diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index dfb2389..ca2c724 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -1,11 +1,5 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import _ from "lodash"; -import { FileUploadService } from "src/file-upload/file-upload.service"; -import { MockCompetencyService } from "src/mockModules/mock-competency/mock-competency.service"; -import { MockDesignationService } from "src/mockModules/mock-designation/mock-designation.service"; -import { MockUserService } from "src/mockModules/mock-user/mock-user.service"; -import { PrismaService } from "src/prisma/prisma.service"; -import { MockRoleService } from "./../mockModules/mock-role/mock-role.service"; import { CreateFileUploadDto, CreateQuestionBankDto, From ba1816379ad0b871ecb7ebe3852b6b6f6e0a9a4e Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Tue, 7 Nov 2023 16:02:33 +0530 Subject: [PATCH 103/175] Added API to fetch Competency Names only. --- .../admin-competency.controller.ts | 28 +++++++++++++++++++ .../admin-competency.service.ts | 10 +++++++ 2 files changed, 38 insertions(+) diff --git a/src/admin-competency/admin-competency.controller.ts b/src/admin-competency/admin-competency.controller.ts index 78bddab..0422e67 100644 --- a/src/admin-competency/admin-competency.controller.ts +++ b/src/admin-competency/admin-competency.controller.ts @@ -84,6 +84,34 @@ export class AdminCompetencyController { } } + @Get("/names") + @ApiOperation({ summary: "get all admin-competency" }) + @ApiResponse({ status: HttpStatus.OK, type: AdminCompetencyArrayResponseDto }) + async findAllCompetencyNames(@Res() res): Promise { + try { + this.logger.log(`Initiated fetching all admin-competency`); + + const adminCompetency = await this.adminCompetencyService.findAllCompetencyNames(); + + this.logger.log(`Successfully fetched all admin-competency`); + + return res.status(HttpStatus.OK).send({ + message: `Successfully fetched all admin-competency`, + data: adminCompetency, + }); + } catch (error) { + this.logger.error("Failed to fetch all admin-competency", error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: errorMessage || "Failed to fetch all admin-competency", + }); + } + } + @Get(":competencyId") @ApiOperation({ summary: "get admin-competency by competencyId" }) @ApiResponse({ status: HttpStatus.OK, type: AdminCompetencyResponseDto }) diff --git a/src/admin-competency/admin-competency.service.ts b/src/admin-competency/admin-competency.service.ts index b8e26ae..482710c 100644 --- a/src/admin-competency/admin-competency.service.ts +++ b/src/admin-competency/admin-competency.service.ts @@ -72,6 +72,16 @@ export class AdminCompetencyService { return adminCompetency; } + public async findAllCompetencyNames() { + const adminCompetencies = await this.prisma.adminCompetency.findMany({ + select:{ + competencyId: true, + name: true, + } + }); + return adminCompetencies; + } + public async findOne(competencyId: number) { const adminCompetency = await this.prisma.adminCompetency.findUnique({ where: { From 874cba64876189248fb269cb76ca3b1d29af5533 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 8 Nov 2023 10:53:18 +0530 Subject: [PATCH 104/175] Added API to Create, Update and Delete Multiple Questions. --- .../dto/create-question-bank.dto.ts | 20 ++++++++ .../dto/update-question-bank.dto.ts | 13 +++++- src/question-bank/question-bank.controller.ts | 41 +++++++++++++++-- src/question-bank/question-bank.service.ts | 46 ++++++++++++++++++- 4 files changed, 115 insertions(+), 5 deletions(-) diff --git a/src/question-bank/dto/create-question-bank.dto.ts b/src/question-bank/dto/create-question-bank.dto.ts index 91a7c04..494ffd8 100644 --- a/src/question-bank/dto/create-question-bank.dto.ts +++ b/src/question-bank/dto/create-question-bank.dto.ts @@ -1,9 +1,12 @@ +import { ApiProperty } from "@nestjs/swagger"; import { + IsArray, IsInt, IsNotEmpty, IsOptional, IsString, } from "class-validator"; +import { UpdateQuestionBankDto2 } from "./update-question-bank.dto"; // CreateQuestionBankDto is used for creating a new question bank export class CreateQuestionBankDto { @@ -66,3 +69,20 @@ export class QuestionBankFilterDto { @IsString() orderBy?: string; } + +export class CreateUpdateDeleteQuesitonsDto { + @IsOptional() + @IsNotEmpty() + createQuestions?: CreateQuestionBankDto[]; + + @IsOptional() + @IsNotEmpty() + updateQuestions?: UpdateQuestionBankDto2[] + + @ApiProperty({isArray: true, example:[1, 2, 3]}) + @IsOptional() + @IsNotEmpty() + @IsArray() + @IsInt({ each: true }) + deleteQuestions?: number[]; +} \ No newline at end of file diff --git a/src/question-bank/dto/update-question-bank.dto.ts b/src/question-bank/dto/update-question-bank.dto.ts index a748473..0d76229 100644 --- a/src/question-bank/dto/update-question-bank.dto.ts +++ b/src/question-bank/dto/update-question-bank.dto.ts @@ -1,4 +1,4 @@ -import { IsNotEmpty, IsString } from "class-validator"; +import { IsInt, IsNotEmpty, IsOptional, IsString } from "class-validator"; export class UpdateQuestionBankDto { // Question for the question bank @@ -6,3 +6,14 @@ export class UpdateQuestionBankDto { @IsString() question: string; } + +export class UpdateQuestionBankDto2 { + @IsNotEmpty() + @IsInt() + questionId: number + + // Question for the question bank + @IsNotEmpty() + @IsString() + question: string; +} \ No newline at end of file diff --git a/src/question-bank/question-bank.controller.ts b/src/question-bank/question-bank.controller.ts index 38d1097..80c8697 100644 --- a/src/question-bank/question-bank.controller.ts +++ b/src/question-bank/question-bank.controller.ts @@ -17,10 +17,17 @@ import { import { QuestionBankService } from "./question-bank.service"; import { CreateQuestionBankDto, + CreateUpdateDeleteQuesitonsDto, QuestionBankFilterDto, } from "./dto/create-question-bank.dto"; import { UpdateQuestionBankDto } from "./dto/update-question-bank.dto"; -import { ApiBody, ApiConsumes, ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { + ApiBody, + ApiConsumes, + ApiOperation, + ApiResponse, + ApiTags, +} from "@nestjs/swagger"; import { ResponseQuestionBankDto } from "./dto/response-question-bank.dto"; import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; import { FileInterceptor } from "@nestjs/platform-express"; @@ -218,11 +225,10 @@ export class QuestionBankController { return res.status(statusCode).json({ statusCode, message: errorMessage || `Failed to upload question bank.`, - }) + }); } } - // Get all the mapped questions for the user @Get("user/:id") @ApiOperation({ @@ -261,4 +267,33 @@ export class QuestionBankController { }); } } + + //API to Create, Update and Delete Multiple Questions + @Post("/updateMultipleQuestions") + @ApiOperation({ summary: "Create, Update and Delete Multiple Questions" }) + @ApiResponse({ status: HttpStatus.OK }) + async createUpdateDeleteQuesitons( + @Res() res, + @Body() createUpdateDeleteQuesitonsDto: CreateUpdateDeleteQuesitonsDto + ) { + // Log the initiaton for csv file upload + this.logger.log(`Initiate updating the question bank.`); + try { + await this.questionBankService.createUpdateDeleteQuesitons(createUpdateDeleteQuesitonsDto); + this.logger.log(`Successfully updated the question bank.`); + return res.status(HttpStatus.CREATED).json({ + message: "Question bank updated sucessfully.", + }); + } catch (error) { + this.logger.error(`Failed to update question bank.`, error); + // get error message and status code + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + // Return an error response + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed to update question bank.`, + }); + } + } } diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index 71561cc..9d61757 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -9,9 +9,13 @@ import { MockRoleService } from "./../mockModules/mock-role/mock-role.service"; import { CreateFileUploadDto, CreateQuestionBankDto, + CreateUpdateDeleteQuesitonsDto, QuestionBankFilterDto, } from "./dto/create-question-bank.dto"; -import { UpdateQuestionBankDto } from "./dto/update-question-bank.dto"; +import { + UpdateQuestionBankDto, + UpdateQuestionBankDto2, +} from "./dto/update-question-bank.dto"; @Injectable() export class QuestionBankService { @@ -309,4 +313,44 @@ export class QuestionBankService { throw new NotFoundException(`question with id #${id} not found`); return question; } + + public async createUpdateDeleteQuesitons( + questionBank: CreateUpdateDeleteQuesitonsDto + ) { + const { createQuestions, updateQuestions, deleteQuestions } = questionBank; + await this.prisma.$transaction(async (prismaClient) => { + if (!_.isUndefined(deleteQuestions) && !_.isEmpty(deleteQuestions)) { + for(const deleteQuestionId of deleteQuestions){ + await prismaClient.questionBank.delete({ + where: { + id : deleteQuestionId + }, + }); + } + } + + if (!_.isUndefined(updateQuestions) && !_.isEmpty(updateQuestions)) { + for(const updateQuestion of updateQuestions){ + await prismaClient.questionBank.update({ + where: { + id: updateQuestion.questionId, + }, + data: { + question: updateQuestion.question, + }, + }); + } + } + + if (!_.isUndefined(createQuestions) && !_.isEmpty(createQuestions)) { + for(const createQuestion of createQuestions){ + await prismaClient.questionBank.create({ data: { + competencyId: createQuestion.competencyId, + competencyLevelNumber: createQuestion.competencyLevelNumber, + question: createQuestion.question + } }); + } + } + }); + } } From f5dd6e1231aebb36a7afc92fcf134c41458df628 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 8 Nov 2023 11:04:36 +0530 Subject: [PATCH 105/175] Added competency Id check when creating new Question. --- src/question-bank/question-bank.service.ts | 30 +++++++++++++++------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index 9d61757..bd19f74 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -320,17 +320,17 @@ export class QuestionBankService { const { createQuestions, updateQuestions, deleteQuestions } = questionBank; await this.prisma.$transaction(async (prismaClient) => { if (!_.isUndefined(deleteQuestions) && !_.isEmpty(deleteQuestions)) { - for(const deleteQuestionId of deleteQuestions){ + for (const deleteQuestionId of deleteQuestions) { await prismaClient.questionBank.delete({ where: { - id : deleteQuestionId + id: deleteQuestionId, }, }); } } if (!_.isUndefined(updateQuestions) && !_.isEmpty(updateQuestions)) { - for(const updateQuestion of updateQuestions){ + for (const updateQuestion of updateQuestions) { await prismaClient.questionBank.update({ where: { id: updateQuestion.questionId, @@ -343,12 +343,24 @@ export class QuestionBankService { } if (!_.isUndefined(createQuestions) && !_.isEmpty(createQuestions)) { - for(const createQuestion of createQuestions){ - await prismaClient.questionBank.create({ data: { - competencyId: createQuestion.competencyId, - competencyLevelNumber: createQuestion.competencyLevelNumber, - question: createQuestion.question - } }); + for (const createQuestion of createQuestions) { + const checkCompentencyId = await prismaClient.competency.findUnique({ + where: { + id: createQuestion.competencyId, + }, + }); + + if (!checkCompentencyId) { + throw new NotFoundException("Competency is not Found"); + } + + await prismaClient.questionBank.create({ + data: { + competencyId: createQuestion.competencyId, + competencyLevelNumber: createQuestion.competencyLevelNumber, + question: createQuestion.question, + }, + }); } } }); From 0adb5b11ae38129f417ce35f1cc3c783b7d80a56 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 8 Nov 2023 11:07:53 +0530 Subject: [PATCH 106/175] Added "ArrayNotEmpty" class validator to CreateUpdateDeleteQuesitonsDto. --- src/question-bank/dto/create-question-bank.dto.ts | 2 ++ src/question-bank/question-bank.service.ts | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/question-bank/dto/create-question-bank.dto.ts b/src/question-bank/dto/create-question-bank.dto.ts index 494ffd8..dbaf39b 100644 --- a/src/question-bank/dto/create-question-bank.dto.ts +++ b/src/question-bank/dto/create-question-bank.dto.ts @@ -1,5 +1,6 @@ import { ApiProperty } from "@nestjs/swagger"; import { + ArrayNotEmpty, IsArray, IsInt, IsNotEmpty, @@ -83,6 +84,7 @@ export class CreateUpdateDeleteQuesitonsDto { @IsOptional() @IsNotEmpty() @IsArray() + @ArrayNotEmpty() @IsInt({ each: true }) deleteQuestions?: number[]; } \ No newline at end of file diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index bd19f74..16249d2 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -13,8 +13,7 @@ import { QuestionBankFilterDto, } from "./dto/create-question-bank.dto"; import { - UpdateQuestionBankDto, - UpdateQuestionBankDto2, + UpdateQuestionBankDto } from "./dto/update-question-bank.dto"; @Injectable() From 0c7d4ce5c11ac2e22ef2be3aeb0717dca9e9d78a Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Wed, 8 Nov 2023 11:21:21 +0530 Subject: [PATCH 107/175] Testcases for survey --- .../admin-department.controller.spec.ts | 3 +- .../question-bank.controller.spec.ts | 3 - .../dto/create-survey-config.dto.ts | 3 + .../survey-config.controller.spec.ts | 95 +++++++++++++------ .../dto/create-survey-cycle-parameter.dto.ts | 3 + .../survey-cycle-parameter.controller.spec.ts | 67 +++++++------ .../survey-cycle-parameter.controller.ts | 2 +- .../survey-cycle-parameter.module.ts | 2 +- .../survey-cycle-parameter.service.ts | 2 +- .../survey-form.controller.spec.ts | 94 +++++++++--------- src/survey-form/survey-form.controller.ts | 7 +- src/survey-form/survey-form.service.ts | 3 + .../user-metadata.controller.spec.ts | 43 +++++---- 13 files changed, 193 insertions(+), 134 deletions(-) diff --git a/src/admin-department/admin-department.controller.spec.ts b/src/admin-department/admin-department.controller.spec.ts index aee2182..b164aee 100644 --- a/src/admin-department/admin-department.controller.spec.ts +++ b/src/admin-department/admin-department.controller.spec.ts @@ -36,7 +36,7 @@ describe("AdminDepartment e2e", () => { }); const testData = { - departmentId : 1 + departmentId : 5 } describe("AdminDepartmentController createOrUpdateAdminDepartment()", () => { @@ -45,6 +45,7 @@ describe("AdminDepartment e2e", () => { .spec() .post(`/admin-department/${testData.departmentId}`) .withPathParams({ departmentId: testData.departmentId }) + .expectStatus(201) .expectBodyContains(testData.departmentId); }); }); diff --git a/src/question-bank/question-bank.controller.spec.ts b/src/question-bank/question-bank.controller.spec.ts index 69b4385..25a881c 100644 --- a/src/question-bank/question-bank.controller.spec.ts +++ b/src/question-bank/question-bank.controller.spec.ts @@ -51,7 +51,6 @@ describe("QuestionBankController", () => { .withBody(createQuestionDto) .expectStatus(201); const createdQuestion = JSON.parse(JSON.stringify(response.body)); - console.log(createdQuestion); expect(createdQuestion.message).toEqual("Question created sucessfully."); expect(createdQuestion).not.toBeNull(); @@ -95,7 +94,6 @@ describe("QuestionBankController", () => { .withBody(updatedQuestionDto) .expectStatus(200); const updatedQuestion = JSON.parse(JSON.stringify(response.body)); - expect(updatedQuestion.message).toEqual( `Successfully updated question for id #${testData.id}` ); @@ -142,7 +140,6 @@ describe("QuestionBankController", () => { }) .expectStatus(200); const questionsForUser = JSON.parse(JSON.stringify(response.body)); - console.log("questionsByUser", questionsForUser); expect(questionsForUser.message).toEqual( `Successfully get all the question for id #${testData.userId}` ); diff --git a/src/survey-config/dto/create-survey-config.dto.ts b/src/survey-config/dto/create-survey-config.dto.ts index c05410b..e0b96b4 100644 --- a/src/survey-config/dto/create-survey-config.dto.ts +++ b/src/survey-config/dto/create-survey-config.dto.ts @@ -1,5 +1,6 @@ import { ApiProperty } from "@nestjs/swagger"; import { TimeUnitsEnum } from "@prisma/client"; +import { Type } from "class-transformer"; import { IsDate, IsEnum, IsInt, IsNotEmpty, IsOptional } from "class-validator"; export class CreateSurveyConfigDto { @@ -18,11 +19,13 @@ export class CreateSurveyConfigDto { // start time for the survey config @IsNotEmpty() @IsDate() + @Type(() => Date) startTime: Date; // end time for the survey config @IsNotEmpty() @IsDate() + @Type(() => Date) endTime: Date; } diff --git a/src/survey-config/survey-config.controller.spec.ts b/src/survey-config/survey-config.controller.spec.ts index 34b2d9a..65b0b9b 100644 --- a/src/survey-config/survey-config.controller.spec.ts +++ b/src/survey-config/survey-config.controller.spec.ts @@ -4,6 +4,7 @@ import { PrismaService } from "../prisma/prisma.service"; import { Test, TestingModule } from "@nestjs/testing"; import { SurveyConfigModule } from "./survey-config.module"; import { ConfigService } from "@nestjs/config"; +import { CreateSurveyConfigDto } from "./dto/create-survey-config.dto"; describe("SurveyConfig e2e", () => { let app: INestApplication; @@ -40,11 +41,12 @@ describe("SurveyConfig e2e", () => { describe("SurveyConfigController createSurveyConfig()", () => { it("should create a new survey config", async () => { - const createSurveyConfigDto = { - name: "Test Survey Config", - departmentId: 1, - startTime: new Date(), - endTime: new Date(), + const createSurveyConfigDto : CreateSurveyConfigDto = { + startTime: new Date("2023-10-31"), + endTime: new Date("2024-01-31"), + departmentId: 4, + onboardingTime: 30, + onboardingTimeUnit: "DAY", }; const response = await pactum @@ -53,12 +55,19 @@ describe("SurveyConfig e2e", () => { .withBody(createSurveyConfigDto) .expectStatus(201); - const createdSurveyConfig = JSON.parse(response.body); - - expect(createdSurveyConfig.name).toBe("Test Survey Config"); - expect(createdSurveyConfig.departmentId).toBe(1); - expect(createdSurveyConfig.startTime).toBeDefined(); - expect(createdSurveyConfig.endTime).toBeDefined(); + const createdSurveyConfig = JSON.parse(JSON.stringify(response.body)); + console.log("createdSurveyConfig", createdSurveyConfig); + expect(createdSurveyConfig.message).toEqual( + `Survey config created successfully` + ); + expect(createdSurveyConfig.data).toHaveProperty("id") + expect(createdSurveyConfig.data.departmentId).toEqual( + createSurveyConfigDto.departmentId + ); + expect(createdSurveyConfig.data.startTime).toBeDefined() + expect(createdSurveyConfig.data.endTime).toBeDefined() + expect(createdSurveyConfig.data.onboardingTime).toEqual(createSurveyConfigDto.onboardingTime); + expect(createdSurveyConfig.data.onboardingTimeUnit).toEqual(createSurveyConfigDto.onboardingTimeUnit); }); }); @@ -69,45 +78,75 @@ describe("SurveyConfig e2e", () => { .get("/survey-config") .expectStatus(200); - const surveyConfigs = JSON.parse(response.body); - - expect(surveyConfigs.length).toBeGreaterThanOrEqual(0); + const surveyConfigs = JSON.parse(JSON.stringify(response.body)); + console.log("surveyConfigs", surveyConfigs); + expect(surveyConfigs.message).toEqual( + "Successfully fetched all survey config." + ); + expect(surveyConfigs.data.length).toBeGreaterThanOrEqual(0); }); }); describe("SurveyConfigController updateSurveyConfigById()", () => { it("should update an existing survey config", async () => { + const testData = { + id: 2, + }; const updateSurveyConfigDto = { - name: "Updated Test Survey Config", - departmentId: 2, + onboardingTime: 2, + onboardingTimeUnit: "MONTH", }; const response = await pactum .spec() - .put("/survey-config/1") + .patch(`/survey-config/update/${testData.id}`) + .withPathParams({ + id: testData.id, + }) .withBody(updateSurveyConfigDto) .expectStatus(200); - const updatedSurveyConfig = JSON.parse(response.body); - - expect(updatedSurveyConfig.name).toBe("Updated Test Survey Config"); - expect(updatedSurveyConfig.departmentId).toBe(2); + const updatedSurveyConfig = JSON.parse(JSON.stringify(response.body)); + + expect(updatedSurveyConfig.message).toEqual( + `Successfully updated survey config for id #${testData.id}` + ); + expect(updatedSurveyConfig.data.id).toEqual(testData.id); + expect(updatedSurveyConfig.data.departmentId).toBeDefined(); + expect(updatedSurveyConfig.data.onboardingTime).toEqual( + updateSurveyConfigDto.onboardingTime + ); + expect(updatedSurveyConfig.data.onboardingTimeUnit).toEqual( + updateSurveyConfigDto.onboardingTimeUnit + ); + expect(updatedSurveyConfig.data.startTime).toBeDefined(); + expect(updatedSurveyConfig.data.endTime).toBeDefined(); }); }); describe("SurveyConfigController deleteSurveyConfigById()", () => { it("should delete an existing survey config", async () => { + const testData = { + id: 2, + }; const response = await pactum .spec() - .delete("/survey-config/1") + .delete(`/survey-config/delete/${testData.id}`) + .withPathParams({ + id: testData.id, + }) .expectStatus(200); - const deletedSurveyConfig = JSON.parse(response.body); - - expect(deletedSurveyConfig.name).toBeDefined(); - expect(deletedSurveyConfig.departmentId).toBeDefined(); - expect(deletedSurveyConfig.startTime).toBeDefined(); - expect(deletedSurveyConfig.endTime).toBeDefined(); + const deletedSurveyConfig = JSON.parse(JSON.stringify(response.body)); + expect(deletedSurveyConfig.message).toEqual( + `Successfully deleted survey config for id #${testData.id}` + ); + expect(deletedSurveyConfig.data.id).toEqual(testData.id); + expect(deletedSurveyConfig.data.departmentId).toBeDefined(); + expect(deletedSurveyConfig.data.onboardingTime).toBeDefined(); + expect(deletedSurveyConfig.data.onboardingTimeUnit).toBeDefined(); + expect(deletedSurveyConfig.data.startTime).toBeDefined(); + expect(deletedSurveyConfig.data.endTime).toBeDefined(); }); }); }); diff --git a/src/survey-cycle-parameter/dto/create-survey-cycle-parameter.dto.ts b/src/survey-cycle-parameter/dto/create-survey-cycle-parameter.dto.ts index 67d858a..655d97e 100644 --- a/src/survey-cycle-parameter/dto/create-survey-cycle-parameter.dto.ts +++ b/src/survey-cycle-parameter/dto/create-survey-cycle-parameter.dto.ts @@ -1,3 +1,4 @@ +import { Type } from "class-transformer"; import { IsBoolean, IsDate, @@ -10,10 +11,12 @@ import { export class CreateSurveyCycleParameterDto { @IsNotEmpty() @IsDate() + @Type(() => Date) startTime: Date; @IsNotEmpty() @IsDate() + @Type(() => Date) endTime: Date; @IsNotEmpty() diff --git a/src/survey-cycle-parameter/survey-cycle-parameter.controller.spec.ts b/src/survey-cycle-parameter/survey-cycle-parameter.controller.spec.ts index dddbbdd..2ef4939 100644 --- a/src/survey-cycle-parameter/survey-cycle-parameter.controller.spec.ts +++ b/src/survey-cycle-parameter/survey-cycle-parameter.controller.spec.ts @@ -3,10 +3,9 @@ import { Test, TestingModule } from "@nestjs/testing"; import { INestApplication, ValidationPipe } from "@nestjs/common"; import { PrismaService } from "../prisma/prisma.service"; import { ConfigService } from "@nestjs/config"; -import { SurveyConfigModule } from "src/survey-config/survey-config.module"; import { CreateSurveyCycleParameterDto } from "./dto/create-survey-cycle-parameter.dto"; -import { ResponseSurveyCycleParameterDto } from "./dto/response-survey-cycle-parameter.dto"; import { UpdateSurveyCycleParameterDto } from "./dto/update-survey-cycle-parameter.dto"; +import { SurveyCycleParameterModule } from "./survey-cycle-parameter.module"; describe("SurveyCycleParameterController e2e", () => { let app: INestApplication; @@ -14,7 +13,7 @@ describe("SurveyCycleParameterController e2e", () => { let module: TestingModule; beforeAll(async () => { module = await Test.createTestingModule({ - imports: [SurveyConfigModule], + imports: [SurveyCycleParameterModule], }).compile(); app = module.createNestApplication(); @@ -39,7 +38,7 @@ describe("SurveyCycleParameterController e2e", () => { app.close(); }); describe("SurveyCycleParameterController createSurveyParameter()", () => { - it("should return created survey cycle parameter with id and name", async function () { + it("should return created survey cycle parameter with id ", async function () { const createSurveyCycleParameterDto: CreateSurveyCycleParameterDto = { startTime: new Date("2023-10-31"), endTime: new Date("2024-01-31"), @@ -53,20 +52,21 @@ describe("SurveyCycleParameterController e2e", () => { .withBody(createSurveyCycleParameterDto) .expectStatus(201); - const createdSurveyCycleParameter = JSON.parse(response); - expect(createdSurveyCycleParameter).toHaveProperty("id"); - expect(createdSurveyCycleParameter.startTime).toEqual( - createSurveyCycleParameterDto.startTime + const createdSurveyCycleParameter = JSON.parse( + JSON.stringify(response.body) ); - expect(createdSurveyCycleParameter.endTime).toEqual( - createSurveyCycleParameterDto.endTime + expect(createdSurveyCycleParameter.message).toEqual( + "Survey cycle parameter created successfully." ); - expect(createdSurveyCycleParameter.isActive).toBeTruthy(); - expect(createdSurveyCycleParameter.surveyConfigId).toEqual( + expect(createdSurveyCycleParameter.data).toHaveProperty("id"); + expect(createdSurveyCycleParameter.data.startTime).toBeDefined(); + expect(createdSurveyCycleParameter.data.endTime).toBeDefined(); + expect(createdSurveyCycleParameter.data.isActive).toBeTruthy(); + expect(createdSurveyCycleParameter.data.surveyConfigId).toEqual( createSurveyCycleParameterDto.surveyConfigId ); - expect(createdSurveyCycleParameter?.createdAt).toBeDefined(); - expect(createdSurveyCycleParameter?.updatedAt).toBeDefined(); + expect(createdSurveyCycleParameter?.data.createdAt).toBeDefined(); + expect(createdSurveyCycleParameter?.data.updatedAt).toBeDefined(); }); }); @@ -74,11 +74,17 @@ describe("SurveyCycleParameterController e2e", () => { it("should get all survey cycle parameter", async () => { const response = await pactum .spec() - .get("/survey-cycle-parameters") + .get("/survey-cycle-parameter") .expectStatus(200); - const getAllSurveyCycleParameter = JSON.parse(response); - expect(Array.isArray(getAllSurveyCycleParameter)).toBeTruthy(); + const getAllSurveyCycleParameter = JSON.parse( + JSON.stringify(response.body) + ); + console.log(getAllSurveyCycleParameter); + expect(getAllSurveyCycleParameter.message).toEqual( + "Successfully fetched all survey cycle parameter." + ); + expect(getAllSurveyCycleParameter.data.length).toBeGreaterThanOrEqual(0); }); }); @@ -88,29 +94,32 @@ describe("SurveyCycleParameterController e2e", () => { id: 1, }; const updateSurveyCycleParameterDto: UpdateSurveyCycleParameterDto = { - startTime: new Date("2023-10-31"), - endTime: new Date("2024-01-31"), + startTime: new Date("2025-10-31"), + endTime: new Date("2025-11-31"), isActive: false, }; const response = await pactum .spec() - .patch(`/survey-cycle-parameter/${testData.id}`) + .patch(`/survey-cycle-parameter/update/${testData.id}`) .withPathParams({ id: testData.id, }) .withBody(updateSurveyCycleParameterDto) .expectStatus(200); - const updatedSurveyCycleParameter = JSON.parse(response); - expect(updatedSurveyCycleParameter).toHaveProperty("id"); - expect(updatedSurveyCycleParameter.startTime).toEqual( - updateSurveyCycleParameterDto.startTime + const updatedSurveyCycleParameter = JSON.parse( + JSON.stringify(response.body) + ); + expect(updatedSurveyCycleParameter.message).toEqual( + `Successfully updated survey cycle parameter for id #${testData.id}` ); - expect(updatedSurveyCycleParameter.endTime).toEqual( - updateSurveyCycleParameterDto.endTime + expect(updatedSurveyCycleParameter.data).toHaveProperty("id"); + expect(updatedSurveyCycleParameter.data.startTime).toBeDefined(); + expect(updatedSurveyCycleParameter.data.endTime).toBeDefined(); + expect(updatedSurveyCycleParameter.data.isActive).toEqual( + updateSurveyCycleParameterDto.isActive ); - expect(updatedSurveyCycleParameter.isActive).toEqual(false); - expect(updatedSurveyCycleParameter?.createdAt).toBeDefined(); - expect(updatedSurveyCycleParameter?.updatedAt).toBeDefined(); + expect(updatedSurveyCycleParameter?.data.createdAt).toBeDefined(); + expect(updatedSurveyCycleParameter?.data.updatedAt).toBeDefined(); }); }); }); diff --git a/src/survey-cycle-parameter/survey-cycle-parameter.controller.ts b/src/survey-cycle-parameter/survey-cycle-parameter.controller.ts index 0e7dd43..89a3847 100644 --- a/src/survey-cycle-parameter/survey-cycle-parameter.controller.ts +++ b/src/survey-cycle-parameter/survey-cycle-parameter.controller.ts @@ -12,7 +12,7 @@ import { Res, } from "@nestjs/common"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; -import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; +import { getPrismaErrorStatusAndMessage } from "../utils/utils"; import { CreateSurveyCycleParameterDto, SurveyCycleParameterFilterDto, diff --git a/src/survey-cycle-parameter/survey-cycle-parameter.module.ts b/src/survey-cycle-parameter/survey-cycle-parameter.module.ts index 7edcac6..1682993 100644 --- a/src/survey-cycle-parameter/survey-cycle-parameter.module.ts +++ b/src/survey-cycle-parameter/survey-cycle-parameter.module.ts @@ -1,5 +1,5 @@ import { Module } from "@nestjs/common"; -import { PrismaModule } from "src/prisma/prisma.module"; +import { PrismaModule } from "../prisma/prisma.module"; import { SurveyCycleParameterController } from "./survey-cycle-parameter.controller"; import { SurveyCycleParameterService } from "./survey-cycle-parameter.service"; diff --git a/src/survey-cycle-parameter/survey-cycle-parameter.service.ts b/src/survey-cycle-parameter/survey-cycle-parameter.service.ts index 5a14b7a..ca4b66d 100644 --- a/src/survey-cycle-parameter/survey-cycle-parameter.service.ts +++ b/src/survey-cycle-parameter/survey-cycle-parameter.service.ts @@ -3,7 +3,7 @@ import { Injectable, NotFoundException, } from "@nestjs/common"; -import { PrismaService } from "src/prisma/prisma.service"; +import { PrismaService } from "../prisma/prisma.service"; import { CreateSurveyCycleParameterDto, SurveyCycleParameterFilterDto, diff --git a/src/survey-form/survey-form.controller.spec.ts b/src/survey-form/survey-form.controller.spec.ts index 31c3f52..8bc7179 100644 --- a/src/survey-form/survey-form.controller.spec.ts +++ b/src/survey-form/survey-form.controller.spec.ts @@ -40,16 +40,17 @@ describe("SurveyFormController e2e", () => { await app.close(); }); - // beforeEach(async () => { - // // You may want to reset or seed the database before each test. - // // This is not shown in the provided code but is important for reliable testing. - // }); + const testData = { + userId: "4f67ae5a-c2a2-4652-81cc-4d1471e1a158", + id: 18, + surveyCycleParameterId: 1, + }; describe("createSurveyForm", () => { it("should create a new survey form", async () => { const createSurveyFormDto: CreateSurveyFormDto = { - userId: "a95f43a6-4bc6-4c5d-8833-8ef2958f9d98", - surveyCycleParameterId: 1, + userId: testData.userId, + surveyCycleParameterId: testData.surveyCycleParameterId, status: SurveyStatusEnum.CREATED, questionsJson: [ { questionId: 1, question: "Is this a dummy question?" }, @@ -61,80 +62,81 @@ describe("SurveyFormController e2e", () => { .post("/survey-form") .withHeaders("Content-Type", "application/json") .withBody(createSurveyFormDto) - .expectStatus(201) - .expectJsonLike({ - message: "SurveyForm created successfully", - data: { - id: 1, - userId: "a95f43a6-4bc6-4c5d-8833-8ef2958f9d98", - surveyCycleParameterId: 1, - status: SurveyStatusEnum.CREATED, - questionsJson: [ - { questionId: 1, question: "Is this a dummy question?" }, - ], - }, - }); - - expect(response.json().data.id).toBeGreaterThan(0); + .expectStatus(201); + + const createdSurveyForm = JSON.parse(JSON.stringify(response.body)); + expect(createdSurveyForm).not.toBeNull(); + expect(createdSurveyForm.message).toEqual( + "SurveyForm created successfully" + ); + expect(createdSurveyForm.data.data.id).toEqual(testData.id); + expect(createdSurveyForm.data.userId).toEqual(createSurveyFormDto.userId); + expect(createdSurveyForm.data.surveyCycleParameterId).toEqual( + createSurveyFormDto.surveyCycleParameterId + ); + expect(createdSurveyForm.data.status).toEqual(createSurveyFormDto.status); + expect(createdSurveyForm.data.questionsJson[0].questionId).toEqual( + createSurveyFormDto.questionsJson[0].questionId + ); + expect(createdSurveyForm.data.questionsJson[0].question).toEqual( + createSurveyFormDto.questionsJson[0].question + ); }); }); describe("getSurveyFormById", () => { it("should get a survey form by ID", async () => { - // Create a survey form in the database (you can use Prisma for this) - // const createdSurveyForm = await createSurveyFormInDatabase(); - const response = await pactum .spec() - .get(`/survey-form/1`) + .get(`/survey-form/${testData.id}`) + .withPathParams({ + id: testData.id, + }) .expectStatus(200); - const surveyForm = response.body; - + const surveyForm = JSON.parse(JSON.stringify(response.body)); + console.log(surveyForm); expect(surveyForm.message).toBe("SurveyForm fetched successfully"); - expect(surveyForm.data.id).toBe(1); + expect(surveyForm.data.id).toEqual(testData.id); }); }); describe("updateSurveyFormStatus", () => { it("should update the status of a survey form", async () => { - // Create a survey form in the database (you can use Prisma for this) - // const createdSurveyForm = await createSurveyFormInDatabase(); - const updateData = { status: SurveyStatusEnum.PUBLISHED }; - const response = await pactum .spec() - .patch(`/survey-form/1`) + .patch(`/survey-form/status/${testData.id}`) + .withPathParams({ + id: testData.id, + }) .withJson(updateData) .expectStatus(200); - const updatedSurveyForm = response.body; - + const updatedSurveyForm = JSON.parse(JSON.stringify(response.body)); expect(updatedSurveyForm.message).toBe("SurveyForm updated successfully"); + expect(updatedSurveyForm.data.id).toEqual(testData.id); expect(updatedSurveyForm.data.status).toBe(SurveyStatusEnum.PUBLISHED); + expect(updatedSurveyForm.data.userId).toBeDefined(); }); }); describe("deleteSurveyForm", () => { it("should delete a survey form by ID", async () => { - // Create a survey form in the database (you can use Prisma for this) - // const createdSurveyForm = await createSurveyFormInDatabase(); - const response = await pactum .spec() - .delete(`/survey-form/1`) + .delete(`/survey-form/${testData.id}`) + .withPathParams({ + id : testData.id + }) .expectStatus(200); - const deletedSurveyForm = response.body; + const deletedSurveyForm = JSON.parse(JSON.stringify(response.body)); expect(deletedSurveyForm.message).toBe("SurveyForm deleted successfully"); - expect(deletedSurveyForm.data.id).toBe(1); + expect(deletedSurveyForm.data.id).toBe(testData.id); + expect(deletedSurveyForm.data.status).toBeDefined(); + expect(deletedSurveyForm.data.userId).toBeDefined(); }); }); }); - -// async function createSurveyFormInDatabase() { -// // Create a survey form in the database (you can use Prisma for this) -// // Return the created survey form for testing purposes -// } diff --git a/src/survey-form/survey-form.controller.ts b/src/survey-form/survey-form.controller.ts index 6902608..2b231c9 100644 --- a/src/survey-form/survey-form.controller.ts +++ b/src/survey-form/survey-form.controller.ts @@ -9,6 +9,7 @@ import { Delete, Res, Logger, + ParseIntPipe, } from "@nestjs/common"; import { SurveyFormService } from "./survey-form.service"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; @@ -62,7 +63,7 @@ export class SurveyFormController { @ApiResponse({ status: HttpStatus.FOUND, type: SurveyFormResponse }) async findSurveyFormById( @Res() res, - @Param("id") id: number + @Param("id", ParseIntPipe) id: number ): Promise { try { this.logger.log(`Initiated fetching new survey form`); @@ -91,7 +92,7 @@ export class SurveyFormController { @ApiResponse({ status: HttpStatus.OK, type: SurveyFormResponse }) async updateSurveyFormStatus( @Res() res, - @Param("id") id: number, + @Param("id", ParseIntPipe) id: number, @Body() updateSurveyFormDto: UpdateSurveyFormStatusDto ): Promise { try { @@ -124,7 +125,7 @@ export class SurveyFormController { @ApiResponse({ status: HttpStatus.OK, type: SurveyFormResponse }) async deleteSurveyForm( @Res() res, - @Param("id") id: number + @Param("id", ParseIntPipe) id: number ): Promise { try { this.logger.log(`Initiated deleting new survey form`); diff --git a/src/survey-form/survey-form.service.ts b/src/survey-form/survey-form.service.ts index 888a7b8..a29bdba 100644 --- a/src/survey-form/survey-form.service.ts +++ b/src/survey-form/survey-form.service.ts @@ -21,7 +21,10 @@ export class SurveyFormService { } async findSurveyFormById(id: number) { + console.log(id); + const surveyForm = await this.prisma.surveyForm.findUnique({ + where: { id }, }); if (!surveyForm) { diff --git a/src/user-metadata/user-metadata.controller.spec.ts b/src/user-metadata/user-metadata.controller.spec.ts index f118daa..847f8fe 100644 --- a/src/user-metadata/user-metadata.controller.spec.ts +++ b/src/user-metadata/user-metadata.controller.spec.ts @@ -38,8 +38,8 @@ describe("UserMetadataController", () => { app.close(); }); - const userId1 = "0f5d0b13-8d72-46c9-a7c4-c1f7e5aa1f17"; - const userId2 = "0f82ac61-9801-488f-babb-3c2d23a413d0"; + const userId1 = "4d45a9e9-4a4d-4c92-aaea-7b5abbd6ff98"; + const userId2 = "4f67ae5a-c2a2-4652-81cc-4d1471e1a158"; describe("UserMetadataController createOrUpdateUserMetadata()", () => { it("should create or update user1 metadata", async () => { @@ -87,27 +87,27 @@ describe("UserMetadataController", () => { }); }); - describe("UserMetadataController deleteUserMetadata()", () => { - it("should delete user1 metadata by ID", async () => { + // describe("UserMetadataController deleteUserMetadata()", () => { + // it("should delete user1 metadata by ID", async () => { - const response = await pactum - .spec() - .delete("/user-metadata/" + userId1) - .expectStatus(200) - .expectBodyContains(userId1); - }); - }); + // const response = await pactum + // .spec() + // .delete("/user-metadata/" + userId1) + // .expectStatus(200) + // .expectBodyContains(userId1); + // }); + // }); - describe("UserMetadataController deleteUserMetadata()", () => { - it("should delete user2 metadata by ID", async () => { + // describe("UserMetadataController deleteUserMetadata()", () => { + // it("should delete user2 metadata by ID", async () => { - const response = await pactum - .spec() - .delete("/user-metadata/" + userId2) - .expectStatus(200) - .expectBodyContains(userId2); - }); - }); + // const response = await pactum + // .spec() + // .delete("/user-metadata/" + userId2) + // .expectStatus(200) + // .expectBodyContains(userId2); + // }); + // }); describe("UserMetadataController findManyUserMetadata()", () => { it("should get all user metadata", async () => { @@ -117,7 +117,8 @@ describe("UserMetadataController", () => { .expectStatus(200) const userMetadata = JSON.parse(JSON.stringify(response.body)); - + console.log(userMetadata); + expect(userMetadata).toStrictEqual({"data": [], "message": "UserMetadata(s) fetched successfully"}); }); From efa378b492aa6442d6cdbc146d862e417572d854 Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Wed, 8 Nov 2023 11:30:03 +0530 Subject: [PATCH 108/175] refactor --- .../survey-config.controller.spec.ts | 2 - .../survey-cycle-parameter.controller.spec.ts | 1 - .../survey-form.controller.spec.ts | 1 - src/survey-form/survey-form.service.ts | 5 +- .../user-metadata.controller.spec.ts | 46 +++++++++---------- 5 files changed, 23 insertions(+), 32 deletions(-) diff --git a/src/survey-config/survey-config.controller.spec.ts b/src/survey-config/survey-config.controller.spec.ts index 65b0b9b..22db028 100644 --- a/src/survey-config/survey-config.controller.spec.ts +++ b/src/survey-config/survey-config.controller.spec.ts @@ -56,7 +56,6 @@ describe("SurveyConfig e2e", () => { .expectStatus(201); const createdSurveyConfig = JSON.parse(JSON.stringify(response.body)); - console.log("createdSurveyConfig", createdSurveyConfig); expect(createdSurveyConfig.message).toEqual( `Survey config created successfully` ); @@ -79,7 +78,6 @@ describe("SurveyConfig e2e", () => { .expectStatus(200); const surveyConfigs = JSON.parse(JSON.stringify(response.body)); - console.log("surveyConfigs", surveyConfigs); expect(surveyConfigs.message).toEqual( "Successfully fetched all survey config." ); diff --git a/src/survey-cycle-parameter/survey-cycle-parameter.controller.spec.ts b/src/survey-cycle-parameter/survey-cycle-parameter.controller.spec.ts index 2ef4939..ff03a0a 100644 --- a/src/survey-cycle-parameter/survey-cycle-parameter.controller.spec.ts +++ b/src/survey-cycle-parameter/survey-cycle-parameter.controller.spec.ts @@ -80,7 +80,6 @@ describe("SurveyCycleParameterController e2e", () => { const getAllSurveyCycleParameter = JSON.parse( JSON.stringify(response.body) ); - console.log(getAllSurveyCycleParameter); expect(getAllSurveyCycleParameter.message).toEqual( "Successfully fetched all survey cycle parameter." ); diff --git a/src/survey-form/survey-form.controller.spec.ts b/src/survey-form/survey-form.controller.spec.ts index 8bc7179..86fbc23 100644 --- a/src/survey-form/survey-form.controller.spec.ts +++ b/src/survey-form/survey-form.controller.spec.ts @@ -95,7 +95,6 @@ describe("SurveyFormController e2e", () => { .expectStatus(200); const surveyForm = JSON.parse(JSON.stringify(response.body)); - console.log(surveyForm); expect(surveyForm.message).toBe("SurveyForm fetched successfully"); expect(surveyForm.data.id).toEqual(testData.id); }); diff --git a/src/survey-form/survey-form.service.ts b/src/survey-form/survey-form.service.ts index a29bdba..ecc0efb 100644 --- a/src/survey-form/survey-form.service.ts +++ b/src/survey-form/survey-form.service.ts @@ -20,11 +20,8 @@ export class SurveyFormService { }); } - async findSurveyFormById(id: number) { - console.log(id); - + async findSurveyFormById(id: number) { const surveyForm = await this.prisma.surveyForm.findUnique({ - where: { id }, }); if (!surveyForm) { diff --git a/src/user-metadata/user-metadata.controller.spec.ts b/src/user-metadata/user-metadata.controller.spec.ts index 847f8fe..0d004da 100644 --- a/src/user-metadata/user-metadata.controller.spec.ts +++ b/src/user-metadata/user-metadata.controller.spec.ts @@ -87,27 +87,27 @@ describe("UserMetadataController", () => { }); }); - // describe("UserMetadataController deleteUserMetadata()", () => { - // it("should delete user1 metadata by ID", async () => { - - // const response = await pactum - // .spec() - // .delete("/user-metadata/" + userId1) - // .expectStatus(200) - // .expectBodyContains(userId1); - // }); - // }); - - // describe("UserMetadataController deleteUserMetadata()", () => { - // it("should delete user2 metadata by ID", async () => { - - // const response = await pactum - // .spec() - // .delete("/user-metadata/" + userId2) - // .expectStatus(200) - // .expectBodyContains(userId2); - // }); - // }); + describe("UserMetadataController deleteUserMetadata()", () => { + it("should delete user1 metadata by ID", async () => { + + const response = await pactum + .spec() + .delete("/user-metadata/" + userId1) + .expectStatus(200) + .expectBodyContains(userId1); + }); + }); + + describe("UserMetadataController deleteUserMetadata()", () => { + it("should delete user2 metadata by ID", async () => { + + const response = await pactum + .spec() + .delete("/user-metadata/" + userId2) + .expectStatus(200) + .expectBodyContains(userId2); + }); + }); describe("UserMetadataController findManyUserMetadata()", () => { it("should get all user metadata", async () => { @@ -116,9 +116,7 @@ describe("UserMetadataController", () => { .get("/user-metadata") .expectStatus(200) - const userMetadata = JSON.parse(JSON.stringify(response.body)); - console.log(userMetadata); - + const userMetadata = JSON.parse(JSON.stringify(response.body)); expect(userMetadata).toStrictEqual({"data": [], "message": "UserMetadata(s) fetched successfully"}); }); From bd4566e587cb3311260f27a81b8b87f75d35881d Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Wed, 8 Nov 2023 16:59:28 +0530 Subject: [PATCH 109/175] TestCases for survey score --- .../survey-form.controller.spec.ts | 26 +-- .../survey-score.controller.spec.ts | 207 ++++++++++++++---- src/survey-score/survey-score.module.ts | 3 +- 3 files changed, 180 insertions(+), 56 deletions(-) diff --git a/src/survey-form/survey-form.controller.spec.ts b/src/survey-form/survey-form.controller.spec.ts index 86fbc23..7d92271 100644 --- a/src/survey-form/survey-form.controller.spec.ts +++ b/src/survey-form/survey-form.controller.spec.ts @@ -42,7 +42,7 @@ describe("SurveyFormController e2e", () => { const testData = { userId: "4f67ae5a-c2a2-4652-81cc-4d1471e1a158", - id: 18, + surveyFormId: 1, surveyCycleParameterId: 1, }; @@ -69,7 +69,7 @@ describe("SurveyFormController e2e", () => { expect(createdSurveyForm.message).toEqual( "SurveyForm created successfully" ); - expect(createdSurveyForm.data.data.id).toEqual(testData.id); + expect(createdSurveyForm.data.data.id).toEqual(testData.surveyFormId); expect(createdSurveyForm.data.userId).toEqual(createSurveyFormDto.userId); expect(createdSurveyForm.data.surveyCycleParameterId).toEqual( createSurveyFormDto.surveyCycleParameterId @@ -88,34 +88,34 @@ describe("SurveyFormController e2e", () => { it("should get a survey form by ID", async () => { const response = await pactum .spec() - .get(`/survey-form/${testData.id}`) + .get(`/survey-form/${testData.surveyFormId}`) .withPathParams({ - id: testData.id, + id: testData.surveyFormId, }) .expectStatus(200); const surveyForm = JSON.parse(JSON.stringify(response.body)); expect(surveyForm.message).toBe("SurveyForm fetched successfully"); - expect(surveyForm.data.id).toEqual(testData.id); + expect(surveyForm.data.id).toEqual(testData.surveyFormId); }); }); describe("updateSurveyFormStatus", () => { it("should update the status of a survey form", async () => { - const updateData = { status: SurveyStatusEnum.PUBLISHED }; + const updateData = { status: SurveyStatusEnum.CLOSED }; const response = await pactum .spec() - .patch(`/survey-form/status/${testData.id}`) + .patch(`/survey-form/status/${testData.surveyFormId}`) .withPathParams({ - id: testData.id, + id: testData.surveyFormId, }) .withJson(updateData) .expectStatus(200); const updatedSurveyForm = JSON.parse(JSON.stringify(response.body)); expect(updatedSurveyForm.message).toBe("SurveyForm updated successfully"); - expect(updatedSurveyForm.data.id).toEqual(testData.id); - expect(updatedSurveyForm.data.status).toBe(SurveyStatusEnum.PUBLISHED); + expect(updatedSurveyForm.data.id).toEqual(testData.surveyFormId); + expect(updatedSurveyForm.data.status).toBe(updateData.status); expect(updatedSurveyForm.data.userId).toBeDefined(); }); }); @@ -124,16 +124,16 @@ describe("SurveyFormController e2e", () => { it("should delete a survey form by ID", async () => { const response = await pactum .spec() - .delete(`/survey-form/${testData.id}`) + .delete(`/survey-form/${testData.surveyFormId}`) .withPathParams({ - id : testData.id + id : testData.surveyFormId }) .expectStatus(200); const deletedSurveyForm = JSON.parse(JSON.stringify(response.body)); expect(deletedSurveyForm.message).toBe("SurveyForm deleted successfully"); - expect(deletedSurveyForm.data.id).toBe(testData.id); + expect(deletedSurveyForm.data.id).toBe(testData.surveyFormId); expect(deletedSurveyForm.data.status).toBeDefined(); expect(deletedSurveyForm.data.userId).toBeDefined(); }); diff --git a/src/survey-score/survey-score.controller.spec.ts b/src/survey-score/survey-score.controller.spec.ts index 81c641c..8f6ef89 100644 --- a/src/survey-score/survey-score.controller.spec.ts +++ b/src/survey-score/survey-score.controller.spec.ts @@ -4,7 +4,11 @@ import { Test, TestingModule } from "@nestjs/testing"; import * as pactum from "pactum"; import { PrismaService } from "../prisma/prisma.service"; import { SurveyScoreModule } from "./survey-score.module"; -import { CreateSurveyScoreDto } from "./dto"; +import { + CreateSurveyScoreDto, + GenerateSurveyScoreDto, + UpdateSurveyScoreDto, +} from "./dto"; describe("SurveyFormController e2e", () => { let app: INestApplication; @@ -43,7 +47,7 @@ describe("SurveyFormController e2e", () => { it("should create a new survey score", async () => { const createSurveyScoreDto: CreateSurveyScoreDto = { competencyId: 1, - competencyLevelNumber: 1, + competencyLevelNumber: 10, score: 30, surveyFormId: 1, }; @@ -54,16 +58,19 @@ describe("SurveyFormController e2e", () => { .withBody(createSurveyScoreDto) .expectStatus(201); - const surveyScore = JSON.parse(response.body); - - expect(surveyScore).toBeDefined(); - expect(surveyScore.surveyFormId).toEqual( - createSurveyScoreDto.surveyFormId + const surveyScore = JSON.parse(JSON.stringify(response.body)); + expect(surveyScore.message).toEqual(`Successfully created survey score`); + expect(surveyScore.data).toHaveProperty("id"); + expect(surveyScore.data.competencyLevelNumber).toEqual( + createSurveyScoreDto.competencyLevelNumber ); - expect(surveyScore.score).toEqual(createSurveyScoreDto.score); - expect(surveyScore.surveyFormId).toEqual( + expect(surveyScore.data.surveyFormId).toEqual( createSurveyScoreDto.surveyFormId ); + expect(surveyScore.data.score).toEqual(createSurveyScoreDto.score); + expect(surveyScore.data.competencyId).toEqual( + createSurveyScoreDto.competencyId + ); }); it("should return a 400 error if the request body is invalid", async () => { @@ -78,53 +85,169 @@ describe("SurveyFormController e2e", () => { .post("/survey-score") .withBody(createSurveyScoreDto) .expectStatus(400); + const createdSurveyScore = JSON.parse(JSON.stringify(response.body)); + expect(createdSurveyScore.error).toEqual("Bad Request"); + }); + }); - const errorMessage = response.json().message; + describe("findAll()", () => { + it("should get all survey scores", async () => { + const response = await pactum + .spec() + .get("/survey-score") + .expectStatus(200); - expect(errorMessage).toContain("Invalid request body"); + const surveyScores = JSON.parse(JSON.stringify(response.body)); + expect(surveyScores.message).toEqual( + "Successfully fetched all survey scores" + ); + expect(surveyScores.data.length).toBeGreaterThanOrEqual(0); }); }); -}); -describe("findAll()", () => { - it("should get all survey scores", async () => { - const response = await pactum.spec().get("/survey-score").expectStatus(200); + describe("findBySurveyFormId()", () => { + it("should get survey scores by survey form ID", async () => { + const surveyFormId = 1; - const surveyScores = JSON.parse(response.body); - - expect(surveyScores).toBeDefined(); - expect(surveyScores.data).toHaveLength(0); + const response = await pactum + .spec() + .get(`/survey-score/survey-form/${surveyFormId}`) + .withPathParams({ + id: surveyFormId, + }) + .expectStatus(200); + + const surveyScores = JSON.parse(JSON.stringify(response.body)); + expect(surveyScores.message).toEqual( + `Successfully fetched survey score with surveyFormId #${surveyFormId}` + ); + expect(surveyScores.data.length).toBeGreaterThanOrEqual(0); + }); }); -}); -describe("findBySurveyFormId()", () => { - it("should get survey scores by survey form ID", async () => { - const surveyFormId = 1; + describe("findOne()", () => { + it("should get a survey score by ID", async () => { + const id = 4; - const response = await pactum - .spec() - .get(`/survey-score/survey-form/${surveyFormId}`) - .expectStatus(200); - - const surveyScores = JSON.parse(response.body); + const response = await pactum + .spec() + .get(`/survey-score/${id}`) + .withPathParams({ + id, + }) + .expectStatus(200); + + const surveyScore = JSON.parse(JSON.stringify(response.body)); + expect(surveyScore.message).toEqual( + `Successfully fetched survey score with id #${id}` + ); + expect(surveyScore.data.id).toEqual(id); + expect(surveyScore.data.surveyFormId).toBeDefined(); + expect(surveyScore.data.competencyId).toBeDefined(); + expect(surveyScore.data.competencyLevelNumber).toBeDefined(); + expect(surveyScore.data.score).toBeDefined(); + }); + }); - expect(surveyScores).toBeDefined(); - expect(surveyScores.data).toHaveLength(0); + describe("update()", () => { + it("should update a survey score by ID", async () => { + const id = 4; + const updateSurveyScoreDto: UpdateSurveyScoreDto = { + score: 87, + }; + const response = await pactum + .spec() + .patch(`/survey-score/${id}`) + .withPathParams({ + id, + }) + .withBody(updateSurveyScoreDto) + .expectStatus(200); + const surveyScore = JSON.parse(JSON.stringify(response.body)); + expect(surveyScore.message).toEqual( + `Successfully updated survey score with id #${id}` + ); + expect(surveyScore.data.id).toEqual(id); + expect(surveyScore.data.surveyFormId).toBeDefined(); + expect(surveyScore.data.competencyId).toBeDefined(); + expect(surveyScore.data.competencyLevelNumber).toBeDefined(); + expect(surveyScore.data.score).toBeDefined(); + }); }); -}); -describe("findOne()", () => { - it("should get a survey score by ID", async () => { - const id = 1; + describe("remove()", () => { + it("should delete a survey score by ID", async () => { + const id = 1; + const response = await pactum + .spec() + .delete(`/survey-score/${id}`) + .withPathParams({ + id, + }) + .expectStatus(200); + const surveyScore = JSON.parse(JSON.stringify(response.body)); + expect(surveyScore.message).toEqual( + `Successfully deleted survey score with id #${id}` + ); + expect(surveyScore.data.id).toEqual(id); + expect(surveyScore.data.surveyFormId).toBeDefined(); + expect(surveyScore.data.competencyId).toBeDefined(); + expect(surveyScore.data.competencyLevelNumber).toBeDefined(); + expect(surveyScore.data.score).toBeDefined(); + }); + }); - const response = await pactum - .spec() - .get(`/survey-score/${id}`) - .expectStatus(200); + describe("generateSurveyScore()", () => { + it("should calculate survey score by survey form id", async () => { + const generateSurveyScoreDto: GenerateSurveyScoreDto = { + surveyFormId: 1, + }; + const response = await pactum + .spec() + .post("/survey-score/calculate-score") + .withHeaders({ + "Content-Type": "application/json", + }) + .withBody(generateSurveyScoreDto) + .expectStatus(200); + const surveyScore = JSON.parse(JSON.stringify(response.body)); + expect(surveyScore.message).toEqual( + `Successfully calculated survey score for surveyFormId #${generateSurveyScoreDto.surveyFormId}` + ); + }); + }); - const surveyScore = JSON.parse(response.body); + describe("getLatestSurveyScoreByUserId()", () => { + it("should get latest survey scores by user id", async () => { + const userId = "4f67ae5a-c2a2-4652-81cc-4d1471e1a158"; + const response = await pactum + .spec() + .get(`/survey-score/latest-survey-score/${userId}`) + .withPathParams({ + userId, + }) + .expectStatus(200); + const surveyScore = JSON.parse(JSON.stringify(response.body)); + expect(surveyScore.message).toEqual( + `Successfully fetched latest survey score for userId #${userId}` + ); + }); + }); - expect(surveyScore).toBeDefined(); - expect(surveyScore.id).toEqual(id); + describe("getAllSurveyScoreByUserId()", () => { + it("should get all survey scores by user id", async () => { + const userId = "4f67ae5a-c2a2-4652-81cc-4d1471e1a158"; + const response = await pactum + .spec() + .get(`/survey-score/latest-survey-score/${userId}`) + .withPathParams({ + userId, + }) + .expectStatus(200); + const surveyScore = JSON.parse(JSON.stringify(response.body)); + expect(surveyScore.message).toEqual( + `Successfully fetched latest survey score for userId #${userId}` + ); + }); }); }); diff --git a/src/survey-score/survey-score.module.ts b/src/survey-score/survey-score.module.ts index 9db82a8..da5f029 100644 --- a/src/survey-score/survey-score.module.ts +++ b/src/survey-score/survey-score.module.ts @@ -4,9 +4,10 @@ import { SurveyFormService } from "../survey-form/survey-form.service"; import { QuestionBankModule } from "../question-bank/question-bank.module"; import { SurveyScoreController } from "./survey-score.controller"; import { SurveyScoreService } from "./survey-score.service"; +import { PrismaModule } from "../prisma/prisma.module"; @Module({ - imports: [QuestionBankModule], + imports: [QuestionBankModule, PrismaModule], controllers: [SurveyScoreController], providers: [SurveyScoreService, ResponseTrackerService, SurveyFormService], }) From 9782985565bb55c537bcbf91fa960ae5a33384f7 Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Fri, 10 Nov 2023 13:10:38 +0530 Subject: [PATCH 110/175] Fixes and refactoring of unit testcases --- .../admin-competency.controller.spec.ts | 80 ++++++--- .../admin-department.controller.spec.ts | 4 +- .../question-bank.controller.spec.ts | 160 +++++++++--------- src/question-bank/question-bank.service.ts | 10 -- .../response-tracker.controller.ts | 3 +- .../response-tracker.service.ts | 1 - .../survey-config.controller.spec.ts | 8 +- .../survey-cycle-parameter.controller.spec.ts | 2 +- .../survey-form.controller.spec.ts | 2 +- .../survey-score.controller.spec.ts | 4 +- .../user-metadata.controller.spec.ts | 50 +++--- 11 files changed, 174 insertions(+), 150 deletions(-) diff --git a/src/admin-competency/admin-competency.controller.spec.ts b/src/admin-competency/admin-competency.controller.spec.ts index 186a113..59113dd 100644 --- a/src/admin-competency/admin-competency.controller.spec.ts +++ b/src/admin-competency/admin-competency.controller.spec.ts @@ -35,7 +35,7 @@ describe("AdminCompetencyController e2e", () => { pactum.request.setBaseUrl(`http://localhost:${PORT}/${apiPrefix}`); }); afterAll(async () => { - app.close(); + await app.close(); }); describe("AdminCompetencyController, findAll()", () => { @@ -43,29 +43,68 @@ describe("AdminCompetencyController e2e", () => { const response = await pactum .spec() .get("/admin-competency") - .expectStatus(200) - + .expectStatus(200); + const adminCompetencies = JSON.parse(JSON.stringify(response.body)); - expect(adminCompetencies.message).toEqual('Successfully fetched all admin-competency'); + expect(adminCompetencies.message).toEqual( + "Successfully fetched all admin-competency" + ); expect(adminCompetencies.data.length).toBeGreaterThanOrEqual(0); }); }); + describe("AdminCompetencyController, findAllCompetencyNames()", () => { + it("should get admin competency by names", async () => { + const response = await pactum + .spec() + .get("/admin-competency/names") + .expectStatus(200); + + const adminCompetencies = JSON.parse(JSON.stringify(response.body)); + expect(adminCompetencies.message).toEqual( + `Successfully fetched all admin-competency` + ); + expect(adminCompetencies.data.length).toBeGreaterThanOrEqual(0); + }); + }); + + describe("AdminCompetencyController findOne()", () => { + const testData = { + competencyId: 1, + }; + it("should findOne admin competency by competencyId", async () => { + const response = await pactum + .spec() + .get(`/admin-competency/${testData.competencyId}`) + .withPathParams({ + competencyId: testData.competencyId, + }) + .expectStatus(200); + + const adminCompetncy = JSON.parse(JSON.stringify(response.body)); + expect(adminCompetncy).not.toBeNull(); + expect(adminCompetncy.message).toEqual( + `Successfully fetched admin-competency with competency id #${testData.competencyId}` + ); + expect(adminCompetncy.data.competencyId).toEqual(testData.competencyId); + expect(adminCompetncy.data.name).toBeDefined(); + expect(adminCompetncy.data.description).toBeDefined(); + }); + }); + describe("AdminCompetencyController update()", () => { - it("should get admin competency by competencyId", async () => { + it("should updated admin competency by competencyId", async () => { const updatedAdminCompetencyDto: UpdateAdminCompetencyDto = { name: "Updated Test Competency Name", description: "Updated Test Description", }; const testData = { - id: 5, - competencyId: 5, + competencyId: 1, }; const response = await pactum .spec() - .patch(`/admin-competency/${testData.id}/${testData.competencyId}`) + .patch(`/admin-competency/${testData.competencyId}`) .withPathParams({ - id: testData.id, competencyId: testData.competencyId, }) .withBody(updatedAdminCompetencyDto) @@ -74,39 +113,36 @@ describe("AdminCompetencyController e2e", () => { const updatedAdminCompetency = JSON.parse(JSON.stringify(response.body)); expect(updatedAdminCompetency).not.toBeNull(); expect(updatedAdminCompetency.message).toEqual( - `Successfully updated admin-competency with id #${testData.id} and competency id #${testData.competencyId}` - ); - expect(updatedAdminCompetency.data.name).toEqual( - updatedAdminCompetencyDto.name + `Successfully updated admin-competency competency id #${testData.competencyId}` ); - expect(updatedAdminCompetency.data.description).toEqual( - updatedAdminCompetencyDto.description + expect(updatedAdminCompetency.data.competencyId).toEqual( + testData.competencyId ); + expect(updatedAdminCompetency.data.name).toBeDefined(); + expect(updatedAdminCompetency.data.description).toBeDefined(); }); }); describe("AdminCompetencyController remove()", () => { it("should delete an admin competency by id", async () => { const testData = { - id: 8, - competencyId: 8, + competencyId: 5, }; const response = await pactum .spec() - .delete(`/admin-competency/${testData.id}/${testData.competencyId}`) + .delete(`/admin-competency/${testData.competencyId}`) .withPathParams({ - id: testData.id, competencyId: testData.competencyId, }) .expectStatus(200); const deletedAdminCompetency = JSON.parse(JSON.stringify(response.body)); console.log("deletedAdminCompetency", deletedAdminCompetency); - + expect(deletedAdminCompetency).not.toBeNull(); expect(deletedAdminCompetency.message).toEqual( - `Successfully deleted admin-competency with id #${testData.id} and competency id #${testData.competencyId}` + `Successfully deleted admin-competency competency id #${testData.competencyId}` ); - expect(deletedAdminCompetency.data.id).toEqual(testData.id); + expect(deletedAdminCompetency.data).toHaveProperty("id"); expect(deletedAdminCompetency.data.competencyId).toEqual( testData.competencyId ); diff --git a/src/admin-department/admin-department.controller.spec.ts b/src/admin-department/admin-department.controller.spec.ts index b164aee..c4dbe0b 100644 --- a/src/admin-department/admin-department.controller.spec.ts +++ b/src/admin-department/admin-department.controller.spec.ts @@ -32,11 +32,11 @@ describe("AdminDepartment e2e", () => { }); afterAll(async () => { - app.close(); + await app.close(); }); const testData = { - departmentId : 5 + departmentId : 1 } describe("AdminDepartmentController createOrUpdateAdminDepartment()", () => { diff --git a/src/question-bank/question-bank.controller.spec.ts b/src/question-bank/question-bank.controller.spec.ts index 25a881c..4b2a255 100644 --- a/src/question-bank/question-bank.controller.spec.ts +++ b/src/question-bank/question-bank.controller.spec.ts @@ -35,101 +35,101 @@ describe("QuestionBankController", () => { pactum.request.setBaseUrl(`http://localhost:${PORT}/${apiPrefix}`); }); afterAll(async () => { - app.close(); + await app.close(); }); - describe("QuestionBankController createQuestionByCompentencyLevel()", () => { - it("should return created question by compentency level", async () => { - const createQuestionDto: CreateQuestionBankDto = { - competencyId: 6, - question: "This is test question.", - competencyLevelNumber: 4, - }; - const response = await pactum - .spec() - .post("/question-bank") - .withBody(createQuestionDto) - .expectStatus(201); - const createdQuestion = JSON.parse(JSON.stringify(response.body)); + describe("QuestionBankController createQuestionByCompentencyLevel()", () => { + it("should return created question by compentency level", async () => { + const createQuestionDto: CreateQuestionBankDto = { + competencyId: 1, + question: "This is test question.", + competencyLevelNumber: 1, + }; + const response = await pactum + .spec() + .post("/question-bank") + .withBody(createQuestionDto) + .expectStatus(201); + const createdQuestion = JSON.parse(JSON.stringify(response.body)); - expect(createdQuestion.message).toEqual("Question created sucessfully."); - expect(createdQuestion).not.toBeNull(); - expect(createdQuestion.data.competencyId).toEqual( - createQuestionDto.competencyId - ); - expect(createdQuestion.data.question).toEqual(createQuestionDto.question); - expect(createdQuestion.data.competencyLevelNumber).toEqual( - createQuestionDto.competencyLevelNumber - ); - }); + expect(createdQuestion.message).toEqual("Question created sucessfully."); + expect(createdQuestion).not.toBeNull(); + expect(createdQuestion.data.competencyId).toEqual( + createQuestionDto.competencyId + ); + expect(createdQuestion.data.question).toEqual(createQuestionDto.question); + expect(createdQuestion.data.competencyLevelNumber).toEqual( + createQuestionDto.competencyLevelNumber + ); }); + }); - describe("QuestionBankController getAllQuestions()", () => { - it("should return all questions", async () => { - const response = await pactum - .spec() - .get("/question-bank") - .expectStatus(200); + describe("QuestionBankController getAllQuestions()", () => { + it("should return all questions", async () => { + const response = await pactum + .spec() + .get("/question-bank") + .expectStatus(200); - const questions = JSON.parse(JSON.stringify(response.body)); - expect(questions.message).toEqual("Successfully fetched all questions."); - expect(questions.data.length).toBeGreaterThanOrEqual(0); - }); + const questions = JSON.parse(JSON.stringify(response.body)); + expect(questions.message).toEqual("Successfully fetched all questions."); + expect(questions.data.length).toBeGreaterThanOrEqual(0); }); + }); - describe("QuestionBankController updateQuestionById()", () => { - const testData = { - id: 1, + describe("QuestionBankController updateQuestionById()", () => { + const testData = { + id: 1, + }; + it("should update the question by id", async () => { + const updatedQuestionDto: UpdateQuestionBankDto = { + question: "Updated Test Question", }; - it("should update the question by id", async () => { - const updatedQuestionDto: UpdateQuestionBankDto = { - question: "Updated Test Question", - }; - const response = await pactum - .spec() - .patch(`/question-bank/update/${testData.id}`) - .withPathParams({ - id: testData.id, - }) - .withBody(updatedQuestionDto) - .expectStatus(200); - const updatedQuestion = JSON.parse(JSON.stringify(response.body)); - expect(updatedQuestion.message).toEqual( - `Successfully updated question for id #${testData.id}` - ); - expect(updatedQuestion.data.question).toEqual( - updatedQuestionDto.question - ); - expect(updatedQuestion.data.competencyId).toBeDefined(); - expect(updatedQuestion.data.competencyLevelNumber).toBeDefined(); - }); + const response = await pactum + .spec() + .patch(`/question-bank/update/${testData.id}`) + .withPathParams({ + id: testData.id, + }) + .withBody(updatedQuestionDto) + .expectStatus(200); + const updatedQuestion = JSON.parse(JSON.stringify(response.body)); + expect(updatedQuestion.message).toEqual( + `Successfully updated question for id #${testData.id}` + ); + expect(updatedQuestion.data.question).toEqual( + updatedQuestionDto.question + ); + expect(updatedQuestion.data.competencyId).toBeDefined(); + expect(updatedQuestion.data.competencyLevelNumber).toBeDefined(); }); + }); - describe("QuestionBankController deleteQuestionById()", () => { - const testData = { - id: 1, - }; - it("should delete a question with given id", async () => { - const response = await pactum - .spec() - .delete(`/question-bank/delete/${testData.id}`) - .withPathParams({ - id: testData.id, - }) - .expectStatus(200); - const deletedQuestion = JSON.parse(JSON.stringify(response.body)); - expect(deletedQuestion.message).toEqual( - `Successfully deleted question for id #${testData.id}` - ); - expect(deletedQuestion.data.question).toBeDefined(); - expect(deletedQuestion.data.competencyId).toBeDefined(); - expect(deletedQuestion.data.competencyLevelNumber).toBeDefined(); - }); + describe("QuestionBankController deleteQuestionById()", () => { + const testData = { + id: 1, + }; + it("should delete a question with given id", async () => { + const response = await pactum + .spec() + .delete(`/question-bank/delete/${testData.id}`) + .withPathParams({ + id: testData.id, + }) + .expectStatus(200); + const deletedQuestion = JSON.parse(JSON.stringify(response.body)); + expect(deletedQuestion.message).toEqual( + `Successfully deleted question for id #${testData.id}` + ); + expect(deletedQuestion.data.question).toBeDefined(); + expect(deletedQuestion.data.competencyId).toBeDefined(); + expect(deletedQuestion.data.competencyLevelNumber).toBeDefined(); }); + }); describe("QuestionBankController getAllQuestionsForUser()", () => { const testData = { - userId: "0f5d0b13-8d72-46c9-a7c4-c1f7e5aa1f17", + userId: "4d45a9e9-4a4d-4c92-aaea-7b5abbd6ff98", }; it("should get all the mapped question for a user with given userId.", async () => { const response = await pactum diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index 8234f4c..d6078a2 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -289,16 +289,6 @@ export class QuestionBankService { }; }); - }); - - const mappedQuestions = _.map(questions, (data) => { - const { id, question } = data; - return { - question, - questionId: id, - }; - }); - questionLists.push(...mappedQuestions); competencyLevelNumber--; } diff --git a/src/response-tracker/response-tracker.controller.ts b/src/response-tracker/response-tracker.controller.ts index 2e31de3..9f5903e 100644 --- a/src/response-tracker/response-tracker.controller.ts +++ b/src/response-tracker/response-tracker.controller.ts @@ -13,14 +13,13 @@ import { Res, } from "@nestjs/common"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; -import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; import { CreateResponseTrackerDto, ResponseTrackerDtoMultipleResponse, ResponseTrackerDtoResponse, UpdateResponseTrackerDto, } from "./dto"; -import { getPrismaErrorStatusAndMessage } from "..//utils/utils"; +import { getPrismaErrorStatusAndMessage } from "../utils/utils"; import { ResponseTrackerService } from "./response-tracker.service"; @Controller("response-tracker") diff --git a/src/response-tracker/response-tracker.service.ts b/src/response-tracker/response-tracker.service.ts index e2320f0..cd82dca 100644 --- a/src/response-tracker/response-tracker.service.ts +++ b/src/response-tracker/response-tracker.service.ts @@ -4,7 +4,6 @@ import { NotAcceptableException, NotFoundException, } from "@nestjs/common"; -import { ResponseTrackerStatusEnum } from "@prisma/client"; import _ from "lodash"; import { CreateResponseTrackerDto, diff --git a/src/survey-config/survey-config.controller.spec.ts b/src/survey-config/survey-config.controller.spec.ts index 22db028..d85dd07 100644 --- a/src/survey-config/survey-config.controller.spec.ts +++ b/src/survey-config/survey-config.controller.spec.ts @@ -36,7 +36,7 @@ describe("SurveyConfig e2e", () => { }); afterAll(async () => { - app.close(); + await app.close(); }); describe("SurveyConfigController createSurveyConfig()", () => { @@ -44,7 +44,7 @@ describe("SurveyConfig e2e", () => { const createSurveyConfigDto : CreateSurveyConfigDto = { startTime: new Date("2023-10-31"), endTime: new Date("2024-01-31"), - departmentId: 4, + departmentId: 1, onboardingTime: 30, onboardingTimeUnit: "DAY", }; @@ -88,7 +88,7 @@ describe("SurveyConfig e2e", () => { describe("SurveyConfigController updateSurveyConfigById()", () => { it("should update an existing survey config", async () => { const testData = { - id: 2, + id: 1, }; const updateSurveyConfigDto = { onboardingTime: 2, @@ -125,7 +125,7 @@ describe("SurveyConfig e2e", () => { describe("SurveyConfigController deleteSurveyConfigById()", () => { it("should delete an existing survey config", async () => { const testData = { - id: 2, + id: 1, }; const response = await pactum .spec() diff --git a/src/survey-cycle-parameter/survey-cycle-parameter.controller.spec.ts b/src/survey-cycle-parameter/survey-cycle-parameter.controller.spec.ts index ff03a0a..cf88e07 100644 --- a/src/survey-cycle-parameter/survey-cycle-parameter.controller.spec.ts +++ b/src/survey-cycle-parameter/survey-cycle-parameter.controller.spec.ts @@ -35,7 +35,7 @@ describe("SurveyCycleParameterController e2e", () => { pactum.request.setBaseUrl(`http://localhost:${PORT}/${apiPrefix}`); }); afterAll(async () => { - app.close(); + await app.close(); }); describe("SurveyCycleParameterController createSurveyParameter()", () => { it("should return created survey cycle parameter with id ", async function () { diff --git a/src/survey-form/survey-form.controller.spec.ts b/src/survey-form/survey-form.controller.spec.ts index 7d92271..906ef70 100644 --- a/src/survey-form/survey-form.controller.spec.ts +++ b/src/survey-form/survey-form.controller.spec.ts @@ -69,7 +69,7 @@ describe("SurveyFormController e2e", () => { expect(createdSurveyForm.message).toEqual( "SurveyForm created successfully" ); - expect(createdSurveyForm.data.data.id).toEqual(testData.surveyFormId); + expect(createdSurveyForm.data).toHaveProperty("id") expect(createdSurveyForm.data.userId).toEqual(createSurveyFormDto.userId); expect(createdSurveyForm.data.surveyCycleParameterId).toEqual( createSurveyFormDto.surveyCycleParameterId diff --git a/src/survey-score/survey-score.controller.spec.ts b/src/survey-score/survey-score.controller.spec.ts index 8f6ef89..f7e12a6 100644 --- a/src/survey-score/survey-score.controller.spec.ts +++ b/src/survey-score/survey-score.controller.spec.ts @@ -127,7 +127,7 @@ describe("SurveyFormController e2e", () => { describe("findOne()", () => { it("should get a survey score by ID", async () => { - const id = 4; + const id = 1; const response = await pactum .spec() @@ -151,7 +151,7 @@ describe("SurveyFormController e2e", () => { describe("update()", () => { it("should update a survey score by ID", async () => { - const id = 4; + const id = 1; const updateSurveyScoreDto: UpdateSurveyScoreDto = { score: 87, }; diff --git a/src/user-metadata/user-metadata.controller.spec.ts b/src/user-metadata/user-metadata.controller.spec.ts index 0d004da..2d5a9a7 100644 --- a/src/user-metadata/user-metadata.controller.spec.ts +++ b/src/user-metadata/user-metadata.controller.spec.ts @@ -35,7 +35,7 @@ describe("UserMetadataController", () => { }); afterAll(async () => { - app.close(); + await app.close(); }); const userId1 = "4d45a9e9-4a4d-4c92-aaea-7b5abbd6ff98"; @@ -79,10 +79,10 @@ describe("UserMetadataController", () => { .spec() .get("/user-metadata") .expectStatus(200) - .expectBodyContains(userId1) - .expectBodyContains(userId2) const userMetadata = JSON.parse(JSON.stringify(response.body)); + expect(userMetadata.message).toEqual("UserMetadata(s) fetched successfully"); + expect(userMetadata.data.length).toBeGreaterThanOrEqual(0) }); }); @@ -98,27 +98,27 @@ describe("UserMetadataController", () => { }); }); - describe("UserMetadataController deleteUserMetadata()", () => { - it("should delete user2 metadata by ID", async () => { - - const response = await pactum - .spec() - .delete("/user-metadata/" + userId2) - .expectStatus(200) - .expectBodyContains(userId2); - }); - }); - - describe("UserMetadataController findManyUserMetadata()", () => { - it("should get all user metadata", async () => { - const response = await pactum - .spec() - .get("/user-metadata") - .expectStatus(200) - - const userMetadata = JSON.parse(JSON.stringify(response.body)); - expect(userMetadata).toStrictEqual({"data": [], "message": "UserMetadata(s) fetched successfully"}); + // describe("UserMetadataController deleteUserMetadata()", () => { + // it("should delete user2 metadata by ID", async () => { + + // const response = await pactum + // .spec() + // .delete("/user-metadata/" + userId2) + // .expectStatus(200) + // .expectBodyContains(userId2); + // }); + // }); + + // describe("UserMetadataController findManyUserMetadata()", () => { + // it("should get all user metadata", async () => { + // const response = await pactum + // .spec() + // .get("/user-metadata") + // .expectStatus(200) + + // const userMetadata = JSON.parse(JSON.stringify(response.body)); + // expect(userMetadata).toStrictEqual({"data": [], "message": "UserMetadata(s) fetched successfully"}); - }); - }); + // }); + // }); }); From 004400dd6b304eed4149e34601f7264d8e7a8467 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 8 Nov 2023 13:10:19 +0530 Subject: [PATCH 111/175] Removing Department from schema. --- prisma/schema.prisma | 100 ++++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 48 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index dbfd164..c83d816 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -16,8 +16,6 @@ model UserMetadata { userId String @unique() @db.Uuid userName String @unique() isNewEmployee Boolean - departmentId Int - Department AdminDepartment @relation(fields: [departmentId], references: [departmentId]) designation String? dateOfJoining DateTime createdAt DateTime @default(now()) @@ -26,6 +24,8 @@ model UserMetadata { isAdmin Boolean ResponseTrackersAsAssessee ResponseTracker[] @relation("AssesseeResponseTrackers") ResponseTrackersAsAssessor ResponseTracker[] @relation("AssessorResponseTrackers") + // departmentId Int + // Department AdminDepartment @relation(fields: [departmentId], references: [departmentId]) @@id([id, userId]) } @@ -55,62 +55,66 @@ model AdminCompetency { @@map("admin_competencies") } -model AdminDepartment { - id Int @default(autoincrement()) - departmentId Int @unique - name String - description String? - UserMetadata UserMetadata[] - SurveyConfig SurveyConfig? +// model AdminDepartment { +// id Int @default(autoincrement()) +// departmentId Int @unique +// name String +// description String? +// UserMetadata UserMetadata[] +// SurveyConfig SurveyConfig? - @@id([id, departmentId]) - @@map("admin_department") -} +// @@id([id, departmentId]) +// @@map("admin_department") +// } model SurveyConfig { - id Int @id @default(autoincrement()) - onboardingTime Int - onboardingTimeUnit TimeUnitsEnum - //will be replaced with surveyCycle in phase two - startTime DateTime - endTime DateTime - departmentId Int @unique - AdminDepartment AdminDepartment @relation(fields: [departmentId], references: [departmentId]) - SurveyCycleParameters SurveyCycleParameter[] + id Int @id @default(autoincrement()) + surveyName String + onboardingTime Int @default(3) + onboardingTimeUnit TimeUnitsEnum @default(MONTH) + startTime DateTime + endTime DateTime + isActive Boolean + SurveyForm SurveyForm[] + // departmentId Int @unique + // AdminDepartment AdminDepartment @relation(fields: [departmentId], references: [departmentId]) + // SurveyCycleParameters SurveyCycleParameter[] @@map("survey_config") } -model SurveyCycleParameter { - id Int @id @default(autoincrement()) - startTime DateTime - endTime DateTime - surveyForm SurveyForm[] - isActive Boolean - surveyConfigId Int? - SurveyConfig SurveyConfig? @relation(fields: [surveyConfigId], references: [id]) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt +// model SurveyCycleParameter { +// id Int @id @default(autoincrement()) +// startTime DateTime +// endTime DateTime +// surveyForm SurveyForm[] +// isActive Boolean +// surveyConfigId Int? +// SurveyConfig SurveyConfig? @relation(fields: [surveyConfigId], references: [id]) +// createdAt DateTime @default(now()) +// updatedAt DateTime @updatedAt - @@map("survey_cycle_parameters") -} +// @@map("survey_cycle_parameters") +// } model SurveyForm { - id Int @id @default(autoincrement()) - userId String @db.Uuid - UserMetadata UserMetadata @relation(fields: [userId], references: [userId]) - questionsJson Json //{questionId: question} - surveyCycleParameterId Int - surveyCycleParameter SurveyCycleParameter @relation(fields: [surveyCycleParameterId], references: [id]) - status SurveyStatusEnum - overallScore Float? - sunbirdCredentialIds String? - ResponseTracker ResponseTracker[] - SurveyScore SurveyScore[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - @@unique([userId, surveyCycleParameterId]) + id Int @id @default(autoincrement()) + userId String @db.Uuid + UserMetadata UserMetadata @relation(fields: [userId], references: [userId]) + questionsJson Json //{questionId: question} + surveyConfigId Int + SurveyConfig SurveyConfig @relation(fields: [surveyConfigId], references: [id]) + status SurveyStatusEnum + overallScore Float? + sunbirdCredentialIds String? + ResponseTracker ResponseTracker[] + SurveyScore SurveyScore[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + // surveyCycleParameterId Int + // surveyCycleParameter SurveyCycleParameter @relation(fields: [surveyCycleParameterId], references: [id]) + + @@unique([userId, surveyConfigId]) @@map("survey_form") } From b57040ac42b8099dce91d82ba843d2299380e48b Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 8 Nov 2023 16:19:28 +0530 Subject: [PATCH 112/175] Removing Department model from MockModels. --- prisma/schema.prisma | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index c83d816..6bc6791 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -175,8 +175,8 @@ model User { designation String Level Level? @relation(fields: [levelNumber], references: [levelNumber]) levelNumber Int? - Department Department? @relation(fields: [departmentId], references: [id]) - departmentId Int? + // Department Department? @relation(fields: [departmentId], references: [id]) + // departmentId Int? @@map("users") } @@ -188,12 +188,12 @@ model Level { CompetencyLevel CompetencyLevel[] } -model Department { - id Int @id @default(autoincrement()) - name String @unique - description String? - users User[] -} +// model Department { +// id Int @id @default(autoincrement()) +// name String @unique +// description String? +// users User[] +// } model Designation { id Int @id @default(autoincrement()) From 8ab572c62b2f95607a02b6874aada3845f2fbef0 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 8 Nov 2023 18:15:42 +0530 Subject: [PATCH 113/175] Upadte prisma schema. --- prisma/schema.prisma | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 6bc6791..24282a1 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -74,7 +74,7 @@ model SurveyConfig { onboardingTimeUnit TimeUnitsEnum @default(MONTH) startTime DateTime endTime DateTime - isActive Boolean + isActive Boolean @default(false) SurveyForm SurveyForm[] // departmentId Int @unique // AdminDepartment AdminDepartment @relation(fields: [departmentId], references: [departmentId]) From 9fe9c6cdc3af9a0ae7d531aee468f33f004d11be Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Tue, 14 Nov 2023 10:20:22 +0530 Subject: [PATCH 114/175] Added "UserMapping" table. --- prisma/schema.prisma | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 24282a1..bb96001 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -76,6 +76,7 @@ model SurveyConfig { endTime DateTime isActive Boolean @default(false) SurveyForm SurveyForm[] + UserMapping UserMapping[] // departmentId Int @unique // AdminDepartment AdminDepartment @relation(fields: [departmentId], references: [departmentId]) // SurveyCycleParameters SurveyCycleParameter[] @@ -83,6 +84,14 @@ model SurveyConfig { @@map("survey_config") } +model UserMapping { + id Int @id @default(autoincrement()) + surveyConfigId Int + SurveyConfig SurveyConfig @relation(fields: [surveyConfigId], references: [id]) + assesseeId String @db.Uuid + assessorIds String[] @db.Uuid +} + // model SurveyCycleParameter { // id Int @id @default(autoincrement()) // startTime DateTime From b13b59e86227f7bb22424d204b6770e951a6af15 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Tue, 14 Nov 2023 10:34:04 +0530 Subject: [PATCH 115/175] Removing Department and SurveyCycle Parameter APIs. --- .../admin-department.controller.spec.ts | 62 ------- .../admin-department.controller.ts | 137 ---------------- .../admin-department.module.ts | 20 --- .../admin-department.service.ts | 106 ------------ .../dto/create-admin-department.dto.ts | 28 ---- .../dto/response-admin-department.dto.ts | 9 - .../dto/update-admin-department.dto.ts | 12 -- src/app.module.ts | 4 - .../dto/create-mock-department.dto.ts | 12 -- .../dto/response-mock-department.dto.ts | 5 - .../dto/update-mock-department.dto.ts | 4 - .../mock-department.controller.ts | 97 ----------- .../mock-department/mock-department.module.ts | 9 - .../mock-department.service.ts | 46 ------ .../mock-user/dto/create-mock-user.dto.ts | 5 - .../mock-user/dto/response-mock-user.dto.ts | 3 - .../mock-user/mock-user.service.ts | 6 - src/mockModules/mock.module.ts | 3 - .../response-tracker.service.ts | 8 +- src/scheduled-tasks/scheduled-tasks.module.ts | 8 +- .../dto/create-survey-cycle-parameter.dto.ts | 57 ------- .../response-survey-cycle-parameter.dto.ts | 11 -- .../dto/update-survey-cycle-parameter.dto.ts | 6 - .../survey-cycle-parameter.controller.spec.ts | 124 -------------- .../survey-cycle-parameter.controller.ts | 155 ------------------ .../survey-cycle-parameter.module.ts | 12 -- .../survey-cycle-parameter.service.ts | 80 --------- .../dto/user-metadata-filter.dto.ts | 5 - src/user-metadata/user-metadata.module.ts | 10 +- 29 files changed, 10 insertions(+), 1034 deletions(-) delete mode 100644 src/admin-department/admin-department.controller.spec.ts delete mode 100644 src/admin-department/admin-department.controller.ts delete mode 100644 src/admin-department/admin-department.module.ts delete mode 100644 src/admin-department/admin-department.service.ts delete mode 100644 src/admin-department/dto/create-admin-department.dto.ts delete mode 100644 src/admin-department/dto/response-admin-department.dto.ts delete mode 100644 src/admin-department/dto/update-admin-department.dto.ts delete mode 100644 src/mockModules/mock-department/dto/create-mock-department.dto.ts delete mode 100644 src/mockModules/mock-department/dto/response-mock-department.dto.ts delete mode 100644 src/mockModules/mock-department/dto/update-mock-department.dto.ts delete mode 100644 src/mockModules/mock-department/mock-department.controller.ts delete mode 100644 src/mockModules/mock-department/mock-department.module.ts delete mode 100644 src/mockModules/mock-department/mock-department.service.ts delete mode 100644 src/survey-cycle-parameter/dto/create-survey-cycle-parameter.dto.ts delete mode 100644 src/survey-cycle-parameter/dto/response-survey-cycle-parameter.dto.ts delete mode 100644 src/survey-cycle-parameter/dto/update-survey-cycle-parameter.dto.ts delete mode 100644 src/survey-cycle-parameter/survey-cycle-parameter.controller.spec.ts delete mode 100644 src/survey-cycle-parameter/survey-cycle-parameter.controller.ts delete mode 100644 src/survey-cycle-parameter/survey-cycle-parameter.module.ts delete mode 100644 src/survey-cycle-parameter/survey-cycle-parameter.service.ts diff --git a/src/admin-department/admin-department.controller.spec.ts b/src/admin-department/admin-department.controller.spec.ts deleted file mode 100644 index c4dbe0b..0000000 --- a/src/admin-department/admin-department.controller.spec.ts +++ /dev/null @@ -1,62 +0,0 @@ -import * as pactum from "pactum"; -import { PrismaService } from "../prisma/prisma.service"; -import { Test, TestingModule } from "@nestjs/testing"; -import { ConfigService } from "@nestjs/config"; -import { INestApplication, ValidationPipe } from "@nestjs/common"; -import { AdminDepartmentModule } from "./admin-department.module"; - -describe("AdminDepartment e2e", () => { - let app: INestApplication; - let prisma: PrismaService; - let module: TestingModule; - beforeAll(async () => { - module = await Test.createTestingModule({ - imports: [AdminDepartmentModule], - }).compile(); - app = module.createNestApplication(); - app.useGlobalPipes( - new ValidationPipe({ - whitelist: true, - }) - ); - const configService = module.get(ConfigService); - const apiPrefix = configService.get("API_PREFIX") || "api"; - const PORT = configService.get("APP_PORT") || 4010; - app.setGlobalPrefix(apiPrefix); - - await app.init(); - await app.listen(PORT); - - prisma = app.get(PrismaService); - pactum.request.setBaseUrl(`http://localhost:${PORT}/${apiPrefix}`); - }); - - afterAll(async () => { - await app.close(); - }); - - const testData = { - departmentId : 1 - } - - describe("AdminDepartmentController createOrUpdateAdminDepartment()", () => { - it("should return created or updated admin department", async function () { - const response = await pactum - .spec() - .post(`/admin-department/${testData.departmentId}`) - .withPathParams({ departmentId: testData.departmentId }) - .expectStatus(201) - .expectBodyContains(testData.departmentId); - }); - }); - - describe("AdminDepartmentController getAllAdminDepartment()", () => { - it("should return all admin departments", async function () { - const response = await pactum - .spec() - .get("/admin-department/") - .expectStatus(200) - .expectBodyContains(testData.departmentId); - }); - }); -}); diff --git a/src/admin-department/admin-department.controller.ts b/src/admin-department/admin-department.controller.ts deleted file mode 100644 index 0f6cb1d..0000000 --- a/src/admin-department/admin-department.controller.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { - Controller, - Get, - HttpStatus, - Logger, - Param, - ParseIntPipe, - Post, - Query, - Res -} from "@nestjs/common"; -import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; -import { getPrismaErrorStatusAndMessage } from "../utils/utils"; -import { AdminDepartmentService } from "./admin-department.service"; -import { FilterAdminDepartmentsDto } from "./dto/create-admin-department.dto"; -import { ResponseAdminDepartmentDto } from "./dto/response-admin-department.dto"; - -@Controller("admin-department") -@ApiTags("admin-department") -export class AdminDepartmentController { - // Create a logger instance for this controller to log events and errors. - private readonly logger = new Logger(AdminDepartmentController.name); - - // The Admin department controller class, injecting the Admin department service. - constructor( - private readonly adminDepartmentService: AdminDepartmentService - ) {} - - // Api to sync department with user org - @Post("sync-department-data") - @ApiOperation({ summary: "sync department with user org" }) // Api operation for swagger - @ApiResponse({ status: HttpStatus.CREATED, type: ResponseAdminDepartmentDto }) // Api response for the swagger - async syncDepartmentData(@Res() res): Promise { - try { - // Log the initiation for the admin department creation - this.logger.log(`Initiate to sync department with user org`); - - const createdOrUpdatedAdminDepartment = - await this.adminDepartmentService.syncDepartmentData(); - // Log the successful creation of the admin department - this.logger.log(`Successfully created admin department.`); - return res.status(HttpStatus.CREATED).json({ - message: "Successfully created admin department.", - data: createdOrUpdatedAdminDepartment, - }); - } catch (error) { - this.logger.error( - `Failed to Successfully created admin department.`, - error - ); - // get error message and status code - const { errorMessage, statusCode } = - getPrismaErrorStatusAndMessage(error); - // Return an error response - return res.status(statusCode).json({ - statusCode, - message: - errorMessage || `Failed to Successfully created admin department.`, - }); - } - } - - // Api for creating or updating a admin department using departmentId - @Post(":departmentId") - @ApiOperation({ summary: "Create or update admin department" }) // Api operation for swagger - @ApiResponse({ status: HttpStatus.CREATED, type: ResponseAdminDepartmentDto }) // Api response for the swagger - async createOrUpdateAdminDepartment( - @Res() res, - @Param("departmentId", ParseIntPipe) departmentId: number - ): Promise { - try { - // Log the initiation for the admin department creation - this.logger.log(`Initiate to create a admin department`); - - const createdOrUpdatedAdminDepartment = - await this.adminDepartmentService.createOrUpdateAdminDepartment( - departmentId - ); - // Log the successful creation of the admin department - this.logger.log(`Successfully created admin department.`); - return res.status(HttpStatus.CREATED).json({ - message: "Admin department created or updated sucessfully.", - data: createdOrUpdatedAdminDepartment, - }); - } catch (error) { - this.logger.error(`Failed to create or update admin department.`, error); - // get error message and status code - const { errorMessage, statusCode } = - getPrismaErrorStatusAndMessage(error); - // Return an error response - return res.status(statusCode).json({ - statusCode, - message: errorMessage || `Failed to create or update admin department.`, - }); - } - } - - // Api for getting all admin department - @Get() - @ApiOperation({ summary: "Fetch all admin departments." }) - @ApiResponse({ - status: HttpStatus.OK, - type: ResponseAdminDepartmentDto, - isArray: true, - }) - async getAllAdminDepartment( - @Res() res, - @Query() filter: FilterAdminDepartmentsDto - ): Promise { - try { - // Log the initiation for fetching all the admin department - this.logger.log(`Initiated fetching all the admin department.`); - const adminDepartments = - await this.adminDepartmentService.getAllAdminDepartment(filter); - - // Log the successful fetching of data from db. - this.logger.log(`Successfully fetched all admin department.`); - - // return the successful response for fetching the data from the db. - return res.status(HttpStatus.OK).json({ - message: "Successfully fetched all admin department.", - data: adminDepartments, - }); - } catch (error) { - // log the error message - this.logger.error(`Failed to get all admin department.`, error); - // get error message and status code - const { errorMessage, statusCode } = - getPrismaErrorStatusAndMessage(error); - // Return an error response - return res.status(statusCode).json({ - statusCode, - message: errorMessage || `Failed to get all admin department.`, - }); - } - } -} diff --git a/src/admin-department/admin-department.module.ts b/src/admin-department/admin-department.module.ts deleted file mode 100644 index b8c7f86..0000000 --- a/src/admin-department/admin-department.module.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Module } from "@nestjs/common"; -import { MockDepartmentService } from "../mockModules/mock-department/mock-department.service"; -import { PrismaModule } from "../prisma/prisma.module"; -import { AdminDepartmentController } from "./admin-department.controller"; -import { AdminDepartmentService } from "./admin-department.service"; - -@Module({ - // Import the PrismaModule to make Prisma service available within this module - imports: [PrismaModule], - - // Declare the AdminDepartmentController as a controller for this module - controllers: [AdminDepartmentController], - - // Declare the AdminDepartmentService as providers (singleton) for this module - providers: [AdminDepartmentService, MockDepartmentService], - - // Export the AdminDepartmentService to make it available for other modules that import this module - exports: [AdminDepartmentService], -}) -export class AdminDepartmentModule {} diff --git a/src/admin-department/admin-department.service.ts b/src/admin-department/admin-department.service.ts deleted file mode 100644 index 5da7e7b..0000000 --- a/src/admin-department/admin-department.service.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { Injectable, NotFoundException } from "@nestjs/common"; -import _ from "lodash"; -import { MockDepartmentService } from "../mockModules/mock-department/mock-department.service"; -import { PrismaService } from "../prisma/prisma.service"; -import { FilterAdminDepartmentsDto } from "./dto/create-admin-department.dto"; - -@Injectable() -export class AdminDepartmentService { - constructor( - private prisma: PrismaService, - private departmentService: MockDepartmentService - ) {} - - public async syncDepartmentData() { - const departments = await this.departmentService.findAll(); - - const response = await Promise.all( - _.map(departments, async (department) => { - const { description, id, name } = department; - const adminDepartmentPayload = { - id, - departmentId: id, - name, - description, - }; - - return await this.prisma.adminDepartment.upsert({ - where: { departmentId: adminDepartmentPayload.departmentId }, - update: adminDepartmentPayload, - create: adminDepartmentPayload, - }); - }) - ); - - return response; - } - - public async createOrUpdateAdminDepartment(id: number) { - // Check the admin department exist for admin department db or not - const checkExistingAdminDepartment = - await this.prisma.adminDepartment.findUnique({ - where: { - departmentId: id, - }, - }); - // Check if the department exist in mock department module or not - const adminDepartment = await this.departmentService.findOne(id); - // If department does not exist in the department module then throw an error - if (!adminDepartment) { - throw new NotFoundException( - `The Mock Admin Department for the #${id} not found.` - ); - } - // If it is existing admin department then update the admin department - if (checkExistingAdminDepartment) { - const updatedAdminDepartment = await this.prisma.adminDepartment.update({ - where: { - departmentId: id, - }, - data: { - name: adminDepartment.name, - description: adminDepartment.description, - }, - }); - return updatedAdminDepartment; - } - return this.prisma.adminDepartment.create({ - data: { - departmentId: adminDepartment.id, - name: adminDepartment.name, - description: adminDepartment.description, - }, - }); - } - - public async getAllAdminDepartment(filter: FilterAdminDepartmentsDto) { - const { departmentId, name, limit = 10, offset = 0, surveyConfig } = filter; - // Get all the admin department and filter using department Id and name. - return await this.prisma.adminDepartment.findMany({ - where: { - departmentId: departmentId ?? undefined, // Optional departmentId filter - name: name ?? undefined, // Optional filter by Name - }, - skip: offset, - take: limit, - select: { - id: true, - departmentId: true, - name: true, - description: true, - SurveyConfig: - surveyConfig == true - ? { - select: { - id: true, - startTime: true, - endTime: true, - onboardingTime: true, - onboardingTimeUnit: true, - }, - } - : {}, - }, - }); - } -} diff --git a/src/admin-department/dto/create-admin-department.dto.ts b/src/admin-department/dto/create-admin-department.dto.ts deleted file mode 100644 index aec4383..0000000 --- a/src/admin-department/dto/create-admin-department.dto.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { IsBoolean, IsInt, IsOptional, IsString } from "class-validator"; - -// Admin department filter dto -export class FilterAdminDepartmentsDto { - // Optional Department id to be filtered for admin departments - @IsOptional() - @IsInt() - departmentId?: number; - - // Optional Name of the admin department to be searched - @IsOptional() - @IsString() - name?: string; - - // Optional limit for pagination, validate that it's an integer. - @IsOptional() - @IsInt() - limit?: number = 10; - - // Optional offset for pagination, validate that it's an integer. - @IsOptional() - @IsInt() - offset?: number = 0; - - @IsOptional() - @IsBoolean() - surveyConfig?: boolean -} diff --git a/src/admin-department/dto/response-admin-department.dto.ts b/src/admin-department/dto/response-admin-department.dto.ts deleted file mode 100644 index 38ecadd..0000000 --- a/src/admin-department/dto/response-admin-department.dto.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { ResponseSurveyConfigDto } from "../../survey-config/dto/response-survey-config.dto"; - -export class ResponseAdminDepartmentDto { - readonly id: number; - readonly name: string; - readonly departmentId: number; - readonly description?: string; - readonly surveyConfig?: ResponseSurveyConfigDto -} diff --git a/src/admin-department/dto/update-admin-department.dto.ts b/src/admin-department/dto/update-admin-department.dto.ts deleted file mode 100644 index a245f1e..0000000 --- a/src/admin-department/dto/update-admin-department.dto.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { IsNotEmpty, IsOptional, IsString } from "class-validator"; - -export class UpdateAdminDepartmentDto { - // Optional Name field to update the name of the admin department - @IsNotEmpty() - @IsString() - name: string; - - @IsNotEmpty() - @IsString() - description: string; -} diff --git a/src/app.module.ts b/src/app.module.ts index 44ea67b..e10e916 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -10,8 +10,6 @@ import { MockFracModule } from "./mockModules/mock.module"; import { SurveyScoreModule } from "./survey-score/survey-score.module"; import { QuestionBankModule } from "./question-bank/question-bank.module"; import { SurveyConfigModule } from "./survey-config/survey-config.module"; -import { SurveyCycleParameterModule } from "./survey-cycle-parameter/survey-cycle-parameter.module"; -import { AdminDepartmentModule } from './admin-department/admin-department.module'; import { UserMetadataModule } from './user-metadata/user-metadata.module'; import { AdminCompetencyModule } from "./admin-competency/admin-competency.module"; import { ScheduledTasksModule } from './scheduled-tasks/scheduled-tasks.module'; @@ -31,8 +29,6 @@ import { FileUploadModule } from './file-upload/file-upload.module'; SurveyScoreModule, QuestionBankModule, SurveyConfigModule, - SurveyCycleParameterModule, - AdminDepartmentModule, UserMetadataModule, AdminCompetencyModule, ScheduledTasksModule, diff --git a/src/mockModules/mock-department/dto/create-mock-department.dto.ts b/src/mockModules/mock-department/dto/create-mock-department.dto.ts deleted file mode 100644 index 45172ee..0000000 --- a/src/mockModules/mock-department/dto/create-mock-department.dto.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { IsNotEmpty, IsOptional, IsString } from "class-validator"; - -export class CreateDepartmentDto { - @IsString() - @IsNotEmpty() - name: string; - - @IsOptional() - @IsString() - @IsNotEmpty() - description?: string; -} diff --git a/src/mockModules/mock-department/dto/response-mock-department.dto.ts b/src/mockModules/mock-department/dto/response-mock-department.dto.ts deleted file mode 100644 index 9afdfcd..0000000 --- a/src/mockModules/mock-department/dto/response-mock-department.dto.ts +++ /dev/null @@ -1,5 +0,0 @@ -export class ResponseDepartmentDto { - readonly id: number; - readonly name: string; - readonly description?: string; -} diff --git a/src/mockModules/mock-department/dto/update-mock-department.dto.ts b/src/mockModules/mock-department/dto/update-mock-department.dto.ts deleted file mode 100644 index 9210fec..0000000 --- a/src/mockModules/mock-department/dto/update-mock-department.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { PartialType } from '@nestjs/mapped-types'; -import { CreateDepartmentDto } from './create-mock-department.dto'; - -export class UpdateDepartmentDto extends PartialType(CreateDepartmentDto) {} diff --git a/src/mockModules/mock-department/mock-department.controller.ts b/src/mockModules/mock-department/mock-department.controller.ts deleted file mode 100644 index 4cb10fb..0000000 --- a/src/mockModules/mock-department/mock-department.controller.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { - Controller, - Get, - Post, - Body, - Patch, - Param, - Delete, - Res, - HttpStatus, - ParseIntPipe, -} from "@nestjs/common"; -import { MockDepartmentService } from "./mock-department.service"; -import { CreateDepartmentDto } from "./dto/create-mock-department.dto"; -import { UpdateDepartmentDto } from "./dto/update-mock-department.dto"; -import { ApiTags } from "@nestjs/swagger"; - -@Controller("department") -@ApiTags("mockFracService/department") -export class MockDepartmentController { - constructor(private readonly mockDepartmentService: MockDepartmentService) {} - - @Post() - async create(@Body() createDepartmentDto: CreateDepartmentDto, @Res() res) { - try { - const department = await this.mockDepartmentService.create(createDepartmentDto); - return res - .status(HttpStatus.OK) - .json({ data: department, message: "Successfully done" }); - } catch (error) { - return res - .status(HttpStatus.INTERNAL_SERVER_ERROR) - .json({ message: error.meta.cause }); - } - } - - @Get() - async findAll(@Res() res) { - try { - const department = await this.mockDepartmentService.findAll(); - return res - .status(HttpStatus.OK) - .json({ data: department, message: "Successfully done" }); - } catch (error) { - return res - .status(HttpStatus.INTERNAL_SERVER_ERROR) - .json({ message: error.meta.cause }); - } - } - - @Get(":id") - async findOne(@Param("id", ParseIntPipe) id: number, @Res() res) { - try { - const department = await this.mockDepartmentService.findOne(id); - return res - .status(HttpStatus.OK) - .json({ data: department, message: "Successfully done" }); - } catch (error) { - - return res - .status(HttpStatus.INTERNAL_SERVER_ERROR) - .json({ message: error.message }); - } - } - - @Patch(":id") - async update( - @Param("id", ParseIntPipe) id: number, - @Body() updateDepartmentDto: UpdateDepartmentDto, - @Res() res - ) { - try { - const department = await this.mockDepartmentService.update(id, updateDepartmentDto); - return res - .status(HttpStatus.OK) - .json({ data: department, message: "Successfully done" }); - } catch (error) { - return res - .status(HttpStatus.INTERNAL_SERVER_ERROR) - .json({ message: error.meta.cause }); - } - } - - @Delete(":id") - async remove(@Param("id", ParseIntPipe) id: number, @Res() res) { - try { - const department = await this.mockDepartmentService.remove(id); - return res - .status(HttpStatus.OK) - .json({ data: department, message: "Successfully done" }); - } catch (error) { - return res - .status(HttpStatus.INTERNAL_SERVER_ERROR) - .json({ message: error.meta.cause }); - } - } -} diff --git a/src/mockModules/mock-department/mock-department.module.ts b/src/mockModules/mock-department/mock-department.module.ts deleted file mode 100644 index 9a7d4f1..0000000 --- a/src/mockModules/mock-department/mock-department.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Module } from "@nestjs/common"; -import { MockDepartmentService } from "./mock-department.service"; -import { MockDepartmentController } from "./mock-department.controller"; - -@Module({ - controllers: [MockDepartmentController], - providers: [MockDepartmentService], -}) -export class MockDepartmentModule {} diff --git a/src/mockModules/mock-department/mock-department.service.ts b/src/mockModules/mock-department/mock-department.service.ts deleted file mode 100644 index b935e88..0000000 --- a/src/mockModules/mock-department/mock-department.service.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Injectable, NotFoundException } from "@nestjs/common"; -import { CreateDepartmentDto } from "./dto/create-mock-department.dto"; -import { UpdateDepartmentDto } from "./dto/update-mock-department.dto"; -import { PrismaService } from "../../prisma/prisma.service"; - -@Injectable() -export class MockDepartmentService { - constructor(private prisma: PrismaService) {} - - public async create(createDepartmentDto: CreateDepartmentDto) { - return await this.prisma.department.create({ - data: createDepartmentDto, - }); - } - - public async findAll() { - return await this.prisma.department.findMany(); - } - - public async findOne(id: number) { - const department = await this.prisma.department.findUnique({ - where: { - id, - }, - }); - if (!department) throw new NotFoundException(`Department with id #${id} not found`); - return department; - } - - public async update(id: number, updateDepartmentDto: UpdateDepartmentDto) { - return await this.prisma.department.update({ - where: { - id, - }, - data: updateDepartmentDto, - }); - } - - public async remove(id: number) { - return await this.prisma.department.delete({ - where: { - id, - }, - }); - } -} diff --git a/src/mockModules/mock-user/dto/create-mock-user.dto.ts b/src/mockModules/mock-user/dto/create-mock-user.dto.ts index 8dab42d..6a230a5 100644 --- a/src/mockModules/mock-user/dto/create-mock-user.dto.ts +++ b/src/mockModules/mock-user/dto/create-mock-user.dto.ts @@ -38,11 +38,6 @@ export class CreateMockUserDto { @IsNumber() levelNumber: number; - @IsOptional() - @IsNotEmpty() - @IsNumber() - departmentId: number; - @IsNotEmpty() @IsString() designation: string; diff --git a/src/mockModules/mock-user/dto/response-mock-user.dto.ts b/src/mockModules/mock-user/dto/response-mock-user.dto.ts index f338731..7497570 100644 --- a/src/mockModules/mock-user/dto/response-mock-user.dto.ts +++ b/src/mockModules/mock-user/dto/response-mock-user.dto.ts @@ -1,4 +1,3 @@ -import { ResponseDepartmentDto } from "src/mockModules/mock-department/dto/response-mock-department.dto"; import { ResponseMockLevelDto } from "src/mockModules/mock-level/dto/response-mock-level.dto"; export class ResponseMockUserDto { @@ -9,8 +8,6 @@ export class ResponseMockUserDto { readonly profilePicture?: string; readonly levelNumber: number; readonly Level: ResponseMockLevelDto; - readonly departmentId?: number; - readonly Department?: ResponseDepartmentDto; readonly designation: String; readonly createdAt?: Date; readonly updatedAt?: Date; diff --git a/src/mockModules/mock-user/mock-user.service.ts b/src/mockModules/mock-user/mock-user.service.ts index a3a0f73..f26405b 100644 --- a/src/mockModules/mock-user/mock-user.service.ts +++ b/src/mockModules/mock-user/mock-user.service.ts @@ -72,12 +72,6 @@ export class MockUserService { levelNumber : true }, }, - Department: { - select: { - id: true, - name: true, - }, - }, createdAt: true, updatedAt: true, }; diff --git a/src/mockModules/mock.module.ts b/src/mockModules/mock.module.ts index f63164c..8cc4e54 100644 --- a/src/mockModules/mock.module.ts +++ b/src/mockModules/mock.module.ts @@ -5,7 +5,6 @@ import { MockCompetencyLevelModule } from "./mock-competency-level/mock-competen import { RouterModule } from "@nestjs/core"; import { MockUserModule } from "./mock-user/mock-user.module"; import { MockLevelModule } from "./mock-level/mock-level.module"; -import { MockDepartmentModule } from "./mock-department/mock-department.module"; import { MockDesignationModule } from './mock-designation/mock-designation.module'; @Module({ @@ -15,7 +14,6 @@ import { MockDesignationModule } from './mock-designation/mock-designation.modul MockCompetencyLevelModule, MockUserModule, MockLevelModule, - MockDepartmentModule, MockDesignationModule, RouterModule.register([ { path: "mockFracService/", module: MockRoleModule }, @@ -23,7 +21,6 @@ import { MockDesignationModule } from './mock-designation/mock-designation.modul { path: "mockFracService/", module: MockCompetencyLevelModule }, { path: "mockFracService/", module: MockUserModule }, { path: "mockFracService/", module: MockLevelModule }, - { path: "mockFracService/", module: MockDepartmentModule }, { path: "mockFracService/", module: MockDesignationModule }, ]), ], diff --git a/src/response-tracker/response-tracker.service.ts b/src/response-tracker/response-tracker.service.ts index cd82dca..358503c 100644 --- a/src/response-tracker/response-tracker.service.ts +++ b/src/response-tracker/response-tracker.service.ts @@ -69,7 +69,7 @@ export class ResponseTrackerService { }, surveyForm: { select: { - surveyCycleParameter: { + SurveyConfig: { select: { endTime: true, }, @@ -101,7 +101,7 @@ export class ResponseTrackerService { }, surveyForm: { select: { - surveyCycleParameter: { + SurveyConfig: { select: { endTime: true, }, @@ -121,7 +121,7 @@ export class ResponseTrackerService { public async getResponsesActiveSurveyByUserId(userId: string) { const surveyForms = await this.prisma.surveyForm.findMany({ where: { - surveyCycleParameter: { + SurveyConfig: { isActive: true, }, userId, @@ -259,7 +259,7 @@ export class ResponseTrackerService { assessorId, }, surveyForm: { - surveyCycleParameter: { + SurveyConfig: { endTime: { gte: today, }, diff --git a/src/scheduled-tasks/scheduled-tasks.module.ts b/src/scheduled-tasks/scheduled-tasks.module.ts index 71cc008..59ec0a9 100644 --- a/src/scheduled-tasks/scheduled-tasks.module.ts +++ b/src/scheduled-tasks/scheduled-tasks.module.ts @@ -1,9 +1,7 @@ import { Module } from "@nestjs/common"; -import { AdminDepartmentModule } from "../admin-department/admin-department.module"; import { QuestionBankModule } from "../question-bank/question-bank.module"; import { ResponseTrackerService } from "../response-tracker/response-tracker.service"; -import { SurveyConfigService } from "../survey-config/survey-config.service"; -import { SurveyCycleParameterService } from "../survey-cycle-parameter/survey-cycle-parameter.service"; +import { SurveyConfigModule } from "../survey-config/survey-config.module"; import { SurveyFormService } from "../survey-form/survey-form.service"; import { SurveyScoreService } from "../survey-score/survey-score.service"; import { SurveyService } from "../survey/survey.service"; @@ -11,13 +9,11 @@ import { UserMetadataModule } from "../user-metadata/user-metadata.module"; import { ScheduledTasksService } from "./scheduled-tasks.service"; @Module({ - imports: [QuestionBankModule, UserMetadataModule, AdminDepartmentModule], + imports: [QuestionBankModule, UserMetadataModule, SurveyConfigModule], providers: [ ScheduledTasksService, SurveyFormService, - SurveyConfigService, SurveyService, - SurveyCycleParameterService, SurveyScoreService, ResponseTrackerService, ], diff --git a/src/survey-cycle-parameter/dto/create-survey-cycle-parameter.dto.ts b/src/survey-cycle-parameter/dto/create-survey-cycle-parameter.dto.ts deleted file mode 100644 index 655d97e..0000000 --- a/src/survey-cycle-parameter/dto/create-survey-cycle-parameter.dto.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Type } from "class-transformer"; -import { - IsBoolean, - IsDate, - IsInt, - IsNotEmpty, - IsNumber, - IsOptional, -} from "class-validator"; - -export class CreateSurveyCycleParameterDto { - @IsNotEmpty() - @IsDate() - @Type(() => Date) - startTime: Date; - - @IsNotEmpty() - @IsDate() - @Type(() => Date) - endTime: Date; - - @IsNotEmpty() - @IsBoolean() - isActive: boolean; - - @IsOptional() - @IsNotEmpty() - @IsInt() - surveyConfigId?: number; - - @IsOptional() - @IsNotEmpty() - @IsDate() - createdAt?: Date = new Date(); - - @IsOptional() - @IsNotEmpty() - @IsDate() - updatedAt?: Date; -} - -export class SurveyCycleParameterFilterDto { - @IsNumber() - @IsOptional() - @IsNotEmpty() - departmentId?: number; - - @IsNumber() - @IsOptional() - @IsNotEmpty() - surveyConfigId?: number; - - @IsBoolean() - @IsOptional() - @IsNotEmpty() - isActive? : boolean; -} diff --git a/src/survey-cycle-parameter/dto/response-survey-cycle-parameter.dto.ts b/src/survey-cycle-parameter/dto/response-survey-cycle-parameter.dto.ts deleted file mode 100644 index d1ba804..0000000 --- a/src/survey-cycle-parameter/dto/response-survey-cycle-parameter.dto.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { TimeUnitsEnum } from "@prisma/client"; - -export class ResponseSurveyCycleParameterDto { - readonly id: number; - readonly startTime: number; - readonly endTime: TimeUnitsEnum; - readonly surveyConfigId: number; - readonly isActive: boolean; - readonly createdAt: Date; - readonly updatedAt: Date; -} diff --git a/src/survey-cycle-parameter/dto/update-survey-cycle-parameter.dto.ts b/src/survey-cycle-parameter/dto/update-survey-cycle-parameter.dto.ts deleted file mode 100644 index a59ac75..0000000 --- a/src/survey-cycle-parameter/dto/update-survey-cycle-parameter.dto.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { PartialType } from "@nestjs/swagger"; -import { CreateSurveyCycleParameterDto } from "./create-survey-cycle-parameter.dto"; - -export class UpdateSurveyCycleParameterDto extends PartialType( - CreateSurveyCycleParameterDto -) {} diff --git a/src/survey-cycle-parameter/survey-cycle-parameter.controller.spec.ts b/src/survey-cycle-parameter/survey-cycle-parameter.controller.spec.ts deleted file mode 100644 index cf88e07..0000000 --- a/src/survey-cycle-parameter/survey-cycle-parameter.controller.spec.ts +++ /dev/null @@ -1,124 +0,0 @@ -import * as pactum from "pactum"; -import { Test, TestingModule } from "@nestjs/testing"; -import { INestApplication, ValidationPipe } from "@nestjs/common"; -import { PrismaService } from "../prisma/prisma.service"; -import { ConfigService } from "@nestjs/config"; -import { CreateSurveyCycleParameterDto } from "./dto/create-survey-cycle-parameter.dto"; -import { UpdateSurveyCycleParameterDto } from "./dto/update-survey-cycle-parameter.dto"; -import { SurveyCycleParameterModule } from "./survey-cycle-parameter.module"; - -describe("SurveyCycleParameterController e2e", () => { - let app: INestApplication; - let prisma: PrismaService; - let module: TestingModule; - beforeAll(async () => { - module = await Test.createTestingModule({ - imports: [SurveyCycleParameterModule], - }).compile(); - - app = module.createNestApplication(); - app.useGlobalPipes( - new ValidationPipe({ - whitelist: true, - }) - ); - - const configService = app.get(ConfigService); - const apiPrefix = configService.get("API_PREFIX") || "api"; - const PORT = configService.get("APP_PORT") || 4010; - app.setGlobalPrefix(apiPrefix); - - await app.init(); - await app.listen(PORT); - - prisma = app.get(PrismaService); - pactum.request.setBaseUrl(`http://localhost:${PORT}/${apiPrefix}`); - }); - afterAll(async () => { - await app.close(); - }); - describe("SurveyCycleParameterController createSurveyParameter()", () => { - it("should return created survey cycle parameter with id ", async function () { - const createSurveyCycleParameterDto: CreateSurveyCycleParameterDto = { - startTime: new Date("2023-10-31"), - endTime: new Date("2024-01-31"), - surveyConfigId: 1, - isActive: true, - }; - - const response = await pactum - .spec() - .post("/survey-cycle-parameter") - .withBody(createSurveyCycleParameterDto) - .expectStatus(201); - - const createdSurveyCycleParameter = JSON.parse( - JSON.stringify(response.body) - ); - expect(createdSurveyCycleParameter.message).toEqual( - "Survey cycle parameter created successfully." - ); - expect(createdSurveyCycleParameter.data).toHaveProperty("id"); - expect(createdSurveyCycleParameter.data.startTime).toBeDefined(); - expect(createdSurveyCycleParameter.data.endTime).toBeDefined(); - expect(createdSurveyCycleParameter.data.isActive).toBeTruthy(); - expect(createdSurveyCycleParameter.data.surveyConfigId).toEqual( - createSurveyCycleParameterDto.surveyConfigId - ); - expect(createdSurveyCycleParameter?.data.createdAt).toBeDefined(); - expect(createdSurveyCycleParameter?.data.updatedAt).toBeDefined(); - }); - }); - - describe("SurveyCycleParameterController getAllSurveyParameter()", () => { - it("should get all survey cycle parameter", async () => { - const response = await pactum - .spec() - .get("/survey-cycle-parameter") - .expectStatus(200); - - const getAllSurveyCycleParameter = JSON.parse( - JSON.stringify(response.body) - ); - expect(getAllSurveyCycleParameter.message).toEqual( - "Successfully fetched all survey cycle parameter." - ); - expect(getAllSurveyCycleParameter.data.length).toBeGreaterThanOrEqual(0); - }); - }); - - describe("SurveyCycleParameterController updateSurveyParameterById()", () => { - it("should update survey cycle parameter by id", async () => { - const testData = { - id: 1, - }; - const updateSurveyCycleParameterDto: UpdateSurveyCycleParameterDto = { - startTime: new Date("2025-10-31"), - endTime: new Date("2025-11-31"), - isActive: false, - }; - const response = await pactum - .spec() - .patch(`/survey-cycle-parameter/update/${testData.id}`) - .withPathParams({ - id: testData.id, - }) - .withBody(updateSurveyCycleParameterDto) - .expectStatus(200); - const updatedSurveyCycleParameter = JSON.parse( - JSON.stringify(response.body) - ); - expect(updatedSurveyCycleParameter.message).toEqual( - `Successfully updated survey cycle parameter for id #${testData.id}` - ); - expect(updatedSurveyCycleParameter.data).toHaveProperty("id"); - expect(updatedSurveyCycleParameter.data.startTime).toBeDefined(); - expect(updatedSurveyCycleParameter.data.endTime).toBeDefined(); - expect(updatedSurveyCycleParameter.data.isActive).toEqual( - updateSurveyCycleParameterDto.isActive - ); - expect(updatedSurveyCycleParameter?.data.createdAt).toBeDefined(); - expect(updatedSurveyCycleParameter?.data.updatedAt).toBeDefined(); - }); - }); -}); diff --git a/src/survey-cycle-parameter/survey-cycle-parameter.controller.ts b/src/survey-cycle-parameter/survey-cycle-parameter.controller.ts deleted file mode 100644 index 69e71c6..0000000 --- a/src/survey-cycle-parameter/survey-cycle-parameter.controller.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { - Body, - Controller, - Get, - HttpStatus, - Logger, - Param, - ParseIntPipe, - Patch, - Post, - Query, - Res, -} from "@nestjs/common"; -import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; -import { getPrismaErrorStatusAndMessage } from "../utils/utils"; - -import { - CreateSurveyCycleParameterDto, - SurveyCycleParameterFilterDto, -} from "./dto/create-survey-cycle-parameter.dto"; -import { ResponseSurveyCycleParameterDto } from "./dto/response-survey-cycle-parameter.dto"; -import { UpdateSurveyCycleParameterDto } from "./dto/update-survey-cycle-parameter.dto"; -import { SurveyCycleParameterService } from "./survey-cycle-parameter.service"; - -@ApiTags("survey-cycle-parameter") -@Controller("survey-cycle-parameter") -export class SurveyCycleParameterController { - // Create a logger instance for this log events and errors - private readonly logger = new Logger(SurveyCycleParameterController.name); - - // The survey parameter controller class, injecting the survey parameter service. - constructor( - private readonly surveyParameterService: SurveyCycleParameterService - ) {} - - // Api to create a new survey parameter - @Post() - @ApiOperation({ summary: "Create a Survey cycle parameter" }) // Api operation for swagger - @ApiResponse({ - status: HttpStatus.CREATED, - type: ResponseSurveyCycleParameterDto, - }) // Api response for swagger - async createSurveyParameter( - @Res() res, - @Body() createSurveyCycleParameterDto: CreateSurveyCycleParameterDto - ): Promise { - try { - // Log the initiation for the Survey Parameter creation - this.logger.log(`Initiate to create a Survey cycle Parameter`); - - const createdSurveyParameter = - await this.surveyParameterService.createSurveyParameter( - createSurveyCycleParameterDto - ); - // Log the successful creation of the survey parameter - this.logger.log(`Successfully Created Survey cycle Parameter`); - // Return a success response with the created survey parameter. - return res.status(HttpStatus.CREATED).json({ - message: "Survey cycle parameter created successfully.", - data: createdSurveyParameter, - }); - } catch (error) { - this.logger.error(`Failed to create new survey cycle Parameter.`, error); - // get error message and status code - const { errorMessage, statusCode } = - getPrismaErrorStatusAndMessage(error); - // Return an error response - return res.status(statusCode).json({ - statusCode, - message: errorMessage || `Failed to create new survey cycle Parameter.`, - }); - } - } - - // Api to get all the survey parameter - @Get() - @ApiOperation({ summary: "Find All Survey cycle parameters" }) // Api operation for swagger - @ApiResponse({ - status: HttpStatus.OK, - type: ResponseSurveyCycleParameterDto, - isArray: true, - }) // Api response for swagger - async getAllSurveyParameter( - @Res() res, - @Query() filter: SurveyCycleParameterFilterDto - ): Promise { - try { - this.logger.log(`Initiated fetching all the survey cycle parameter.`); - const surveyParameters = - await this.surveyParameterService.getAllSurveyParameter(filter); - - if (!surveyParameters.length) throw Error("No record found"); - this.logger.log(`Fetched successfully all the survey cycle parameter.`); - - return res.status(HttpStatus.OK).json({ - message: "Successfully fetched all survey cycle parameter.", - data: surveyParameters, - }); - } catch (error) { - this.logger.error(`Failed to fetch survey cycle parameters`, error); - const { errorMessage, statusCode } = - getPrismaErrorStatusAndMessage(error); // get error message and status code - // Return an error response - return res.status(statusCode).json({ - statusCode, - message: errorMessage || `Failed to Get all survey cycle parameters.`, - }); - } - } - - // Api to update the survey parameter by using the Id. - @Patch("update/:id") - @ApiOperation({ summary: "Update the survey cycle parameter by Id." }) - @ApiResponse({ - status: HttpStatus.OK, - type: ResponseSurveyCycleParameterDto, - }) - async updateSurveyParameterById( - @Res() res, - @Param("id", ParseIntPipe) id: number, - @Body() updateSurveyCycleParameterDto: UpdateSurveyCycleParameterDto - ): Promise { - try { - this.logger.log( - `Initiated updating the survey cycle parameter for id #${id}` - ); - let updatedSurveyParameter = - await this.surveyParameterService.updateSurveyParameterById( - id, - updateSurveyCycleParameterDto - ); - this.logger.log( - `Successfully updated survey cycle parameter for id #${id}` - ); - return res.status(HttpStatus.OK).json({ - message: `Successfully updated survey cycle parameter for id #${id}`, - data: updatedSurveyParameter, - }); - } catch (error) { - this.logger.error( - `Failed to update survey cycle parameter for id #${id}`, - error - ); - const { errorMessage, statusCode } = - getPrismaErrorStatusAndMessage(error); // get error message and status code - // Return an error response - return res.status(statusCode).json({ - statusCode, - message: - errorMessage || - `Failed to update survey cycle parameter for id #${id}`, - }); - } - } -} diff --git a/src/survey-cycle-parameter/survey-cycle-parameter.module.ts b/src/survey-cycle-parameter/survey-cycle-parameter.module.ts deleted file mode 100644 index 1682993..0000000 --- a/src/survey-cycle-parameter/survey-cycle-parameter.module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Module } from "@nestjs/common"; -import { PrismaModule } from "../prisma/prisma.module"; -import { SurveyCycleParameterController } from "./survey-cycle-parameter.controller"; -import { SurveyCycleParameterService } from "./survey-cycle-parameter.service"; - -@Module({ - imports: [PrismaModule], - controllers: [SurveyCycleParameterController], - providers: [SurveyCycleParameterService], - exports: [SurveyCycleParameterService], -}) -export class SurveyCycleParameterModule {} diff --git a/src/survey-cycle-parameter/survey-cycle-parameter.service.ts b/src/survey-cycle-parameter/survey-cycle-parameter.service.ts deleted file mode 100644 index 74bed9f..0000000 --- a/src/survey-cycle-parameter/survey-cycle-parameter.service.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { - ConflictException, - Injectable, - NotFoundException, -} from "@nestjs/common"; -import { PrismaService } from "../prisma/prisma.service"; - -import { - CreateSurveyCycleParameterDto, - SurveyCycleParameterFilterDto, -} from "./dto/create-survey-cycle-parameter.dto"; -import { UpdateSurveyCycleParameterDto } from "./dto/update-survey-cycle-parameter.dto"; - -@Injectable() -export class SurveyCycleParameterService { - constructor(private prisma: PrismaService) {} - - // To check if the survey parameter exist in database or not - async checkIfSurveyParameterExists() { - return this.prisma.surveyCycleParameter.findFirst(); - } - - async createSurveyParameter( - createSurveyCycleParameterDto: CreateSurveyCycleParameterDto - ) { - const existingSurveyParameter = await this.checkIfSurveyParameterExists(); - - if (existingSurveyParameter) { - throw new ConflictException("Survey parameter already exists."); - } - - // create a new survey parameter - const newSurveyParameter = await this.prisma.surveyCycleParameter.create({ - data: createSurveyCycleParameterDto, - }); - return newSurveyParameter; - } - - async getAllSurveyParameter(filter: SurveyCycleParameterFilterDto) { - const { departmentId, surveyConfigId, isActive } = filter; - return this.prisma.surveyCycleParameter.findMany({ - where: { - isActive: isActive ?? undefined, - SurveyConfig: { - id: surveyConfigId ?? undefined, - AdminDepartment: { - id: departmentId ?? undefined, - }, - }, - }, - orderBy: { - createdAt: "desc", - }, - }); - } - - async updateSurveyParameterById( - id: number, - updateSurveyCycleParameter: UpdateSurveyCycleParameterDto - ) { - // check if for the given id, data is available in db or not - const findSurveyParameterId = - await this.prisma.surveyCycleParameter.findUnique({ - where: { - id, - }, - }); - - if (!findSurveyParameterId) { - throw new NotFoundException(`Survey parameter with ID ${id} not found.`); - } - // update an existing survey parameter by its unique identifier (id). - return this.prisma.surveyCycleParameter.update({ - where: { - id, - }, - data: updateSurveyCycleParameter, - }); - } -} diff --git a/src/user-metadata/dto/user-metadata-filter.dto.ts b/src/user-metadata/dto/user-metadata-filter.dto.ts index 78e2270..f81af00 100644 --- a/src/user-metadata/dto/user-metadata-filter.dto.ts +++ b/src/user-metadata/dto/user-metadata-filter.dto.ts @@ -12,11 +12,6 @@ export class UserMetadataFilterDto { @IsNotEmpty() isNewEmployee?: boolean; - @IsInt() - @IsOptional() - @IsNotEmpty() - departmentId?: number; - @IsString() @IsOptional() @IsNotEmpty() diff --git a/src/user-metadata/user-metadata.module.ts b/src/user-metadata/user-metadata.module.ts index c88d10a..f9f7f15 100644 --- a/src/user-metadata/user-metadata.module.ts +++ b/src/user-metadata/user-metadata.module.ts @@ -1,15 +1,13 @@ import { Module } from "@nestjs/common"; +import { MockUserService } from "src/mockModules/mock-user/mock-user.service"; +import { SurveyConfigModule } from "../survey-config/survey-config.module"; import { UserMetadataController } from "./user-metadata.controller"; import { UserMetadataService } from "./user-metadata.service"; -import { SurveyConfigService } from "../survey-config/survey-config.service"; -import { MockUserService } from "../mockModules/mock-user/mock-user.service"; -import { PrismaModule } from "../prisma/prisma.module"; -import { AdminDepartmentModule } from "../admin-department/admin-department.module"; @Module({ - imports: [AdminDepartmentModule,PrismaModule], + imports:[SurveyConfigModule], controllers: [UserMetadataController], - providers: [UserMetadataService, SurveyConfigService, MockUserService], + providers: [UserMetadataService, MockUserService], exports: [UserMetadataService], }) export class UserMetadataModule {} From fae5f04aa72bea7982295773c4798eb773f0d317 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Tue, 14 Nov 2023 10:47:46 +0530 Subject: [PATCH 116/175] Updating seed data. --- prisma/seeds/data/mock-departments.data.ts | 27 ---------------------- prisma/seeds/data/mock-users.data.ts | 20 ---------------- prisma/seeds/seed.ts | 5 ---- 3 files changed, 52 deletions(-) delete mode 100644 prisma/seeds/data/mock-departments.data.ts diff --git a/prisma/seeds/data/mock-departments.data.ts b/prisma/seeds/data/mock-departments.data.ts deleted file mode 100644 index c0f2673..0000000 --- a/prisma/seeds/data/mock-departments.data.ts +++ /dev/null @@ -1,27 +0,0 @@ -export const departments = [ - { - id: 1, - name: "Software Development", - description: "Responsible for software development and coding.", - }, - { - id: 2, - name: "IT Operations", - description: "Manages IT infrastructure and operations.", - }, - { - id: 3, - name: "Quality Assurance", - description: "Ensures the quality of software products.", - }, - { - id: 4, - name: "Network and Security", - description: "Handles network administration and cybersecurity.", - }, - { - id: 5, - name: "Data Analytics", - description: "Analyzes and extracts insights from data.", - }, -]; diff --git a/prisma/seeds/data/mock-users.data.ts b/prisma/seeds/data/mock-users.data.ts index 4e38fca..4d87ef8 100644 --- a/prisma/seeds/data/mock-users.data.ts +++ b/prisma/seeds/data/mock-users.data.ts @@ -8,7 +8,6 @@ export const users = [ userName: "Amit Sharma", password: "P@ssw0rd1", designation: "CEO", - departmentId: 1, levelNumber: 4, // Assuming Amit Sharma has a competency level of "Strategic Leadership" }, { @@ -18,7 +17,6 @@ export const users = [ userName: "Neha Patil", password: "P@ssw0rd2", designation: "Software Engineer", - departmentId: 1, levelNumber: 2, // competency level for Neha Patil }, { @@ -28,7 +26,6 @@ export const users = [ userName: "Rajesh Kumar", password: "P@ssw0rd3", designation: "Software Engineer", - departmentId: 1, levelNumber: 3, // competency level for Rajesh Kumar }, { @@ -38,7 +35,6 @@ export const users = [ userName: "Priya Jain", password: "P@ssw0rd4", designation: "QA Engineer", - departmentId: 3, levelNumber: 2, // competency level for Priya Jain }, { @@ -48,7 +44,6 @@ export const users = [ userName: "Arjun Singh", password: "P@ssw0rd5", designation: "QA Engineer", - departmentId: 3, levelNumber: 3, // competency level for Arjun Singh }, { @@ -58,7 +53,6 @@ export const users = [ userName: "Deepa Mishra", password: "P@ssw0rd6", designation: "Software Architect", - departmentId: 2, levelNumber: 4, // competency level for Deepa Mishra }, { @@ -68,7 +62,6 @@ export const users = [ userName: "Rahul Verma", password: "P@ssw0rd7", designation: "Software Architect", - departmentId: 2, levelNumber: 3, // competency level for Rahul Verma }, { @@ -78,7 +71,6 @@ export const users = [ userName: "Anita Sen", password: "P@ssw0rd8", designation: "Software Engineer", - departmentId: 2, levelNumber: 2, // competency level for Anita Sen }, { @@ -88,7 +80,6 @@ export const users = [ userName: "Mohit Gupta", password: "P@ssw0rd9", designation: "Software Engineer", - departmentId: 2, levelNumber: 3, // competency level for Mohit Gupta }, { @@ -98,7 +89,6 @@ export const users = [ userName: "Pooja Rawat", password: "P@ssw0rd10", designation: "Software Engineer", - departmentId: 3, levelNumber: 3, // competency level for Pooja Rawat }, { @@ -108,7 +98,6 @@ export const users = [ userName: "Anurag Sharma", password: "P@ssw0rd11", designation: "Software Engineer", - departmentId: 3, levelNumber: 2, // competency level for Anurag Sharma }, { @@ -118,7 +107,6 @@ export const users = [ userName: "Meera Rani", password: "P@ssw0rd12", designation: "Software Engineer", - departmentId: 3, levelNumber: 2, // competency level for Meera Rani }, { @@ -128,7 +116,6 @@ export const users = [ userName: "Vikas Patel", password: "P@ssw0rd13", designation: "Software Engineer", - departmentId: 4, levelNumber: 3, // competency level for Vikas Patel }, { @@ -138,7 +125,6 @@ export const users = [ userName: "Divya Malik", password: "P@ssw0rd14", designation: "Software Engineer", - departmentId: 5, levelNumber: 2, // competency level for Divya Malik }, { @@ -148,7 +134,6 @@ export const users = [ userName: "Rajiv Khanna", password: "P@ssw0rd15", designation: "Software Engineer", - departmentId: 3, levelNumber: 4, // competency level for Rajiv Khanna }, { @@ -158,7 +143,6 @@ export const users = [ userName: "Swati Shah", password: "P@ssw0rd16", designation: "QA Engineer", - departmentId: 3, levelNumber: 3, // competency level for Swati Shah }, { @@ -168,7 +152,6 @@ export const users = [ userName: "Manish Agrawal", password: "P@ssw0rd17", designation: "QA Engineer", - departmentId: 3, levelNumber: 4, // competency level for Manish Agrawal }, { @@ -178,7 +161,6 @@ export const users = [ userName: "Pallavi Reddy", password: "P@ssw0rd18", designation: "QA Engineer", - departmentId: 3, levelNumber: 2, // competency level for Pallavi Reddy }, { @@ -188,7 +170,6 @@ export const users = [ userName: "Deepak Mishra", password: "P@ssw0rd19", designation: "QA Engineer", - departmentId: 3, levelNumber: 4, // competency level for Deepak Mishra }, { @@ -198,7 +179,6 @@ export const users = [ userName: "Komal Choudhary", password: "P@ssw0rd20", designation: "QA Engineer", - departmentId: 3, levelNumber: 2, // competency level for Komal Choudhary }, ]; diff --git a/prisma/seeds/seed.ts b/prisma/seeds/seed.ts index b261cfc..c298087 100644 --- a/prisma/seeds/seed.ts +++ b/prisma/seeds/seed.ts @@ -2,7 +2,6 @@ import { PrismaClient } from "@prisma/client"; import { competencies } from "./data/mock-competencies.data"; import { competenciesToCompetencyLevels } from "./data/mock-competenciesToCompetencyLevels.data"; import { competencyLevels } from "./data/mock-competencyLevels.data"; -import { departments } from "./data/mock-departments.data"; import { designations } from "./data/mock-designation.data"; import { levels } from "./data/mock-levels.data"; import { roles } from "./data/mock-roles.data"; @@ -13,10 +12,6 @@ const prisma = new PrismaClient(); async function seed() { await prisma.$transaction([ - prisma.department.createMany({ - data: departments, - }), - prisma.designation.createMany({ data: designations, }), From 4340c261a343ae62cc7ef0b3a2ec4d4d32487b3e Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Tue, 14 Nov 2023 15:13:51 +0530 Subject: [PATCH 117/175] API for question bank template --- src/config/multer-options.config.ts | 15 ++++++++ src/question-bank/question-bank.controller.ts | 37 ++++++++++++++++++- src/question-bank/question-bank.service.ts | 5 +++ 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/config/multer-options.config.ts b/src/config/multer-options.config.ts index 93d5fb6..442d537 100644 --- a/src/config/multer-options.config.ts +++ b/src/config/multer-options.config.ts @@ -1,11 +1,26 @@ +import { UnsupportedMediaTypeException } from "@nestjs/common"; import { MulterOptions } from "@nestjs/platform-express/multer/interfaces/multer-options.interface"; import { diskStorage } from "multer"; +export const csvFileFilter = ( + req: Request, + file: Express.Multer.File, + callback +) => { + if (!file.originalname.match(/\.(csv)$/)) { + return callback( + new UnsupportedMediaTypeException("Only csv files are allowed!"), + false + ); + } + callback(null, true); +}; export const multerOptions: MulterOptions = { dest: "src/uploads", // this is the destination of the uploaded files on disk limits: { fileSize: 1024 * 1024 * 5, // max size 5MB }, + fileFilter: csvFileFilter, storage: diskStorage({ destination: "src/uploads", filename: (_req, file, cb) => { diff --git a/src/question-bank/question-bank.controller.ts b/src/question-bank/question-bank.controller.ts index 87cf98a..30d4a8b 100644 --- a/src/question-bank/question-bank.controller.ts +++ b/src/question-bank/question-bank.controller.ts @@ -211,7 +211,7 @@ export class QuestionBankController { // Log the initiaton for csv file upload this.logger.log(`Initiate to upload a csv file.`); try { - this.questionBankService.uploadCsvFile(file.path); + await this.questionBankService.uploadCsvFile(file.path); this.logger.log(`Successfully uploaded question bank.`); return res.status(HttpStatus.CREATED).json({ message: "Question bank uploaded sucessfully.", @@ -279,7 +279,9 @@ export class QuestionBankController { // Log the initiaton for csv file upload this.logger.log(`Initiate updating the question bank.`); try { - await this.questionBankService.createUpdateDeleteQuesitons(createUpdateDeleteQuesitonsDto); + await this.questionBankService.createUpdateDeleteQuesitons( + createUpdateDeleteQuesitonsDto + ); this.logger.log(`Successfully updated the question bank.`); return res.status(HttpStatus.CREATED).json({ message: "Question bank updated sucessfully.", @@ -296,4 +298,35 @@ export class QuestionBankController { }); } } + + // API to get question bank template header + @Get("/template") + @ApiOperation({ summary: "get question bank template header" }) + @ApiResponse({ status: HttpStatus.OK }) + async getQuestionBankTemplate( + @Res() res + ) { + this.logger.log(`Initiate to fetch the question bank template header.`); + try { + const header = await this.questionBankService.getQuestionBankTemplate(); + this.logger.log( + `Successfully fetch the question bank template header.` + ); + return res.status(HttpStatus.CREATED).json({ + message: "Successfully fetch the question bank template header.", + data : header + }); + } catch (error) { + this.logger.error(`Failed to get the question bank template header.`, error); + // get error message and status code + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + // Return an error response + return res.status(statusCode).json({ + statusCode, + message: + errorMessage || `Failed to get the question bank template header.`, + }); + } + } } diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index d6078a2..4bc2398 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -362,4 +362,9 @@ export class QuestionBankService { } }); } + + public async getQuestionBankTemplate() { + const header = ["competency", "competencyLevelNumber", "question"]; + return header; + } } From 9eb50685df92190fe3dbca03bc702245869d1e8b Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Tue, 14 Nov 2023 15:17:20 +0530 Subject: [PATCH 118/175] API for question bank template --- src/config/multer-options.config.ts | 15 -------- src/question-bank/question-bank.controller.ts | 37 +------------------ src/question-bank/question-bank.service.ts | 5 --- 3 files changed, 2 insertions(+), 55 deletions(-) diff --git a/src/config/multer-options.config.ts b/src/config/multer-options.config.ts index 442d537..93d5fb6 100644 --- a/src/config/multer-options.config.ts +++ b/src/config/multer-options.config.ts @@ -1,26 +1,11 @@ -import { UnsupportedMediaTypeException } from "@nestjs/common"; import { MulterOptions } from "@nestjs/platform-express/multer/interfaces/multer-options.interface"; import { diskStorage } from "multer"; -export const csvFileFilter = ( - req: Request, - file: Express.Multer.File, - callback -) => { - if (!file.originalname.match(/\.(csv)$/)) { - return callback( - new UnsupportedMediaTypeException("Only csv files are allowed!"), - false - ); - } - callback(null, true); -}; export const multerOptions: MulterOptions = { dest: "src/uploads", // this is the destination of the uploaded files on disk limits: { fileSize: 1024 * 1024 * 5, // max size 5MB }, - fileFilter: csvFileFilter, storage: diskStorage({ destination: "src/uploads", filename: (_req, file, cb) => { diff --git a/src/question-bank/question-bank.controller.ts b/src/question-bank/question-bank.controller.ts index 30d4a8b..87cf98a 100644 --- a/src/question-bank/question-bank.controller.ts +++ b/src/question-bank/question-bank.controller.ts @@ -211,7 +211,7 @@ export class QuestionBankController { // Log the initiaton for csv file upload this.logger.log(`Initiate to upload a csv file.`); try { - await this.questionBankService.uploadCsvFile(file.path); + this.questionBankService.uploadCsvFile(file.path); this.logger.log(`Successfully uploaded question bank.`); return res.status(HttpStatus.CREATED).json({ message: "Question bank uploaded sucessfully.", @@ -279,9 +279,7 @@ export class QuestionBankController { // Log the initiaton for csv file upload this.logger.log(`Initiate updating the question bank.`); try { - await this.questionBankService.createUpdateDeleteQuesitons( - createUpdateDeleteQuesitonsDto - ); + await this.questionBankService.createUpdateDeleteQuesitons(createUpdateDeleteQuesitonsDto); this.logger.log(`Successfully updated the question bank.`); return res.status(HttpStatus.CREATED).json({ message: "Question bank updated sucessfully.", @@ -298,35 +296,4 @@ export class QuestionBankController { }); } } - - // API to get question bank template header - @Get("/template") - @ApiOperation({ summary: "get question bank template header" }) - @ApiResponse({ status: HttpStatus.OK }) - async getQuestionBankTemplate( - @Res() res - ) { - this.logger.log(`Initiate to fetch the question bank template header.`); - try { - const header = await this.questionBankService.getQuestionBankTemplate(); - this.logger.log( - `Successfully fetch the question bank template header.` - ); - return res.status(HttpStatus.CREATED).json({ - message: "Successfully fetch the question bank template header.", - data : header - }); - } catch (error) { - this.logger.error(`Failed to get the question bank template header.`, error); - // get error message and status code - const { errorMessage, statusCode } = - getPrismaErrorStatusAndMessage(error); - // Return an error response - return res.status(statusCode).json({ - statusCode, - message: - errorMessage || `Failed to get the question bank template header.`, - }); - } - } } diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index 4bc2398..d6078a2 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -362,9 +362,4 @@ export class QuestionBankService { } }); } - - public async getQuestionBankTemplate() { - const header = ["competency", "competencyLevelNumber", "question"]; - return header; - } } From cedd31636d6420ac07b171a3920a1264303bdd0a Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Tue, 14 Nov 2023 15:21:10 +0530 Subject: [PATCH 119/175] Question bank template API --- src/config/multer-options.config.ts | 15 ++++++++ src/question-bank/question-bank.controller.ts | 37 ++++++++++++++++++- src/question-bank/question-bank.service.ts | 5 +++ 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/config/multer-options.config.ts b/src/config/multer-options.config.ts index 93d5fb6..442d537 100644 --- a/src/config/multer-options.config.ts +++ b/src/config/multer-options.config.ts @@ -1,11 +1,26 @@ +import { UnsupportedMediaTypeException } from "@nestjs/common"; import { MulterOptions } from "@nestjs/platform-express/multer/interfaces/multer-options.interface"; import { diskStorage } from "multer"; +export const csvFileFilter = ( + req: Request, + file: Express.Multer.File, + callback +) => { + if (!file.originalname.match(/\.(csv)$/)) { + return callback( + new UnsupportedMediaTypeException("Only csv files are allowed!"), + false + ); + } + callback(null, true); +}; export const multerOptions: MulterOptions = { dest: "src/uploads", // this is the destination of the uploaded files on disk limits: { fileSize: 1024 * 1024 * 5, // max size 5MB }, + fileFilter: csvFileFilter, storage: diskStorage({ destination: "src/uploads", filename: (_req, file, cb) => { diff --git a/src/question-bank/question-bank.controller.ts b/src/question-bank/question-bank.controller.ts index 87cf98a..30d4a8b 100644 --- a/src/question-bank/question-bank.controller.ts +++ b/src/question-bank/question-bank.controller.ts @@ -211,7 +211,7 @@ export class QuestionBankController { // Log the initiaton for csv file upload this.logger.log(`Initiate to upload a csv file.`); try { - this.questionBankService.uploadCsvFile(file.path); + await this.questionBankService.uploadCsvFile(file.path); this.logger.log(`Successfully uploaded question bank.`); return res.status(HttpStatus.CREATED).json({ message: "Question bank uploaded sucessfully.", @@ -279,7 +279,9 @@ export class QuestionBankController { // Log the initiaton for csv file upload this.logger.log(`Initiate updating the question bank.`); try { - await this.questionBankService.createUpdateDeleteQuesitons(createUpdateDeleteQuesitonsDto); + await this.questionBankService.createUpdateDeleteQuesitons( + createUpdateDeleteQuesitonsDto + ); this.logger.log(`Successfully updated the question bank.`); return res.status(HttpStatus.CREATED).json({ message: "Question bank updated sucessfully.", @@ -296,4 +298,35 @@ export class QuestionBankController { }); } } + + // API to get question bank template header + @Get("/template") + @ApiOperation({ summary: "get question bank template header" }) + @ApiResponse({ status: HttpStatus.OK }) + async getQuestionBankTemplate( + @Res() res + ) { + this.logger.log(`Initiate to fetch the question bank template header.`); + try { + const header = await this.questionBankService.getQuestionBankTemplate(); + this.logger.log( + `Successfully fetch the question bank template header.` + ); + return res.status(HttpStatus.CREATED).json({ + message: "Successfully fetch the question bank template header.", + data : header + }); + } catch (error) { + this.logger.error(`Failed to get the question bank template header.`, error); + // get error message and status code + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + // Return an error response + return res.status(statusCode).json({ + statusCode, + message: + errorMessage || `Failed to get the question bank template header.`, + }); + } + } } diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index d6078a2..a8f2854 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -362,4 +362,9 @@ export class QuestionBankService { } }); } + + public async getQuestionBankTemplate() { + const header = ["competency", "competencyLevelNu", "question"]; + return header; + } } From 7ceff304ad6e0dc21b0fe1c187e95d4c61876ec7 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 15 Nov 2023 09:47:36 +0530 Subject: [PATCH 120/175] Minor schema changes. And survey logic updates after removing department. --- prisma/schema.prisma | 5 +- src/file-upload/file-upload.module.ts | 9 +- src/question-bank/question-bank.controller.ts | 2 +- src/question-bank/question-bank.service.ts | 23 ++- .../response-tracker.service.ts | 4 +- src/scheduled-tasks/scheduled-tasks.module.ts | 2 + .../scheduled-tasks.service.ts | 180 +++++++++--------- .../dto/create-survey-config.dto.ts | 50 ++++- .../dto/response-survey-config.dto.ts | 9 +- .../dto/update-survey-config.dto.ts | 51 ++++- src/survey-config/survey-config.controller.ts | 26 ++- src/survey-config/survey-config.module.ts | 13 +- src/survey-config/survey-config.service.ts | 139 ++++++++++++-- src/survey-form/dto/create-survey-form.dto.ts | 2 +- .../dto/response-survey-form.dto.ts | 18 +- src/survey-form/survey-form.module.ts | 3 +- src/survey-form/survey-form.service.ts | 8 +- src/survey/survey.controller.ts | 29 +-- src/survey/survey.module.ts | 1 + src/survey/survey.service.ts | 117 ++++++------ src/user-metadata/user-metadata.module.ts | 8 +- src/user-metadata/user-metadata.service.ts | 85 +++++---- .../custom-decorators/isTwoFieldsRequired.ts | 38 ++++ src/utils/utils.ts | 19 ++ 24 files changed, 562 insertions(+), 279 deletions(-) create mode 100644 src/utils/custom-decorators/isTwoFieldsRequired.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index bb96001..4054ff0 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -15,6 +15,7 @@ model UserMetadata { id Int @unique() @default(autoincrement()) userId String @unique() @db.Uuid userName String @unique() + profilePicture String? isNewEmployee Boolean designation String? dateOfJoining DateTime @@ -87,7 +88,7 @@ model SurveyConfig { model UserMapping { id Int @id @default(autoincrement()) surveyConfigId Int - SurveyConfig SurveyConfig @relation(fields: [surveyConfigId], references: [id]) + SurveyConfig SurveyConfig @relation(fields: [surveyConfigId], references: [id], onDelete: Cascade) assesseeId String @db.Uuid assessorIds String[] @db.Uuid } @@ -130,7 +131,7 @@ model SurveyForm { model ResponseTracker { id Int @id @default(autoincrement()) surveyFormId Int - surveyForm SurveyForm @relation(fields: [surveyFormId], references: [id]) + surveyForm SurveyForm @relation(fields: [surveyFormId], references: [id], onDelete: Cascade) assesseeId String @db.Uuid assessorId String @db.Uuid responseJson Json[] @default([]) //{questionId: number, answer: (Yes, No and DoNotKnow)} diff --git a/src/file-upload/file-upload.module.ts b/src/file-upload/file-upload.module.ts index 05f67f7..e31bd76 100644 --- a/src/file-upload/file-upload.module.ts +++ b/src/file-upload/file-upload.module.ts @@ -1,9 +1,10 @@ -import { Module } from '@nestjs/common'; -import { FileUploadService } from './file-upload.service'; -import { FileUploadController } from './file-upload.controller'; +import { Module } from "@nestjs/common"; +import { FileUploadService } from "./file-upload.service"; +import { FileUploadController } from "./file-upload.controller"; @Module({ controllers: [FileUploadController], - providers: [FileUploadService] + providers: [FileUploadService], + exports: [FileUploadService], }) export class FileUploadModule {} diff --git a/src/question-bank/question-bank.controller.ts b/src/question-bank/question-bank.controller.ts index 87cf98a..2854b1b 100644 --- a/src/question-bank/question-bank.controller.ts +++ b/src/question-bank/question-bank.controller.ts @@ -211,7 +211,7 @@ export class QuestionBankController { // Log the initiaton for csv file upload this.logger.log(`Initiate to upload a csv file.`); try { - this.questionBankService.uploadCsvFile(file.path); + await this.questionBankService.uploadCsvFile(file.path); this.logger.log(`Successfully uploaded question bank.`); return res.status(HttpStatus.CREATED).json({ message: "Question bank uploaded sucessfully.", diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index d6078a2..b97649b 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -53,8 +53,8 @@ export class QuestionBankService { const { competencyId, competencyLevelNumber, - limit = 10, - offset = 0, + limit, + offset, orderBy, } = filter; return this.prisma.questionBank.findMany({ @@ -65,8 +65,8 @@ export class QuestionBankService { orderBy: { [orderBy || "createdAt"]: "asc", // Default sorting by createdAt }, - skip: offset, - take: limit, + skip: offset ?? undefined, + take: limit ?? undefined, }); } @@ -118,9 +118,20 @@ export class QuestionBankService { public async uploadCsvFile(filepath) { // Parsed the uploaded data - const parsedData = await this.fileUploadService.parseCSV(filepath); + let parsedData; + try { + parsedData = await this.fileUploadService.parseCSV(filepath); + } catch (error) { + await this.fileUploadService.deleteUploadedFile(filepath); + throw error; + } // Store the parsedData in the db - await this.bulkUploadQuestions(parsedData); + try { + await this.bulkUploadQuestions(parsedData); + } catch (error) { + await this.fileUploadService.deleteUploadedFile(filepath); + throw error; + } // Clean up after the sucessful upload of csv data await this.fileUploadService.deleteUploadedFile(filepath); } diff --git a/src/response-tracker/response-tracker.service.ts b/src/response-tracker/response-tracker.service.ts index 358503c..47217cb 100644 --- a/src/response-tracker/response-tracker.service.ts +++ b/src/response-tracker/response-tracker.service.ts @@ -65,6 +65,7 @@ export class ResponseTrackerService { select: { designation: true, userName: true, + profilePicture: true, }, }, surveyForm: { @@ -96,7 +97,8 @@ export class ResponseTrackerService { Assessor: { select: { designation: true, - userName: true, // add profile picture when added + userName: true, + profilePicture: true, }, }, surveyForm: { diff --git a/src/scheduled-tasks/scheduled-tasks.module.ts b/src/scheduled-tasks/scheduled-tasks.module.ts index 59ec0a9..84eaf40 100644 --- a/src/scheduled-tasks/scheduled-tasks.module.ts +++ b/src/scheduled-tasks/scheduled-tasks.module.ts @@ -7,6 +7,7 @@ import { SurveyScoreService } from "../survey-score/survey-score.service"; import { SurveyService } from "../survey/survey.service"; import { UserMetadataModule } from "../user-metadata/user-metadata.module"; import { ScheduledTasksService } from "./scheduled-tasks.service"; +import { PrismaService } from "../prisma/prisma.service"; @Module({ imports: [QuestionBankModule, UserMetadataModule, SurveyConfigModule], @@ -16,6 +17,7 @@ import { ScheduledTasksService } from "./scheduled-tasks.service"; SurveyService, SurveyScoreService, ResponseTrackerService, + PrismaService ], }) export class ScheduledTasksModule {} diff --git a/src/scheduled-tasks/scheduled-tasks.service.ts b/src/scheduled-tasks/scheduled-tasks.service.ts index 257d9f9..c33d156 100644 --- a/src/scheduled-tasks/scheduled-tasks.service.ts +++ b/src/scheduled-tasks/scheduled-tasks.service.ts @@ -1,24 +1,19 @@ -import { Injectable, Logger, NotFoundException } from "@nestjs/common"; +import { Injectable, Logger } from "@nestjs/common"; import { Cron, CronExpression } from "@nestjs/schedule"; -import { - AdminDepartment, - SurveyConfig, - SurveyStatusEnum, -} from "@prisma/client"; +import { SurveyConfig, SurveyStatusEnum } from "@prisma/client"; +import { PrismaService } from "../prisma/prisma.service"; import { SurveyConfigService } from "../survey-config/survey-config.service"; -import { SurveyCycleParameterService } from "../survey-cycle-parameter/survey-cycle-parameter.service"; import { SurveyFormService } from "../survey-form/survey-form.service"; import { SurveyScoreService } from "../survey-score/survey-score.service"; import { SurveyService } from "../survey/survey.service"; import { UserMetadataService } from "../user-metadata/user-metadata.service"; -import { AdminDepartmentService } from "../admin-department/admin-department.service"; +import { isDayBeforeToday, isToday } from "../utils/utils"; @Injectable() export class ScheduledTasksService { constructor( - private adminDepartmentService: AdminDepartmentService, + private prismaService: PrismaService, private surveyConfigService: SurveyConfigService, - private surveyParamsService: SurveyCycleParameterService, private surveyService: SurveyService, private userMetadataService: UserMetadataService, private surveyFormService: SurveyFormService, @@ -29,55 +24,52 @@ export class ScheduledTasksService { @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT) async surveyCron() { this.logger.log( - `Start CRON Job for Surveys. Fetching data of all departments.` + `Start CRON Job for Surveys. Fetching data of all survey configurations.` ); - const departments = await this.adminDepartmentService.getAllAdminDepartment({}); + const surveyConfigs = await this.surveyConfigService.getAllSurveyConfig({}); this.logger.log( - `Fetched data of all departments. Cycling through every department to start or end surveys.` + `Fetched data of all survey configurations. Cycling through every survey configuration to start or end surveys.` ); - for (const department of departments) { - const surveyConfig = await this.surveyConfigService.getAllSurveyConfig({ - departmentId: department.id, - }); - - const surveyConfigParam = await this.surveyParamsService.getAllSurveyParameter({ - surveyConfigId: surveyConfig[0].id, - }); - + for (const surveyConfig of surveyConfigs) { this.logger.log( - `Checking if a survey is Active for the department "${department.name}" with id: "${department.departmentId}".` + `Checking if the surveyConfig "${surveyConfig.surveyName}" with id: "${surveyConfig.id}" is Active.` ); - if (surveyConfigParam && surveyConfigParam[0].isActive == true) { + if (surveyConfig.isActive == true) { + this.logger.log(`The survey is ACTIVE.`); - this.logger.log(`A survey is ACTIVE for department "${department.name}"`); - - if (this.isDayBeforeToday(surveyConfigParam[0].endTime)) { + if (isDayBeforeToday(surveyConfig.endTime)) { this.logger.log( `Survey will end tomorrow. Sending remainder notification to all survey participants.` ); //send notifications - } else if (this.isToday(surveyConfigParam[0].endTime)) { - + } else if (isToday(surveyConfig.endTime)) { this.logger.log(`Fetching all SurveyForms related to the survey.`); const surveyForms = - await this.surveyFormService.findSurveyFormBySurveyCycleParameterId( - surveyConfigParam[0].id + await this.surveyFormService.findSurveyFormBySurveyConfigId( + surveyConfig.id ); - this.logger.log(`Calculation scores for all "${surveyForms.length}" SurveyForms related to the survey.`); + this.logger.log( + `Calculation scores for all "${surveyForms.length}" SurveyForms related to the survey.` + ); for (const surveyForm of surveyForms) { - //calculate score for each form - this.logger.log(`Calculation score for SurveyForms with id: "${surveyForm.id}" for user with id: "${surveyForm.userId}".`); + this.logger.log( + `Calculating the score for SurveyForm with id: "${surveyForm.id}" for user with id: "${surveyForm.userId}".` + ); await this.surveyScoreService.calculateSurveyScoreBySurveyFormId( surveyForm.id ); + this.logger.log( + `Calculated the score for SurveyForm with id: "${surveyForm.id}" for user with id: "${surveyForm.userId}".` + ); - //update the survey form as "CLOSED" - this.logger.log(`Calculated the score and updating the surveyFrom's status to "CLOSED."`); + this.logger.log( + `Updating the surveyFrom's (id: "${surveyForm.id}") status to "CLOSED".` + ); await this.surveyFormService.updateSurveyFormStatus( surveyForm.id, SurveyStatusEnum.CLOSED @@ -85,80 +77,78 @@ export class ScheduledTasksService { this.logger.log(`Updated the surveyFrom's status to "CLOSED."`); } + this.logger.log( + `Deleting the userMapping for the survey as it will end today.` + ); + await this.prismaService.userMapping.deleteMany({ + where: { surveyConfigId: surveyConfig.id }, + }); + this.logger.log( + `Deleted the userMapping for the survey as it will end today.` + ); + this.logger.log(`Deactivating the Survey as it will end today.`); - await this.surveyParamsService.updateSurveyParameterById( - surveyConfigParam[0].id, + await this.surveyConfigService.updateSurveyConfigById( + surveyConfig.id, { isActive: false } ); - this.logger.log(`Deactivated the Survey with for the department "${department.name}" with id: "${department.departmentId}.`); - + this.logger.log( + `Deactivated the Survey "${surveyConfig.surveyName}" with surveyConfigId: "${surveyConfig.id}".` + ); } } else { - await this.startSurvey(department, surveyConfig[0]); + await this.startSurvey(surveyConfig); } } } - async startSurvey(department: AdminDepartment, surveyConfig: SurveyConfig) { - this.logger.log(`No survey is ACTIVE for department "${department.name}"`); - if (this.isDayBeforeToday(surveyConfig.startTime)) { - //update department data - this.logger.log(`The survey starts tomorrow. Updating department data.`); - const updateDepartment = - await this.adminDepartmentService.createOrUpdateAdminDepartment( - department.id - ); - this.logger.log(`Updated department's data.`); - + async startSurvey(surveyConfig: SurveyConfig) { + this.logger.log( + `The survey with surveyConfigId: "${surveyConfig.id}" is not ACTIVE.` + ); + if (isDayBeforeToday(surveyConfig.startTime)) { //update user data - this.logger.log(`Fetching users that belong to "${department.name}" department.`); - const updateUsers = await this.userMetadataService.findManyUserMetadata({ - departmentId: department.id, - }); - this.logger.log(`Updating all "${updateUsers.length}" user's data that belongs to the department "${department.name}".`); - for (const user of updateUsers) { - await this.userMetadataService.createOrUpdateUserMetadata(user.userId); - } - this.logger.log(`Updated all "${updateUsers.length}" user's data that belongs to the department "${department.name}".`); - } else if (this.isToday(surveyConfig.startTime)) { - this.logger.log(`The survey starts today.`); - const surveyConfigParam = - await this.surveyParamsService.getAllSurveyParameter({ - surveyConfigId: surveyConfig.id, - }); - - this.logger.log(`Activating the Survey with for the department "${department.name}" with id: "${department.departmentId}.`); - if (surveyConfigParam) { - await this.surveyParamsService.updateSurveyParameterById( - surveyConfigParam[0].id, - { isActive: false } + this.logger.log( + `Fetching userIds of the users who are participating in the survey.` + ); + const updateUsers = + await this.surveyService.fetchUserIdsOfSurveyParticipants( + surveyConfig.id ); + this.logger.log( + `Updating all "${updateUsers.size}" user's data who are participating in the survey.` + ); + for (const userId of updateUsers) { + await this.userMetadataService.createOrUpdateUserMetadata(userId); } + this.logger.log( + `Updated all "${updateUsers.size}" user's data who are participating in the survey.` + ); + } else if (isToday(surveyConfig.startTime)) { + this.logger.log( + `Activating the Survey "${surveyConfig.surveyName}" with surveyConfigId: "${surveyConfig.id}".` + ); + await this.surveyConfigService.updateSurveyConfigById(surveyConfig.id, { + isActive: true, + }); + this.logger.log( + `Activated the Survey "${surveyConfig.surveyName}" with surveyConfigId: "${surveyConfig.id}".` + ); - this.logger.log(`Generating SurveyForms for every user that belongs to the department "${department.name}".`); - await this.surveyService.generateSurveyFormsForDepartment(department.id); + this.logger.log( + `Generating SurveyForms for every user who is participating in the Survey "${surveyConfig.surveyName}" with id: "${surveyConfig.id}".` + ); + await this.surveyService.generateSurveyFormsForSurveyConfig( + surveyConfig.id + ); + this.logger.log( + `Generated SurveyForms for every user who is participating in the Survey "${surveyConfig.surveyName}" with id: "${surveyConfig.id}".` + ); // send notifications for all survey participant - this.logger.log(`Sending notification to every user that belongs to the department "${department.name}".`); + this.logger.log( + `Sending notification to every user who is participating in the Survey "${surveyConfig.surveyName}" with id: "${surveyConfig.id}".` + ); } } - - isDayBeforeToday(date) { - const today = new Date(); - today.setHours(0, 0, 0, 0); - - const yesterday = new Date(date); - yesterday.setDate(yesterday.getDate() - 1); - - return yesterday.getTime() === today.getTime(); - } - - isToday(date) { - const today = new Date(); - today.setHours(0, 0, 0, 0); - - const givenDate = new Date(date); - - return givenDate.getTime() === today.getTime(); - } } diff --git a/src/survey-config/dto/create-survey-config.dto.ts b/src/survey-config/dto/create-survey-config.dto.ts index e0b96b4..1c6cf85 100644 --- a/src/survey-config/dto/create-survey-config.dto.ts +++ b/src/survey-config/dto/create-survey-config.dto.ts @@ -1,32 +1,58 @@ import { ApiProperty } from "@nestjs/swagger"; import { TimeUnitsEnum } from "@prisma/client"; import { Type } from "class-transformer"; -import { IsDate, IsEnum, IsInt, IsNotEmpty, IsOptional } from "class-validator"; +import { + ArrayNotEmpty, + IsArray, + IsDate, + IsEnum, + IsInt, + IsNotEmpty, + IsOptional, + IsString, + IsUUID, +} from "class-validator"; +import { UUID } from "crypto"; +import { IsTwoFieldsRequired } from "../../utils/custom-decorators/isTwoFieldsRequired"; export class CreateSurveyConfigDto { + @IsNotEmpty() - @IsInt() - departmentId: number; + @IsString() + surveyName: string; + @IsOptional() @IsNotEmpty() + @IsTwoFieldsRequired("onboardingTime", "onboardingTimeUnit") @IsInt() - onboardingTime: number; + onboardingTime?: number; + @IsOptional() + @IsNotEmpty() @ApiProperty({ enum: TimeUnitsEnum }) + @IsTwoFieldsRequired("onboardingTimeUnit", "onboardingTime") @IsEnum(TimeUnitsEnum, { each: true }) - onboardingTimeUnit: TimeUnitsEnum; + onboardingTimeUnit?: TimeUnitsEnum; // start time for the survey config @IsNotEmpty() @IsDate() @Type(() => Date) + @IsTwoFieldsRequired("startTime", "endTime") startTime: Date; // end time for the survey config @IsNotEmpty() @IsDate() @Type(() => Date) + @IsTwoFieldsRequired("endTime", "startTime") endTime: Date; + + @ApiProperty({ + type: "string" || "number", + format: "binary", + }) + file: any; } // SurveyConfigFilterDto is used for filtering and paginating survey config. @@ -35,7 +61,7 @@ export class SurveyConfigFilterDto { @IsOptional() @IsNotEmpty() @IsInt() - departmentId?: number; + configId?: number; //Optional startTime filter validate that its valid date-time value @IsOptional() @@ -59,3 +85,15 @@ export class SurveyConfigFilterDto { @IsInt() offset?: number = 0; } + +export class UserMappingFileUploadDto { + @IsNotEmpty() + @IsUUID() + assesseeId: string; + + @IsNotEmpty() + @IsArray() + @ArrayNotEmpty() + @IsUUID("all", { each: true }) + assessorIds: UUID[]; +} diff --git a/src/survey-config/dto/response-survey-config.dto.ts b/src/survey-config/dto/response-survey-config.dto.ts index b5c0aed..b62be45 100644 --- a/src/survey-config/dto/response-survey-config.dto.ts +++ b/src/survey-config/dto/response-survey-config.dto.ts @@ -1,13 +1,16 @@ import { TimeUnitsEnum } from "@prisma/client"; -import { ResponseSurveyCycleParameterDto } from "src/survey-cycle-parameter/dto/response-survey-cycle-parameter.dto"; export class ResponseSurveyConfigDto { readonly id: number; - readonly departmentId: number; + readonly surveyName: string; readonly onboardingTime: number; readonly onboardingTimeUnit: TimeUnitsEnum; readonly startTime: Date; readonly endTime: Date; readonly isActive: boolean; - readonly SurveyCycleParameters?: ResponseSurveyCycleParameterDto[]; +} + +export class ConfigResponseDTO { + readonly data: ResponseSurveyConfigDto; + readonly message: string; } diff --git a/src/survey-config/dto/update-survey-config.dto.ts b/src/survey-config/dto/update-survey-config.dto.ts index 25c9b44..9263ef7 100644 --- a/src/survey-config/dto/update-survey-config.dto.ts +++ b/src/survey-config/dto/update-survey-config.dto.ts @@ -1,4 +1,51 @@ -import { PartialType } from '@nestjs/swagger'; +import { ApiProperty, PartialType } from '@nestjs/swagger'; import { CreateSurveyConfigDto } from './create-survey-config.dto'; +import { IsBoolean, IsDate, IsEnum, IsInt, IsNotEmpty, IsOptional, IsString } from 'class-validator'; +import { IsTwoFieldsRequired } from '../../utils/custom-decorators/isTwoFieldsRequired'; +import { TimeUnitsEnum } from '@prisma/client'; +import { Type } from 'class-transformer'; -export class UpdateSurveyConfigDto extends PartialType(CreateSurveyConfigDto) {} +export class UpdateSurveyConfigDto { + + @IsOptional() + @IsNotEmpty() + @IsString() + surveyName?: string; + + @IsOptional() + @IsNotEmpty() + @IsTwoFieldsRequired("onboardingTime", "onboardingTimeUnit") + @IsInt() + onboardingTime?: number; + + @IsOptional() + @IsNotEmpty() + @ApiProperty({ enum: TimeUnitsEnum }) + @IsTwoFieldsRequired("onboardingTimeUnit", "onboardingTime") + @IsEnum(TimeUnitsEnum, { each: true }) + onboardingTimeUnit?: TimeUnitsEnum; + + @IsOptional() + @IsNotEmpty() + @IsDate() + @Type(() => Date) + startTime?: Date; + + @IsOptional() + @IsNotEmpty() + @IsDate() + @Type(() => Date) + endTime?: Date; + + @IsOptional() + @ApiProperty({ + type: "string" || "number", + format: "binary", + }) + file?: any; + + @IsOptional() + @IsNotEmpty() + @IsBoolean() + isActive?: boolean; +} diff --git a/src/survey-config/survey-config.controller.ts b/src/survey-config/survey-config.controller.ts index f66bbc3..d945aca 100644 --- a/src/survey-config/survey-config.controller.ts +++ b/src/survey-config/survey-config.controller.ts @@ -11,6 +11,8 @@ import { Query, Param, ParseIntPipe, + UseInterceptors, + UploadedFile, } from "@nestjs/common"; import { SurveyConfigService } from "./survey-config.service"; import { @@ -18,9 +20,11 @@ import { SurveyConfigFilterDto, } from "./dto/create-survey-config.dto"; import { UpdateSurveyConfigDto } from "./dto/update-survey-config.dto"; -import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; -import { ResponseSurveyConfigDto } from "./dto/response-survey-config.dto"; +import { ApiBody, ApiConsumes, ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { ConfigResponseDTO, ResponseSurveyConfigDto } from "./dto/response-survey-config.dto"; import { getPrismaErrorStatusAndMessage } from "../utils/utils"; +import { FileInterceptor } from "@nestjs/platform-express"; +import { multerOptions } from "../config/multer-options.config"; @Controller("survey-config") @ApiTags("survey-config") @@ -32,18 +36,21 @@ export class SurveyConfigController { constructor(private readonly surveyConfigService: SurveyConfigService) {} @Post() + @ApiConsumes("multipart/form-data") @ApiOperation({ summary: "Create a survey config" }) // Describes the api operation for Swagger. - @ApiResponse({ status: HttpStatus.CREATED, type: ResponseSurveyConfigDto }) // Describes the response for Swagger. + @UseInterceptors(FileInterceptor("file", multerOptions)) + @ApiResponse({ status: HttpStatus.CREATED, type: ConfigResponseDTO }) async createSurveyConfig( @Res() res, + @UploadedFile() file, @Body() createSurveyConfigDto: CreateSurveyConfigDto - ): Promise { + ): Promise { try { // Log the initiation for the Survey config creation this.logger.log(`Creating a new survey config`); const createdSurveyConfig = await this.surveyConfigService.createSurveyConfig( - createSurveyConfigDto + file.path, createSurveyConfigDto ); // Log the successful creation of the survey config @@ -106,19 +113,22 @@ export class SurveyConfigController { // Update the survey config @Patch("/update/:id") + @ApiConsumes("multipart/form-data") @ApiOperation({ summary: "Update the survey config by ID" }) // Api operation for swagger - @ApiResponse({ status: HttpStatus.OK, type: ResponseSurveyConfigDto }) // Api operation for swagger + @ApiResponse({ status: HttpStatus.OK, type: ConfigResponseDTO }) // Api operation for swagger async updateSurveyConfigById( @Res() res, @Param("id", ParseIntPipe) id: number, + @UploadedFile() file, @Body() updateSurveyConfigDto: UpdateSurveyConfigDto - ): Promise { + ): Promise { try { this.logger.log(`Initiated updating the survey config for id #${id}`); const updateSurveyConfig = await this.surveyConfigService.updateSurveyConfigById( id, - updateSurveyConfigDto + updateSurveyConfigDto, + file ? file.path: null, ); this.logger.log(`Successfully updated survey config for id #${id}`); return res.status(HttpStatus.OK).json({ diff --git a/src/survey-config/survey-config.module.ts b/src/survey-config/survey-config.module.ts index 64e4506..da49838 100644 --- a/src/survey-config/survey-config.module.ts +++ b/src/survey-config/survey-config.module.ts @@ -1,13 +1,14 @@ -import { Module } from "@nestjs/common"; -import { SurveyConfigService } from "./survey-config.service"; -import { SurveyConfigController } from "./survey-config.controller"; -import { PrismaModule } from "../prisma/prisma.module"; +import { Module, forwardRef } from "@nestjs/common"; +import { FileUploadService } from "../file-upload/file-upload.service"; import { PrismaService } from "../prisma/prisma.service"; +import { UserMetadataModule } from "../user-metadata/user-metadata.module"; +import { SurveyConfigController } from "./survey-config.controller"; +import { SurveyConfigService } from "./survey-config.service"; @Module({ - imports: [PrismaModule], + imports: [forwardRef(() => UserMetadataModule)], controllers: [SurveyConfigController], - providers: [SurveyConfigService], + providers: [SurveyConfigService, FileUploadService, PrismaService], exports: [SurveyConfigService], }) export class SurveyConfigModule {} diff --git a/src/survey-config/survey-config.service.ts b/src/survey-config/survey-config.service.ts index 3bb350e..e8a8ad7 100644 --- a/src/survey-config/survey-config.service.ts +++ b/src/survey-config/survey-config.service.ts @@ -1,39 +1,98 @@ -import { Injectable, NotFoundException, Query, Res } from "@nestjs/common"; +import { + Inject, + Injectable, + NotFoundException, + forwardRef, +} from "@nestjs/common"; +import { FileUploadService } from "../file-upload/file-upload.service"; +import { PrismaService } from "../prisma/prisma.service"; import { CreateSurveyConfigDto, SurveyConfigFilterDto, + UserMappingFileUploadDto, } from "./dto/create-survey-config.dto"; import { UpdateSurveyConfigDto } from "./dto/update-survey-config.dto"; -import { PrismaService } from "../prisma/prisma.service"; +import { UserMetadataService } from "../user-metadata/user-metadata.service"; @Injectable() export class SurveyConfigService { - constructor(private prisma: PrismaService) {} - async createSurveyConfig(createSurveyConfigDto: CreateSurveyConfigDto) { - // create new survey config - const newConfig = await this.prisma.surveyConfig.create({ - data: createSurveyConfigDto, - }); + constructor( + private prisma: PrismaService, + private fileUploadService: FileUploadService, + @Inject(forwardRef(() => UserMetadataService)) + private userMetadataService: UserMetadataService + ) {} + async createSurveyConfig( + filepath, + createSurveyConfigDto: CreateSurveyConfigDto + ) { + let newConfig; + let userIdList: Set = new Set(); + try { + await this.prisma.$transaction(async (prismaClient) => { + newConfig = await prismaClient.surveyConfig.create({ + data: { + surveyName: createSurveyConfigDto.surveyName, + endTime: createSurveyConfigDto.endTime, + startTime: createSurveyConfigDto.startTime, + }, + }); + + const parsedData = await this.fileUploadService.parseCSV(filepath); + + for (const obj of parsedData) { + obj.assessorIds = obj.assessorIds.split(", "); + + const userMapping: UserMappingFileUploadDto = obj; + + try { + userIdList = await this.userMetadataService.validateAndFetchUserIds( + prismaClient, + userIdList, + userMapping + ); + } catch (error) { + throw error; + } + + await prismaClient.userMapping.create({ + data: { + surveyConfigId: newConfig.id, + assesseeId: userMapping.assesseeId, + assessorIds: userMapping.assessorIds, + }, + }); + } + }); + await this.fileUploadService.deleteUploadedFile(filepath); + } catch (error) { + await this.fileUploadService.deleteUploadedFile(filepath); + throw error; + } + return newConfig; } async getAllSurveyConfig(filter: SurveyConfigFilterDto) { - const { departmentId, startTime, endTime, limit = 10, offset = 0 } = filter; + const { configId, startTime, endTime, limit, offset } = filter; return await this.prisma.surveyConfig.findMany({ where: { - departmentId: departmentId ?? undefined, // Optional departmentId filter - startTime: startTime ?? undefined, // Optional startTime filter - endTime: endTime ?? undefined, // Optional endTime filter + id: configId ?? undefined, + startTime: startTime ?? undefined, + endTime: endTime ?? undefined, }, - skip: +offset, - take: +limit, + skip: offset ?? undefined, + take: limit ?? undefined, }); } async updateSurveyConfigById( surveyConfigId: number, - updateSurveyConfig: UpdateSurveyConfigDto + updateSurveyConfig: UpdateSurveyConfigDto, + filepath = null ) { + let updatedConfig; + let userIdList: Set = new Set(); // Check the document available for the given surveyId in database to update. const findSurveyConfigId = await this.prisma.surveyConfig.findUnique({ where: { @@ -46,12 +105,50 @@ export class SurveyConfigService { ); } // Update the survey config by the id - return await this.prisma.surveyConfig.update({ - where: { - id: surveyConfigId, - }, - data: updateSurveyConfig, - }); + try { + await this.prisma.$transaction(async (prismaClient) => { + if (filepath != null) { + const parsedData = await this.fileUploadService.parseCSV(filepath); + + for (const obj of parsedData) { + obj.assessorIds = obj.assessorIds.split(", "); + + const userMapping: UserMappingFileUploadDto = obj; + + try { + userIdList = await this.userMetadataService.validateAndFetchUserIds( + prismaClient, + userIdList, + userMapping + ); + } catch (error) { + throw error; + } + + await prismaClient.userMapping.create({ + data: { + surveyConfigId: updatedConfig.id, + assesseeId: userMapping.assesseeId, + assessorIds: userMapping.assessorIds, + }, + }); + } + + delete updateSurveyConfig.file; + await this.fileUploadService.deleteUploadedFile(filepath); + } + + updatedConfig = await this.prisma.surveyConfig.update({ + where: { + id: surveyConfigId, + }, + data: updateSurveyConfig, + }); + }); + } catch (error) { + throw error; + } + return updatedConfig; } async deleteSurveyConfig(surveyConfigId: number) { diff --git a/src/survey-form/dto/create-survey-form.dto.ts b/src/survey-form/dto/create-survey-form.dto.ts index 43ca5cb..38506a6 100644 --- a/src/survey-form/dto/create-survey-form.dto.ts +++ b/src/survey-form/dto/create-survey-form.dto.ts @@ -28,7 +28,7 @@ export class CreateSurveyFormDto { @IsNumber() @IsNotEmpty() - surveyCycleParameterId: number; + surveyConfigId: number; @ApiProperty({ enum: SurveyStatusEnum, example: "CREATED" }) @IsEnum(SurveyStatusEnum) diff --git a/src/survey-form/dto/response-survey-form.dto.ts b/src/survey-form/dto/response-survey-form.dto.ts index e602b42..99f73c9 100644 --- a/src/survey-form/dto/response-survey-form.dto.ts +++ b/src/survey-form/dto/response-survey-form.dto.ts @@ -2,15 +2,19 @@ import { ApiProperty } from "@nestjs/swagger"; import { SurveyStatusEnum } from "@prisma/client"; import { JsonValue } from "@prisma/client/runtime/library"; import { SurveyScoreResponse } from "src/survey-score/dto"; +import { ResponseSurveyConfigDto } from "../../survey-config/dto/response-survey-config.dto"; +import { ResponseTracker } from "../../response-tracker/dto"; export class ResponseSurveyFormDto { - readonly id: number; - readonly userId: string; - readonly surveyCycleParameterId: number; - readonly status: SurveyStatusEnum; - readonly questionsJson: JsonValue; - readonly overallScore: number | null; - readonly sunbirdCredentialIds: string | null; + readonly id?: number; + readonly userId?: string; + readonly surveyConfigId?: number; + readonly SurveyConfig?: ResponseSurveyConfigDto; + readonly status?: SurveyStatusEnum; + readonly questionsJson?: JsonValue; + readonly overallScore?: number | null; + readonly sunbirdCredentialIds?: string | null; + readonly ResponseTracker?: ResponseTracker } export class SurveyFormResponse { diff --git a/src/survey-form/survey-form.module.ts b/src/survey-form/survey-form.module.ts index 0e640b2..53a5d56 100644 --- a/src/survey-form/survey-form.module.ts +++ b/src/survey-form/survey-form.module.ts @@ -6,6 +6,7 @@ import { ConfigService } from "@nestjs/config"; @Module({ controllers: [SurveyFormController], - providers: [SurveyFormService, PrismaService, ConfigService], + providers: [SurveyFormService, PrismaService], + exports:[SurveyFormService] }) export class SurveyFormModule {} diff --git a/src/survey-form/survey-form.service.ts b/src/survey-form/survey-form.service.ts index ecc0efb..02a3bbb 100644 --- a/src/survey-form/survey-form.service.ts +++ b/src/survey-form/survey-form.service.ts @@ -37,15 +37,15 @@ export class SurveyFormService { }); } - async findSurveyFormBySurveyCycleParameterId(paramId: number) { + async findSurveyFormBySurveyConfigId(configId: number) { const surveyForms = await this.prisma.surveyForm.findMany({ where: { - surveyCycleParameterId: paramId, + surveyConfigId: configId, }, }); if (!surveyForms || surveyForms.length == 0) { throw new NotFoundException( - `Survey Forms not found for SurveyCycleParameter with id #${paramId}` + `Survey Forms not found for SurveyCycleParameter with id #${configId}` ); } return surveyForms; @@ -132,7 +132,7 @@ export class SurveyFormService { where: { userId, status: SurveyStatusEnum.PUBLISHED, - surveyCycleParameter: { + SurveyConfig: { isActive: true, }, }, diff --git a/src/survey/survey.controller.ts b/src/survey/survey.controller.ts index f6b77f5..9fa9b5b 100644 --- a/src/survey/survey.controller.ts +++ b/src/survey/survey.controller.ts @@ -11,6 +11,7 @@ import { import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; import { SurveyService } from "./survey.service"; +import { ResponseSurveyFormDto, SurveyFormResponse } from "../survey-form/dto"; @Controller("survey") @ApiTags("survey") @@ -18,30 +19,32 @@ export class SurveyController { private readonly logger = new Logger(SurveyController.name); constructor(private readonly surveyService: SurveyService) {} - @Post(":departmentId") - @ApiOperation({ summary: "Create Survey-Forms for a department." }) + @Post(":configId") + @ApiOperation({ summary: "Create Survey-Forms for a SurveyConfig." }) @ApiResponse({ status: HttpStatus.CREATED }) - async generateSurveyFormsForDepartment( + async generateSurveyFormsForSurveyConfig( @Res() res, - @Param("departmentId") departmentId: number + @Param("configId") configId: number ) { try { - this.logger.log(`Initiated creating new survey forms for a department.`); + this.logger.log( + `Initiated creating new survey forms for a Survey Config.` + ); const surveyForms = - await this.surveyService.generateSurveyFormsForDepartment(departmentId); + await this.surveyService.generateSurveyFormsForSurveyConfig(configId); this.logger.log( - `Successfully created new survey forms for a department.` + `Successfully created new survey forms for a Survey Config.` ); return res.status(HttpStatus.CREATED).json({ data: surveyForms, - message: `SurveyForms created successfully for department with id #${departmentId}`, + message: `SurveyForms created successfully for Survey Config with id #${configId}`, }); } catch (error) { this.logger.error( - `Failed to create new survey forms for department with id #${departmentId}`, + `Failed to create new survey forms for Survey Config with id #${configId}`, error ); @@ -50,7 +53,7 @@ export class SurveyController { return res.status(statusCode).json({ message: errorMessage || - `Could not create survey forms for department with id #${departmentId}`, + `Could not create survey forms for Survey Config with id #${configId}`, }); } } @@ -61,7 +64,7 @@ export class SurveyController { async getSurveysToBeFilledByUser( @Res() res, @Param("userId", ParseUUIDPipe) userId: string - ) { + ): Promise<{ data: number; message: string }> { try { this.logger.log( `Getting number of surveys to be filled by user with id #${userId}.` @@ -97,11 +100,11 @@ export class SurveyController { @Get("latest-survey-response/:userId") @ApiOperation({ summary: "Fetch latest survey response by userId" }) - @ApiResponse({ status: HttpStatus.OK }) + @ApiResponse({ status: HttpStatus.OK, type: ResponseSurveyFormDto }) async getLatestSurveyResponsesForUserId( @Res() res, @Param("userId") userId: string - ) { + ): Promise { try { this.logger.log( `Initiated fetching latest survey response by userId #${userId}` diff --git a/src/survey/survey.module.ts b/src/survey/survey.module.ts index c24e579..0a5b6e8 100644 --- a/src/survey/survey.module.ts +++ b/src/survey/survey.module.ts @@ -12,5 +12,6 @@ import { SurveyService } from "./survey.service"; SurveyFormService, ], controllers: [SurveyController], + exports:[SurveyService] }) export class SurveyModule {} diff --git a/src/survey/survey.service.ts b/src/survey/survey.service.ts index c132f31..51e63bd 100644 --- a/src/survey/survey.service.ts +++ b/src/survey/survey.service.ts @@ -1,10 +1,5 @@ -import { - BadRequestException, - Injectable, - NotFoundException, -} from "@nestjs/common"; +import { Injectable, NotFoundException } from "@nestjs/common"; import { ResponseTrackerStatusEnum, SurveyStatusEnum } from "@prisma/client"; -import _ from "lodash"; import { PrismaService } from "src/prisma/prisma.service"; import { CreateSurveyFormDto, @@ -12,6 +7,7 @@ import { } from "src/survey-form/dto"; import { SurveyFormService } from "src/survey-form/survey-form.service"; import { QuestionBankService } from "../question-bank/question-bank.service"; +import _ from "lodash"; @Injectable() export class SurveyService { @@ -22,27 +18,31 @@ export class SurveyService { ) {} async getSurveysToBeFilledByUser(userId: string) { - const responseCount = await this.prisma.surveyForm.findFirst({ + const responseCount = await this.prisma.responseTracker.findMany({ where: { - userId, - surveyCycleParameter: { - isActive: true, - }, - ResponseTracker: { - every: { - assessorId: userId, - status: ResponseTrackerStatusEnum.PENDING, + assessorId: userId, + status: ResponseTrackerStatusEnum.PENDING, + surveyForm: { + SurveyConfig: { + isActive: true, }, }, }, - orderBy: { - createdAt: "desc", - }, - select: { - ResponseTracker: { - select: { - id: true, - status: true, + }); + if (!responseCount) { + return 0; + } + return responseCount.length; + } + + async getSurveysFilledByUser(userId: string) { + const responseCount = await this.prisma.responseTracker.findMany({ + where: { + assessorId: userId, + status: ResponseTrackerStatusEnum.COMPLETED, + surveyForm: { + SurveyConfig: { + isActive: true, }, }, }, @@ -50,14 +50,14 @@ export class SurveyService { if (!responseCount) { return 0; } - return responseCount.ResponseTracker.length; + return responseCount.length; } async getLatestSurveyResponsesForUserId(userId: string) { const responses = await this.prisma.surveyForm.findFirst({ where: { userId, - surveyCycleParameter: { + SurveyConfig: { isActive: true, }, ResponseTracker: { @@ -95,63 +95,42 @@ export class SurveyService { return responses; } - async generateSurveyFormsForDepartment(departmentId: number) { + async generateSurveyFormsForSurveyConfig(configId: number) { let surveyForms: ResponseSurveyFormDto[] = []; - const users = await this.prisma.userMetadata.findMany({ - where: { - departmentId, - }, - }); - const surveyCycleParameter = await this.prisma.surveyConfig.findFirst({ + //logic to fetch user mapping + const userMapping = await this.prisma.userMapping.findMany({ where: { - departmentId, - SurveyCycleParameters: { - every: { isActive: true }, - }, - }, - select: { - SurveyCycleParameters: { - select: { - id: true, - }, - }, + surveyConfigId: configId, }, }); - - const surveyCycleParameterId = _.get( - surveyCycleParameter, - "SurveyCycleParameters.[0].id", - 0 - ); - - if (!surveyCycleParameterId || _.isEqual(surveyCycleParameterId, 0)) { - throw new BadRequestException( - `No any active survey cycle found for department id #${departmentId}` + if (_.isUndefined(userMapping) || _.isEmpty(userMapping)) { + throw new NotFoundException( + `No user mapping found for Survey Ci=onfig with id ${configId}` ); } - for (const user of users) { + for (const mapping of userMapping) { //get questions for the user according to their designation const questions = await this.questionBank.getAllQuestionsForUser( - user.userId + mapping.assesseeId ); //create the CreateSurveyFormDto for the user const surveyFormDto: CreateSurveyFormDto = { - userId: user.userId, - surveyCycleParameterId, + userId: mapping.assesseeId, + surveyConfigId: configId, status: SurveyStatusEnum.PUBLISHED, questionsJson: questions, }; const surveyForm = await this.surveyForm.createSurveyForm(surveyFormDto); surveyForms.push(surveyForm); - for (const assessor of users) { - if (user.userId !== assessor.userId) { + for (const assessor of mapping.assessorIds) { + if (mapping.assesseeId !== assessor) { await this.prisma.responseTracker.create({ data: { surveyFormId: surveyForm.id, - assesseeId: user.userId, - assessorId: assessor.userId, + assesseeId: mapping.assesseeId, + assessorId: assessor, status: ResponseTrackerStatusEnum.PENDING, }, }); @@ -160,4 +139,20 @@ export class SurveyService { } return surveyForms; } + + public async fetchUserIdsOfSurveyParticipants(surveyConfigId: number): Promise> { + try { + let userIdList: Set = new Set(); + const userMappings = await this.prisma.userMapping.findMany({ + where: { surveyConfigId }, + }); + for(const userMapping of userMappings){ + const userValidationArray = [...new Set([...userMapping.assessorIds, userMapping.assesseeId])]; + userIdList = new Set([...userIdList, ...userValidationArray]); + } + return userIdList; + } catch (error) { + throw error; + } + } } diff --git a/src/user-metadata/user-metadata.module.ts b/src/user-metadata/user-metadata.module.ts index f9f7f15..05e3742 100644 --- a/src/user-metadata/user-metadata.module.ts +++ b/src/user-metadata/user-metadata.module.ts @@ -1,13 +1,15 @@ -import { Module } from "@nestjs/common"; +import { Module, forwardRef } from "@nestjs/common"; import { MockUserService } from "src/mockModules/mock-user/mock-user.service"; import { SurveyConfigModule } from "../survey-config/survey-config.module"; import { UserMetadataController } from "./user-metadata.controller"; import { UserMetadataService } from "./user-metadata.service"; +import { PrismaService } from "../prisma/prisma.service"; +import { SurveyModule } from "../survey/survey.module"; @Module({ - imports:[SurveyConfigModule], + imports:[forwardRef(() => SurveyConfigModule), SurveyModule], controllers: [UserMetadataController], - providers: [UserMetadataService, MockUserService], + providers: [UserMetadataService, PrismaService, MockUserService], exports: [UserMetadataService], }) export class UserMetadataModule {} diff --git a/src/user-metadata/user-metadata.service.ts b/src/user-metadata/user-metadata.service.ts index c63f22d..9055020 100644 --- a/src/user-metadata/user-metadata.service.ts +++ b/src/user-metadata/user-metadata.service.ts @@ -1,46 +1,40 @@ -import { Injectable, NotFoundException } from "@nestjs/common"; +import { Inject, Injectable, NotFoundException, forwardRef } from "@nestjs/common"; import { TimeUnitsEnum, UserRolesEnum } from "@prisma/client"; import _ from "lodash"; import { MockUserService } from "../mockModules/mock-user/mock-user.service"; import { PrismaService } from "../prisma/prisma.service"; import { SurveyConfigService } from "../survey-config/survey-config.service"; import { UserMetadataFilterDto } from "./dto"; -import { AdminDepartmentService } from "../admin-department/admin-department.service"; +import { UserMappingFileUploadDto } from "../survey-config/dto/create-survey-config.dto"; +import { SurveyService } from "../survey/survey.service"; @Injectable() export class UserMetadataService { constructor( private prisma: PrismaService, private mockUser: MockUserService, + @Inject(forwardRef(()=>SurveyConfigService)) private surveyConfig: SurveyConfigService, - private adminDepartmentService: AdminDepartmentService + private surveyService: SurveyService ) {} public async syncUserDataWithFrac() { - // sync department data first before users because of table relation - await this.adminDepartmentService.syncDepartmentData(); - // fetch all users const users = await this.mockUser.findAll(); // update or create data in user metadata table const usersMetaDataPayload = await Promise.all( users.map(async (user) => { - const { Department, createdAt, designation, id, role, userName } = user; - - const isNewEmployee = await this.isEmployeeNew( - user.createdAt, - user.Department!.id - ); + const { createdAt, designation, id, role, userName, profilePicture } = user; const userMetadataPayload = { userId: id, userName, - departmentId: _.get(Department, "id", 0), designation, - isNewEmployee, + isNewEmployee: false, dateOfJoining: createdAt, isAdmin: role == _.get(UserRolesEnum, "ADMIN", "") ? true : false, + profilePicture }; return await this.prisma.userMetadata.upsert({ @@ -60,18 +54,14 @@ export class UserMetadataService { select: this.metadataSelect, }); const user = await this.mockUser.findOne(userId); - const isNewEmployee = await this.isEmployeeNew( - user.createdAt, - user.Department!.id - ); const userObj = { userId: user.id, userName: user.userName, - isNewEmployee: isNewEmployee, + isNewEmployee: false, dateOfJoining: user.createdAt, isAdmin: user.role == UserRolesEnum.ADMIN ? true : false, - departmentId: user.Department!.id, designation: user.designation, + profilePicture: user.profilePicture }; if (!userMetadata) { @@ -84,11 +74,11 @@ export class UserMetadataService { userMetadata = await this.prisma.userMetadata.update({ where: { userId }, data: { - isNewEmployee: isNewEmployee, + isNewEmployee: false, dateOfJoining: user.createdAt, isAdmin: user.role == UserRolesEnum.ADMIN ? true : false, - departmentId: user.Department!.id, designation: user.designation, + profilePicture: user.profilePicture }, select: this.metadataSelect, }); @@ -113,22 +103,28 @@ export class UserMetadataService { const { isAdmin, isNewEmployee, - departmentId, designation, - limit = 10, - offset = 0, + limit, + offset, } = filter; - return await this.prisma.userMetadata.findMany({ + let users = await this.prisma.userMetadata.findMany({ where: { isAdmin: isAdmin ?? undefined, isNewEmployee: isNewEmployee ?? undefined, - departmentId: departmentId ?? undefined, designation: designation ?? undefined, }, - skip: +offset, - take: +limit, + skip: offset ?? undefined, + take: limit ?? undefined, select: this.metadataSelect, }); + for(const user of users){ + const surveysToBeFilled = await this.surveyService.getSurveysToBeFilledByUser(user.userId); + const surveysFilled = await this.surveyService.getSurveysFilledByUser(user.userId); + user["surveysToBeFilled"] = surveysToBeFilled; + user["surveysFilled"] = surveysFilled; + } + + return users; } async deleteUserMetadata(userId: string) { @@ -140,10 +136,10 @@ export class UserMetadataService { public async isEmployeeNew( dateOfJoining: Date, - departmentId: number + configId: number ): Promise { const surveyConfig = await this.surveyConfig.getAllSurveyConfig({ - departmentId, + configId, }); if (surveyConfig.length == 0) return true; @@ -173,7 +169,6 @@ export class UserMetadataService { select: { userId: true, dateOfJoining: true, - departmentId: true, designation: true, isNewEmployee: true, SurveyForm: { @@ -182,7 +177,7 @@ export class UserMetadataService { }, select: { id: true, - surveyCycleParameterId: true, + surveyConfigId: true, questionsJson: true, status: true, overallScore: true, @@ -211,13 +206,35 @@ export class UserMetadataService { return userMetadata; } + public async validateAndFetchUserIds(prismaClient, userIdList: Set, userMapping: UserMappingFileUploadDto) { + const userValidationArray = [...new Set([...userMapping.assessorIds, userMapping.assesseeId])]; + + const idsNotInUserIdList: string[] = userValidationArray.filter(id => !userIdList.has(id)); + + for (const id of idsNotInUserIdList) { + try { + const user = await prismaClient.userMetadata.findUnique({ + where: { userId: id }, + }); + console.log({user,}) + if(!user){ + await this.createOrUpdateUserMetadata(id); + } + } catch (error) { + throw new NotFoundException(`User with id: ${id} not found.`); + } + } + + return userIdList = new Set([...userIdList, ...idsNotInUserIdList]); + } + metadataSelect = { userId: true, userName: true, dateOfJoining: true, - departmentId: true, isAdmin: true, designation: true, isNewEmployee: true, + profilePicture: true }; } diff --git a/src/utils/custom-decorators/isTwoFieldsRequired.ts b/src/utils/custom-decorators/isTwoFieldsRequired.ts new file mode 100644 index 0000000..dbb7484 --- /dev/null +++ b/src/utils/custom-decorators/isTwoFieldsRequired.ts @@ -0,0 +1,38 @@ +import { + registerDecorator, + ValidationOptions, + ValidationArguments, + ValidatorConstraint, + ValidatorConstraintInterface, + } from "class-validator"; + + @ValidatorConstraint({ async: false }) + export class TwoFieldValidator implements ValidatorConstraintInterface { + validate(value: any, args: ValidationArguments) { + const [relatedPropertyName] = args.constraints; + const relatedValue = (args.object as any)[relatedPropertyName]; + return (value && relatedValue) || (!value && !relatedValue); + } + + defaultMessage(args: ValidationArguments) { + return `Both '${args.property}' and '${args.constraints[0]}' must be given or both must be not given.`; + } + } + + export function IsTwoFieldsRequired( + propertyName: string, + relatedPropertyName: string, + validationOptions?: ValidationOptions + ) { + return function (object: Record, propertyName: string) { + registerDecorator({ + name: "isTwoFieldsRequired", + target: object.constructor, + propertyName, + constraints: [relatedPropertyName], + options: validationOptions, + validator: TwoFieldValidator, + }); + }; + } + \ No newline at end of file diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 3e5f7d6..ccd92a3 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -47,3 +47,22 @@ export function getPrismaErrorStatusAndMessage(error: any): { errorMessage: error.message, }; } + +export function isDayBeforeToday(date) { + const today = new Date(); + today.setHours(0, 0, 0, 0); + + const yesterday = new Date(date); + yesterday.setDate(yesterday.getDate() - 1); + + return yesterday.getTime() === today.getTime(); +} + +export function isToday(date) { + const today = new Date(); + today.setHours(0, 0, 0, 0); + + const givenDate = new Date(date); + + return givenDate.getTime() === today.getTime(); +} From d25c8f7db93200156f8046e04e981fc9fd17ed57 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 15 Nov 2023 11:47:23 +0530 Subject: [PATCH 121/175] Bug fixed for the API to fetch the surveys to be filled (response trackers) for a user. --- .../response-tracker.controller.ts | 18 ++++++++---------- .../response-tracker.service.ts | 16 ++++++++++------ 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/response-tracker/response-tracker.controller.ts b/src/response-tracker/response-tracker.controller.ts index 9f5903e..cf09a8f 100644 --- a/src/response-tracker/response-tracker.controller.ts +++ b/src/response-tracker/response-tracker.controller.ts @@ -164,9 +164,9 @@ export class ResponseTrackerController { } } - @Get("assessor/:assessorId/:surveyFormId") + @Get("assessor/:assessorId") @ApiOperation({ - summary: "get response tracker by assessorId & surveyFormId", + summary: "get response tracker by assessorId", }) @ApiResponse({ status: HttpStatus.OK, @@ -174,27 +174,25 @@ export class ResponseTrackerController { }) async findByAssessorId( @Res() res, - @Param("assessorId", ParseUUIDPipe) assessorId: string, - @Param("surveyFormId", ParseIntPipe) surveyFormId: number + @Param("assessorId", ParseUUIDPipe) assessorId: string ): Promise { try { this.logger.log( - `Initiated fetching response tracker with assessorId#${assessorId} and surveyFormId #${surveyFormId}` + `Initiated fetching response tracker with assessorId#${assessorId}.` ); const response = await this.responseTrackerService.findByAssessorIdAndSurveyFormId( - assessorId, - surveyFormId + assessorId ); return res.status(HttpStatus.OK).json({ data: response, - message: `Successfully fetched response tracker for assessorId #${assessorId} and surveyFormId #${surveyFormId}`, + message: `Successfully fetched response tracker for assessorId #${assessorId}`, }); } catch (error) { this.logger.error( - `Failed to fetch response tracker with assessorId #${assessorId} and surveyFormId #${surveyFormId}`, + `Failed to fetch response tracker with assessorId #${assessorId}`, error ); @@ -205,7 +203,7 @@ export class ResponseTrackerController { statusCode, message: errorMessage || - `Failed to fetch response tracker for assessorId #${assessorId} and surveyFormId #${surveyFormId}`, + `Failed to fetch response tracker for assessorId #${assessorId}`, }); } } diff --git a/src/response-tracker/response-tracker.service.ts b/src/response-tracker/response-tracker.service.ts index 47217cb..b896a8e 100644 --- a/src/response-tracker/response-tracker.service.ts +++ b/src/response-tracker/response-tracker.service.ts @@ -55,11 +55,17 @@ export class ResponseTrackerService { } public async findByAssessorIdAndSurveyFormId( - assessorId: string, - surveyFormId: number + assessorId: string ) { const response = await this.prisma.responseTracker.findMany({ - where: { assessorId, surveyFormId }, + where: { + assessorId, + surveyForm: { + SurveyConfig:{ + isActive: true, + } + } + }, include: { Assessee: { select: { @@ -82,7 +88,7 @@ export class ResponseTrackerService { if (!response || response.length === 0) throw new NotFoundException( - `Response tracker with assessorId #${assessorId} and surveyFormId #${surveyFormId} not found` + `Response tracker with assessorId #${assessorId} not found` ); return response; } @@ -228,7 +234,6 @@ export class ResponseTrackerService { public async updateBySurveyFormId( updateResponseTrackerDto: UpdateResponseTrackerDto ) { - const { surveyFormId, assesseeId, assessorId, responseJson } = updateResponseTrackerDto; const isQuestionsValid = await this.validateQuestions( @@ -272,7 +277,6 @@ export class ResponseTrackerService { }); } - public async validateQuestions( questionData: responseObject[], surveyFormId: number From c7be9d7bf8a6d5905948a3dc00f101464e20ef92 Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Wed, 15 Nov 2023 11:48:23 +0530 Subject: [PATCH 122/175] API for question bank template --- src/question-bank/question-bank.controller.ts | 12 ++-- src/question-bank/question-bank.service.ts | 71 ++++++++++++++++++- 2 files changed, 75 insertions(+), 8 deletions(-) diff --git a/src/question-bank/question-bank.controller.ts b/src/question-bank/question-bank.controller.ts index 30d4a8b..e9bef91 100644 --- a/src/question-bank/question-bank.controller.ts +++ b/src/question-bank/question-bank.controller.ts @@ -301,23 +301,23 @@ export class QuestionBankController { // API to get question bank template header @Get("/template") - @ApiOperation({ summary: "get question bank template header" }) + @ApiOperation({ summary: "get question bank template" }) @ApiResponse({ status: HttpStatus.OK }) async getQuestionBankTemplate( @Res() res ) { - this.logger.log(`Initiate to fetch the question bank template header.`); + this.logger.log(`Initiate to fetch the question bank template.`); try { const header = await this.questionBankService.getQuestionBankTemplate(); this.logger.log( - `Successfully fetch the question bank template header.` + `Successfully fetch the question bank template.` ); return res.status(HttpStatus.CREATED).json({ - message: "Successfully fetch the question bank template header.", + message: "Successfully fetch the question bank template.", data : header }); } catch (error) { - this.logger.error(`Failed to get the question bank template header.`, error); + this.logger.error(`Failed to get the question bank template.`, error); // get error message and status code const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); @@ -325,7 +325,7 @@ export class QuestionBankController { return res.status(statusCode).json({ statusCode, message: - errorMessage || `Failed to get the question bank template header.`, + errorMessage || `Failed to get the question bank template.`, }); } } diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index a8f2854..61e470a 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -363,8 +363,75 @@ export class QuestionBankService { }); } + public async getUnmappedCompetency() { + const QuestionsMappedWithCompetency = + await this.prisma.questionBank.findMany(); + // console.log("QuestionsMappedWithCompetency", QuestionsMappedWithCompetency); + const mappedCompetenciesSet = new Set( + QuestionsMappedWithCompetency.map( + (mapped) => `${mapped.competencyId}_${mapped.competencyLevelNumber}` + ) + ); + // get all competencies + const allCompetencies = + await this.competencyService.findAllCompetenciesWithLevelNames(); + + const unmappedCompetencies: { + competencyId: number; + competencyLevelNumber: number; + }[] = []; + + allCompetencies.forEach((competency) => { + const { competencyLevels } = competency; + competencyLevels.forEach(async (level) => { + const key = `${level.competencyId}_${level.competencyLevel.levelNumber}`; + + if (!mappedCompetenciesSet.has(key)) { + unmappedCompetencies.push({ + competencyId: level.competencyId, + competencyLevelNumber: level.competencyLevel.levelNumber, + }); + } + }); + }); + const data: any = []; + // Use map to create an array of promises + const promises = unmappedCompetencies.map(async (item) => { + const competency = ( + await this.competencyService.findCompetencyById(item.competencyId) + ).name; + const competencyAndLevel = { + competency, + competencyLevelNumber: item.competencyLevelNumber, + }; + return competencyAndLevel; // Return the value to the map function + }); + + try { + await Promise.all(promises) + .then((result) => { + // All asynchronous operations have completed + data.push(...result); // Push all results to the data array + }) + .catch((error) => { + // Handle errors if any of the promises reject + console.error(error); + }); + } catch (error) { + console.error("Error storing data in the database:", error); + throw new Error("Failed to store data in the database"); + } + return data; + } + public async getQuestionBankTemplate() { - const header = ["competency", "competencyLevelNu", "question"]; - return header; + const header = ["competency", "competencyLevelNumber", "question"]; + // get all unmapped competency and their competencyLevelNumber for which there + // is no any question associated with it + const unmappedCompetencies = await this.getUnmappedCompetency(); + return { + header, + unmappedCompetencies, + }; } } From 3172210746f44e249ebdd1a741c1be6097ca29ef Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 15 Nov 2023 11:47:23 +0530 Subject: [PATCH 123/175] Bug fixed for the API to fetch the surveys to be filled (response trackers) for a user. --- src/response-tracker/response-tracker.controller.ts | 2 +- src/response-tracker/response-tracker.service.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/response-tracker/response-tracker.controller.ts b/src/response-tracker/response-tracker.controller.ts index cf09a8f..7d14a42 100644 --- a/src/response-tracker/response-tracker.controller.ts +++ b/src/response-tracker/response-tracker.controller.ts @@ -182,7 +182,7 @@ export class ResponseTrackerController { ); const response = - await this.responseTrackerService.findByAssessorIdAndSurveyFormId( + await this.responseTrackerService.findByAssessorId( assessorId ); diff --git a/src/response-tracker/response-tracker.service.ts b/src/response-tracker/response-tracker.service.ts index b896a8e..b92b0da 100644 --- a/src/response-tracker/response-tracker.service.ts +++ b/src/response-tracker/response-tracker.service.ts @@ -54,7 +54,7 @@ export class ResponseTrackerService { return response; } - public async findByAssessorIdAndSurveyFormId( + public async findByAssessorId( assessorId: string ) { const response = await this.prisma.responseTracker.findMany({ From b1f70858bcc72961fb4c537b8aece2ce8585de26 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 15 Nov 2023 12:26:38 +0530 Subject: [PATCH 124/175] Bug fix for the fetch response tracker by assessorId API. --- .../response-tracker.controller.ts | 23 ++++++++----------- .../response-tracker.service.ts | 18 ++++++++------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/response-tracker/response-tracker.controller.ts b/src/response-tracker/response-tracker.controller.ts index 9f5903e..52c3a97 100644 --- a/src/response-tracker/response-tracker.controller.ts +++ b/src/response-tracker/response-tracker.controller.ts @@ -164,9 +164,9 @@ export class ResponseTrackerController { } } - @Get("assessor/:assessorId/:surveyFormId") + @Get("assessor/:assessorId") @ApiOperation({ - summary: "get response tracker by assessorId & surveyFormId", + summary: "get response tracker by assessorId", }) @ApiResponse({ status: HttpStatus.OK, @@ -174,27 +174,24 @@ export class ResponseTrackerController { }) async findByAssessorId( @Res() res, - @Param("assessorId", ParseUUIDPipe) assessorId: string, - @Param("surveyFormId", ParseIntPipe) surveyFormId: number + @Param("assessorId", ParseUUIDPipe) assessorId: string ): Promise { try { this.logger.log( - `Initiated fetching response tracker with assessorId#${assessorId} and surveyFormId #${surveyFormId}` + `Initiated fetching response tracker with assessorId#${assessorId}.` ); - const response = - await this.responseTrackerService.findByAssessorIdAndSurveyFormId( - assessorId, - surveyFormId - ); + const response = await this.responseTrackerService.findByAssessorId( + assessorId + ); return res.status(HttpStatus.OK).json({ data: response, - message: `Successfully fetched response tracker for assessorId #${assessorId} and surveyFormId #${surveyFormId}`, + message: `Successfully fetched response tracker for assessorId #${assessorId}.`, }); } catch (error) { this.logger.error( - `Failed to fetch response tracker with assessorId #${assessorId} and surveyFormId #${surveyFormId}`, + `Failed to fetch response tracker with assessorId #${assessorId}.`, error ); @@ -205,7 +202,7 @@ export class ResponseTrackerController { statusCode, message: errorMessage || - `Failed to fetch response tracker for assessorId #${assessorId} and surveyFormId #${surveyFormId}`, + `Failed to fetch response tracker for assessorId #${assessorId}.`, }); } } diff --git a/src/response-tracker/response-tracker.service.ts b/src/response-tracker/response-tracker.service.ts index cd82dca..d1edab8 100644 --- a/src/response-tracker/response-tracker.service.ts +++ b/src/response-tracker/response-tracker.service.ts @@ -54,12 +54,16 @@ export class ResponseTrackerService { return response; } - public async findByAssessorIdAndSurveyFormId( - assessorId: string, - surveyFormId: number - ) { + public async findByAssessorId(assessorId: string) { const response = await this.prisma.responseTracker.findMany({ - where: { assessorId, surveyFormId }, + where: { + assessorId, + surveyForm: { + surveyCycleParameter: { + isActive: true, + }, + }, + }, include: { Assessee: { select: { @@ -81,7 +85,7 @@ export class ResponseTrackerService { if (!response || response.length === 0) throw new NotFoundException( - `Response tracker with assessorId #${assessorId} and surveyFormId #${surveyFormId} not found` + `Response tracker with assessorId #${assessorId} not found` ); return response; } @@ -226,7 +230,6 @@ export class ResponseTrackerService { public async updateBySurveyFormId( updateResponseTrackerDto: UpdateResponseTrackerDto ) { - const { surveyFormId, assesseeId, assessorId, responseJson } = updateResponseTrackerDto; const isQuestionsValid = await this.validateQuestions( @@ -270,7 +273,6 @@ export class ResponseTrackerService { }); } - public async validateQuestions( questionData: responseObject[], surveyFormId: number From 96cb4d5bab64372904f512344c6f0ddbe9d3bee0 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 15 Nov 2023 15:47:58 +0530 Subject: [PATCH 125/175] Fixes suggested for PR. --- src/survey/survey.controller.ts | 5 +++-- src/user-metadata/user-metadata.service.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/survey/survey.controller.ts b/src/survey/survey.controller.ts index 9fa9b5b..dd32808 100644 --- a/src/survey/survey.controller.ts +++ b/src/survey/survey.controller.ts @@ -4,6 +4,7 @@ import { HttpStatus, Logger, Param, + ParseIntPipe, ParseUUIDPipe, Post, Res, @@ -24,7 +25,7 @@ export class SurveyController { @ApiResponse({ status: HttpStatus.CREATED }) async generateSurveyFormsForSurveyConfig( @Res() res, - @Param("configId") configId: number + @Param("configId", ParseIntPipe) configId: number ) { try { this.logger.log( @@ -103,7 +104,7 @@ export class SurveyController { @ApiResponse({ status: HttpStatus.OK, type: ResponseSurveyFormDto }) async getLatestSurveyResponsesForUserId( @Res() res, - @Param("userId") userId: string + @Param("userId", ParseUUIDPipe) userId: string ): Promise { try { this.logger.log( diff --git a/src/user-metadata/user-metadata.service.ts b/src/user-metadata/user-metadata.service.ts index 9055020..9dae97c 100644 --- a/src/user-metadata/user-metadata.service.ts +++ b/src/user-metadata/user-metadata.service.ts @@ -216,7 +216,7 @@ export class UserMetadataService { const user = await prismaClient.userMetadata.findUnique({ where: { userId: id }, }); - console.log({user,}) + if(!user){ await this.createOrUpdateUserMetadata(id); } From 1bfa789da04568cbe19cee6a5493afa67128394f Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Wed, 15 Nov 2023 17:18:04 +0530 Subject: [PATCH 126/175] Bug fixes for question bank bulk upload --- src/question-bank/question-bank.service.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index 61e470a..5292bc8 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -1,4 +1,4 @@ -import { Injectable, NotFoundException } from "@nestjs/common"; +import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; import _ from "lodash"; import { CreateFileUploadDto, @@ -126,6 +126,15 @@ export class QuestionBankService { } public async bulkUploadQuestions(data: CreateFileUploadDto[]) { + // check the header from the data + const headers = Object.keys(data[0]); + const requiredHeaders = ["competency", "competencyLevelNumber", "question"]; + const nonValidHeader = headers.filter( + (item) => !requiredHeaders.includes(item) + ); + if (nonValidHeader.length) { + throw new BadRequestException(`Invalid columns ${nonValidHeader}`); + } // Check if the competency Existed or not the given csv data if not then filter and throw error const allCompetencies = await this.competencyService.findAllCompetencies(); From 9b63bc1305426598b696beac100d1e18538c3449 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 15 Nov 2023 18:27:30 +0530 Subject: [PATCH 127/175] Added API to fetch WPCAS home screen data, fixed update survey config API. --- src/question-bank/question-bank.service.ts | 20 ++------ .../dto/response-response-tracker.dto.ts | 6 +++ .../response-tracker.service.ts | 46 +++++++++++++++---- src/survey-config/survey-config.controller.ts | 1 + src/survey-config/survey-config.service.ts | 25 ++++++---- src/survey/dto/index.ts | 1 + src/survey/dto/response-homescreen.dto.ts | 12 +++++ src/survey/survey.controller.ts | 29 ++++++++++++ src/survey/survey.module.ts | 15 +++--- src/survey/survey.service.ts | 20 +++++++- .../dto/user-metadata-response.dto.ts | 1 - src/user-metadata/user-metadata.module.ts | 5 +- src/user-metadata/user-metadata.service.ts | 4 +- src/utils/utils.ts | 9 ++++ 14 files changed, 151 insertions(+), 43 deletions(-) create mode 100644 src/survey/dto/index.ts create mode 100644 src/survey/dto/response-homescreen.dto.ts diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index b97649b..71bc560 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -50,13 +50,8 @@ export class QuestionBankService { async getAllQuestions(filter: QuestionBankFilterDto) { // Get all questions and filter using compentencyId and compentencyLevelId - const { - competencyId, - competencyLevelNumber, - limit, - offset, - orderBy, - } = filter; + const { competencyId, competencyLevelNumber, limit, offset, orderBy } = + filter; return this.prisma.questionBank.findMany({ where: { competencyId: competencyId ?? undefined, // Optional compentencyId filter @@ -117,16 +112,11 @@ export class QuestionBankService { } public async uploadCsvFile(filepath) { - // Parsed the uploaded data - let parsedData; try { + // Parsed the uploaded data + let parsedData; parsedData = await this.fileUploadService.parseCSV(filepath); - } catch (error) { - await this.fileUploadService.deleteUploadedFile(filepath); - throw error; - } - // Store the parsedData in the db - try { + // Store the parsedData in the db await this.bulkUploadQuestions(parsedData); } catch (error) { await this.fileUploadService.deleteUploadedFile(filepath); diff --git a/src/response-tracker/dto/response-response-tracker.dto.ts b/src/response-tracker/dto/response-response-tracker.dto.ts index 7c86d5b..096954f 100644 --- a/src/response-tracker/dto/response-response-tracker.dto.ts +++ b/src/response-tracker/dto/response-response-tracker.dto.ts @@ -21,3 +21,9 @@ export class ResponseTrackerDtoMultipleResponse { message: string; statusCode?: number; } + +export class SurveyDataResponse{ + totalSurveys: number; + totalSurveysFilled: number; + totalSurveysToBeFilled: number; +} \ No newline at end of file diff --git a/src/response-tracker/response-tracker.service.ts b/src/response-tracker/response-tracker.service.ts index b92b0da..90d712a 100644 --- a/src/response-tracker/response-tracker.service.ts +++ b/src/response-tracker/response-tracker.service.ts @@ -12,6 +12,7 @@ import { import { UpdateResponseTrackerDto } from "./dto/update-response-tracker.dto"; import { IResponseTracker } from "./interfaces/response-tracker.interface"; import { ResponseTrackerStatusEnum } from "@prisma/client"; +import { SurveyDataResponse } from "./dto"; @Injectable() export class ResponseTrackerService { @@ -54,17 +55,15 @@ export class ResponseTrackerService { return response; } - public async findByAssessorId( - assessorId: string - ) { + public async findByAssessorId(assessorId: string) { const response = await this.prisma.responseTracker.findMany({ - where: { - assessorId, + where: { + assessorId, surveyForm: { - SurveyConfig:{ + SurveyConfig: { isActive: true, - } - } + }, + }, }, include: { Assessee: { @@ -305,4 +304,35 @@ export class ResponseTrackerService { questionIds = _.sortBy(questionIds); return _.isEqual(questionIds, surveyFormQuestionIds); } + + async fetchActiveSurveyFormData(): Promise { + const isActiveSurveyCondition = { + surveyForm: { + SurveyConfig: { + isActive: true, + }, + }, + }; + + const totalSurveys = await this.getCountByStatus(isActiveSurveyCondition); + + const totalSurveysToBeFilled = await this.getCountByStatus({ + ...isActiveSurveyCondition, + status: ResponseTrackerStatusEnum.PENDING, + }); + + const totalSurveysFilled = await this.getCountByStatus({ + ...isActiveSurveyCondition, + status: ResponseTrackerStatusEnum.COMPLETED, + }); + + return { totalSurveys, totalSurveysFilled, totalSurveysToBeFilled }; + } + + async getCountByStatus(whereCondition): Promise { + console.log(whereCondition); + return await this.prisma.responseTracker.count({ + where: whereCondition, + }); + } } diff --git a/src/survey-config/survey-config.controller.ts b/src/survey-config/survey-config.controller.ts index d945aca..566a30e 100644 --- a/src/survey-config/survey-config.controller.ts +++ b/src/survey-config/survey-config.controller.ts @@ -114,6 +114,7 @@ export class SurveyConfigController { // Update the survey config @Patch("/update/:id") @ApiConsumes("multipart/form-data") + @UseInterceptors(FileInterceptor("file", multerOptions)) @ApiOperation({ summary: "Update the survey config by ID" }) // Api operation for swagger @ApiResponse({ status: HttpStatus.OK, type: ConfigResponseDTO }) // Api operation for swagger async updateSurveyConfigById( diff --git a/src/survey-config/survey-config.service.ts b/src/survey-config/survey-config.service.ts index e8a8ad7..3c8ec3b 100644 --- a/src/survey-config/survey-config.service.ts +++ b/src/survey-config/survey-config.service.ts @@ -1,6 +1,7 @@ import { Inject, Injectable, + NotAcceptableException, NotFoundException, forwardRef, } from "@nestjs/common"; @@ -13,6 +14,7 @@ import { } from "./dto/create-survey-config.dto"; import { UpdateSurveyConfigDto } from "./dto/update-survey-config.dto"; import { UserMetadataService } from "../user-metadata/user-metadata.service"; +import { isDateInPast } from "../utils/utils"; @Injectable() export class SurveyConfigService { @@ -93,18 +95,23 @@ export class SurveyConfigService { ) { let updatedConfig; let userIdList: Set = new Set(); - // Check the document available for the given surveyId in database to update. - const findSurveyConfigId = await this.prisma.surveyConfig.findUnique({ + + const surveyConfig = await this.prisma.surveyConfig.findUnique({ where: { id: surveyConfigId, }, }); - if (!findSurveyConfigId) { + if (!surveyConfig) { throw new NotFoundException( - `Survey config with ID ${findSurveyConfigId} not found.` + `Survey config with ID ${surveyConfigId} not found.` ); } - // Update the survey config by the id + if (isDateInPast(new Date(surveyConfig.startTime), new Date())) { + throw new NotAcceptableException( + `Cannot update a survey config after the startTime has already passed.` + ); + } + try { await this.prisma.$transaction(async (prismaClient) => { if (filepath != null) { @@ -127,14 +134,13 @@ export class SurveyConfigService { await prismaClient.userMapping.create({ data: { - surveyConfigId: updatedConfig.id, + surveyConfigId, assesseeId: userMapping.assesseeId, assessorIds: userMapping.assessorIds, }, }); } - delete updateSurveyConfig.file; await this.fileUploadService.deleteUploadedFile(filepath); } @@ -145,10 +151,13 @@ export class SurveyConfigService { data: updateSurveyConfig, }); }); + return updatedConfig; } catch (error) { + if (filepath) { + await this.fileUploadService.deleteUploadedFile(filepath); + } throw error; } - return updatedConfig; } async deleteSurveyConfig(surveyConfigId: number) { diff --git a/src/survey/dto/index.ts b/src/survey/dto/index.ts new file mode 100644 index 0000000..d87d520 --- /dev/null +++ b/src/survey/dto/index.ts @@ -0,0 +1 @@ +export * from "./response-homescreen.dto"; \ No newline at end of file diff --git a/src/survey/dto/response-homescreen.dto.ts b/src/survey/dto/response-homescreen.dto.ts new file mode 100644 index 0000000..85c3a94 --- /dev/null +++ b/src/survey/dto/response-homescreen.dto.ts @@ -0,0 +1,12 @@ +import { SurveyDataResponse } from "../../response-tracker/dto"; +import { ResponseUserMetadataDto } from "../../user-metadata/dto/user-metadata-response.dto"; + +export class HomeScreenResponse{ + readonly users: ResponseUserMetadataDto[]; + readonly surveyData: SurveyDataResponse; +} + +export class HomeScreenAPIResponse { + readonly data: HomeScreenResponse; + readonly message: string; +} \ No newline at end of file diff --git a/src/survey/survey.controller.ts b/src/survey/survey.controller.ts index dd32808..fefabcc 100644 --- a/src/survey/survey.controller.ts +++ b/src/survey/survey.controller.ts @@ -13,6 +13,7 @@ import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; import { SurveyService } from "./survey.service"; import { ResponseSurveyFormDto, SurveyFormResponse } from "../survey-form/dto"; +import { HomeScreenAPIResponse } from "./dto"; @Controller("survey") @ApiTags("survey") @@ -137,4 +138,32 @@ export class SurveyController { }); } } + + @Get("home-screen") + @ApiOperation({ summary: "Fetch WPCAS home screen data" }) + @ApiResponse({ status: HttpStatus.OK, type: HomeScreenAPIResponse }) + async wpcasHomeScreenApi( + @Res() res, + ): Promise { + try { + this.logger.log(`Initiated fetching Home Screen data for WPCAS.`); + + const surveyForm = await this.surveyService.wpcasHomeScreenApi(); + + this.logger.log(`Successfully fetched Home Screen data for WPCAS.`); + + return res.status(HttpStatus.OK).send({ + data: surveyForm, + message: `Successfully fetched Home Screen data for WPCAS.`, + }); + } catch (error) { + this.logger.error(`Failed to fetch Home Screen data for WPCAS.`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + return res.status(statusCode).send({ + message: errorMessage || `Could not fetch Home Screen data for WPCAS.`, + }); + } + } } diff --git a/src/survey/survey.module.ts b/src/survey/survey.module.ts index 0a5b6e8..d4fbbb1 100644 --- a/src/survey/survey.module.ts +++ b/src/survey/survey.module.ts @@ -1,17 +1,20 @@ -import { Module } from "@nestjs/common"; +import { Module, forwardRef } from "@nestjs/common"; import { SurveyFormService } from "src/survey-form/survey-form.service"; import { QuestionBankModule } from "../question-bank/question-bank.module"; import { SurveyFormModule } from "../survey-form/survey-form.module"; import { SurveyController } from "./survey.controller"; import { SurveyService } from "./survey.service"; +import { UserMetadataModule } from "../user-metadata/user-metadata.module"; +import { ResponseTrackerService } from "../response-tracker/response-tracker.service"; @Module({ - imports:[QuestionBankModule, SurveyFormModule], - providers: [ - SurveyService, - SurveyFormService, + imports: [ + QuestionBankModule, + SurveyFormModule, + forwardRef(() => UserMetadataModule), ], + providers: [SurveyService, SurveyFormService, ResponseTrackerService], controllers: [SurveyController], - exports:[SurveyService] + exports: [SurveyService], }) export class SurveyModule {} diff --git a/src/survey/survey.service.ts b/src/survey/survey.service.ts index 51e63bd..67ee9de 100644 --- a/src/survey/survey.service.ts +++ b/src/survey/survey.service.ts @@ -1,4 +1,4 @@ -import { Injectable, NotFoundException } from "@nestjs/common"; +import { Inject, Injectable, NotFoundException, forwardRef } from "@nestjs/common"; import { ResponseTrackerStatusEnum, SurveyStatusEnum } from "@prisma/client"; import { PrismaService } from "src/prisma/prisma.service"; import { @@ -8,13 +8,19 @@ import { import { SurveyFormService } from "src/survey-form/survey-form.service"; import { QuestionBankService } from "../question-bank/question-bank.service"; import _ from "lodash"; +import { UserMetadataService } from "../user-metadata/user-metadata.service"; +import { ResponseTrackerService } from "../response-tracker/response-tracker.service"; +import { HomeScreenResponse } from "./dto"; @Injectable() export class SurveyService { constructor( private prisma: PrismaService, private surveyForm: SurveyFormService, - private questionBank: QuestionBankService + private questionBank: QuestionBankService, + @Inject(forwardRef(()=>UserMetadataService)) + private userMetadata: UserMetadataService, + private responseTracker: ResponseTrackerService ) {} async getSurveysToBeFilledByUser(userId: string) { @@ -155,4 +161,14 @@ export class SurveyService { throw error; } } + + public async wpcasHomeScreenApi(): Promise{ + try { + const users = await this.userMetadata.findManyUserMetadata({}); + const surveyData = await this.responseTracker.fetchActiveSurveyFormData(); + return {users, surveyData}; + } catch (error) { + throw error; + } + } } diff --git a/src/user-metadata/dto/user-metadata-response.dto.ts b/src/user-metadata/dto/user-metadata-response.dto.ts index 71013da..b080139 100644 --- a/src/user-metadata/dto/user-metadata-response.dto.ts +++ b/src/user-metadata/dto/user-metadata-response.dto.ts @@ -3,7 +3,6 @@ import { ResponseSurveyFormDto } from "src/survey-form/dto"; export class ResponseUserMetadataDto { readonly userId: string; readonly designation?: string | null; - readonly departmentId: number; readonly isAdmin: boolean; readonly isNewEmployee: boolean; readonly dateOfJoining: Date; diff --git a/src/user-metadata/user-metadata.module.ts b/src/user-metadata/user-metadata.module.ts index 05e3742..cb31233 100644 --- a/src/user-metadata/user-metadata.module.ts +++ b/src/user-metadata/user-metadata.module.ts @@ -7,7 +7,10 @@ import { PrismaService } from "../prisma/prisma.service"; import { SurveyModule } from "../survey/survey.module"; @Module({ - imports:[forwardRef(() => SurveyConfigModule), SurveyModule], + imports: [ + forwardRef(() => SurveyConfigModule), + forwardRef(() => SurveyModule), + ], controllers: [UserMetadataController], providers: [UserMetadataService, PrismaService, MockUserService], exports: [UserMetadataService], diff --git a/src/user-metadata/user-metadata.service.ts b/src/user-metadata/user-metadata.service.ts index 9dae97c..e4734bb 100644 --- a/src/user-metadata/user-metadata.service.ts +++ b/src/user-metadata/user-metadata.service.ts @@ -15,6 +15,7 @@ export class UserMetadataService { private mockUser: MockUserService, @Inject(forwardRef(()=>SurveyConfigService)) private surveyConfig: SurveyConfigService, + @Inject(forwardRef(()=>SurveyService)) private surveyService: SurveyService ) {} @@ -234,7 +235,6 @@ export class UserMetadataService { dateOfJoining: true, isAdmin: true, designation: true, - isNewEmployee: true, - profilePicture: true + isNewEmployee: true }; } diff --git a/src/utils/utils.ts b/src/utils/utils.ts index ccd92a3..da491f5 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -66,3 +66,12 @@ export function isToday(date) { return givenDate.getTime() === today.getTime(); } + +export function isDateInPast(compareDate: Date, referenceDate: Date): boolean { + // Convert both dates to UTC to ensure accurate comparison + const compareDateUTC = new Date(compareDate.toUTCString()); + const referenceDateUTC = new Date(referenceDate.toUTCString()); + + // Compare the dates + return compareDateUTC < referenceDateUTC; +} From 11014b45a3b39a6ca2a43e73b28943ab319ac072 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 15 Nov 2023 18:47:28 +0530 Subject: [PATCH 128/175] Added API to fetch sample user mapping data. --- .../dto/response-survey-config.dto.ts | 10 ++++++ src/survey-config/survey-config.controller.ts | 34 ++++++++++++++++++- src/survey-config/survey-config.service.ts | 33 ++++++++++++++---- 3 files changed, 69 insertions(+), 8 deletions(-) diff --git a/src/survey-config/dto/response-survey-config.dto.ts b/src/survey-config/dto/response-survey-config.dto.ts index b62be45..363360b 100644 --- a/src/survey-config/dto/response-survey-config.dto.ts +++ b/src/survey-config/dto/response-survey-config.dto.ts @@ -14,3 +14,13 @@ export class ConfigResponseDTO { readonly data: ResponseSurveyConfigDto; readonly message: string; } + +export class UserMappingDTO { + assesseeId: string; + assessorIds: string; +} + +export class SampleUserMappingResponse { + data: UserMappingDTO[]; + message: string; +} diff --git a/src/survey-config/survey-config.controller.ts b/src/survey-config/survey-config.controller.ts index 566a30e..36e337f 100644 --- a/src/survey-config/survey-config.controller.ts +++ b/src/survey-config/survey-config.controller.ts @@ -21,7 +21,7 @@ import { } from "./dto/create-survey-config.dto"; import { UpdateSurveyConfigDto } from "./dto/update-survey-config.dto"; import { ApiBody, ApiConsumes, ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; -import { ConfigResponseDTO, ResponseSurveyConfigDto } from "./dto/response-survey-config.dto"; +import { ConfigResponseDTO, ResponseSurveyConfigDto, SampleUserMappingResponse } from "./dto/response-survey-config.dto"; import { getPrismaErrorStatusAndMessage } from "../utils/utils"; import { FileInterceptor } from "@nestjs/platform-express"; import { multerOptions } from "../config/multer-options.config"; @@ -184,4 +184,36 @@ export class SurveyConfigController { }); } } + + @Get("user-mapping-sample") + @ApiOperation({ summary: "Get sample user mapping data" }) // Api operation for swagger + @ApiResponse({ + status: HttpStatus.OK, + type: SampleUserMappingResponse, + isArray: true, + }) + async getUserMappingSampleForSurveyConfig( + @Res() res + ): Promise { + try { + this.logger.log(`Initiated fetching sample user mapping data.`); + const sampleData = await this.surveyConfigService.getUserMappingSampleForSurveyConfig(); + this.logger.log(`Successfully fetched sample user mapping data.`); + + return res.status(HttpStatus.OK).json({ + message: "Kindly substitute the placeholder UUIDs with authentic and valid ones.", + data: sampleData, + }); + } catch (error) { + this.logger.error(`Failed to fetch sample user mapping data`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); // get error message and status code + + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed to fetch sample user mapping data.`, + }); + } + } } diff --git a/src/survey-config/survey-config.service.ts b/src/survey-config/survey-config.service.ts index 3c8ec3b..6735601 100644 --- a/src/survey-config/survey-config.service.ts +++ b/src/survey-config/survey-config.service.ts @@ -15,6 +15,7 @@ import { import { UpdateSurveyConfigDto } from "./dto/update-survey-config.dto"; import { UserMetadataService } from "../user-metadata/user-metadata.service"; import { isDateInPast } from "../utils/utils"; +import { UserMappingDTO } from "./dto/response-survey-config.dto"; @Injectable() export class SurveyConfigService { @@ -95,7 +96,7 @@ export class SurveyConfigService { ) { let updatedConfig; let userIdList: Set = new Set(); - + const surveyConfig = await this.prisma.surveyConfig.findUnique({ where: { id: surveyConfigId, @@ -111,7 +112,7 @@ export class SurveyConfigService { `Cannot update a survey config after the startTime has already passed.` ); } - + try { await this.prisma.$transaction(async (prismaClient) => { if (filepath != null) { @@ -123,11 +124,12 @@ export class SurveyConfigService { const userMapping: UserMappingFileUploadDto = obj; try { - userIdList = await this.userMetadataService.validateAndFetchUserIds( - prismaClient, - userIdList, - userMapping - ); + userIdList = + await this.userMetadataService.validateAndFetchUserIds( + prismaClient, + userIdList, + userMapping + ); } catch (error) { throw error; } @@ -180,4 +182,21 @@ export class SurveyConfigService { }); return deletedSurveyConfig; } + + public async getUserMappingSampleForSurveyConfig(): Promise { + return [ + { + assesseeId: "8a98a623-2ad4-40af-bfb3-1a53051e89e9", + assessorIds: "8e11986e-36d4-4f7d-bf5e-b436a25ee661, 53fc7f0c-8724-4cba-80b9-3f6040e3aeae", + }, + { + assesseeId: "8e11986e-36d4-4f7d-bf5e-b436a25ee661", + assessorIds: "8a98a623-2ad4-40af-bfb3-1a53051e89e9, 53fc7f0c-8724-4cba-80b9-3f6040e3aeae", + }, + { + assesseeId: "53fc7f0c-8724-4cba-80b9-3f6040e3aeae", + assessorIds: "8a98a623-2ad4-40af-bfb3-1a53051e89e9, 8e11986e-36d4-4f7d-bf5e-b436a25ee661", + }, + ]; + } } From 429f696e756eb92e032bc8cec85ee224ffdc3bb3 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 15 Nov 2023 21:28:39 +0530 Subject: [PATCH 129/175] Fetch extra userData for fetch-latest-survey-form API. --- src/survey-form/survey-form.service.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/survey-form/survey-form.service.ts b/src/survey-form/survey-form.service.ts index 02a3bbb..e9d31aa 100644 --- a/src/survey-form/survey-form.service.ts +++ b/src/survey-form/survey-form.service.ts @@ -143,6 +143,8 @@ export class SurveyFormService { select: { userId: true, designation: true, + userName: true, + profilePicture: true, }, }, }, From fc0c79b23c98bcbcd44d0314f2df2a1c550a772e Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 15 Nov 2023 21:36:28 +0530 Subject: [PATCH 130/175] Removing default pagination applied for SurveyConfigFilterDto. --- src/survey-config/dto/create-survey-config.dto.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/survey-config/dto/create-survey-config.dto.ts b/src/survey-config/dto/create-survey-config.dto.ts index 1c6cf85..6be0694 100644 --- a/src/survey-config/dto/create-survey-config.dto.ts +++ b/src/survey-config/dto/create-survey-config.dto.ts @@ -78,12 +78,12 @@ export class SurveyConfigFilterDto { // Optional limit for pagination, validate that it's an integer. @IsOptional() @IsInt() - limit?: number = 10; + limit?: number; // Optional offset for pagination, validate that it's an integer. @IsOptional() @IsInt() - offset?: number = 0; + offset?: number; } export class UserMappingFileUploadDto { From e781d67e13381eee981ceb7921d2311b839cb668 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Thu, 16 Nov 2023 10:46:43 +0530 Subject: [PATCH 131/175] Added wpcasScore to FetchAllUserMetadata API. --- src/survey-form/survey-form.service.ts | 22 ++++++++++++++++++++++ src/user-metadata/user-metadata.module.ts | 3 ++- src/user-metadata/user-metadata.service.ts | 6 +++++- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/survey-form/survey-form.service.ts b/src/survey-form/survey-form.service.ts index e9d31aa..e041c3f 100644 --- a/src/survey-form/survey-form.service.ts +++ b/src/survey-form/survey-form.service.ts @@ -150,4 +150,26 @@ export class SurveyFormService { }, }); } + + async fetchLatestSurveyScoreByUserId(userId: string): Promise { + const latestScore = await this.prisma.surveyForm.findFirst({ + where: { + userId, + status: SurveyStatusEnum.CLOSED, + SurveyConfig: { + isActive: true, + }, + }, + orderBy:{ + createdAt: "desc" + }, + select: { + overallScore: true + }, + }); + if(!latestScore || (latestScore.overallScore != null && latestScore.overallScore<0)){ + return null; + } + return latestScore.overallScore; + } } diff --git a/src/user-metadata/user-metadata.module.ts b/src/user-metadata/user-metadata.module.ts index cb31233..8ee2ec9 100644 --- a/src/user-metadata/user-metadata.module.ts +++ b/src/user-metadata/user-metadata.module.ts @@ -5,6 +5,7 @@ import { UserMetadataController } from "./user-metadata.controller"; import { UserMetadataService } from "./user-metadata.service"; import { PrismaService } from "../prisma/prisma.service"; import { SurveyModule } from "../survey/survey.module"; +import { SurveyFormService } from "../survey-form/survey-form.service"; @Module({ imports: [ @@ -12,7 +13,7 @@ import { SurveyModule } from "../survey/survey.module"; forwardRef(() => SurveyModule), ], controllers: [UserMetadataController], - providers: [UserMetadataService, PrismaService, MockUserService], + providers: [UserMetadataService, PrismaService, MockUserService, SurveyFormService], exports: [UserMetadataService], }) export class UserMetadataModule {} diff --git a/src/user-metadata/user-metadata.service.ts b/src/user-metadata/user-metadata.service.ts index e4734bb..cf4bd07 100644 --- a/src/user-metadata/user-metadata.service.ts +++ b/src/user-metadata/user-metadata.service.ts @@ -7,6 +7,7 @@ import { SurveyConfigService } from "../survey-config/survey-config.service"; import { UserMetadataFilterDto } from "./dto"; import { UserMappingFileUploadDto } from "../survey-config/dto/create-survey-config.dto"; import { SurveyService } from "../survey/survey.service"; +import { SurveyFormService } from "../survey-form/survey-form.service"; @Injectable() export class UserMetadataService { @@ -16,7 +17,8 @@ export class UserMetadataService { @Inject(forwardRef(()=>SurveyConfigService)) private surveyConfig: SurveyConfigService, @Inject(forwardRef(()=>SurveyService)) - private surveyService: SurveyService + private surveyService: SurveyService, + private surveyForm: SurveyFormService ) {} public async syncUserDataWithFrac() { @@ -121,8 +123,10 @@ export class UserMetadataService { for(const user of users){ const surveysToBeFilled = await this.surveyService.getSurveysToBeFilledByUser(user.userId); const surveysFilled = await this.surveyService.getSurveysFilledByUser(user.userId); + const wpcasScore = await this.surveyForm.fetchLatestSurveyScoreByUserId(user.userId); user["surveysToBeFilled"] = surveysToBeFilled; user["surveysFilled"] = surveysFilled; + user["wpcasScore"] = wpcasScore; } return users; From 5717005c16f49703b8d99398693c62fb41fc2432 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 15 Nov 2023 12:26:38 +0530 Subject: [PATCH 132/175] Bug fix for the fetch response tracker by assessorId API. --- src/response-tracker/response-tracker.controller.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/response-tracker/response-tracker.controller.ts b/src/response-tracker/response-tracker.controller.ts index 7d14a42..52c3a97 100644 --- a/src/response-tracker/response-tracker.controller.ts +++ b/src/response-tracker/response-tracker.controller.ts @@ -181,18 +181,17 @@ export class ResponseTrackerController { `Initiated fetching response tracker with assessorId#${assessorId}.` ); - const response = - await this.responseTrackerService.findByAssessorId( - assessorId - ); + const response = await this.responseTrackerService.findByAssessorId( + assessorId + ); return res.status(HttpStatus.OK).json({ data: response, - message: `Successfully fetched response tracker for assessorId #${assessorId}`, + message: `Successfully fetched response tracker for assessorId #${assessorId}.`, }); } catch (error) { this.logger.error( - `Failed to fetch response tracker with assessorId #${assessorId}`, + `Failed to fetch response tracker with assessorId #${assessorId}.`, error ); @@ -203,7 +202,7 @@ export class ResponseTrackerController { statusCode, message: errorMessage || - `Failed to fetch response tracker for assessorId #${assessorId}`, + `Failed to fetch response tracker for assessorId #${assessorId}.`, }); } } From 34ff05c15d1545b126a7c28c570d3f5d307da4c8 Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Tue, 14 Nov 2023 15:13:51 +0530 Subject: [PATCH 133/175] API for question bank template --- src/config/multer-options.config.ts | 15 ++++++++ src/question-bank/question-bank.controller.ts | 35 ++++++++++++++++++- src/question-bank/question-bank.service.ts | 5 +++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/config/multer-options.config.ts b/src/config/multer-options.config.ts index 93d5fb6..442d537 100644 --- a/src/config/multer-options.config.ts +++ b/src/config/multer-options.config.ts @@ -1,11 +1,26 @@ +import { UnsupportedMediaTypeException } from "@nestjs/common"; import { MulterOptions } from "@nestjs/platform-express/multer/interfaces/multer-options.interface"; import { diskStorage } from "multer"; +export const csvFileFilter = ( + req: Request, + file: Express.Multer.File, + callback +) => { + if (!file.originalname.match(/\.(csv)$/)) { + return callback( + new UnsupportedMediaTypeException("Only csv files are allowed!"), + false + ); + } + callback(null, true); +}; export const multerOptions: MulterOptions = { dest: "src/uploads", // this is the destination of the uploaded files on disk limits: { fileSize: 1024 * 1024 * 5, // max size 5MB }, + fileFilter: csvFileFilter, storage: diskStorage({ destination: "src/uploads", filename: (_req, file, cb) => { diff --git a/src/question-bank/question-bank.controller.ts b/src/question-bank/question-bank.controller.ts index 2854b1b..30d4a8b 100644 --- a/src/question-bank/question-bank.controller.ts +++ b/src/question-bank/question-bank.controller.ts @@ -279,7 +279,9 @@ export class QuestionBankController { // Log the initiaton for csv file upload this.logger.log(`Initiate updating the question bank.`); try { - await this.questionBankService.createUpdateDeleteQuesitons(createUpdateDeleteQuesitonsDto); + await this.questionBankService.createUpdateDeleteQuesitons( + createUpdateDeleteQuesitonsDto + ); this.logger.log(`Successfully updated the question bank.`); return res.status(HttpStatus.CREATED).json({ message: "Question bank updated sucessfully.", @@ -296,4 +298,35 @@ export class QuestionBankController { }); } } + + // API to get question bank template header + @Get("/template") + @ApiOperation({ summary: "get question bank template header" }) + @ApiResponse({ status: HttpStatus.OK }) + async getQuestionBankTemplate( + @Res() res + ) { + this.logger.log(`Initiate to fetch the question bank template header.`); + try { + const header = await this.questionBankService.getQuestionBankTemplate(); + this.logger.log( + `Successfully fetch the question bank template header.` + ); + return res.status(HttpStatus.CREATED).json({ + message: "Successfully fetch the question bank template header.", + data : header + }); + } catch (error) { + this.logger.error(`Failed to get the question bank template header.`, error); + // get error message and status code + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + // Return an error response + return res.status(statusCode).json({ + statusCode, + message: + errorMessage || `Failed to get the question bank template header.`, + }); + } + } } diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index 71bc560..bc58d1d 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -363,4 +363,9 @@ export class QuestionBankService { } }); } + + public async getQuestionBankTemplate() { + const header = ["competency", "competencyLevelNumber", "question"]; + return header; + } } From 5da8adec310ee09114b6df8f8a1738c546668e05 Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Tue, 14 Nov 2023 15:17:20 +0530 Subject: [PATCH 134/175] API for question bank template --- src/config/multer-options.config.ts | 15 -------- src/question-bank/question-bank.controller.ts | 37 +------------------ src/question-bank/question-bank.service.ts | 5 --- 3 files changed, 2 insertions(+), 55 deletions(-) diff --git a/src/config/multer-options.config.ts b/src/config/multer-options.config.ts index 442d537..93d5fb6 100644 --- a/src/config/multer-options.config.ts +++ b/src/config/multer-options.config.ts @@ -1,26 +1,11 @@ -import { UnsupportedMediaTypeException } from "@nestjs/common"; import { MulterOptions } from "@nestjs/platform-express/multer/interfaces/multer-options.interface"; import { diskStorage } from "multer"; -export const csvFileFilter = ( - req: Request, - file: Express.Multer.File, - callback -) => { - if (!file.originalname.match(/\.(csv)$/)) { - return callback( - new UnsupportedMediaTypeException("Only csv files are allowed!"), - false - ); - } - callback(null, true); -}; export const multerOptions: MulterOptions = { dest: "src/uploads", // this is the destination of the uploaded files on disk limits: { fileSize: 1024 * 1024 * 5, // max size 5MB }, - fileFilter: csvFileFilter, storage: diskStorage({ destination: "src/uploads", filename: (_req, file, cb) => { diff --git a/src/question-bank/question-bank.controller.ts b/src/question-bank/question-bank.controller.ts index 30d4a8b..87cf98a 100644 --- a/src/question-bank/question-bank.controller.ts +++ b/src/question-bank/question-bank.controller.ts @@ -211,7 +211,7 @@ export class QuestionBankController { // Log the initiaton for csv file upload this.logger.log(`Initiate to upload a csv file.`); try { - await this.questionBankService.uploadCsvFile(file.path); + this.questionBankService.uploadCsvFile(file.path); this.logger.log(`Successfully uploaded question bank.`); return res.status(HttpStatus.CREATED).json({ message: "Question bank uploaded sucessfully.", @@ -279,9 +279,7 @@ export class QuestionBankController { // Log the initiaton for csv file upload this.logger.log(`Initiate updating the question bank.`); try { - await this.questionBankService.createUpdateDeleteQuesitons( - createUpdateDeleteQuesitonsDto - ); + await this.questionBankService.createUpdateDeleteQuesitons(createUpdateDeleteQuesitonsDto); this.logger.log(`Successfully updated the question bank.`); return res.status(HttpStatus.CREATED).json({ message: "Question bank updated sucessfully.", @@ -298,35 +296,4 @@ export class QuestionBankController { }); } } - - // API to get question bank template header - @Get("/template") - @ApiOperation({ summary: "get question bank template header" }) - @ApiResponse({ status: HttpStatus.OK }) - async getQuestionBankTemplate( - @Res() res - ) { - this.logger.log(`Initiate to fetch the question bank template header.`); - try { - const header = await this.questionBankService.getQuestionBankTemplate(); - this.logger.log( - `Successfully fetch the question bank template header.` - ); - return res.status(HttpStatus.CREATED).json({ - message: "Successfully fetch the question bank template header.", - data : header - }); - } catch (error) { - this.logger.error(`Failed to get the question bank template header.`, error); - // get error message and status code - const { errorMessage, statusCode } = - getPrismaErrorStatusAndMessage(error); - // Return an error response - return res.status(statusCode).json({ - statusCode, - message: - errorMessage || `Failed to get the question bank template header.`, - }); - } - } } diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index bc58d1d..71bc560 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -363,9 +363,4 @@ export class QuestionBankService { } }); } - - public async getQuestionBankTemplate() { - const header = ["competency", "competencyLevelNumber", "question"]; - return header; - } } From 0ec49a1057881dd83fe6bf1c5a533fa3e8226d65 Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Tue, 14 Nov 2023 15:21:10 +0530 Subject: [PATCH 135/175] Question bank template API --- src/config/multer-options.config.ts | 15 ++++++++ src/question-bank/question-bank.controller.ts | 37 ++++++++++++++++++- src/question-bank/question-bank.service.ts | 5 +++ 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/config/multer-options.config.ts b/src/config/multer-options.config.ts index 93d5fb6..442d537 100644 --- a/src/config/multer-options.config.ts +++ b/src/config/multer-options.config.ts @@ -1,11 +1,26 @@ +import { UnsupportedMediaTypeException } from "@nestjs/common"; import { MulterOptions } from "@nestjs/platform-express/multer/interfaces/multer-options.interface"; import { diskStorage } from "multer"; +export const csvFileFilter = ( + req: Request, + file: Express.Multer.File, + callback +) => { + if (!file.originalname.match(/\.(csv)$/)) { + return callback( + new UnsupportedMediaTypeException("Only csv files are allowed!"), + false + ); + } + callback(null, true); +}; export const multerOptions: MulterOptions = { dest: "src/uploads", // this is the destination of the uploaded files on disk limits: { fileSize: 1024 * 1024 * 5, // max size 5MB }, + fileFilter: csvFileFilter, storage: diskStorage({ destination: "src/uploads", filename: (_req, file, cb) => { diff --git a/src/question-bank/question-bank.controller.ts b/src/question-bank/question-bank.controller.ts index 87cf98a..30d4a8b 100644 --- a/src/question-bank/question-bank.controller.ts +++ b/src/question-bank/question-bank.controller.ts @@ -211,7 +211,7 @@ export class QuestionBankController { // Log the initiaton for csv file upload this.logger.log(`Initiate to upload a csv file.`); try { - this.questionBankService.uploadCsvFile(file.path); + await this.questionBankService.uploadCsvFile(file.path); this.logger.log(`Successfully uploaded question bank.`); return res.status(HttpStatus.CREATED).json({ message: "Question bank uploaded sucessfully.", @@ -279,7 +279,9 @@ export class QuestionBankController { // Log the initiaton for csv file upload this.logger.log(`Initiate updating the question bank.`); try { - await this.questionBankService.createUpdateDeleteQuesitons(createUpdateDeleteQuesitonsDto); + await this.questionBankService.createUpdateDeleteQuesitons( + createUpdateDeleteQuesitonsDto + ); this.logger.log(`Successfully updated the question bank.`); return res.status(HttpStatus.CREATED).json({ message: "Question bank updated sucessfully.", @@ -296,4 +298,35 @@ export class QuestionBankController { }); } } + + // API to get question bank template header + @Get("/template") + @ApiOperation({ summary: "get question bank template header" }) + @ApiResponse({ status: HttpStatus.OK }) + async getQuestionBankTemplate( + @Res() res + ) { + this.logger.log(`Initiate to fetch the question bank template header.`); + try { + const header = await this.questionBankService.getQuestionBankTemplate(); + this.logger.log( + `Successfully fetch the question bank template header.` + ); + return res.status(HttpStatus.CREATED).json({ + message: "Successfully fetch the question bank template header.", + data : header + }); + } catch (error) { + this.logger.error(`Failed to get the question bank template header.`, error); + // get error message and status code + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + // Return an error response + return res.status(statusCode).json({ + statusCode, + message: + errorMessage || `Failed to get the question bank template header.`, + }); + } + } } diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index 71bc560..2637a56 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -363,4 +363,9 @@ export class QuestionBankService { } }); } + + public async getQuestionBankTemplate() { + const header = ["competency", "competencyLevelNu", "question"]; + return header; + } } From 748d921549fc5c3f0a6593142b1bffbcf7578e0d Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Wed, 15 Nov 2023 11:48:23 +0530 Subject: [PATCH 136/175] API for question bank template --- src/question-bank/question-bank.controller.ts | 12 ++-- src/question-bank/question-bank.service.ts | 71 ++++++++++++++++++- 2 files changed, 75 insertions(+), 8 deletions(-) diff --git a/src/question-bank/question-bank.controller.ts b/src/question-bank/question-bank.controller.ts index 30d4a8b..e9bef91 100644 --- a/src/question-bank/question-bank.controller.ts +++ b/src/question-bank/question-bank.controller.ts @@ -301,23 +301,23 @@ export class QuestionBankController { // API to get question bank template header @Get("/template") - @ApiOperation({ summary: "get question bank template header" }) + @ApiOperation({ summary: "get question bank template" }) @ApiResponse({ status: HttpStatus.OK }) async getQuestionBankTemplate( @Res() res ) { - this.logger.log(`Initiate to fetch the question bank template header.`); + this.logger.log(`Initiate to fetch the question bank template.`); try { const header = await this.questionBankService.getQuestionBankTemplate(); this.logger.log( - `Successfully fetch the question bank template header.` + `Successfully fetch the question bank template.` ); return res.status(HttpStatus.CREATED).json({ - message: "Successfully fetch the question bank template header.", + message: "Successfully fetch the question bank template.", data : header }); } catch (error) { - this.logger.error(`Failed to get the question bank template header.`, error); + this.logger.error(`Failed to get the question bank template.`, error); // get error message and status code const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); @@ -325,7 +325,7 @@ export class QuestionBankController { return res.status(statusCode).json({ statusCode, message: - errorMessage || `Failed to get the question bank template header.`, + errorMessage || `Failed to get the question bank template.`, }); } } diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index 2637a56..396bf1e 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -364,8 +364,75 @@ export class QuestionBankService { }); } + public async getUnmappedCompetency() { + const QuestionsMappedWithCompetency = + await this.prisma.questionBank.findMany(); + // console.log("QuestionsMappedWithCompetency", QuestionsMappedWithCompetency); + const mappedCompetenciesSet = new Set( + QuestionsMappedWithCompetency.map( + (mapped) => `${mapped.competencyId}_${mapped.competencyLevelNumber}` + ) + ); + // get all competencies + const allCompetencies = + await this.competencyService.findAllCompetenciesWithLevelNames(); + + const unmappedCompetencies: { + competencyId: number; + competencyLevelNumber: number; + }[] = []; + + allCompetencies.forEach((competency) => { + const { competencyLevels } = competency; + competencyLevels.forEach(async (level) => { + const key = `${level.competencyId}_${level.competencyLevel.levelNumber}`; + + if (!mappedCompetenciesSet.has(key)) { + unmappedCompetencies.push({ + competencyId: level.competencyId, + competencyLevelNumber: level.competencyLevel.levelNumber, + }); + } + }); + }); + const data: any = []; + // Use map to create an array of promises + const promises = unmappedCompetencies.map(async (item) => { + const competency = ( + await this.competencyService.findCompetencyById(item.competencyId) + ).name; + const competencyAndLevel = { + competency, + competencyLevelNumber: item.competencyLevelNumber, + }; + return competencyAndLevel; // Return the value to the map function + }); + + try { + await Promise.all(promises) + .then((result) => { + // All asynchronous operations have completed + data.push(...result); // Push all results to the data array + }) + .catch((error) => { + // Handle errors if any of the promises reject + console.error(error); + }); + } catch (error) { + console.error("Error storing data in the database:", error); + throw new Error("Failed to store data in the database"); + } + return data; + } + public async getQuestionBankTemplate() { - const header = ["competency", "competencyLevelNu", "question"]; - return header; + const header = ["competency", "competencyLevelNumber", "question"]; + // get all unmapped competency and their competencyLevelNumber for which there + // is no any question associated with it + const unmappedCompetencies = await this.getUnmappedCompetency(); + return { + header, + unmappedCompetencies, + }; } } From d057d2271f62c30eabcbc85022c62acad8eb1719 Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Wed, 15 Nov 2023 17:18:04 +0530 Subject: [PATCH 137/175] Bug fixes for question bank bulk upload --- src/question-bank/question-bank.service.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index 396bf1e..27724b5 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -1,4 +1,4 @@ -import { Injectable, NotFoundException } from "@nestjs/common"; +import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; import _ from "lodash"; import { CreateFileUploadDto, @@ -127,6 +127,15 @@ export class QuestionBankService { } public async bulkUploadQuestions(data: CreateFileUploadDto[]) { + // check the header from the data + const headers = Object.keys(data[0]); + const requiredHeaders = ["competency", "competencyLevelNumber", "question"]; + const nonValidHeader = headers.filter( + (item) => !requiredHeaders.includes(item) + ); + if (nonValidHeader.length) { + throw new BadRequestException(`Invalid columns ${nonValidHeader}`); + } // Check if the competency Existed or not the given csv data if not then filter and throw error const allCompetencies = await this.competencyService.findAllCompetencies(); From 0df3b81a440452e9a615772b3ad3b1d2a7edc925 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 15 Nov 2023 09:47:36 +0530 Subject: [PATCH 138/175] Minor schema changes. And survey logic updates after removing department. --- src/question-bank/question-bank.service.ts | 9 +++++++-- src/survey-config/dto/create-survey-config.dto.ts | 12 ++++++++++++ src/survey-config/survey-config.service.ts | 11 +++++------ 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index 27724b5..ab1e6ed 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -50,8 +50,13 @@ export class QuestionBankService { async getAllQuestions(filter: QuestionBankFilterDto) { // Get all questions and filter using compentencyId and compentencyLevelId - const { competencyId, competencyLevelNumber, limit, offset, orderBy } = - filter; + const { + competencyId, + competencyLevelNumber, + limit, + offset, + orderBy, + } = filter; return this.prisma.questionBank.findMany({ where: { competencyId: competencyId ?? undefined, // Optional compentencyId filter diff --git a/src/survey-config/dto/create-survey-config.dto.ts b/src/survey-config/dto/create-survey-config.dto.ts index 6be0694..d94078f 100644 --- a/src/survey-config/dto/create-survey-config.dto.ts +++ b/src/survey-config/dto/create-survey-config.dto.ts @@ -97,3 +97,15 @@ export class UserMappingFileUploadDto { @IsUUID("all", { each: true }) assessorIds: UUID[]; } + +export class UserMappingFileUploadDto { + @IsNotEmpty() + @IsUUID() + assesseeId: string; + + @IsNotEmpty() + @IsArray() + @ArrayNotEmpty() + @IsUUID("all", { each: true }) + assessorIds: UUID[]; +} diff --git a/src/survey-config/survey-config.service.ts b/src/survey-config/survey-config.service.ts index 6735601..e7eb586 100644 --- a/src/survey-config/survey-config.service.ts +++ b/src/survey-config/survey-config.service.ts @@ -124,12 +124,11 @@ export class SurveyConfigService { const userMapping: UserMappingFileUploadDto = obj; try { - userIdList = - await this.userMetadataService.validateAndFetchUserIds( - prismaClient, - userIdList, - userMapping - ); + userIdList = await this.userMetadataService.validateAndFetchUserIds( + prismaClient, + userIdList, + userMapping + ); } catch (error) { throw error; } From 9cbb617e3fc35a32f44be1554175a8b693087b8e Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 15 Nov 2023 11:47:23 +0530 Subject: [PATCH 139/175] Bug fixed for the API to fetch the surveys to be filled (response trackers) for a user. --- src/response-tracker/response-tracker.service.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/response-tracker/response-tracker.service.ts b/src/response-tracker/response-tracker.service.ts index 90d712a..c28ecc5 100644 --- a/src/response-tracker/response-tracker.service.ts +++ b/src/response-tracker/response-tracker.service.ts @@ -55,15 +55,17 @@ export class ResponseTrackerService { return response; } - public async findByAssessorId(assessorId: string) { + public async findByAssessorIdAndSurveyFormId( + assessorId: string + ) { const response = await this.prisma.responseTracker.findMany({ - where: { - assessorId, + where: { + assessorId, surveyForm: { SurveyConfig: { isActive: true, - }, - }, + } + } }, include: { Assessee: { From bc3d6f06e3a8c0028f5de7dc3e864c728379ae32 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 15 Nov 2023 11:47:23 +0530 Subject: [PATCH 140/175] Bug fixed for the API to fetch the surveys to be filled (response trackers) for a user. --- src/response-tracker/response-tracker.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/response-tracker/response-tracker.service.ts b/src/response-tracker/response-tracker.service.ts index c28ecc5..f2eef60 100644 --- a/src/response-tracker/response-tracker.service.ts +++ b/src/response-tracker/response-tracker.service.ts @@ -55,7 +55,7 @@ export class ResponseTrackerService { return response; } - public async findByAssessorIdAndSurveyFormId( + public async findByAssessorId( assessorId: string ) { const response = await this.prisma.responseTracker.findMany({ From 48f44022952213a82b71b261875116c410424216 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Fri, 17 Nov 2023 10:52:59 +0530 Subject: [PATCH 141/175] Minor code fixes after a PR review. --- src/response-tracker/response-tracker.service.ts | 1 - src/survey/survey.service.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/response-tracker/response-tracker.service.ts b/src/response-tracker/response-tracker.service.ts index f2eef60..8005583 100644 --- a/src/response-tracker/response-tracker.service.ts +++ b/src/response-tracker/response-tracker.service.ts @@ -332,7 +332,6 @@ export class ResponseTrackerService { } async getCountByStatus(whereCondition): Promise { - console.log(whereCondition); return await this.prisma.responseTracker.count({ where: whereCondition, }); diff --git a/src/survey/survey.service.ts b/src/survey/survey.service.ts index 67ee9de..46532a8 100644 --- a/src/survey/survey.service.ts +++ b/src/survey/survey.service.ts @@ -112,7 +112,7 @@ export class SurveyService { }); if (_.isUndefined(userMapping) || _.isEmpty(userMapping)) { throw new NotFoundException( - `No user mapping found for Survey Ci=onfig with id ${configId}` + `No user mapping found for Survey Config with id ${configId}` ); } From f2bb5892dbd4b06f709bd709fd4df0f77662e485 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Fri, 17 Nov 2023 11:09:59 +0530 Subject: [PATCH 142/175] Removed duplicate "UserMappingFileUploadDto". --- src/survey-config/dto/create-survey-config.dto.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/survey-config/dto/create-survey-config.dto.ts b/src/survey-config/dto/create-survey-config.dto.ts index d94078f..6be0694 100644 --- a/src/survey-config/dto/create-survey-config.dto.ts +++ b/src/survey-config/dto/create-survey-config.dto.ts @@ -97,15 +97,3 @@ export class UserMappingFileUploadDto { @IsUUID("all", { each: true }) assessorIds: UUID[]; } - -export class UserMappingFileUploadDto { - @IsNotEmpty() - @IsUUID() - assesseeId: string; - - @IsNotEmpty() - @IsArray() - @ArrayNotEmpty() - @IsUUID("all", { each: true }) - assessorIds: UUID[]; -} From 470f7dd066d6e5f35c845b510702b9b69a9c7d9c Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Fri, 17 Nov 2023 12:40:55 +0530 Subject: [PATCH 143/175] Remove whitespaces while formatting the assessorIds string fetched from the userMapping csv. --- src/survey-config/survey-config.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/survey-config/survey-config.service.ts b/src/survey-config/survey-config.service.ts index e7eb586..777b9ac 100644 --- a/src/survey-config/survey-config.service.ts +++ b/src/survey-config/survey-config.service.ts @@ -44,7 +44,7 @@ export class SurveyConfigService { const parsedData = await this.fileUploadService.parseCSV(filepath); for (const obj of parsedData) { - obj.assessorIds = obj.assessorIds.split(", "); + obj.assessorIds = obj.assessorIds.replace(/\s/g, '').split(","); const userMapping: UserMappingFileUploadDto = obj; @@ -119,7 +119,7 @@ export class SurveyConfigService { const parsedData = await this.fileUploadService.parseCSV(filepath); for (const obj of parsedData) { - obj.assessorIds = obj.assessorIds.split(", "); + obj.assessorIds = obj.assessorIds.replace(/\s/g, '').split(","); const userMapping: UserMappingFileUploadDto = obj; From 322a4898ea1659236cf5d1c01ec28f04a04e8342 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Fri, 17 Nov 2023 13:10:31 +0530 Subject: [PATCH 144/175] Delete existing userMapping when updating a surveyConfig if the userMapping is provided oncemore. --- src/survey-config/survey-config.service.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/survey-config/survey-config.service.ts b/src/survey-config/survey-config.service.ts index 777b9ac..e8b9182 100644 --- a/src/survey-config/survey-config.service.ts +++ b/src/survey-config/survey-config.service.ts @@ -118,6 +118,12 @@ export class SurveyConfigService { if (filepath != null) { const parsedData = await this.fileUploadService.parseCSV(filepath); + await prismaClient.userMapping.deleteMany({ + where:{ + surveyConfigId + } + }); + for (const obj of parsedData) { obj.assessorIds = obj.assessorIds.replace(/\s/g, '').split(","); From ee0dfefdded79d4a33e8636d79c06cce7265390d Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Fri, 17 Nov 2023 13:12:00 +0530 Subject: [PATCH 145/175] Added a check to determine if the survey is active or not for generateSurveyFormsForSurveyConfig API. --- src/survey/survey.module.ts | 6 ++++-- src/survey/survey.service.ts | 12 ++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/survey/survey.module.ts b/src/survey/survey.module.ts index d4fbbb1..4845a5f 100644 --- a/src/survey/survey.module.ts +++ b/src/survey/survey.module.ts @@ -1,17 +1,19 @@ import { Module, forwardRef } from "@nestjs/common"; import { SurveyFormService } from "src/survey-form/survey-form.service"; import { QuestionBankModule } from "../question-bank/question-bank.module"; +import { ResponseTrackerService } from "../response-tracker/response-tracker.service"; +import { SurveyConfigModule } from "../survey-config/survey-config.module"; import { SurveyFormModule } from "../survey-form/survey-form.module"; +import { UserMetadataModule } from "../user-metadata/user-metadata.module"; import { SurveyController } from "./survey.controller"; import { SurveyService } from "./survey.service"; -import { UserMetadataModule } from "../user-metadata/user-metadata.module"; -import { ResponseTrackerService } from "../response-tracker/response-tracker.service"; @Module({ imports: [ QuestionBankModule, SurveyFormModule, forwardRef(() => UserMetadataModule), + forwardRef(() => SurveyConfigModule) ], providers: [SurveyService, SurveyFormService, ResponseTrackerService], controllers: [SurveyController], diff --git a/src/survey/survey.service.ts b/src/survey/survey.service.ts index 46532a8..c97eb24 100644 --- a/src/survey/survey.service.ts +++ b/src/survey/survey.service.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable, NotFoundException, forwardRef } from "@nestjs/common"; +import { Inject, Injectable, NotAcceptableException, NotFoundException, forwardRef } from "@nestjs/common"; import { ResponseTrackerStatusEnum, SurveyStatusEnum } from "@prisma/client"; import { PrismaService } from "src/prisma/prisma.service"; import { @@ -11,6 +11,7 @@ import _ from "lodash"; import { UserMetadataService } from "../user-metadata/user-metadata.service"; import { ResponseTrackerService } from "../response-tracker/response-tracker.service"; import { HomeScreenResponse } from "./dto"; +import { SurveyConfigService } from "../survey-config/survey-config.service"; @Injectable() export class SurveyService { @@ -20,7 +21,8 @@ export class SurveyService { private questionBank: QuestionBankService, @Inject(forwardRef(()=>UserMetadataService)) private userMetadata: UserMetadataService, - private responseTracker: ResponseTrackerService + private responseTracker: ResponseTrackerService, + private surveyConfig: SurveyConfigService ) {} async getSurveysToBeFilledByUser(userId: string) { @@ -104,6 +106,12 @@ export class SurveyService { async generateSurveyFormsForSurveyConfig(configId: number) { let surveyForms: ResponseSurveyFormDto[] = []; + const surveyConfig = await this.surveyConfig.getAllSurveyConfig({configId: configId}); + + if(surveyConfig[0].isActive == false) { + throw new NotAcceptableException(`The survey with name: ${surveyConfig[0].surveyName} is not 'ACTIVE', thus cannot create the surveyForms.`); + } + //logic to fetch user mapping const userMapping = await this.prisma.userMapping.findMany({ where: { From dbb36bc38f31e62dcde6a377bc7ab8209b16dbe8 Mon Sep 17 00:00:00 2001 From: Aman Agarwal Date: Mon, 20 Nov 2023 19:29:26 +0530 Subject: [PATCH 146/175] adding dockerfile fixes --- Dockerfile | 21 +- docker-compose.yaml | 43 ++-- env-example | 2 +- package.json | 2 +- .../20231120091730_test/migration.sql | 243 ++++++++++++++++++ prisma/migrations/migration_lock.toml | 3 + 6 files changed, 266 insertions(+), 48 deletions(-) create mode 100644 prisma/migrations/20231120091730_test/migration.sql create mode 100644 prisma/migrations/migration_lock.toml diff --git a/Dockerfile b/Dockerfile index c3a4084..953a06b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,22 +1,3 @@ -# RUN apk add --no-cache bash -# RUN npm i -g @nestjs/cli typescript ts-node - -# COPY package*.json /tmp/app/ -# RUN cd /tmp/app && npm install - -# COPY . /usr/src/app -# RUN cp -a /tmp/app/node_modules /usr/src/app -# COPY ./wait-for-it.sh /opt/wait-for-it.sh -# COPY ./startup.dev.sh /opt/startup.dev.sh -# RUN sed -i 's/\r//g' /opt/wait-for-it.sh -# RUN sed -i 's/\r//g' /opt/startup.dev.sh - -# WORKDIR /usr/src/app -# RUN cp env-example .env - -# RUN npm run build - -# CMD ["/opt/startup.dev.sh"] # Use the official Node.js 18 image as a base FROM node:18.16.1-alpine @@ -36,7 +17,7 @@ COPY package*.json ./ # Copy the Prisma configuration and migration files # This line copies the "prisma" directory from your project's root into the Docker container's working directory. COPY prisma ./prisma/ - +COPY env-example ./.env # Install project dependencies RUN npm install diff --git a/docker-compose.yaml b/docker-compose.yaml index 56df483..b18f3ee 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,4 +1,17 @@ +version: '3' + services: + api: + build: + context: . + dockerfile: Dockerfile + ports: + - ${APP_PORT}:${APP_PORT} + networks: + - mynetwork + depends_on: + - postgres + postgres: image: postgres:15.3-alpine ports: @@ -9,30 +22,8 @@ services: POSTGRES_USER: ${DATABASE_USERNAME} POSTGRES_PASSWORD: ${DATABASE_PASSWORD} POSTGRES_DB: ${DATABASE_NAME} + networks: + - mynetwork - # maildev: - # build: - # context: . - # dockerfile: maildev.Dockerfile - # ports: - # - ${MAIL_CLIENT_PORT}:1080 - # - ${MAIL_PORT}:1025 - - # adminer: - # image: adminer - # restart: always - # ports: - # - 8080:8080 - - # Uncomment to use redis - # redis: - # image: redis:7-alpine - # ports: - # - 6379:6379 - - api: - build: - context: . - dockerfile: Dockerfile - ports: - - ${APP_PORT}:${APP_PORT} +networks: + mynetwork: diff --git a/env-example b/env-example index e3eda1f..f1e6b4d 100644 --- a/env-example +++ b/env-example @@ -7,4 +7,4 @@ DATABASE_USERNAME=postgres DATABASE_PASSWORD=secret DATABASE_NAME=service-db DATABASE_PORT=5432 -DATABASE_URL="" +DATABASE_URL="postgresql://postgres:secret@postgres:5432/service-db?sslmode=prefer" diff --git a/package.json b/package.json index 1e5ea6f..8652fa0 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "start": "nest start", "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", - "start:prod": "node dist/main", + "start:prod": "npx prisma migrate deploy && node dist/main", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", "test": "jest", "test:watch": "jest --watch", diff --git a/prisma/migrations/20231120091730_test/migration.sql b/prisma/migrations/20231120091730_test/migration.sql new file mode 100644 index 0000000..fafe2c8 --- /dev/null +++ b/prisma/migrations/20231120091730_test/migration.sql @@ -0,0 +1,243 @@ +-- CreateEnum +CREATE TYPE "SurveyStatusEnum" AS ENUM ('CREATED', 'PUBLISHED'); + +-- CreateEnum +CREATE TYPE "ResponseTrackerStatusEnum" AS ENUM ('PENDING', 'COMPLETED'); + +-- CreateEnum +CREATE TYPE "TimeUnitsEnum" AS ENUM ('DAY', 'MONTH', 'YEAR'); + +-- CreateEnum +CREATE TYPE "UserRolesEnum" AS ENUM ('ADMIN', 'COMSUMER'); + +-- CreateTable +CREATE TABLE "UserMetadata" ( + "id" SERIAL NOT NULL, + "userId" INTEGER NOT NULL, + "teamId" INTEGER NOT NULL, + "isNewEmployee" BOOLEAN NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "UserMetadata_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "question_bank" ( + "id" SERIAL NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "competencyId" INTEGER NOT NULL, + "competencyLevelId" TEXT NOT NULL, + "question" TEXT NOT NULL, + "levelId" INTEGER NOT NULL, + + CONSTRAINT "question_bank_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "survey_config" ( + "id" SERIAL NOT NULL, + "maxQuestions" INTEGER NOT NULL, + "startTime" TIMESTAMP(3) NOT NULL, + "endTime" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "survey_config_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "survey_parameters" ( + "id" SERIAL NOT NULL, + "onboardingTime" INTEGER NOT NULL, + "onboardingTimeUnit" "TimeUnitsEnum" NOT NULL, + "surveyCycle" INTEGER NOT NULL, + + CONSTRAINT "survey_parameters_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "survey_form" ( + "id" SERIAL NOT NULL, + "userId" INTEGER NOT NULL, + "questionsJson" JSONB NOT NULL, + "surveyConfigId" INTEGER NOT NULL, + "status" "SurveyStatusEnum" NOT NULL, + + CONSTRAINT "survey_form_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "response_tracker" ( + "id" SERIAL NOT NULL, + "surveyFormId" INTEGER NOT NULL, + "assesseeId" INTEGER NOT NULL, + "assessorId" INTEGER NOT NULL, + "responseJson" JSONB, + "status" "ResponseTrackerStatusEnum" NOT NULL, + + CONSTRAINT "response_tracker_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "survey_scores" ( + "id" SERIAL NOT NULL, + "userId" INTEGER NOT NULL, + "surveyFormId" INTEGER NOT NULL, + "roleId" INTEGER, + "competencyId" INTEGER NOT NULL, + "competencyLevelId" INTEGER NOT NULL, + "score" INTEGER NOT NULL, + + CONSTRAINT "survey_scores_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "users" ( + "id" SERIAL NOT NULL, + "email" TEXT NOT NULL, + "role" "UserRolesEnum" NOT NULL, + "username" TEXT NOT NULL, + "password" TEXT NOT NULL, + "profilePicture" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "levelId" INTEGER, + "teamId" INTEGER, + + CONSTRAINT "users_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Level" ( + "id" SERIAL NOT NULL, + "lable" TEXT NOT NULL, + "description" TEXT, + + CONSTRAINT "Level_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Team" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + + CONSTRAINT "Team_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "roles" ( + "id" SERIAL NOT NULL, + "roleName" TEXT NOT NULL, + "description" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "roles_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "competencies" ( + "id" SERIAL NOT NULL, + "competencyName" TEXT NOT NULL, + "description" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "competencies_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "competency_levels" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "competency_levels_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "_RoleToUser" ( + "A" INTEGER NOT NULL, + "B" INTEGER NOT NULL +); + +-- CreateTable +CREATE TABLE "_CompetencyToRole" ( + "A" INTEGER NOT NULL, + "B" INTEGER NOT NULL +); + +-- CreateTable +CREATE TABLE "_CompetencyToCompetencyLevel" ( + "A" INTEGER NOT NULL, + "B" INTEGER NOT NULL +); + +-- CreateIndex +CREATE UNIQUE INDEX "UserMetadata_userId_key" ON "UserMetadata"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "question_bank_competencyLevelId_question_key" ON "question_bank"("competencyLevelId", "question"); + +-- CreateIndex +CREATE UNIQUE INDEX "survey_form_surveyConfigId_key" ON "survey_form"("surveyConfigId"); + +-- CreateIndex +CREATE UNIQUE INDEX "response_tracker_surveyFormId_assesseeId_assessorId_key" ON "response_tracker"("surveyFormId", "assesseeId", "assessorId"); + +-- CreateIndex +CREATE UNIQUE INDEX "users_email_key" ON "users"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "_RoleToUser_AB_unique" ON "_RoleToUser"("A", "B"); + +-- CreateIndex +CREATE INDEX "_RoleToUser_B_index" ON "_RoleToUser"("B"); + +-- CreateIndex +CREATE UNIQUE INDEX "_CompetencyToRole_AB_unique" ON "_CompetencyToRole"("A", "B"); + +-- CreateIndex +CREATE INDEX "_CompetencyToRole_B_index" ON "_CompetencyToRole"("B"); + +-- CreateIndex +CREATE UNIQUE INDEX "_CompetencyToCompetencyLevel_AB_unique" ON "_CompetencyToCompetencyLevel"("A", "B"); + +-- CreateIndex +CREATE INDEX "_CompetencyToCompetencyLevel_B_index" ON "_CompetencyToCompetencyLevel"("B"); + +-- AddForeignKey +ALTER TABLE "survey_form" ADD CONSTRAINT "survey_form_surveyConfigId_fkey" FOREIGN KEY ("surveyConfigId") REFERENCES "survey_config"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "response_tracker" ADD CONSTRAINT "response_tracker_surveyFormId_fkey" FOREIGN KEY ("surveyFormId") REFERENCES "survey_form"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "survey_scores" ADD CONSTRAINT "survey_scores_surveyFormId_fkey" FOREIGN KEY ("surveyFormId") REFERENCES "survey_form"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "users" ADD CONSTRAINT "users_levelId_fkey" FOREIGN KEY ("levelId") REFERENCES "Level"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "users" ADD CONSTRAINT "users_teamId_fkey" FOREIGN KEY ("teamId") REFERENCES "Team"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_RoleToUser" ADD CONSTRAINT "_RoleToUser_A_fkey" FOREIGN KEY ("A") REFERENCES "roles"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_RoleToUser" ADD CONSTRAINT "_RoleToUser_B_fkey" FOREIGN KEY ("B") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_CompetencyToRole" ADD CONSTRAINT "_CompetencyToRole_A_fkey" FOREIGN KEY ("A") REFERENCES "competencies"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_CompetencyToRole" ADD CONSTRAINT "_CompetencyToRole_B_fkey" FOREIGN KEY ("B") REFERENCES "roles"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_CompetencyToCompetencyLevel" ADD CONSTRAINT "_CompetencyToCompetencyLevel_A_fkey" FOREIGN KEY ("A") REFERENCES "competencies"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_CompetencyToCompetencyLevel" ADD CONSTRAINT "_CompetencyToCompetencyLevel_B_fkey" FOREIGN KEY ("B") REFERENCES "competency_levels"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..fbffa92 --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "postgresql" \ No newline at end of file From af5a81d058c45c62dd74c916f1dc771d97b92a7a Mon Sep 17 00:00:00 2001 From: VamshiBatta07 <“batta.v@esmagico.in”> Date: Wed, 22 Nov 2023 10:13:40 +0530 Subject: [PATCH 147/175] Bug fix for "fetchLatestSurveyScoreByUserId". --- src/admin-competency/admin-competency.service.ts | 8 +++----- src/survey-form/survey-form.service.ts | 10 ++++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/admin-competency/admin-competency.service.ts b/src/admin-competency/admin-competency.service.ts index 6c4fa0d..69a0092 100644 --- a/src/admin-competency/admin-competency.service.ts +++ b/src/admin-competency/admin-competency.service.ts @@ -5,9 +5,8 @@ import { forwardRef, } from "@nestjs/common"; import _ from "lodash"; -import { PrismaService } from "../prisma/prisma.service"; import { MockCompetencyService } from "../mockModules/mock-competency/mock-competency.service"; -import { UpdateAdminCompetencyDto } from "./dto"; +import { PrismaService } from "../prisma/prisma.service"; @Injectable() export class AdminCompetencyService { @@ -72,13 +71,12 @@ export class AdminCompetencyService { return adminCompetency; } - public async findAllCompetencyNames() { const adminCompetencies = await this.prisma.adminCompetency.findMany({ - select:{ + select: { competencyId: true, name: true, - } + }, }); return adminCompetencies; } diff --git a/src/survey-form/survey-form.service.ts b/src/survey-form/survey-form.service.ts index e041c3f..53c0897 100644 --- a/src/survey-form/survey-form.service.ts +++ b/src/survey-form/survey-form.service.ts @@ -2,6 +2,7 @@ import { Injectable, NotFoundException } from "@nestjs/common"; import { SurveyStatusEnum } from "@prisma/client"; import { PrismaService } from "../prisma/prisma.service"; import { CreateSurveyFormDto, SurveyScoresForUser } from "./dto"; +import _ from "lodash"; @Injectable() export class SurveyFormService { @@ -152,24 +153,25 @@ export class SurveyFormService { } async fetchLatestSurveyScoreByUserId(userId: string): Promise { - const latestScore = await this.prisma.surveyForm.findFirst({ + const latestScore = await this.prisma.surveyForm.findMany({ where: { userId, status: SurveyStatusEnum.CLOSED, SurveyConfig: { - isActive: true, + isActive: false, }, }, orderBy:{ createdAt: "desc" }, + take: 1, select: { overallScore: true }, }); - if(!latestScore || (latestScore.overallScore != null && latestScore.overallScore<0)){ + if(_.isEmpty(latestScore) || (latestScore[0].overallScore != null && latestScore[0].overallScore<0)){ return null; } - return latestScore.overallScore; + return latestScore[0].overallScore; } } From 05c657ffda4376c1a6b00e69b2fa6850d9df0540 Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:47:15 +0530 Subject: [PATCH 148/175] removal of Level and related changes in mockUser service and question bank --- prisma/schema.prisma | 16 ++++++++-------- src/mockModules/mock-user/mock-user.service.ts | 6 +----- src/question-bank/question-bank.service.ts | 2 +- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 4054ff0..8950763 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -183,7 +183,7 @@ model User { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt designation String - Level Level? @relation(fields: [levelNumber], references: [levelNumber]) + // Level Level? @relation(fields: [levelNumber], references: [levelNumber]) levelNumber Int? // Department Department? @relation(fields: [departmentId], references: [id]) // departmentId Int? @@ -191,12 +191,12 @@ model User { @@map("users") } -model Level { - levelNumber Int @id @unique - description String? - users User[] - CompetencyLevel CompetencyLevel[] -} +// model Level { +// levelNumber Int @id @unique +// description String? +// users User[] +// CompetencyLevel CompetencyLevel[] +// } // model Department { // id Int @id @default(autoincrement()) @@ -251,7 +251,7 @@ model CompetencyLevel { name String @unique description String? levelNumber Int - Level Level @relation(fields: [levelNumber], references: [levelNumber]) + // Level Level @relation(fields: [levelNumber], references: [levelNumber]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt competencies CompetencyToCompetencyLevel[] diff --git a/src/mockModules/mock-user/mock-user.service.ts b/src/mockModules/mock-user/mock-user.service.ts index f26405b..cee859d 100644 --- a/src/mockModules/mock-user/mock-user.service.ts +++ b/src/mockModules/mock-user/mock-user.service.ts @@ -67,11 +67,7 @@ export class MockUserService { userName: true, profilePicture: true, designation:true, - Level: { - select: { - levelNumber : true - }, - }, + levelNumber: true, createdAt: true, updatedAt: true, }; diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index ab1e6ed..d0e7ad1 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -279,7 +279,7 @@ export class QuestionBankService { } // Get competencyLevel for an user - let competencyLevelNumber = user.Level?.levelNumber; + let competencyLevelNumber = user.levelNumber; const questionLists: any = []; if (competencyIds?.length && competencyLevelNumber) { From 90ace969e153ae83eec5025f453ffa48943b3053 Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Thu, 23 Nov 2023 13:32:28 +0530 Subject: [PATCH 149/175] Level and LevelNumber removal and fixes --- prisma/schema.prisma | 2 +- prisma/seeds/data/mock-levels.data.ts | 22 --- prisma/seeds/data/mock-users.data.ts | 20 --- prisma/seeds/seed.ts | 7 +- .../mock-level/dto/create-mock-level.dto.ts | 12 -- .../mock-level/dto/response-mock-level.dto.ts | 4 - .../mock-level/dto/update-mock-level.dto.ts | 4 - .../mock-level/mock-level.controller.spec.ts | 149 ------------------ .../mock-level/mock-level.controller.ts | 111 ------------- .../mock-level/mock-level.module.ts | 10 -- .../mock-level/mock-level.service.spec.ts | 128 --------------- .../mock-level/mock-level.service.ts | 45 ------ .../mock-role/mock-role.service.ts | 13 +- .../mock-user/dto/response-mock-user.dto.ts | 3 - .../mock-user/mock-user.service.ts | 5 +- src/mockModules/mock.module.ts | 3 - src/question-bank/question-bank.controller.ts | 3 +- src/question-bank/question-bank.service.ts | 97 ++++++------ 18 files changed, 64 insertions(+), 574 deletions(-) delete mode 100644 prisma/seeds/data/mock-levels.data.ts delete mode 100644 src/mockModules/mock-level/dto/create-mock-level.dto.ts delete mode 100644 src/mockModules/mock-level/dto/response-mock-level.dto.ts delete mode 100644 src/mockModules/mock-level/dto/update-mock-level.dto.ts delete mode 100644 src/mockModules/mock-level/mock-level.controller.spec.ts delete mode 100644 src/mockModules/mock-level/mock-level.controller.ts delete mode 100644 src/mockModules/mock-level/mock-level.module.ts delete mode 100644 src/mockModules/mock-level/mock-level.service.spec.ts delete mode 100644 src/mockModules/mock-level/mock-level.service.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 8950763..1de94d8 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -184,7 +184,7 @@ model User { updatedAt DateTime @updatedAt designation String // Level Level? @relation(fields: [levelNumber], references: [levelNumber]) - levelNumber Int? + // levelNumber Int? // Department Department? @relation(fields: [departmentId], references: [id]) // departmentId Int? diff --git a/prisma/seeds/data/mock-levels.data.ts b/prisma/seeds/data/mock-levels.data.ts deleted file mode 100644 index 2cc092d..0000000 --- a/prisma/seeds/data/mock-levels.data.ts +++ /dev/null @@ -1,22 +0,0 @@ -export const levels = [ - { - levelNumber: 1, - description: "Basic Proficiency", - }, - { - levelNumber: 2, - description: "Intermediate Skill", - }, - { - levelNumber: 3, - description: "Advanced Expertise", - }, - { - levelNumber: 4, - description: "Strategic Leadership", - }, - { - levelNumber: 5, - description: "Industry Thought Leader", - }, -]; diff --git a/prisma/seeds/data/mock-users.data.ts b/prisma/seeds/data/mock-users.data.ts index 4d87ef8..9df3807 100644 --- a/prisma/seeds/data/mock-users.data.ts +++ b/prisma/seeds/data/mock-users.data.ts @@ -8,7 +8,6 @@ export const users = [ userName: "Amit Sharma", password: "P@ssw0rd1", designation: "CEO", - levelNumber: 4, // Assuming Amit Sharma has a competency level of "Strategic Leadership" }, { id: "4d45a9e9-4a4d-4c92-aaea-7b5abbd6ff98", // Unique UUID for Neha Patil @@ -17,7 +16,6 @@ export const users = [ userName: "Neha Patil", password: "P@ssw0rd2", designation: "Software Engineer", - levelNumber: 2, // competency level for Neha Patil }, { id: "abaa7220-5d2e-4e05-842a-95b2c4ce1876", // Unique UUID for Rajesh Kumar @@ -26,7 +24,6 @@ export const users = [ userName: "Rajesh Kumar", password: "P@ssw0rd3", designation: "Software Engineer", - levelNumber: 3, // competency level for Rajesh Kumar }, { id: "0f5d0b13-8d72-46c9-a7c4-c1f7e5aa1f17", // Unique UUID for Priya Jain @@ -35,7 +32,6 @@ export const users = [ userName: "Priya Jain", password: "P@ssw0rd4", designation: "QA Engineer", - levelNumber: 2, // competency level for Priya Jain }, { id: "bbf1f7cf-4216-458e-8d98-0d9204ae57ef", // Unique UUID for Arjun Singh @@ -44,7 +40,6 @@ export const users = [ userName: "Arjun Singh", password: "P@ssw0rd5", designation: "QA Engineer", - levelNumber: 3, // competency level for Arjun Singh }, { id: "a3bf8f0e-22a9-4a5e-8c76-e80b67a1a6e9", // Unique UUID for Deepa Mishra @@ -53,7 +48,6 @@ export const users = [ userName: "Deepa Mishra", password: "P@ssw0rd6", designation: "Software Architect", - levelNumber: 4, // competency level for Deepa Mishra }, { id: "ac6b9e42-efea-401a-bd67-9cf9110ac3c9", // Unique UUID for Rahul Verma @@ -62,7 +56,6 @@ export const users = [ userName: "Rahul Verma", password: "P@ssw0rd7", designation: "Software Architect", - levelNumber: 3, // competency level for Rahul Verma }, { id: "0f82ac61-9801-488f-babb-3c2d23a413d0", // Unique UUID for Anita Sen @@ -71,7 +64,6 @@ export const users = [ userName: "Anita Sen", password: "P@ssw0rd8", designation: "Software Engineer", - levelNumber: 2, // competency level for Anita Sen }, { id: "69d7871b-3b09-4b5b-9ea0-0ca27c2ac9e3", // Unique UUID for Mohit Gupta @@ -80,7 +72,6 @@ export const users = [ userName: "Mohit Gupta", password: "P@ssw0rd9", designation: "Software Engineer", - levelNumber: 3, // competency level for Mohit Gupta }, { id: "7b8005e4-e3d5-4a0d-81d6-d2965a9d5c2c", // Unique UUID for Pooja Rawat @@ -89,7 +80,6 @@ export const users = [ userName: "Pooja Rawat", password: "P@ssw0rd10", designation: "Software Engineer", - levelNumber: 3, // competency level for Pooja Rawat }, { id: "1536c7ad-234e-44d3-b6d4-8ca655b46e2a", // Unique UUID for Anurag Sharma @@ -98,7 +88,6 @@ export const users = [ userName: "Anurag Sharma", password: "P@ssw0rd11", designation: "Software Engineer", - levelNumber: 2, // competency level for Anurag Sharma }, { id: "9a1f8f5a-0d9e-40e2-b7ea-df493a303db7", // Unique UUID for Meera Rani @@ -107,7 +96,6 @@ export const users = [ userName: "Meera Rani", password: "P@ssw0rd12", designation: "Software Engineer", - levelNumber: 2, // competency level for Meera Rani }, { id: "fbd6d6b9-4c4f-48c1-9147-3006a369e190", // Unique UUID for Vikas Patel @@ -116,7 +104,6 @@ export const users = [ userName: "Vikas Patel", password: "P@ssw0rd13", designation: "Software Engineer", - levelNumber: 3, // competency level for Vikas Patel }, { id: "4f67ae5a-c2a2-4652-81cc-4d1471e1a158", // Unique UUID for Divya Malik @@ -125,7 +112,6 @@ export const users = [ userName: "Divya Malik", password: "P@ssw0rd14", designation: "Software Engineer", - levelNumber: 2, // competency level for Divya Malik }, { id: "a6a862ac-f5f7-4d6b-a297-c1497e5652e5", // Unique UUID for Rajiv Khanna @@ -134,7 +120,6 @@ export const users = [ userName: "Rajiv Khanna", password: "P@ssw0rd15", designation: "Software Engineer", - levelNumber: 4, // competency level for Rajiv Khanna }, { id: "72e9272e-3a4e-45f6-877c-6567f3f9785a", // Unique UUID for Swati Shah @@ -143,7 +128,6 @@ export const users = [ userName: "Swati Shah", password: "P@ssw0rd16", designation: "QA Engineer", - levelNumber: 3, // competency level for Swati Shah }, { id: "5d0b5fb7-d350-4315-988e-df878e96287d", // Unique UUID for Manish Agrawal @@ -152,7 +136,6 @@ export const users = [ userName: "Manish Agrawal", password: "P@ssw0rd17", designation: "QA Engineer", - levelNumber: 4, // competency level for Manish Agrawal }, { id: "a1f3e09a-369f-4e7b-997b-891274f1020e", // Unique UUID for Pallavi Reddy @@ -161,7 +144,6 @@ export const users = [ userName: "Pallavi Reddy", password: "P@ssw0rd18", designation: "QA Engineer", - levelNumber: 2, // competency level for Pallavi Reddy }, { id: "d35d1e80-d2df-4b6a-9a67-8ef5f2877a57", // Unique UUID for Deepak Mishra @@ -170,7 +152,6 @@ export const users = [ userName: "Deepak Mishra", password: "P@ssw0rd19", designation: "QA Engineer", - levelNumber: 4, // competency level for Deepak Mishra }, { id: "7646a865-513f-4529-8ecf-170220e2d6b8", // Unique UUID for Komal Choudhary @@ -179,6 +160,5 @@ export const users = [ userName: "Komal Choudhary", password: "P@ssw0rd20", designation: "QA Engineer", - levelNumber: 2, // competency level for Komal Choudhary }, ]; diff --git a/prisma/seeds/seed.ts b/prisma/seeds/seed.ts index c298087..067e2f1 100644 --- a/prisma/seeds/seed.ts +++ b/prisma/seeds/seed.ts @@ -3,7 +3,6 @@ import { competencies } from "./data/mock-competencies.data"; import { competenciesToCompetencyLevels } from "./data/mock-competenciesToCompetencyLevels.data"; import { competencyLevels } from "./data/mock-competencyLevels.data"; import { designations } from "./data/mock-designation.data"; -import { levels } from "./data/mock-levels.data"; import { roles } from "./data/mock-roles.data"; import { rolesToCompetencies } from "./data/mock-rolesToCompetencies.data"; import { users } from "./data/mock-users.data"; @@ -19,11 +18,7 @@ async function seed() { prisma.role.createMany({ data: roles, }), - - prisma.level.createMany({ - data: levels, - }), - + prisma.competencyLevel.createMany({ data: competencyLevels, }), diff --git a/src/mockModules/mock-level/dto/create-mock-level.dto.ts b/src/mockModules/mock-level/dto/create-mock-level.dto.ts deleted file mode 100644 index 4111250..0000000 --- a/src/mockModules/mock-level/dto/create-mock-level.dto.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { IsInt, IsNotEmpty, IsOptional, IsString } from "class-validator"; - -export class CreateMockLevelDto { - @IsInt() - @IsNotEmpty() - levelNumber: number; - - @IsOptional() - @IsString() - @IsNotEmpty() - description?: string; -} diff --git a/src/mockModules/mock-level/dto/response-mock-level.dto.ts b/src/mockModules/mock-level/dto/response-mock-level.dto.ts deleted file mode 100644 index e2a9a8e..0000000 --- a/src/mockModules/mock-level/dto/response-mock-level.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -export class ResponseMockLevelDto { - readonly levelNumber: number; - readonly description?: string; -} diff --git a/src/mockModules/mock-level/dto/update-mock-level.dto.ts b/src/mockModules/mock-level/dto/update-mock-level.dto.ts deleted file mode 100644 index 64e32c1..0000000 --- a/src/mockModules/mock-level/dto/update-mock-level.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { PartialType } from '@nestjs/mapped-types'; -import { CreateMockLevelDto } from './create-mock-level.dto'; - -export class UpdateMockLevelDto extends PartialType(CreateMockLevelDto) {} diff --git a/src/mockModules/mock-level/mock-level.controller.spec.ts b/src/mockModules/mock-level/mock-level.controller.spec.ts deleted file mode 100644 index 8b2e9bb..0000000 --- a/src/mockModules/mock-level/mock-level.controller.spec.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { Test, TestingModule } from "@nestjs/testing"; -import { MockLevelController } from "./mock-level.controller"; -import { MockLevelService } from "./mock-level.service"; -import { CreateMockLevelDto } from "./dto/create-mock-level.dto"; -import { UpdateMockLevelDto } from "./dto/update-mock-level.dto"; -import { NotFoundException } from "@nestjs/common"; - -describe("MockLevelController", () => { - let controller: MockLevelController; - let mockLevelService: MockLevelService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [MockLevelController], - providers: [MockLevelService], - }).compile(); - - controller = module.get(MockLevelController); - mockLevelService = module.get(MockLevelService); - }); - - it("should be defined", () => { - expect(controller).toBeDefined(); - }); - - describe("create", () => { - it("should create a new mock level", async () => { - const createMockLevelDto: CreateMockLevelDto = { - levelNumber: 1, - description: "Test Description", - }; - - const mockLevel = { - id: 1, - levelNumber: 1, - description: "Test description", - }; // Define a mock response from the service - - // Create mock request objects - const req = {} as Request; - - jest.spyOn(mockLevelService, "create").mockResolvedValue(mockLevel); - - const result = await controller.create(req, createMockLevelDto); - - expect(result).toEqual({ data: mockLevel, message: "Successfully done" }); - }); - }); - - describe("findAll", () => { - it("should return an array of mock levels", async () => { - const mockLevels = [ - /* Define an array of mock levels */ - ]; - - jest.spyOn(mockLevelService, "findAll").mockResolvedValue(mockLevels); - - // Create mock request objects - const req = {} as Request; - - const result = await controller.findAll(req); - - expect(result).toEqual({ - data: mockLevels, - message: "Successfully done", - }); - }); - }); - - describe("findOne", () => { - it("should return a single mock level by ID", async () => { - const mockLevelId = 1; - const mockLevel = { - /* Define one or more properties for this object*/ - id: 1, - levelNumber: 1, - description: "this is test description", - }; // Define a mock response from the service - - jest.spyOn(mockLevelService, "findOne").mockResolvedValue(mockLevel); - - // Create mock request objects - const req = {} as Request; - - const result = await controller.findOne(req, mockLevelId); - - expect(result).toEqual({ data: mockLevel, message: "Successfully done" }); - }); - - it("should handle NotFoundException for non-existent ID", async () => { - const mockLevelId = 999; // Assuming this ID does not exist - jest - .spyOn(mockLevelService, "findOne") - .mockRejectedValue(new NotFoundException()); - - // Create mock request objects - const req = {} as Request; - - const result = await controller.findOne(req, mockLevelId); - - expect(result).toEqual({ message: "Level with id #999 not found" }); - }); - }); - - describe("update", () => { - it("should update a mock level by ID", async () => { - const mockLevelId = 1; - const updateMockLevelDto: UpdateMockLevelDto = { - levelNumber: 1, - description: "Updated Description", - }; - - const mockLevel = { - id: mockLevelId, - levelNumber: 1, - description: "this is test description", - }; // Define a mock response from the service - - jest.spyOn(mockLevelService, "update").mockResolvedValue(mockLevel); - // Create mock request objects - const req = {} as Request; - const result = await controller.update( - req, - mockLevelId, - updateMockLevelDto - ); - - expect(result).toEqual({ data: mockLevel, message: "Successfully done" }); - }); - }); - - describe("remove", () => { - it("should delete a mock level by ID", async () => { - const mockLevelId = 1; - const mockLevel = { - id: mockLevelId, - levelNumber: 1, - description: "test", - }; // Define a mock response from the service - - jest.spyOn(mockLevelService, "remove").mockResolvedValue(mockLevel); - // Create mock request objects - const req = {} as Request; - const result = await controller.remove(req, mockLevelId); - - expect(result).toEqual({ data: mockLevel, message: "Successfully done" }); - }); - }); -}); diff --git a/src/mockModules/mock-level/mock-level.controller.ts b/src/mockModules/mock-level/mock-level.controller.ts deleted file mode 100644 index 6bbf455..0000000 --- a/src/mockModules/mock-level/mock-level.controller.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { - Controller, - Get, - Post, - Body, - Patch, - Param, - Delete, - HttpStatus, - ParseIntPipe, - Res, -} from "@nestjs/common"; -import { MockLevelService } from "./mock-level.service"; -import { CreateMockLevelDto } from "./dto/create-mock-level.dto"; -import { UpdateMockLevelDto } from "./dto/update-mock-level.dto"; -import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; -import { ResponseMockLevelDto } from "./dto/response-mock-level.dto"; - -@Controller("level") -@ApiTags("mockFracService/level") -export class MockLevelController { - constructor(private readonly mockLevelService: MockLevelService) {} - - @Post() - @ApiOperation({ summary: "create new mock level" }) - @ApiResponse({ status: HttpStatus.CREATED, type: ResponseMockLevelDto }) - async create(@Res() res, @Body() createMockLevelDto: CreateMockLevelDto) { - try { - const level = await this.mockLevelService.create(createMockLevelDto); - return res - .status(HttpStatus.OK) - .json({ data: level, message: "Successfully done" }); - } catch (error) { - return res - .status(HttpStatus.INTERNAL_SERVER_ERROR) - .json({ message: error.meta.cause }); - } - } - - @Get() - @ApiOperation({ summary: "get all mock level" }) - @ApiResponse({ - status: HttpStatus.OK, - type: ResponseMockLevelDto, - isArray: true, - }) - async findAll(@Res() res) { - try { - const level = await this.mockLevelService.findAll(); - return res - .status(HttpStatus.OK) - .json({ data: level, message: "Successfully done" }); - } catch (error) { - return res - .status(HttpStatus.INTERNAL_SERVER_ERROR) - .json({ message: error.meta.cause }); - } - } - - @Get(":id") - @ApiOperation({ summary: "get mock level by id" }) - @ApiResponse({ status: HttpStatus.OK, type: ResponseMockLevelDto }) - async findOne(@Res() res, @Param("id", ParseIntPipe) id: number) { - try { - const level = await this.mockLevelService.findOne(id); - return res - .status(HttpStatus.OK) - .json({ data: level, message: "Successfully done" }); - } catch (error) { - return res - .status(HttpStatus.INTERNAL_SERVER_ERROR) - .json({ message: error.message }); - } - } - - @Patch(":id") - @ApiOperation({ summary: "update mock level by id" }) - @ApiResponse({ status: HttpStatus.OK, type: ResponseMockLevelDto }) - async update( - @Res() res, - @Param("id", ParseIntPipe) id: number, - @Body() updateMockLevelDto: UpdateMockLevelDto - ) { - try { - const level = await this.mockLevelService.update(id, updateMockLevelDto); - return res - .status(HttpStatus.OK) - .json({ data: level, message: "Successfully done" }); - } catch (error) { - return res - .status(HttpStatus.INTERNAL_SERVER_ERROR) - .json({ message: error.meta.cause }); - } - } - - @Delete(":id") - @ApiOperation({ summary: "delete mock level by id" }) - @ApiResponse({ status: HttpStatus.OK, type: ResponseMockLevelDto }) - async remove(@Res() res, @Param("id", ParseIntPipe) id: number) { - try { - const level = await this.mockLevelService.remove(id); - return res - .status(HttpStatus.OK) - .json({ data: level, message: "Successfully done" }); - } catch (error) { - return res - .status(HttpStatus.INTERNAL_SERVER_ERROR) - .json({ message: error.meta.cause }); - } - } -} diff --git a/src/mockModules/mock-level/mock-level.module.ts b/src/mockModules/mock-level/mock-level.module.ts deleted file mode 100644 index 9bcb897..0000000 --- a/src/mockModules/mock-level/mock-level.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Module } from "@nestjs/common"; -import { MockLevelService } from "./mock-level.service"; -import { MockLevelController } from "./mock-level.controller"; -import { PrismaService } from "src/prisma/prisma.service"; - -@Module({ - controllers: [MockLevelController], - providers: [MockLevelService], -}) -export class MockLevelModule {} diff --git a/src/mockModules/mock-level/mock-level.service.spec.ts b/src/mockModules/mock-level/mock-level.service.spec.ts deleted file mode 100644 index 267bf8f..0000000 --- a/src/mockModules/mock-level/mock-level.service.spec.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { Test, TestingModule } from "@nestjs/testing"; -import { MockLevelService } from "./mock-level.service"; -import { PrismaService } from "../../prisma/prisma.service"; -import { CreateMockLevelDto } from "./dto/create-mock-level.dto"; -import { UpdateMockLevelDto } from "./dto/update-mock-level.dto"; - -import { NotFoundException } from "@nestjs/common"; - -describe("MockLevelService", () => { - let service: MockLevelService; - let prismaService: PrismaService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [MockLevelService, PrismaService], - }).compile(); - - service = module.get(MockLevelService); - prismaService = module.get(PrismaService); - }); - - it("should be defined", () => { - expect(service).toBeDefined(); - }); - - describe("create", () => { - it("should create a new mock level", async () => { - const createMockLevelDto: CreateMockLevelDto = { - label: "Test Level", - description: "Test Description", - }; - - const mockLevel = { - id: 1024567893, - label: 'Test Level', - description: 'Test Description' - }; // Define a mock response from the database - - jest.spyOn(prismaService.level, "create").mockResolvedValue(mockLevel); - - const result = await service.create(createMockLevelDto); - - expect(result).toEqual(mockLevel); - }); - }); - - describe("findAll", () => { - it("should return an array of mock levels", async () => { - const mockLevels = [ - { id: 1, label: "test", description: "test" }, - { id: 2, label: "test2", description: "test2" }, - ]; - - jest.spyOn(prismaService.level, "findMany").mockResolvedValue(mockLevels); - - const result = await service.findAll(); - - expect(result).toEqual(mockLevels); - }); - }); - - describe("findOne", () => { - it("should return a single mock level by ID", async () => { - const mockLevelId = 1; - const mockLevel = { - id: 1, - label: "test", - description: "test" - }; // Define a mock response from the database - - jest - .spyOn(prismaService.level, "findUnique") - .mockResolvedValue(mockLevel); - - const result = await service.findOne(mockLevelId); - - expect(result).toEqual(mockLevel); - }); - - it("should throw NotFoundException for non-existent ID", async () => { - const mockLevelId = 999; // Assuming this ID does not exist - jest.spyOn(prismaService.level, "findUnique").mockResolvedValue(null); - - await expect(service.findOne(mockLevelId)).rejects.toThrowError( - NotFoundException - ); - }); - }); - - describe("update", () => { - it("should update a mock level by ID", async () => { - const mockLevelId = 1; - const updateMockLevelDto: UpdateMockLevelDto = { - label: "Updated Label", - description: "Updated Description", - }; - - const mockLevel = { - id: 1, - label: 'Updated Label', - description: 'Updated Description' - }; // Define a mock response from the database - - jest.spyOn(prismaService.level, "update").mockResolvedValue(mockLevel); - - const result = await service.update(mockLevelId, updateMockLevelDto); - - expect(result).toEqual(mockLevel); - }); - }); - - describe("remove", () => { - it("should delete a mock level by ID", async () => { - const mockLevelId = 1; - const mockLevel = { - id: 1, - label: '', - description: '' - }; // Define a mock response from the database - - jest.spyOn(prismaService.level, "delete").mockResolvedValue(mockLevel); - - const result = await service.remove(mockLevelId); - - expect(result).toEqual(mockLevel); - }); - }); -}); diff --git a/src/mockModules/mock-level/mock-level.service.ts b/src/mockModules/mock-level/mock-level.service.ts deleted file mode 100644 index f598d47..0000000 --- a/src/mockModules/mock-level/mock-level.service.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Injectable, NotFoundException } from "@nestjs/common"; -import { CreateMockLevelDto } from "./dto/create-mock-level.dto"; -import { UpdateMockLevelDto } from "./dto/update-mock-level.dto"; -import { PrismaService } from "../../prisma/prisma.service"; - -@Injectable() -export class MockLevelService { - constructor(private prisma: PrismaService) {} - public async create(createMockLevelDto: CreateMockLevelDto) { - return await this.prisma.level.create({ - data: createMockLevelDto, - }); - } - - public async findAll() { - return await this.prisma.level.findMany(); - } - - public async findOne(id: number) { - const level = await this.prisma.level.findUnique({ - where: { - levelNumber : id, - }, - }); - if (!level) throw new NotFoundException(`Level with id #${id} not found`); - return level; - } - - public async update(id: number, updateMockLevelDto: UpdateMockLevelDto) { - return await this.prisma.level.update({ - where: { - levelNumber: id, - }, - data: updateMockLevelDto, - }); - } - - public async remove(id: number) { - return await this.prisma.level.delete({ - where: { - levelNumber: id, - }, - }); - } -} diff --git a/src/mockModules/mock-role/mock-role.service.ts b/src/mockModules/mock-role/mock-role.service.ts index be8f237..b4fd8ba 100644 --- a/src/mockModules/mock-role/mock-role.service.ts +++ b/src/mockModules/mock-role/mock-role.service.ts @@ -37,7 +37,7 @@ export class MockRoleService { competencies: { select: { competency: true, - competencyId: true + competencyId: true, }, }, }, @@ -123,4 +123,15 @@ export class MockRoleService { }, }); } + + public async getCompetenciesByRoleId(id: number) { + return this.prisma.roleToCompetency.findMany({ + where: { + roleId: id, + }, + select: { + competencyId: true, + } + }); + } } diff --git a/src/mockModules/mock-user/dto/response-mock-user.dto.ts b/src/mockModules/mock-user/dto/response-mock-user.dto.ts index 7497570..feb0823 100644 --- a/src/mockModules/mock-user/dto/response-mock-user.dto.ts +++ b/src/mockModules/mock-user/dto/response-mock-user.dto.ts @@ -1,4 +1,3 @@ -import { ResponseMockLevelDto } from "src/mockModules/mock-level/dto/response-mock-level.dto"; export class ResponseMockUserDto { readonly id: string; @@ -6,8 +5,6 @@ export class ResponseMockUserDto { readonly role: string; readonly userName: string; readonly profilePicture?: string; - readonly levelNumber: number; - readonly Level: ResponseMockLevelDto; readonly designation: String; readonly createdAt?: Date; readonly updatedAt?: Date; diff --git a/src/mockModules/mock-user/mock-user.service.ts b/src/mockModules/mock-user/mock-user.service.ts index cee859d..f311d23 100644 --- a/src/mockModules/mock-user/mock-user.service.ts +++ b/src/mockModules/mock-user/mock-user.service.ts @@ -67,7 +67,10 @@ export class MockUserService { userName: true, profilePicture: true, designation:true, - levelNumber: true, + // Level: { + // select: { + // levelNumber : true + // }, createdAt: true, updatedAt: true, }; diff --git a/src/mockModules/mock.module.ts b/src/mockModules/mock.module.ts index 8cc4e54..5a0c749 100644 --- a/src/mockModules/mock.module.ts +++ b/src/mockModules/mock.module.ts @@ -4,7 +4,6 @@ import { MockCompetencyModule } from "./mock-competency/mock-competency.module"; import { MockCompetencyLevelModule } from "./mock-competency-level/mock-competency-level.module"; import { RouterModule } from "@nestjs/core"; import { MockUserModule } from "./mock-user/mock-user.module"; -import { MockLevelModule } from "./mock-level/mock-level.module"; import { MockDesignationModule } from './mock-designation/mock-designation.module'; @Module({ @@ -13,14 +12,12 @@ import { MockDesignationModule } from './mock-designation/mock-designation.modul MockCompetencyModule, MockCompetencyLevelModule, MockUserModule, - MockLevelModule, MockDesignationModule, RouterModule.register([ { path: "mockFracService/", module: MockRoleModule }, { path: "mockFracService/", module: MockCompetencyModule }, { path: "mockFracService/", module: MockCompetencyLevelModule }, { path: "mockFracService/", module: MockUserModule }, - { path: "mockFracService/", module: MockLevelModule }, { path: "mockFracService/", module: MockDesignationModule }, ]), ], diff --git a/src/question-bank/question-bank.controller.ts b/src/question-bank/question-bank.controller.ts index e9bef91..c18c509 100644 --- a/src/question-bank/question-bank.controller.ts +++ b/src/question-bank/question-bank.controller.ts @@ -13,6 +13,7 @@ import { ParseIntPipe, UseInterceptors, UploadedFile, + ParseUUIDPipe, } from "@nestjs/common"; import { QuestionBankService } from "./question-bank.service"; import { @@ -241,7 +242,7 @@ export class QuestionBankController { }) async getAllQuestionsForUser( @Res() res, - @Param("id") id: string + @Param("id", ParseUUIDPipe) id: string ): Promise { try { this.logger.log( diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index d0e7ad1..d6f0736 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -1,4 +1,8 @@ -import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; +import { + BadRequestException, + Injectable, + NotFoundException, +} from "@nestjs/common"; import _ from "lodash"; import { CreateFileUploadDto, @@ -50,13 +54,8 @@ export class QuestionBankService { async getAllQuestions(filter: QuestionBankFilterDto) { // Get all questions and filter using compentencyId and compentencyLevelId - const { - competencyId, - competencyLevelNumber, - limit, - offset, - orderBy, - } = filter; + const { competencyId, competencyLevelNumber, limit, offset, orderBy } = + filter; return this.prisma.questionBank.findMany({ where: { competencyId: competencyId ?? undefined, // Optional compentencyId filter @@ -258,58 +257,50 @@ export class QuestionBankService { await this.mockDesignationService.findAllRolesForDesignation(designation); // Get roleId of each role of the user - const roleIds = userRoles?.Roles.map((item) => { - return item.id; + const roles = userRoles?.Roles.map((item) => { + return { + id: item.id, + name: item.name, + }; }); - // unique competencyId associated with the roles of the user - const competencyIds: any = []; - if (roleIds?.length) { - for (let i = 0; i < roleIds.length; i++) { - const competency = (await this.mockRoleService.findRoleById(roleIds[i])) - .competencies; - competencyIds.push( - ...competency.filter((item) => { - if (!competencyIds.includes(item.competencyId)) { - competencyIds.push(item.competencyId); - } - }) + const competenciesForRole = await Promise.all( + roles?.map(async (role) => { + const competency = await this.mockRoleService.getCompetenciesByRoleId( + role.id ); - } - } - - // Get competencyLevel for an user - let competencyLevelNumber = user.levelNumber; + return competency; + }) || [] + ); + + // Flatten the array and extract unique competencyIds + const uniqueCompetencyIds = Array.from( + new Set(competenciesForRole.flat().map((comp) => comp.competencyId)) + ); const questionLists: any = []; - if (competencyIds?.length && competencyLevelNumber) { - for (let i = 0; i < competencyIds.length; i++) { - while (competencyLevelNumber) { - const questions = await this.prisma.questionBank.findMany({ - where: { - competencyId: competencyIds[i], - competencyLevelNumber, - }, - select: { - id: true, - question: true, - }, - }); - - const mappedQuestions = _.map(questions, (data) => { - const { id, question } = data; - return { - question, - questionId: id, - }; - }); + if (uniqueCompetencyIds?.length) { + for (let i = 0; i < uniqueCompetencyIds.length; i++) { + const questions = await this.prisma.questionBank.findMany({ + where: { + competencyId: uniqueCompetencyIds[i], + }, + select: { + id: true, + question: true, + }, + }); - questionLists.push(...mappedQuestions); - competencyLevelNumber--; - } + const mappedQuestions = _.map(questions, (data) => { + const { id, question } = data; + return { + question, + questionId: id, + }; + }); + questionLists.push(...mappedQuestions); } - } - + } if (!questionLists.length) { throw new NotFoundException(`No Question Found for user with #${userId}`); } From 61a1062757e3a044016a990a823b9260e9fa260a Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Tue, 5 Dec 2023 13:26:40 +0530 Subject: [PATCH 150/175] Fix for cors issue --- src/main.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index 429e9b6..62013ba 100644 --- a/src/main.ts +++ b/src/main.ts @@ -16,7 +16,12 @@ async function bootstrap() { const app = await NestFactory.create(AppModule, { cors: true }); // Enable Cross-Origin Resource Sharing (CORS) - app.enableCors(); + app.enableCors({ + origin: true, + methods: ["GET", "POST","PUT","PATCH", "DELETE"], + allowedHeaders: "Content-Type", + credentials:true + }); // Enable using the container for class-validator useContainer(app.select(AppModule), { fallbackOnErrors: true }); From 2385992464fbb60ab47dd72d0b95db573efd55a5 Mon Sep 17 00:00:00 2001 From: Prashant Kumar <144701828+prashantesmagico@users.noreply.github.com> Date: Tue, 5 Dec 2023 13:43:18 +0530 Subject: [PATCH 151/175] Minor fix --- src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index 62013ba..850e1b6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -19,7 +19,7 @@ async function bootstrap() { app.enableCors({ origin: true, methods: ["GET", "POST","PUT","PATCH", "DELETE"], - allowedHeaders: "Content-Type", + allowedHeaders: "*", credentials:true }); From fb7d4f36d72bfe3b072c36afb14c9d75f5b0c0df Mon Sep 17 00:00:00 2001 From: Ajay Maurya Date: Wed, 13 Dec 2023 16:26:06 +0530 Subject: [PATCH 152/175] assessor-survey-response-fix --- .../response-tracker.controller.ts | 4 ++++ .../response-tracker.service.ts | 22 ++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/response-tracker/response-tracker.controller.ts b/src/response-tracker/response-tracker.controller.ts index 52c3a97..5e0621a 100644 --- a/src/response-tracker/response-tracker.controller.ts +++ b/src/response-tracker/response-tracker.controller.ts @@ -185,6 +185,10 @@ export class ResponseTrackerController { assessorId ); + this.logger.log( + `Successfully fetched response tracker for assessorId #${assessorId}.` + ); + return res.status(HttpStatus.OK).json({ data: response, message: `Successfully fetched response tracker for assessorId #${assessorId}.`, diff --git a/src/response-tracker/response-tracker.service.ts b/src/response-tracker/response-tracker.service.ts index 8005583..3b68cbb 100644 --- a/src/response-tracker/response-tracker.service.ts +++ b/src/response-tracker/response-tracker.service.ts @@ -58,14 +58,24 @@ export class ResponseTrackerService { public async findByAssessorId( assessorId: string ) { + const user = await this.prisma.userMetadata.findUnique({ + where: { userId: assessorId }, + }); + + if (!user) { + throw new NotFoundException( + `Assessor with user id #${assessorId} not found.` + ); + } + const response = await this.prisma.responseTracker.findMany({ - where: { - assessorId, + where: { + assessorId, surveyForm: { SurveyConfig: { isActive: true, - } - } + }, + }, }, include: { Assessee: { @@ -87,10 +97,6 @@ export class ResponseTrackerService { }, }); - if (!response || response.length === 0) - throw new NotFoundException( - `Response tracker with assessorId #${assessorId} not found` - ); return response; } From 7ace3f645bc8f0f8dde267807f92beb31cbcfbf1 Mon Sep 17 00:00:00 2001 From: Aman Agarwal Date: Wed, 13 Dec 2023 17:36:10 +0530 Subject: [PATCH 153/175] feat(docker): setting it up for external postgres --- docker-compose.yaml | 32 +-- .../20231120091730_test/migration.sql | 243 ---------------- .../migrations/20231213113943_/migration.sql | 263 ++++++++++++++++++ 3 files changed, 278 insertions(+), 260 deletions(-) delete mode 100644 prisma/migrations/20231120091730_test/migration.sql create mode 100644 prisma/migrations/20231213113943_/migration.sql diff --git a/docker-compose.yaml b/docker-compose.yaml index b18f3ee..200a4b9 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -8,22 +8,20 @@ services: ports: - ${APP_PORT}:${APP_PORT} networks: - - mynetwork - depends_on: - - postgres - - postgres: - image: postgres:15.3-alpine - ports: - - ${DATABASE_PORT}:5432 - volumes: - - ./.data/db:/var/lib/postgresql/data - environment: - POSTGRES_USER: ${DATABASE_USERNAME} - POSTGRES_PASSWORD: ${DATABASE_PASSWORD} - POSTGRES_DB: ${DATABASE_NAME} - networks: - - mynetwork + - samagra_compass networks: - mynetwork: + samagra_compass: + external: true + +# postgres: +# image: postgres:15.3-alpine +# ports: +# - ${DATABASE_PORT}:5432 +# volumes: +# - ./.data/db:/var/lib/postgresql/data +# environment: +# POSTGRES_USER: ${DATABASE_USERNAME} +# POSTGRES_PASSWORD: ${DATABASE_PASSWORD} +# POSTGRES_DB: ${DATABASE_NAME} + diff --git a/prisma/migrations/20231120091730_test/migration.sql b/prisma/migrations/20231120091730_test/migration.sql deleted file mode 100644 index fafe2c8..0000000 --- a/prisma/migrations/20231120091730_test/migration.sql +++ /dev/null @@ -1,243 +0,0 @@ --- CreateEnum -CREATE TYPE "SurveyStatusEnum" AS ENUM ('CREATED', 'PUBLISHED'); - --- CreateEnum -CREATE TYPE "ResponseTrackerStatusEnum" AS ENUM ('PENDING', 'COMPLETED'); - --- CreateEnum -CREATE TYPE "TimeUnitsEnum" AS ENUM ('DAY', 'MONTH', 'YEAR'); - --- CreateEnum -CREATE TYPE "UserRolesEnum" AS ENUM ('ADMIN', 'COMSUMER'); - --- CreateTable -CREATE TABLE "UserMetadata" ( - "id" SERIAL NOT NULL, - "userId" INTEGER NOT NULL, - "teamId" INTEGER NOT NULL, - "isNewEmployee" BOOLEAN NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "UserMetadata_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "question_bank" ( - "id" SERIAL NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "competencyId" INTEGER NOT NULL, - "competencyLevelId" TEXT NOT NULL, - "question" TEXT NOT NULL, - "levelId" INTEGER NOT NULL, - - CONSTRAINT "question_bank_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "survey_config" ( - "id" SERIAL NOT NULL, - "maxQuestions" INTEGER NOT NULL, - "startTime" TIMESTAMP(3) NOT NULL, - "endTime" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "survey_config_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "survey_parameters" ( - "id" SERIAL NOT NULL, - "onboardingTime" INTEGER NOT NULL, - "onboardingTimeUnit" "TimeUnitsEnum" NOT NULL, - "surveyCycle" INTEGER NOT NULL, - - CONSTRAINT "survey_parameters_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "survey_form" ( - "id" SERIAL NOT NULL, - "userId" INTEGER NOT NULL, - "questionsJson" JSONB NOT NULL, - "surveyConfigId" INTEGER NOT NULL, - "status" "SurveyStatusEnum" NOT NULL, - - CONSTRAINT "survey_form_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "response_tracker" ( - "id" SERIAL NOT NULL, - "surveyFormId" INTEGER NOT NULL, - "assesseeId" INTEGER NOT NULL, - "assessorId" INTEGER NOT NULL, - "responseJson" JSONB, - "status" "ResponseTrackerStatusEnum" NOT NULL, - - CONSTRAINT "response_tracker_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "survey_scores" ( - "id" SERIAL NOT NULL, - "userId" INTEGER NOT NULL, - "surveyFormId" INTEGER NOT NULL, - "roleId" INTEGER, - "competencyId" INTEGER NOT NULL, - "competencyLevelId" INTEGER NOT NULL, - "score" INTEGER NOT NULL, - - CONSTRAINT "survey_scores_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "users" ( - "id" SERIAL NOT NULL, - "email" TEXT NOT NULL, - "role" "UserRolesEnum" NOT NULL, - "username" TEXT NOT NULL, - "password" TEXT NOT NULL, - "profilePicture" TEXT, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "levelId" INTEGER, - "teamId" INTEGER, - - CONSTRAINT "users_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "Level" ( - "id" SERIAL NOT NULL, - "lable" TEXT NOT NULL, - "description" TEXT, - - CONSTRAINT "Level_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "Team" ( - "id" SERIAL NOT NULL, - "name" TEXT NOT NULL, - "description" TEXT, - - CONSTRAINT "Team_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "roles" ( - "id" SERIAL NOT NULL, - "roleName" TEXT NOT NULL, - "description" TEXT, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "roles_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "competencies" ( - "id" SERIAL NOT NULL, - "competencyName" TEXT NOT NULL, - "description" TEXT, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "competencies_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "competency_levels" ( - "id" SERIAL NOT NULL, - "name" TEXT NOT NULL, - "description" TEXT, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "competency_levels_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "_RoleToUser" ( - "A" INTEGER NOT NULL, - "B" INTEGER NOT NULL -); - --- CreateTable -CREATE TABLE "_CompetencyToRole" ( - "A" INTEGER NOT NULL, - "B" INTEGER NOT NULL -); - --- CreateTable -CREATE TABLE "_CompetencyToCompetencyLevel" ( - "A" INTEGER NOT NULL, - "B" INTEGER NOT NULL -); - --- CreateIndex -CREATE UNIQUE INDEX "UserMetadata_userId_key" ON "UserMetadata"("userId"); - --- CreateIndex -CREATE UNIQUE INDEX "question_bank_competencyLevelId_question_key" ON "question_bank"("competencyLevelId", "question"); - --- CreateIndex -CREATE UNIQUE INDEX "survey_form_surveyConfigId_key" ON "survey_form"("surveyConfigId"); - --- CreateIndex -CREATE UNIQUE INDEX "response_tracker_surveyFormId_assesseeId_assessorId_key" ON "response_tracker"("surveyFormId", "assesseeId", "assessorId"); - --- CreateIndex -CREATE UNIQUE INDEX "users_email_key" ON "users"("email"); - --- CreateIndex -CREATE UNIQUE INDEX "_RoleToUser_AB_unique" ON "_RoleToUser"("A", "B"); - --- CreateIndex -CREATE INDEX "_RoleToUser_B_index" ON "_RoleToUser"("B"); - --- CreateIndex -CREATE UNIQUE INDEX "_CompetencyToRole_AB_unique" ON "_CompetencyToRole"("A", "B"); - --- CreateIndex -CREATE INDEX "_CompetencyToRole_B_index" ON "_CompetencyToRole"("B"); - --- CreateIndex -CREATE UNIQUE INDEX "_CompetencyToCompetencyLevel_AB_unique" ON "_CompetencyToCompetencyLevel"("A", "B"); - --- CreateIndex -CREATE INDEX "_CompetencyToCompetencyLevel_B_index" ON "_CompetencyToCompetencyLevel"("B"); - --- AddForeignKey -ALTER TABLE "survey_form" ADD CONSTRAINT "survey_form_surveyConfigId_fkey" FOREIGN KEY ("surveyConfigId") REFERENCES "survey_config"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "response_tracker" ADD CONSTRAINT "response_tracker_surveyFormId_fkey" FOREIGN KEY ("surveyFormId") REFERENCES "survey_form"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "survey_scores" ADD CONSTRAINT "survey_scores_surveyFormId_fkey" FOREIGN KEY ("surveyFormId") REFERENCES "survey_form"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "users" ADD CONSTRAINT "users_levelId_fkey" FOREIGN KEY ("levelId") REFERENCES "Level"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "users" ADD CONSTRAINT "users_teamId_fkey" FOREIGN KEY ("teamId") REFERENCES "Team"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "_RoleToUser" ADD CONSTRAINT "_RoleToUser_A_fkey" FOREIGN KEY ("A") REFERENCES "roles"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "_RoleToUser" ADD CONSTRAINT "_RoleToUser_B_fkey" FOREIGN KEY ("B") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "_CompetencyToRole" ADD CONSTRAINT "_CompetencyToRole_A_fkey" FOREIGN KEY ("A") REFERENCES "competencies"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "_CompetencyToRole" ADD CONSTRAINT "_CompetencyToRole_B_fkey" FOREIGN KEY ("B") REFERENCES "roles"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "_CompetencyToCompetencyLevel" ADD CONSTRAINT "_CompetencyToCompetencyLevel_A_fkey" FOREIGN KEY ("A") REFERENCES "competencies"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "_CompetencyToCompetencyLevel" ADD CONSTRAINT "_CompetencyToCompetencyLevel_B_fkey" FOREIGN KEY ("B") REFERENCES "competency_levels"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20231213113943_/migration.sql b/prisma/migrations/20231213113943_/migration.sql new file mode 100644 index 0000000..835e0ac --- /dev/null +++ b/prisma/migrations/20231213113943_/migration.sql @@ -0,0 +1,263 @@ +-- CreateEnum +CREATE TYPE "SurveyStatusEnum" AS ENUM ('CREATED', 'PUBLISHED', 'CLOSED'); + +-- CreateEnum +CREATE TYPE "ResponseTrackerStatusEnum" AS ENUM ('PENDING', 'COMPLETED'); + +-- CreateEnum +CREATE TYPE "TimeUnitsEnum" AS ENUM ('DAY', 'MONTH', 'YEAR'); + +-- CreateEnum +CREATE TYPE "UserRolesEnum" AS ENUM ('ADMIN', 'CONSUMER'); + +-- CreateTable +CREATE TABLE "UserMetadata" ( + "id" SERIAL NOT NULL, + "userId" UUID NOT NULL, + "userName" TEXT NOT NULL, + "profilePicture" TEXT, + "isNewEmployee" BOOLEAN NOT NULL, + "designation" TEXT, + "dateOfJoining" TIMESTAMP(3) NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "isAdmin" BOOLEAN NOT NULL, + + CONSTRAINT "UserMetadata_pkey" PRIMARY KEY ("id","userId") +); + +-- CreateTable +CREATE TABLE "question_bank" ( + "id" SERIAL NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "competencyId" INTEGER NOT NULL, + "competencyLevelNumber" INTEGER NOT NULL, + "question" TEXT NOT NULL, + + CONSTRAINT "question_bank_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "admin_competencies" ( + "id" SERIAL NOT NULL, + "competencyId" INTEGER NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "competencyLevels" JSONB[], + + CONSTRAINT "admin_competencies_pkey" PRIMARY KEY ("id","competencyId") +); + +-- CreateTable +CREATE TABLE "survey_config" ( + "id" SERIAL NOT NULL, + "surveyName" TEXT NOT NULL, + "onboardingTime" INTEGER NOT NULL DEFAULT 3, + "onboardingTimeUnit" "TimeUnitsEnum" NOT NULL DEFAULT 'MONTH', + "startTime" TIMESTAMP(3) NOT NULL, + "endTime" TIMESTAMP(3) NOT NULL, + "isActive" BOOLEAN NOT NULL DEFAULT false, + + CONSTRAINT "survey_config_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "UserMapping" ( + "id" SERIAL NOT NULL, + "surveyConfigId" INTEGER NOT NULL, + "assesseeId" UUID NOT NULL, + "assessorIds" UUID[], + + CONSTRAINT "UserMapping_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "survey_form" ( + "id" SERIAL NOT NULL, + "userId" UUID NOT NULL, + "questionsJson" JSONB NOT NULL, + "surveyConfigId" INTEGER NOT NULL, + "status" "SurveyStatusEnum" NOT NULL, + "overallScore" DOUBLE PRECISION, + "sunbirdCredentialIds" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "survey_form_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "response_tracker" ( + "id" SERIAL NOT NULL, + "surveyFormId" INTEGER NOT NULL, + "assesseeId" UUID NOT NULL, + "assessorId" UUID NOT NULL, + "responseJson" JSONB[] DEFAULT ARRAY[]::JSONB[], + "status" "ResponseTrackerStatusEnum" NOT NULL, + + CONSTRAINT "response_tracker_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "survey_scores" ( + "id" SERIAL NOT NULL, + "surveyFormId" INTEGER NOT NULL, + "competencyId" INTEGER NOT NULL, + "competencyLevelNumber" INTEGER NOT NULL, + "score" DOUBLE PRECISION NOT NULL, + + CONSTRAINT "survey_scores_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "users" ( + "id" TEXT NOT NULL, + "email" TEXT NOT NULL, + "role" "UserRolesEnum" NOT NULL, + "userName" TEXT NOT NULL, + "password" TEXT NOT NULL, + "profilePicture" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "designation" TEXT NOT NULL, + + CONSTRAINT "users_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Designation" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + + CONSTRAINT "Designation_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "roles" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "designationId" INTEGER, + + CONSTRAINT "roles_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "competencies" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "competencies_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "RoleToCompetency" ( + "roleId" INTEGER NOT NULL, + "competencyId" INTEGER NOT NULL, + + CONSTRAINT "RoleToCompetency_pkey" PRIMARY KEY ("roleId","competencyId") +); + +-- CreateTable +CREATE TABLE "competency_levels" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "levelNumber" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "competency_levels_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "CompetencyToCompetencyLevel" ( + "competencyId" INTEGER NOT NULL, + "competencyLevelId" INTEGER NOT NULL, + + CONSTRAINT "CompetencyToCompetencyLevel_pkey" PRIMARY KEY ("competencyId","competencyLevelId") +); + +-- CreateIndex +CREATE UNIQUE INDEX "UserMetadata_id_key" ON "UserMetadata"("id"); + +-- CreateIndex +CREATE UNIQUE INDEX "UserMetadata_userId_key" ON "UserMetadata"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "UserMetadata_userName_key" ON "UserMetadata"("userName"); + +-- CreateIndex +CREATE UNIQUE INDEX "question_bank_competencyId_competencyLevelNumber_question_key" ON "question_bank"("competencyId", "competencyLevelNumber", "question"); + +-- CreateIndex +CREATE UNIQUE INDEX "admin_competencies_competencyId_key" ON "admin_competencies"("competencyId"); + +-- CreateIndex +CREATE UNIQUE INDEX "survey_form_userId_surveyConfigId_key" ON "survey_form"("userId", "surveyConfigId"); + +-- CreateIndex +CREATE UNIQUE INDEX "response_tracker_surveyFormId_assesseeId_assessorId_key" ON "response_tracker"("surveyFormId", "assesseeId", "assessorId"); + +-- CreateIndex +CREATE UNIQUE INDEX "survey_scores_surveyFormId_competencyId_competencyLevelNumb_key" ON "survey_scores"("surveyFormId", "competencyId", "competencyLevelNumber"); + +-- CreateIndex +CREATE UNIQUE INDEX "users_email_key" ON "users"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "Designation_name_key" ON "Designation"("name"); + +-- CreateIndex +CREATE UNIQUE INDEX "roles_name_key" ON "roles"("name"); + +-- CreateIndex +CREATE UNIQUE INDEX "competencies_name_key" ON "competencies"("name"); + +-- CreateIndex +CREATE UNIQUE INDEX "competency_levels_name_key" ON "competency_levels"("name"); + +-- AddForeignKey +ALTER TABLE "UserMapping" ADD CONSTRAINT "UserMapping_surveyConfigId_fkey" FOREIGN KEY ("surveyConfigId") REFERENCES "survey_config"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "survey_form" ADD CONSTRAINT "survey_form_userId_fkey" FOREIGN KEY ("userId") REFERENCES "UserMetadata"("userId") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "survey_form" ADD CONSTRAINT "survey_form_surveyConfigId_fkey" FOREIGN KEY ("surveyConfigId") REFERENCES "survey_config"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "response_tracker" ADD CONSTRAINT "response_tracker_surveyFormId_fkey" FOREIGN KEY ("surveyFormId") REFERENCES "survey_form"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "response_tracker" ADD CONSTRAINT "response_tracker_assesseeId_fkey" FOREIGN KEY ("assesseeId") REFERENCES "UserMetadata"("userId") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "response_tracker" ADD CONSTRAINT "response_tracker_assessorId_fkey" FOREIGN KEY ("assessorId") REFERENCES "UserMetadata"("userId") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "survey_scores" ADD CONSTRAINT "survey_scores_surveyFormId_fkey" FOREIGN KEY ("surveyFormId") REFERENCES "survey_form"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "roles" ADD CONSTRAINT "roles_designationId_fkey" FOREIGN KEY ("designationId") REFERENCES "Designation"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "RoleToCompetency" ADD CONSTRAINT "RoleToCompetency_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "roles"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "RoleToCompetency" ADD CONSTRAINT "RoleToCompetency_competencyId_fkey" FOREIGN KEY ("competencyId") REFERENCES "competencies"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "CompetencyToCompetencyLevel" ADD CONSTRAINT "CompetencyToCompetencyLevel_competencyId_fkey" FOREIGN KEY ("competencyId") REFERENCES "competencies"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "CompetencyToCompetencyLevel" ADD CONSTRAINT "CompetencyToCompetencyLevel_competencyLevelId_fkey" FOREIGN KEY ("competencyLevelId") REFERENCES "competency_levels"("id") ON DELETE CASCADE ON UPDATE CASCADE; From cca439a22b47713c207265a6983577f3d87e7a75 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 Date: Tue, 12 Dec 2023 14:50:17 +0530 Subject: [PATCH 154/175] Integrating SunbirdRC and Passbook. --- package.json | 1 + .../20231212091810_wpcas_init/migration.sql | 281 ++++++++++++++++++ .../migration.sql | 12 + prisma/schema.prisma | 10 + .../admin-competency.service.ts | 10 + src/app.module.ts | 2 + .../credential-did.controller.ts | 35 +++ src/credential-did/credential-did.module.ts | 14 + src/credential-did/credential-did.service.ts | 46 +++ .../dto/create-update-did.dto.ts | 16 + src/credential-did/dto/index.ts | 1 + .../external-services.module.ts | 9 + src/external-services/passbook/dto/index.ts | 1 + .../passbook/dto/passbook.dto.ts | 9 + .../passbook/passbook.module.ts | 8 + .../passbook/passbook.service.ts | 50 ++++ .../sunbird-rc/dto/credentail-schema.dto.ts | 106 +++++++ src/external-services/sunbird-rc/dto/index.ts | 1 + .../sunbird-rc/schema/index.ts | 1 + .../schema/wpcas-credential.schema.ts | 60 ++++ .../sunbird-rc/sunbird-rc.module.ts | 10 + .../sunbird-rc/sunbird-rc.service.ts | 144 +++++++++ src/scheduled-tasks/scheduled-tasks.module.ts | 14 +- .../scheduled-tasks.service.ts | 107 +++---- src/survey-config/survey-config.module.ts | 4 +- src/survey-config/survey-config.service.ts | 5 +- src/survey-form/survey-form.service.ts | 2 +- src/survey/survey.controller.ts | 2 +- src/survey/survey.module.ts | 7 +- src/survey/survey.service.ts | 120 +++++++- 30 files changed, 1002 insertions(+), 86 deletions(-) create mode 100644 prisma/migrations/20231212091810_wpcas_init/migration.sql create mode 100644 prisma/migrations/20231212091927_new_table_to_store_credential_dids/migration.sql create mode 100644 src/credential-did/credential-did.controller.ts create mode 100644 src/credential-did/credential-did.module.ts create mode 100644 src/credential-did/credential-did.service.ts create mode 100644 src/credential-did/dto/create-update-did.dto.ts create mode 100644 src/credential-did/dto/index.ts create mode 100644 src/external-services/external-services.module.ts create mode 100644 src/external-services/passbook/dto/index.ts create mode 100644 src/external-services/passbook/dto/passbook.dto.ts create mode 100644 src/external-services/passbook/passbook.module.ts create mode 100644 src/external-services/passbook/passbook.service.ts create mode 100644 src/external-services/sunbird-rc/dto/credentail-schema.dto.ts create mode 100644 src/external-services/sunbird-rc/dto/index.ts create mode 100644 src/external-services/sunbird-rc/schema/index.ts create mode 100644 src/external-services/sunbird-rc/schema/wpcas-credential.schema.ts create mode 100644 src/external-services/sunbird-rc/sunbird-rc.module.ts create mode 100644 src/external-services/sunbird-rc/sunbird-rc.service.ts diff --git a/package.json b/package.json index 8652fa0..8eeb648 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "@nestjs/schedule": "^3.0.4", "@nestjs/swagger": "^7.1.11", "@prisma/client": "^5.4.2", + "axios": "^1.6.2", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "csv-parser": "^3.0.0", diff --git a/prisma/migrations/20231212091810_wpcas_init/migration.sql b/prisma/migrations/20231212091810_wpcas_init/migration.sql new file mode 100644 index 0000000..8ce0e34 --- /dev/null +++ b/prisma/migrations/20231212091810_wpcas_init/migration.sql @@ -0,0 +1,281 @@ +-- CreateEnum +CREATE TYPE "SurveyStatusEnum" AS ENUM ('CREATED', 'PUBLISHED', 'CLOSED'); + +-- CreateEnum +CREATE TYPE "ResponseTrackerStatusEnum" AS ENUM ('PENDING', 'COMPLETED'); + +-- CreateEnum +CREATE TYPE "TimeUnitsEnum" AS ENUM ('DAY', 'MONTH', 'YEAR'); + +-- CreateEnum +CREATE TYPE "UserRolesEnum" AS ENUM ('ADMIN', 'CONSUMER'); + +-- CreateTable +CREATE TABLE "UserMetadata" ( + "id" SERIAL NOT NULL, + "userId" UUID NOT NULL, + "userName" TEXT NOT NULL, + "profilePicture" TEXT, + "isNewEmployee" BOOLEAN NOT NULL, + "designation" TEXT, + "dateOfJoining" TIMESTAMP(3) NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "isAdmin" BOOLEAN NOT NULL, + + CONSTRAINT "UserMetadata_pkey" PRIMARY KEY ("id","userId") +); + +-- CreateTable +CREATE TABLE "question_bank" ( + "id" SERIAL NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "competencyId" INTEGER NOT NULL, + "competencyLevelNumber" INTEGER NOT NULL, + "question" TEXT NOT NULL, + + CONSTRAINT "question_bank_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "admin_competencies" ( + "id" SERIAL NOT NULL, + "competencyId" INTEGER NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "competencyLevels" JSONB[], + + CONSTRAINT "admin_competencies_pkey" PRIMARY KEY ("id","competencyId") +); + +-- CreateTable +CREATE TABLE "survey_config" ( + "id" SERIAL NOT NULL, + "surveyName" TEXT NOT NULL, + "onboardingTime" INTEGER NOT NULL DEFAULT 3, + "onboardingTimeUnit" "TimeUnitsEnum" NOT NULL DEFAULT 'MONTH', + "startTime" TIMESTAMP(3) NOT NULL, + "endTime" TIMESTAMP(3) NOT NULL, + "isActive" BOOLEAN NOT NULL DEFAULT false, + + CONSTRAINT "survey_config_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "UserMapping" ( + "id" SERIAL NOT NULL, + "surveyConfigId" INTEGER NOT NULL, + "assesseeId" UUID NOT NULL, + "assessorIds" UUID[], + + CONSTRAINT "UserMapping_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "survey_form" ( + "id" SERIAL NOT NULL, + "userId" UUID NOT NULL, + "questionsJson" JSONB NOT NULL, + "surveyConfigId" INTEGER NOT NULL, + "status" "SurveyStatusEnum" NOT NULL, + "overallScore" DOUBLE PRECISION, + "sunbirdCredentialIds" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "survey_form_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "response_tracker" ( + "id" SERIAL NOT NULL, + "surveyFormId" INTEGER NOT NULL, + "assesseeId" UUID NOT NULL, + "assessorId" UUID NOT NULL, + "responseJson" JSONB[] DEFAULT ARRAY[]::JSONB[], + "status" "ResponseTrackerStatusEnum" NOT NULL, + + CONSTRAINT "response_tracker_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "survey_scores" ( + "id" SERIAL NOT NULL, + "surveyFormId" INTEGER NOT NULL, + "competencyId" INTEGER NOT NULL, + "competencyLevelNumber" INTEGER NOT NULL, + "score" DOUBLE PRECISION NOT NULL, + + CONSTRAINT "survey_scores_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "users" ( + "id" TEXT NOT NULL, + "email" TEXT NOT NULL, + "role" "UserRolesEnum" NOT NULL, + "userName" TEXT NOT NULL, + "password" TEXT NOT NULL, + "profilePicture" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "designation" TEXT NOT NULL, + "levelNumber" INTEGER, + + CONSTRAINT "users_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Level" ( + "levelNumber" INTEGER NOT NULL, + "description" TEXT, + + CONSTRAINT "Level_pkey" PRIMARY KEY ("levelNumber") +); + +-- CreateTable +CREATE TABLE "Designation" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + + CONSTRAINT "Designation_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "roles" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "designationId" INTEGER, + + CONSTRAINT "roles_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "competencies" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "competencies_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "RoleToCompetency" ( + "roleId" INTEGER NOT NULL, + "competencyId" INTEGER NOT NULL, + + CONSTRAINT "RoleToCompetency_pkey" PRIMARY KEY ("roleId","competencyId") +); + +-- CreateTable +CREATE TABLE "competency_levels" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "levelNumber" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "competency_levels_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "CompetencyToCompetencyLevel" ( + "competencyId" INTEGER NOT NULL, + "competencyLevelId" INTEGER NOT NULL, + + CONSTRAINT "CompetencyToCompetencyLevel_pkey" PRIMARY KEY ("competencyId","competencyLevelId") +); + +-- CreateIndex +CREATE UNIQUE INDEX "UserMetadata_id_key" ON "UserMetadata"("id"); + +-- CreateIndex +CREATE UNIQUE INDEX "UserMetadata_userId_key" ON "UserMetadata"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "UserMetadata_userName_key" ON "UserMetadata"("userName"); + +-- CreateIndex +CREATE UNIQUE INDEX "question_bank_competencyId_competencyLevelNumber_question_key" ON "question_bank"("competencyId", "competencyLevelNumber", "question"); + +-- CreateIndex +CREATE UNIQUE INDEX "admin_competencies_competencyId_key" ON "admin_competencies"("competencyId"); + +-- CreateIndex +CREATE UNIQUE INDEX "survey_form_userId_surveyConfigId_key" ON "survey_form"("userId", "surveyConfigId"); + +-- CreateIndex +CREATE UNIQUE INDEX "response_tracker_surveyFormId_assesseeId_assessorId_key" ON "response_tracker"("surveyFormId", "assesseeId", "assessorId"); + +-- CreateIndex +CREATE UNIQUE INDEX "survey_scores_surveyFormId_competencyId_competencyLevelNumb_key" ON "survey_scores"("surveyFormId", "competencyId", "competencyLevelNumber"); + +-- CreateIndex +CREATE UNIQUE INDEX "users_email_key" ON "users"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "Level_levelNumber_key" ON "Level"("levelNumber"); + +-- CreateIndex +CREATE UNIQUE INDEX "Designation_name_key" ON "Designation"("name"); + +-- CreateIndex +CREATE UNIQUE INDEX "roles_name_key" ON "roles"("name"); + +-- CreateIndex +CREATE UNIQUE INDEX "competencies_name_key" ON "competencies"("name"); + +-- CreateIndex +CREATE UNIQUE INDEX "competency_levels_name_key" ON "competency_levels"("name"); + +-- AddForeignKey +ALTER TABLE "UserMapping" ADD CONSTRAINT "UserMapping_surveyConfigId_fkey" FOREIGN KEY ("surveyConfigId") REFERENCES "survey_config"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "survey_form" ADD CONSTRAINT "survey_form_userId_fkey" FOREIGN KEY ("userId") REFERENCES "UserMetadata"("userId") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "survey_form" ADD CONSTRAINT "survey_form_surveyConfigId_fkey" FOREIGN KEY ("surveyConfigId") REFERENCES "survey_config"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "response_tracker" ADD CONSTRAINT "response_tracker_surveyFormId_fkey" FOREIGN KEY ("surveyFormId") REFERENCES "survey_form"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "response_tracker" ADD CONSTRAINT "response_tracker_assesseeId_fkey" FOREIGN KEY ("assesseeId") REFERENCES "UserMetadata"("userId") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "response_tracker" ADD CONSTRAINT "response_tracker_assessorId_fkey" FOREIGN KEY ("assessorId") REFERENCES "UserMetadata"("userId") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "survey_scores" ADD CONSTRAINT "survey_scores_surveyFormId_fkey" FOREIGN KEY ("surveyFormId") REFERENCES "survey_form"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "users" ADD CONSTRAINT "users_levelNumber_fkey" FOREIGN KEY ("levelNumber") REFERENCES "Level"("levelNumber") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "roles" ADD CONSTRAINT "roles_designationId_fkey" FOREIGN KEY ("designationId") REFERENCES "Designation"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "RoleToCompetency" ADD CONSTRAINT "RoleToCompetency_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "roles"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "RoleToCompetency" ADD CONSTRAINT "RoleToCompetency_competencyId_fkey" FOREIGN KEY ("competencyId") REFERENCES "competencies"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "competency_levels" ADD CONSTRAINT "competency_levels_levelNumber_fkey" FOREIGN KEY ("levelNumber") REFERENCES "Level"("levelNumber") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "CompetencyToCompetencyLevel" ADD CONSTRAINT "CompetencyToCompetencyLevel_competencyId_fkey" FOREIGN KEY ("competencyId") REFERENCES "competencies"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "CompetencyToCompetencyLevel" ADD CONSTRAINT "CompetencyToCompetencyLevel_competencyLevelId_fkey" FOREIGN KEY ("competencyLevelId") REFERENCES "competency_levels"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20231212091927_new_table_to_store_credential_dids/migration.sql b/prisma/migrations/20231212091927_new_table_to_store_credential_dids/migration.sql new file mode 100644 index 0000000..2de7557 --- /dev/null +++ b/prisma/migrations/20231212091927_new_table_to_store_credential_dids/migration.sql @@ -0,0 +1,12 @@ +-- CreateTable +CREATE TABLE "credential_did" ( + "id" SERIAL NOT NULL, + "authorDid" TEXT NOT NULL, + "schemaDid" TEXT NOT NULL, + "schemaVersion" TEXT NOT NULL, + + CONSTRAINT "credential_did_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "credential_did_schemaDid_schemaVersion_key" ON "credential_did"("schemaDid", "schemaVersion"); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 1de94d8..3b3a2b3 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -68,6 +68,16 @@ model AdminCompetency { // @@map("admin_department") // } +model CredentialDid{ + id Int @id @default(autoincrement()) + authorDid String + schemaDid String + schemaVersion String + + @@unique([schemaDid, schemaVersion]) + @@map("credential_did") +} + model SurveyConfig { id Int @id @default(autoincrement()) surveyName String diff --git a/src/admin-competency/admin-competency.service.ts b/src/admin-competency/admin-competency.service.ts index 69a0092..3e153d2 100644 --- a/src/admin-competency/admin-competency.service.ts +++ b/src/admin-competency/admin-competency.service.ts @@ -138,4 +138,14 @@ export class AdminCompetencyService { }); return adminCompetency; } + + public async fetchCompetencyLevel(competencyId:number, competencyLevelNumber: number){ + try { + const competency = await this.findOne(competencyId); + const competencyLevel = _.get(competency, "competencyLevels", []); + return _.find(competencyLevel, competencyLevelNumber); + } catch (error) { + throw error; + } + } } diff --git a/src/app.module.ts b/src/app.module.ts index e10e916..0eeb0d2 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -16,6 +16,7 @@ import { ScheduledTasksModule } from './scheduled-tasks/scheduled-tasks.module'; import { ScheduleModule } from "@nestjs/schedule"; import { SurveyModule } from './survey/survey.module'; import { FileUploadModule } from './file-upload/file-upload.module'; +import { ExternalServicesModule } from "./external-services/external-services.module"; @Module({ imports: [ @@ -35,6 +36,7 @@ import { FileUploadModule } from './file-upload/file-upload.module'; ScheduleModule.forRoot(), SurveyModule, FileUploadModule, + ExternalServicesModule ], controllers: [AppController], providers: [AppService, PrismaService], diff --git a/src/credential-did/credential-did.controller.ts b/src/credential-did/credential-did.controller.ts new file mode 100644 index 0000000..d5ec726 --- /dev/null +++ b/src/credential-did/credential-did.controller.ts @@ -0,0 +1,35 @@ +import { Body, Controller, HttpStatus, Logger, Post, Res, UseInterceptors } from "@nestjs/common"; +import { ApiTags } from "@nestjs/swagger"; +import { CredentialDIDService } from "./credential-did.service"; +import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; +import { CreateUpdateDIDDto } from "./dto"; + +@Controller("credentialDID") +@ApiTags("credentialDID") +export class CredentialDIDController { + private readonly logger = new Logger(CredentialDIDController.name); + constructor(private readonly credentialDIDService: CredentialDIDService) {} + + @Post() + async createOrUpdateDIDs(@Res() res, @Body() createUpdateDID: CreateUpdateDIDDto) { + // Log the initiaton for csv file upload + this.logger.log(`Creating or updating DIDs`); + try { + const result = await this.credentialDIDService.createOrUpdateDIDs(createUpdateDID) + this.logger.log(`Created or updated DIDs successfully.`); + return res.status(HttpStatus.OK).json({ + message: "Created or updated DIDs successfully.", + }); + } catch (error) { + this.logger.error(`Failed to create or update DIDs..`, error); + // get error message and status code + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + // Return an error response + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed to create or update DIDs.`, + }); + } + } +} diff --git a/src/credential-did/credential-did.module.ts b/src/credential-did/credential-did.module.ts new file mode 100644 index 0000000..01a7dd1 --- /dev/null +++ b/src/credential-did/credential-did.module.ts @@ -0,0 +1,14 @@ +import { Module } from "@nestjs/common"; +import { PrismaService } from "../prisma/prisma.service"; +import { CredentialDIDService } from "./credential-did.service"; +import { CredentialDIDController } from "./credential-did.controller"; +import { PrismaModule } from "src/prisma/prisma.module"; +import { SunbirdRcService } from "src/external-services/sunbird-rc/sunbird-rc.service"; + +@Module({ + imports: [PrismaModule], + controllers: [CredentialDIDController], + providers: [CredentialDIDService, SunbirdRcService], + exports: [CredentialDIDService], +}) +export class CredentialDIDModule {} diff --git a/src/credential-did/credential-did.service.ts b/src/credential-did/credential-did.service.ts new file mode 100644 index 0000000..ac64739 --- /dev/null +++ b/src/credential-did/credential-did.service.ts @@ -0,0 +1,46 @@ +import { + ForbiddenException, + Inject, + Injectable, + forwardRef +} from "@nestjs/common"; +import { SunbirdRcService } from "src/external-services/sunbird-rc/sunbird-rc.service"; +import { PrismaService } from "../prisma/prisma.service"; +import { CreateUpdateDIDDto } from "./dto"; + +@Injectable() +export class CredentialDIDService { + constructor( + private prisma: PrismaService, + @Inject(forwardRef(()=>SunbirdRcService)) + private sunbirdRc: SunbirdRcService + ) {} + + async createOrUpdateDIDs(createOrUpdateDID: CreateUpdateDIDDto) { + try { + await this.sunbirdRc.resolveDid(createOrUpdateDID.authorDid); + await this.sunbirdRc.fetchCredSchemaByIdAndVersion(createOrUpdateDID.schemaDid, createOrUpdateDID.schemaVersion); + const did = await this.prisma.credentialDid.findMany(); + + return await this.prisma.credentialDid.upsert({ + where: { + id: did[0].id ?? 1, + }, + update: createOrUpdateDID, + create: createOrUpdateDID, + }); + } catch (error) { + throw error; + } + } + + async findDIDs() { + const did = await this.prisma.credentialDid.findFirst(); + if (!did) { + throw new ForbiddenException( + `Please configure the schema and author DIDs (used to issue credentials).` + ); + } + return did; + } +} diff --git a/src/credential-did/dto/create-update-did.dto.ts b/src/credential-did/dto/create-update-did.dto.ts new file mode 100644 index 0000000..02ae31a --- /dev/null +++ b/src/credential-did/dto/create-update-did.dto.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsOptional } from 'class-validator'; + +export class CreateUpdateDIDDto { + @ApiProperty({ required: false, description: 'Author DID' }) + @IsNotEmpty() + authorDid: string; + + @ApiProperty({ required: false, description: 'Schema DID' }) + @IsNotEmpty() + schemaDid: string; + + @ApiProperty({ required: false, description: 'Schema DID' }) + @IsNotEmpty() + schemaVersion: string; +} \ No newline at end of file diff --git a/src/credential-did/dto/index.ts b/src/credential-did/dto/index.ts new file mode 100644 index 0000000..43dc9ec --- /dev/null +++ b/src/credential-did/dto/index.ts @@ -0,0 +1 @@ +export * from "./create-update-did.dto" \ No newline at end of file diff --git a/src/external-services/external-services.module.ts b/src/external-services/external-services.module.ts new file mode 100644 index 0000000..b8fa1cf --- /dev/null +++ b/src/external-services/external-services.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { PassbookModule } from './passbook/passbook.module'; +import { SunbirdRcModule } from './sunbird-rc/sunbird-rc.module'; + +@Module({ + imports: [PassbookModule, SunbirdRcModule], + exports: [PassbookModule, SunbirdRcModule] +}) +export class ExternalServicesModule {} diff --git a/src/external-services/passbook/dto/index.ts b/src/external-services/passbook/dto/index.ts new file mode 100644 index 0000000..4438cc8 --- /dev/null +++ b/src/external-services/passbook/dto/index.ts @@ -0,0 +1 @@ +export * from './passbook.dto' \ No newline at end of file diff --git a/src/external-services/passbook/dto/passbook.dto.ts b/src/external-services/passbook/dto/passbook.dto.ts new file mode 100644 index 0000000..989f36d --- /dev/null +++ b/src/external-services/passbook/dto/passbook.dto.ts @@ -0,0 +1,9 @@ +import { + IsString, +} from "class-validator"; +import { SurveyScoreCredentailDto } from "src/external-services/sunbird-rc/dto"; + +export class AddFeedbackDto extends SurveyScoreCredentailDto { + @IsString() + certificateId: string; +} \ No newline at end of file diff --git a/src/external-services/passbook/passbook.module.ts b/src/external-services/passbook/passbook.module.ts new file mode 100644 index 0000000..dc814ac --- /dev/null +++ b/src/external-services/passbook/passbook.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common'; +import { PassbookService } from './passbook.service'; + +@Module({ + providers: [PassbookService], + exports: [PassbookService] +}) +export class PassbookModule {} diff --git a/src/external-services/passbook/passbook.service.ts b/src/external-services/passbook/passbook.service.ts new file mode 100644 index 0000000..94ee08c --- /dev/null +++ b/src/external-services/passbook/passbook.service.ts @@ -0,0 +1,50 @@ +import { Injectable } from "@nestjs/common"; +import axios from "axios"; +import { AddFeedbackDto } from "./dto"; + +@Injectable() +export class PassbookService { + private apiUrl = "http://172.17.0.2:3000/api"; + + async getUser(userId: string): Promise { + try { + const response = await axios.get(`${this.apiUrl}/user`, { + params: { + userId: userId, + }, + }); + return response.data; + } catch (error) { + // Handle errors + throw new Error("Failed to fetch data from the external API"); + } + } + + async createUser(userId: string): Promise { + try { + const response = await axios.post(`${this.apiUrl}/user`, { + data: { + userId: userId, + }, + }); + return response.data; + } catch (error) { + // Handle errors + throw new Error("Failed to fetch data from the external API"); + } + } + + async addFeedback(feedback: AddFeedbackDto): Promise { + try { + const response = await axios.post(`${this.apiUrl}/user/feedback`, { + data: { + ...feedback + }, + }); + return response.data; + } catch (error) { + // Handle errors + throw new Error(`Failed to add feedback for user with id #${feedback.userId}`); + } + } +} diff --git a/src/external-services/sunbird-rc/dto/credentail-schema.dto.ts b/src/external-services/sunbird-rc/dto/credentail-schema.dto.ts new file mode 100644 index 0000000..46d794d --- /dev/null +++ b/src/external-services/sunbird-rc/dto/credentail-schema.dto.ts @@ -0,0 +1,106 @@ +import { + IsDateString, + IsInt, + Min, + Max, + IsString, + ValidateNested, + ArrayMinSize, + ArrayNotEmpty, + IsNumber, + IsOptional, + IsNotEmpty, + IsArray, + IsEnum, +} from "class-validator"; +import { Type } from "class-transformer"; + +class LevelCredentailDto { + @IsInt() + levelNumber: number; + + @IsString() + name: string; + + @IsOptional() + @IsNotEmpty() + @IsNumber() + @Min(0, { message: "Score must be at least 0" }) + @Max(100, { message: "Score must not exceed 100" }) + score: number | null; +} + +export class CompetencyCredentailDto { + @IsInt() + id: number; + + @IsString() + name: string; + + @ArrayMinSize(1, { message: "Levels must have at least one item" }) + @ArrayNotEmpty({ message: "Levels must not be empty" }) + @ValidateNested({ each: true }) + @Type(() => LevelCredentailDto) + levels: LevelCredentailDto[]; +} + +export class SurveyScoreCredentailDto { + @IsDateString() + dateOfSurveyScore: string; + + @IsOptional() + @IsNotEmpty() + @IsNumber() + @Min(0, { message: "Overall score must be at least 0" }) + @Max(100, { message: "Overall score must not exceed 100" }) + overallScore: number | null; + + @ArrayMinSize(1, { message: "Competencies must have at least one item" }) + @ArrayNotEmpty({ message: "Competencies must not be empty" }) + @ValidateNested({ each: true }) + @Type(() => CompetencyCredentailDto) + competencies: CompetencyCredentailDto[]; + + @IsString() + userId: string; +} + +enum CredentialStatus { + DRAFT = "DRAFT", + PUBLISHED = "PUBLISHED", + ARCHIVED = "ARCHIVED", +} + +export class createSchemaInputDTO { + @IsString() + @IsNotEmpty() + type: string; + + @IsString() + @IsNotEmpty() + version: string; + + @IsString() + @IsNotEmpty() + id: string; + + @IsString() + @IsNotEmpty() + name: string; + + @IsString() + @IsNotEmpty() + author: string; + + @IsDateString() + @IsNotEmpty() + authored: string; + + @IsArray() + @IsString({ each: true }) + tags: string[]; + + @IsEnum(CredentialStatus) + @IsNotEmpty() + status: CredentialStatus; +} diff --git a/src/external-services/sunbird-rc/dto/index.ts b/src/external-services/sunbird-rc/dto/index.ts new file mode 100644 index 0000000..23dc5c6 --- /dev/null +++ b/src/external-services/sunbird-rc/dto/index.ts @@ -0,0 +1 @@ +export * from './credentail-schema.dto'; \ No newline at end of file diff --git a/src/external-services/sunbird-rc/schema/index.ts b/src/external-services/sunbird-rc/schema/index.ts new file mode 100644 index 0000000..2a156a2 --- /dev/null +++ b/src/external-services/sunbird-rc/schema/index.ts @@ -0,0 +1 @@ +export * from "./wpcas-credential.schema"; \ No newline at end of file diff --git a/src/external-services/sunbird-rc/schema/wpcas-credential.schema.ts b/src/external-services/sunbird-rc/schema/wpcas-credential.schema.ts new file mode 100644 index 0000000..dc7dd08 --- /dev/null +++ b/src/external-services/sunbird-rc/schema/wpcas-credential.schema.ts @@ -0,0 +1,60 @@ +import { SurveyScoreCredentailDto } from "../dto"; + +export const schema = { + $schema: "https://json-schema.org/draft/2019-09/schema", + description: + "The individual has achieved a score in the most recent peer survey.", + type: "object", + properties: { + dateOfSurveyScore: { + type: "string", + }, + overallScore: { + type: "number", + minimum: 0, + maximum: 100, + }, + competencies: { + type: "array", + items: { + type: "object", + properties: { + name: { + type: "string", + }, + levels: { + type: "array", + items: { + type: "object", + properties: { + name: { + type: "string", + }, + score: { + type: "number", + minimum: 0, + maximum: 100, + }, + }, + required: ["name", "score"], + }, + }, + }, + required: ["id", "name", "levels"], + }, + }, + userId: { + type: "string", + }, + }, + required: ["dateOfSurveyScore", "overallScore", "competencies", "userId"], + additionalProperties: true, +}; + + +export const createSchema = (id: string) => { + return { + $id: id, + ...schema, + } +}; \ No newline at end of file diff --git a/src/external-services/sunbird-rc/sunbird-rc.module.ts b/src/external-services/sunbird-rc/sunbird-rc.module.ts new file mode 100644 index 0000000..22c8b9d --- /dev/null +++ b/src/external-services/sunbird-rc/sunbird-rc.module.ts @@ -0,0 +1,10 @@ +import { Module, forwardRef } from '@nestjs/common'; +import { SunbirdRcService } from './sunbird-rc.service'; +import { CredentialDIDModule } from 'src/credential-did/credential-did.module'; +import { CredentialDIDService } from 'src/credential-did/credential-did.service'; + +@Module({ + providers: [CredentialDIDService, SunbirdRcService], + exports: [SunbirdRcService] +}) +export class SunbirdRcModule {} diff --git a/src/external-services/sunbird-rc/sunbird-rc.service.ts b/src/external-services/sunbird-rc/sunbird-rc.service.ts new file mode 100644 index 0000000..5afd78f --- /dev/null +++ b/src/external-services/sunbird-rc/sunbird-rc.service.ts @@ -0,0 +1,144 @@ +import { Inject, Injectable, NotAcceptableException, forwardRef } from "@nestjs/common"; +import axios from "axios"; +import { SurveyScoreCredentailDto } from "./dto"; +import { CredentialDIDService } from "src/credential-did/credential-did.service"; + +@Injectable() +export class SunbirdRcService { + constructor( + @Inject(forwardRef(()=>CredentialDIDService)) + private credentialDID: CredentialDIDService + ){} + // private credentialUrl = "http://localhost:3000"; + // private credSchemaUrl = "http://localhost:3333"; + // private identityUrl = "http://localhost:3332"; + private sunbirdRcUrl = "http://compass.samagra.io"; + + async addNewCredSchema(schema: any): Promise { + try { + const response = await axios.post( + `${this.sunbirdRcUrl}/credential-schema`, + { + data: { + ...schema + }, + } + ); + return response.data; + } catch (error) { + // Handle errors + throw new Error("Failed to fetch data from the external API"); + } + } + + async fetchCredSchemaByIdAndVersion(schemaDid: string, version: string): Promise { + try { + const response = await axios.get( + `${this.sunbirdRcUrl}/credential-schema/${schemaDid}/${version}` + ); + if(response.status != 200){ + throw new NotAcceptableException(`Authentication failed for the Schema with did #${schemaDid} and version #${version}`); + } + return response.data; + } catch (error) { + // Handle errors + throw new Error("Failed to fetch data from the external API"); + } + } + + async issueCredential(credentialData: SurveyScoreCredentailDto): Promise { + try { + + const dids = await this.credentialDID.findDIDs(); + const response = await axios.post( + `${this.sunbirdRcUrl}/credentials/issue`, + { + data: { + credential: { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1", + ], + type: ["VerifiableCredential", "WPCASSurveyScoreCredential"], + issuer: dids.authorDid, + expirationDate: "2023-02-08T11:56:27.259Z", + credentialSubject: { + ...credentialData + }, + options: { + created: new Date(), + credentialStatus: { + type: "RevocationList2020Status", + }, + }, + }, + credentialSchemaId: dids.schemaDid, + credentialSchemaVersion: dids.schemaVersion, + tags: ["tag1", "tag2", "tag3"], + }, + } + ); + return response.data; + } catch (error) { + // Handle errors + throw new Error(`Failed to issue credentials for user with id #${credentialData.userId}`); + } + } + + async verifyCredentail(did: string): Promise { + try { + const response = await axios.post( + `${this.sunbirdRcUrl}/credentials/${did}/verify` + ); + return response.data; + } catch (error) { + // Handle errors + throw new Error("Failed to fetch data from the external API"); + } + } + + async resolveDid(did: string): Promise { + try { + const response = await axios.post( + `${this.sunbirdRcUrl}/did/resolve/${did}` + ); + if(response.status != 200){ + throw new NotAcceptableException(`Authentication failed for the did #${did}`); + } + return response.data; + } catch (error) { + // Handle errors + throw new Error("Failed to fetch data from the external API"); + } + } + + async renderCredential(credentialDID: string): Promise { + try { + const response = await axios.post( + `${this.sunbirdRcUrl}/credentials/render`, + { + data: { + credentialId: credentialDID, + template: + ' Certificate
Certificate of Completion

This is to certify that

{{name}}

has completed the course

{{programme}}

with score of {{grade}}%




', + output: "PDF", + }, + } + ); + return response.data; + } catch (error) { + // Handle errors + throw new Error("Failed to fetch data from the external API"); + } + } + + async getCredentailById(credentialDID: string): Promise { + try { + const response = await axios.get(`${this.sunbirdRcUrl}/credentials/${credentialDID}`); + return response.data; + } catch (error) { + // Handle errors + throw new Error("Failed to fetch data from the external API"); + } + } +} diff --git a/src/scheduled-tasks/scheduled-tasks.module.ts b/src/scheduled-tasks/scheduled-tasks.module.ts index 84eaf40..4686a1f 100644 --- a/src/scheduled-tasks/scheduled-tasks.module.ts +++ b/src/scheduled-tasks/scheduled-tasks.module.ts @@ -8,16 +8,26 @@ import { SurveyService } from "../survey/survey.service"; import { UserMetadataModule } from "../user-metadata/user-metadata.module"; import { ScheduledTasksService } from "./scheduled-tasks.service"; import { PrismaService } from "../prisma/prisma.service"; +import { AdminCompetencyModule } from "src/admin-competency/admin-competency.module"; +import { SunbirdRcModule } from "src/external-services/sunbird-rc/sunbird-rc.module"; +import { PassbookModule } from "src/external-services/passbook/passbook.module"; @Module({ - imports: [QuestionBankModule, UserMetadataModule, SurveyConfigModule], + imports: [ + QuestionBankModule, + UserMetadataModule, + SurveyConfigModule, + AdminCompetencyModule, + SunbirdRcModule, + PassbookModule, + ], providers: [ ScheduledTasksService, SurveyFormService, SurveyService, SurveyScoreService, ResponseTrackerService, - PrismaService + PrismaService, ], }) export class ScheduledTasksModule {} diff --git a/src/scheduled-tasks/scheduled-tasks.service.ts b/src/scheduled-tasks/scheduled-tasks.service.ts index c33d156..8e42cbd 100644 --- a/src/scheduled-tasks/scheduled-tasks.service.ts +++ b/src/scheduled-tasks/scheduled-tasks.service.ts @@ -8,6 +8,8 @@ import { SurveyScoreService } from "../survey-score/survey-score.service"; import { SurveyService } from "../survey/survey.service"; import { UserMetadataService } from "../user-metadata/user-metadata.service"; import { isDayBeforeToday, isToday } from "../utils/utils"; +import { PassbookService } from "src/external-services/passbook/passbook.service"; +import { SunbirdRcService } from "src/external-services/sunbird-rc/sunbird-rc.service"; @Injectable() export class ScheduledTasksService { @@ -17,34 +19,28 @@ export class ScheduledTasksService { private surveyService: SurveyService, private userMetadataService: UserMetadataService, private surveyFormService: SurveyFormService, - private surveyScoreService: SurveyScoreService + private surveyScoreService: SurveyScoreService, + private passbookService: PassbookService, + private sunbirdRcService: SunbirdRcService ) {} private readonly logger = new Logger(ScheduledTasksService.name); @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT) async surveyCron() { - this.logger.log( - `Start CRON Job for Surveys. Fetching data of all survey configurations.` - ); + this.logger.log(`Start CRON Job for Surveys. Fetching data of all survey configurations.`); const surveyConfigs = await this.surveyConfigService.getAllSurveyConfig({}); - this.logger.log( - `Fetched data of all survey configurations. Cycling through every survey configuration to start or end surveys.` - ); + this.logger.log(`Fetched data of all survey configurations. Cycling through every survey configuration to start or end surveys.`); for (const surveyConfig of surveyConfigs) { - this.logger.log( - `Checking if the surveyConfig "${surveyConfig.surveyName}" with id: "${surveyConfig.id}" is Active.` - ); + this.logger.log(`Checking if the surveyConfig "${surveyConfig.surveyName}" with id: "${surveyConfig.id}" is Active.`); if (surveyConfig.isActive == true) { - this.logger.log(`The survey is ACTIVE.`); + this.logger.log(`The survey #${surveyConfig.surveyName} is ACTIVE.`); if (isDayBeforeToday(surveyConfig.endTime)) { - this.logger.log( - `Survey will end tomorrow. Sending remainder notification to all survey participants.` - ); + // this.logger.log(`Survey will end tomorrow. Sending remainder notification to all survey participants.`); //send notifications } else if (isToday(surveyConfig.endTime)) { this.logger.log(`Fetching all SurveyForms related to the survey.`); @@ -53,48 +49,45 @@ export class ScheduledTasksService { surveyConfig.id ); - this.logger.log( - `Calculation scores for all "${surveyForms.length}" SurveyForms related to the survey.` - ); + this.logger.log(`Calculation scores for all "${surveyForms.length}" SurveyForms related to the survey.`); for (const surveyForm of surveyForms) { - this.logger.log( - `Calculating the score for SurveyForm with id: "${surveyForm.id}" for user with id: "${surveyForm.userId}".` - ); + + this.logger.log(`Calculating the score for SurveyForm with id: "${surveyForm.id}" for user with id: "${surveyForm.userId}".`); await this.surveyScoreService.calculateSurveyScoreBySurveyFormId( surveyForm.id ); - this.logger.log( - `Calculated the score for SurveyForm with id: "${surveyForm.id}" for user with id: "${surveyForm.userId}".` - ); + this.logger.log(`Calculated the score for SurveyForm with id: "${surveyForm.id}" for user with id: "${surveyForm.userId}".`); - this.logger.log( - `Updating the surveyFrom's (id: "${surveyForm.id}") status to "CLOSED".` - ); + this.logger.log(`Updating the surveyFrom's (id: "${surveyForm.id}") status to "CLOSED".`); await this.surveyFormService.updateSurveyFormStatus( surveyForm.id, SurveyStatusEnum.CLOSED ); - this.logger.log(`Updated the surveyFrom's status to "CLOSED."`); + + this.logger.log(`Fetching the data to create a sunbirdRC certificate."`); + const credData = await this.surveyService.createCredentialSchemaBySurveyFormId(surveyForm.id); + + this.logger.log(`Issuing sunbirdRC credentials."`); + const sunbirdCred: any = await this.sunbirdRcService.issueCredential(credData); + + this.logger.log(`Credentials issued. Pushing credentials to passbook."`); + await this.passbookService.addFeedback({ + ...credData, + certificateId: sunbirdCred.id + }); + this.logger.log(`Added credentials to passbook."`); } - this.logger.log( - `Deleting the userMapping for the survey as it will end today.` - ); + this.logger.log(`Deleting the userMapping for the survey as it will end today.`); await this.prismaService.userMapping.deleteMany({ where: { surveyConfigId: surveyConfig.id }, }); - this.logger.log( - `Deleted the userMapping for the survey as it will end today.` - ); this.logger.log(`Deactivating the Survey as it will end today.`); await this.surveyConfigService.updateSurveyConfigById( surveyConfig.id, { isActive: false } ); - this.logger.log( - `Deactivated the Survey "${surveyConfig.surveyName}" with surveyConfigId: "${surveyConfig.id}".` - ); } } else { await this.startSurvey(surveyConfig); @@ -103,52 +96,34 @@ export class ScheduledTasksService { } async startSurvey(surveyConfig: SurveyConfig) { - this.logger.log( - `The survey with surveyConfigId: "${surveyConfig.id}" is not ACTIVE.` - ); + this.logger.log(`The survey with surveyConfigId: "${surveyConfig.id}" is not ACTIVE.`); if (isDayBeforeToday(surveyConfig.startTime)) { //update user data - this.logger.log( - `Fetching userIds of the users who are participating in the survey.` - ); - const updateUsers = - await this.surveyService.fetchUserIdsOfSurveyParticipants( - surveyConfig.id - ); - this.logger.log( - `Updating all "${updateUsers.size}" user's data who are participating in the survey.` + this.logger.log(`Fetching userIds of the users who are participating in the survey.`); + const updateUsers = await this.surveyService.fetchUserIdsOfSurveyParticipants( + surveyConfig.id ); + this.logger.log(`Updating all "${updateUsers.size}" user's data who are participating in the survey.`); for (const userId of updateUsers) { await this.userMetadataService.createOrUpdateUserMetadata(userId); } - this.logger.log( - `Updated all "${updateUsers.size}" user's data who are participating in the survey.` - ); + } else if (isToday(surveyConfig.startTime)) { - this.logger.log( - `Activating the Survey "${surveyConfig.surveyName}" with surveyConfigId: "${surveyConfig.id}".` - ); + + this.logger.log(`Activating the Survey "${surveyConfig.surveyName}" with surveyConfigId: "${surveyConfig.id}".`); await this.surveyConfigService.updateSurveyConfigById(surveyConfig.id, { isActive: true, }); - this.logger.log( - `Activated the Survey "${surveyConfig.surveyName}" with surveyConfigId: "${surveyConfig.id}".` - ); - this.logger.log( - `Generating SurveyForms for every user who is participating in the Survey "${surveyConfig.surveyName}" with id: "${surveyConfig.id}".` - ); + this.logger.log(`Generating SurveyForms for every user who is participating in the Survey "${surveyConfig.surveyName}" with id: "${surveyConfig.id}".`); await this.surveyService.generateSurveyFormsForSurveyConfig( surveyConfig.id ); - this.logger.log( - `Generated SurveyForms for every user who is participating in the Survey "${surveyConfig.surveyName}" with id: "${surveyConfig.id}".` - ); // send notifications for all survey participant - this.logger.log( - `Sending notification to every user who is participating in the Survey "${surveyConfig.surveyName}" with id: "${surveyConfig.id}".` - ); + // this.logger.log( + // `Sending notification to every user who is participating in the Survey "${surveyConfig.surveyName}" with id: "${surveyConfig.id}".` + // ); } } } diff --git a/src/survey-config/survey-config.module.ts b/src/survey-config/survey-config.module.ts index da49838..743f04d 100644 --- a/src/survey-config/survey-config.module.ts +++ b/src/survey-config/survey-config.module.ts @@ -4,11 +4,13 @@ import { PrismaService } from "../prisma/prisma.service"; import { UserMetadataModule } from "../user-metadata/user-metadata.module"; import { SurveyConfigController } from "./survey-config.controller"; import { SurveyConfigService } from "./survey-config.service"; +import { CredentialDIDService } from "src/credential-did/credential-did.service"; +import { SunbirdRcService } from "src/external-services/sunbird-rc/sunbird-rc.service"; @Module({ imports: [forwardRef(() => UserMetadataModule)], controllers: [SurveyConfigController], - providers: [SurveyConfigService, FileUploadService, PrismaService], + providers: [SurveyConfigService, FileUploadService, PrismaService,CredentialDIDService, SunbirdRcService], exports: [SurveyConfigService], }) export class SurveyConfigModule {} diff --git a/src/survey-config/survey-config.service.ts b/src/survey-config/survey-config.service.ts index e8b9182..1fb7153 100644 --- a/src/survey-config/survey-config.service.ts +++ b/src/survey-config/survey-config.service.ts @@ -16,6 +16,7 @@ import { UpdateSurveyConfigDto } from "./dto/update-survey-config.dto"; import { UserMetadataService } from "../user-metadata/user-metadata.service"; import { isDateInPast } from "../utils/utils"; import { UserMappingDTO } from "./dto/response-survey-config.dto"; +import { CredentialDIDService } from "src/credential-did/credential-did.service"; @Injectable() export class SurveyConfigService { @@ -23,7 +24,8 @@ export class SurveyConfigService { private prisma: PrismaService, private fileUploadService: FileUploadService, @Inject(forwardRef(() => UserMetadataService)) - private userMetadataService: UserMetadataService + private userMetadataService: UserMetadataService, + private credentialService: CredentialDIDService ) {} async createSurveyConfig( filepath, @@ -32,6 +34,7 @@ export class SurveyConfigService { let newConfig; let userIdList: Set = new Set(); try { + await this.credentialService.findDIDs(); await this.prisma.$transaction(async (prismaClient) => { newConfig = await prismaClient.surveyConfig.create({ data: { diff --git a/src/survey-form/survey-form.service.ts b/src/survey-form/survey-form.service.ts index 53c0897..d5e2213 100644 --- a/src/survey-form/survey-form.service.ts +++ b/src/survey-form/survey-form.service.ts @@ -46,7 +46,7 @@ export class SurveyFormService { }); if (!surveyForms || surveyForms.length == 0) { throw new NotFoundException( - `Survey Forms not found for SurveyCycleParameter with id #${configId}` + `Survey Forms not found for SurveyConfig with id #${configId}` ); } return surveyForms; diff --git a/src/survey/survey.controller.ts b/src/survey/survey.controller.ts index fefabcc..c813fc4 100644 --- a/src/survey/survey.controller.ts +++ b/src/survey/survey.controller.ts @@ -148,7 +148,7 @@ export class SurveyController { try { this.logger.log(`Initiated fetching Home Screen data for WPCAS.`); - const surveyForm = await this.surveyService.wpcasHomeScreenApi(); + const surveyForm = await this.surveyService.createCredentialSchemaBySurveyFormId(1); this.logger.log(`Successfully fetched Home Screen data for WPCAS.`); diff --git a/src/survey/survey.module.ts b/src/survey/survey.module.ts index 4845a5f..b42ba2a 100644 --- a/src/survey/survey.module.ts +++ b/src/survey/survey.module.ts @@ -1,5 +1,7 @@ import { Module, forwardRef } from "@nestjs/common"; +import { AdminCompetencyModule } from "src/admin-competency/admin-competency.module"; import { SurveyFormService } from "src/survey-form/survey-form.service"; +import { SurveyScoreService } from "src/survey-score/survey-score.service"; import { QuestionBankModule } from "../question-bank/question-bank.module"; import { ResponseTrackerService } from "../response-tracker/response-tracker.service"; import { SurveyConfigModule } from "../survey-config/survey-config.module"; @@ -13,9 +15,10 @@ import { SurveyService } from "./survey.service"; QuestionBankModule, SurveyFormModule, forwardRef(() => UserMetadataModule), - forwardRef(() => SurveyConfigModule) + forwardRef(() => SurveyConfigModule), + AdminCompetencyModule ], - providers: [SurveyService, SurveyFormService, ResponseTrackerService], + providers: [SurveyService, SurveyFormService, ResponseTrackerService, SurveyScoreService], controllers: [SurveyController], exports: [SurveyService], }) diff --git a/src/survey/survey.service.ts b/src/survey/survey.service.ts index c97eb24..4c44985 100644 --- a/src/survey/survey.service.ts +++ b/src/survey/survey.service.ts @@ -1,5 +1,11 @@ -import { Inject, Injectable, NotAcceptableException, NotFoundException, forwardRef } from "@nestjs/common"; -import { ResponseTrackerStatusEnum, SurveyStatusEnum } from "@prisma/client"; +import { + Inject, + Injectable, + NotAcceptableException, + NotFoundException, + forwardRef, +} from "@nestjs/common"; +import { AdminCompetency, ResponseTrackerStatusEnum, SurveyStatusEnum } from "@prisma/client"; import { PrismaService } from "src/prisma/prisma.service"; import { CreateSurveyFormDto, @@ -12,6 +18,9 @@ import { UserMetadataService } from "../user-metadata/user-metadata.service"; import { ResponseTrackerService } from "../response-tracker/response-tracker.service"; import { HomeScreenResponse } from "./dto"; import { SurveyConfigService } from "../survey-config/survey-config.service"; +import { SurveyScoreService } from "src/survey-score/survey-score.service"; +import { CompetencyCredentailDto, SurveyScoreCredentailDto } from "src/external-services/sunbird-rc/dto"; +import { AdminCompetencyService } from "src/admin-competency/admin-competency.service"; @Injectable() export class SurveyService { @@ -19,10 +28,12 @@ export class SurveyService { private prisma: PrismaService, private surveyForm: SurveyFormService, private questionBank: QuestionBankService, - @Inject(forwardRef(()=>UserMetadataService)) + @Inject(forwardRef(() => UserMetadataService)) private userMetadata: UserMetadataService, private responseTracker: ResponseTrackerService, - private surveyConfig: SurveyConfigService + private surveyConfig: SurveyConfigService, + private surveyScore: SurveyScoreService, + private adminCompetency: AdminCompetencyService ) {} async getSurveysToBeFilledByUser(userId: string) { @@ -106,10 +117,14 @@ export class SurveyService { async generateSurveyFormsForSurveyConfig(configId: number) { let surveyForms: ResponseSurveyFormDto[] = []; - const surveyConfig = await this.surveyConfig.getAllSurveyConfig({configId: configId}); + const surveyConfig = await this.surveyConfig.getAllSurveyConfig({ + configId: configId, + }); - if(surveyConfig[0].isActive == false) { - throw new NotAcceptableException(`The survey with name: ${surveyConfig[0].surveyName} is not 'ACTIVE', thus cannot create the surveyForms.`); + if (surveyConfig[0].isActive == false) { + throw new NotAcceptableException( + `The survey with name: ${surveyConfig[0].surveyName} is not 'ACTIVE', thus cannot create the surveyForms.` + ); } //logic to fetch user mapping @@ -154,14 +169,18 @@ export class SurveyService { return surveyForms; } - public async fetchUserIdsOfSurveyParticipants(surveyConfigId: number): Promise> { + public async fetchUserIdsOfSurveyParticipants( + surveyConfigId: number + ): Promise> { try { let userIdList: Set = new Set(); const userMappings = await this.prisma.userMapping.findMany({ where: { surveyConfigId }, }); - for(const userMapping of userMappings){ - const userValidationArray = [...new Set([...userMapping.assessorIds, userMapping.assesseeId])]; + for (const userMapping of userMappings) { + const userValidationArray = [ + ...new Set([...userMapping.assessorIds, userMapping.assesseeId]), + ]; userIdList = new Set([...userIdList, ...userValidationArray]); } return userIdList; @@ -170,13 +189,90 @@ export class SurveyService { } } - public async wpcasHomeScreenApi(): Promise{ + public async wpcasHomeScreenApi(): Promise { try { const users = await this.userMetadata.findManyUserMetadata({}); const surveyData = await this.responseTracker.fetchActiveSurveyFormData(); - return {users, surveyData}; + return { users, surveyData }; + } catch (error) { + throw error; + } + } + + public async createCredentialSchemaBySurveyFormId(surveyFormId: number) { + try { + let returnObj: SurveyScoreCredentailDto = {} as SurveyScoreCredentailDto; + let competencies: AdminCompetency[] = [] as AdminCompetency[]; + + const surveyForm = await this.surveyForm.findSurveyFormById( + surveyFormId + ); + const surveyScores = await this.surveyScore.findBySurveyFormId( + surveyFormId + ); + + const distinctCompetencyIds = _.uniqBy(surveyScores, 'competencyId').map(obj => obj.competencyId); + for(const competencyId of distinctCompetencyIds){ + const competency = await this.adminCompetency.findOne(competencyId); + competencies.push(competency); + } + + returnObj.userId = surveyForm.userId; + returnObj.dateOfSurveyScore = surveyForm.createdAt.toISOString(); + returnObj.overallScore = surveyForm.overallScore; + returnObj.competencies = await this.transformData(surveyScores, competencies); + + return returnObj; } catch (error) { throw error; } } + + public async transformData(scores, competencies) { + const result= [] as CompetencyCredentailDto[]; + + // Group scores by surveyFormId and competencyId + const groupedScores = _.groupBy(scores, 'surveyFormId'); + + // Iterate through competencies and transform the data + competencies.forEach((competency) => { + const competencyId = competency.id; + const competencyName = competency.name; + + const levels = competency.competencyLevels.map((level) => { + const levelNumber = level.competencyLevelNumber; + const levelName = level.competencyLevelName; + + // Find scores for the current competency and level + const competencyScores = groupedScores[competencyId] || []; + const matchingScore = competencyScores.find((score) => score.competencyLevelNumber === levelNumber); + + // Calculate score percentage + const scorePercentage = matchingScore ? matchingScore.score.toFixed(2) : 'N/A'; + if(scorePercentage != 'N/A'){ + return { + levelNumber, + name: levelName, + score: parseFloat(scorePercentage), + }; + } else { + return null; + } + }); + + const filteredLevels = _.compact(levels); + + // Add the transformed data to the result array + result.push({ + id: competencyId, + name: competencyName, + levels: filteredLevels, + } as CompetencyCredentailDto); + }); + + // Sort result array by competency ID + const sortedResult = _.sortBy(result, 'id'); + + return sortedResult; + } } From 6d48a3773506f1aa7ff060f19e89adedb686feb0 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 Date: Tue, 12 Dec 2023 14:50:42 +0530 Subject: [PATCH 155/175] commit 2. --- package-lock.json | 67 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index c5884c4..2fdcea7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@nestjs/schedule": "^3.0.4", "@nestjs/swagger": "^7.1.11", "@prisma/client": "^5.4.2", + "axios": "^1.6.2", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "csv-parser": "^3.0.0", @@ -3548,8 +3549,17 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "node_modules/axios": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } }, "node_modules/babel-jest": { "version": "28.1.3", @@ -4198,7 +4208,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -4451,7 +4460,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -5347,6 +5355,25 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/fork-ts-checker-webpack-plugin": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-8.0.0.tgz", @@ -5445,6 +5472,19 @@ "node": ">=8" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/form-data-lite": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/form-data-lite/-/form-data-lite-1.0.3.tgz", @@ -9093,6 +9133,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -9693,20 +9738,6 @@ } } }, - "node_modules/supertest/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/supertest/node_modules/formidable": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.1.tgz", From cc02cead6bb1fcb392869f82241312d2c3b48b9d Mon Sep 17 00:00:00 2001 From: VamshiBatta07 Date: Thu, 14 Dec 2023 16:18:33 +0530 Subject: [PATCH 156/175] Bug Fixes. --- .../20231212091810_wpcas_init/migration.sql | 281 ------------------ .../migration.sql | 12 - src/survey/survey.controller.ts | 2 +- 3 files changed, 1 insertion(+), 294 deletions(-) delete mode 100644 prisma/migrations/20231212091810_wpcas_init/migration.sql delete mode 100644 prisma/migrations/20231212091927_new_table_to_store_credential_dids/migration.sql diff --git a/prisma/migrations/20231212091810_wpcas_init/migration.sql b/prisma/migrations/20231212091810_wpcas_init/migration.sql deleted file mode 100644 index 8ce0e34..0000000 --- a/prisma/migrations/20231212091810_wpcas_init/migration.sql +++ /dev/null @@ -1,281 +0,0 @@ --- CreateEnum -CREATE TYPE "SurveyStatusEnum" AS ENUM ('CREATED', 'PUBLISHED', 'CLOSED'); - --- CreateEnum -CREATE TYPE "ResponseTrackerStatusEnum" AS ENUM ('PENDING', 'COMPLETED'); - --- CreateEnum -CREATE TYPE "TimeUnitsEnum" AS ENUM ('DAY', 'MONTH', 'YEAR'); - --- CreateEnum -CREATE TYPE "UserRolesEnum" AS ENUM ('ADMIN', 'CONSUMER'); - --- CreateTable -CREATE TABLE "UserMetadata" ( - "id" SERIAL NOT NULL, - "userId" UUID NOT NULL, - "userName" TEXT NOT NULL, - "profilePicture" TEXT, - "isNewEmployee" BOOLEAN NOT NULL, - "designation" TEXT, - "dateOfJoining" TIMESTAMP(3) NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "isAdmin" BOOLEAN NOT NULL, - - CONSTRAINT "UserMetadata_pkey" PRIMARY KEY ("id","userId") -); - --- CreateTable -CREATE TABLE "question_bank" ( - "id" SERIAL NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "competencyId" INTEGER NOT NULL, - "competencyLevelNumber" INTEGER NOT NULL, - "question" TEXT NOT NULL, - - CONSTRAINT "question_bank_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "admin_competencies" ( - "id" SERIAL NOT NULL, - "competencyId" INTEGER NOT NULL, - "name" TEXT NOT NULL, - "description" TEXT, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "competencyLevels" JSONB[], - - CONSTRAINT "admin_competencies_pkey" PRIMARY KEY ("id","competencyId") -); - --- CreateTable -CREATE TABLE "survey_config" ( - "id" SERIAL NOT NULL, - "surveyName" TEXT NOT NULL, - "onboardingTime" INTEGER NOT NULL DEFAULT 3, - "onboardingTimeUnit" "TimeUnitsEnum" NOT NULL DEFAULT 'MONTH', - "startTime" TIMESTAMP(3) NOT NULL, - "endTime" TIMESTAMP(3) NOT NULL, - "isActive" BOOLEAN NOT NULL DEFAULT false, - - CONSTRAINT "survey_config_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "UserMapping" ( - "id" SERIAL NOT NULL, - "surveyConfigId" INTEGER NOT NULL, - "assesseeId" UUID NOT NULL, - "assessorIds" UUID[], - - CONSTRAINT "UserMapping_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "survey_form" ( - "id" SERIAL NOT NULL, - "userId" UUID NOT NULL, - "questionsJson" JSONB NOT NULL, - "surveyConfigId" INTEGER NOT NULL, - "status" "SurveyStatusEnum" NOT NULL, - "overallScore" DOUBLE PRECISION, - "sunbirdCredentialIds" TEXT, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "survey_form_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "response_tracker" ( - "id" SERIAL NOT NULL, - "surveyFormId" INTEGER NOT NULL, - "assesseeId" UUID NOT NULL, - "assessorId" UUID NOT NULL, - "responseJson" JSONB[] DEFAULT ARRAY[]::JSONB[], - "status" "ResponseTrackerStatusEnum" NOT NULL, - - CONSTRAINT "response_tracker_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "survey_scores" ( - "id" SERIAL NOT NULL, - "surveyFormId" INTEGER NOT NULL, - "competencyId" INTEGER NOT NULL, - "competencyLevelNumber" INTEGER NOT NULL, - "score" DOUBLE PRECISION NOT NULL, - - CONSTRAINT "survey_scores_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "users" ( - "id" TEXT NOT NULL, - "email" TEXT NOT NULL, - "role" "UserRolesEnum" NOT NULL, - "userName" TEXT NOT NULL, - "password" TEXT NOT NULL, - "profilePicture" TEXT, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "designation" TEXT NOT NULL, - "levelNumber" INTEGER, - - CONSTRAINT "users_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "Level" ( - "levelNumber" INTEGER NOT NULL, - "description" TEXT, - - CONSTRAINT "Level_pkey" PRIMARY KEY ("levelNumber") -); - --- CreateTable -CREATE TABLE "Designation" ( - "id" SERIAL NOT NULL, - "name" TEXT NOT NULL, - "description" TEXT, - - CONSTRAINT "Designation_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "roles" ( - "id" SERIAL NOT NULL, - "name" TEXT NOT NULL, - "description" TEXT, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "designationId" INTEGER, - - CONSTRAINT "roles_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "competencies" ( - "id" SERIAL NOT NULL, - "name" TEXT NOT NULL, - "description" TEXT, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "competencies_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "RoleToCompetency" ( - "roleId" INTEGER NOT NULL, - "competencyId" INTEGER NOT NULL, - - CONSTRAINT "RoleToCompetency_pkey" PRIMARY KEY ("roleId","competencyId") -); - --- CreateTable -CREATE TABLE "competency_levels" ( - "id" SERIAL NOT NULL, - "name" TEXT NOT NULL, - "description" TEXT, - "levelNumber" INTEGER NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "competency_levels_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "CompetencyToCompetencyLevel" ( - "competencyId" INTEGER NOT NULL, - "competencyLevelId" INTEGER NOT NULL, - - CONSTRAINT "CompetencyToCompetencyLevel_pkey" PRIMARY KEY ("competencyId","competencyLevelId") -); - --- CreateIndex -CREATE UNIQUE INDEX "UserMetadata_id_key" ON "UserMetadata"("id"); - --- CreateIndex -CREATE UNIQUE INDEX "UserMetadata_userId_key" ON "UserMetadata"("userId"); - --- CreateIndex -CREATE UNIQUE INDEX "UserMetadata_userName_key" ON "UserMetadata"("userName"); - --- CreateIndex -CREATE UNIQUE INDEX "question_bank_competencyId_competencyLevelNumber_question_key" ON "question_bank"("competencyId", "competencyLevelNumber", "question"); - --- CreateIndex -CREATE UNIQUE INDEX "admin_competencies_competencyId_key" ON "admin_competencies"("competencyId"); - --- CreateIndex -CREATE UNIQUE INDEX "survey_form_userId_surveyConfigId_key" ON "survey_form"("userId", "surveyConfigId"); - --- CreateIndex -CREATE UNIQUE INDEX "response_tracker_surveyFormId_assesseeId_assessorId_key" ON "response_tracker"("surveyFormId", "assesseeId", "assessorId"); - --- CreateIndex -CREATE UNIQUE INDEX "survey_scores_surveyFormId_competencyId_competencyLevelNumb_key" ON "survey_scores"("surveyFormId", "competencyId", "competencyLevelNumber"); - --- CreateIndex -CREATE UNIQUE INDEX "users_email_key" ON "users"("email"); - --- CreateIndex -CREATE UNIQUE INDEX "Level_levelNumber_key" ON "Level"("levelNumber"); - --- CreateIndex -CREATE UNIQUE INDEX "Designation_name_key" ON "Designation"("name"); - --- CreateIndex -CREATE UNIQUE INDEX "roles_name_key" ON "roles"("name"); - --- CreateIndex -CREATE UNIQUE INDEX "competencies_name_key" ON "competencies"("name"); - --- CreateIndex -CREATE UNIQUE INDEX "competency_levels_name_key" ON "competency_levels"("name"); - --- AddForeignKey -ALTER TABLE "UserMapping" ADD CONSTRAINT "UserMapping_surveyConfigId_fkey" FOREIGN KEY ("surveyConfigId") REFERENCES "survey_config"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "survey_form" ADD CONSTRAINT "survey_form_userId_fkey" FOREIGN KEY ("userId") REFERENCES "UserMetadata"("userId") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "survey_form" ADD CONSTRAINT "survey_form_surveyConfigId_fkey" FOREIGN KEY ("surveyConfigId") REFERENCES "survey_config"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "response_tracker" ADD CONSTRAINT "response_tracker_surveyFormId_fkey" FOREIGN KEY ("surveyFormId") REFERENCES "survey_form"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "response_tracker" ADD CONSTRAINT "response_tracker_assesseeId_fkey" FOREIGN KEY ("assesseeId") REFERENCES "UserMetadata"("userId") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "response_tracker" ADD CONSTRAINT "response_tracker_assessorId_fkey" FOREIGN KEY ("assessorId") REFERENCES "UserMetadata"("userId") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "survey_scores" ADD CONSTRAINT "survey_scores_surveyFormId_fkey" FOREIGN KEY ("surveyFormId") REFERENCES "survey_form"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "users" ADD CONSTRAINT "users_levelNumber_fkey" FOREIGN KEY ("levelNumber") REFERENCES "Level"("levelNumber") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "roles" ADD CONSTRAINT "roles_designationId_fkey" FOREIGN KEY ("designationId") REFERENCES "Designation"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "RoleToCompetency" ADD CONSTRAINT "RoleToCompetency_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "roles"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "RoleToCompetency" ADD CONSTRAINT "RoleToCompetency_competencyId_fkey" FOREIGN KEY ("competencyId") REFERENCES "competencies"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "competency_levels" ADD CONSTRAINT "competency_levels_levelNumber_fkey" FOREIGN KEY ("levelNumber") REFERENCES "Level"("levelNumber") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "CompetencyToCompetencyLevel" ADD CONSTRAINT "CompetencyToCompetencyLevel_competencyId_fkey" FOREIGN KEY ("competencyId") REFERENCES "competencies"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "CompetencyToCompetencyLevel" ADD CONSTRAINT "CompetencyToCompetencyLevel_competencyLevelId_fkey" FOREIGN KEY ("competencyLevelId") REFERENCES "competency_levels"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20231212091927_new_table_to_store_credential_dids/migration.sql b/prisma/migrations/20231212091927_new_table_to_store_credential_dids/migration.sql deleted file mode 100644 index 2de7557..0000000 --- a/prisma/migrations/20231212091927_new_table_to_store_credential_dids/migration.sql +++ /dev/null @@ -1,12 +0,0 @@ --- CreateTable -CREATE TABLE "credential_did" ( - "id" SERIAL NOT NULL, - "authorDid" TEXT NOT NULL, - "schemaDid" TEXT NOT NULL, - "schemaVersion" TEXT NOT NULL, - - CONSTRAINT "credential_did_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "credential_did_schemaDid_schemaVersion_key" ON "credential_did"("schemaDid", "schemaVersion"); diff --git a/src/survey/survey.controller.ts b/src/survey/survey.controller.ts index c813fc4..fefabcc 100644 --- a/src/survey/survey.controller.ts +++ b/src/survey/survey.controller.ts @@ -148,7 +148,7 @@ export class SurveyController { try { this.logger.log(`Initiated fetching Home Screen data for WPCAS.`); - const surveyForm = await this.surveyService.createCredentialSchemaBySurveyFormId(1); + const surveyForm = await this.surveyService.wpcasHomeScreenApi(); this.logger.log(`Successfully fetched Home Screen data for WPCAS.`); From d9ef5a8487cb617964a3c2d5faa55b83d5d7097e Mon Sep 17 00:00:00 2001 From: VamshiBatta07 Date: Thu, 14 Dec 2023 18:48:41 +0530 Subject: [PATCH 157/175] Credential table migration and bug fixes. --- .../migration.sql | 12 ++++++++++++ src/credential-did/credential-did.service.ts | 19 +++++++++++-------- .../dto/create-update-did.dto.ts | 5 ++++- 3 files changed, 27 insertions(+), 9 deletions(-) create mode 100644 prisma/migrations/20231214105545_credential_database/migration.sql diff --git a/prisma/migrations/20231214105545_credential_database/migration.sql b/prisma/migrations/20231214105545_credential_database/migration.sql new file mode 100644 index 0000000..2de7557 --- /dev/null +++ b/prisma/migrations/20231214105545_credential_database/migration.sql @@ -0,0 +1,12 @@ +-- CreateTable +CREATE TABLE "credential_did" ( + "id" SERIAL NOT NULL, + "authorDid" TEXT NOT NULL, + "schemaDid" TEXT NOT NULL, + "schemaVersion" TEXT NOT NULL, + + CONSTRAINT "credential_did_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "credential_did_schemaDid_schemaVersion_key" ON "credential_did"("schemaDid", "schemaVersion"); diff --git a/src/credential-did/credential-did.service.ts b/src/credential-did/credential-did.service.ts index ac64739..3853b6e 100644 --- a/src/credential-did/credential-did.service.ts +++ b/src/credential-did/credential-did.service.ts @@ -20,15 +20,18 @@ export class CredentialDIDService { try { await this.sunbirdRc.resolveDid(createOrUpdateDID.authorDid); await this.sunbirdRc.fetchCredSchemaByIdAndVersion(createOrUpdateDID.schemaDid, createOrUpdateDID.schemaVersion); - const did = await this.prisma.credentialDid.findMany(); + const did = await this.prisma.credentialDid.findFirst(); - return await this.prisma.credentialDid.upsert({ - where: { - id: did[0].id ?? 1, - }, - update: createOrUpdateDID, - create: createOrUpdateDID, - }); + if (!did) { + return await this.prisma.credentialDid.create({ + data: createOrUpdateDID, + }); + } else { + return await this.prisma.credentialDid.update({ + where: { id: did.id }, + data: createOrUpdateDID, + }); + } } catch (error) { throw error; } diff --git a/src/credential-did/dto/create-update-did.dto.ts b/src/credential-did/dto/create-update-did.dto.ts index 02ae31a..d0b1c70 100644 --- a/src/credential-did/dto/create-update-did.dto.ts +++ b/src/credential-did/dto/create-update-did.dto.ts @@ -1,16 +1,19 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsOptional } from 'class-validator'; +import { IsNotEmpty, IsString } from 'class-validator'; export class CreateUpdateDIDDto { @ApiProperty({ required: false, description: 'Author DID' }) + @IsString() @IsNotEmpty() authorDid: string; @ApiProperty({ required: false, description: 'Schema DID' }) + @IsString() @IsNotEmpty() schemaDid: string; @ApiProperty({ required: false, description: 'Schema DID' }) + @IsString() @IsNotEmpty() schemaVersion: string; } \ No newline at end of file From 581badc25fe73c9e37790bf27d93ea54358fb7bd Mon Sep 17 00:00:00 2001 From: VamshiBatta07 Date: Thu, 14 Dec 2023 18:50:30 +0530 Subject: [PATCH 158/175] CRON job, sunbirdRC and credential tabled api bug fixes. --- src/app.module.ts | 4 +- .../sunbird-rc/sunbird-rc.service.ts | 16 +++-- .../scheduled-tasks.service.ts | 67 +++++++++++++------ src/survey-config/survey-config.service.ts | 11 +++ src/survey-form/survey-form.service.ts | 10 +++ src/survey/survey.service.ts | 2 +- src/utils/utils.ts | 17 +++++ 7 files changed, 96 insertions(+), 31 deletions(-) diff --git a/src/app.module.ts b/src/app.module.ts index 0eeb0d2..1ffe9e7 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -17,6 +17,7 @@ import { ScheduleModule } from "@nestjs/schedule"; import { SurveyModule } from './survey/survey.module'; import { FileUploadModule } from './file-upload/file-upload.module'; import { ExternalServicesModule } from "./external-services/external-services.module"; +import { CredentialDIDModule } from "./credential-did/credential-did.module"; @Module({ imports: [ @@ -36,7 +37,8 @@ import { ExternalServicesModule } from "./external-services/external-services.mo ScheduleModule.forRoot(), SurveyModule, FileUploadModule, - ExternalServicesModule + ExternalServicesModule, + CredentialDIDModule ], controllers: [AppController], providers: [AppService, PrismaService], diff --git a/src/external-services/sunbird-rc/sunbird-rc.service.ts b/src/external-services/sunbird-rc/sunbird-rc.service.ts index 5afd78f..39f9714 100644 --- a/src/external-services/sunbird-rc/sunbird-rc.service.ts +++ b/src/external-services/sunbird-rc/sunbird-rc.service.ts @@ -42,7 +42,7 @@ export class SunbirdRcService { return response.data; } catch (error) { // Handle errors - throw new Error("Failed to fetch data from the external API"); + throw new Error("Failed to fetch data from the external API(fetchCredSchemaByIdAndVersion)"); } } @@ -50,6 +50,8 @@ export class SunbirdRcService { try { const dids = await this.credentialDID.findDIDs(); + const creationDate = new Date(); + const expirationDate = new Date(creationDate).setFullYear(creationDate.getFullYear() + 1); const response = await axios.post( `${this.sunbirdRcUrl}/credentials/issue`, { @@ -61,12 +63,12 @@ export class SunbirdRcService { ], type: ["VerifiableCredential", "WPCASSurveyScoreCredential"], issuer: dids.authorDid, - expirationDate: "2023-02-08T11:56:27.259Z", + expirationDate: expirationDate, credentialSubject: { ...credentialData }, options: { - created: new Date(), + created: creationDate.toISOString(), credentialStatus: { type: "RevocationList2020Status", }, @@ -93,7 +95,7 @@ export class SunbirdRcService { return response.data; } catch (error) { // Handle errors - throw new Error("Failed to fetch data from the external API"); + throw new Error("Failed to fetch data from the external API(verifyCredentail)"); } } @@ -108,7 +110,7 @@ export class SunbirdRcService { return response.data; } catch (error) { // Handle errors - throw new Error("Failed to fetch data from the external API"); + throw new Error("Failed to fetch data from the external API(resolveDid)"); } } @@ -128,7 +130,7 @@ export class SunbirdRcService { return response.data; } catch (error) { // Handle errors - throw new Error("Failed to fetch data from the external API"); + throw new Error("Failed to fetch data from the external API(renderCredential)"); } } @@ -138,7 +140,7 @@ export class SunbirdRcService { return response.data; } catch (error) { // Handle errors - throw new Error("Failed to fetch data from the external API"); + throw new Error("Failed to fetch data from the external API(getCredentailById)"); } } } diff --git a/src/scheduled-tasks/scheduled-tasks.service.ts b/src/scheduled-tasks/scheduled-tasks.service.ts index 8e42cbd..acfa765 100644 --- a/src/scheduled-tasks/scheduled-tasks.service.ts +++ b/src/scheduled-tasks/scheduled-tasks.service.ts @@ -7,7 +7,7 @@ import { SurveyFormService } from "../survey-form/survey-form.service"; import { SurveyScoreService } from "../survey-score/survey-score.service"; import { SurveyService } from "../survey/survey.service"; import { UserMetadataService } from "../user-metadata/user-metadata.service"; -import { isDayBeforeToday, isToday } from "../utils/utils"; +import { isDayBeforeToday, isToday, isTomorrow } from "../utils/utils"; import { PassbookService } from "src/external-services/passbook/passbook.service"; import { SunbirdRcService } from "src/external-services/sunbird-rc/sunbird-rc.service"; @@ -40,7 +40,7 @@ export class ScheduledTasksService { this.logger.log(`The survey #${surveyConfig.surveyName} is ACTIVE.`); if (isDayBeforeToday(surveyConfig.endTime)) { - // this.logger.log(`Survey will end tomorrow. Sending remainder notification to all survey participants.`); + this.logger.log(`Survey will end tomorrow. Sending remainder notification to all survey participants.`); //send notifications } else if (isToday(surveyConfig.endTime)) { this.logger.log(`Fetching all SurveyForms related to the survey.`); @@ -52,30 +52,47 @@ export class ScheduledTasksService { this.logger.log(`Calculation scores for all "${surveyForms.length}" SurveyForms related to the survey.`); for (const surveyForm of surveyForms) { + this.logger.log(`Updating the surveyFrom's (id: "${surveyForm.id}") status to "CLOSED".`); + await this.surveyFormService.updateSurveyFormStatus( + surveyForm.id, + SurveyStatusEnum.CLOSED + ); + this.logger.log(`Calculating the score for SurveyForm with id: "${surveyForm.id}" for user with id: "${surveyForm.userId}".`); await this.surveyScoreService.calculateSurveyScoreBySurveyFormId( surveyForm.id ); this.logger.log(`Calculated the score for SurveyForm with id: "${surveyForm.id}" for user with id: "${surveyForm.userId}".`); - this.logger.log(`Updating the surveyFrom's (id: "${surveyForm.id}") status to "CLOSED".`); - await this.surveyFormService.updateSurveyFormStatus( - surveyForm.id, - SurveyStatusEnum.CLOSED - ); - this.logger.log(`Fetching the data to create a sunbirdRC certificate."`); + this.logger.log(`Fetching the data to create a sunbirdRC certificate.`); const credData = await this.surveyService.createCredentialSchemaBySurveyFormId(surveyForm.id); - this.logger.log(`Issuing sunbirdRC credentials."`); - const sunbirdCred: any = await this.sunbirdRcService.issueCredential(credData); - - this.logger.log(`Credentials issued. Pushing credentials to passbook."`); - await this.passbookService.addFeedback({ - ...credData, - certificateId: sunbirdCred.id - }); - this.logger.log(`Added credentials to passbook."`); + this.logger.log(`Issuing sunbirdRC credentials.`); + let sunbirdCred : any; + try { + sunbirdCred = await this.sunbirdRcService.issueCredential(credData); + } catch (error) { + this.logger.error(error); + } + + if(sunbirdCred){ + try { + this.logger.log(`Credentials issued. Pushing credentials to passbook.`); + await this.passbookService.addFeedback({ + ...credData, + certificateId: sunbirdCred.id + }); + this.logger.log(`Added credentials to passbook."`); + + } catch (error) { + this.logger.error(error); + } + + this.logger.log(`Updating credentials and overall score in the surveyFrom.`); + await this.surveyFormService.updateOverallScoreAndCredentialDid(surveyForm.id, credData.overallScore, sunbirdCred.id); + + } } this.logger.log(`Deleting the userMapping for the survey as it will end today.`); @@ -83,11 +100,14 @@ export class ScheduledTasksService { where: { surveyConfigId: surveyConfig.id }, }); - this.logger.log(`Deactivating the Survey as it will end today.`); - await this.surveyConfigService.updateSurveyConfigById( - surveyConfig.id, - { isActive: false } + this.logger.log(`Deactivating the Survey "${surveyConfig.surveyName}" as it will end today.`); + await this.surveyConfigService.deactivateSurveyConfig( + surveyConfig.id ); + this.logger.log(`The Survey "${surveyConfig.surveyName}" ends today.`); + + } else { + this.logger.log(`The survey #"${surveyConfig.surveyName}" with surveyConfigId: "${surveyConfig.id}" is ongoing.`); } } else { await this.startSurvey(surveyConfig); @@ -97,7 +117,7 @@ export class ScheduledTasksService { async startSurvey(surveyConfig: SurveyConfig) { this.logger.log(`The survey with surveyConfigId: "${surveyConfig.id}" is not ACTIVE.`); - if (isDayBeforeToday(surveyConfig.startTime)) { + if (isTomorrow(surveyConfig.startTime)) { //update user data this.logger.log(`Fetching userIds of the users who are participating in the survey.`); const updateUsers = await this.surveyService.fetchUserIdsOfSurveyParticipants( @@ -119,11 +139,14 @@ export class ScheduledTasksService { await this.surveyService.generateSurveyFormsForSurveyConfig( surveyConfig.id ); + this.logger.log(`The Survey "${surveyConfig.surveyName}" with id: "${surveyConfig.id}" has been started.`); // send notifications for all survey participant // this.logger.log( // `Sending notification to every user who is participating in the Survey "${surveyConfig.surveyName}" with id: "${surveyConfig.id}".` // ); + } else { + this.logger.log(`The survey with surveyConfigId: "${surveyConfig.id}" is to be Activated in the future or a dead survey.`); } } } diff --git a/src/survey-config/survey-config.service.ts b/src/survey-config/survey-config.service.ts index 1fb7153..baf4105 100644 --- a/src/survey-config/survey-config.service.ts +++ b/src/survey-config/survey-config.service.ts @@ -191,6 +191,17 @@ export class SurveyConfigService { return deletedSurveyConfig; } + async deactivateSurveyConfig(surveyConfigId: number) { + return await this.prisma.surveyConfig.update({ + where: { + id: surveyConfigId, + }, + data: { + isActive: false + }, + }); + } + public async getUserMappingSampleForSurveyConfig(): Promise { return [ { diff --git a/src/survey-form/survey-form.service.ts b/src/survey-form/survey-form.service.ts index d5e2213..a669e6f 100644 --- a/src/survey-form/survey-form.service.ts +++ b/src/survey-form/survey-form.service.ts @@ -174,4 +174,14 @@ export class SurveyFormService { } return latestScore[0].overallScore; } + + async updateOverallScoreAndCredentialDid(surveyFormId: number, overallScore: number|null, sunbirdCredentialIds: string){ + return await this.prisma.surveyForm.update({ + where:{id: surveyFormId}, + data:{ + overallScore, + sunbirdCredentialIds + } + }) + } } diff --git a/src/survey/survey.service.ts b/src/survey/survey.service.ts index 4c44985..319efa9 100644 --- a/src/survey/survey.service.ts +++ b/src/survey/survey.service.ts @@ -232,7 +232,7 @@ export class SurveyService { const result= [] as CompetencyCredentailDto[]; // Group scores by surveyFormId and competencyId - const groupedScores = _.groupBy(scores, 'surveyFormId'); + const groupedScores = _.groupBy(scores, 'competencyId'); // Iterate through competencies and transform the data competencies.forEach((competency) => { diff --git a/src/utils/utils.ts b/src/utils/utils.ts index da491f5..53b66c6 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -48,6 +48,20 @@ export function getPrismaErrorStatusAndMessage(error: any): { }; } +export function isTomorrow(inputDate: Date): boolean { + const currentDate = new Date(); + + const tomorrow = new Date(currentDate); + tomorrow.setDate(currentDate.getDate() + 1); + + tomorrow.setHours(0, 0, 0, 0); + + inputDate.setHours(0, 0, 0, 0); + + // Compare the input date with tomorrow + return inputDate.getTime() === tomorrow.getTime(); +} + export function isDayBeforeToday(date) { const today = new Date(); today.setHours(0, 0, 0, 0); @@ -63,6 +77,7 @@ export function isToday(date) { today.setHours(0, 0, 0, 0); const givenDate = new Date(date); + givenDate.setHours(0, 0, 0, 0); return givenDate.getTime() === today.getTime(); } @@ -70,7 +85,9 @@ export function isToday(date) { export function isDateInPast(compareDate: Date, referenceDate: Date): boolean { // Convert both dates to UTC to ensure accurate comparison const compareDateUTC = new Date(compareDate.toUTCString()); + compareDateUTC.setHours(0, 0, 0, 0); const referenceDateUTC = new Date(referenceDate.toUTCString()); + referenceDateUTC.setHours(0, 0, 0, 0); // Compare the dates return compareDateUTC < referenceDateUTC; From 40f77d68af9da4222e29ef9573a5c8996a1d6b68 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 Date: Thu, 14 Dec 2023 19:03:31 +0530 Subject: [PATCH 159/175] Removing unnecessary functions. --- .../admin-competency.service.ts | 18 ++-- .../sunbird-rc/sunbird-rc.service.ts | 92 ++++++++----------- 2 files changed, 45 insertions(+), 65 deletions(-) diff --git a/src/admin-competency/admin-competency.service.ts b/src/admin-competency/admin-competency.service.ts index 3e153d2..04b73c4 100644 --- a/src/admin-competency/admin-competency.service.ts +++ b/src/admin-competency/admin-competency.service.ts @@ -139,13 +139,13 @@ export class AdminCompetencyService { return adminCompetency; } - public async fetchCompetencyLevel(competencyId:number, competencyLevelNumber: number){ - try { - const competency = await this.findOne(competencyId); - const competencyLevel = _.get(competency, "competencyLevels", []); - return _.find(competencyLevel, competencyLevelNumber); - } catch (error) { - throw error; - } - } + // public async fetchCompetencyLevel(competencyId:number, competencyLevelNumber: number){ + // try { + // const competency = await this.findOne(competencyId); + // const competencyLevel = _.get(competency, "competencyLevels", []); + // return _.find(competencyLevel, competencyLevelNumber); + // } catch (error) { + // throw error; + // } + // } } diff --git a/src/external-services/sunbird-rc/sunbird-rc.service.ts b/src/external-services/sunbird-rc/sunbird-rc.service.ts index 39f9714..3b3153b 100644 --- a/src/external-services/sunbird-rc/sunbird-rc.service.ts +++ b/src/external-services/sunbird-rc/sunbird-rc.service.ts @@ -14,22 +14,22 @@ export class SunbirdRcService { // private identityUrl = "http://localhost:3332"; private sunbirdRcUrl = "http://compass.samagra.io"; - async addNewCredSchema(schema: any): Promise { - try { - const response = await axios.post( - `${this.sunbirdRcUrl}/credential-schema`, - { - data: { - ...schema - }, - } - ); - return response.data; - } catch (error) { - // Handle errors - throw new Error("Failed to fetch data from the external API"); - } - } + // async addNewCredSchema(schema: any): Promise { + // try { + // const response = await axios.post( + // `${this.sunbirdRcUrl}/credential-schema`, + // { + // data: { + // ...schema + // }, + // } + // ); + // return response.data; + // } catch (error) { + // // Handle errors + // throw new Error("Failed to fetch data from the external API"); + // } + // } async fetchCredSchemaByIdAndVersion(schemaDid: string, version: string): Promise { try { @@ -87,17 +87,17 @@ export class SunbirdRcService { } } - async verifyCredentail(did: string): Promise { - try { - const response = await axios.post( - `${this.sunbirdRcUrl}/credentials/${did}/verify` - ); - return response.data; - } catch (error) { - // Handle errors - throw new Error("Failed to fetch data from the external API(verifyCredentail)"); - } - } + // async verifyCredentail(did: string): Promise { + // try { + // const response = await axios.post( + // `${this.sunbirdRcUrl}/credentials/${did}/verify` + // ); + // return response.data; + // } catch (error) { + // // Handle errors + // throw new Error("Failed to fetch data from the external API(verifyCredentail)"); + // } + // } async resolveDid(did: string): Promise { try { @@ -114,33 +114,13 @@ export class SunbirdRcService { } } - async renderCredential(credentialDID: string): Promise { - try { - const response = await axios.post( - `${this.sunbirdRcUrl}/credentials/render`, - { - data: { - credentialId: credentialDID, - template: - ' Certificate
Certificate of Completion

This is to certify that

{{name}}

has completed the course

{{programme}}

with score of {{grade}}%




', - output: "PDF", - }, - } - ); - return response.data; - } catch (error) { - // Handle errors - throw new Error("Failed to fetch data from the external API(renderCredential)"); - } - } - - async getCredentailById(credentialDID: string): Promise { - try { - const response = await axios.get(`${this.sunbirdRcUrl}/credentials/${credentialDID}`); - return response.data; - } catch (error) { - // Handle errors - throw new Error("Failed to fetch data from the external API(getCredentailById)"); - } - } + // async getCredentailById(credentialDID: string): Promise { + // try { + // const response = await axios.get(`${this.sunbirdRcUrl}/credentials/${credentialDID}`); + // return response.data; + // } catch (error) { + // // Handle errors + // throw new Error("Failed to fetch data from the external API(getCredentailById)"); + // } + // } } From ccd9ba9d53c53a2185fc09bcb910d7cfa6d9c8ee Mon Sep 17 00:00:00 2001 From: VamshiBatta07 Date: Fri, 15 Dec 2023 19:04:34 +0530 Subject: [PATCH 160/175] removed credentials check when setting up a survey. --- src/survey-config/survey-config.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/survey-config/survey-config.service.ts b/src/survey-config/survey-config.service.ts index baf4105..8f18ed2 100644 --- a/src/survey-config/survey-config.service.ts +++ b/src/survey-config/survey-config.service.ts @@ -34,7 +34,7 @@ export class SurveyConfigService { let newConfig; let userIdList: Set = new Set(); try { - await this.credentialService.findDIDs(); + // await this.credentialService.findDIDs(); await this.prisma.$transaction(async (prismaClient) => { newConfig = await prismaClient.surveyConfig.create({ data: { From 177e59d6d40b77fa2f10b8d82cb82e998a4587be Mon Sep 17 00:00:00 2001 From: VamshiBatta07 Date: Fri, 15 Dec 2023 19:41:07 +0530 Subject: [PATCH 161/175] Updated readme. --- README.md | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6aef540..38be4c7 100644 --- a/README.md +++ b/README.md @@ -1 +1,50 @@ -# Microservice boilerplate +# WPCAS - Workplace Competency Assessment Score + +## About + +WPCAS is a vital component of COMPASS, a goal-oriented human resource management system (GO-HRM) designed to help organizations align their objectives with well-defined targets for teams and individuals. This system maps competencies required to achieve these targets and establishes a connection between capacity and performance management. + +WPCAS specifically focuses on Workplace Competency Assessment Scores, offering a comprehensive 360-degree feedback survey for employees. This survey involves input from juniors, seniors, and colleagues, providing valuable insights into an employee's strengths and weaknesses based on workplace performance. + +## Tech Stack + +- **Language:** TypeScript +- **Database:** PostgreSQL +- **ORM:** Prisma +- **Framework:** NestJS +- **Node Version:** 18.16.1-alpine + +## Setup + +1. Install the necessary package dependencies: + ```bash + npm i + ``` + +2. Set up PostgreSQL in your local environment. + +3. Configure environment variables: + - Create an environment variable file (e.g., `.env`) using the example file as a reference. + +4. Generate Prisma migrations: + ```bash + npx prisma migrate dev + ``` + - If seed data is required, populate it by running: + ```bash + npx prisma db seed + ``` + or for a complete reset (including deleting all previous data): + ```bash + npx prisma migrate reset + ``` + +5. Running a Local Development Server: + ```bash + npm run start:dev + ``` + - Access the Swagger API documentation at `http://YOUR_APP_PORT/api/docs` + +## License + +This project is licensed under the [LICENSE NAME] - see the [LICENSE.md](LICENSE.md) file for details. \ No newline at end of file From 3b1b6eed7cce70727e91a563721417d74e74efc1 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 Date: Tue, 19 Dec 2023 09:49:25 +0530 Subject: [PATCH 162/175] Bug fix for sunbirdRc's issue credential API. --- src/external-services/sunbird-rc/sunbird-rc.service.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/external-services/sunbird-rc/sunbird-rc.service.ts b/src/external-services/sunbird-rc/sunbird-rc.service.ts index 3b3153b..3d990c7 100644 --- a/src/external-services/sunbird-rc/sunbird-rc.service.ts +++ b/src/external-services/sunbird-rc/sunbird-rc.service.ts @@ -42,7 +42,7 @@ export class SunbirdRcService { return response.data; } catch (error) { // Handle errors - throw new Error("Failed to fetch data from the external API(fetchCredSchemaByIdAndVersion)"); + throw new Error(`Failed to fetch data from the external API(fetchCredSchemaByIdAndVersion). Error: ${error.message}`); } } @@ -63,8 +63,10 @@ export class SunbirdRcService { ], type: ["VerifiableCredential", "WPCASSurveyScoreCredential"], issuer: dids.authorDid, + issuanceDate: creationDate, expirationDate: expirationDate, credentialSubject: { + id: dids.authorDid, ...credentialData }, options: { @@ -83,7 +85,7 @@ export class SunbirdRcService { return response.data; } catch (error) { // Handle errors - throw new Error(`Failed to issue credentials for user with id #${credentialData.userId}`); + throw new Error(`Failed to issue credentials for user with id #${credentialData.userId}. Error: ${error.message}`); } } @@ -110,7 +112,7 @@ export class SunbirdRcService { return response.data; } catch (error) { // Handle errors - throw new Error("Failed to fetch data from the external API(resolveDid)"); + throw new Error(`Failed to fetch data from the external API(resolveDid). Error: ${error.message}`); } } From 549aa0cef34ef8d6a8f4862c4c510b5027028212 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 Date: Wed, 20 Dec 2023 14:12:04 +0530 Subject: [PATCH 163/175] External service configuration improvement. --- .env.example | 12 ++++++++++ env-example | 10 --------- .../passbook/passbook.service.ts | 2 +- .../sunbird-rc/sunbird-rc.service.ts | 22 +------------------ 4 files changed, 14 insertions(+), 32 deletions(-) create mode 100644 .env.example delete mode 100644 env-example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..bb55a0b --- /dev/null +++ b/.env.example @@ -0,0 +1,12 @@ +NODE_ENV=development +APP_NAME="WPCAS Survey API" +API_PREFIX=api +APP_PORT=4010 + +DATABASE_USERNAME=postgres +DATABASE_PASSWORD=secret +DATABASE_NAME=wpcas-db +DATABASE_PORT=5432 +PASSBOOK_SERVICE_URL = https://passbook.hosted.url +SUNBIRD_SERVICE_URL = https://sunbird.hosted.url +DATABASE_URL=postgresql://${DATABASE_USERNAME}:${DATABASE_PASSWORD}@localhost:${DATABASE_PORT}/${DATABASE_NAME}?schema=public diff --git a/env-example b/env-example deleted file mode 100644 index f1e6b4d..0000000 --- a/env-example +++ /dev/null @@ -1,10 +0,0 @@ -NODE_ENV=development -APP_NAME="WPCAS Survey API" -API_PREFIX=api -APP_PORT=4010 - -DATABASE_USERNAME=postgres -DATABASE_PASSWORD=secret -DATABASE_NAME=service-db -DATABASE_PORT=5432 -DATABASE_URL="postgresql://postgres:secret@postgres:5432/service-db?sslmode=prefer" diff --git a/src/external-services/passbook/passbook.service.ts b/src/external-services/passbook/passbook.service.ts index 94ee08c..e06a4f3 100644 --- a/src/external-services/passbook/passbook.service.ts +++ b/src/external-services/passbook/passbook.service.ts @@ -4,7 +4,7 @@ import { AddFeedbackDto } from "./dto"; @Injectable() export class PassbookService { - private apiUrl = "http://172.17.0.2:3000/api"; + private apiUrl = process.env.PASSBOOK_SERVICE_URL; async getUser(userId: string): Promise { try { diff --git a/src/external-services/sunbird-rc/sunbird-rc.service.ts b/src/external-services/sunbird-rc/sunbird-rc.service.ts index 3d990c7..a437d7e 100644 --- a/src/external-services/sunbird-rc/sunbird-rc.service.ts +++ b/src/external-services/sunbird-rc/sunbird-rc.service.ts @@ -9,27 +9,7 @@ export class SunbirdRcService { @Inject(forwardRef(()=>CredentialDIDService)) private credentialDID: CredentialDIDService ){} - // private credentialUrl = "http://localhost:3000"; - // private credSchemaUrl = "http://localhost:3333"; - // private identityUrl = "http://localhost:3332"; - private sunbirdRcUrl = "http://compass.samagra.io"; - - // async addNewCredSchema(schema: any): Promise { - // try { - // const response = await axios.post( - // `${this.sunbirdRcUrl}/credential-schema`, - // { - // data: { - // ...schema - // }, - // } - // ); - // return response.data; - // } catch (error) { - // // Handle errors - // throw new Error("Failed to fetch data from the external API"); - // } - // } + private sunbirdRcUrl = process.env.SUNBIRD_SERVICE_URL; async fetchCredSchemaByIdAndVersion(schemaDid: string, version: string): Promise { try { From 4c0b1cc1b768826d8351a1610aea4c45b7c63fc4 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 Date: Wed, 20 Dec 2023 17:34:43 +0530 Subject: [PATCH 164/175] SunbirdRc and SurveyCron bug fixes. --- .../sunbird-rc/sunbird-rc.service.ts | 48 +++++++++---------- .../scheduled-tasks.service.ts | 4 +- src/survey/survey.service.ts | 4 +- src/utils/utils.ts | 14 +++--- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/external-services/sunbird-rc/sunbird-rc.service.ts b/src/external-services/sunbird-rc/sunbird-rc.service.ts index 3d990c7..fa73574 100644 --- a/src/external-services/sunbird-rc/sunbird-rc.service.ts +++ b/src/external-services/sunbird-rc/sunbird-rc.service.ts @@ -51,35 +51,35 @@ export class SunbirdRcService { const dids = await this.credentialDID.findDIDs(); const creationDate = new Date(); - const expirationDate = new Date(creationDate).setFullYear(creationDate.getFullYear() + 1); + const expirationDate = new Date(); + expirationDate.setFullYear(creationDate.getFullYear() + 1); + const response = await axios.post( `${this.sunbirdRcUrl}/credentials/issue`, { - data: { - credential: { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1", - ], - type: ["VerifiableCredential", "WPCASSurveyScoreCredential"], - issuer: dids.authorDid, - issuanceDate: creationDate, - expirationDate: expirationDate, - credentialSubject: { - id: dids.authorDid, - ...credentialData - }, - options: { - created: creationDate.toISOString(), - credentialStatus: { - type: "RevocationList2020Status", - }, + credential: { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1", + ], + type: ["VerifiableCredential", "WPCASSurveyScoreCredential"], + issuer: dids.authorDid, + issuanceDate: creationDate, + expirationDate: expirationDate, + credentialSubject: { + id: dids.authorDid, + ...credentialData + }, + options: { + created: creationDate.toISOString(), + credentialStatus: { + type: "RevocationList2020Status", }, }, - credentialSchemaId: dids.schemaDid, - credentialSchemaVersion: dids.schemaVersion, - tags: ["tag1", "tag2", "tag3"], }, + credentialSchemaId: dids.schemaDid, + credentialSchemaVersion: dids.schemaVersion, + tags: ["tag1", "tag2", "tag3"], } ); return response.data; @@ -103,7 +103,7 @@ export class SunbirdRcService { async resolveDid(did: string): Promise { try { - const response = await axios.post( + const response = await axios.get( `${this.sunbirdRcUrl}/did/resolve/${did}` ); if(response.status != 200){ diff --git a/src/scheduled-tasks/scheduled-tasks.service.ts b/src/scheduled-tasks/scheduled-tasks.service.ts index acfa765..ba74f81 100644 --- a/src/scheduled-tasks/scheduled-tasks.service.ts +++ b/src/scheduled-tasks/scheduled-tasks.service.ts @@ -81,7 +81,7 @@ export class ScheduledTasksService { this.logger.log(`Credentials issued. Pushing credentials to passbook.`); await this.passbookService.addFeedback({ ...credData, - certificateId: sunbirdCred.id + certificateId: sunbirdCred["credentail"]["id"] }); this.logger.log(`Added credentials to passbook."`); @@ -90,7 +90,7 @@ export class ScheduledTasksService { } this.logger.log(`Updating credentials and overall score in the surveyFrom.`); - await this.surveyFormService.updateOverallScoreAndCredentialDid(surveyForm.id, credData.overallScore, sunbirdCred.id); + await this.surveyFormService.updateOverallScoreAndCredentialDid(surveyForm.id, credData.overallScore, sunbirdCred["credentail"]["id"]); } } diff --git a/src/survey/survey.service.ts b/src/survey/survey.service.ts index 319efa9..709a297 100644 --- a/src/survey/survey.service.ts +++ b/src/survey/survey.service.ts @@ -248,12 +248,12 @@ export class SurveyService { const matchingScore = competencyScores.find((score) => score.competencyLevelNumber === levelNumber); // Calculate score percentage - const scorePercentage = matchingScore ? matchingScore.score.toFixed(2) : 'N/A'; + const scorePercentage = matchingScore ? matchingScore.score.toFixed(2) : null; if(scorePercentage != 'N/A'){ return { levelNumber, name: levelName, - score: parseFloat(scorePercentage), + score: scorePercentage == -1 ? null : parseFloat(scorePercentage), }; } else { return null; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 53b66c6..a92e17d 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -54,9 +54,9 @@ export function isTomorrow(inputDate: Date): boolean { const tomorrow = new Date(currentDate); tomorrow.setDate(currentDate.getDate() + 1); - tomorrow.setHours(0, 0, 0, 0); + tomorrow.setUTCHours(0, 0, 0, 0); - inputDate.setHours(0, 0, 0, 0); + inputDate.setUTCHours(0, 0, 0, 0); // Compare the input date with tomorrow return inputDate.getTime() === tomorrow.getTime(); @@ -64,7 +64,7 @@ export function isTomorrow(inputDate: Date): boolean { export function isDayBeforeToday(date) { const today = new Date(); - today.setHours(0, 0, 0, 0); + today.setUTCHours(0, 0, 0, 0); const yesterday = new Date(date); yesterday.setDate(yesterday.getDate() - 1); @@ -74,10 +74,10 @@ export function isDayBeforeToday(date) { export function isToday(date) { const today = new Date(); - today.setHours(0, 0, 0, 0); + today.setUTCHours(0, 0, 0, 0); const givenDate = new Date(date); - givenDate.setHours(0, 0, 0, 0); + givenDate.setUTCHours(0, 0, 0, 0); return givenDate.getTime() === today.getTime(); } @@ -85,9 +85,9 @@ export function isToday(date) { export function isDateInPast(compareDate: Date, referenceDate: Date): boolean { // Convert both dates to UTC to ensure accurate comparison const compareDateUTC = new Date(compareDate.toUTCString()); - compareDateUTC.setHours(0, 0, 0, 0); + compareDateUTC.setUTCHours(0, 0, 0, 0); const referenceDateUTC = new Date(referenceDate.toUTCString()); - referenceDateUTC.setHours(0, 0, 0, 0); + referenceDateUTC.setUTCHours(0, 0, 0, 0); // Compare the dates return compareDateUTC < referenceDateUTC; From 702bcb40d995db39d64dddce332686d83be450c7 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 Date: Wed, 27 Mar 2024 16:32:23 +0530 Subject: [PATCH 165/175] example env update --- .env.example | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index bb55a0b..11ba9e6 100644 --- a/.env.example +++ b/.env.example @@ -5,8 +5,8 @@ APP_PORT=4010 DATABASE_USERNAME=postgres DATABASE_PASSWORD=secret -DATABASE_NAME=wpcas-db +DATABASE_NAME=service-db DATABASE_PORT=5432 -PASSBOOK_SERVICE_URL = https://passbook.hosted.url -SUNBIRD_SERVICE_URL = https://sunbird.hosted.url -DATABASE_URL=postgresql://${DATABASE_USERNAME}:${DATABASE_PASSWORD}@localhost:${DATABASE_PORT}/${DATABASE_NAME}?schema=public +PASSBOOK_SERVICE_URL=https://compass.samagra.io +SUNBIRD_SERVICE_URL=https://compass.samagra.io +DATABASE_URL=postgresql://${DATABASE_USERNAME}:${DATABASE_PASSWORD}@postgres:${DATABASE_PORT}/${DATABASE_NAME}?schema=public From a3da6d534703fe4d4cf3421288426adcd9204b05 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 Date: Wed, 27 Mar 2024 16:24:38 +0530 Subject: [PATCH 166/175] partial commit. --- .gitignore | 1 + .../external-services.module.ts | 5 ++- .../tarento/tarento.module.ts | 8 ++++ .../tarento/tarento.service.ts | 42 +++++++++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 src/external-services/tarento/tarento.module.ts create mode 100644 src/external-services/tarento/tarento.service.ts diff --git a/.gitignore b/.gitignore index f6a091e..fc919cd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # compiled output dist node_modules +.data # enviornment variables .env diff --git a/src/external-services/external-services.module.ts b/src/external-services/external-services.module.ts index b8fa1cf..7a141ca 100644 --- a/src/external-services/external-services.module.ts +++ b/src/external-services/external-services.module.ts @@ -1,9 +1,10 @@ import { Module } from '@nestjs/common'; import { PassbookModule } from './passbook/passbook.module'; import { SunbirdRcModule } from './sunbird-rc/sunbird-rc.module'; +import { TarentoModule } from './tarento/tarento.module'; @Module({ - imports: [PassbookModule, SunbirdRcModule], - exports: [PassbookModule, SunbirdRcModule] + imports: [PassbookModule, SunbirdRcModule, TarentoModule], + exports: [PassbookModule, SunbirdRcModule, TarentoModule] }) export class ExternalServicesModule {} diff --git a/src/external-services/tarento/tarento.module.ts b/src/external-services/tarento/tarento.module.ts new file mode 100644 index 0000000..6dac2bd --- /dev/null +++ b/src/external-services/tarento/tarento.module.ts @@ -0,0 +1,8 @@ +import { Module } from "@nestjs/common"; +import { TarentoService } from "./tarento.service"; + +@Module({ + providers: [TarentoService], + exports: [TarentoService] +}) +export class TarentoModule {} \ No newline at end of file diff --git a/src/external-services/tarento/tarento.service.ts b/src/external-services/tarento/tarento.service.ts new file mode 100644 index 0000000..2d79a4b --- /dev/null +++ b/src/external-services/tarento/tarento.service.ts @@ -0,0 +1,42 @@ +import { Injectable } from "@nestjs/common"; +import axios from "axios"; + +@Injectable() +export class TarentoService { + private fracApiUrl = process.env.FRAC_SERVICE_URL; + private userApiUrl = process.env.USER_SERVICE_URL; + + async getUser(userId: string): Promise { + try { + const response = await axios.get(`${this.userApiUrl}`, { + params: { + userId: userId, + }, + }); + return response.data; + } catch (error) { + // Handle errors + throw new Error("Failed to fetch FRAC data from the Tarento's API"); + } + } + + async getFracData(): Promise { + try { + const response = await axios.get(`${this.fracApiUrl}`, { + params: {}, + }); + return response.data; + } catch (error) { + // Handle errors + throw new Error("Failed to fetch FRAC data from the Tarento's API"); + } + } + + async formatFracData(): Promise { + try { + + } catch (error) { + throw new Error(error) + } + } +} From f5ad260e78cc1cf31455a78f535b0f38d71a2947 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 Date: Fri, 5 Apr 2024 19:15:31 +0530 Subject: [PATCH 167/175] Tarento API Integration (user and Frac). --- .env.example | 3 + .../migration.sql | 2 + prisma/schema.prisma | 9 +- .../external-services.module.ts | 20 +- .../tarento/tarento.controller.ts | 36 ++ .../tarento/tarento.module.ts | 5 +- .../tarento/tarento.service.ts | 337 +++++++++++++++++- .../mock-role/mock-role.controller.ts | 43 +++ .../mock-role/mock-role.service.ts | 112 +++++- src/question-bank/question-bank.module.ts | 4 +- src/question-bank/question-bank.service.ts | 13 +- src/scheduled-tasks/scheduled-tasks.module.ts | 2 + .../scheduled-tasks.service.ts | 10 +- src/user-metadata/user-metadata.module.ts | 3 +- src/user-metadata/user-metadata.service.ts | 27 +- 15 files changed, 590 insertions(+), 36 deletions(-) create mode 100644 prisma/migrations/20240405124851_adding_activities_column/migration.sql create mode 100644 src/external-services/tarento/tarento.controller.ts diff --git a/.env.example b/.env.example index 11ba9e6..c8d3292 100644 --- a/.env.example +++ b/.env.example @@ -10,3 +10,6 @@ DATABASE_PORT=5432 PASSBOOK_SERVICE_URL=https://compass.samagra.io SUNBIRD_SERVICE_URL=https://compass.samagra.io DATABASE_URL=postgresql://${DATABASE_USERNAME}:${DATABASE_PASSWORD}@postgres:${DATABASE_PORT}/${DATABASE_NAME}?schema=public +USER_SERVICE_TOKEN= +USER_SERVICE_COOKIE= +USER_SERVICE_URL= diff --git a/prisma/migrations/20240405124851_adding_activities_column/migration.sql b/prisma/migrations/20240405124851_adding_activities_column/migration.sql new file mode 100644 index 0000000..42c33d1 --- /dev/null +++ b/prisma/migrations/20240405124851_adding_activities_column/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "roles" ADD COLUMN "activities" TEXT[] DEFAULT ARRAY[]::TEXT[]; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 3b3a2b3..4cc350f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -68,10 +68,10 @@ model AdminCompetency { // @@map("admin_department") // } -model CredentialDid{ - id Int @id @default(autoincrement()) - authorDid String - schemaDid String +model CredentialDid { + id Int @id @default(autoincrement()) + authorDid String + schemaDid String schemaVersion String @@unique([schemaDid, schemaVersion]) @@ -231,6 +231,7 @@ model Role { competencies RoleToCompetency[] Designation Designation? @relation(fields: [designationId], references: [id]) designationId Int? + activities String[] @default([]) @@map("roles") } diff --git a/src/external-services/external-services.module.ts b/src/external-services/external-services.module.ts index 7a141ca..b38d841 100644 --- a/src/external-services/external-services.module.ts +++ b/src/external-services/external-services.module.ts @@ -1,10 +1,18 @@ -import { Module } from '@nestjs/common'; -import { PassbookModule } from './passbook/passbook.module'; -import { SunbirdRcModule } from './sunbird-rc/sunbird-rc.module'; -import { TarentoModule } from './tarento/tarento.module'; +import { Module } from "@nestjs/common"; +import { PassbookModule } from "./passbook/passbook.module"; +import { SunbirdRcModule } from "./sunbird-rc/sunbird-rc.module"; +import { TarentoModule } from "./tarento/tarento.module"; +import { RouterModule } from "@nestjs/core"; @Module({ - imports: [PassbookModule, SunbirdRcModule, TarentoModule], - exports: [PassbookModule, SunbirdRcModule, TarentoModule] + imports: [ + PassbookModule, + SunbirdRcModule, + TarentoModule, + RouterModule.register([ + { path: "ext-service/", module: TarentoModule }, + ]), + ], + exports: [PassbookModule, SunbirdRcModule, TarentoModule], }) export class ExternalServicesModule {} diff --git a/src/external-services/tarento/tarento.controller.ts b/src/external-services/tarento/tarento.controller.ts new file mode 100644 index 0000000..77e5f4a --- /dev/null +++ b/src/external-services/tarento/tarento.controller.ts @@ -0,0 +1,36 @@ +import { Controller, HttpStatus, Post, Res } from "@nestjs/common"; +import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { TarentoService } from "./tarento.service"; +import { getPrismaErrorStatusAndMessage } from "src/utils/utils"; + +@Controller("tarento") +@ApiTags("ext-service/tarento") +export class TarentoController { + constructor(private readonly tarentoService: TarentoService) {} + + @Post("syncFracData") + @ApiOperation({ summary: "Sync FRAC data." }) + @ApiResponse({ + status: HttpStatus.OK, + isArray: true, + }) + async findAll(@Res() res) { + try { + const response = await this.tarentoService.formatAndSyncFracData(); + console.log(response); + return res.status(HttpStatus.OK).json({ + message: response, + }); + } catch (error) { + console.error(`Failed to create new survey score`, error); + + const { errorMessage, statusCode } = + getPrismaErrorStatusAndMessage(error); + + return res.status(statusCode).json({ + statusCode, + message: errorMessage || `Failed to create survey score`, + }); + } + } +} diff --git a/src/external-services/tarento/tarento.module.ts b/src/external-services/tarento/tarento.module.ts index 6dac2bd..4acde87 100644 --- a/src/external-services/tarento/tarento.module.ts +++ b/src/external-services/tarento/tarento.module.ts @@ -1,8 +1,11 @@ import { Module } from "@nestjs/common"; import { TarentoService } from "./tarento.service"; +import { PrismaService } from "src/prisma/prisma.service"; +import { TarentoController } from "./tarento.controller"; @Module({ - providers: [TarentoService], + controllers:[TarentoController], + providers: [TarentoService, PrismaService], exports: [TarentoService] }) export class TarentoModule {} \ No newline at end of file diff --git a/src/external-services/tarento/tarento.service.ts b/src/external-services/tarento/tarento.service.ts index 2d79a4b..e3fad40 100644 --- a/src/external-services/tarento/tarento.service.ts +++ b/src/external-services/tarento/tarento.service.ts @@ -1,42 +1,355 @@ import { Injectable } from "@nestjs/common"; import axios from "axios"; +import _ from "lodash"; +import { PrismaService } from "src/prisma/prisma.service"; @Injectable() export class TarentoService { + constructor(private prisma: PrismaService) {} private fracApiUrl = process.env.FRAC_SERVICE_URL; - private userApiUrl = process.env.USER_SERVICE_URL; async getUser(userId: string): Promise { try { - const response = await axios.get(`${this.userApiUrl}`, { - params: { - userId: userId, + const baseUrl = process.env.USER_SERVICE_URL || ""; + if (baseUrl == "") { + throw new Error("User service URL not available"); + } + const headers = { + Authorization: "bearer " + process.env.USER_SERVICE_TOKEN, + "Content-Type": "application/json", + }; + + const data = { + request: { + filters: { + id: userId, + }, }, - }); - return response.data; + }; + + let response = await axios.post(baseUrl, data, { headers }); + + return response; } catch (error) { // Handle errors - throw new Error("Failed to fetch FRAC data from the Tarento's API"); + console.log(error) + throw new Error("Failed to fetch user data from the Tarento's API"); } } async getFracData(): Promise { try { const response = await axios.get(`${this.fracApiUrl}`, { - params: {}, + headers: { + Accept: "application/json", + "Accept-Language": "en-GB,en;q=0.9", + Cookie: + "connect.sid=s%3A3R7JwXRshJ5kaAM2AoHznye9VSbWIoks.CTD%2BcCE9y%2FDYcQ6FhBS5Fi2cFbl5h9SAIA51Me%2FzU3g", + }, }); - return response.data; + return response.data.result.framework.categories; } catch (error) { // Handle errors throw new Error("Failed to fetch FRAC data from the Tarento's API"); } } - async formatFracData(): Promise { + async formatAndSyncFracData(): Promise { try { - + const fracData = await this.getFracData(); + + await this.prisma.$transaction(async (prismaClient) => { + //sync designations(positions) + const designations = _.find(fracData, { + name: "Positions", + status: "Live", + }); + const syncedDesignations = await Promise.all( + designations.terms.map(async (position) => { + if (position.status == "Live") { + const designationDTO = { + name: position.name, + description: position.description, + }; + + return await prismaClient.designation.upsert({ + where: { + name: designationDTO.name, + }, + create: designationDTO, + update: designationDTO, + }); + } + }) + ); + + console.log("syncedDesignations", syncedDesignations); + + //sync roles + const roles = _.find(fracData, { name: "Roles", status: "Live" }); + const syncedRoles = await Promise.all( + roles.terms + .filter((role) => role.status === "Live") + .map(async (role) => { + const roleDTO = { + name: role.name, + description: role.description, + }; + + try { + return await prismaClient.role.upsert({ + where: { + name: roleDTO.name, + }, + create: roleDTO, + update: roleDTO, + }); + } catch (error) { + console.error("Error upserting role:", error); + throw new Error(error); + } + }) + ); + + // console.log("syncedRoles", syncedRoles); + + //map designatioToRoles + await Promise.all( + designations.terms.map(async (position) => { + if (position.associations.length > 0) { + const positionToBeMapped = _.find(syncedDesignations, { + name: position.name, + description: position.description, + }); + position.associations.map(async (roleToBeMapped) => { + if (roleToBeMapped.status == "Live") { + const roleAdded = await prismaClient.role.findUnique({ + where: { + name: roleToBeMapped.name, + description: roleToBeMapped.description, + }, + }); + if (roleAdded != null) { + let designationId = positionToBeMapped.id; + let roleId = roleAdded.id; + + await prismaClient.role.update({ + where: { + id: roleId, + }, + data: { + designationId, + }, + }); + } + } + }); + } + }) + ); + + //sync competencyLevels + const competencyLevels = _.find(fracData, { + name: "Competency Levels", + status: "Live", + }); + const syncedCompetencyLevels = await Promise.all( + competencyLevels.terms.map(async (competencyLevel) => { + if (competencyLevel.status == "Live") { + const competencyLevelDTO = { + name: competencyLevel.name, + description: competencyLevel.description, + levelNumber: + competencyLevel.moreProperties.levelNumber || + competencyLevel.index, + }; + + return await prismaClient.competencyLevel.upsert({ + where: { + name: competencyLevelDTO.name, + }, + create: competencyLevelDTO, + update: competencyLevelDTO, + }); + } + }) + ); + + // console.log("syncedCompetencyLevels", syncedCompetencyLevels); + + //sync competencies + const competencies = _.find(fracData, { + name: "Competencies", + status: "Live", + }); + const syncedCompetency = await Promise.all( + competencies.terms.map(async (competency) => { + if (competency.status == "Live") { + const competencyDTO = { + name: competency.name, + description: competency.description, + }; + + return await prismaClient.competency.upsert({ + where: { + name: competencyDTO.name, + }, + create: competencyDTO, + update: competencyDTO, + }); + } + }) + ); + + // console.log("syncedCompetency", syncedCompetency); + + //map competenciesToCompetencyLevels + await Promise.all( + competencies.terms.map(async (competency) => { + if (competency.associations && competency.associations.length > 0) { + const competencyToBeMapped = _.find(syncedCompetency, { + name: competency.name, + description: competency.description, + }); + competency.associations.forEach( + async (competencyLevelToBeMapped) => { + if (competencyLevelToBeMapped.status == "Live") { + const competencyLevelAdded = _.find( + syncedCompetencyLevels, + { + name: competencyLevelToBeMapped.name, + } + ); + if (competencyLevelAdded) { + const competencyId = competencyToBeMapped?.id; + const competencyLevelId = competencyLevelAdded?.id; + await prismaClient.competencyToCompetencyLevel.upsert({ + where: { + competencyId_competencyLevelId: { + competencyId, + competencyLevelId, + }, + }, + update: { + competencyId, + competencyLevelId, + }, + create: { + competencyId, + competencyLevelId, + }, + }); + } + } + } + ); + } + }) + ); + + console.log("Successfully mapped competenciesToCompetencyLevels"); + + //map rolesToCompetencies + const activities = _.find(fracData, { + name: "Activities", + status: "Live", + }); + await Promise.all( + roles.terms.map(async (role) => { + if (role.associations && role.associations.length > 0) { + let activitiesToBeAdded: string[] = []; + const roleToBeMapped = _.find(syncedRoles, { + name: role.name, + description: role.description, + }); + if (roleToBeMapped) { + for (const activity of role.associations) { + if (activity.status === "Live") { + const activityWithAssociations = _.find(activities.terms, { + name: activity.name, + description: activity.description, + }); + activitiesToBeAdded.push(activity.name); + if ( + activityWithAssociations && + activityWithAssociations.associations && + activityWithAssociations.associations.length > 0 + ) { + for (const competency of activityWithAssociations.associations) { + if (competency.status === "Live") { + const competencyAdded = _.find(syncedCompetency, { + name: competency.name, + }); + if (competencyAdded) { + const roleId = roleToBeMapped.id; + const competencyId = competencyAdded.id; + + await prismaClient.roleToCompetency.upsert({ + where: { + roleId_competencyId: { + roleId, + competencyId, + }, + }, + update: { + roleId, + competencyId, + }, + create: { + roleId, + competencyId, + }, + }); + } + } + } + } + } + } + console.log("activitiesToBeAdded: ", activitiesToBeAdded); + await prismaClient.role.update({ + where: { + id: roleToBeMapped.id, + }, + data: { + activities: activitiesToBeAdded, + }, + }); + } + } + }) + ); + console.log("Successfully mapped rolesToCompetencies"); + + //add activities to roles + await Promise.all( + roles.terms + .filter((role) => role.status === "Live") + .map(async (role) => { + const roleDTO = { + name: role.name, + description: role.description, + }; + + try { + return await prismaClient.role.upsert({ + where: { + name: roleDTO.name, + }, + create: roleDTO, + update: roleDTO, + }); + } catch (error) { + console.error("Error upserting role:", error); + throw new Error(error); + } + }) + ); + }); + + return "Tarento's FRAC data synced successfully"; } catch (error) { - throw new Error(error) + throw new Error(error); } } } diff --git a/src/mockModules/mock-role/mock-role.controller.ts b/src/mockModules/mock-role/mock-role.controller.ts index a267d15..fad39bc 100644 --- a/src/mockModules/mock-role/mock-role.controller.ts +++ b/src/mockModules/mock-role/mock-role.controller.ts @@ -6,6 +6,7 @@ import { HttpStatus, Param, ParseIntPipe, + ParseUUIDPipe, Patch, Post, Res, @@ -62,6 +63,48 @@ export class MockRoleController { } } + @Get("formatedRoles") + @ApiOperation({ summary: "fetch all mock roles" }) + @ApiResponse({ + status: HttpStatus.OK, + type: ResponseMockRoleDto, + isArray: true, + }) + async getAllRolesFormated(@Res() res) { + try { + const roles = await this.roleService.findAllRoles(); + return res + .status(HttpStatus.OK) + .json(roles); + + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } + + @Get("userId/:id") + @ApiOperation({ summary: "fetch all mock roles" }) + @ApiResponse({ + status: HttpStatus.OK, + type: ResponseMockRoleDto, + isArray: true, + }) + async getAllRolesByUserId(@Res() res, @Param("id", ParseUUIDPipe) id: string) { + try { + const roles = await this.roleService.findRolesByUserId(id); + return res + .status(HttpStatus.OK) + .json(roles); + + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } + @Get(":id") @ApiOperation({ summary: "get mock role by id" }) @ApiResponse({ status: HttpStatus.OK, type: ResponseMockRoleDto }) diff --git a/src/mockModules/mock-role/mock-role.service.ts b/src/mockModules/mock-role/mock-role.service.ts index b4fd8ba..9d11703 100644 --- a/src/mockModules/mock-role/mock-role.service.ts +++ b/src/mockModules/mock-role/mock-role.service.ts @@ -22,7 +22,94 @@ export class MockRoleService { } public async findAllRoles() { - return this.prisma.role.findMany(); + const roles = await this.prisma.role.findMany({ + select: { + id: true, + name: true, + description: true, + competencies: { + select: { + competency: { + select: { + id: true, + name: true, + competencyLevels: { + select: { + competencyLevel: { + select: { + id: true, + name: true, + levelNumber: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }); + const transformedRoles = this.transformRolesObject(roles); + + return { roles: transformedRoles }; + } + + public async findRolesByUserId(userId: string) { + const user = await this.prisma.userMetadata.findUnique({ + where: { + userId, + }, + }); + if (!user) { + throw new Error("User data not found."); + } + if (!user.designation) { + throw new Error("User does not have any designation"); + } + + const roles = await this.prisma.designation.findUnique({ + where: { + name: user.designation, + }, + select: { + Roles: { + select: { + id: true, + name: true, + description: true, + competencies: { + select: { + competency: { + select: { + id: true, + name: true, + competencyLevels: { + select: { + competencyLevel: { + select: { + id: true, + name: true, + levelNumber: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }); + + if(!roles){ + throw new Error(`No roles found for the user(designation: ${user.designation}) with id: ${userId}`); + } + const transformedRoles = this.transformRolesObject(roles.Roles); + + return { roles: transformedRoles }; } public async findRoleById(id: number) { @@ -134,4 +221,27 @@ export class MockRoleService { } }); } + + public transformRolesObject(roles) { + const transformedRoles: any = []; + roles.map(async (role) => { + const transformedRole = { + id: role.id, + name: role.name, + description: role.description, + competency: role.competencies.map((competency) => ({ + id: competency.competency.id, + name: competency.competency.name, + levels: competency.competency.competencyLevels.map((level) => ({ + id: level.competencyLevel.id, + name: level.competencyLevel.name, + levelNumber: level.competencyLevel.levelNumber, + })), + })), + }; + transformedRoles.push(transformedRole); + }); + + return transformedRoles; + } } diff --git a/src/question-bank/question-bank.module.ts b/src/question-bank/question-bank.module.ts index 9b28ddb..5b02971 100644 --- a/src/question-bank/question-bank.module.ts +++ b/src/question-bank/question-bank.module.ts @@ -8,6 +8,7 @@ import { MockRoleService } from '../mockModules/mock-role/mock-role.service'; import { MockCompetencyService } from '../mockModules/mock-competency/mock-competency.service'; import { MockCompetencyLevelService } from '../mockModules/mock-competency-level/mock-competency-level.service'; import { FileUploadService } from '../file-upload/file-upload.service'; +import { TarentoService } from 'src/external-services/tarento/tarento.service'; @Module({ // Import the PrismaModule to make Prisma service available within this module @@ -24,7 +25,8 @@ import { FileUploadService } from '../file-upload/file-upload.service'; MockUserService, MockDesignationService, MockRoleService, - FileUploadService + FileUploadService, + TarentoService ], // Export the QuestionBankService to make it available for other modules that import this module diff --git a/src/question-bank/question-bank.service.ts b/src/question-bank/question-bank.service.ts index d6f0736..2b5130e 100644 --- a/src/question-bank/question-bank.service.ts +++ b/src/question-bank/question-bank.service.ts @@ -17,6 +17,8 @@ import { MockUserService } from "../mockModules/mock-user/mock-user.service"; import { MockRoleService } from "./../mockModules/mock-role/mock-role.service"; import { MockDesignationService } from "../mockModules/mock-designation/mock-designation.service"; import { FileUploadService } from "../file-upload/file-upload.service"; +import axios from "axios"; +import { TarentoService } from "src/external-services/tarento/tarento.service"; @Injectable() export class QuestionBankService { @@ -26,7 +28,8 @@ export class QuestionBankService { private mockUserService: MockUserService, private mockDesignationService: MockDesignationService, private mockRoleService: MockRoleService, - private fileUploadService: FileUploadService + private fileUploadService: FileUploadService, + private tarentoService: TarentoService ) {} async createQuestionByCompentencyId( @@ -243,14 +246,18 @@ export class QuestionBankService { } async getAllQuestionsForUser(userId: string) { - const user = await this.mockUserService.findOne(userId); + // const user = await this.mockUserService.findOne(userId); + + let response = await this.tarentoService.getUser(userId); + + const user = response.data.result?.response?.content[0]?.profileDetails?.professionalDetails[0]; if (!user) { // Handle the case when the user is not found. throw new Error("User not found"); } // Get the user's designation - const designation = user.designation; + const designation = user?.designation; // Get all the roles for a designation const userRoles = diff --git a/src/scheduled-tasks/scheduled-tasks.module.ts b/src/scheduled-tasks/scheduled-tasks.module.ts index 4686a1f..fb6eb4a 100644 --- a/src/scheduled-tasks/scheduled-tasks.module.ts +++ b/src/scheduled-tasks/scheduled-tasks.module.ts @@ -11,6 +11,7 @@ import { PrismaService } from "../prisma/prisma.service"; import { AdminCompetencyModule } from "src/admin-competency/admin-competency.module"; import { SunbirdRcModule } from "src/external-services/sunbird-rc/sunbird-rc.module"; import { PassbookModule } from "src/external-services/passbook/passbook.module"; +import { TarentoModule } from "src/external-services/tarento/tarento.module"; @Module({ imports: [ @@ -20,6 +21,7 @@ import { PassbookModule } from "src/external-services/passbook/passbook.module"; AdminCompetencyModule, SunbirdRcModule, PassbookModule, + TarentoModule ], providers: [ ScheduledTasksService, diff --git a/src/scheduled-tasks/scheduled-tasks.service.ts b/src/scheduled-tasks/scheduled-tasks.service.ts index ba74f81..adebcf0 100644 --- a/src/scheduled-tasks/scheduled-tasks.service.ts +++ b/src/scheduled-tasks/scheduled-tasks.service.ts @@ -10,6 +10,7 @@ import { UserMetadataService } from "../user-metadata/user-metadata.service"; import { isDayBeforeToday, isToday, isTomorrow } from "../utils/utils"; import { PassbookService } from "src/external-services/passbook/passbook.service"; import { SunbirdRcService } from "src/external-services/sunbird-rc/sunbird-rc.service"; +import { TarentoService } from "src/external-services/tarento/tarento.service"; @Injectable() export class ScheduledTasksService { @@ -21,7 +22,8 @@ export class ScheduledTasksService { private surveyFormService: SurveyFormService, private surveyScoreService: SurveyScoreService, private passbookService: PassbookService, - private sunbirdRcService: SunbirdRcService + private sunbirdRcService: SunbirdRcService, + private tarentoService: TarentoService ) {} private readonly logger = new Logger(ScheduledTasksService.name); @@ -149,4 +151,10 @@ export class ScheduledTasksService { this.logger.log(`The survey with surveyConfigId: "${surveyConfig.id}" is to be Activated in the future or a dead survey.`); } } + + + @Cron(process.env.FRAC_CRON_EPX ?? CronExpression.EVERY_1ST_DAY_OF_MONTH_AT_MIDNIGHT) + async fracSyncCron(){ + await this.tarentoService.formatAndSyncFracData(); + } } diff --git a/src/user-metadata/user-metadata.module.ts b/src/user-metadata/user-metadata.module.ts index 8ee2ec9..3ec0232 100644 --- a/src/user-metadata/user-metadata.module.ts +++ b/src/user-metadata/user-metadata.module.ts @@ -6,6 +6,7 @@ import { UserMetadataService } from "./user-metadata.service"; import { PrismaService } from "../prisma/prisma.service"; import { SurveyModule } from "../survey/survey.module"; import { SurveyFormService } from "../survey-form/survey-form.service"; +import { TarentoService } from "src/external-services/tarento/tarento.service"; @Module({ imports: [ @@ -13,7 +14,7 @@ import { SurveyFormService } from "../survey-form/survey-form.service"; forwardRef(() => SurveyModule), ], controllers: [UserMetadataController], - providers: [UserMetadataService, PrismaService, MockUserService, SurveyFormService], + providers: [UserMetadataService, PrismaService, MockUserService, SurveyFormService, TarentoService], exports: [UserMetadataService], }) export class UserMetadataModule {} diff --git a/src/user-metadata/user-metadata.service.ts b/src/user-metadata/user-metadata.service.ts index cf4bd07..1f0f8cb 100644 --- a/src/user-metadata/user-metadata.service.ts +++ b/src/user-metadata/user-metadata.service.ts @@ -8,6 +8,7 @@ import { UserMetadataFilterDto } from "./dto"; import { UserMappingFileUploadDto } from "../survey-config/dto/create-survey-config.dto"; import { SurveyService } from "../survey/survey.service"; import { SurveyFormService } from "../survey-form/survey-form.service"; +import { TarentoService } from "src/external-services/tarento/tarento.service"; @Injectable() export class UserMetadataService { @@ -18,7 +19,8 @@ export class UserMetadataService { private surveyConfig: SurveyConfigService, @Inject(forwardRef(()=>SurveyService)) private surveyService: SurveyService, - private surveyForm: SurveyFormService + private surveyForm: SurveyFormService, + private tarentoService: TarentoService ) {} public async syncUserDataWithFrac() { @@ -56,15 +58,27 @@ export class UserMetadataService { where: { userId }, select: this.metadataSelect, }); - const user = await this.mockUser.findOne(userId); + // const user = await this.mockUser.findOne(userId); + let response = await this.tarentoService.getUser(userId); + + const user = { + id: response.data.result?.response?.content[0]?.id, + userName: response.data.result?.response?.content[0].userName, + createdAt: response.data.result?.response?.content[0]?.createdDate, + role: (response.data.result?.response?.content[0]?.organisations[0]?.roles.indexOf("ADMIN") != -1) ? "ADMIN" : "", // user service has "roles" for every user + profilePicture: userMetadata?.profilePicture, + designation: response.data.result?.response?.content[0]?.profileDetails?.professionalDetails[0]?.designation, + dateOfJoining: response.data.result?.response?.content[0]?.profileDetails?.professionalDetails[0]?.doj + }; + const userObj = { userId: user.id, userName: user.userName, isNewEmployee: false, - dateOfJoining: user.createdAt, - isAdmin: user.role == UserRolesEnum.ADMIN ? true : false, + dateOfJoining: user.dateOfJoining, + isAdmin: user.role == "ADMIN" ? true : false, designation: user.designation, - profilePicture: user.profilePicture + profilePicture: null // user service doesn't store profilePicture }; if (!userMetadata) { @@ -239,6 +253,7 @@ export class UserMetadataService { dateOfJoining: true, isAdmin: true, designation: true, - isNewEmployee: true + isNewEmployee: true, + profilePicture: true, }; } From 1137cb07d73f993064661bc543b2eb5d3c0925b7 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 Date: Mon, 8 Apr 2024 11:30:45 +0530 Subject: [PATCH 168/175] New API for 3cp portal. --- .../mock-competency.controller.ts | 21 ++++++++++ .../mock-competency.service.ts | 40 +++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/src/mockModules/mock-competency/mock-competency.controller.ts b/src/mockModules/mock-competency/mock-competency.controller.ts index fee7791..52d5761 100644 --- a/src/mockModules/mock-competency/mock-competency.controller.ts +++ b/src/mockModules/mock-competency/mock-competency.controller.ts @@ -69,6 +69,27 @@ export class MockCompetencyController { } } + @Get("competencyWithLevels") + @ApiOperation({ summary: "fetch all mock competencies with levels" }) + @ApiResponse({ + status: HttpStatus.CREATED, + type: ResponseCompetencyDto, + isArray: true, + }) + async getAllCompetenciesWithLevels(@Res() res) { + try { + const competency = await this.competencyService.findCompetenciesWithLevelNames(); + return res.status(HttpStatus.OK).json({ + data: competency, + message: "Competencies with levels successfully fetched.", + }); + } catch (error) { + return res + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json({ message: error.meta.cause }); + } + } + @Get(":id") @ApiOperation({ summary: "get mock competency by id" }) @ApiResponse({ status: HttpStatus.OK, type: ResponseCompetencyDto }) diff --git a/src/mockModules/mock-competency/mock-competency.service.ts b/src/mockModules/mock-competency/mock-competency.service.ts index bf2d7f9..25a52ab 100644 --- a/src/mockModules/mock-competency/mock-competency.service.ts +++ b/src/mockModules/mock-competency/mock-competency.service.ts @@ -144,4 +144,44 @@ export class MockCompetencyService { }, }); } + + public async findCompetenciesWithLevelNames() { + const competencies = await this.prisma.competency.findMany({ + select: { + id: true, + name: true, + competencyLevels: { + select: { + competencyLevel: { + select: { + id: true, + name: true, + levelNumber: true, + }, + }, + }, + }, + }, + }); + + return this.transformCompetencyObject(competencies); + } + + public transformCompetencyObject(competencies) { + const transformedCompetencies: any = []; + competencies.map(async (competency) => { + const transformedCompetency = { + id: competency.id, + name: competency.name, + levels: competency.competencyLevels.map((level) => ({ + id: level.competencyLevel.id, + name: level.competencyLevel.name, + levelNumber: level.competencyLevel.levelNumber, + })), + }; + transformedCompetencies.push(transformedCompetency); + }); + + return transformedCompetencies; + } } From 2123e9788b5c7135261b0873378824bbf5d9e39c Mon Sep 17 00:00:00 2001 From: VamshiBatta07 Date: Tue, 16 Apr 2024 12:00:43 +0530 Subject: [PATCH 169/175] Update sync all users API. --- .../tarento/tarento.service.ts | 32 ++++ src/user-metadata/user-metadata.controller.ts | 12 +- src/user-metadata/user-metadata.service.ts | 146 ++++++++++++------ src/utils/utils.ts | 15 ++ 4 files changed, 153 insertions(+), 52 deletions(-) diff --git a/src/external-services/tarento/tarento.service.ts b/src/external-services/tarento/tarento.service.ts index e3fad40..4a2b902 100644 --- a/src/external-services/tarento/tarento.service.ts +++ b/src/external-services/tarento/tarento.service.ts @@ -8,6 +8,38 @@ export class TarentoService { constructor(private prisma: PrismaService) {} private fracApiUrl = process.env.FRAC_SERVICE_URL; + async getAllUsers(): Promise { + try { + const baseUrl = process.env.USER_SERVICE_URL || ""; + if (baseUrl == "") { + throw new Error("User service URL not available"); + } + const headers = { + Authorization: "bearer " + process.env.USER_SERVICE_TOKEN, + "Content-Type": "application/json", + }; + + const data = { + request: { + filters: { + }, + }, + }; + + let response = await axios.post(baseUrl, data, { headers }); + + if(response.data.result.response.content.lenght < 1) { + throw new Error("Zero users fetched."); + } + + return response.data.result.response.content; + } catch (error) { + // Handle errors + console.log(error) + throw new Error("Failed to fetch user data from the Tarento's API"); + } + } + async getUser(userId: string): Promise { try { const baseUrl = process.env.USER_SERVICE_URL || ""; diff --git a/src/user-metadata/user-metadata.controller.ts b/src/user-metadata/user-metadata.controller.ts index fdcc051..39878d9 100644 --- a/src/user-metadata/user-metadata.controller.ts +++ b/src/user-metadata/user-metadata.controller.ts @@ -26,30 +26,30 @@ export class UserMetadataController { private readonly logger = new Logger(UserMetadataController.name); @Post("sync-user-metadata") - @ApiOperation({ summary: "Sync userMetadata with user org" }) + @ApiOperation({ summary: "Sync userMetadata with user service" }) @ApiResponse({ status: HttpStatus.CREATED, type: UserResponseMessage }) async SyncUserDataWithFrac(@Res() res): Promise { try { - this.logger.log(`Initiated sync of userMetadata with user org`); + this.logger.log(`Initiated sync of userMetadata with user service`); const userMetadata = await this.userMetadataService.syncUserDataWithFrac(); - this.logger.log(`Successfully sync userMetadata with user org.`); + this.logger.log(`Successfully sync userMetadata with user service.`); return res.status(HttpStatus.CREATED).json({ - message: "Successfully sync userMetadata with user org.", + message: "Successfully sync userMetadata with user service.", data: userMetadata, }); } catch (error) { - this.logger.error(`Failed to sync userMetadata with user org.`, error); + this.logger.error(`Failed to sync userMetadata with user service.`, error); const { errorMessage, statusCode } = getPrismaErrorStatusAndMessage(error); return res.status(statusCode).json({ statusCode, - message: errorMessage || `Failed sync userMetadata with user org.`, + message: errorMessage || `Failed sync userMetadata with user service.`, }); } } diff --git a/src/user-metadata/user-metadata.service.ts b/src/user-metadata/user-metadata.service.ts index 1f0f8cb..c4970e2 100644 --- a/src/user-metadata/user-metadata.service.ts +++ b/src/user-metadata/user-metadata.service.ts @@ -1,4 +1,9 @@ -import { Inject, Injectable, NotFoundException, forwardRef } from "@nestjs/common"; +import { + Inject, + Injectable, + NotFoundException, + forwardRef, +} from "@nestjs/common"; import { TimeUnitsEnum, UserRolesEnum } from "@prisma/client"; import _ from "lodash"; import { MockUserService } from "../mockModules/mock-user/mock-user.service"; @@ -9,15 +14,16 @@ import { UserMappingFileUploadDto } from "../survey-config/dto/create-survey-con import { SurveyService } from "../survey/survey.service"; import { SurveyFormService } from "../survey-form/survey-form.service"; import { TarentoService } from "src/external-services/tarento/tarento.service"; +import { convertToISODateTime } from "src/utils/utils"; @Injectable() export class UserMetadataService { constructor( private prisma: PrismaService, private mockUser: MockUserService, - @Inject(forwardRef(()=>SurveyConfigService)) + @Inject(forwardRef(() => SurveyConfigService)) private surveyConfig: SurveyConfigService, - @Inject(forwardRef(()=>SurveyService)) + @Inject(forwardRef(() => SurveyService)) private surveyService: SurveyService, private surveyForm: SurveyFormService, private tarentoService: TarentoService @@ -25,32 +31,64 @@ export class UserMetadataService { public async syncUserDataWithFrac() { // fetch all users - const users = await this.mockUser.findAll(); + let users = await this.tarentoService.getAllUsers(); + let userCount = 0; // update or create data in user metadata table const usersMetaDataPayload = await Promise.all( users.map(async (user) => { - const { createdAt, designation, id, role, userName, profilePicture } = user; + try { + const isAdmin = user.organisations[0]?.roles.includes("ADMIN") ?? false; - const userMetadataPayload = { - userId: id, - userName, - designation, - isNewEmployee: false, - dateOfJoining: createdAt, - isAdmin: role == _.get(UserRolesEnum, "ADMIN", "") ? true : false, - profilePicture - }; + const designation = + user.profileDetails?.professionalDetails?.length > 0 + ? user.profileDetails?.professionalDetails[0]?.designation + : ""; + + const dateOfJoining = + user.profileDetails?.professionalDetails?.length > 0 + ? user.profileDetails?.professionalDetails[0]?.doj + : user.createdDate; + + const dateOfJoiningISO = await convertToISODateTime(dateOfJoining); - return await this.prisma.userMetadata.upsert({ - where: { userId: userMetadataPayload.userId }, - create: userMetadataPayload, - update: userMetadataPayload, - }); + const userMetadataPayload = { + userId: user.id, + userName: user.userName, + designation, + isNewEmployee: false, + dateOfJoining: dateOfJoiningISO, + isAdmin, + profilePicture: user?.profilePicture ?? "", + }; + + userCount++; + console.log("userData: ", { + userName: user.userName, + designation: designation, + userCount: userCount, + }); + + // Upsert user metadata in Prisma + return await this.prisma.userMetadata.upsert({ + where: { userId: userMetadataPayload.userId }, + create: userMetadataPayload, + update: userMetadataPayload, + }); + } catch (error) { + console.error(`Error upserting user metadata for user ID ${user.id}:`, error); + // Return null or any specific value to indicate that upsert failed for this user + return null; + } }) ); - return usersMetaDataPayload; + // Filter out any null values (failed upserts) from the results + const successfulUpserts = usersMetaDataPayload.filter( + (result) => result !== null + ); + + return successfulUpserts; } async createOrUpdateUserMetadata(userId: string) { @@ -65,10 +103,19 @@ export class UserMetadataService { id: response.data.result?.response?.content[0]?.id, userName: response.data.result?.response?.content[0].userName, createdAt: response.data.result?.response?.content[0]?.createdDate, - role: (response.data.result?.response?.content[0]?.organisations[0]?.roles.indexOf("ADMIN") != -1) ? "ADMIN" : "", // user service has "roles" for every user + role: + response.data.result?.response?.content[0]?.organisations[0]?.roles.indexOf( + "ADMIN" + ) != -1 + ? "ADMIN" + : "", // user service has "roles" for every user profilePicture: userMetadata?.profilePicture, - designation: response.data.result?.response?.content[0]?.profileDetails?.professionalDetails[0]?.designation, - dateOfJoining: response.data.result?.response?.content[0]?.profileDetails?.professionalDetails[0]?.doj + designation: + response.data.result?.response?.content[0]?.profileDetails + ?.professionalDetails[0]?.designation, + dateOfJoining: + response.data.result?.response?.content[0]?.profileDetails + ?.professionalDetails[0]?.doj, }; const userObj = { @@ -78,7 +125,7 @@ export class UserMetadataService { dateOfJoining: user.dateOfJoining, isAdmin: user.role == "ADMIN" ? true : false, designation: user.designation, - profilePicture: null // user service doesn't store profilePicture + profilePicture: null, // user service doesn't store profilePicture }; if (!userMetadata) { @@ -95,7 +142,7 @@ export class UserMetadataService { dateOfJoining: user.createdAt, isAdmin: user.role == UserRolesEnum.ADMIN ? true : false, designation: user.designation, - profilePicture: user.profilePicture + profilePicture: user.profilePicture, }, select: this.metadataSelect, }); @@ -117,27 +164,26 @@ export class UserMetadataService { } async findManyUserMetadata(filter: UserMetadataFilterDto) { - const { - isAdmin, - isNewEmployee, - designation, - limit, - offset, - } = filter; - let users = await this.prisma.userMetadata.findMany({ + const { isAdmin, isNewEmployee, designation, limit, offset } = filter; + let users = await this.prisma.userMetadata.findMany({ where: { isAdmin: isAdmin ?? undefined, isNewEmployee: isNewEmployee ?? undefined, designation: designation ?? undefined, }, - skip: offset ?? undefined, + skip: offset ?? undefined, take: limit ?? undefined, select: this.metadataSelect, }); - for(const user of users){ - const surveysToBeFilled = await this.surveyService.getSurveysToBeFilledByUser(user.userId); - const surveysFilled = await this.surveyService.getSurveysFilledByUser(user.userId); - const wpcasScore = await this.surveyForm.fetchLatestSurveyScoreByUserId(user.userId); + for (const user of users) { + const surveysToBeFilled = + await this.surveyService.getSurveysToBeFilledByUser(user.userId); + const surveysFilled = await this.surveyService.getSurveysFilledByUser( + user.userId + ); + const wpcasScore = await this.surveyForm.fetchLatestSurveyScoreByUserId( + user.userId + ); user["surveysToBeFilled"] = surveysToBeFilled; user["surveysFilled"] = surveysFilled; user["wpcasScore"] = wpcasScore; @@ -225,26 +271,34 @@ export class UserMetadataService { return userMetadata; } - public async validateAndFetchUserIds(prismaClient, userIdList: Set, userMapping: UserMappingFileUploadDto) { - const userValidationArray = [...new Set([...userMapping.assessorIds, userMapping.assesseeId])]; - - const idsNotInUserIdList: string[] = userValidationArray.filter(id => !userIdList.has(id)); + public async validateAndFetchUserIds( + prismaClient, + userIdList: Set, + userMapping: UserMappingFileUploadDto + ) { + const userValidationArray = [ + ...new Set([...userMapping.assessorIds, userMapping.assesseeId]), + ]; + + const idsNotInUserIdList: string[] = userValidationArray.filter( + (id) => !userIdList.has(id) + ); for (const id of idsNotInUserIdList) { try { const user = await prismaClient.userMetadata.findUnique({ where: { userId: id }, }); - - if(!user){ + + if (!user) { await this.createOrUpdateUserMetadata(id); } } catch (error) { throw new NotFoundException(`User with id: ${id} not found.`); } } - - return userIdList = new Set([...userIdList, ...idsNotInUserIdList]); + + return (userIdList = new Set([...userIdList, ...idsNotInUserIdList])); } metadataSelect = { diff --git a/src/utils/utils.ts b/src/utils/utils.ts index a92e17d..0bb235a 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -92,3 +92,18 @@ export function isDateInPast(compareDate: Date, referenceDate: Date): boolean { // Compare the dates return compareDateUTC < referenceDateUTC; } + +export function convertToISODateTime(dateString) { + // Create a new Date object from the input date string + const date = new Date(dateString); + + // Check if the date is invalid + if (isNaN(date.getTime())) { + return "Invalid Date"; + } + + // Convert the Date object to ISO-8601 DateTime format + const isoDateTime = date.toISOString(); + + return isoDateTime; +} From fa6457ef003f6368ab36118110e8391b3ed247aa Mon Sep 17 00:00:00 2001 From: VamshiBatta07 Date: Tue, 16 Apr 2024 12:03:18 +0530 Subject: [PATCH 170/175] Created user sync CRON and frac sync CRON update. --- .env.example | 6 +++++- src/scheduled-tasks/scheduled-tasks.service.ts | 14 +++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index c8d3292..88fefbe 100644 --- a/.env.example +++ b/.env.example @@ -9,7 +9,11 @@ DATABASE_NAME=service-db DATABASE_PORT=5432 PASSBOOK_SERVICE_URL=https://compass.samagra.io SUNBIRD_SERVICE_URL=https://compass.samagra.io -DATABASE_URL=postgresql://${DATABASE_USERNAME}:${DATABASE_PASSWORD}@postgres:${DATABASE_PORT}/${DATABASE_NAME}?schema=public +DATABASE_URL=postgresql://${DATABASE_USERNAME}:${DATABASE_PASSWORD}@172.17.0.1:${DATABASE_PORT}/${DATABASE_NAME}?schema=public USER_SERVICE_TOKEN= USER_SERVICE_COOKIE= USER_SERVICE_URL= +FRAC_SERVICE_URL= +##expression configured to trigger the CRON job EVERY_WEEKEND +FRAC_CRON_EPX="0 0 * * 6,0" +USER_CRON_EPX="0 0 * * 6,0" \ No newline at end of file diff --git a/src/scheduled-tasks/scheduled-tasks.service.ts b/src/scheduled-tasks/scheduled-tasks.service.ts index adebcf0..be81bba 100644 --- a/src/scheduled-tasks/scheduled-tasks.service.ts +++ b/src/scheduled-tasks/scheduled-tasks.service.ts @@ -11,6 +11,7 @@ import { isDayBeforeToday, isToday, isTomorrow } from "../utils/utils"; import { PassbookService } from "src/external-services/passbook/passbook.service"; import { SunbirdRcService } from "src/external-services/sunbird-rc/sunbird-rc.service"; import { TarentoService } from "src/external-services/tarento/tarento.service"; +import { AdminCompetencyService } from "src/admin-competency/admin-competency.service"; @Injectable() export class ScheduledTasksService { @@ -23,7 +24,8 @@ export class ScheduledTasksService { private surveyScoreService: SurveyScoreService, private passbookService: PassbookService, private sunbirdRcService: SunbirdRcService, - private tarentoService: TarentoService + private tarentoService: TarentoService, + private adminCompetency: AdminCompetencyService ) {} private readonly logger = new Logger(ScheduledTasksService.name); @@ -155,6 +157,16 @@ export class ScheduledTasksService { @Cron(process.env.FRAC_CRON_EPX ?? CronExpression.EVERY_1ST_DAY_OF_MONTH_AT_MIDNIGHT) async fracSyncCron(){ + this.logger.log(`Initialized Syncing FRAC data`); await this.tarentoService.formatAndSyncFracData(); + await this.adminCompetency.syncCompetencyData(); + this.logger.log(`Successfully synced FRAC data`); + } + + @Cron(process.env.USER_CRON_EPX ?? CronExpression.EVERY_1ST_DAY_OF_MONTH_AT_MIDNIGHT) + async userSyncCron(){ + this.logger.log(`Initialized Syncing User data`); + await this.userMetadataService.syncUserDataWithFrac(); + this.logger.log(`Successfully synced User data`); } } From 44c85e3920f47ba2775ff4caf93673acb16096fd Mon Sep 17 00:00:00 2001 From: VamshiBatta07 Date: Tue, 16 Apr 2024 23:54:51 +0530 Subject: [PATCH 171/175] fixing designation to role mapping. --- .../migration.sql | 41 ++ prisma/schema.prisma | 34 +- .../tarento/tarento.service.ts | 378 ++++++++++-------- .../mock-designation.controller.ts | 3 +- .../mock-designation.service.ts | 62 ++- .../mock-role/mock-role.service.ts | 50 +-- 6 files changed, 345 insertions(+), 223 deletions(-) create mode 100644 prisma/migrations/20240416181904_fix_designation_to_role_mapping/migration.sql diff --git a/prisma/migrations/20240416181904_fix_designation_to_role_mapping/migration.sql b/prisma/migrations/20240416181904_fix_designation_to_role_mapping/migration.sql new file mode 100644 index 0000000..5f4d0a8 --- /dev/null +++ b/prisma/migrations/20240416181904_fix_designation_to_role_mapping/migration.sql @@ -0,0 +1,41 @@ +/* + Warnings: + + - You are about to drop the column `designationId` on the `roles` table. All the data in the column will be lost. + - You are about to drop the `Designation` table. If the table is not empty, all the data it contains will be lost. + +*/ +-- DropForeignKey +ALTER TABLE "roles" DROP CONSTRAINT "roles_designationId_fkey"; + +-- AlterTable +ALTER TABLE "roles" DROP COLUMN "designationId"; + +-- DropTable +DROP TABLE "Designation"; + +-- CreateTable +CREATE TABLE "designations" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + + CONSTRAINT "designations_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "DesignationToRole" ( + "designationId" INTEGER NOT NULL, + "roleId" INTEGER NOT NULL, + + CONSTRAINT "DesignationToRole_pkey" PRIMARY KEY ("designationId","roleId") +); + +-- CreateIndex +CREATE UNIQUE INDEX "designations_name_key" ON "designations"("name"); + +-- AddForeignKey +ALTER TABLE "DesignationToRole" ADD CONSTRAINT "DesignationToRole_designationId_fkey" FOREIGN KEY ("designationId") REFERENCES "designations"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "DesignationToRole" ADD CONSTRAINT "DesignationToRole_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "roles"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 4cc350f..34390a3 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -216,26 +216,36 @@ model User { // } model Designation { - id Int @id @default(autoincrement()) - name String @unique + id Int @id @default(autoincrement()) + name String @unique description String? - Roles Role[] + roles DesignationToRole[] + + @@map("designations") } model Role { - id Int @id @default(autoincrement()) - name String @unique - description String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - competencies RoleToCompetency[] - Designation Designation? @relation(fields: [designationId], references: [id]) - designationId Int? - activities String[] @default([]) + id Int @id @default(autoincrement()) + name String @unique + description String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + competencies RoleToCompetency[] + designations DesignationToRole[] + activities String[] @default([]) @@map("roles") } +model DesignationToRole { + designation Designation @relation(fields: [designationId], references: [id], onDelete: Cascade) + designationId Int + role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) + roleId Int + + @@id([designationId, roleId]) +} + model Competency { id Int @id @default(autoincrement()) name String @unique diff --git a/src/external-services/tarento/tarento.service.ts b/src/external-services/tarento/tarento.service.ts index 4a2b902..c0c0190 100644 --- a/src/external-services/tarento/tarento.service.ts +++ b/src/external-services/tarento/tarento.service.ts @@ -1,4 +1,5 @@ import { Injectable } from "@nestjs/common"; +import { Competency, CompetencyLevel, Designation, Role } from "@prisma/client"; import axios from "axios"; import _ from "lodash"; import { PrismaService } from "src/prisma/prisma.service"; @@ -21,21 +22,20 @@ export class TarentoService { const data = { request: { - filters: { - }, + filters: {}, }, }; let response = await axios.post(baseUrl, data, { headers }); - if(response.data.result.response.content.lenght < 1) { + if (response.data.result.response.content.lenght < 1) { throw new Error("Zero users fetched."); } return response.data.result.response.content; } catch (error) { // Handle errors - console.log(error) + console.log(error); throw new Error("Failed to fetch user data from the Tarento's API"); } } @@ -64,7 +64,7 @@ export class TarentoService { return response; } catch (error) { // Handle errors - console.log(error) + console.log(error); throw new Error("Failed to fetch user data from the Tarento's API"); } } @@ -96,116 +96,157 @@ export class TarentoService { name: "Positions", status: "Live", }); - const syncedDesignations = await Promise.all( - designations.terms.map(async (position) => { - if (position.status == "Live") { - const designationDTO = { - name: position.name, - description: position.description, - }; - - return await prismaClient.designation.upsert({ + const syncedDesignations: Designation[] = []; + + for (const position of designations.terms) { + if (position.status === "Live") { + const designationDTO = { + name: position.name, + description: position.description, + }; + + try { + const result = await prismaClient.designation.upsert({ where: { name: designationDTO.name, }, create: designationDTO, update: designationDTO, }); + syncedDesignations.push(result); + } catch (error) { + console.error("Error upserting designation:", error); + throw new Error(error); } - }) - ); + } + } - console.log("syncedDesignations", syncedDesignations); + // console.log("syncedDesignations", syncedDesignations); //sync roles const roles = _.find(fracData, { name: "Roles", status: "Live" }); - const syncedRoles = await Promise.all( - roles.terms - .filter((role) => role.status === "Live") - .map(async (role) => { - const roleDTO = { - name: role.name, - description: role.description, - }; - - try { - return await prismaClient.role.upsert({ + const syncedRoles: Role[] = []; + + for (const role of roles.terms) { + if (role.status === "Live") { + const roleDTO = { + name: role.name, + description: role.description, + }; + + try { + const syncedRole = await prismaClient.role.upsert({ + where: { + name: roleDTO.name, + }, + create: roleDTO, + update: roleDTO, + }); + syncedRoles.push(syncedRole); + } catch (error) { + console.error("Error upserting role:", error); + throw new Error(error); + } + } + } + + // console.log("syncedRoles", syncedRoles); + + //map designatioToRoles + for (const position of designations.terms) { + if (position.associations.length > 0) { + const positionToBeMapped = _.find(syncedDesignations, { + name: position.name, + description: position.description, + }); + // console.log("positionToBeMapped: ", positionToBeMapped); + + for (const roleToBeMapped of position.associations) { + // console.log("roleToBeMapped: ", roleToBeMapped); + if (roleToBeMapped.status == "Live") { + const roleAdded = await prismaClient.role.findUnique({ where: { - name: roleDTO.name, + name: roleToBeMapped.name, + description: roleToBeMapped.description, }, - create: roleDTO, - update: roleDTO, }); - } catch (error) { - console.error("Error upserting role:", error); - throw new Error(error); - } - }) - ); + // console.log("roleBeingMapped: ", roleAdded); - // console.log("syncedRoles", syncedRoles); + if (roleAdded != null && positionToBeMapped != null) { + let designationId = positionToBeMapped.id; + let roleId = roleAdded.id; - //map designatioToRoles - await Promise.all( - designations.terms.map(async (position) => { - if (position.associations.length > 0) { - const positionToBeMapped = _.find(syncedDesignations, { - name: position.name, - description: position.description, - }); - position.associations.map(async (roleToBeMapped) => { - if (roleToBeMapped.status == "Live") { - const roleAdded = await prismaClient.role.findUnique({ - where: { - name: roleToBeMapped.name, - description: roleToBeMapped.description, - }, - }); - if (roleAdded != null) { - let designationId = positionToBeMapped.id; - let roleId = roleAdded.id; + console.log( + "designationToBeMapped: ", + positionToBeMapped.name + ); + console.log("roleBeingMapped: ", roleAdded.name); - await prismaClient.role.update({ + try { + await prismaClient.designationToRole.upsert({ where: { - id: roleId, + designationId_roleId: { + designationId, + roleId, + }, }, - data: { + update: { designationId, + roleId, + }, + create: { + designationId, + roleId, }, }); + } catch (error) { + console.error( + "Error syncing competenciesToCompetencyLevels:", + error + ); + throw new Error(error); } } - }); + } } - }) - ); + } + } + + console.log("Successfully mapped designatioToRoles"); //sync competencyLevels const competencyLevels = _.find(fracData, { name: "Competency Levels", status: "Live", }); - const syncedCompetencyLevels = await Promise.all( - competencyLevels.terms.map(async (competencyLevel) => { - if (competencyLevel.status == "Live") { - const competencyLevelDTO = { - name: competencyLevel.name, - description: competencyLevel.description, - levelNumber: - competencyLevel.moreProperties.levelNumber || - competencyLevel.index, - }; - - return await prismaClient.competencyLevel.upsert({ + const syncedCompetencyLevels: CompetencyLevel[] = []; + + for (const competencyLevel of competencyLevels.terms) { + if (competencyLevel.status === "Live") { + const competencyLevelDTO = { + name: competencyLevel.name, + description: competencyLevel.description, + levelNumber: + competencyLevel.moreProperties.levelNumber || + competencyLevel.index, + }; + + try { + const upsertResult = await prismaClient.competencyLevel.upsert({ where: { name: competencyLevelDTO.name, }, create: competencyLevelDTO, update: competencyLevelDTO, }); + + syncedCompetencyLevels.push(upsertResult); + } catch (error) { + console.error("Error upserting role:", error); + throw new Error(error); } - }) - ); + } + } // console.log("syncedCompetencyLevels", syncedCompetencyLevels); @@ -214,24 +255,30 @@ export class TarentoService { name: "Competencies", status: "Live", }); - const syncedCompetency = await Promise.all( - competencies.terms.map(async (competency) => { - if (competency.status == "Live") { - const competencyDTO = { - name: competency.name, - description: competency.description, - }; - - return await prismaClient.competency.upsert({ + const syncedCompetencies: Competency[] = []; + for (const competency of competencies.terms) { + if (competency.status === "Live") { + const competencyDTO = { + name: competency.name, + description: competency.description, + }; + + try { + const result = await prismaClient.competency.upsert({ where: { name: competencyDTO.name, }, create: competencyDTO, update: competencyDTO, }); + + syncedCompetencies.push(result); + } catch (error) { + console.error("Error upserting competency:", error); + throw new Error(error); } - }) - ); + } + } // console.log("syncedCompetency", syncedCompetency); @@ -239,7 +286,7 @@ export class TarentoService { await Promise.all( competencies.terms.map(async (competency) => { if (competency.associations && competency.associations.length > 0) { - const competencyToBeMapped = _.find(syncedCompetency, { + const competencyToBeMapped = _.find(syncedCompetencies, { name: competency.name, description: competency.description, }); @@ -252,25 +299,34 @@ export class TarentoService { name: competencyLevelToBeMapped.name, } ); - if (competencyLevelAdded) { + if (competencyLevelAdded && competencyToBeMapped) { const competencyId = competencyToBeMapped?.id; const competencyLevelId = competencyLevelAdded?.id; - await prismaClient.competencyToCompetencyLevel.upsert({ - where: { - competencyId_competencyLevelId: { + + try { + await prismaClient.competencyToCompetencyLevel.upsert({ + where: { + competencyId_competencyLevelId: { + competencyId, + competencyLevelId, + }, + }, + update: { competencyId, competencyLevelId, }, - }, - update: { - competencyId, - competencyLevelId, - }, - create: { - competencyId, - competencyLevelId, - }, - }); + create: { + competencyId, + competencyLevelId, + }, + }); + } catch (error) { + console.error( + "Error syncing competenciesToCompetencyLevels:", + error + ); + throw new Error(error); + } } } } @@ -286,36 +342,36 @@ export class TarentoService { name: "Activities", status: "Live", }); - await Promise.all( - roles.terms.map(async (role) => { - if (role.associations && role.associations.length > 0) { - let activitiesToBeAdded: string[] = []; - const roleToBeMapped = _.find(syncedRoles, { - name: role.name, - description: role.description, - }); - if (roleToBeMapped) { - for (const activity of role.associations) { - if (activity.status === "Live") { - const activityWithAssociations = _.find(activities.terms, { - name: activity.name, - description: activity.description, - }); - activitiesToBeAdded.push(activity.name); - if ( - activityWithAssociations && - activityWithAssociations.associations && - activityWithAssociations.associations.length > 0 - ) { - for (const competency of activityWithAssociations.associations) { - if (competency.status === "Live") { - const competencyAdded = _.find(syncedCompetency, { - name: competency.name, - }); - if (competencyAdded) { - const roleId = roleToBeMapped.id; - const competencyId = competencyAdded.id; - + for (const role of roles.terms) { + if (role.associations && role.associations.length > 0) { + let activitiesToBeAdded: string[] = []; + const roleToBeMapped = _.find(syncedRoles, { + name: role.name, + description: role.description, + }); + if (roleToBeMapped) { + for (const activity of role.associations) { + if (activity.status === "Live") { + const activityWithAssociations = _.find(activities.terms, { + name: activity.name, + description: activity.description, + }); + activitiesToBeAdded.push(activity.name); + if ( + activityWithAssociations && + activityWithAssociations.associations && + activityWithAssociations.associations.length > 0 + ) { + for (const competency of activityWithAssociations.associations) { + if (competency.status === "Live") { + const competencyAdded = _.find(syncedCompetencies, { + name: competency.name, + }); + if (competencyAdded) { + const roleId = roleToBeMapped.id; + const competencyId = competencyAdded.id; + + try { await prismaClient.roleToCompetency.upsert({ where: { roleId_competencyId: { @@ -332,54 +388,36 @@ export class TarentoService { competencyId, }, }); + } catch (error) { + console.error( + "Error syncing rolesToCompetencies:", + error + ); + throw new Error(error); } } } } } } - console.log("activitiesToBeAdded: ", activitiesToBeAdded); - await prismaClient.role.update({ - where: { - id: roleToBeMapped.id, - }, - data: { - activities: activitiesToBeAdded, - }, - }); } + console.log("activitiesToBeAdded: ", activitiesToBeAdded); + await prismaClient.role.update({ + where: { + id: roleToBeMapped.id, + }, + data: { + activities: activitiesToBeAdded, + }, + }); } - }) - ); + } + } console.log("Successfully mapped rolesToCompetencies"); - - //add activities to roles - await Promise.all( - roles.terms - .filter((role) => role.status === "Live") - .map(async (role) => { - const roleDTO = { - name: role.name, - description: role.description, - }; - - try { - return await prismaClient.role.upsert({ - where: { - name: roleDTO.name, - }, - create: roleDTO, - update: roleDTO, - }); - } catch (error) { - console.error("Error upserting role:", error); - throw new Error(error); - } - }) - ); + console.log("Successfully mapped activities to roles"); }); - return "Tarento's FRAC data synced successfully"; + return "FRAC data synced successfully"; } catch (error) { throw new Error(error); } diff --git a/src/mockModules/mock-designation/mock-designation.controller.ts b/src/mockModules/mock-designation/mock-designation.controller.ts index 96aee06..1ce6b2f 100644 --- a/src/mockModules/mock-designation/mock-designation.controller.ts +++ b/src/mockModules/mock-designation/mock-designation.controller.ts @@ -15,7 +15,7 @@ import { ResponseDesignationDto, UpdateDesignationDto, } from "./dto"; -import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { ApiBody, ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; @Controller("designation") @ApiTags("mockFracService/designation") @@ -119,6 +119,7 @@ export class MockDesignationController { @Post("addRoleToDesignation/:id") @ApiOperation({ summary: "Add a Role to mock designation." }) @ApiResponse({ status: HttpStatus.OK, type: ResponseDesignationDto}) + @ApiBody({ schema:{ example:{ roleId: 1 } } }) async addRoleToDesignation(@Param("id") id: number, @Res() res, @Body() {roleId}: any) { try { const designation = await this.designationService.addRoleToDesignation(id, roleId); diff --git a/src/mockModules/mock-designation/mock-designation.service.ts b/src/mockModules/mock-designation/mock-designation.service.ts index 5e10fc2..ee13b34 100644 --- a/src/mockModules/mock-designation/mock-designation.service.ts +++ b/src/mockModules/mock-designation/mock-designation.service.ts @@ -45,42 +45,72 @@ export class MockDesignationService { } async addRoleToDesignation(id: number, roleId: number) { - return await this.prisma.designation.update({ + try { + await this.prisma.designationToRole.upsert({ + where: { + designationId_roleId: { + designationId: id, + roleId: roleId, + }, + }, + update: { + designationId: id, + roleId: roleId, + }, + create: { + designationId: id, + roleId: roleId, + }, + }); + } catch (error) { + throw new Error("Error adding role to designation."); + } + + const designation = await this.prisma.designation.findUnique({ where: { id, }, - data: { - Roles: { - connect: [{ id: roleId }], - }, - }, select: { id: true, name: true, description: true, - Roles: { - orderBy: { - createdAt: "asc", - }, + roles: { select: { - id: true, - name: true, - description: true, + role: { + select: { + id: true, + name: true, + description: true, + }, + }, }, }, }, }); + + return { + id: designation?.id, + name: designation?.name, + description: designation?.description, + Role: designation?.roles.map((item) => item.role), + }; } async findAllRolesForDesignation(designation: string) { - return this.prisma.designation.findUnique({ + const result = await this.prisma.designation.findUnique({ where: { name: designation, }, select: { - Roles: true, + roles: { + select: { + role: true, + }, + }, }, }); - + if (!result) return { Roles: [] }; + const roles = result.roles.map((item) => item.role); + return { Roles: roles }; } } diff --git a/src/mockModules/mock-role/mock-role.service.ts b/src/mockModules/mock-role/mock-role.service.ts index 9d11703..894b315 100644 --- a/src/mockModules/mock-role/mock-role.service.ts +++ b/src/mockModules/mock-role/mock-role.service.ts @@ -1,13 +1,8 @@ -import { - ForbiddenException, - Injectable, - NotFoundException, -} from "@nestjs/common"; +import { Injectable, NotFoundException } from "@nestjs/common"; import { PrismaService } from "../../prisma/prisma.service"; import { CreateMockRoleDto, UpdateMockRoleDto } from "./dto"; import { MockCompetencyService } from "../mock-competency/mock-competency.service"; import { CreateCompetencyDto } from "../mock-competency/dto"; -import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library"; @Injectable() export class MockRoleService { @@ -68,29 +63,33 @@ export class MockRoleService { throw new Error("User does not have any designation"); } - const roles = await this.prisma.designation.findUnique({ + const response = await this.prisma.designation.findUnique({ where: { name: user.designation, }, select: { - Roles: { + roles: { select: { - id: true, - name: true, - description: true, - competencies: { + role: { select: { - competency: { + id: true, + name: true, + description: true, + competencies: { select: { - id: true, - name: true, - competencyLevels: { + competency: { select: { - competencyLevel: { + id: true, + name: true, + competencyLevels: { select: { - id: true, - name: true, - levelNumber: true, + competencyLevel: { + select: { + id: true, + name: true, + levelNumber: true, + }, + }, }, }, }, @@ -104,10 +103,13 @@ export class MockRoleService { }, }); - if(!roles){ - throw new Error(`No roles found for the user(designation: ${user.designation}) with id: ${userId}`); + if (!response) { + throw new Error( + `No roles found for the user(designation: ${user.designation}) with id: ${userId}` + ); } - const transformedRoles = this.transformRolesObject(roles.Roles); + const roles = response.roles.map((item) => item.role); + const transformedRoles = this.transformRolesObject(roles); return { roles: transformedRoles }; } @@ -218,7 +220,7 @@ export class MockRoleService { }, select: { competencyId: true, - } + }, }); } From c0861e1ec23363660ce2cf4428778c0f14d65229 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 Date: Tue, 16 Apr 2024 23:59:13 +0530 Subject: [PATCH 172/175] disabling seeding. --- prisma/seeds/seed.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/prisma/seeds/seed.ts b/prisma/seeds/seed.ts index 067e2f1..18e782e 100644 --- a/prisma/seeds/seed.ts +++ b/prisma/seeds/seed.ts @@ -41,12 +41,12 @@ async function seed() { ]); } -seed() - .catch((error) => { - console.log("\n\n Seeding error:--------", error); - process.exit(1); - }) - .finally(() => { - console.log("\n\n Seeding stopped/completed"); - prisma.$disconnect(); - }); +// seed() +// .catch((error) => { +// console.log("\n\n Seeding error:--------", error); +// process.exit(1); +// }) +// .finally(() => { +// console.log("\n\n Seeding stopped/completed"); +// prisma.$disconnect(); +// }); From 2882c879ed63f16be0b970958ec3476fb8471c39 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 Date: Wed, 17 Apr 2024 12:28:46 +0530 Subject: [PATCH 173/175] fix: user api integration in createOrUpdateUserMetadata API --- src/user-metadata/user-metadata.service.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/user-metadata/user-metadata.service.ts b/src/user-metadata/user-metadata.service.ts index c4970e2..f549084 100644 --- a/src/user-metadata/user-metadata.service.ts +++ b/src/user-metadata/user-metadata.service.ts @@ -111,11 +111,13 @@ export class UserMetadataService { : "", // user service has "roles" for every user profilePicture: userMetadata?.profilePicture, designation: - response.data.result?.response?.content[0]?.profileDetails - ?.professionalDetails[0]?.designation, + response.data.result?.response?.content[0]?.profileDetails?.professionalDetails?.length > 0 + ? response.data.result?.response?.content[0]?.profileDetails?.professionalDetails[0]?.designation + : "", dateOfJoining: - response.data.result?.response?.content[0]?.profileDetails - ?.professionalDetails[0]?.doj, + response.data.result?.response?.content[0]?.profileDetails?.professionalDetails?.length > 0 + ? response.data.result?.response?.content[0]?.profileDetails?.professionalDetails[0]?.doj + : response.data.result?.response?.content[0]?.createdDate, }; const userObj = { @@ -139,7 +141,7 @@ export class UserMetadataService { where: { userId }, data: { isNewEmployee: false, - dateOfJoining: user.createdAt, + dateOfJoining: user.dateOfJoining, isAdmin: user.role == UserRolesEnum.ADMIN ? true : false, designation: user.designation, profilePicture: user.profilePicture, From c0d718d71be121edca58b44e3bcb4719eca9274a Mon Sep 17 00:00:00 2001 From: kh4l1d64 <144687945+kh4l1d64@users.noreply.github.com> Date: Wed, 17 Apr 2024 13:35:11 +0530 Subject: [PATCH 174/175] Update .env.example --- .env.example | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.env.example b/.env.example index 88fefbe..d4dfc17 100644 --- a/.env.example +++ b/.env.example @@ -1,14 +1,14 @@ NODE_ENV=development APP_NAME="WPCAS Survey API" API_PREFIX=api -APP_PORT=4010 +APP_PORT= -DATABASE_USERNAME=postgres -DATABASE_PASSWORD=secret -DATABASE_NAME=service-db -DATABASE_PORT=5432 -PASSBOOK_SERVICE_URL=https://compass.samagra.io -SUNBIRD_SERVICE_URL=https://compass.samagra.io +DATABASE_USERNAME= +DATABASE_PASSWORD= +DATABASE_NAME= +DATABASE_PORT= +PASSBOOK_SERVICE_URL= +SUNBIRD_SERVICE_URL= DATABASE_URL=postgresql://${DATABASE_USERNAME}:${DATABASE_PASSWORD}@172.17.0.1:${DATABASE_PORT}/${DATABASE_NAME}?schema=public USER_SERVICE_TOKEN= USER_SERVICE_COOKIE= @@ -16,4 +16,4 @@ USER_SERVICE_URL= FRAC_SERVICE_URL= ##expression configured to trigger the CRON job EVERY_WEEKEND FRAC_CRON_EPX="0 0 * * 6,0" -USER_CRON_EPX="0 0 * * 6,0" \ No newline at end of file +USER_CRON_EPX="0 0 * * 6,0" From 816acbca05270d72fef3cbb897d716e2db416334 Mon Sep 17 00:00:00 2001 From: VamshiBatta07 Date: Wed, 17 Jul 2024 10:28:44 +0530 Subject: [PATCH 175/175] Creating views for Metabase. --- .env.example | 1 + package-lock.json | 125 ++++++++++++++++++++++++++++ package.json | 1 + prisma/scripts/createViewQueries.ts | 90 ++++++++++++++++++++ prisma/scripts/moveViewsQueries.ts | 81 ++++++++++++++++++ prisma/seeds/seed.ts | 79 +++++++++++++++--- 6 files changed, 367 insertions(+), 10 deletions(-) create mode 100644 prisma/scripts/createViewQueries.ts create mode 100644 prisma/scripts/moveViewsQueries.ts diff --git a/.env.example b/.env.example index d4dfc17..9650f87 100644 --- a/.env.example +++ b/.env.example @@ -6,6 +6,7 @@ APP_PORT= DATABASE_USERNAME= DATABASE_PASSWORD= DATABASE_NAME= +TELEMETRY_DATABASE_NAME= DATABASE_PORT= PASSBOOK_SERVICE_URL= SUNBIRD_SERVICE_URL= diff --git a/package-lock.json b/package-lock.json index 2fdcea7..63a8703 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "csv-parser": "^3.0.0", "lodash": "^4.17.21", "multer": "^1.4.5-lts.1", + "pg": "^8.12.0", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.2.0" @@ -8902,6 +8903,87 @@ "node": ">=8" } }, + "node_modules/pg": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.12.0.tgz", + "integrity": "sha512-A+LHUSnwnxrnL/tZ+OLfqR1SxLN3c/pgDztZ47Rpbsd4jUytsTtwQo/TLPRzPJMp/1pbhYVhH9cuSZLAajNfjQ==", + "dependencies": { + "pg-connection-string": "^2.6.4", + "pg-pool": "^3.6.2", + "pg-protocol": "^1.6.1", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz", + "integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz", + "integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz", + "integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, "node_modules/phin": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/phin/-/phin-3.7.0.tgz", @@ -9024,6 +9106,41 @@ "trouter": "^2.0.1" } }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -9596,6 +9713,14 @@ "node": ">=0.10.0" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", diff --git a/package.json b/package.json index 8eeb648..5664e15 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "csv-parser": "^3.0.0", "lodash": "^4.17.21", "multer": "^1.4.5-lts.1", + "pg": "^8.12.0", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.2.0" diff --git a/prisma/scripts/createViewQueries.ts b/prisma/scripts/createViewQueries.ts new file mode 100644 index 0000000..6ae1ccb --- /dev/null +++ b/prisma/scripts/createViewQueries.ts @@ -0,0 +1,90 @@ +export const createViewQueries = [ + ` + DROP VIEW IF EXISTS telemetry_user_details + `, + + ` + DROP VIEW IF EXISTS position_competency_mapping + `, + + ` + DROP VIEW IF EXISTS metrics_wpcas_overall + `, + + ` + CREATE VIEW telemetry_user_details AS + SELECT + u."userId", + u."userName", + u."isAdmin", + u.designation AS position, + COUNT(rt_surveyed.id) FILTER (WHERE rt_surveyed.status = 'COMPLETED') AS "surveyedCount", + COUNT(rt_filled.id) FILTER (WHERE rt_filled.status = 'COMPLETED') AS "surveysFilled", + COUNT(rt_received.id) AS "surveysReceived", + COUNT(rt_pending.id) FILTER (WHERE rt_pending.status = 'PENDING') AS "surveysYetToBeFilled", + COALESCE(NULLIF(COUNT(rt_filled.id) FILTER (WHERE rt_filled.status = 'COMPLETED'), 0) / NULLIF(COUNT(rt_received.id), 0), 0) AS "surveyResponseRate", + COALESCE( + ( + SELECT ss.score + FROM survey_scores ss + JOIN survey_form sf ON ss."surveyFormId" = sf.id + WHERE sf."userId" = u."userId" + ORDER BY sf."createdAt" DESC + LIMIT 1 + ), null + ) AS "wpcasScore" + FROM + "UserMetadata" u + LEFT JOIN + response_tracker rt_surveyed ON rt_surveyed."assesseeId" = u."userId" + LEFT JOIN + response_tracker rt_filled ON rt_filled."assessorId" = u."userId" + LEFT JOIN + response_tracker rt_received ON rt_received."assesseeId" = u."userId" + LEFT JOIN + response_tracker rt_pending ON rt_pending."assessorId" = u."userId" + GROUP BY + u."userId", u."userName", u."designation", u."isAdmin" + `, + + ` + CREATE VIEW position_competency_mapping AS + SELECT + d.id AS "designationId", + d.name AS "designationName", + r.id AS "roleId", + r.name AS "roleName", + c.id AS "competencyId", + c.name AS "competencyName", + cl.id AS "competencyLevelId", + cl.name AS "competencyLevelName", + cl."levelNumber" AS "competencyLevelNumber" + FROM + designations d + JOIN + "DesignationToRole" dr ON d.id = dr."designationId" + JOIN + roles r ON dr."roleId" = r.id + JOIN + "RoleToCompetency" rc ON r.id = rc."roleId" + JOIN + competencies c ON rc."competencyId" = c.id + JOIN + "CompetencyToCompetencyLevel" ccl ON c.id = ccl."competencyId" + JOIN + competency_levels cl ON ccl."competencyLevelId" = cl.id + ORDER BY + d.id, r.id, c.id, cl.id, cl."levelNumber" + `, + + ` + CREATE VIEW metrics_wpcas_overall AS + SELECT + (SELECT COUNT(*) FROM "UserMetadata") AS "registeredUsers", + (SELECT COUNT(*) FROM response_tracker) AS "overallSurveysReceived", + (SELECT COUNT(*) FROM response_tracker WHERE status = 'COMPLETED') AS "overallSurveysCompleted", + (SELECT COUNT(*) FROM response_tracker WHERE status = 'PENDING') AS "unfilledSurveyCount", + COALESCE(NULLIF((SELECT COUNT(*) FROM response_tracker WHERE status = 'PENDING'), 0) / NULLIF((SELECT COUNT(*) FROM response_tracker), 0), 0) AS "overallSurveyNonResponseRate", + COALESCE(NULLIF((SELECT COUNT(*) FROM response_tracker WHERE status = 'COMPLETED'), 0) / NULLIF((SELECT COUNT(*) FROM response_tracker), 0), 0) AS "overallSurveyResponseRate" + ` +]; \ No newline at end of file diff --git a/prisma/scripts/moveViewsQueries.ts b/prisma/scripts/moveViewsQueries.ts new file mode 100644 index 0000000..e91b178 --- /dev/null +++ b/prisma/scripts/moveViewsQueries.ts @@ -0,0 +1,81 @@ +const dbName = process.env.DATABASE_NAME; +const dbUserName = process.env.DATABASE_USERNAME; +const dbPassword = process.env.DATABASE_PASSWORD; +const dbPort = process.env.DATABASE_PORT; +const dbHost = '172.17.0.1'; + +export const copyViewQueries = [ + `CREATE EXTENSION IF NOT EXISTS postgres_fdw`, + ` + DO $$ + BEGIN + IF NOT EXISTS ( + SELECT 1 + FROM pg_foreign_server + WHERE srvname = 'wpcas_server' + ) THEN + EXECUTE 'CREATE SERVER wpcas_server + FOREIGN DATA WRAPPER postgres_fdw + OPTIONS (host ''${dbHost}'', dbname ''${dbName}'', port ''${dbPort}'')'; + END IF; + END $$; + `, + ` + DO $$ + BEGIN + IF NOT EXISTS ( + SELECT 1 + FROM pg_user_mappings + WHERE srvname = 'wpcas_server' AND usename = '${dbUserName}' + ) THEN + EXECUTE 'CREATE USER MAPPING FOR ${dbUserName} + SERVER wpcas_server + OPTIONS (user ''${dbUserName}'', password ''${dbPassword}'')'; + END IF; + END $$; + `, + ` + CREATE FOREIGN TABLE IF NOT EXISTS telemetry_user_details ( + "userId" TEXT, + "userName" TEXT, + "isAdmin" BOOLEAN, + "position" TEXT, + "surveyedCount" BIGINT, + "surveysFilled" BIGINT, + "surveysReceived" BIGINT, + "surveysYetToBeFilled" BIGINT, + "surveyResponseRate" BIGINT, + "wpcasScore" DOUBLE PRECISION + ) + SERVER wpcas_server + OPTIONS (schema_name 'public', table_name 'telemetry_user_details'); + `, + ` + CREATE FOREIGN TABLE IF NOT EXISTS position_competency_mapping ( + "designationId" INTEGER, + "designationName" TEXT, + "roleId" INTEGER, + "roleName" TEXT, + "competencyId" INTEGER, + "competencyName" TEXT, + "competencyLevelId" INTEGER, + "competencyLevelName" TEXT, + "competencyLevelNumber" INTEGER + ) + SERVER wpcas_server + OPTIONS (schema_name 'public', table_name 'position_competency_mapping'); + `, + ` + CREATE FOREIGN TABLE IF NOT EXISTS metrics_wpcas_overall ( + "registeredUsers" BIGINT, + "overallSurveysReceived" BIGINT, + "overallSurveysCompleted" BIGINT, + "unfilledSurveyCount" BIGINT, + "overallSurveyNonResponseRate" BIGINT, + "overallSurveyResponseRate" BIGINT, + "timeStamp" date + ) + SERVER wpcas_server + OPTIONS (schema_name 'public', table_name 'metrics_wpcas_overall'); + ` +]; \ No newline at end of file diff --git a/prisma/seeds/seed.ts b/prisma/seeds/seed.ts index 18e782e..bed1602 100644 --- a/prisma/seeds/seed.ts +++ b/prisma/seeds/seed.ts @@ -1,4 +1,4 @@ -import { PrismaClient } from "@prisma/client"; +import { Prisma, PrismaClient } from "@prisma/client"; import { competencies } from "./data/mock-competencies.data"; import { competenciesToCompetencyLevels } from "./data/mock-competenciesToCompetencyLevels.data"; import { competencyLevels } from "./data/mock-competencyLevels.data"; @@ -6,8 +6,13 @@ import { designations } from "./data/mock-designation.data"; import { roles } from "./data/mock-roles.data"; import { rolesToCompetencies } from "./data/mock-rolesToCompetencies.data"; import { users } from "./data/mock-users.data"; +import { Logger } from "@nestjs/common"; +import { copyViewQueries } from "../scripts/moveViewsQueries"; +import { createViewQueries } from "../scripts/createViewQueries"; +const { Client } = require('pg'); const prisma = new PrismaClient(); +const telemetryDbName = process.env.TELEMETRY_DATABASE_NAME; async function seed() { await prisma.$transaction([ @@ -41,12 +46,66 @@ async function seed() { ]); } -// seed() -// .catch((error) => { -// console.log("\n\n Seeding error:--------", error); -// process.exit(1); -// }) -// .finally(() => { -// console.log("\n\n Seeding stopped/completed"); -// prisma.$disconnect(); -// }); +async function createViews() { + let logger = new Logger("CreatingViews"); + logger.log(`Started creating views`); + + for (const sql of createViewQueries) { + logger.log(sql); + await prisma.$executeRaw`${Prisma.raw(sql)}`; + } + + const res:any = await prisma.$queryRaw`${Prisma.raw(`SELECT datname FROM pg_database WHERE datname = '${telemetryDbName}'`)}`; + if (res.length === 0) { + // Create the telemetry-views database if it does not exist + await prisma.$queryRaw`${Prisma.raw(`CREATE DATABASE "${telemetryDbName}"`)}`; + logger.log(`Database "${telemetryDbName}" created.`); + } else { + logger.log(`Database "${telemetryDbName}" already exists.`); + } + + logger.log(`Successfully created views`); +} + +async function moveViews() { + let logger = new Logger("MovingViews"); + + const telemetryClient = new Client({ + user: process.env.DATABASE_USERNAME, + host: '172.17.0.1', + database: process.env.TELEMETRY_DATABASE_NAME, + password: process.env.DATABASE_PASSWORD, + port: 5432, + }); + + await telemetryClient.connect(); + + logger.log(copyViewQueries); + + logger.log(`Started moving views`); + + for (const sql of copyViewQueries) { + logger.log(sql); + await telemetryClient.query(sql); + } + + await telemetryClient.end(); + + logger.log(`Successfully moved views`); +} + +// execute the functions +async function main() { + try { + // await seed(); + await createViews(); + await moveViews(); + } catch (e) { + console.error(e); + process.exit(1); + } finally { + await prisma.$disconnect(); + } +} + +main();