diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 00000000..236b5a78 --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,38 @@ +language: "en" + +early_access: false + +reviews: + request_changes_workflow: true + high_level_summary: true + poem: false + review_status: true + collapse_walkthrough: false + path_filters: + - "!**/.xml" + path_instructions: + - path: "**/*.js" + instructions: "Review the JavaScript code for conformity with the Google JavaScript style guide, highlighting any deviations." + - path: "**/*.ts" + instructions: | + "Review the JavaScript code for conformity with the Google JavaScript style guide, highlighting any deviations. Ensure that: + - The code adheres to best practices associated with nodejs. + - The code adheres to best practices associated with nestjs framework. + - The code adheres to best practices recommended for performance. + - The code adheres to similar naming conventions for controllers, models, services, methods, variables." + auto_review: + enabled: true + ignore_title_keywords: + - "WIP" + - "DO NOT MERGE" + drafts: false + base_branches: + - "master" + - "dev" + - "feat/*" + - "feat-*" + - "release-*" + - "Shiksha-2.0" + +chat: + auto_reply: true diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 9f8bb3f1..bef5b4a8 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -1,8 +1,8 @@ -name: Docker Image CI +name: Docker Image CI on: push: - branches: [ main ] + branches: [ oblf-21stFeb ] jobs: build: diff --git a/.github/workflows/eks-pratham-deployment.yaml b/.github/workflows/eks-pratham-deployment.yaml new file mode 100644 index 00000000..ee3ba67a --- /dev/null +++ b/.github/workflows/eks-pratham-deployment.yaml @@ -0,0 +1,78 @@ +name: Deploy to EKS-Pratham +on: + push: + branches: + - june15-pilot +env: + ECR_REPOSITORY: ${{ secrets.ECR_REPOSITORY }} + EKS_CLUSTER_NAME: ${{ secrets.EKS_CLUSTER_NAME }} + AWS_REGION: ${{ secrets.AWS_REGION_NAME }} +jobs: + build: + name: Deployment + runs-on: ubuntu-latest + steps: + - name: Set short git commit SHA + id: commit + uses: prompt/actions-commit-hash@v2 + - name: Check out code + uses: actions/checkout@v2 + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{env.AWS_REGION}} + - name: Setup Node Env + uses: actions/setup-node@v3 + with: + node-version: 21.1.0 + - name: Copy .env file + env: + ENV_FILE_CONTENT: ${{ secrets.ENV_FILE_CONTENT }} + run: echo "$ENV_FILE_CONTENT" > manifest/configmap.yaml + - name: Show PWD and list content + run: | + echo "Current Working Directory: pwd" + pwd + ls -ltra + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + - name: Build, tag, and push image to Amazon ECR + env: + ECR_REGISTRY: ${{ secrets.ECR_REPOSITORY }} + IMAGE_TAG: ${{ secrets.ECR_IMAGE }} + run: | + docker build -t ${{ secrets.ECR_REPOSITORY }}:${{ secrets.IMAGE_TAG }} . + docker push ${{ secrets.ECR_REPOSITORY }}:${{ secrets.IMAGE_TAG }} + - name: Update kube config + run: aws eks update-kubeconfig --name ${{ secrets.EKS_CLUSTER_NAME }} --region ${{ secrets.AWS_REGION_NAME }} + - name: Deploy to EKS + env: + ECR_REGISTRY: ${{ secrets.ECR_REPOSITORY }} + IMAGE_TAG: ${{ secrets.IMAGE_TAG }} + ECR_REPOSITORY: ${{ secrets.ECR_REPOSITORY }} + ECR_IMAGE: ${{ secrets.ECR_IMAGE }} + run: | + export ECR_REPOSITORY=${{ secrets.ECR_REPOSITORY }} + export IMAGE_TAG=${{ secrets.IMAGE_TAG }} + export ECR_IMAGE=${{ secrets.ECR_IMAGE }} + envsubst < manifest/backend.yaml > manifest/backend-updated.yaml + cat manifest/backend-updated.yaml + rm -rf manifest/backend-service.yaml + kubectl delete deployment backend + kubectl delete service backend + kubectl delete cm backend-service-config + kubectl apply -f manifest/backend-updated.yaml + kubectl apply -f manifest/configmap.yaml + sleep 10 + kubectl get pods + kubectl get services + kubectl get deployment diff --git a/.github/workflows/pratham-qa.yml b/.github/workflows/pratham-qa.yml new file mode 100644 index 00000000..b3fb1940 --- /dev/null +++ b/.github/workflows/pratham-qa.yml @@ -0,0 +1,21 @@ +name: Deploy to QA +on: + push: + branches: + - Shiksha-2.0 +jobs: + deploy: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - + name: Deploy Stack + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.HOST_NAME_PRATHAM_QA }} + username: ${{ secrets.USERNAME_PRATHAM_QA }} + key: ${{ secrets.EC2_SSH_KEY_PRATHAM_QA }} + port: ${{ secrets.PORT_PRATHAM_QA }} + script: | + cd ${{ secrets.TARGET_DIR_PRATHAM_QA }} + ./deploy.sh diff --git a/.github/workflows/prod-oblf.yml b/.github/workflows/prod-oblf.yml new file mode 100644 index 00000000..622e3cca --- /dev/null +++ b/.github/workflows/prod-oblf.yml @@ -0,0 +1,42 @@ +name: Deploy to DEV +on: + push: + branches: + - prod-oblf +jobs: + build: + name: Generate Build and Deploy to DEV + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup Node Env + uses: actions/setup-node@v3 + with: + node-version: 21.1.0 + + - name: Deploy to Server 1 + uses: easingthemes/ssh-deploy@main + env: + SSH_PRIVATE_KEY: ${{ secrets.EC2_SSH_KEY }} + REMOTE_HOST: ${{ secrets.HOST_DNS }} + REMOTE_USER: ${{ secrets.USERNAME_OBLF }} + TARGET: ${{ secrets.TARGET_DIR }} + + - name: Set up SSH key + run: | + mkdir -p ~/.ssh + echo "${{ secrets.EC2_SSH_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + # Add the SSH key to the known_hosts file (replace hostname with your actual hostname) + ssh-keyscan -H ${{ secrets.HOST_DNS }} >> ~/.ssh/known_hosts + sudo apt-get install sshpass + + - name: Deploy to server + run: | + sshpass -p '${{ secrets.EC2_SSH_KEY }}' ssh -v -o StrictHostKeyChecking=no ${{ secrets.USERNAME_OBLF }}@${{ secrets.HOST_DNS }} <<'ENDSSH' + cd /home/oblf-21Feb/oblf-githubaction-backend + ./deploy.sh + ENDSSH + diff --git a/Dockerfile b/Dockerfile index 12c14c7f..b7649c97 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:21.1.0 as dependencies +FROM node:20 as dependencies WORKDIR usr/src/app COPY package*.json ./ RUN npm install diff --git a/Dockerfile1 b/Dockerfile1 deleted file mode 100644 index 7add4912..00000000 --- a/Dockerfile1 +++ /dev/null @@ -1,30 +0,0 @@ -FROM node:16-alpine As development - -WORKDIR /usr/src/app - -#COPY package*.json ./ - -COPY . . - -RUN yarn install --only=development --ignore-engines - -COPY . . - -RUN yarn run build - -FROM node:16-alpine as production - -ARG NODE_ENV=production -ENV NODE_ENV=${NODE_ENV} - -WORKDIR /usr/src/app - -COPY . ./ - -RUN yarn install --only=production --ignore-engines - -COPY . . - -COPY --from=development /usr/src/app/dist ./dist - -CMD ["node", "dist/main"] diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 00000000..4ae6904f --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,34 @@ +pipeline { + agent any + stages { + stage('Checkout'){ + steps{ + cleanWs() + sh 'rm -rf *' + //checkout scmGit(branches: [[name: '*/main']], extensions: [], userRemoteConfigs: [[credentialsId: 'githubtoken', url: 'https://github.com/tekdi/onest.network.backend.git']]) + // checkout scmGit(branches: [[name: '*/dev']], extensions: [], userRemoteConfigs: [[credentialsId: 'ONEST-ID', url: 'https://github.com/tekdi/onest.network.backend.git']]) + checkout scmGit(branches: [[name: '*/Shiksha-2.0']], extensions: [], userRemoteConfigs: [[credentialsId: 'github-1', url: 'https://github.com/tekdi/shiksha-backend.git']]) + } + } + + stage ('Build-image') { + steps { + sh 'docker build -t shiksha-backend-2.0 .' + } + } + + stage ('Deploy') { + steps { + sh 'docker-compose up -d --force-recreate --no-deps backend' + } + } + post { + stage ('Deploy') { + // Send notification to Slack + slackSend(channel: '#your_channel_name', color: 'good', message: "Build ${currentBuild.result}: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})") + } + } + + + } +} diff --git a/Jenkinsfile1 b/Jenkinsfile1 deleted file mode 100644 index 95db6d10..00000000 --- a/Jenkinsfile1 +++ /dev/null @@ -1,30 +0,0 @@ -pipeline { - agent any - stages { - stage('clean workspace'){ - steps{ - cleanWs() - } - } - stage('Checkout'){ - - steps{ - - git branch: 'main', credentialsId: 'github-1', url: 'https://github.com/tekdi/shiksha-backend.git' - echo "========================== ***Repository cloned Successfully*** ==========================" - - } - } - - stage ('Build&Deploy') { - - steps { - - sh 'cp -r /shiksha/.env .' - sh 'docker build -t backend .' - sh 'docker-compose up -d --force-recreate --no-deps' - } - } - - } -} diff --git a/docker-compose.yml b/docker-compose.yml index 15d0bdad..b5fdaf96 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,46 +1,11 @@ -version: '3.7' +version: "3.6" services: - main: - container_name: shiksha-backend - build: - context: . - target: development - volumes: - - .:/usr/src/app - - /usr/src/app/node_modules + backend: + image: shiksha-backend-2.0 + container_name: "shiksha-backend-2.0" + restart: always ports: - - ${SERVER_PORT}:${SERVER_PORT} - - 9229:9229 - command: yarn run start:dev + - 3000:3000 env_file: - - .env - networks: - - webnet - restart: always - # depends_on: - # - redis - # - postgres - # redis: - # container_name: redis - # image: redis:5 - # networks: - # - webnet - # postgres: - # container_name: postgres - # image: postgres:11 - # networks: - # - webnet - # environment: - # POSTGRES_PASSWORD: ${DB_PASSWORD} - # POSTGRES_USER: ${DB_USERNAME} - # POSTGRES_DB: ${DB_DATABASE_NAME} - # PG_DATA: /var/lib/postgresql/data - # ports: - # - 5432:5432 - # volumes: - # - pgdata:/var/lib/postgresql/data -networks: - webnet: -# volumes: -# pgdata: + - /home/ubuntu-backend-shiksha2.0/.env diff --git a/manifest/backend.yaml b/manifest/backend.yaml new file mode 100644 index 00000000..a4119b8f --- /dev/null +++ b/manifest/backend.yaml @@ -0,0 +1,44 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + app: backend + name: backend +spec: + replicas: 1 + selector: + matchLabels: + app: backend + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + app: backend + spec: + containers: + - image: ${ECR_REPOSITORY}:${IMAGE_TAG} + imagePullPolicy: "" + name: backend-pratham + ports: + - containerPort: 3000 + envFrom: + - configMapRef: + name: backend-service-config + resources: {} + restartPolicy: Always +status: {} + +--- +apiVersion: v1 +kind: Service +metadata: + name: backend +spec: + type: ClusterIP + ports: + - port: 3000 + protocol: TCP + selector: + app: backend diff --git a/package-lock.json b/package-lock.json index 2b66cd26..05826c82 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,7 +1,7 @@ { "name": "shiksha-backend-v2", "version": "0.0.1", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -11,31 +11,38 @@ "dependencies": { "@nestjs/axios": "^0.0.7", "@nestjs/common": "^8.4.2", - "@nestjs/config": "^2.0.0", + "@nestjs/config": "^2.3.4", "@nestjs/core": "^8.0.0", "@nestjs/jwt": "^8.0.0", + "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^8.0.0", "@nestjs/schedule": "^1.1.0", - "@nestjs/swagger": "^5.2.0", + "@nestjs/swagger": "^5.2.1", + "@nestjs/typeorm": "^10.0.2", "axios": "^0.26.1", "cache-manager": "^3.6.1", "class-transformer": "^0.5.1", "class-validator": "^0.13.2", + "date-fns": "^3.6.0", "dotenv": "^16.0.0", "express": "^4.17.3", "form-data": "^4.0.0", "graphql-tag": "^2.12.6", "jwt-decode": "^3.1.2", - "moment": "^2.29.3", + "moment": "^2.30.1", "multer": "^1.4.4", "node-cron": "^3.0.1", "node-schedule": "^2.1.0", "object-resolve-path": "^1.1.1", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1", + "pg": "^8.11.3", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.2.0", "swagger-ui-express": "^4.3.0", - "templates.js": "^0.3.11" + "templates.js": "^0.3.11", + "typeorm": "^0.3.20" }, "devDependencies": { "@nestjs/cli": "^8.0.0", @@ -47,6 +54,7 @@ "@types/jest": "27.4.1", "@types/node": "^16.0.0", "@types/supertest": "^2.0.11", + "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", "eslint": "^8.0.1", @@ -834,7 +842,7 @@ "version": "0.8.0", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", - "dev": true, + "devOptional": true, "engines": { "node": ">= 12" } @@ -843,7 +851,7 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", - "dev": true, + "devOptional": true, "dependencies": { "@cspotcode/source-map-consumer": "0.8.0" }, @@ -891,6 +899,95 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -1353,21 +1450,29 @@ } }, "node_modules/@nestjs/config": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-2.0.0.tgz", - "integrity": "sha512-Hi1k/1S5ogsS5c0OtNm72thiLSngijOaLDFaGI5ZPxNGpF23lctPg6ox3pYIOhXVRX/u+eiUIp71gswH2k8YNw==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-2.3.4.tgz", + "integrity": "sha512-IGdSF+0F9MJO6dCRTEahdxPz4iVijjtolcFBxnY+2QYM3bXYQvAgzskGZi+WkAFJN/VzR3TEp60gN5sI74GxPA==", "dependencies": { - "dotenv": "16.0.0", - "dotenv-expand": "8.0.2", + "dotenv": "16.1.4", + "dotenv-expand": "10.0.0", "lodash": "4.17.21", - "uuid": "8.3.2" + "uuid": "9.0.0" }, "peerDependencies": { - "@nestjs/common": "^7.0.0 || ^8.0.0", + "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0", "reflect-metadata": "^0.1.13", "rxjs": "^6.0.0 || ^7.2.0" } }, + "node_modules/@nestjs/config/node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@nestjs/core": { "version": "8.4.1", "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-8.4.1.tgz", @@ -1437,6 +1542,15 @@ } } }, + "node_modules/@nestjs/passport": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-10.0.3.tgz", + "integrity": "sha512-znJ9Y4S8ZDVY+j4doWAJ8EuuVO7SkQN3yOBmzxbGaXbvcSwFDAdGJ+OMCg52NdzIO4tQoN4pYKx8W6M0ArfFRQ==", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "passport": "^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0" + } + }, "node_modules/@nestjs/platform-express": { "version": "8.4.1", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-8.4.1.tgz", @@ -1661,6 +1775,33 @@ } } }, + "node_modules/@nestjs/typeorm": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-10.0.2.tgz", + "integrity": "sha512-H738bJyydK4SQkRCTeh1aFBxoO1E9xdL/HaLGThwrqN95os5mEyAtK7BLADOS+vldP4jDZ2VQPLj4epWwRqCeQ==", + "dependencies": { + "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.13 || ^0.2.0", + "rxjs": "^7.2.0", + "typeorm": "^0.3.0" + } + }, + "node_modules/@nestjs/typeorm/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/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1713,6 +1854,15 @@ "npm": ">=5.0.0" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@sinonjs/commons": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", @@ -1731,6 +1881,11 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@sqltools/formatter": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", + "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==" + }, "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -1744,25 +1899,25 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node12": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node14": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node16": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", - "dev": true + "devOptional": true }, "node_modules/@types/babel__core": { "version": "7.1.18", @@ -2064,6 +2219,12 @@ "@types/superagent": "*" } }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true + }, "node_modules/@types/yargs": { "version": "16.0.4", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", @@ -2425,6 +2586,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", "dev": true }, "node_modules/accepts": { @@ -2443,7 +2605,7 @@ "version": "8.7.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true, + "devOptional": true, "bin": { "acorn": "bin/acorn" }, @@ -2504,7 +2666,7 @@ "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.4.0" } @@ -2592,7 +2754,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -2611,6 +2772,11 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + }, "node_modules/anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", @@ -2624,6 +2790,14 @@ "node": ">= 8" } }, + "node_modules/app-root-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", + "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/append-field": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", @@ -2633,7 +2807,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "devOptional": true }, "node_modules/argparse": { "version": "2.0.1", @@ -2780,7 +2954,6 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, "funding": [ { "type": "github", @@ -2977,6 +3150,14 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "engines": { + "node": ">=4" + } + }, "node_modules/busboy": { "version": "0.2.14", "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", @@ -3154,6 +3335,31 @@ "node": ">=8" } }, + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "dependencies": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/cli-highlight/node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" + }, "node_modules/cli-spinners": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", @@ -3194,7 +3400,6 @@ "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -3424,7 +3629,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "devOptional": true }, "node_modules/cron": { "version": "1.8.2", @@ -3450,7 +3655,6 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -3498,11 +3702,24 @@ "node": ">=10" } }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/dayjs": { + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + }, "node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { "ms": "2.1.2" }, @@ -3622,7 +3839,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.3.1" } @@ -3664,6 +3881,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "deprecated": "Use your platform's native DOMException instead", "dev": true, "dependencies": { "webidl-conversions": "^5.0.0" @@ -3682,21 +3900,29 @@ } }, "node_modules/dotenv": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz", - "integrity": "sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==", + "version": "16.1.4", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.1.4.tgz", + "integrity": "sha512-m55RtE8AsPeJBpOIFKihEmqUcoVncQIwo7x9U8ZwLEZw9ZpXboz2c+rvog+jUaJvVrZ5kBOeYQBX5+8Aa/OZQw==", "engines": { "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, "node_modules/dotenv-expand": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-8.0.2.tgz", - "integrity": "sha512-vKKAk+VOzAWOV/dPIeSYqhgC/TQY+6L6Ibkzfsr8xd1stdBsTuGu9asCOXgbYbBeS+f2Y6lqqEuw7riOA+xEUQ==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz", + "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==", "engines": { "node": ">=12" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -3731,8 +3957,7 @@ "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/encodeurl": { "version": "1.0.2", @@ -3783,7 +4008,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, "engines": { "node": ">=6" } @@ -3803,15 +4027,14 @@ } }, "node_modules/escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "dev": true, "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" + "esutils": "^2.0.2" }, "bin": { "escodegen": "bin/escodegen.js", @@ -3824,36 +4047,6 @@ "source-map": "~0.6.1" } }, - "node_modules/escodegen/node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/eslint": { "version": "8.11.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.11.0.tgz", @@ -4404,9 +4597,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -4422,6 +4615,32 @@ } } }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/fork-ts-checker-webpack-plugin": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-7.2.1.tgz", @@ -4638,7 +4857,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -4756,9 +4974,9 @@ "dev": true }, "node_modules/graphql": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.5.0.tgz", - "integrity": "sha512-qbHgh8Ix+j/qY+a/ZcJnFQ+j8ezakqPiHwPiZhV/3PgGlgf96QMBB5/f2rkiC9sgLoy/xvT6TSiaf2nTHJh5iA==", + "version": "16.8.1", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", + "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", "peer": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" @@ -4828,6 +5046,14 @@ "node": ">=8" } }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "engines": { + "node": "*" + } + }, "node_modules/html-encoding-sniffer": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", @@ -4912,7 +5138,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, "funding": [ { "type": "github", @@ -5106,7 +5331,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -5209,8 +5433,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", @@ -5295,6 +5518,23 @@ "node": ">=6" } }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jest": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", @@ -6396,7 +6636,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "devOptional": true }, "node_modules/makeerror": { "version": "1.0.12", @@ -6524,6 +6764,14 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mkdirp": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", @@ -6536,9 +6784,9 @@ } }, "node_modules/moment": { - "version": "2.29.3", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz", - "integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==", + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "engines": { "node": "*" } @@ -6563,6 +6811,7 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4.tgz", "integrity": "sha512-2wY2+xD4udX612aMqMcB8Ws2Voq6NIUPEtD1be6m411T4uDH/VtL9i//xvcyFlTVfRdaBsk7hV5tgrGQqhuBiw==", + "deprecated": "Multer 1.x is affected by CVE-2022-24434. This is fixed in v1.4.4-lts.1 which drops support for versions of Node.js before 6. Please upgrade to at least Node.js 6 and version 1.4.4-lts.1 of Multer. If you need support for older versions of Node.js, we are open to accepting patches that would fix the CVE on the main 1.x release line, whilst maintaining compatibility with Node.js 0.10.", "dependencies": { "append-field": "^1.0.0", "busboy": "^0.2.11", @@ -6594,6 +6843,16 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -6921,6 +7180,11 @@ "node": ">=6" } }, + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6954,8 +7218,15 @@ "node_modules/parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dependencies": { + "parse5": "^6.0.1" + } }, "node_modules/parseurl": { "version": "1.3.3", @@ -6965,6 +7236,61 @@ "node": ">= 0.8" } }, + "node_modules/passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-jwt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", + "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", + "dependencies": { + "jsonwebtoken": "^9.0.0", + "passport-strategy": "^1.0.0" + } + }, + "node_modules/passport-jwt/node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6986,7 +7312,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -6997,6 +7322,29 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "engines": { + "node": "14 || >=16.14" + } + }, "node_modules/path-to-regexp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz", @@ -7011,15 +7359,103 @@ "node": ">=8" } }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "node_modules/pg": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", + "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.6.2", + "pg-pool": "^3.6.1", + "pg-protocol": "^1.6.0", + "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.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" + }, + "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.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", + "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", + "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + }, + "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/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { @@ -7059,13 +7495,39 @@ "node": ">=4" } }, - "node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true, + "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": ">= 0.8.0" + "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/prettier": { @@ -7297,7 +7759,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -7490,10 +7951,9 @@ } }, "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -7584,11 +8044,22 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -7600,7 +8071,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } @@ -7685,8 +8155,17 @@ "version": "1.4.8", "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", "dev": true }, + "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", @@ -7752,7 +8231,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -7762,11 +8240,21 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -7774,6 +8262,15 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -8067,6 +8564,25 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/throat": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", @@ -8237,7 +8753,7 @@ "version": "10.7.0", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", - "dev": true, + "devOptional": true, "dependencies": { "@cspotcode/source-map-support": "0.7.0", "@tsconfig/node10": "^1.0.7", @@ -8337,18 +8853,6 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, - "node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -8396,64 +8900,320 @@ "is-typedarray": "^1.0.0" } }, - "node_modules/typescript": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz", - "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", - "dev": true, + "node_modules/typeorm": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.20.tgz", + "integrity": "sha512-sJ0T08dV5eoZroaq9uPKBoNcGslHBR4E4y+EBHs//SiGbblGe7IeduP/IH4ddCcj0qp3PHwDwGnuvqEAnKlq/Q==", + "dependencies": { + "@sqltools/formatter": "^1.2.5", + "app-root-path": "^3.1.0", + "buffer": "^6.0.3", + "chalk": "^4.1.2", + "cli-highlight": "^2.1.11", + "dayjs": "^1.11.9", + "debug": "^4.3.4", + "dotenv": "^16.0.3", + "glob": "^10.3.10", + "mkdirp": "^2.1.3", + "reflect-metadata": "^0.2.1", + "sha.js": "^2.4.11", + "tslib": "^2.5.0", + "uuid": "^9.0.0", + "yargs": "^17.6.2" + }, "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "typeorm": "cli.js", + "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js", + "typeorm-ts-node-esm": "cli-ts-node-esm.js" }, "engines": { - "node": ">=4.2.0" + "node": ">=16.13.0" + }, + "funding": { + "url": "https://opencollective.com/typeorm" + }, + "peerDependencies": { + "@google-cloud/spanner": "^5.18.0", + "@sap/hana-client": "^2.12.25", + "better-sqlite3": "^7.1.2 || ^8.0.0 || ^9.0.0", + "hdb-pool": "^0.1.6", + "ioredis": "^5.0.4", + "mongodb": "^5.8.0", + "mssql": "^9.1.1 || ^10.0.1", + "mysql2": "^2.2.5 || ^3.0.1", + "oracledb": "^6.3.0", + "pg": "^8.5.1", + "pg-native": "^3.0.0", + "pg-query-stream": "^4.0.0", + "redis": "^3.1.1 || ^4.0.0", + "sql.js": "^1.4.0", + "sqlite3": "^5.0.3", + "ts-node": "^10.7.0", + "typeorm-aurora-data-api-driver": "^2.0.0" + }, + "peerDependenciesMeta": { + "@google-cloud/spanner": { + "optional": true + }, + "@sap/hana-client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "hdb-pool": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "mongodb": { + "optional": true + }, + "mssql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "pg-query-stream": { + "optional": true + }, + "redis": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "ts-node": { + "optional": true + }, + "typeorm-aurora-data-api-driver": { + "optional": true + } } }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, + "node_modules/typeorm/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typeorm/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/typeorm/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=12" } }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "node_modules/typeorm/node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", "engines": { - "node": ">= 0.8" + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, + "node_modules/typeorm/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "dependencies": { - "punycode": "^2.1.0" + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "node_modules/typeorm/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { - "node": ">= 0.4.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "node_modules/typeorm/node_modules/mkdirp": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", + "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", "bin": { - "uuid": "dist/bin/uuid" + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typeorm/node_modules/reflect-metadata": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz", + "integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==" + }, + "node_modules/typeorm/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/typeorm/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/typeorm/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/typeorm/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/typescript": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz", + "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", + "devOptional": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" } }, "node_modules/v8-compile-cache": { @@ -8466,7 +9226,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", - "dev": true + "devOptional": true }, "node_modules/v8-to-istanbul": { "version": "8.1.1", @@ -8511,6 +9271,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", "dev": true, "dependencies": { "browser-process-hrtime": "^1.0.0" @@ -8666,7 +9427,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -8752,7 +9512,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -8765,6 +9524,17 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -8827,7 +9597,6 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, "engines": { "node": ">=10" } @@ -8850,7 +9619,6 @@ "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -8868,7 +9636,6 @@ "version": "20.2.9", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, "engines": { "node": ">=10" } @@ -8877,6763 +9644,10 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6" } } - }, - "dependencies": { - "@ampproject/remapping": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", - "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.0" - } - }, - "@angular-devkit/core": { - "version": "13.2.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.2.6.tgz", - "integrity": "sha512-8h2mWdBTN/dYwZuzKMg2IODlOWMdbJcpQG4XVrkk9ejCPP+3aX5Aa3glCe/voN6eBNiRfs8YDM0jxmpN2aWVtg==", - "dev": true, - "requires": { - "ajv": "8.9.0", - "ajv-formats": "2.1.1", - "fast-json-stable-stringify": "2.1.0", - "magic-string": "0.25.7", - "rxjs": "6.6.7", - "source-map": "0.7.3" - }, - "dependencies": { - "ajv": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", - "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "requires": { - "ajv": "^8.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz", - "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - } - } - }, - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "@angular-devkit/schematics": { - "version": "13.2.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-13.2.6.tgz", - "integrity": "sha512-mPgSqdnZRuPSMeUA+T+mwVCrq2yhXpcYm1/Rjbhy09CyHs4wSrFv21WHCrE6shlvXpcmwr0n+I0DIeagAPmjUA==", - "dev": true, - "requires": { - "@angular-devkit/core": "13.2.6", - "jsonc-parser": "3.0.0", - "magic-string": "0.25.7", - "ora": "5.4.1", - "rxjs": "6.6.7" - }, - "dependencies": { - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "@angular-devkit/schematics-cli": { - "version": "13.2.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-13.2.6.tgz", - "integrity": "sha512-VdMLn4DoTswjk+1RL+pod8EwLkzh8pMT2OBJ9dhsITru1sr0/2nhsqRwZzZylAXjrFwdfPj1E/vfcAfSkmMGvw==", - "dev": true, - "requires": { - "@angular-devkit/core": "13.2.6", - "@angular-devkit/schematics": "13.2.6", - "ansi-colors": "4.1.1", - "inquirer": "8.2.0", - "minimist": "1.2.5", - "symbol-observable": "4.0.0" - }, - "dependencies": { - "inquirer": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.0.tgz", - "integrity": "sha512-0crLweprevJ02tTuA6ThpoAERAGyVILC4sS74uib58Xf/zSr1/ZWtmm7D5CI+bSQEaA04f0K7idaHpQbSWgiVQ==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.2.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - } - } - } - }, - "@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.16.7" - } - }, - "@babel/compat-data": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", - "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", - "dev": true - }, - "@babel/core": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.7.tgz", - "integrity": "sha512-djHlEfFHnSnTAcPb7dATbiM5HxGOP98+3JLBZtjRb5I7RXrw7kFRoG2dXM8cm3H+o11A8IFH/uprmJpwFynRNQ==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.7", - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-module-transforms": "^7.17.7", - "@babel/helpers": "^7.17.7", - "@babel/parser": "^7.17.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", - "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", - "dev": true, - "requires": { - "@babel/types": "^7.17.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/helper-compilation-targets": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", - "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-validator-option": "^7.16.7", - "browserslist": "^4.17.5", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", - "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", - "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-module-imports": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", - "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-module-transforms": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", - "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.17.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", - "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", - "dev": true - }, - "@babel/helper-simple-access": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", - "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", - "dev": true, - "requires": { - "@babel/types": "^7.17.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", - "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", - "dev": true - }, - "@babel/helpers": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.7.tgz", - "integrity": "sha512-TKsj9NkjJfTBxM7Phfy7kv6yYc4ZcOo+AaWGqQOKTPDOmcGkIFb5xNA746eKisQkm4yavUYh4InYM9S+VnO01w==", - "dev": true, - "requires": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" - } - }, - "@babel/highlight": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", - "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.7.tgz", - "integrity": "sha512-bm3AQf45vR4gKggRfvJdYJ0gFLoCbsPxiFLSH6hTVYABptNHY6l9NrhnucVjQ/X+SPtLANT9lc0fFhikj+VBRA==", - "dev": true - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz", - "integrity": "sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/traverse": { - "version": "7.17.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", - "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.3", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.3", - "@babel/types": "^7.17.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "dependencies": { - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", - "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.16.7", - "to-fast-properties": "^2.0.0" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", - "dev": true - }, - "@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", - "dev": true, - "requires": { - "@cspotcode/source-map-consumer": "0.8.0" - } - }, - "@eslint/eslintrc": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", - "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.3.1", - "globals": "^13.9.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - } - }, - "@humanwhocodes/config-array": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", - "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - } - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jest/console": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", - "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - } - } - }, - "@jest/core": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", - "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", - "dev": true, - "requires": { - "@jest/console": "^27.5.1", - "@jest/reporters": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^27.5.1", - "jest-config": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-resolve-dependencies": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "jest-watcher": "^27.5.1", - "micromatch": "^4.0.4", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - } - } - }, - "@jest/environment": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", - "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - } - } - }, - "@jest/fake-timers": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", - "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@sinonjs/fake-timers": "^8.0.1", - "@types/node": "*", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - } - } - }, - "@jest/globals": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", - "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/types": "^27.5.1", - "expect": "^27.5.1" - } - }, - "@jest/reporters": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", - "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-haste-map": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^8.1.0" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - } - } - }, - "@jest/source-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", - "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9", - "source-map": "^0.6.0" - } - }, - "@jest/test-result": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", - "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", - "dev": true, - "requires": { - "@jest/console": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", - "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", - "dev": true, - "requires": { - "@jest/test-result": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-runtime": "^27.5.1" - } - }, - "@jest/transform": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", - "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^27.5.1", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-util": "^27.5.1", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - } - }, - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - } - } - }, - "@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@nestjs/axios": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-0.0.7.tgz", - "integrity": "sha512-at8nj+1Nb8UleHcIN5QqZYeWX54m4m9s9gxzVE1qWy00neX2rg0+h2TfbWsnDi2tc23zIxqexanxMOJZbzO0CA==", - "requires": { - "axios": "0.26.0" - }, - "dependencies": { - "axios": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.0.tgz", - "integrity": "sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og==", - "requires": { - "follow-redirects": "^1.14.8" - } - } - } - }, - "@nestjs/cli": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-8.2.3.tgz", - "integrity": "sha512-p//DACefn40VClXroxzS+2pkSdaXYvKKcr4bFOUBMTdJqT9he+U9ifcJUkg/h1bFU/Y8SS2cUsTyl0ZBHHHRcw==", - "dev": true, - "requires": { - "@angular-devkit/core": "13.2.6", - "@angular-devkit/schematics": "13.2.6", - "@angular-devkit/schematics-cli": "13.2.6", - "@nestjs/schematics": "^8.0.3", - "chalk": "3.0.0", - "chokidar": "3.5.3", - "cli-table3": "0.6.1", - "commander": "4.1.1", - "fork-ts-checker-webpack-plugin": "7.2.1", - "inquirer": "7.3.3", - "node-emoji": "1.11.0", - "ora": "5.4.1", - "os-name": "4.0.1", - "rimraf": "3.0.2", - "shelljs": "0.8.5", - "source-map-support": "0.5.21", - "tree-kill": "1.2.2", - "tsconfig-paths": "3.14.0", - "tsconfig-paths-webpack-plugin": "3.5.2", - "typescript": "4.6.2", - "webpack": "5.70.0", - "webpack-node-externals": "3.0.0" - }, - "dependencies": { - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } - } - }, - "@nestjs/common": { - "version": "8.4.3", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-8.4.3.tgz", - "integrity": "sha512-QIhWykB7IPOwHQB/K9wMwmQKibQ5dhg9dt8ySOoD36uFFwN3RJQelzMFF9Rtu7hrMPk6pSyismEUKQ8BZMUD9w==", - "requires": { - "axios": "0.26.1", - "iterare": "1.2.1", - "tslib": "2.3.1", - "uuid": "8.3.2" - } - }, - "@nestjs/config": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-2.0.0.tgz", - "integrity": "sha512-Hi1k/1S5ogsS5c0OtNm72thiLSngijOaLDFaGI5ZPxNGpF23lctPg6ox3pYIOhXVRX/u+eiUIp71gswH2k8YNw==", - "requires": { - "dotenv": "16.0.0", - "dotenv-expand": "8.0.2", - "lodash": "4.17.21", - "uuid": "8.3.2" - } - }, - "@nestjs/core": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-8.4.1.tgz", - "integrity": "sha512-JUV2cy/5z8MS2SRwszLmyOCclMMlyumxIbC1YFFlnSInhu7ODhrHLIMztyGmyAIuaehbOnyXPtHkjl01rHxc5w==", - "requires": { - "@nuxtjs/opencollective": "0.3.2", - "fast-safe-stringify": "2.1.1", - "iterare": "1.2.1", - "object-hash": "3.0.0", - "path-to-regexp": "3.2.0", - "tslib": "2.3.1", - "uuid": "8.3.2" - } - }, - "@nestjs/jwt": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-8.0.0.tgz", - "integrity": "sha512-fz2LQgYY2zmuD8S+8UE215anwKyXlnB/1FwJQLVR47clNfMeFMK8WCxmn6xdPhF5JKuV1crO6FVabb1qWzDxqQ==", - "requires": { - "@types/jsonwebtoken": "8.5.4", - "jsonwebtoken": "8.5.1" - } - }, - "@nestjs/mapped-types": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-1.0.1.tgz", - "integrity": "sha512-NFvofzSinp00j5rzUd4tf+xi9od6383iY0JP7o0Bnu1fuItAUkWBgc4EKuIQ3D+c2QI3i9pG1kDWAeY27EMGtg==", - "requires": {} - }, - "@nestjs/platform-express": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-8.4.1.tgz", - "integrity": "sha512-c18zuhoegJsS1vZueGaNyvU8mLyFtS+6FJ2WAzDOXNpchpq0okrEPdKBifIisWuxgffQYKIufoK73vEfXSfDLg==", - "requires": { - "body-parser": "1.19.2", - "cors": "2.8.5", - "express": "4.17.3", - "multer": "1.4.4", - "tslib": "2.3.1" - } - }, - "@nestjs/schedule": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-1.1.0.tgz", - "integrity": "sha512-0QpbwClUildXqlyoaygG+aIQZNNMv31XDyQxX+Ob1zw/3I8+AVrDlBwZHQ+tlhIcJFR8aG+VTH8xwIjXwtS1UA==", - "requires": { - "cron": "1.8.2", - "uuid": "8.3.2" - } - }, - "@nestjs/schematics": { - "version": "8.0.8", - "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-8.0.8.tgz", - "integrity": "sha512-xIIb5YnMQN/OJQ68+MCapy2bXvTxSWgINoqQbyZWkLL/yTIuROvZCdtV850NPGyr7f7l93VBP0ZPitbFIexy3Q==", - "dev": true, - "requires": { - "@angular-devkit/core": "13.2.5", - "@angular-devkit/schematics": "13.2.5", - "fs-extra": "10.0.1", - "jsonc-parser": "3.0.0", - "pluralize": "8.0.0" - }, - "dependencies": { - "@angular-devkit/core": { - "version": "13.2.5", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.2.5.tgz", - "integrity": "sha512-WuWp/1R0FtCHPBcJLF13lTLHETtDGFUX0ULfGPRaYB5OVCSQcovVp5UbZTTy/Ss3ub3EOEmJlU8kMJfBrWuq+A==", - "dev": true, - "requires": { - "ajv": "8.9.0", - "ajv-formats": "2.1.1", - "fast-json-stable-stringify": "2.1.0", - "magic-string": "0.25.7", - "rxjs": "6.6.7", - "source-map": "0.7.3" - } - }, - "@angular-devkit/schematics": { - "version": "13.2.5", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-13.2.5.tgz", - "integrity": "sha512-kAye6VYiF9JQAoeO+BYhy8eT2QOmhB+WLziRjXoFCBxh5+yXTygTVfs9fD5jmIpHmeu4hd2ErSh69yT5xWcD9g==", - "dev": true, - "requires": { - "@angular-devkit/core": "13.2.5", - "jsonc-parser": "3.0.0", - "magic-string": "0.25.7", - "ora": "5.4.1", - "rxjs": "6.6.7" - } - }, - "ajv": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", - "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "requires": { - "ajv": "^8.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz", - "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - } - } - }, - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "@nestjs/swagger": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-5.2.1.tgz", - "integrity": "sha512-7dNa08WCnTsW/oAk3Ujde+z64JMfNm19DhpXasFR8oJp/9pggYAbYU927HpA+GJsSFJX6adjIRZsCKUqaGWznw==", - "requires": { - "@nestjs/mapped-types": "1.0.1", - "lodash": "4.17.21", - "path-to-regexp": "3.2.0" - } - }, - "@nestjs/testing": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-8.4.1.tgz", - "integrity": "sha512-LOvsLuNh2eRwAtyeoIZWmUZ08wt7QrPrKQujWuRyv+vBYtC+FLHjqreWLpwG2yulNEoQs9Qlr2ubPvFGT1953g==", - "dev": true, - "requires": { - "optional": "0.1.4", - "tslib": "2.3.1" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@nuxtjs/opencollective": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz", - "integrity": "sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==", - "requires": { - "chalk": "^4.1.0", - "consola": "^2.15.0", - "node-fetch": "^2.6.1" - } - }, - "@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true - }, - "@tsconfig/node10": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", - "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", - "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", - "dev": true - }, - "@tsconfig/node14": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", - "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", - "dev": true - }, - "@tsconfig/node16": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", - "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", - "dev": true - }, - "@types/babel__core": { - "version": "7.1.18", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.18.tgz", - "integrity": "sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", - "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" - } - }, - "@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dev": true, - "requires": { - "@types/connect": "*", - "@types/node": "*" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - } - } - }, - "@types/cache-manager": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/@types/cache-manager/-/cache-manager-3.4.3.tgz", - "integrity": "sha512-71aBXoFYXZW4TnDHHH8gExw2lS28BZaWeKefgsiJI7QYZeJfUEbMKw6CQtzGjlYQcGIWwB76hcCrkVA3YHSvsw==", - "dev": true - }, - "@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dev": true, - "requires": { - "@types/node": "*" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - } - } - }, - "@types/cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", - "dev": true - }, - "@types/cron": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@types/cron/-/cron-1.7.3.tgz", - "integrity": "sha512-iPmUXyIJG1Js+ldPYhOQcYU3kCAQ2FWrSkm1FJPoii2eYSn6wEW6onPukNTT0bfiflexNSRPl6KWmAIqS+36YA==", - "dev": true, - "requires": { - "@types/node": "*", - "moment": ">=2.14.0" - } - }, - "@types/eslint": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", - "integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==", - "dev": true, - "requires": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "@types/eslint-scope": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", - "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", - "dev": true, - "requires": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", - "dev": true - }, - "@types/express": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", - "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", - "dev": true, - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.18", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.17.28", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", - "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", - "dev": true, - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - } - } - }, - "@types/graceful-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", - "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", - "dev": true, - "requires": { - "@types/node": "*" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - } - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "27.4.1", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.4.1.tgz", - "integrity": "sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw==", - "dev": true, - "requires": { - "jest-matcher-utils": "^27.0.0", - "pretty-format": "^27.0.0" - } - }, - "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", - "dev": true - }, - "@types/jsonwebtoken": { - "version": "8.5.4", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.4.tgz", - "integrity": "sha512-4L8msWK31oXwdtC81RmRBAULd0ShnAHjBuKT9MRQpjP0piNrZdXyTRcKY9/UIfhGeKIT4PvF5amOOUbbT/9Wpg==", - "requires": { - "@types/node": "*" - } - }, - "@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", - "dev": true - }, - "@types/node": { - "version": "16.11.26", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz", - "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==" - }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "@types/prettier": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.4.tgz", - "integrity": "sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA==", - "dev": true - }, - "@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", - "dev": true - }, - "@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", - "dev": true - }, - "@types/serve-static": { - "version": "1.13.10", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", - "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", - "dev": true, - "requires": { - "@types/mime": "^1", - "@types/node": "*" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - } - } - }, - "@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "@types/superagent": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.15.tgz", - "integrity": "sha512-mu/N4uvfDN2zVQQ5AYJI/g4qxn2bHB6521t1UuH09ShNWjebTqN0ZFuYK9uYjcgmI0dTQEs+Owi1EO6U0OkOZQ==", - "dev": true, - "requires": { - "@types/cookiejar": "*", - "@types/node": "*" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - } - } - }, - "@types/supertest": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.11.tgz", - "integrity": "sha512-uci4Esokrw9qGb9bvhhSVEjd6rkny/dk5PK/Qz4yxKiyppEI+dOPlNrZBahE3i+PoKFYyDxChVXZ/ysS/nrm1Q==", - "dev": true, - "requires": { - "@types/superagent": "*" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.15.0.tgz", - "integrity": "sha512-u6Db5JfF0Esn3tiAKELvoU5TpXVSkOpZ78cEGn/wXtT2RVqs2vkt4ge6N8cRCyw7YVKhmmLDbwI2pg92mlv7cA==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.15.0", - "@typescript-eslint/type-utils": "5.15.0", - "@typescript-eslint/utils": "5.15.0", - "debug": "^4.3.2", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.2.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/parser": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.15.0.tgz", - "integrity": "sha512-NGAYP/+RDM2sVfmKiKOCgJYPstAO40vPAgACoWPO/+yoYKSgAXIFaBKsV8P0Cc7fwKgvj27SjRNX4L7f4/jCKQ==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.15.0", - "@typescript-eslint/types": "5.15.0", - "@typescript-eslint/typescript-estree": "5.15.0", - "debug": "^4.3.2" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.15.0.tgz", - "integrity": "sha512-EFiZcSKrHh4kWk0pZaa+YNJosvKE50EnmN4IfgjkA3bTHElPtYcd2U37QQkNTqwMCS7LXeDeZzEqnsOH8chjSg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.15.0", - "@typescript-eslint/visitor-keys": "5.15.0" - } - }, - "@typescript-eslint/type-utils": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.15.0.tgz", - "integrity": "sha512-KGeDoEQ7gHieLydujGEFLyLofipe9PIzfvA/41urz4hv+xVxPEbmMQonKSynZ0Ks2xDhJQ4VYjB3DnRiywvKDA==", - "dev": true, - "requires": { - "@typescript-eslint/utils": "5.15.0", - "debug": "^4.3.2", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/types": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.15.0.tgz", - "integrity": "sha512-yEiTN4MDy23vvsIksrShjNwQl2vl6kJeG9YkVJXjXZnkJElzVK8nfPsWKYxcsGWG8GhurYXP4/KGj3aZAxbeOA==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.15.0.tgz", - "integrity": "sha512-Hb0e3dGc35b75xLzixM3cSbG1sSbrTBQDfIScqdyvrfJZVEi4XWAT+UL/HMxEdrJNB8Yk28SKxPLtAhfCbBInA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.15.0", - "@typescript-eslint/visitor-keys": "5.15.0", - "debug": "^4.3.2", - "globby": "^11.0.4", - "is-glob": "^4.0.3", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/utils": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.15.0.tgz", - "integrity": "sha512-081rWu2IPKOgTOhHUk/QfxuFog8m4wxW43sXNOMSCdh578tGJ1PAaWPsj42LOa7pguh173tNlMigsbrHvh/mtA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.15.0", - "@typescript-eslint/types": "5.15.0", - "@typescript-eslint/typescript-estree": "5.15.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.15.0.tgz", - "integrity": "sha512-+vX5FKtgvyHbmIJdxMJ2jKm9z2BIlXJiuewI8dsDYMp5LzPUcuTT78Ya5iwvQg3VqSVdmxyM8Anj1Jeq7733ZQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.15.0", - "eslint-visitor-keys": "^3.0.0" - } - }, - "@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", - "dev": true, - "requires": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true - }, - "@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", - "dev": true, - "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", - "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", - "dev": true, - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", - "dev": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "abab": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", - "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", - "dev": true - }, - "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - } - }, - "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true - }, - "acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dev": true, - "requires": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - }, - "dependencies": { - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, - "acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "dev": true - } - } - }, - "acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true, - "requires": {} - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true - }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "requires": { - "debug": "4" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "dependencies": { - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - } - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - } - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=" - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", - "dev": true - }, - "async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "axios": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", - "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", - "requires": { - "follow-redirects": "^1.14.8" - } - }, - "babel-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", - "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", - "dev": true, - "requires": { - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", - "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", - "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^27.5.1", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - } - } - }, - "body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.7", - "raw-body": "2.4.3", - "type-is": "~1.6.18" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true - }, - "browserslist": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.0.tgz", - "integrity": "sha512-bnpOoa+DownbciXj0jVGENf8VYQnE2LNWomhYuCsMmmx9Jd9lwq0WXODuwpSsp8AVdKM2/HorrzxAfbKvWTByQ==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001313", - "electron-to-chromium": "^1.4.76", - "escalade": "^3.1.1", - "node-releases": "^2.0.2", - "picocolors": "^1.0.0" - } - }, - "bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "requires": { - "fast-json-stable-stringify": "2.x" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" - }, - "busboy": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", - "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", - "requires": { - "dicer": "0.2.5", - "readable-stream": "1.1.x" - } - }, - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" - }, - "cache-manager": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/cache-manager/-/cache-manager-3.6.1.tgz", - "integrity": "sha512-jxJvGYhN5dUgpriAdsDnnYbKse4dEXI5i3XpwTfPq5utPtXH1uYXWyGLHGlbSlh9Vq4ytrgAUVwY+IodNeKigA==", - "requires": { - "async": "3.2.3", - "lodash": "^4.17.21", - "lru-cache": "6.0.0" - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001317", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001317.tgz", - "integrity": "sha512-xIZLh8gBm4dqNX0gkzrBeyI86J2eCjWzYAs40q88smG844YIrN4tVQl/RhquHvKEKImWWFIVh1Lxe5n1G/N+GQ==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true - }, - "ci-info": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", - "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", - "dev": true - }, - "cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", - "dev": true - }, - "class-transformer": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", - "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" - }, - "class-validator": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.13.2.tgz", - "integrity": "sha512-yBUcQy07FPlGzUjoLuUfIOXzgynnQPPruyK1Ge2B74k9ROwnle1E+NxLWnUv5OLU8hA/qL5leAE9XnXq3byaBw==", - "requires": { - "libphonenumber-js": "^1.9.43", - "validator": "^13.7.0" - } - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", - "dev": true - }, - "cli-table3": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.1.tgz", - "integrity": "sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==", - "dev": true, - "requires": { - "colors": "1.4.0", - "string-width": "^4.2.0" - } - }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "optional": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "consola": { - "version": "2.15.3", - "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", - "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" - }, - "content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "requires": { - "safe-buffer": "5.2.1" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } - } - }, - "cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "cookiejar": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", - "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==", - "dev": true - }, - "core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" - }, - "cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "requires": { - "object-assign": "^4", - "vary": "^1" - } - }, - "cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", - "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - } - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "cron": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/cron/-/cron-1.8.2.tgz", - "integrity": "sha512-Gk2c4y6xKEO8FSAUTklqtfSr7oTq0CiPQeLBG5Fl0qoXpZyMcj1SG59YL+hqq04bu6/IuEA7lMkYDAplQNKkyg==", - "requires": { - "moment-timezone": "^0.5.x" - } - }, - "cron-parser": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-3.5.0.tgz", - "integrity": "sha512-wyVZtbRs6qDfFd8ap457w3XVntdvqcwBGxBoTvJQH9KGVKL/fB+h2k3C8AqiVxvUQKN1Ps/Ns46CNViOpVDhfQ==", - "requires": { - "is-nan": "^1.3.2", - "luxon": "^1.26.0" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true - }, - "cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, - "requires": { - "cssom": "~0.3.6" - }, - "dependencies": { - "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - } - } - }, - "data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "dev": true, - "requires": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - } - }, - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", - "dev": true - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", - "dev": true - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true - }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", - "dev": true, - "requires": { - "clone": "^1.0.2" - } - }, - "define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "requires": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "dezalgo": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", - "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", - "dev": true, - "requires": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "dicer": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", - "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", - "requires": { - "readable-stream": "1.1.x", - "streamsearch": "0.1.2" - } - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dev": true, - "requires": { - "webidl-conversions": "^5.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true - } - } - }, - "dotenv": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz", - "integrity": "sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==" - }, - "dotenv-expand": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-8.0.2.tgz", - "integrity": "sha512-vKKAk+VOzAWOV/dPIeSYqhgC/TQY+6L6Ibkzfsr8xd1stdBsTuGu9asCOXgbYbBeS+f2Y6lqqEuw7riOA+xEUQ==" - }, - "ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "electron-to-chromium": { - "version": "1.4.84", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.84.tgz", - "integrity": "sha512-b+DdcyOiZtLXHdgEG8lncYJdxbdJWJvclPNMg0eLUDcSOSO876WA/pYjdSblUTd7eJdIs4YdIxHWGazx7UPSJw==", - "dev": true - }, - "emittery": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", - "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "enhanced-resolve": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz", - "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", - "dev": true, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - } - } - }, - "eslint": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.11.0.tgz", - "integrity": "sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==", - "dev": true, - "requires": { - "@eslint/eslintrc": "^1.2.1", - "@humanwhocodes/config-array": "^0.9.2", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.6.0", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - } - } - }, - "eslint-config-prettier": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", - "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", - "dev": true, - "requires": {} - }, - "eslint-plugin-prettier": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz", - "integrity": "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==", - "dev": true, - "requires": { - "prettier-linter-helpers": "^1.0.0" - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "dependencies": { - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - } - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true - }, - "espree": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", - "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", - "dev": true, - "requires": { - "acorn": "^8.7.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.3.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - } - }, - "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, - "requires": { - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - } - }, - "express": { - "version": "4.17.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz", - "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==", - "requires": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.19.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.4.2", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.9.7", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.17.2", - "serve-static": "1.14.2", - "setprototypeof": "1.2.0", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - } - } - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", - "dev": true - }, - "fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" - }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "fb-watchman": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", - "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", - "dev": true - }, - "follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" - }, - "fork-ts-checker-webpack-plugin": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-7.2.1.tgz", - "integrity": "sha512-uOfQdg/iQ8iokQ64qcbu8iZb114rOmaKLQFu7hU14/eJaKgsP91cQ7ts7v2iiDld6TzDe84Meksha8/MkWiCyw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "chalk": "^4.1.2", - "chokidar": "^3.5.3", - "cosmiconfig": "^7.0.1", - "deepmerge": "^4.2.2", - "fs-extra": "^10.0.0", - "memfs": "^3.4.1", - "minimatch": "^3.0.4", - "schema-utils": "4.0.0", - "semver": "^7.3.5", - "tapable": "^2.2.1" - }, - "dependencies": { - "ajv": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz", - "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "requires": { - "ajv": "^8.0.0" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - } - } - }, - "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==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "formidable": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.0.1.tgz", - "integrity": "sha512-rjTMNbp2BpfQShhFbR3Ruk3qk2y9jKpvMW78nJgx8QKtxjDVrwbZG+wvDOmVbifHyOUOQJXxqEy6r0faRrPzTQ==", - "dev": true, - "requires": { - "dezalgo": "1.0.3", - "hexoid": "1.0.0", - "once": "1.4.0", - "qs": "6.9.3" - }, - "dependencies": { - "qs": { - "version": "6.9.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.3.tgz", - "integrity": "sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==", - "dev": true - } - } - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "fs-extra": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", - "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, - "globals": { - "version": "13.12.1", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", - "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", - "dev": true - }, - "graphql": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.5.0.tgz", - "integrity": "sha512-qbHgh8Ix+j/qY+a/ZcJnFQ+j8ezakqPiHwPiZhV/3PgGlgf96QMBB5/f2rkiC9sgLoy/xvT6TSiaf2nTHJh5iA==", - "peer": true - }, - "graphql-tag": { - "version": "2.12.6", - "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", - "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", - "requires": { - "tslib": "^2.1.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "requires": { - "get-intrinsic": "^1.1.1" - } - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - }, - "hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "dev": true - }, - "html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "dev": true, - "requires": { - "whatwg-encoding": "^1.0.5" - } - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - } - }, - "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - } - }, - "https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dev": true, - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true - }, - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - } - } - }, - "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.19", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true - }, - "is-nan": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", - "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", - "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - } - }, - "istanbul-reports": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", - "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "iterare": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", - "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==" - }, - "jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", - "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", - "dev": true, - "requires": { - "@jest/core": "^27.5.1", - "import-local": "^3.0.2", - "jest-cli": "^27.5.1" - } - }, - "jest-changed-files": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", - "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "execa": "^5.0.0", - "throat": "^6.0.1" - } - }, - "jest-circus": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", - "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3", - "throat": "^6.0.1" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - } - } - }, - "jest-cli": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", - "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", - "dev": true, - "requires": { - "@jest/core": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "prompts": "^2.0.1", - "yargs": "^16.2.0" - } - }, - "jest-config": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", - "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", - "dev": true, - "requires": { - "@babel/core": "^7.8.0", - "@jest/test-sequencer": "^27.5.1", - "@jest/types": "^27.5.1", - "babel-jest": "^27.5.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.9", - "jest-circus": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-jasmine2": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - } - }, - "jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-docblock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", - "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", - "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-environment-jsdom": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", - "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1", - "jsdom": "^16.6.0" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - } - } - }, - "jest-environment-node": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", - "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - } - } - }, - "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "dev": true - }, - "jest-haste-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", - "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^27.5.1", - "jest-serializer": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "micromatch": "^4.0.4", - "walker": "^1.0.7" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - } - } - }, - "jest-jasmine2": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", - "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "throat": "^6.0.1" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - } - } - }, - "jest-leak-detector": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", - "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", - "dev": true, - "requires": { - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-mock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", - "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - } - } - }, - "jest-pnp-resolver": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", - "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "dev": true, - "requires": {} - }, - "jest-regex-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", - "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", - "dev": true - }, - "jest-resolve": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", - "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - } - }, - "jest-resolve-dependencies": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", - "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-snapshot": "^27.5.1" - } - }, - "jest-runner": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", - "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", - "dev": true, - "requires": { - "@jest/console": "^27.5.1", - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-leak-detector": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "source-map-support": "^0.5.6", - "throat": "^6.0.1" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - } - } - }, - "jest-runtime": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", - "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/globals": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "dependencies": { - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - } - } - }, - "jest-serializer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", - "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", - "dev": true, - "requires": { - "@types/node": "*", - "graceful-fs": "^4.2.9" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - } - } - }, - "jest-snapshot": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", - "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", - "dev": true, - "requires": { - "@babel/core": "^7.7.2", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.0.0", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^27.5.1", - "semver": "^7.3.2" - } - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - } - } - }, - "jest-validate": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", - "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "leven": "^3.1.0", - "pretty-format": "^27.5.1" - } - }, - "jest-watcher": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", - "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", - "dev": true, - "requires": { - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^27.5.1", - "string-length": "^4.0.1" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - } - } - }, - "jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "dev": true, - "requires": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - }, - "dependencies": { - "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - } - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "jsonc-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", - "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", - "dev": true - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "jsonwebtoken": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", - "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", - "requires": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^5.6.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } - } - }, - "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "jwt-decode": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", - "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==" - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "dependencies": { - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - } - } - }, - "libphonenumber-js": { - "version": "1.9.50", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.9.50.tgz", - "integrity": "sha512-cCzQPChw2XbordcO2LKiw5Htx5leHVfFk/EXkxNHqJfFo7Fndcb1kF5wPJpc316vCJhhikedYnVysMh3Sc7Ocw==" - }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", - "dev": true - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" - }, - "lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" - }, - "lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" - }, - "lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - } - }, - "long-timeout": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz", - "integrity": "sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==" - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "luxon": { - "version": "1.28.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz", - "integrity": "sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==" - }, - "macos-release": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", - "integrity": "sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g==", - "dev": true - }, - "magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.4" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "requires": { - "tmpl": "1.0.5" - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "memfs": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", - "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", - "dev": true, - "requires": { - "fs-monkey": "1.0.3" - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, - "moment": { - "version": "2.29.3", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz", - "integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==" - }, - "moment-timezone": { - "version": "0.5.34", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.34.tgz", - "integrity": "sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg==", - "requires": { - "moment": ">= 2.9.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "multer": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4.tgz", - "integrity": "sha512-2wY2+xD4udX612aMqMcB8Ws2Voq6NIUPEtD1be6m411T4uDH/VtL9i//xvcyFlTVfRdaBsk7hV5tgrGQqhuBiw==", - "requires": { - "append-field": "^1.0.0", - "busboy": "^0.2.11", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.4", - "object-assign": "^4.1.1", - "on-finished": "^2.3.0", - "type-is": "^1.6.4", - "xtend": "^4.0.0" - }, - "dependencies": { - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "requires": { - "ee-first": "1.1.1" - } - } - } - }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node-cron": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.1.tgz", - "integrity": "sha512-RAWZTNn2M5KDIUV/389UX0EXsqvdFAwc9QwHQceh0Ga56dygqSRthqIjwpgZsoDspHGt2rkHdk9Z4RgfPMdALw==" - }, - "node-emoji": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", - "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", - "dev": true, - "requires": { - "lodash": "^4.17.21" - } - }, - "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "requires": { - "whatwg-url": "^5.0.0" - }, - "dependencies": { - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - } - } - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", - "dev": true - }, - "node-releases": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", - "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", - "dev": true - }, - "node-schedule": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.1.0.tgz", - "integrity": "sha512-nl4JTiZ7ZQDc97MmpTq9BQjYhq7gOtoh7SiPH069gBFBj0PzD8HI7zyFs6rzqL8Y5tTiEEYLxgtbx034YPrbyQ==", - "requires": { - "cron-parser": "^3.5.0", - "long-timeout": "0.1.1", - "sorted-array-functions": "^1.3.0" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "nwsapi": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", - "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" - }, - "object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - }, - "object-resolve-path": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-resolve-path/-/object-resolve-path-1.1.1.tgz", - "integrity": "sha1-p/j5Poogr4DkQhe6fbVDFtnRIjI=" - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "optional": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/optional/-/optional-0.1.4.tgz", - "integrity": "sha512-gtvrrCfkE08wKcgXaVwQVgwEQ8vel2dc5DDBn9RLQZ3YtmtkBss6A2HY6BnJH4N/4Ku97Ri/SF8sNWE2225WJw==", - "dev": true - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "dependencies": { - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - } - } - }, - "ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "requires": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - } - }, - "os-name": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", - "integrity": "sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==", - "dev": true, - "requires": { - "macos-release": "^2.5.0", - "windows-release": "^4.0.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-to-regexp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz", - "integrity": "sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==" - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "prettier": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", - "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", - "dev": true - }, - "prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "requires": { - "fast-diff": "^1.1.2" - } - }, - "pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - } - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==" - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", - "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", - "requires": { - "bytes": "3.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, - "requires": { - "resolve": "^1.1.6" - } - }, - "reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", - "dev": true, - "requires": { - "is-core-module": "^2.8.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "resolve.exports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", - "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", - "dev": true - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "rxjs": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", - "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", - "requires": { - "tslib": "^2.1.0" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dev": true, - "requires": { - "xmlchars": "^2.2.0" - } - }, - "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "send": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", - "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "1.8.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "serve-static": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", - "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.2" - } - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "dev": true, - "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - } - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "sorted-array-functions": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz", - "integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "stack-utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", - "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "streamsearch": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", - "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, - "string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "superagent": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-7.1.1.tgz", - "integrity": "sha512-CQ2weSS6M+doIwwYFoMatklhRbx6sVNdB99OEJ5czcP3cng76Ljqus694knFWgOj3RkrtxZqIgpe6vhe0J7QWQ==", - "dev": true, - "requires": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.3", - "debug": "^4.3.3", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^2.0.1", - "methods": "^1.1.2", - "mime": "^2.5.0", - "qs": "^6.10.1", - "readable-stream": "^3.6.0", - "semver": "^7.3.5" - }, - "dependencies": { - "qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "dev": true, - "requires": { - "side-channel": "^1.0.4" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - } - } - }, - "supertest": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.2.2.tgz", - "integrity": "sha512-wCw9WhAtKJsBvh07RaS+/By91NNE0Wh0DN19/hWPlBOU8tAfOtbZoVSV4xXeoKoxgPx0rx2y+y+8660XtE7jzg==", - "dev": true, - "requires": { - "methods": "^1.1.2", - "superagent": "^7.1.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", - "dev": true, - "requires": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "swagger-ui-dist": { - "version": "4.10.3", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.10.3.tgz", - "integrity": "sha512-eR4vsd7sYo0Sx7ZKRP5Z04yij7JkNmIlUQfrDQgC+xO5ABYx+waabzN+nDsQTLAJ4Z04bjkRd8xqkJtbxr3G7w==" - }, - "swagger-ui-express": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.3.0.tgz", - "integrity": "sha512-jN46SEEe9EoXa3ZgZoKgnSF6z0w3tnM1yqhO4Y+Q4iZVc8JOQB960EZpIAz6rNROrDApVDwcMHR0mhlnc/5Omw==", - "requires": { - "swagger-ui-dist": ">=4.1.3" - } - }, - "symbol-observable": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", - "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", - "dev": true - }, - "symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true - }, - "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true - }, - "templates.js": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/templates.js/-/templates.js-0.3.11.tgz", - "integrity": "sha1-qAtXgaoySmHpK+GmlHDVATQd6QQ=" - }, - "terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - } - }, - "terser": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz", - "integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==", - "dev": true, - "requires": { - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map": "~0.7.2", - "source-map-support": "~0.5.20" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - } - } - }, - "terser-webpack-plugin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz", - "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==", - "dev": true, - "requires": { - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.2" - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "throat": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", - "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" - }, - "tough-cookie": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", - "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", - "dev": true, - "requires": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.1.2" - }, - "dependencies": { - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - } - } - }, - "tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dev": true, - "requires": { - "punycode": "^2.1.1" - } - }, - "tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true - }, - "ts-jest": { - "version": "27.1.3", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.3.tgz", - "integrity": "sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA==", - "dev": true, - "requires": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^27.0.0", - "json5": "2.x", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "7.x", - "yargs-parser": "20.x" - } - }, - "ts-loader": { - "version": "9.2.8", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.2.8.tgz", - "integrity": "sha512-gxSak7IHUuRtwKf3FIPSW1VpZcqF9+MBrHOvBp9cjHh+525SjtCIJKVGjRKIAfxBwDGDGCFF00rTfzB1quxdSw==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.0.0", - "micromatch": "^4.0.0", - "semver": "^7.3.4" - } - }, - "ts-node": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", - "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "0.7.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.0", - "yn": "3.1.1" - } - }, - "tsconfig-paths": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.0.tgz", - "integrity": "sha512-cg/1jAZoL57R39+wiw4u/SCC6Ic9Q5NqjBOb+9xISedOYurfog9ZNmKJSxAnb2m/5Bq4lE9lhUcau33Ml8DM0g==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.0", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - } - } - }, - "tsconfig-paths-webpack-plugin": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-3.5.2.tgz", - "integrity": "sha512-EhnfjHbzm5IYI9YPNVIxx1moxMI4bpHD2e0zTXeDNQcwjjRaGepP7IhTHJkyDBG0CAOoxRfe7jCG630Ou+C6Pw==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.7.0", - "tsconfig-paths": "^3.9.0" - } - }, - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "typescript": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz", - "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", - "dev": true - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "v8-compile-cache-lib": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", - "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", - "dev": true - }, - "v8-to-istanbul": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", - "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - }, - "dependencies": { - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - } - } - }, - "validator": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", - "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "dev": true, - "requires": { - "browser-process-hrtime": "^1.0.0" - } - }, - "w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "dev": true, - "requires": { - "xml-name-validator": "^3.0.0" - } - }, - "walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "requires": { - "makeerror": "1.0.12" - } - }, - "watchpack": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", - "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", - "dev": true, - "requires": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - } - }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", - "dev": true, - "requires": { - "defaults": "^1.0.3" - } - }, - "webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true - }, - "webpack": { - "version": "5.70.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.70.0.tgz", - "integrity": "sha512-ZMWWy8CeuTTjCxbeaQI21xSswseF2oNOwc70QSKNePvmxE7XW36i7vpBMYZFAUHPwQiEbNGCEYIOOlyRbdGmxw==", - "dev": true, - "requires": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.4.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.9.2", - "es-module-lexer": "^0.9.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.3.1", - "webpack-sources": "^3.2.3" - } - }, - "webpack-node-externals": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", - "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", - "dev": true - }, - "webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true - }, - "whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dev": true, - "requires": { - "iconv-lite": "0.4.24" - } - }, - "whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true - }, - "whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "dev": true, - "requires": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "windows-release": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", - "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==", - "dev": true, - "requires": { - "execa": "^4.0.2" - }, - "dependencies": { - "execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true - } - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", - "dev": true, - "requires": {} - }, - "xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, - "xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - } } } diff --git a/package.json b/package.json index dd07daad..58a4c265 100644 --- a/package.json +++ b/package.json @@ -23,31 +23,39 @@ "dependencies": { "@nestjs/axios": "^0.0.7", "@nestjs/common": "^8.4.2", - "@nestjs/config": "^2.0.0", + "@nestjs/config": "^2.3.4", "@nestjs/core": "^8.0.0", - "@nestjs/jwt": "^8.0.0", + "@nestjs/jwt": "^8.0.1", + "@nestjs/passport": "^10.0.3", "@nestjs/platform-express": "^8.0.0", "@nestjs/schedule": "^1.1.0", - "@nestjs/swagger": "^5.2.0", + "@nestjs/swagger": "^5.2.1", + "@nestjs/typeorm": "^10.0.2", "axios": "^0.26.1", "cache-manager": "^3.6.1", "class-transformer": "^0.5.1", "class-validator": "^0.13.2", + "date-fns": "^3.6.0", "dotenv": "^16.0.0", "express": "^4.17.3", "form-data": "^4.0.0", "graphql-tag": "^2.12.6", + "in-memory-faceted-search": "^1.0.1", "jwt-decode": "^3.1.2", - "moment": "^2.29.3", + "moment": "^2.30.1", "multer": "^1.4.4", "node-cron": "^3.0.1", "node-schedule": "^2.1.0", "object-resolve-path": "^1.1.1", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1", + "pg": "^8.11.3", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.2.0", "swagger-ui-express": "^4.3.0", - "templates.js": "^0.3.11" + "templates.js": "^0.3.11", + "typeorm": "^0.3.20" }, "devDependencies": { "@nestjs/cli": "^8.0.0", @@ -59,6 +67,7 @@ "@types/jest": "27.4.1", "@types/node": "^16.0.0", "@types/supertest": "^2.0.11", + "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", "eslint": "^8.0.1", diff --git a/src/Question/dto/question.dto.ts b/src/Question/dto/question.dto.ts deleted file mode 100644 index db9177a2..00000000 --- a/src/Question/dto/question.dto.ts +++ /dev/null @@ -1,249 +0,0 @@ -import { ApiProperty } from "@nestjs/swagger"; -import { Exclude, Expose } from "class-transformer"; -import { StudentDto } from "src/student/dto/student.dto"; - -export class QuestionDto { - @Expose() - examQuestionId: string; - - @ApiProperty({ - description: - "Body contains the text, graphics, media objects and interactions that describe the question’s content.", - }) - @Expose() - body: string; - - @ApiProperty({ - description: - "Instructions on how to understand, attempt or how the question will be evaluated.", - }) - @Expose() - instructions: string; - - @ApiProperty({ - description: "Feedback shown to the students after response processing.", - }) - @Expose() - feedback: [string]; - - @ApiProperty({ - description: - "Hints are shown to the students after response processing or when the student requests for hints.", - }) - @Expose() - hints: [string]; - - @ApiProperty({ - description: "options of question.", - }) - @Expose() - options: [string]; - - @ApiProperty({ - description: "List of media used in the question", - }) - @Expose() - media: [string]; - - @ApiProperty({ - description: - "Information about answer to the question, when it is correct and optionally, how it is scored.", - }) - @Expose() - responseDeclaration: [string]; - - @ApiProperty({ - description: - "Information about the outcome variables of the question, i.e the values that are output of a question session.", - }) - @Expose() - outcomeDeclaration: [string]; - - @ApiProperty({ - description: - "Declaration of template variables that are to used for the purposes of cloning questions, i.e. auto-generating different sets of values for variables in the question.", - }) - @Expose() - templateDeclaration: [string]; - - @ApiProperty({ - description: - "One or more template rules to assign values to the template variables.", - }) - @Expose() - templateProcessing: [string]; - - @ApiProperty({ - description: - "Rules to assign values to outcome variables based on the student's reponses.", - }) - @Expose() - responseProcessing: [string]; - - @ApiProperty({ - description: "Cognitive processes involved to answer the question.", - }) - @Expose() - bloomsLevel: [string]; - - @ApiProperty({ - description: "Difficulty level of the question.", - }) - @Expose() - qlevel: [string]; - - @ApiProperty({ - description: "Purpose served by the question.", - }) - @Expose() - purpose: string; - - @ApiProperty({ - description: "Expected time for one attempt of the question.", - }) - @Expose() - expectedDuration: number; - - @ApiProperty({ - description: "Maximum score that can be awarded for the question.", - }) - @Expose() - maxScore: number; - - @ApiProperty({ - description: - "One of the standard question types - mcq, mtf, ftb, mmcq, essay, short answers, programming, other. this can be auto-derived based on the interactions used in the question.", - }) - @Expose() - type: string; - - @ApiProperty({ - description: - "If the question is visible for all or only for those who created it and/or for some specific systems or use cases.", - }) - @Expose() - visibility: string; - - @ApiProperty({ - description: - "Set to true if question data has template variables and template processing, else it is set to false.", - }) - @Expose() - isTemplate: boolean; - - @ApiProperty({ - description: "List of interactions present in the question.", - }) - @Expose() - interactions: [string]; - - @ApiProperty({ - description: "true, if question data has solutions, else, set to false", - }) - @Expose() - solutionAvailable: boolean; - - @ApiProperty({ - description: - "One of the values: responseProcessing (if question has inbuild response processing), offline (if scoring will be done offline and/or manually) or external (if an external system does the evaluation and submit the score).", - }) - @Expose() - scoringMode: string; - - @ApiProperty({ - description: - "Version of the QuML specification using which the question is created.", - }) - @Expose() - qumlVersion: string; - - @ApiProperty({ - description: - "Total cumulative time spent, in milliseconds, on the question by all users.", - }) - @Expose() - totalTimeSpent: number; - - @ApiProperty({ - description: "Average time spent per attempt, in milliseconds.", - }) - @Expose() - avgTimeSpent: number; - - @ApiProperty({ - description: "total number of attempts.", - }) - @Expose() - numAttempts: number; - - @ApiProperty({ - description: "Number of attempts where the user response is correct.", - }) - @Expose() - numCorrectAttempts: number; - - @ApiProperty({ - description: "Number of attempts where the user response is in-correct.", - }) - @Expose() - numInCorrectAttempts: number; - - @ApiProperty({ - description: - "Total number of attempts where the user did not give a response.", - }) - @Expose() - numSkips: number; - - @ApiProperty({ - description: "Average rating of the question.", - }) - @Expose() - avgRating: number; - - @ApiProperty({ - description: "Total number of ratings given for the question.", - }) - @Expose() - totalRatings: number; - - @ApiProperty({}) - @Expose() - topic: string; - - @ApiProperty({}) - @Expose() - subject: string; - - @ApiProperty({}) - @Expose() - class: string; - - @ApiProperty({}) - @Expose() - questionId: string; - - @ApiProperty({}) - @Expose() - language: string; - - @ApiProperty({}) - @Expose() - compatibilityLevel: string; - - @ApiProperty({}) - @Expose() - learningOutcome: string; - - @ApiProperty({}) - @Expose() - source: string; - - @ApiProperty({}) - @Expose() - answer: string; - - constructor(obj: QuestionDto) { - Object.assign(this, obj); - } -} diff --git a/src/Question/dto/question.search.dto.ts b/src/Question/dto/question.search.dto.ts deleted file mode 100644 index 70bcdcdd..00000000 --- a/src/Question/dto/question.search.dto.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; - -export class QuestionSearchDto { - @ApiProperty({ - type: String, - description: "Limit", - }) - limit: string; - - @ApiProperty({ - type: Object, - description: "Filters", - }) - @ApiPropertyOptional() - filters: object; - - constructor(partial: Partial) { - Object.assign(this, partial); - } -} diff --git a/src/Question/question.controller.ts b/src/Question/question.controller.ts deleted file mode 100644 index 0dfa1f04..00000000 --- a/src/Question/question.controller.ts +++ /dev/null @@ -1,309 +0,0 @@ -import { - ApiBasicAuth, - ApiBody, - ApiCreatedResponse, - ApiForbiddenResponse, - ApiOkResponse, - ApiQuery, - ApiTags, -} from "@nestjs/swagger"; -import { - CacheInterceptor, - ClassSerializerInterceptor, - Controller, - Get, - UseInterceptors, - Query, - Param, - Req, - Request, - Inject, - Post, - SerializeOptions, - Body, - Put, -} from "@nestjs/common"; -import { - DikshaQuestionToken, - QumlQuestionService, -} from "src/adapters/diksha/quml.adapter"; -import { IServicelocator } from "src/adapters/questionservicelocator"; -import { QuestionDto } from "./dto/question.dto"; -import { HasuraQuestionToken } from "src/adapters/hasura/question.adapter"; - -@ApiTags("Question") -@Controller("question") -export class QuestionController { - constructor( - @Inject(DikshaQuestionToken) private dikshaProvider: IServicelocator, - - @Inject(HasuraQuestionToken) - private hasuraProvider: IServicelocator - ) {} - - @Get(":adapter/search") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - //@ApiBasicAuth("access-token") - @ApiOkResponse({ description: "Get all Questions detail." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @ApiQuery({ name: "questionType", required: false }) - @ApiQuery({ name: "subject", required: false }) - @ApiQuery({ name: "limit", required: false }) - @ApiQuery({ name: "language", required: false }) - @ApiQuery({ name: "medium", required: false }) - @ApiQuery({ name: "bloomsLevel", required: false }) - @ApiQuery({ name: "topic", required: false }) - @ApiQuery({ name: "className", required: false }) - public async getAllQuestions( - @Param("adapter") adapter: string, - @Query("questionType") questionType: string, - @Query("subject") subject: [string], - @Query("limit") limit: string, - @Query("language") language: string, - @Query("medium") medium: string, - @Query("bloomsLevel") bloomsLevel: [string], - @Query("topic") topic: [string], - @Query("className") className: [string], - @Req() request: Request - ) { - if (adapter === "diksha") { - return this.dikshaProvider.getAllQuestions( - questionType, - subject, - limit, - language, - medium, - bloomsLevel, - topic, - className, - request - ); - } - } - - @Get(":adapter/questionIds") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - //@ApiBasicAuth("access-token") - @ApiOkResponse({ description: "Get all Questions detail." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @ApiQuery({ name: "questionIds", required: false }) - public async getAllQuestionsByQuestionIds( - @Param("adapter") adapter: string, - @Query("questionIds") questionIds: [string], - @Req() request: Request - ) { - if (adapter === "diksha") { - return this.dikshaProvider.getAllQuestionsByQuestionIds( - questionIds, - request - ); - } - } - - @Get(":adapter/subjectlist") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiOkResponse({ description: "Get all subject list" }) - @ApiQuery({ name: "gradeLevel", required: true }) - @ApiForbiddenResponse({ description: "Forbidden" }) - public async getSubjectList( - @Param("adapter") adapter: string, - @Query("gradeLevel") gradeLevel: string - ) { - if (adapter === "diksha") { - return this.dikshaProvider.getSubjectList(gradeLevel); - } - } - - @Get(":adapter/topicslist") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiOkResponse({ description: "Get all subject list" }) - @ApiQuery({ name: "subject", required: true }) - @ApiForbiddenResponse({ description: "Forbidden" }) - public async getTopicsList( - @Param("adapter") adapter: string, - @Query("subject") subject: string - ) { - if (adapter === "diksha") { - return this.dikshaProvider.getTopicsList(subject); - } - } - - @Get(":adapter/questionid") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - // @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "Get Questions detail." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @ApiQuery({ name: "questionId", required: false }) - public async getOneQuestion( - @Param("adapter") adapter: string, - @Query("questionId") questionId: string, - @Req() request: Request - ) { - if (adapter === "diksha") { - return this.dikshaProvider.getOneQuestion(questionId, request); - } - } - - @Get(":adapter/competencieslist") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - //@ApiBasicAuth("access-token") - @ApiOkResponse({ description: "Get all competencies list." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @ApiQuery({ name: "subject", required: false }) - @ApiQuery({ name: "limit", required: false }) - public async getcompetenciesList( - @Param("adapter") adapter: string, - @Query("subject") subject: string, - @Query("limit") limit: string, - @Req() request: Request - ) { - if (adapter === "diksha") { - return this.dikshaProvider.getCompetenciesList(subject, limit, request); - } - } - - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "Question detail." }) - @SerializeOptions({ - strategy: "excludeAll", - }) - @ApiQuery({ name: "adapter" }) - getQuestion( - @Param("id") questionId: string, - @Query("adapter") adapter: string, - @Req() request: Request - ) { - if (adapter === "diksha") { - return this.dikshaProvider.getQuestion(questionId, request); - } else if (adapter === "shiksha") { - return this.hasuraProvider.getQuestion(questionId, request); - } - } - - @Post() - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Question has been created successfully.", - }) - @ApiBody({ type: QuestionDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @ApiQuery({ name: "adapter" }) - public async createQuestion( - @Req() request: Request, - @Query("adapter") adapter: string, - @Body() questionDto: QuestionDto - ) { - if (adapter === "diksha") { - return this.dikshaProvider.createQuestion(request, questionDto); - } else if (adapter === "shiksha") { - return this.hasuraProvider.createQuestion(request, questionDto); - } - } - - @Put("/:id") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Question has been updated successfully.", - }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @ApiQuery({ name: "adapter" }) - public async updateQuestion( - @Param("id") questionId: string, - @Req() request: Request, - @Body() questionDto: QuestionDto, - @Query("adapter") adapter: string - ) { - if (adapter === "diksha") { - return this.dikshaProvider.updateQuestion( - questionId, - request, - questionDto - ); - } else if (adapter === "shiksha") { - return this.hasuraProvider.updateQuestion( - questionId, - request, - questionDto - ); - } - } - - @Post("filter/:adapter") - @UseInterceptors(ClassSerializerInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: " Ok." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @ApiQuery({ name: "limit", required: false }) - @ApiQuery({ name: "body", required: false }) - @ApiQuery({ name: "className", required: false }) - @ApiQuery({ name: "maxScore", required: false }) - @ApiQuery({ name: "questionId", required: false }) - @ApiQuery({ name: "subject", required: false }) - @ApiQuery({ name: "topic", required: false }) - @ApiQuery({ name: "type", required: false }) - @ApiQuery({ name: "page", required: false }) - public async filterQuestion( - @Param("adapter") adapter: string, - @Query("limit") limit: string, - @Query("body") body: string, - @Query("className") className: string, - @Query("maxScore") maxScore: string, - @Query("questionId") questionId: string, - @Query("subject") subject: string, - @Query("topic") topic: string, - @Query("type") type: string, - @Query("page") page: number, - @Req() request: Request - ) { - if (adapter === "diksha") { - return this.dikshaProvider.filterQuestion( - limit, - body, - className, - maxScore, - questionId, - subject, - topic, - type, - page, - request - ); - } else if (adapter === "shiksha") { - return this.hasuraProvider.filterQuestion( - limit, - body, - className, - maxScore, - questionId, - subject, - topic, - type, - page, - request - ); - } - } - - @Post(":adapter/bulkImport") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Bulk Question has been created successfully.", - }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async bulkImport( - @Param("adapter") adapter: string, - @Req() request: Request, - @Body() questionDto: [Object] - ) { - if (adapter === "diksha") { - return this.dikshaProvider.bulkImport(request, questionDto); - } else if (adapter === "shiksha") { - return this.hasuraProvider.bulkImport(request, questionDto); - } - } -} diff --git a/src/Question/question.module.ts b/src/Question/question.module.ts deleted file mode 100644 index 2f725705..00000000 --- a/src/Question/question.module.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { CacheModule, Module } from "@nestjs/common"; -import { HttpModule } from "@nestjs/axios"; -import { QuestionController } from "./question.controller"; -import { - DikshaQuestionToken, - QumlQuestionService, -} from "src/adapters/diksha/quml.adapter"; -import { - HasuraQuestionToken, - QuestionService, -} from "src/adapters/hasura/question.adapter"; -const ttl = process.env.TTL as never; -@Module({ - imports: [ - HttpModule, - CacheModule.register({ - ttl: ttl, - }), - ], - controllers: [QuestionController], - providers: [ - QumlQuestionService, - { provide: DikshaQuestionToken, useClass: QumlQuestionService }, - { provide: HasuraQuestionToken, useClass: QuestionService }, - ], -}) -export class QuestionModule {} diff --git a/src/adapters/announcementsservicelocator.ts b/src/adapters/announcementsservicelocator.ts deleted file mode 100644 index 43786ef8..00000000 --- a/src/adapters/announcementsservicelocator.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { AnnouncementsFilterDto } from "src/announcements/dto/announcements-filter.dto"; -import { AnnouncementsDto } from "src/announcements/dto/announcements.dto"; - -export interface IServicelocator { - getAnnouncement(announcementId: any, request: any); - getAnnouncementSet(request: any, filters?: AnnouncementsFilterDto); - updateAnnouncement( - announcementId: string, - request: any, - announcementData: AnnouncementsDto - ); - createAnnouncement(request: any, announcementData: AnnouncementsDto); - deleteAnnouncement(announcementId: string, request: any); -} diff --git a/src/adapters/assignprivilegelocater.ts b/src/adapters/assignprivilegelocater.ts new file mode 100644 index 00000000..22799cf0 --- /dev/null +++ b/src/adapters/assignprivilegelocater.ts @@ -0,0 +1,7 @@ +import { Response } from "express"; +import { CreatePrivilegeRoleDto } from "src/rbac/assign-privilege/dto/create-assign-privilege.dto"; + +export interface IServicelocatorprivilegeRole { + createPrivilegeRole(request: any, createPrivilegeRole:CreatePrivilegeRoleDto, response:Response); + getPrivilegeRole(userId, request, response:Response); +} \ No newline at end of file diff --git a/src/adapters/assignroleservicelocater.ts b/src/adapters/assignroleservicelocater.ts new file mode 100644 index 00000000..00f0487a --- /dev/null +++ b/src/adapters/assignroleservicelocater.ts @@ -0,0 +1,7 @@ +import { CreateAssignRoleDto } from "src/rbac/assign-role/dto/create-assign-role.dto"; +import { Response } from "express"; +export interface IServicelocatorassignRole { + createAssignRole(request: any, createAssignRoleDto: CreateAssignRoleDto, response: Response); + getAssignedRole(userId, request, response: Response); + deleteAssignedRole(deleteAssignRoleDto, response: Response); +} \ No newline at end of file diff --git a/src/adapters/attendanceservicelocator.ts b/src/adapters/attendanceservicelocator.ts index 6decbbe1..536dd6b2 100644 --- a/src/adapters/attendanceservicelocator.ts +++ b/src/adapters/attendanceservicelocator.ts @@ -3,8 +3,15 @@ import { AttendanceSearchDto } from "src/attendance/dto/attendance-search.dto"; import { AttendanceDto } from "src/attendance/dto/attendance.dto"; export interface IServicelocator { - checkAndAddAttendance(request: Request, attendanceDto: AttendanceDto): unknown; - getAttendance(tenantId: string, attendanceId: string, request: any); + // checkAndAddAttendance(request: Request, attendanceDto: AttendanceDto): unknown; + // getAttendance(tenantId: string, attendanceId: string, request: any); + attendanceReport( + attendanceStatsDto + ); + updateAttendanceRecord( + request, + attendanceDto + ); updateAttendance( attendanceId: string, request: any, @@ -14,7 +21,7 @@ export interface IServicelocator { multipleAttendance( tenantId: string, request: any, - attendanceData: [AttendanceDto] + attendanceData: any ); searchAttendance( tenantId: string, diff --git a/src/adapters/auth/auth.adapter.ts b/src/adapters/auth/auth.adapter.ts deleted file mode 100644 index 9a5dea91..00000000 --- a/src/adapters/auth/auth.adapter.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { HttpService } from "@nestjs/axios"; -import { AuthDto } from "src/auth/dto/auth-dto"; - -@Injectable() -export class HasuraAuthService { - axios = require("axios"); - - constructor(private httpService: HttpService) {} - - public async login(request: any, response: any, loginDto: AuthDto) { - const qs = require("qs"); - const data = qs.stringify({ - username: loginDto.username, - password: loginDto.password, - grant_type: "password", - client_id: "hasura", - client_secret: process.env.KEYCLOAK_HASURA_CLIENT_SECRET, - }); - - const config = { - method: "post", - url: process.env.KEYCLOAK + process.env.KEYCLOAK_USER_TOKEN, - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - data: data, - }; - - try { - const res = await this.axios(config); - return response.status(200).send(res.data); - } catch (error) { - console.error(error, "err"); - return error; - } - } -} diff --git a/src/adapters/cohortMembersservicelocator.ts b/src/adapters/cohortMembersservicelocator.ts index faefab95..8b00a0f7 100644 --- a/src/adapters/cohortMembersservicelocator.ts +++ b/src/adapters/cohortMembersservicelocator.ts @@ -1,9 +1,22 @@ import { CohortMembersSearchDto } from "src/cohortMembers/dto/cohortMembers-search.dto"; import { CohortMembersDto } from "src/cohortMembers/dto/cohortMembers.dto"; - +import { CohortMembersUpdateDto } from "src/cohortMembers/dto/cohortMember-update.dto"; +import { Response } from "express"; export interface IServicelocatorcohortMembers { - createCohortMembers(request: any, cohortMembersDto: CohortMembersDto); - getCohortMembers(tenantId, cohortMembershipId, request); - searchCohortMembers(tenantid, request: any, cohortMembersSearchDto: CohortMembersSearchDto); - updateCohortMembers(cohortMembershipId: string, request: any, cohortMembersDto: CohortMembersDto); + createCohortMembers( + loginUser: any, + cohortMembersDto: CohortMembersDto, + response: any, + tenantId: string, + ); + getCohortMembers(cohortMemberId: string, tenantId: string, fieldvalue: string, response: Response); + searchCohortMembers(cohortMembersSearchDto: CohortMembersSearchDto, tenantId: string, response: Response); + updateCohortMembers( + cohortMembershipId: string, + loginUser: any, + cohortMemberUpdateDto: CohortMembersUpdateDto, + + response: any + ); + deleteCohortMemberById(tenantid, cohortMembershipId, response); } diff --git a/src/adapters/cohortservicelocator.ts b/src/adapters/cohortservicelocator.ts index a2a9d287..b15d0493 100644 --- a/src/adapters/cohortservicelocator.ts +++ b/src/adapters/cohortservicelocator.ts @@ -1,10 +1,13 @@ import { CohortCreateDto } from "src/cohort/dto/cohort-create.dto"; +import { CohortUpdateDto } from "src/cohort/dto/cohort-update.dto"; import { CohortSearchDto } from "src/cohort/dto/cohort-search.dto"; import { CohortDto } from "src/cohort/dto/cohort.dto"; +import { Response } from "express"; export interface IServicelocatorcohort { - createCohort(request: any, cohortDto: CohortCreateDto); - getCohort(tenantId, cohortId, request, res); - searchCohort(tenantid, request: any, cohortSearchDto: CohortSearchDto, res); - updateCohort(cohortId: string, request: any, cohortDto: CohortCreateDto); + getCohortsDetails(cohortId: string,response); + createCohort(request: any, cohortDto: CohortCreateDto,response); + searchCohort(tenantid, request: any, cohortSearchDto: CohortSearchDto,response); + updateCohort(cohortId: string, request: any, cohortUpdateDto: CohortUpdateDto,response); + updateCohortStatus(cohortId: string, request: any,response); } diff --git a/src/adapters/commentservicelocator.ts b/src/adapters/commentservicelocator.ts deleted file mode 100644 index 6792cd76..00000000 --- a/src/adapters/commentservicelocator.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { CommentSearchDto } from "src/comment/dto/comment-search.dto"; -import { CommentDto } from "src/comment/dto/comment.dto"; - -export interface IServicelocator { - getComment(commentId: string, request: any); - createComment(request: any, commentDto: CommentDto); - updateComment(commentId: string, request: any, commentDto: CommentDto); - searchComment(request: any, commentSearchDto: CommentSearchDto); -} diff --git a/src/adapters/diksha/quml.adapter.ts b/src/adapters/diksha/quml.adapter.ts deleted file mode 100644 index aeccfa6a..00000000 --- a/src/adapters/diksha/quml.adapter.ts +++ /dev/null @@ -1,511 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { HttpService } from "@nestjs/axios"; -import { SuccessResponse } from "src/success-response"; -import { QuestionDto } from "src/Question/dto/question.dto"; -import { IServicelocator } from "../questionservicelocator"; -import e from "express"; -export const DikshaQuestionToken = "Question"; -@Injectable() -export class QumlQuestionService implements IServicelocator { - constructor(private httpService: HttpService) {} - url = process.env.DIKSHADEVBASEAPIURL; - public async getAllQuestions( - questionType: string, - subject: [string], - limit: string, - language: string, - medium: string, - bloomsLevel: [string], - topic: [string], - className: [string], - request: any - ) { - var axios = require("axios"); - - var data = { - request: { - filters: { - objectType: "Question", - status: ["Live"], - qType: questionType, - subject: subject, - language: language, - topic: topic, - gradeLevel: className, - medium: medium, - bloomsLevel: bloomsLevel, - }, - limit: limit, - }, - }; - - var config = { - method: "post", - url: `${this.url}/composite/v3/search`, - data: data, - }; - - const response = await axios(config); - const responseData = response.data.result.Question; - let arrayIds = responseData.map((e: any) => { - return e.identifier; - }); - - let questionArray = []; - for (let value of arrayIds) { - let questionData = this.getQuestions(value); - questionArray.push(await questionData); - } - - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: questionArray, - }); - } - - public async getQuestions(value: any) { - var axios = require("axios"); - - let config = { - method: "get", - url: `${this.url}/question/v1/read/${value}?fields=body,qType,answer,responseDeclaration,name,solutions,editorState,media,name,board,medium,gradeLevel,subject,topic,learningOutcome,marks,maxScore,bloomsLevel,compatibilityLevel,language,source`, - }; - - const response = await axios(config); - - const data = response?.data; - const final = data.result.question; - - const mappedResponse = { - body: final.body, - - instructions: final.instructions, - - feedback: final.feedback, - - topic: final.topic, - - subject: final.subject, - - class: final.gradeLevel, - - questionId: final.identifier, - - hints: final.hints, - - options: final.editorState.options, - - answer: final.answer, - - media: final.media, - - responseDeclaration: final.responseDeclaration, - - outcomeDeclaration: final.outcomeDeclaration, - - templateDeclaration: final.templateDeclaration, - - templateProcessing: final.templateProcessing, - - responseProcessing: final.responseProcessing, - - bloomsLevel: final.bloomsLevel, - - qlevel: final.qlevel, - - purpose: final.purpose, - - expectedDuration: final.expectedDuration, - - maxScore: final.maxScore, - - type: final.qType, - - visibility: final.visibility, - - isTemplate: final.isTemplate, - - interactions: final.interactions, - - solutionAvailable: final.solutionAvailable, - - scoringMode: final.scoringMode, - - qumlVersion: final.qumlVersion, - - totalTimeSpent: final.totalTimeSpent, - - avgTimeSpent: final.avgTimeSpent, - - numAttempts: final.numAttempts, - - numCorrectAttempts: final.numCorrectAttempts, - - numInCorrectAttempts: final.numInCorrectAttempts, - - numSkips: final.numSkips, - - source: final.source, - - learningOutcome: final.learningOutcome, - - compatibilityLevel: final.compatibilityLevel, - - language: final.language, - - avgRating: final.avgRating, - - totalRatings: final.totalRatings, - - examQuestionId: final.examQuestionId, - }; - - let res = new QuestionDto(mappedResponse); - return res; - } - - public async getAllQuestionsByQuestionIds( - questionIds: [string], - request: any - ) { - var axios = require("axios"); - let questionArray = []; - for (let value of questionIds) { - let config = { - method: "get", - url: `${this.url}/question/v1/read/${value}?fields=body,qType,answer,responseDeclaration,name,solutions,editorState,media,name,board,medium,gradeLevel,subject,topic,learningOutcome,marks,maxScore,bloomsLevel,compatibilityLevel,language,source`, - }; - - const response = await axios(config); - const data = response?.data; - const final = data.result.question; - - const mappedResponse = { - body: final.body, - - instructions: final.instructions, - - feedback: final.feedback, - - topic: final.topic, - - subject: final.subject, - - class: final.gradeLevel, - - questionId: final.identifier, - - hints: final.hints, - - options: final.editorState.options, - - answer: final.answer, - - media: final.media, - - responseDeclaration: final.responseDeclaration, - - outcomeDeclaration: final.outcomeDeclaration, - - templateDeclaration: final.templateDeclaration, - - templateProcessing: final.templateProcessing, - - responseProcessing: final.responseProcessing, - - bloomsLevel: final.bloomsLevel, - - qlevel: final.qlevel, - - purpose: final.purpose, - - expectedDuration: final.expectedDuration, - - maxScore: final.maxScore, - - type: final.qType, - - visibility: final.visibility, - - isTemplate: final.isTemplate, - - interactions: final.interactions, - - solutionAvailable: final.solutionAvailable, - - scoringMode: final.scoringMode, - - qumlVersion: final.qumlVersion, - - totalTimeSpent: final.totalTimeSpent, - - avgTimeSpent: final.avgTimeSpent, - - numAttempts: final.numAttempts, - - numCorrectAttempts: final.numCorrectAttempts, - - numInCorrectAttempts: final.numInCorrectAttempts, - - numSkips: final.numSkips, - - source: final.source, - - learningOutcome: final.learningOutcome, - - compatibilityLevel: final.compatibilityLevel, - - language: final.language, - - avgRating: final.avgRating, - - totalRatings: final.totalRatings, - - examQuestionId: final.examQuestionId, - }; - - let res = new QuestionDto(mappedResponse); - questionArray.push(res); - } - - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: questionArray, - }); - } - - public async getSubjectList(gradeLevel: string) { - try { - var axios = require("axios"); - let subjects = Array; - var config = { - method: "get", - url: "https://diksha.gov.in/api/framework/v1/read/mh_k-12_1?categories=board,gradeLevel,medium,class,subject", - headers: { - "Content-Type": "application/json", - }, - }; - const responseData = await axios(config); - const categories = responseData.data.result.framework.categories; - if (typeof categories !== "undefined" && categories.length > 0) { - const grades = categories.find((o) => o.code === "gradeLevel"); - let terms = grades.terms; - if (typeof terms !== "undefined" && terms.length > 0) { - let associations = terms.find((o) => o.code === gradeLevel); - subjects = associations.associations; - } - } - - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: subjects, - }); - } catch (e) { - return `${e}`; - } - } - public async getTopicsList(subject: string) { - try { - var axios = require("axios"); - var data = { - request: { - filters: { - objectType: "Question", - status: ["Live"], - subject: [subject], - }, - }, - }; - - var config = { - method: "post", - url: "https://vdn.diksha.gov.in/action/composite/v3/search", - headers: { - "Content-Type": "application/json", - }, - data: data, - }; - - const responseData = await axios(config); - const categories = responseData.data.result.Question; - const topicList = categories.map((e: any) => { - return e.se_topics[0]; - }); - const subjectTopicList = [...new Set(topicList)]; - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: subjectTopicList, - }); - } catch (e) { - return `${e}`; - } - } - public async getOneQuestion(questionId: string, request: any) { - var axios = require("axios"); - - let config = { - method: "get", - url: `${this.url}/question/v1/read/${questionId}?fields=body,qType,answer,responseDeclaration,name,solutions,editorState,media,name,board,medium,gradeLevel,subject,topic,learningOutcome,marks,maxScore,bloomsLevel,compatibilityLevel,language,source`, - }; - - const response = await axios(config); - - const data = response?.data; - - const final = data.result.question; - - const mappedResponse = { - body: final.body, - - instructions: final.instructions, - - feedback: final.feedback, - - topic: final.topic, - - subject: final.subject, - - class: final.gradeLevel, - - questionId: final.identifier, - - hints: final.hints, - - options: final.editorState.options, - - answer: final.answer, - - media: final.media, - - responseDeclaration: final.responseDeclaration, - - outcomeDeclaration: final.outcomeDeclaration, - - templateDeclaration: final.templateDeclaration, - - templateProcessing: final.templateProcessing, - - responseProcessing: final.responseProcessing, - - bloomsLevel: final.bloomsLevel, - - qlevel: final.qlevel, - - purpose: final.purpose, - - expectedDuration: final.expectedDuration, - - maxScore: final.maxScore, - - type: final.qType, - - visibility: final.visibility, - - isTemplate: final.isTemplate, - - interactions: final.interactions, - - solutionAvailable: final.solutionAvailable, - - scoringMode: final.scoringMode, - - qumlVersion: final.qumlVersion, - - totalTimeSpent: final.totalTimeSpent, - - avgTimeSpent: final.avgTimeSpent, - - numAttempts: final.numAttempts, - - numCorrectAttempts: final.numCorrectAttempts, - - numInCorrectAttempts: final.numInCorrectAttempts, - - numSkips: final.numSkips, - - source: final.source, - - learningOutcome: final.learningOutcome, - - compatibilityLevel: final.compatibilityLevel, - - language: final.language, - - avgRating: final.avgRating, - - totalRatings: final.totalRatings, - - examQuestionId: final.examQuestionId, - }; - - let res = new QuestionDto(mappedResponse); - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: res, - }); - } - - public async getCompetenciesList( - subject: string, - limit: string, - request: any - ) { - var axios = require("axios"); - try { - var data = { - request: { - filters: { - objectType: "Question", - status: ["Live"], - - subject: subject, - }, - limit: limit, - }, - }; - - var config = { - method: "post", - url: `${this.url}/composite/v3/search`, - data: data, - }; - - const response = await axios(config); - const responseData = response.data.result.Question; - const resData = responseData.map((e: any) => { - return e.bloomsLevel; - }); - let bloomsLevel = [...new Set(resData)]; - - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: bloomsLevel, - }); - } catch (e) { - return ` Competencies not found.`; - } - } - - getQuestion(questionId: string, request: any) {} - createQuestion(request: any, questionDto: QuestionDto) {} - updateQuestion(questionId: string, request: any, questionDto: QuestionDto) {} - filterQuestion( - limit: string, - body: string, - className: string, - maxScore: string, - questionId: string, - subject: string, - topic: string, - type: string, - page: number, - request: any - ) {} - bulkImport(request: any, questionDto: [Object]) {} -} diff --git a/src/adapters/fieldsservicelocator.ts b/src/adapters/fieldsservicelocator.ts index 9f5e4c59..13e69f59 100644 --- a/src/adapters/fieldsservicelocator.ts +++ b/src/adapters/fieldsservicelocator.ts @@ -2,16 +2,17 @@ import { FieldsSearchDto } from "src/fields/dto/fields-search.dto"; import { FieldsDto } from "src/fields/dto/fields.dto"; import { FieldValuesDto } from "src/fields/dto/field-values.dto"; import { FieldValuesSearchDto } from "src/fields/dto/field-values-search.dto"; +import { Response } from "express"; export interface IServicelocatorfields { //fields - createFields(request: any, fieldsDto: FieldsDto); - getFields(tenantId, fieldsId, request); - searchFields(tenantid, request: any, fieldsSearchDto: FieldsSearchDto); - updateFields(fieldsId: string, request: any, fieldsDto: FieldsDto); + createFields(request: any, fieldsDto: FieldsDto, response: Response); + // getFields(tenantId, fieldsId, request); + searchFields(tenantid, request: any, fieldsSearchDto: FieldsSearchDto,response: Response); + // updateFields(fieldsId: string, request: any, fieldsDto: FieldsDto); //field values - createFieldValues(request: any, fieldValuesDto: FieldValuesDto); - getFieldValues(id, request); - searchFieldValues(request: any, fieldValuesSearchDto: FieldValuesSearchDto); + createFieldValues(request: any, fieldValuesDto: FieldValuesDto,response : Response); + // getFieldValues(id, request); + searchFieldValues(request: any, fieldValuesSearchDto: FieldValuesSearchDto, response: Response); updateFieldValues(id: string, request: any, fieldValuesDto: FieldValuesDto); } diff --git a/src/adapters/groupservicelocator.ts b/src/adapters/groupservicelocator.ts deleted file mode 100644 index 1b57627e..00000000 --- a/src/adapters/groupservicelocator.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { GroupSearchDto } from "src/group/dto/group-search.dto"; -import { GroupDto } from "src/group/dto/group.dto"; - -export interface IServicelocatorgroup { - getGroup(groupId, request); - createGroup(request: any, groupDto: GroupDto); - updateGroup(groupId: string, request: any, groupDto: GroupDto); - searchGroup(request: any, groupSearchDto: GroupSearchDto); - findMembersOfGroup(id, role, request); - findGroupsByUserId(id, role, request); - findMembersOfChildGroup(groupId: string, role: string, request: any); -} diff --git a/src/adapters/hasura/announcements.adapter.ts b/src/adapters/hasura/announcements.adapter.ts deleted file mode 100644 index a374f53f..00000000 --- a/src/adapters/hasura/announcements.adapter.ts +++ /dev/null @@ -1,313 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { HttpService } from "@nestjs/axios"; -import { SuccessResponse } from "src/success-response"; -import { IServicelocator } from "../announcementsservicelocator"; -import { AnnouncementsDto } from "src/announcements/dto/announcements.dto"; -import { AnnouncementsFilterDto } from "src/announcements/dto/announcements-filter.dto"; - -export const AnnouncementsToken = "Announcements"; -// TODO: Refactor as per Shiksha 2.0 -@Injectable() -export class AnnouncementsService implements IServicelocator { - constructor(private httpService: HttpService) {} - baseURL = process.env.REGISTRYHASURA; - adminSecret = process.env.REGISTRYHASURAADMINSECRET; - - //to get details of a given announcement - public async getAnnouncement(announcementId: string, request: any) { - var axios = require("axios"); - var data = { - query: `query getAnnouncement($id:Int!) { - announcements(where: {id: {_eq: $id}}) { - id, - title, - status, - type, - modified_at, - data, - is_pinned, - is_dismissable, - additional_tags, - } - }`, - variables: { - id: announcementId, - }, - }; - var config = { - method: "post", - url: this.baseURL, - headers: { - "x-hasura-admin-secret": this.adminSecret, - "Content-Type": "application/json", - }, - data: data, - }; - - const responseData = await axios(config); - const response = responseData.data.data.announcements; - - let responsedata = await this.mappedResponse(response); - let x = new SuccessResponse({ - statusCode: 200, - message: "ok.", - data: responsedata, - }); - return x; - } - - //to get the data of announcements by page - public async getAnnouncementSet( - request: any, - filters: AnnouncementsFilterDto - ) { - var axios = require("axios"); - - //adding select clause to filters - let selectClause = ``, - queryVar = ``; - let variables = {}; - - //add corr select and data statement to query - if (filters?.pageSize) { - variables["_limit"] = parseInt(filters.pageSize); - } - if (filters?.pageIndex) { - variables["_offset"] = parseInt(filters.pageIndex); - } - if (filters?.title) { - variables["_title"] = `%${filters.title}%`; - queryVar += `$_title: String,`; - selectClause += `title: {_ilike: $_title}`; - } - if (filters?.author) { - variables["_author"] = filters.author; - queryVar += `$_author: [String],`; - selectClause += `author:{_in: $_author},`; - } - if (filters?.isPinned) { - variables["_isPinned"] = filters?.isPinned; - queryVar += `$_isPinned: Boolean,`; - selectClause += `is_pinned: {_eq: $_isPinned},`; - } - if (filters?.pinnedAnnouncementProperties) { - variables["_isDismissable"] = - filters?.pinnedAnnouncementProperties?.isDismissable; - queryVar += `$_isDismissable: Boolean,`; - selectClause += `is_dismissable: {_eq: $_isDismissable},`; - } - if (filters?.status) { - variables["_status"] = filters?.status; - queryVar += `$_status: String,`; - selectClause += `status: {_ilike: $_status},`; - } - if (filters?.announcementType) { - variables["_type"] = filters?.announcementType; - queryVar += `$_type: [String]`; - selectClause += `type: {_in: $_type},`; - } - if (filters?.startDate && filters?.endDate) { - variables["_startDate"] = filters?.startDate; - variables["_endDate"] = filters?.endDate; - queryVar += `$_startDate: timestamptz, $_endDate: timestamptz,`; - selectClause += `modified_at: {_gte: $_startDate, _lte: $_endDate},`; - } - - let data = { - query: `query get_announcement_set($_limit: Int, $_offset: Int, ${queryVar}) { - announcements(limit: $_limit, offset: $_offset, order_by: {modified_at: desc}, where: { ${selectClause} }) { - additional_tags - author - data - id - is_dismissable - is_pinned - modified_at - status - title - type - } - announcements_aggregate(where: {${selectClause}}) { - aggregate { - count - } - } - } - `, - variables: variables, - }; - var config = { - method: "post", - url: this.baseURL, - headers: { - "x-hasura-admin-secret": this.adminSecret, - "Content-Type": "application/json", - }, - data: data, - }; - - const responseData = await axios(config); - const response = responseData.data.data.announcements; - let responsedata = await this.mappedResponse(response); - return new SuccessResponse({ - statusCode: 200, - message: "ok.", - data: { - count: responseData.data.data.announcements_aggregate.aggregate.count, - data: responsedata, - }, - }); - } - - //to update a given announcement - public async updateAnnouncement( - announcementId: string, - request: any, - announcementsData: AnnouncementsDto - ) { - var axios = require("axios"); - let isPresent: any; - - var updateData = { - query: `mutation updateAnnouncements($id: Int!, $additional_tags: _text = "", $data: String = "", $is_dismissable: Boolean = false, $is_pinned: Boolean = false, $modified_at: timestamptz, $status: String = "",$author: String = "", $title: String = "", $type: String = "") { - update_announcements(_set: {additional_tags: $additional_tags, data: $data, is_dismissable: $is_dismissable, is_pinned: $is_pinned, modified_at: $modified_at, status: $status,author: $author, title: $title, type: $type}, where: {id: {_eq: $id}}) { - affected_rows - }}`, - variables: { - id: parseInt(announcementId), - additional_tags: `{${announcementsData.additionalTags.toString()}}`, - data: announcementsData.data, - is_dismissable: - announcementsData.pinnedAnnouncementProperties?.is_dismissable, - is_pinned: announcementsData.isPinned, - modified_at: announcementsData.dateModified, - status: announcementsData.status, - author: announcementsData.author, - title: announcementsData.title, - type: announcementsData.announcementType, - }, - }; - var updateConfig = { - method: "post", - url: this.baseURL, - headers: { - "x-hasura-admin-secret": this.adminSecret, - "Content-Type": "application/json", - }, - data: updateData, - }; - - const responseData = await axios(updateConfig); - const response = responseData.data; - - return new SuccessResponse({ - statusCode: 200, - message: "ok.", - data: response, - }); - } - - //to create an announcement - public async createAnnouncement(request: any, announcementsData: any) { - var axios = require("axios"); - var data = { - query: `mutation createAnnouncement($additional_tags: _text, $data: String = "", $is_dismissable: Boolean = false, $is_pinned: Boolean = false, $status: String = "", $title: String = "", $type: String = "") { - insert_announcements_one(object: {additional_tags: $additional_tags, data: $data, is_dismissable: $is_dismissable, is_pinned: $is_pinned, status: $status, title: $title, type: $type}) { - id - } - } - `, - variables: { - additional_tags: `{${announcementsData?.additionalTags?.toString()}}`, - data: announcementsData.data, - is_dismissable: - announcementsData.pinnedAnnouncementProperties?.is_dismissable, - is_pinned: announcementsData.isPinned, - status: announcementsData.status, - title: announcementsData.title, - type: announcementsData.announcementType, - }, - }; - - var config = { - method: "post", - url: this.baseURL, - headers: { - "x-hasura-admin-secret": this.adminSecret, - "Content-Type": "application/json", - }, - data: data, - }; - - const responseData = await axios(config); - const response = responseData.data; - let final = { - ...response, - result: { - Announcements: { osid: response.data.insert_announcements_one.id }, - }, - }; - - return new SuccessResponse({ - statusCode: 200, - message: "ok.", - data: final, - }); - } - - //to delete an announcement - public async deleteAnnouncement(announcementId: string, request: any) { - var axios = require("axios"); - var data = { - query: `mutation delete_announcement($id: Int!) { - delete_announcements_by_pk(id: $id){ - id - } - } - `, - variables: { - id: announcementId, - }, - }; - var config = { - method: "post", - url: this.baseURL, - headers: { - "x-hasura-admin-secret": this.adminSecret, - "Content-Type": "application/json", - }, - data: data, - }; - - const responseData = await axios(config); - const response = responseData.data.data; - let x = new SuccessResponse({ - statusCode: 200, - message: "Deleted announcement successfully", - data: response, - }); - return x; - } - - public async mappedResponse(result: any) { - const announcementResponse = result.map((obj: any) => { - const announcementMapping = { - announcementId: obj?.id ? `${obj.id}` : "", - title: obj?.title ? `${obj.title}` : "", - status: obj?.status ? `${obj.status}` : "", - author: obj?.author ? `${obj.author}` : "", - announcementType: obj?.type ? `${obj.type}` : "", - dateModified: obj?.modified_at ? `${obj.modified_at}` : "", - data: obj?.data ? `${obj.data}` : "", - isPinned: obj?.is_pinned ? obj.is_pinned : false, - additionalTags: obj?.additional_tags ? obj.additional_tags : [], - pinnedAnnouncementProperties: { - isDismissable: obj?.is_dismissable ?? false, - }, - }; - return new AnnouncementsDto(announcementMapping); - }); - - return announcementResponse; - } -} diff --git a/src/adapters/hasura/assessmentset.adapter.ts b/src/adapters/hasura/assessmentset.adapter.ts deleted file mode 100644 index e6c1bde8..00000000 --- a/src/adapters/hasura/assessmentset.adapter.ts +++ /dev/null @@ -1,188 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { HttpService } from "@nestjs/axios"; -import { SuccessResponse } from "src/success-response"; -import { AssessmentsetDto } from "src/assessmentset/dto/assessmentset.dto"; -@Injectable() -export class AssessmentsetService { - constructor(private httpService: HttpService) {} - - public async createAssessmentSet( - request: any, - assessmentsetDto: AssessmentsetDto - ) { - var axios = require("axios"); - try { - var data = { - query: `mutation CreateAssessmentset($gradeType:String,$options:String,$title:String,$type:String,$typeDetails:String) { - insert_assessmentset_one(object: {gradeType: $gradeType, options: $options, title: $title, type: $type, typeDetails: $typeDetails}) { - assessmentsetId - } -}`, - variables: { - gradeType: assessmentsetDto.gradeType, - options: assessmentsetDto.options, - title: assessmentsetDto.title, - type: assessmentsetDto.type, - typeDetails: assessmentsetDto.typeDetails, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: response.data, - }); - } catch (e) { - return `${e}`; - } - } - public async getAssessmentset(assessmentsetId: any, request: any) { - var axios = require("axios"); - try { - var data = { - query: `query GetAssessmentset($assessmentsetId:uuid) { - assessmentset(where: {assessmentsetId: {_eq: $assessmentsetId}}) { - assessmentsetId - gradeType - title - options - type - typeDetails - created_at - updated_at - } - }`, - variables: { assessmentsetId: assessmentsetId }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - const result = await this.mappedResponse( - response.data.data.assessmentset - ); - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result[0], - }); - } catch (e) { - return `${e}`; - } - } - - public async searchAssessmentset( - limit: string, - assessmentsetId: string, - type: string, - title: string, - gradeType: string, - request: any - ) { - var axios = require("axios"); - try { - const searchData = { - assessmentsetId, - type, - title, - gradeType, - }; - - let query = ""; - Object.keys(searchData).forEach((e) => { - if (searchData[e] && searchData[e] != "") { - query += `${e}:{_eq:"${searchData[e]}"}`; - } - }); - - var data = { - query: `query GetAssessmentset($limit:Int) { - assessmentset_aggregate { - aggregate { - count - } - } - - assessmentset(limit:$limit,where: {${query}}) { - assessmentsetId - gradeType - title - options - type - typeDetails - created_at - updated_at - } - }`, - variables: { limit: parseInt(limit) }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - let result = await this.mappedResponse(response.data.data.assessmentset); - - const count = - response?.data?.data?.assessmentset_aggregate?.aggregate?.count; - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - totalCount: count, - data: result, - }); - } catch (e) { - return `${e}`; - } - } - - public async mappedResponse(result: any) { - const assessmentSetResponse = result.map((obj: any) => { - const assessmentSetMapping = { - id: obj?.assessmentsetId ? `${obj.assessmentsetId}` : "", - assessmentsetId: obj?.assessmentsetId ? `${obj.assessmentsetId}` : "", - title: obj?.title ? `${obj.title}` : "", - type: obj?.type ? obj.type : "", - typeDetails: obj?.typeDetails ? obj.typeDetails : "", - gradeType: obj?.gradeType ? `${obj.gradeType}` : "", - options: obj?.options ? `${obj.options}` : "", - createdAt: obj?.created_at ? `${obj.created_at}` : "", - updatedAt: obj?.updated_at ? `${obj.updated_at}` : "", - }; - return new AssessmentsetDto(assessmentSetMapping); - }); - - return assessmentSetResponse; - } -} diff --git a/src/adapters/hasura/attendance.adapter.ts b/src/adapters/hasura/attendance.adapter.ts index a60ba2ae..80874673 100644 --- a/src/adapters/hasura/attendance.adapter.ts +++ b/src/adapters/hasura/attendance.adapter.ts @@ -8,7 +8,6 @@ import moment from "moment"; import jwt_decode from "jwt-decode"; import { IServicelocator } from "../attendanceservicelocator"; import { UserDto } from "src/user/dto/user.dto"; -import { StudentDto } from "src/student/dto/student.dto"; import { ErrorResponse } from "src/error-response"; import { AttendanceDateDto } from "src/attendance/dto/attendance-date.dto"; export const ShikshaAttendanceToken = "ShikshaAttendance"; @@ -18,7 +17,12 @@ export class AttendanceHasuraService implements IServicelocator { axios = require("axios"); constructor(private httpService: HttpService) {} - + public async attendanceReport(attendanceStatsDto: any) { + + } + public async updateAttendanceRecord(request: any, attendanceDto: any) { + + } public async getAttendance( tenantId: string, attendanceId: string, @@ -73,49 +77,55 @@ export class AttendanceHasuraService implements IServicelocator { } public async createAttendance(request: any, attendanceDto: AttendanceDto) { - let query = ""; - Object.keys(attendanceDto).forEach((e) => { - if (attendanceDto[e] && attendanceDto[e] != "") { - query += `${e}: "${attendanceDto[e]}", `; - } - }); + try{ + let query = ""; + Object.keys(attendanceDto).forEach((e) => { + if (attendanceDto[e] && attendanceDto[e] != "") { + query += `${e}: "${attendanceDto[e]}", `; + } + }); - const data = { - query: `mutation CreateAttendance { - insert_Attendance_one(object: {${query}}) { - attendanceId + const data = { + query: `mutation CreateAttendance { + insert_Attendance_one(object: {${query}}) { + attendanceId + } } - } - `, - variables: {}, - }; + `, + variables: {}, + }; - const config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; + const config = { + method: "post", + url: process.env.REGISTRYHASURA, + headers: { + Authorization: request.headers.authorization, + "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, + "Content-Type": "application/json", + }, + data: data, + }; - const response = await this.axios(config); + const response = await this.axios(config); - if (response?.data?.errors) { - return new ErrorResponse({ - errorCode: "400", - errorMessage: response.data.errors[0].message, - }); - } + if (response?.data?.errors) { + return new ErrorResponse({ + errorCode: "400", + errorMessage: response.data.errors[0].message, + }); + } - const result = response.data.data.insert_Attendance_one; + const result = response.data.data.insert_Attendance_one; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); + return new SuccessResponse({ + statusCode: 200, + message: "Ok.", + data: result, + }); + }catch (e) { + console.error(e); + return e; + } } public async updateAttendance( @@ -123,59 +133,65 @@ export class AttendanceHasuraService implements IServicelocator { request: any, attendanceDto: AttendanceDto ) { - const attendanceSchema = new AttendanceDto(attendanceDto); - - let query = ""; - Object.keys(attendanceDto).forEach((e) => { - if ( - attendanceDto[e] && - attendanceDto[e] != "" && - Object.keys(attendanceSchema).includes(e) - ) { - query += `${e}: "${attendanceDto[e]}", `; - } - }); + try{ + const attendanceSchema = new AttendanceDto(attendanceDto); + + let query = ""; + Object.keys(attendanceDto).forEach((e) => { + if ( + attendanceDto[e] && + attendanceDto[e] != "" && + Object.keys(attendanceSchema).includes(e) + ) { + query += `${e}: "${attendanceDto[e]}", `; + } + }); - const data = { - query: `mutation UpdateAttendance($attendanceId:uuid) { - update_Attendance(where: {attendanceId: {_eq: $attendanceId}}, _set: {${query}}) { - affected_rows - returning { - attendanceId + const data = { + query: `mutation UpdateAttendance($attendanceId:uuid) { + update_Attendance(where: {attendanceId: {_eq: $attendanceId}}, _set: {${query}}) { + affected_rows + returning { + attendanceId + } } - } - }`, - variables: { - attendanceId: attendanceId, - }, - }; + }`, + variables: { + attendanceId: attendanceId, + }, + }; - const config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; + const config = { + method: "post", + url: process.env.REGISTRYHASURA, + headers: { + Authorization: request.headers.authorization, + "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, + "Content-Type": "application/json", + }, + data: data, + }; - const response = await this.axios(config); + const response = await this.axios(config); - if (response?.data?.errors) { - return new ErrorResponse({ - errorCode: "400", - errorMessage: response.data.errors[0].message, - }); - } + if (response?.data?.errors) { + return new ErrorResponse({ + errorCode: "400", + errorMessage: response.data.errors[0].message, + }); + } - const result = response.data.data.update_Attendance; + const result = response.data.data.update_Attendance; - return new SuccessResponse({ - statusCode: 200, - message: "Ok. Updated Successfully", - data: result, - }); + return new SuccessResponse({ + statusCode: 200, + message: "Ok. Updated Successfully", + data: result, + }); + }catch (e) { + console.error(e); + return e; + } } public async searchAttendance( @@ -184,15 +200,13 @@ export class AttendanceHasuraService implements IServicelocator { attendanceSearchDto: AttendanceSearchDto ) { try{ - const decoded: any = jwt_decode(request.headers.authorization); - let offset = 0; if (attendanceSearchDto.page > 1) { offset = - parseInt(attendanceSearchDto.limit) * (attendanceSearchDto.page - 1); + (attendanceSearchDto.limit) * (attendanceSearchDto.page - 1); } - attendanceSearchDto.filters["tenantId"] = { _eq: tenantId ? tenantId : "" }; + attendanceSearchDto.filters["tenantId"] = tenantId ? tenantId : ""; Object.keys(attendanceSearchDto.filters).forEach((item) => { Object.keys(attendanceSearchDto.filters[item]).forEach((e) => { if (!e.startsWith("_")) { @@ -233,14 +247,13 @@ export class AttendanceHasuraService implements IServicelocator { } }`, variables: { - limit: parseInt(attendanceSearchDto.limit) - ? parseInt(attendanceSearchDto.limit) + limit: (attendanceSearchDto.limit) + ? (attendanceSearchDto.limit) : 10, offset: offset, filters: attendanceSearchDto.filters, }, }; - const config = { method: "post", url: process.env.REGISTRYHASURA, @@ -272,13 +285,12 @@ export class AttendanceHasuraService implements IServicelocator { totalCount: count, data: mappedResponse, }); + }catch (e) { console.error(e); - return new ErrorResponse({ - errorCode: "400", - errorMessage: e, - }); + return e; } + } public async attendanceByDate( @@ -287,8 +299,6 @@ export class AttendanceHasuraService implements IServicelocator { attendanceSearchDto: AttendanceDateDto ) { try{ - const decoded: any = jwt_decode(request.headers.authorization); - let offset = 0; if (attendanceSearchDto.page > 1) { offset = @@ -371,11 +381,8 @@ export class AttendanceHasuraService implements IServicelocator { data: mappedResponse, }); }catch (e) { - console.error(e); - return new ErrorResponse({ - errorCode: "400", - errorMessage: e, - }); + console.error(e); + return e; } } @@ -384,6 +391,7 @@ export class AttendanceHasuraService implements IServicelocator { attendanceDto: AttendanceDto ) { // Api Checks attendance by date and userId , that is daywise attendance + try { const decoded: any = jwt_decode(request.headers.authorization); const userId = @@ -393,8 +401,8 @@ export class AttendanceHasuraService implements IServicelocator { const attendanceToSearch = new AttendanceSearchDto({}); attendanceToSearch.filters = { - attendanceDate: { _eq: attendanceDto.attendanceDate }, - userId: { _eq: attendanceDto.userId }, + attendanceDate: attendanceDto.attendanceDate, // Assign the value directly + userId: attendanceDto.userId, }; const attendanceFound: any = await this.searchAttendance( @@ -424,7 +432,10 @@ export class AttendanceHasuraService implements IServicelocator { // Else - Create new entry return await this.createAttendance(request, attendanceDto); } - + } catch (e) { + console.error(e); + return e; + } } // bulk attendance api @@ -435,8 +446,7 @@ export class AttendanceHasuraService implements IServicelocator { ) { const responses = []; const errors = []; - - const decoded: any = jwt_decode(request.headers.authorization); + try { for (const attendance of attendanceData) { attendance.tenantId = tenantId; const attendanceRes: any = await this.checkAndAddAttendance( @@ -452,15 +462,17 @@ export class AttendanceHasuraService implements IServicelocator { }); } } - - return { - statusCode: 200, - totalCount: attendanceData.length, - successCount: responses.length, - responses, - errors, - }; - + } catch (e) { + console.error(e); + return e; + } + return { + statusCode: 200, + totalCount: attendanceData.length, + successCount: responses.length, + responses, + errors, + }; } public async mappedResponse(result: any) { diff --git a/src/adapters/hasura/cohort.adapter.ts b/src/adapters/hasura/cohort.adapter.ts index 1e12acd0..69bf39da 100644 --- a/src/adapters/hasura/cohort.adapter.ts +++ b/src/adapters/hasura/cohort.adapter.ts @@ -9,10 +9,10 @@ import { CohortDto } from "src/cohort/dto/cohort.dto"; import { CohortSearchDto } from "src/cohort/dto/cohort-search.dto"; import { IServicelocatorcohort } from "../cohortservicelocator"; import { UserDto } from "src/user/dto/user.dto"; -import { StudentDto } from "src/student/dto/student.dto"; import { FieldsService } from "./services/fields.service"; import { CohortCreateDto } from "src/cohort/dto/cohort-create.dto"; import { FieldValuesDto } from "src/fields/dto/field-values.dto"; +import { CohortUpdateDto } from "src/cohort/dto/cohort-update.dto"; export const HasuraCohortToken = "HasuraCohort"; @Injectable() export class HasuraCohortService implements IServicelocatorcohort { @@ -22,10 +22,11 @@ export class HasuraCohortService implements IServicelocatorcohort { private httpService: HttpService, private fieldsService: FieldsService ) {} + public async getCohortList(tenantid: any, id: any, request: any, response: any) { + } public async createCohort(request: any, cohortCreateDto: CohortCreateDto) { try{ - const decoded: any = jwt_decode(request.headers.authorization); var axios = require("axios"); let query = ""; @@ -112,10 +113,10 @@ export class HasuraCohortService implements IServicelocatorcohort { }); } } - } catch (e) { + }catch (e) { console.error(e); return new ErrorResponse({ - errorCode: "400", + errorCode: "401", errorMessage: e, }); } @@ -150,6 +151,7 @@ export class HasuraCohortService implements IServicelocatorcohort { type status image + attendanceCaptureImage metadata createdAt updatedAt @@ -259,190 +261,10 @@ export class HasuraCohortService implements IServicelocatorcohort { } } - public async searchCohort( - tenantId: string, - request: any, - cohortSearchDto: CohortSearchDto, - res: any - ) { - let entityFilter = cohortSearchDto; - let filedsFilter = entityFilter?.filters["fields"]; - //remove fields from filter - delete entityFilter.filters["fields"]; - let newCohortSearchDto = null; - //check fields value present or not - if (filedsFilter) { - //apply filter on fields value - let response_fields_value = - await this.fieldsService.searchFieldValuesFilter(request,filedsFilter); - if (response_fields_value?.data?.errors) { - return res.status(200).send({ - errorCode: response_fields_value?.data?.errors[0]?.extensions?.code, - errorMessage: response_fields_value?.data?.errors[0]?.message, - }); - } else { - //get filter result - let result_FieldValues = response_fields_value?.data?.data?.FieldValues; - //fetch cohot id list - let cohort_id_list = []; - for (let i = 0; i < result_FieldValues.length; i++) { - cohort_id_list.push(result_FieldValues[i].itemId); - } - //remove duplicate entries - cohort_id_list = cohort_id_list.filter( - (item, index) => cohort_id_list.indexOf(item) === index - ); - let cohort_filter = new Object(entityFilter.filters); - cohort_filter["cohortId"] = { - _in: cohort_id_list, - }; - newCohortSearchDto = new CohortSearchDto({ - limit: entityFilter.limit, - page: entityFilter.page, - filters: cohort_filter, - }); - } - } else { - newCohortSearchDto = new CohortSearchDto({ - limit: entityFilter.limit, - page: entityFilter.page, - filters: entityFilter.filters, - }); - } - if (newCohortSearchDto) { - const response = await this.searchCohortQuery( - request, - tenantId, - newCohortSearchDto - ); - if (response?.data?.errors) { - return res.status(200).send({ - errorCode: response?.data?.errors[0]?.extensions?.code, - errorMessage: response?.data?.errors[0]?.message, - }); - } else { - let result = response?.data?.data?.Cohort; - let cohortResponse = await this.mappedResponse(result); - //const count = cohortResponse.length; - const count = result.length; - //get cohort fields value - let result_data = await this.searchCohortFields( - tenantId, - cohortResponse - ); - return res.status(200).send({ - statusCode: 200, - message: "Ok.", - totalCount: count, - data: result_data, - }); - } - } else { - return res.status(200).send({ - errorCode: "filter invalid", - errorMessage: "filter invalid", - }); - } - } - - public async updateCohort( - cohortId: string, - request: any, - cohortUpdateDto: CohortCreateDto - ) { - var axios = require("axios"); - - let query = ""; - Object.keys(cohortUpdateDto).forEach((e) => { - if ( - cohortUpdateDto[e] && - cohortUpdateDto[e] != "" && - e != "fieldValues" - ) { - if (Array.isArray(cohortUpdateDto[e])) { - query += `${e}: "${JSON.stringify(cohortUpdateDto[e])}", `; - } else { - query += `${e}: "${cohortUpdateDto[e]}", `; - } - } - }); - - var data = { - query: ` - mutation UpdateCohort($cohortId:uuid!) { - update_Cohort_by_pk( - pk_columns: { - cohortId: $cohortId - }, - _set: { - ${query} - } - ) { - cohortId - } - } - `, - variables: { - cohortId: cohortId, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - if (response?.data?.errors) { - return new ErrorResponse({ - errorCode: response?.data?.errors[0]?.extensions?.code, - errorMessage: response?.data?.errors[0]?.message, - }); - } else { - let result = response.data.update_Cohort_by_pk; - let fieldCreate = true; - let fieldError = []; - //update fields values - let field_value_array = cohortUpdateDto.fieldValues.split("|"); - if (field_value_array.length > 0) { - for (let i = 0; i < field_value_array.length; i++) { - let fieldValues = field_value_array[i].split(":"); - //update values - let fieldValuesUpdate = new FieldValuesDto({ - value: fieldValues[1] ? fieldValues[1] : "", - }); - - const response_field_values = - await this.fieldsService.updateFieldValues( - fieldValues[0] ? fieldValues[0] : "", - fieldValuesUpdate - ); - if (response_field_values?.data?.errors) { - fieldCreate = false; - fieldError.push(response_field_values?.data); - } - } - } - if (fieldCreate) { - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } else { - return new ErrorResponse({ - errorCode: "filed value update error", - errorMessage: JSON.stringify(fieldError), - }); - } - } - } + public async searchCohort(tenantId: string,request: any,cohortSearchDto: CohortSearchDto) {} + public async updateCohortStatus(cohortId: string, request: any) {} + public async getCohortsDetails(cohortId: string) {} + public async updateCohort(cohortId: string,request: any,cohortUpdateDto: CohortUpdateDto) {} public async mappedResponse(result: any) { const cohortResponse = result.map((item: any) => { @@ -468,13 +290,14 @@ export class HasuraCohortService implements IServicelocatorcohort { return cohortResponse; } - public async searchCohortFields(tenantId: string, cohorts: any) { + public async searchCohortFields(request:any ,tenantId: string, cohorts: any) { let cohort_fields = []; for (let i = 0; i < cohorts.length; i++) { let new_obj = new Object(cohorts[i]); let cohortId = new_obj["cohortId"]; //get fields let response = await this.fieldsService.getFieldsContext( + request, tenantId, "Cohort", cohortId @@ -492,73 +315,5 @@ export class HasuraCohortService implements IServicelocatorcohort { request:any, tenantId: string, cohortSearchDto: CohortSearchDto - ) { - try{ - const decoded: any = jwt_decode(request.headers.authorization); - var axios = require("axios"); - - let offset = 0; - if (cohortSearchDto.page > 1) { - offset = parseInt(cohortSearchDto.limit) * (cohortSearchDto.page - 1); - } - - let temp_filters = cohortSearchDto.filters; - //add tenantid - let filters = new Object(temp_filters); - filters["tenantId"] = { _eq: tenantId ? tenantId : "" }; - - Object.keys(cohortSearchDto.filters).forEach((item) => { - Object.keys(cohortSearchDto.filters[item]).forEach((e) => { - if (!e.startsWith("_")) { - filters[item][`_${e}`] = filters[item][e]; - delete filters[item][e]; - } - }); - }); - var data = { - query: `query SearchCohort($filters:Cohort_bool_exp,$limit:Int, $offset:Int) { - Cohort(where:$filters, limit: $limit, offset: $offset,) { - tenantId - programId - cohortId - parentId - referenceId - name - type - status - image - metadata - createdAt - updatedAt - createdBy - updatedBy - } - }`, - variables: { - limit: parseInt(cohortSearchDto.limit), - offset: offset, - filters: cohortSearchDto.filters, - }, - }; - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - Authorization: request.headers.authorization, - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - return response; - } catch (e) { - console.error(e); - return new ErrorResponse({ - errorCode: "400", - errorMessage: e, - }); - } - } + ) {} } diff --git a/src/adapters/hasura/cohortMembers.adapter.ts b/src/adapters/hasura/cohortMembers.adapter.ts index 7f36d4e9..3c1fd56e 100644 --- a/src/adapters/hasura/cohortMembers.adapter.ts +++ b/src/adapters/hasura/cohortMembers.adapter.ts @@ -3,10 +3,10 @@ import { HttpService } from "@nestjs/axios"; import { SuccessResponse } from "src/success-response"; import { ErrorResponse } from "src/error-response"; const resolvePath = require("object-resolve-path"); -import jwt_decode from "jwt-decode"; import { CohortMembersDto } from "src/cohortMembers/dto/cohortMembers.dto"; import { CohortMembersSearchDto } from "src/cohortMembers/dto/cohortMembers-search.dto"; import { IServicelocatorcohortMembers } from "../cohortMembersservicelocator"; +import { CohortMembersUpdateDto } from "src/cohortMembers/dto/cohortMember-update.dto"; @Injectable() export class HasuraCohortMembersService @@ -18,8 +18,7 @@ export class HasuraCohortMembersService request: any, cohortMembers: CohortMembersDto ) { - try{ - const decoded: any = jwt_decode(request.headers.authorization); + try { var axios = require("axios"); let query = ""; Object.keys(cohortMembers).forEach((e) => { @@ -76,219 +75,14 @@ export class HasuraCohortMembersService } } - public async getCohortMembers( - tenantId: string, - cohortMembershipId: any, - request: any - ) { - var axios = require("axios"); - - var data = { - query: `query GetCohortMembers($cohortMembershipId:uuid!, $tenantId:uuid!) { - CohortMembers( - where:{ - tenantId:{ - _eq:$tenantId - } - cohortMembershipId:{ - _eq:$cohortMembershipId - }, - } - ){ - tenantId - cohortMembershipId - cohortId - userId - role - createdAt - updatedAt - createdBy - updatedBy - } - }`, - variables: { - cohortMembershipId: cohortMembershipId, - tenantId: tenantId, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - if (response?.data?.errors) { - return new ErrorResponse({ - errorCode: response?.data?.errors[0]?.extensions?.code, - errorMessage: response?.data?.errors[0]?.message, - }); - } else { - let result = response?.data?.data?.CohortMembers; - const cohortMembersResponse = await this.mappedResponse(result); - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: cohortMembersResponse[0], - }); - } - } - - public async searchCohortMembers( - tenantId: string, - request: any, - cohortMembersSearchDto: CohortMembersSearchDto - ) { - try{ - const decoded: any = jwt_decode(request.headers.authorization); - var axios = require("axios"); - - let offset = 0; - if (cohortMembersSearchDto.page > 1) { - offset = - parseInt(cohortMembersSearchDto.limit) * - (cohortMembersSearchDto.page - 1); - } - - let temp_filters = cohortMembersSearchDto.filters; - //add tenantid - let filters = new Object(temp_filters); - filters["tenantId"] = { _eq: tenantId ? tenantId : "" }; - - Object.keys(cohortMembersSearchDto.filters).forEach((item) => { - Object.keys(cohortMembersSearchDto.filters[item]).forEach((e) => { - if (!e.startsWith("_")) { - filters[item][`_${e}`] = filters[item][e]; - delete filters[item][e]; - } - }); - }); - var data = { - query: `query SearchCohortMembers($filters:CohortMembers_bool_exp,$limit:Int, $offset:Int) { - CohortMembers(where:$filters, limit: $limit, offset: $offset,) { - tenantId - cohortMembershipId - cohortId - userId - role - createdAt - updatedAt - createdBy - updatedBy - } - }`, - variables: { - limit: parseInt(cohortMembersSearchDto.limit), - offset: offset, - filters: cohortMembersSearchDto.filters, - }, - }; - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - Authorization: request.headers.authorization, - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - if (response?.data?.errors) { - return new ErrorResponse({ - errorCode: response?.data?.errors[0]?.extensions?.code, - errorMessage: response?.data?.errors[0]?.message, - }); - } else { - let result = response.data.data.CohortMembers; - const cohortMembersResponse = await this.mappedResponse(result); - const count = cohortMembersResponse.length; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - totalCount: count, - data: cohortMembersResponse, - }); - } - } catch (e) { - console.error(e); - return new ErrorResponse({ - errorCode: "400", - errorMessage: e, - }); - } - } - + public async searchCohortMembers(cohortMembersSearchDto, tenantId, res) {} + public async getCohortMembers(cohortMemberId, tenantId, fieldvalue, res) {} public async updateCohortMembers( cohortMembershipId: string, request: any, - cohortMembersDto: CohortMembersDto + cohortMembersUpdateDto: CohortMembersUpdateDto, + response: any ) { - var axios = require("axios"); - - let query = ""; - Object.keys(cohortMembersDto).forEach((e) => { - if (cohortMembersDto[e] && cohortMembersDto[e] != "") { - if (Array.isArray(cohortMembersDto[e])) { - query += `${e}: "${JSON.stringify(cohortMembersDto[e])}", `; - } else { - query += `${e}: "${cohortMembersDto[e]}", `; - } - } - }); - - var data = { - query: ` - mutation UpdateCohortMembers($cohortMembershipId:uuid!) { - update_CohortMembers_by_pk( - pk_columns: { - cohortMembershipId: $cohortMembershipId - }, - _set: { - ${query} - } - ) { - cohortMembershipId - } - } - `, - variables: { - cohortMembershipId: cohortMembershipId, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - if (response?.data?.errors) { - return new ErrorResponse({ - errorCode: response?.data?.errors[0]?.extensions?.code, - errorMessage: response?.data?.errors[0]?.message, - }); - } else { - let result = response.data.data; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } } public async mappedResponse(result: any) { @@ -311,4 +105,6 @@ export class HasuraCohortMembersService return cohortMembersResponse; } + + public async deleteCohortMemberById() {} } diff --git a/src/adapters/hasura/comment.adapter.ts b/src/adapters/hasura/comment.adapter.ts deleted file mode 100644 index 10a5c4be..00000000 --- a/src/adapters/hasura/comment.adapter.ts +++ /dev/null @@ -1,282 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { HttpService } from "@nestjs/axios"; -import { SuccessResponse } from "src/success-response"; -import { IServicelocator } from "../commentservicelocator"; -import { CommentDto } from "src/comment/dto/comment.dto"; -import { CommentSearchDto } from "src/comment/dto/comment-search.dto"; -import jwt_decode from "jwt-decode"; -export const HasuraCommentToken = "HasuraComment"; -@Injectable() -export class HasuraCommentService implements IServicelocator { - constructor(private httpService: HttpService) {} - userUrl = `${process.env.BASEAPIURL}/User`; - public async createComment(request: any, commentDto: CommentDto) { - var axios = require("axios"); - const authToken = request.headers.authorization; - const decoded: any = jwt_decode(authToken); - let email = decoded.email; - - let userData = { - filters: { - email: { - eq: `${email}`, - }, - }, - }; - let user = { - method: "post", - url: `${this.userUrl}/search`, - headers: { - Authorization: request.headers.authorization, - }, - data: userData, - }; - const resData = await axios(user); - const res = resData.data[0]; - commentDto.userId = res.osid; - const commentSchema = new CommentDto(commentDto); - let query = ""; - Object.keys(commentDto).forEach((e) => { - if ( - commentDto[e] && - commentDto[e] != "" && - Object.keys(commentSchema).includes(e) - ) { - if (Array.isArray(commentDto[e])) { - query += `${e}: ${JSON.stringify(commentDto[e])}, `; - } else { - query += `${e}: "${commentDto[e]}", `; - } - } - }); - - var data = { - query: `mutation CreateComment { - insert_comment_one(object: {${query}}) { - commentId - } - } - `, - variables: {}, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - const result = response.data.data.insert_comment_one; - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async updateComment(id: string, request: any, commentDto: CommentDto) { - var axios = require("axios"); - const authToken = request.headers.authorization; - const decoded: any = jwt_decode(authToken); - let email = decoded.email; - let updateData = { - filters: { - email: { - eq: `${email}`, - }, - }, - }; - let configData = { - method: "post", - url: `${this.userUrl}/search`, - headers: { - Authorization: request.headers.authorization, - }, - data: updateData, - }; - const userResponse = await axios(configData); - const resultData = userResponse.data[0]; - commentDto.userId = resultData.osid; - const commentSchema = new CommentDto(commentDto); - let query = ""; - Object.keys(commentDto).forEach((e) => { - if ( - commentDto[e] && - commentDto[e] != "" && - Object.keys(commentSchema).includes(e) - ) { - if (Array.isArray(commentDto[e])) { - query += `${e}: ${JSON.stringify(commentDto[e])}, `; - } else { - query += `${e}: "${commentDto[e]}", `; - } - } - }); - - var data = { - query: `mutation UpdateComment($commentId:uuid) { - update_comment(where: {commentId: {_eq: $commentId}}, _set: {${query}}) { - affected_rows - }}`, - variables: { - commentId: id, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - const result = response.data.data; - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async getComment(commentId: any, request: any) { - var axios = require("axios"); - - var data = { - query: `query GetComment($commentId:uuid!) { - comment_by_pk(commentId: $commentId) { - comment - commentId - context - contextId - created_at - parentId - privacy - status - updated_at - userId - } - } - `, - variables: { commentId: commentId }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - const result = await this.mappedResponse([ - response.data.data.comment_by_pk, - ]); - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result[0], - }); - } - - public async searchComment(request: any, commentSearchDto: CommentSearchDto) { - var axios = require("axios"); - - let offset = 0; - if (commentSearchDto.page > 1) { - offset = parseInt(commentSearchDto.limit) * (commentSearchDto.page - 1); - } - - let filters = commentSearchDto.filters; - - Object.keys(commentSearchDto.filters).forEach((item) => { - Object.keys(commentSearchDto.filters[item]).forEach((e) => { - if (!e.startsWith("_")) { - filters[item][`_${e}`] = filters[item][e]; - delete filters[item][e]; - } - }); - }); - var data = { - query: `query SearchComment($filters:comment_bool_exp,$limit:Int, $offset:Int) { - comment_aggregate { - aggregate { - count - } - } - comment(where:$filters, limit: $limit, offset: $offset,) { - comment - commentId - context - contextId - created_at - parentId - privacy - status - updated_at - userId - } - }`, - variables: { - limit: parseInt(commentSearchDto.limit), - offset: offset, - filters: commentSearchDto.filters, - }, - }; - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - const result = await this.mappedResponse(response.data.data.comment); - const count = response?.data?.data?.comment_aggregate?.aggregate?.count; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - totalCount: count, - data: result, - }); - } - - public async mappedResponse(result: any) { - const commentResponse = result.map((obj: any) => { - const commentMapping = { - id: obj?.commentId ? `${obj.commentId}` : "", - commentId: obj?.commentId ? `${obj.commentId}` : "", - contextId: obj?.contextId ? `${obj.contextId}` : "", - context: obj?.context ? `${obj.context}` : "", - userId: obj?.userId ? `${obj.userId}` : "", - comment: obj?.comment ? `${obj.comment}` : "", - privacy: obj?.privacy ? `${obj.privacy}` : "", - parentId: obj?.parentId ? `${obj.parentId}` : "", - status: obj?.status ? `${obj.status}` : "", - createdAt: obj?.created_at ? `${obj.created_at}` : "", - updatedAt: obj?.updated_at ? `${obj.updated_at}` : "", - }; - return new CommentDto(commentMapping); - }); - - return commentResponse; - } -} diff --git a/src/adapters/hasura/fields.adapter.ts b/src/adapters/hasura/fields.adapter.ts index 49805cc9..7f1d7ff6 100644 --- a/src/adapters/hasura/fields.adapter.ts +++ b/src/adapters/hasura/fields.adapter.ts @@ -9,7 +9,6 @@ import { FieldValuesDto } from "src/fields/dto/field-values.dto"; import { FieldValuesSearchDto } from "src/fields/dto/field-values-search.dto"; import { IServicelocatorfields } from "../fieldsservicelocator"; import { UserDto } from "src/user/dto/user.dto"; -import { StudentDto } from "src/student/dto/student.dto"; export const HasuraFieldsToken = "HasuraFields"; import { FieldsService } from "./services/fields.service"; @@ -23,26 +22,18 @@ export class HasuraFieldsService implements IServicelocatorfields { //fields public async createFields(request: any, fieldsDto: FieldsDto) { - try{ - const response = await this.fieldsService.createFields(request, fieldsDto); - if (response?.data?.errors) { - return new ErrorResponse({ - errorCode: response?.data?.errors[0]?.extensions?.code, - errorMessage: response?.data?.errors[0]?.message, - }); - } else { - const result = response.data.data.insert_Fields_one; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - } catch (e) { - console.error(e); + const response = await this.fieldsService.createFields(request,fieldsDto); + if (response?.data?.errors) { return new ErrorResponse({ - errorCode: "400", - errorMessage: e, + errorCode: response?.data?.errors[0]?.extensions?.code, + errorMessage: response?.data?.errors[0]?.message, + }); + } else { + const result = response.data.data.insert_Fields_one; + return new SuccessResponse({ + statusCode: 200, + message: "Ok.", + data: result, }); } } @@ -70,35 +61,27 @@ export class HasuraFieldsService implements IServicelocatorfields { request: any, fieldsSearchDto: FieldsSearchDto ) { - try{ - const response = await this.fieldsService.searchFields( - request, - tenantId, - fieldsSearchDto - ); - if (response?.data?.errors) { - return new ErrorResponse({ - errorCode: response?.data?.errors[0]?.extensions?.code, - errorMessage: response?.data?.errors[0]?.message, - }); - } else { - let result = response.data.data.Fields; - const fieldsResponse = await this.mappedResponse(result); - const count = fieldsResponse.length; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - totalCount: count, - data: fieldsResponse, - }); - } - } catch (e) { - console.error(e); - return new ErrorResponse({ - errorCode: "400", - errorMessage: e, - }); - } + const response = await this.fieldsService.searchFields( + request, + tenantId, + fieldsSearchDto + ); + if (response?.data?.errors) { + return new ErrorResponse({ + errorCode: response?.data?.errors[0]?.extensions?.code, + errorMessage: response?.data?.errors[0]?.message, + }); + } else { + let result = response.data.data.Fields; + const fieldsResponse = await this.mappedResponse(result); + const count = fieldsResponse.length; + return new SuccessResponse({ + statusCode: 200, + message: "Ok.", + totalCount: count, + data: fieldsResponse, + }); + } } public async updateFields( @@ -124,7 +107,7 @@ export class HasuraFieldsService implements IServicelocatorfields { //field values public async createFieldValues(request: any, fieldValuesDto: FieldValuesDto) { - const response = await this.fieldsService.createFieldValues(request, fieldValuesDto); + const response = await this.fieldsService.createFieldValues(request,fieldValuesDto); if (response?.data?.errors) { return new ErrorResponse({ errorCode: response?.data?.errors[0]?.extensions?.code, @@ -162,32 +145,24 @@ export class HasuraFieldsService implements IServicelocatorfields { request: any, fieldValuesSearchDto: FieldValuesSearchDto ) { - try{ - const response = await this.fieldsService.searchFieldValues( - request, - fieldValuesSearchDto - ); - if (response?.data?.errors) { - return new ErrorResponse({ - errorCode: response?.data?.errors[0]?.extensions?.code, - errorMessage: response?.data?.errors[0]?.message, - }); - } else { - let result = response.data.data.FieldValues; - const fieldValuesResponse = await this.mappedResponseValues(result); - const count = fieldValuesResponse.length; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - totalCount: count, - data: fieldValuesResponse, - }); - } - } catch (e) { - console.error(e); + const response = await this.fieldsService.searchFieldValues( + request, + fieldValuesSearchDto + ); + if (response?.data?.errors) { return new ErrorResponse({ - errorCode: "400", - errorMessage: e, + errorCode: response?.data?.errors[0]?.extensions?.code, + errorMessage: response?.data?.errors[0]?.message, + }); + } else { + let result = response.data.data.FieldValues; + const fieldValuesResponse = await this.mappedResponseValues(result); + const count = fieldValuesResponse.length; + return new SuccessResponse({ + statusCode: 200, + message: "Ok.", + totalCount: count, + data: fieldValuesResponse, }); } } diff --git a/src/adapters/hasura/group.adapter.ts b/src/adapters/hasura/group.adapter.ts deleted file mode 100644 index 408d4eb5..00000000 --- a/src/adapters/hasura/group.adapter.ts +++ /dev/null @@ -1,649 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { GroupInterface } from "../../group/interfaces/group.interface"; -import { HttpService } from "@nestjs/axios"; -import { SuccessResponse } from "src/success-response"; -const resolvePath = require("object-resolve-path"); -import { GroupDto } from "src/group/dto/group.dto"; -import { GroupSearchDto } from "src/group/dto/group-search.dto"; -import { IServicelocatorgroup } from "../groupservicelocator"; -import { UserDto } from "src/user/dto/user.dto"; -import { StudentDto } from "src/student/dto/student.dto"; -export const HasuraGroupToken = "HasuraGroup"; -@Injectable() -export class HasuraGroupService implements IServicelocatorgroup { - private group: GroupInterface; - - constructor(private httpService: HttpService) {} - - url = `${process.env.BASEAPIURL}`; - - public async getGroup(groupId: any, request: any) { - var axios = require("axios"); - - var data = { - query: `query GetGroup($groupId:uuid!) { - group_by_pk(groupId: $groupId) { - groupId - deactivationReason - created_at - image - mediumOfInstruction - metaData - name - option - schoolId - section - teacherId - gradeLevel - status - type - updated_at - parentId - } - }`, - variables: { - groupId: groupId, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - let result = [response?.data?.data?.group_by_pk]; - const groupResponse = await this.mappedResponse(result); - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: groupResponse[0], - }); - } - - public async createGroup(request: any, groupDto: GroupDto) { - var axios = require("axios"); - - let query = ""; - Object.keys(groupDto).forEach((e) => { - if (groupDto[e] && groupDto[e] != "") { - if (Array.isArray(groupDto[e])) { - query += `${e}: ${JSON.stringify(groupDto[e])}, `; - } else { - query += `${e}: "${groupDto[e]}", `; - } - } - }); - - var data = { - query: `mutation CreateGroup { - insert_group_one(object: {${query}}) { - groupId - } - } - `, - variables: {}, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - const result = response.data.data.insert_group_one; - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async updateGroup(groupId: string, request: any, groupDto: GroupDto) { - var axios = require("axios"); - - let query = ""; - Object.keys(groupDto).forEach((e) => { - if (groupDto[e] && groupDto[e] != "") { - if (Array.isArray(groupDto[e])) { - query += `${e}: ${JSON.stringify(groupDto[e])}, `; - } else { - query += `${e}: ${groupDto[e]}, `; - } - } - }); - - var data = { - query: `mutation UpdateGroup($groupId:uuid) { - update_group(where: {groupId: {_eq: $groupId}}, _set: {${query}}) { - affected_rows - } -}`, - variables: { - groupId: groupId, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - const result = response.data.data; - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async searchGroup(request: any, groupSearchDto: GroupSearchDto) { - var axios = require("axios"); - - let offset = 0; - if (groupSearchDto.page > 1) { - offset = parseInt(groupSearchDto.limit) * (groupSearchDto.page - 1); - } - - let filters = groupSearchDto.filters; - - Object.keys(groupSearchDto.filters).forEach((item) => { - Object.keys(groupSearchDto.filters[item]).forEach((e) => { - if (!e.startsWith("_")) { - filters[item][`_${e}`] = filters[item][e]; - delete filters[item][e]; - } - }); - }); - var data = { - query: `query SearchGroup($filters:group_bool_exp,$limit:Int, $offset:Int) { - group_aggregate { - aggregate { - count - } - } - group(where:$filters, limit: $limit, offset: $offset,) { - groupId - deactivationReason - created_at - image - mediumOfInstruction - metaData - name - option - schoolId - section - status - teacherId - gradeLevel - type - updated_at - parentId - } - }`, - variables: { - limit: parseInt(groupSearchDto.limit), - offset: offset, - filters: groupSearchDto.filters, - }, - }; - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - let result = response.data.data.group; - const groupResponse = await this.mappedResponse(result); - const count = response?.data?.data?.group_aggregate?.aggregate?.count; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - totalCount: count, - data: groupResponse, - }); - } - - public async findMembersOfGroup(groupId: string, role: string, request: any) { - let axios = require("axios"); - let userData = []; - var findMember = { - query: `query GetGroupMembership($groupId:uuid,$role:String) { - groupmembership(where: {groupId: {_eq: $groupId}, role: {_eq: $role}}) { - userId - role - } - }`, - variables: { - groupId: groupId, - role: role, - }, - }; - - var getMemberData = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: findMember, - }; - - const response = await axios(getMemberData); - let result = response.data.data.groupmembership; - if (Array.isArray(result)) { - let userIds = result.map((e: any) => { - return e.userId; - }); - if (result[0].role == "Student") { - for (let value of userIds) { - let studentSearch = { - method: "get", - url: `${this.url}/Student/${value}`, - headers: { - Authorization: request.headers.authorization, - }, - }; - - const response = await axios(studentSearch); - let responseData = await this.StudentMappedResponse([response.data]); - let studentData = responseData[0]; - - userData.push(studentData); - } - } else { - for (let value of userIds) { - let classFinal = { - method: "get", - url: `${this.url}/User/${value}`, - headers: { - Authorization: request.headers.authorization, - }, - }; - - const responseData = await axios(classFinal); - - let response = await this.userMappedResponse([responseData.data]); - let teacherDetailDto = response[0]; - userData.push(teacherDetailDto); - } - } - - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: userData, - }); - } else { - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: { msg: "Unable to get data !!" }, - }); - } - } - - public async findGroupsByUserId(userId: string, role: string, request: any) { - let axios = require("axios"); - var findMember = { - query: `query GetGroup($userId:String,$role:String) { - groupmembership(where: {userId: {_eq: $userId}, role: {_eq: $role}}) { - group { - created_at - deactivationReason - gradeLevel - groupId - image - mediumOfInstruction - metaData - name - option - schoolId - section - status - teacherId - type - updated_at - parentId - } - } - } - `, - variables: { - userId: userId, - role: role, - }, - }; - - var getMemberData = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: findMember, - }; - const response = await axios(getMemberData); - let groupData = response.data.data.groupmembership; - const groupList = groupData.map((e: any) => { - return e.group; - }); - - const groupResponse = await this.mappedResponse(groupList); - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: groupResponse, - }); - } - - public async findMembersOfChildGroup( - parentId: string, - role: string, - request: any - ) { - let axios = require("axios"); - let userData = []; - let userIds = []; - var findParentId = { - query: `query GetGroupParentId($parentId:String) { - group(where: {parentId: {_eq: $parentId}}) { - groupId - } - }`, - variables: { - parentId: parentId, - }, - }; - - var getParentId = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: findParentId, - }; - - const groupResponse = await axios(getParentId); - let groupIds = groupResponse.data.data.group.map((e: any) => { - return e.groupId; - }); - - var findMember = { - query: `query GetGroupMembership($groupIds:[uuid!],$role:String) { - groupmembership(where: {groupId: {_in:$groupIds},role: {_eq:$role }}) { - userId - role - } - }`, - variables: { - groupIds, - role: role, - }, - }; - - var getMemberData = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: findMember, - }; - - const response = await axios(getMemberData); - let result = await response.data.data.groupmembership; - - result.map((e: any) => { - return userIds.push(e.userId); - }); - for (let userId of userIds) { - if (role == "Student") { - let studentSearch = { - method: "get", - url: `${this.url}/Student/${userId}`, - headers: { - Authorization: request.headers.authorization, - }, - }; - - const response = await axios(studentSearch); - - let responseData = await this.StudentMappedResponse([response.data]); - let studentData = responseData[0]; - - userData.push(studentData); - } else { - let classFinal = { - method: "get", - url: `${this.url}/User/${userId}`, - headers: { - Authorization: request.headers.authorization, - }, - }; - - const responseData = await axios(classFinal); - - let response = await this.userMappedResponse([responseData.data]); - let teacherDetailDto = response[0]; - userData.push(teacherDetailDto); - } - } - - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: userData, - }); - } - - public async mappedResponse(result: any) { - const groupResponse = result.map((item: any) => { - const groupMapping = { - id: item?.groupId ? `${item.groupId}` : "", - groupId: item?.groupId ? `${item.groupId}` : "", - schoolId: item?.schoolId ? `${item.schoolId}` : "", - name: item?.name ? `${item.name}` : "", - type: item?.type ? `${item.type}` : "", - section: item?.section ? `${item.section}` : "", - status: item?.status ? `${item.status}` : "", - deactivationReason: item?.deactivationReason - ? `${item.deactivationReason}` - : "", - mediumOfInstruction: item?.mediumOfInstruction - ? `${item.mediumOfInstruction}` - : "", - teacherId: item?.teacherId ? `${item.teacherId}` : "", - parentId: item?.parentId ? `${item.parentId}` : "", - image: item?.image ? `${item.image}` : "", - metaData: item?.metaData ? item.metaData : [], - option: item?.option ? item.option : [], - gradeLevel: item?.gradeLevel ? `${item.gradeLevel}` : "", - createdAt: item?.created_at ? `${item.created_at}` : "", - updatedAt: item?.updated_at ? `${item.updated_at}` : "", - }; - return new GroupDto(groupMapping); - }); - - return groupResponse; - } - public async StudentMappedResponse(result: any) { - const studentResponse = result.map((item: any) => { - const studentMapping = { - studentId: item?.osid ? `${item.osid}` : "", - refId1: item?.admissionNo ? `${item.admissionNo}` : "", - refId2: item?.refId2 ? `${item.refId2}` : "", - aadhaar: item?.aadhaar ? `${item.aadhaar}` : "", - firstName: item?.firstName ? `${item.firstName}` : "", - middleName: item?.middleName ? `${item.middleName}` : "", - lastName: item?.lastName ? `${item.lastName}` : "", - groupId: item?.groupId ? `${item.groupId}` : "", - schoolId: item?.schoolId ? `${item.schoolId}` : "", - studentEmail: item?.studentEmail ? `${item.studentEmail}` : "", - studentPhoneNumber: item?.studentPhoneNumber - ? item.studentPhoneNumber - : "", - iscwsn: item?.iscwsn ? `${item.iscwsn}` : "", - gender: item?.gender ? `${item.gender}` : "", - socialCategory: item?.socialCategory ? `${item.socialCategory}` : "", - religion: item?.religion ? `${item.religion}` : "", - singleGirl: item?.singleGirl ? item.singleGirl : "", - weight: item?.weight ? `${item.weight}` : "", - height: item?.height ? `${item.height}` : "", - bloodGroup: item?.bloodGroup ? `${item.bloodGroup}` : "", - birthDate: item?.birthDate ? `${item.birthDate}` : "", - homeless: item?.homeless ? item.homeless : "", - bpl: item?.bpl ? item.bpl : "", - migrant: item?.migrant ? item.migrant : "", - status: item?.status ? `${item.status}` : "", - - fatherFirstName: item?.fatherFirstName ? `${item.fatherFirstName}` : "", - - fatherMiddleName: item?.fatherMiddleName - ? `${item.fatherMiddleName}` - : "", - - fatherLastName: item?.fatherLastName ? `${item.fatherLastName}` : "", - fatherPhoneNumber: item?.fatherPhoneNumber - ? item.fatherPhoneNumber - : "", - fatherEmail: item?.fatherEmail ? `${item.fatherEmail}` : "", - - motherFirstName: item?.motherFirstName ? `${item.motherFirstName}` : "", - motherMiddleName: item?.motherMiddleName - ? `${item.motherMiddleName}` - : "", - motherLastName: item?.motherLastName ? `${item.motherLastName}` : "", - motherPhoneNumber: item?.motherPhoneNumber - ? item.motherPhoneNumber - : "", - motherEmail: item?.motherEmail ? `${item.motherEmail}` : "", - - guardianFirstName: item?.guardianFirstName - ? `${item.guardianFirstName}` - : "", - guardianMiddleName: item?.guardianMiddleName - ? `${item.guardianMiddleName}` - : "", - guardianLastName: item?.guardianLastName - ? `${item.guardianLastName}` - : "", - guardianPhoneNumber: item?.guardianPhoneNumber - ? item.guardianPhoneNumber - : "", - guardianEmail: item?.guardianEmail ? `${item.guardianEmail}` : "", - image: item?.image ? `${item.image}` : "", - deactivationReason: item?.deactivationReason - ? `${item.deactivationReason}` - : "", - studentAddress: item?.studentAddress ? `${item.studentAddress}` : "", - village: item?.village ? `${item.village}` : "", - block: item?.block ? `${item.block}` : "", - district: item?.district ? `${item.district}` : "", - stateId: item?.stateId ? `${item.stateId}` : "", - pincode: item?.pincode ? item.pincode : "", - locationId: item?.locationId ? `${item.locationId}` : "", - metaData: item?.metaData ? item.metaData : [], - createdAt: item?.osCreatedAt ? `${item.osCreatedAt}` : "", - updatedAt: item?.osUpdatedAt ? `${item.osUpdatedAt}` : "", - createdBy: item?.osCreatedBy ? `${item.osCreatedBy}` : "", - updatedBy: item?.osUpdatedBy ? `${item.osUpdatedBy}` : "", - }; - return new StudentDto(studentMapping); - }); - - return studentResponse; - } - - public async userMappedResponse(result: any) { - const userResponse = result.map((item: any) => { - const userMapping = { - userId: item?.osid ? `${item.osid}` : "", - refId1: item?.refId1 ? `${item.refId1}` : "", - refId2: item?.refId2 ? `${item.refId2}` : "", - refId3: item?.refId3 ? `${item.refId3}` : "", - firstName: item?.firstName ? `${item.firstName}` : "", - middleName: item?.middleName ? `${item.middleName}` : "", - lastName: item?.lastName ? `${item.lastName}` : "", - phoneNumber: item?.phoneNumber ? `${item.phoneNumber}` : "", - email: item?.email ? `${item.email}` : "", - aadhaar: item?.aadhaar ? `${item.aadhaar}` : "", - gender: item?.gender ? `${item.gender}` : "", - socialCategory: item?.socialCategory ? `${item.socialCategory}` : "", - birthDate: item?.birthDate ? `${item.birthDate}` : "", - designation: item?.designation ? `${item.designation}` : "", - cadre: item?.cadre ? `${item.cadre}` : "", - profQualification: item?.profQualification - ? `${item.profQualification}` - : "", - joiningDate: item?.joiningDate ? `${item.joiningDate}` : "", - subjectIds: item.subjectIds ? item.subjectIds : [], - bloodGroup: item?.bloodGroup ? `${item.bloodGroup}` : "", - maritalStatus: item?.maritalStatus ? `${item.maritalStatus}` : "", - compSkills: item?.compSkills ? `${item.compSkills}` : "", - disability: item?.disability ? `${item.disability}` : "", - religion: item?.religion ? `${item.religion}` : "", - homeDistance: item?.homeDistance ? `${item.homeDistance}` : "", - employmentType: item?.employmentType ? `${item.employmentType}` : "", - schoolId: item?.schoolId ? `${item.schoolId}` : "", - address: item?.address ? `${item.address}` : "", - village: item?.village ? `${item.village}` : "", - block: item?.block ? `${item.block}` : "", - district: item?.district ? `${item.district}` : "", - stateId: item?.stateId ? `${item.stateId}` : "", - pincode: item?.pincode ? item.pincode : "", - locationId: item?.locationId ? `${item.locationId}` : "", - image: item?.image ? `${item.image}` : "", - status: item?.status ? `${item.status}` : "", - deactivationReason: item?.deactivationReason - ? `${item.deactivationReason}` - : "", - reportsTo: item?.reportsTo ? `${item.reportsTo}` : "", - retirementDate: item?.retirementDate ? `${item.retirementDate}` : "", - workingStatus: item?.workingStatus ? `${item.workingStatus}` : "", - fcmToken: item?.fcmToken ? `${item.fcmToken}` : "", - role: item?.role ? `${item.role}` : "", - employeeCode: item?.employeeCode ? `${item.employeeCode}` : "", - metaData: item?.metaData ? item.metaData : [], - createdAt: item?.osCreatedAt ? `${item.osCreatedAt}` : "", - updatedAt: item?.osUpdatedAt ? `${item.osUpdatedAt}` : "", - createdBy: item?.osCreatedBy ? `${item.osCreatedBy}` : "", - updatedBy: item?.osUpdatedBy ? `${item.osUpdatedBy}` : "", - }; - return new UserDto(userMapping); - }); - - return userResponse; - } -} diff --git a/src/adapters/hasura/groupMembership.adapter.ts b/src/adapters/hasura/groupMembership.adapter.ts deleted file mode 100644 index 5f8fd84b..00000000 --- a/src/adapters/hasura/groupMembership.adapter.ts +++ /dev/null @@ -1,240 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { HttpService } from "@nestjs/axios"; -import { SuccessResponse } from "src/success-response"; -const resolvePath = require("object-resolve-path"); -import { GroupMembershipDto } from "src/groupMembership/dto/groupMembership.dto"; -import { GroupMembershipSearchDto } from "src/groupMembership/dto/groupMembership-search.dto"; - -@Injectable() -export class GroupMembershipService { - constructor(private httpService: HttpService) {} - - public async getGroupMembership(groupMembershipId: any, request: any) { - var axios = require("axios"); - - var data = { - query: `query GetGroupMembership($groupMembershipId:uuid!) { - groupmembership_by_pk(groupMembershipId: $groupMembershipId) { - created_at - groupId - groupMembershipId - schoolId - role - updated_at - userId - } - }`, - variables: { - groupMembershipId: groupMembershipId, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - let result = [response?.data?.data?.groupmembership_by_pk]; - let groupMembershipResponse = await this.mappedResponse(result); - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: groupMembershipResponse[0], - }); - } - - public async createGroupMembership( - request: any, - groupMembership: GroupMembershipDto - ) { - var axios = require("axios"); - - let query = ""; - Object.keys(groupMembership).forEach((e) => { - if (groupMembership[e] && groupMembership[e] != "") { - if (Array.isArray(groupMembership[e])) { - query += `${e}: ${JSON.stringify(groupMembership[e])}, `; - } else { - query += `${e}: "${groupMembership[e]}", `; - } - } - }); - - var data = { - query: `mutation CreateGroupMembership { - insert_groupmembership_one(object: {${query}}) { - groupMembershipId - } - } - `, - variables: {}, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - const result = response.data.data.insert_groupmembership_one; - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async updateGroupMembership( - groupMembershipId: string, - request: any, - groupMembershipDto: GroupMembershipDto - ) { - var axios = require("axios"); - - let query = ""; - Object.keys(groupMembershipDto).forEach((e) => { - if (groupMembershipDto[e] && groupMembershipDto[e] != "") { - if (Array.isArray(groupMembershipDto[e])) { - query += `${e}: ${JSON.stringify(groupMembershipDto[e])}, `; - } else { - query += `${e}: ${groupMembershipDto[e]}, `; - } - } - }); - - var data = { - query: `mutation UpdateGroupMembership($groupMembershipId:uuid) { - update_groupmembership(where: { groupMembershipId: {_eq: $ groupMembershipId}}, _set: {${query}}) { - affected_rows - } -}`, - variables: { - groupMembershipId: groupMembershipId, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - const result = response.data.data; - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async searchGroupMembership( - request: any, - groupMembershipSearchDto: GroupMembershipSearchDto - ) { - var axios = require("axios"); - - let offset = 0; - if (groupMembershipSearchDto.page > 1) { - offset = - parseInt(groupMembershipSearchDto.limit) * - (groupMembershipSearchDto.page - 1); - } - - let filters = groupMembershipSearchDto.filters; - - Object.keys(groupMembershipSearchDto.filters).forEach((item) => { - Object.keys(groupMembershipSearchDto.filters[item]).forEach((e) => { - if (!e.startsWith("_")) { - filters[item][`_${e}`] = filters[item][e]; - delete filters[item][e]; - } - }); - }); - var data = { - query: `query SearchGroupMembership($filters:groupmembership_bool_exp,$limit:Int, $offset:Int) { - groupmembership_aggregate { - aggregate { - count - } - } - groupmembership(where:$filters, limit: $limit, offset: $offset,) { - created_at - groupId - groupMembershipId - schoolId - role - updated_at - userId - } - }`, - variables: { - limit: parseInt(groupMembershipSearchDto.limit), - offset: offset, - filters: groupMembershipSearchDto.filters, - }, - }; - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - let result = response.data.data.groupmembership; - let groupMembershipResponse = await this.mappedResponse(result); - const count = - response?.data?.data?.groupmembership_aggregate?.aggregate?.count; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - totalCount: count, - data: groupMembershipResponse, - }); - } - - public async mappedResponse(result: any) { - const groupMembershipResponse = result.map((obj: any) => { - const groupMembershipMapping = { - id: obj?.groupMembershipId ? `${obj.groupMembershipId}` : "", - groupMembershipId: obj?.groupMembershipId - ? `${obj.groupMembershipId}` - : "", - groupId: obj?.groupId ? `${obj.groupId}` : "", - schoolId: obj?.schoolId ? `${obj.schoolId}` : "", - userId: obj?.userId ? `${obj.userId}` : "", - role: obj?.role ? `${obj.role}` : "", - created_at: obj?.created_at ? `${obj.created_at}` : "", - updated_at: obj?.updated_at ? `${obj.updated_at}` : "", - }; - return new GroupMembershipDto(groupMembershipMapping); - }); - - return groupMembershipResponse; - } -} diff --git a/src/adapters/hasura/hasura.module.ts b/src/adapters/hasura/hasura.module.ts index b050363e..a9f65f8c 100644 --- a/src/adapters/hasura/hasura.module.ts +++ b/src/adapters/hasura/hasura.module.ts @@ -1,12 +1,7 @@ import { HttpModule } from "@nestjs/axios"; import { Module } from "@nestjs/common"; import { AttendanceHasuraService } from "./attendance.adapter"; -import { HasuraCommentService } from "./comment.adapter"; import { HasuraConfigService } from "./config.adapter"; -import { HasuraGroupService } from "./group.adapter"; -import { HasuraHolidayService } from "./holiday.adapter"; -import { HasuraLikeService } from "./like.adapter"; -import { SchoolHasuraService } from "./school.adapter"; import { HasuraCohortService } from "./cohort.adapter"; import { HasuraCohortMembersService } from "./cohortMembers.adapter"; import { HasuraFieldsService } from "./fields.adapter"; @@ -17,30 +12,20 @@ import { HasuraUserService } from "./user.adapter"; imports: [HttpModule], providers: [ AttendanceHasuraService, - SchoolHasuraService, - HasuraGroupService, HasuraCohortService, HasuraCohortMembersService, - HasuraCommentService, HasuraConfigService, - HasuraLikeService, - HasuraHolidayService, HasuraFieldsService, FieldsService, - HasuraUserService + HasuraUserService, ], exports: [ AttendanceHasuraService, - SchoolHasuraService, - HasuraGroupService, HasuraCohortService, HasuraCohortMembersService, - HasuraCommentService, HasuraConfigService, - HasuraLikeService, - HasuraHolidayService, HasuraFieldsService, - HasuraUserService + HasuraUserService, ], }) export class HasuraModule {} diff --git a/src/adapters/hasura/holiday.adapter.ts b/src/adapters/hasura/holiday.adapter.ts deleted file mode 100644 index 99d82637..00000000 --- a/src/adapters/hasura/holiday.adapter.ts +++ /dev/null @@ -1,290 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { HttpService } from "@nestjs/axios"; -import { SuccessResponse } from "src/success-response"; - -import { IServicelocator } from "../holidayservicelocator"; -import { HolidayDto } from "src/holiday/dto/holiday.dto"; -import { HolidaySearchDto } from "src/holiday/dto/holiday-search.dto"; -export const HasuraHolidayToken = "HasuraHoliday"; -@Injectable() -export class HasuraHolidayService implements IServicelocator { - constructor(private httpService: HttpService) {} - - public async createHoliday(request: any, holidayDto: HolidayDto) { - var axios = require("axios"); - - const holidaySchema = new HolidayDto(holidayDto); - let query = ""; - Object.keys(holidayDto).forEach((e) => { - if ( - holidayDto[e] && - holidayDto[e] != "" && - Object.keys(holidaySchema).includes(e) - ) { - if (Array.isArray(holidayDto[e])) { - query += `${e}: ${JSON.stringify(holidayDto[e])}, `; - } else { - query += `${e}: "${holidayDto[e]}", `; - } - } - }); - - var data = { - query: `mutation CreateHoliday { - insert_holiday_one(object: {${query}}) { - holidayId - } - } - `, - variables: {}, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - const result = response.data.data.insert_holiday_one; - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async updateHoliday(id: string, request: any, holidayDto: HolidayDto) { - var axios = require("axios"); - - const holidaySchema = new HolidayDto(holidayDto); - let query = ""; - Object.keys(holidayDto).forEach((e) => { - if ( - holidayDto[e] && - holidayDto[e] != "" && - Object.keys(holidaySchema).includes(e) - ) { - if (Array.isArray(holidayDto[e])) { - query += `${e}: ${JSON.stringify(holidayDto[e])}, `; - } else { - query += `${e}: "${holidayDto[e]}", `; - } - } - }); - - var data = { - query: `mutation UpdateHoliday($holidayId:uuid) { - update_holiday(where: {holidayId: {_eq: $holidayId}}, _set: {${query}}) { - affected_rows - }}`, - variables: { - holidayId: id, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - const result = response.data.data; - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async getHoliday(holidayId: any, request: any) { - var axios = require("axios"); - - var data = { - query: `query GetHoliday($holidayId:uuid!) { - holiday_by_pk(holidayId: $holidayId) { - context - contextId - created_at - date - holidayId - remark - updated_at - year - } - } - `, - variables: { holidayId: holidayId }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - let result = [response.data.data.holiday_by_pk]; - const holidayData = await this.mappedResponse(result); - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: holidayData[0], - }); - } - - public async searchHoliday(request: any, holidaySearchDto: HolidaySearchDto) { - var axios = require("axios"); - - let offset = 0; - if (holidaySearchDto.page > 1) { - offset = parseInt(holidaySearchDto.limit) * (holidaySearchDto.page - 1); - } - - let filters = holidaySearchDto.filters; - - Object.keys(holidaySearchDto.filters).forEach((item) => { - Object.keys(holidaySearchDto.filters[item]).forEach((e) => { - if (!e.startsWith("_")) { - filters[item][`_${e}`] = filters[item][e]; - delete filters[item][e]; - } - }); - }); - var data = { - query: `query SearchHoliday($filters:holiday_bool_exp,$limit:Int, $offset:Int) { - holiday_aggregate { - aggregate { - count - } - } - holiday(where:$filters, limit: $limit, offset: $offset,) { - context - contextId - created_at - date - holidayId - remark - updated_at - year - } - }`, - variables: { - limit: parseInt(holidaySearchDto.limit), - offset: offset, - filters: holidaySearchDto.filters, - }, - }; - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - let result = response.data.data.holiday; - const holidayData = await this.mappedResponse(result); - const count = response?.data?.data?.holiday_aggregate?.aggregate?.count; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - totalCount: count, - data: holidayData, - }); - } - - public async holidayFilter(fromDate: string, toDate: string, request: any) { - let axios = require("axios"); - - let searchData = { - fromDate, - toDate, - }; - - let query = `date:{_gte: "${searchData.fromDate}"}, _and: {date: {_lte: "${searchData.toDate}"}} `; - - var data = { - query: `query searchHoliday { - holiday_aggregate { - aggregate { - count - } - } - holiday( where: {${query}}) { - context - contextId - created_at - date - holidayId - remark - updated_at - year - } -}`, - variables: {}, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - let result = response.data.data.holiday; - const holidayData = await this.mappedResponse(result); - const count = response?.data?.data?.holiday_aggregate?.aggregate?.count; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - totalCount: count, - data: holidayData, - }); - } - - public async mappedResponse(result: any) { - const holidayResponse = result.map((item: any) => { - const holidayMapping = { - id: item?.holidayId ? `${item.holidayId}` : "", - holidayId: item?.holidayId ? `${item.holidayId}` : "", - date: item?.remark ? item.remark : "", - remark: item?.remark ? `${item.remark}` : "", - year: item?.year ? item.year : "", - context: item?.context ? `${item.context}` : "", - contextId: item?.contextId ? `${item.contextId}` : "", - createdAt: item?.created_at ? `${item.created_at}` : "", - updatedAt: item?.updated_at ? `${item.updated_at}` : "", - }; - return new HolidayDto(holidayMapping); - }); - - return holidayResponse; - } -} diff --git a/src/adapters/hasura/like.adapter.ts b/src/adapters/hasura/like.adapter.ts deleted file mode 100644 index 0a6c1ecb..00000000 --- a/src/adapters/hasura/like.adapter.ts +++ /dev/null @@ -1,345 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { HttpService } from "@nestjs/axios"; -import { SuccessResponse } from "src/success-response"; -import { LikeDto } from "src/like/dto/like.dto"; -import { LikeSearchDto } from "src/like/dto/like-search.dto"; -import { IServicelocator } from "../likeservicelocator"; -import jwt_decode from "jwt-decode"; -export const HasuraLikeToken = "HasuraLike"; -@Injectable() -export class HasuraLikeService implements IServicelocator { - constructor(private httpService: HttpService) {} - userUrl = `${process.env.BASEAPIURL}/User`; - - public async createLike(request: any, likeDto: LikeDto) { - const authToken = request.headers.authorization; - const decoded: any = jwt_decode(authToken); - let email = decoded.email; - let axios = require("axios"); - let userData = { - filters: { - email: { - eq: `${email}`, - }, - }, - }; - let searchData = { - method: "post", - url: `${this.userUrl}/search`, - headers: { - Authorization: request.headers.authorization, - }, - data: userData, - }; - const resData = await axios(searchData); - const resultData = resData.data[0]; - likeDto.userId = resultData.osid; - const likeSchema = new LikeDto(likeDto); - - let query = ""; - Object.keys(likeDto).forEach((e) => { - if ( - likeDto[e] && - likeDto[e] != "" && - Object.keys(likeSchema).includes(e) - ) { - if (Array.isArray(likeDto[e])) { - query += `${e}: ${JSON.stringify(likeDto[e])}, `; - } else { - query += `${e}: "${likeDto[e]}", `; - } - } - }); - - var data = { - query: `mutation CreateLike { - insert_like_one(object: {${query}}) { - likeId - } - } - `, - variables: {}, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - const result = response.data.data.insert_like_one; - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async updateLike(id: string, request: any, likeDto: LikeDto) { - var axios = require("axios"); - const authToken = request.headers.authorization; - const decoded: any = jwt_decode(authToken); - let email = decoded.email; - let updateData = { - filters: { - email: { - eq: `${email}`, - }, - }, - }; - let configData = { - method: "post", - url: `${this.userUrl}/search`, - headers: { - Authorization: request.headers.authorization, - }, - data: updateData, - }; - const userResponse = await axios(configData); - const resultData = userResponse.data[0]; - likeDto.userId = resultData.osid; - const likeSchema = new LikeDto(likeDto); - let query = ""; - Object.keys(likeDto).forEach((e) => { - if ( - likeDto[e] && - likeDto[e] != "" && - Object.keys(likeSchema).includes(e) - ) { - if (Array.isArray(likeDto[e])) { - query += `${e}: ${JSON.stringify(likeDto[e])}, `; - } else { - query += `${e}: "${likeDto[e]}", `; - } - } - }); - - var data = { - query: `mutation UpdateLike($likeId:uuid) { - update_like(where: {likeId: {_eq: $likeId}}, _set: {${query}}) { - affected_rows - }}`, - variables: { - likeId: id, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - const result = response.data.data; - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async getLike(likeId: any, request: any) { - var axios = require("axios"); - - var data = { - query: `query GetLike($likeId:uuid!) { - like_by_pk(likeId: $likeId) { - userId - updated_at - type - likeId - created_at - contextId - context - } - } - `, - variables: { likeId: likeId }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - let result = [response.data.data.like_by_pk]; - const likeDto = await this.mappedResponse(result); - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: likeDto[0], - }); - } - - public async searchLike(request: any, likeSearchDto: LikeSearchDto) { - var axios = require("axios"); - - let offset = 0; - if (likeSearchDto.page > 1) { - offset = parseInt(likeSearchDto.limit) * (likeSearchDto.page - 1); - } - - let filters = likeSearchDto.filters; - - Object.keys(likeSearchDto.filters).forEach((item) => { - Object.keys(likeSearchDto.filters[item]).forEach((e) => { - if (!e.startsWith("_")) { - filters[item][`_${e}`] = filters[item][e]; - delete filters[item][e]; - } - }); - }); - var data = { - query: `query SearchLike($filters:like_bool_exp,$limit:Int, $offset:Int) { - like_aggregate { - aggregate { - count - } - } - like(where:$filters, limit: $limit, offset: $offset,) { - userId - updated_at - type - likeId - created_at - contextId - context - } - }`, - variables: { - limit: parseInt(likeSearchDto.limit), - offset: offset, - filters: likeSearchDto.filters, - }, - }; - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - let result = response.data.data.like; - const likeDto = await this.mappedResponse(result); - const count = response?.data?.data?.like_aggregate?.aggregate?.count; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - totalCount: count, - data: likeDto, - }); - } - public async getCountLike(contextId: string, context: string, request: any) { - var axios = require("axios"); - - var data = { - query: `query SearchLike($contextId:String,$context:String) { - like(where: {contextId: {_eq: $contextId}, context: {_eq: $context}}) { - userId - updated_at - type - likeId - created_at - contextId - context - } - }`, - variables: { - contextId: contextId, - context: context, - }, - }; - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - let result = response.data.data.like; - const likeDto = await this.mappedResponse(result); - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: likeDto.length, - }); - } - - public async deleteLike(likeId: string, request: any) { - var axios = require("axios"); - var data = { - query: `mutation UpdateLike($likeId:uuid) { - delete_like(where: {likeId: {_eq: $likeId}}) { - affected_rows - }}`, - variables: { - likeId: likeId, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - const result = response.data.data; - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async mappedResponse(result: any) { - const likeResponse = result.map((obj: any) => { - const likeMapping = { - id: obj?.likeId ? `${obj.likeId}` : "", - likeId: obj?.likeId ? `${obj.likeId}` : "", - contextId: obj?.contextId ? `${obj.contextId}` : "", - context: obj?.context ? `${obj.context}` : "", - userId: obj?.userId ? `${obj.userId}` : "", - type: obj?.type ? `${obj.type}` : "", - createdAt: obj?.created_at ? `${obj.created_at}` : "", - updatedAt: obj?.updated_at ? `${obj.updated_at}` : "", - }; - return new LikeDto(likeMapping); - }); - - return likeResponse; - } -} diff --git a/src/adapters/hasura/mentorTracking.adapter.ts b/src/adapters/hasura/mentorTracking.adapter.ts deleted file mode 100644 index 8192a036..00000000 --- a/src/adapters/hasura/mentorTracking.adapter.ts +++ /dev/null @@ -1,311 +0,0 @@ -import { HttpService } from "@nestjs/axios"; -import { Injectable } from "@nestjs/common"; -import { SuccessResponse } from "src/success-response"; -import { MentorTrackingDto } from "src/mentorTracking/dto/mentorTracking.dto"; -import * as FormData from "form-data"; -import { FeedbackCreateDto } from "src/mentorTracking/dto/feedback-create.dto"; -@Injectable() -export class MentorTrackingService { - constructor(private httpService: HttpService) {} - - public async getMentorTracking(mentorId: string, request: any) { - var axios = require("axios"); - - var data = { - query: `query getMentorTracking($mentorTrackingId: uuid!) { - mentortracking(where: {mentorTrackingId: {_eq: $mentorTrackingId}}) { - mentorTrackingId - created_at - feedback - mentorId - scheduleVisitDate - status - teacherId - schoolId - updated_at - visitDate - lastVisited - } -}`, - variables: { mentorTrackingId: mentorId }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - let result = await this.mappedResponse(response.data.data.mentortracking); - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async createMentorTracking( - request: any, - mentorTrackingDto: MentorTrackingDto - ) { - var axios = require("axios"); - - let query = ""; - Object.keys(mentorTrackingDto).forEach((e) => { - if (mentorTrackingDto[e] && mentorTrackingDto[e] != "") { - query += `${e}: "${mentorTrackingDto[e]}", `; - } - }); - var data = { - query: `mutation createMentorTracking { - insert_mentortracking_one(object: {${query}}) { - mentorTrackingId - } -}`, - variables: {}, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - const result = response.data.data.insert_mentortracking_one; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async updateMentorTracking( - mentorTrackingId: string, - request: any, - mentorTrackingDto: MentorTrackingDto - ) { - const mentorSchema = new MentorTrackingDto(mentorTrackingDto); - let query = ""; - Object.keys(mentorTrackingDto).forEach((e) => { - if ( - mentorTrackingDto[e] && - mentorTrackingDto[e] != "" && - Object.keys(mentorSchema).includes(e) - ) { - if (Array.isArray(mentorTrackingDto[e])) { - query += `${e}: ${JSON.stringify(mentorTrackingDto[e])}, `; - } else { - query += `${e}: "${mentorTrackingDto[e]}", `; - } - } - }); - - var axios = require("axios"); - var data = { - query: `mutation updateMentorTracking($mentorTrackingId: uuid) { - update_mentortracking(where: {mentorTrackingId: {_eq: $mentorTrackingId}}, _set: {${query}}) { - affected_rows - } -}`, - variables: { - mentorTrackingId: mentorTrackingId, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - const result = response.data.data; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async feedback( - mentorTrackingId: string, - feedbackCreateDto: FeedbackCreateDto, - request: any - ) { - let mentorTrackingDto = feedbackCreateDto; - const mentorSchema = new FeedbackCreateDto(mentorTrackingDto); - let query = ""; - Object.keys(mentorTrackingDto).forEach((e) => { - if ( - mentorTrackingDto[e] && - mentorTrackingDto[e] != "" && - Object.keys(mentorSchema).includes(e) - ) { - if (Array.isArray(mentorTrackingDto[e])) { - query += `${e}: ${JSON.stringify(mentorTrackingDto[e])}, `; - } else { - query += `${e}: ${JSON.stringify(mentorTrackingDto[e])}, `; - } - } - }); - - var axios = require("axios"); - var data = { - query: `mutation updateMentorTracking($mentorTrackingId: uuid) { - update_mentortracking(where: {mentorTrackingId: {_eq: $mentorTrackingId}}, _set: {${query}}) { - affected_rows - } -}`, - variables: { - mentorTrackingId: mentorTrackingId, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - const result = response.data.data; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async searchMentorTracking( - limit: string, - mentorTrackingId: string, - mentorId: string, - teacherId: string, - schoolId: string, - scheduleVisitDate: Date, - visitDate: Date, - page: number, - status: string, - request: any - ) { - var axios = require("axios"); - - const searchData = { - mentorTrackingId, - mentorId, - teacherId, - schoolId, - scheduleVisitDate, - visitDate, - status, - }; - let offset = 0; - - if (page > 1) { - offset = parseInt(limit) * (page - 1); - } - - let query = ""; - Object.keys(searchData).forEach((e) => { - if (searchData[e] && searchData[e] != "") { - query += `${e}:{_eq:"${searchData[e]}"}`; - } - }); - - var data = { - query: `query searchMentorTracking($offset:Int,$limit:Int) { - mentortracking_aggregate { - aggregate { - count - } - } - mentortracking(limit: $limit, offset: $offset, where: {${query}}) { - mentorTrackingId - created_at - feedback - mentorId - scheduleVisitDate - status - teacherId - schoolId - updated_at - visitDate - lastVisited - } -}`, - variables: { - limit: parseInt(limit), - offset: offset, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - let result = await this.mappedResponse(response.data.data.mentortracking); - const count = - response?.data?.data?.mentortracking_aggregate?.aggregate?.count; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - totalCount: count, - data: result, - }); - } - - public async mappedResponse(result: any) { - const mentorResponse = result.map((obj: any) => { - const mentorMapping = { - id: obj?.mentorTrackingId ? `${obj.mentorTrackingId}` : "", - mentorTrackingId: obj?.mentorTrackingId - ? `${obj.mentorTrackingId}` - : "", - mentorId: obj?.mentorId ? `${obj.mentorId}` : "", - teacherId: obj?.teacherId ? `${obj.teacherId}` : "", - schoolId: obj?.schoolId ? `${obj.schoolId}` : "", - scheduleVisitDate: obj?.scheduleVisitDate - ? `${obj.scheduleVisitDate}` - : "", - visitDate: obj?.visitDate ? `${obj.visitDate}` : "", - feedback: obj?.feedback ? `${obj.feedback}` : "", - status: obj?.status ? obj.status : "", - lastVisited: obj?.lastVisited ? obj.lastVisited : "", - createdAt: obj?.created_at ? `${obj.created_at}` : "", - updatedAt: obj?.updated_at ? `${obj.updated_at}` : "", - }; - return new MentorTrackingDto(mentorMapping); - }); - - return mentorResponse; - } -} diff --git a/src/adapters/hasura/monitorTracking.adapter.ts b/src/adapters/hasura/monitorTracking.adapter.ts deleted file mode 100644 index e4312b9a..00000000 --- a/src/adapters/hasura/monitorTracking.adapter.ts +++ /dev/null @@ -1,241 +0,0 @@ -import { HttpService } from "@nestjs/axios"; -import { Injectable } from "@nestjs/common"; -import { SuccessResponse } from "src/success-response"; -import { MonitorTrackingDto } from "src/monitorTracking/dto/monitorTracking.dto"; -@Injectable() -export class MonitorTrackingService { - constructor(private httpService: HttpService) {} - - public async getMonitorTracking(monitorId: string, request: any) { - var axios = require("axios"); - - var data = { - query: `query GetMonitorTracking($monitorTrackingId:uuid) { - monitortracking(where: {monitorTrackingId: {_eq:$monitorTrackingId }}) { - created_at - feedback - monitorTrackingId - scheduleVisitDate - schoolId - monitorId - status - updated_at - visitDate - lastVisited - } - }`, - variables: { monitorTrackingId: monitorId }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - let result = await this.mappedResponse(response.data.data.monitortracking); - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async createMonitorTracking( - request: any, - monitorTrackingDto: MonitorTrackingDto - ) { - var axios = require("axios"); - - let query = ""; - Object.keys(monitorTrackingDto).forEach((e) => { - if (monitorTrackingDto[e] && monitorTrackingDto[e] != "") { - query += `${e}: "${monitorTrackingDto[e]}", `; - } - }); - var data = { - query: `mutation CreateMonitorTracking { - insert_monitortracking_one(object: {${query}}) { - monitorTrackingId - } - }`, - variables: {}, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - const result = response.data.data.insert_monitortracking_one; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async updateMonitorTracking( - monitorTrackingId: string, - request: any, - monitorTrackingDto: MonitorTrackingDto - ) { - var axios = require("axios"); - - let query = ""; - Object.keys(monitorTrackingDto).forEach((e) => { - if (monitorTrackingDto[e] && monitorTrackingDto[e] != "") { - query += `${e}:"${monitorTrackingDto[e]}"`; - } - }); - - var data = { - query: `mutation UpdatedMonitorTracking($monitorTrackingId:uuid) { - update_monitortracking(where: {monitorTrackingId: {_eq: $monitorTrackingId}}, _set: {${query}}) { - affected_rows - } -}`, - variables: { - monitorTrackingId: monitorTrackingId, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - const result = response.data.data; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async searchMonitorTracking( - limit: string, - monitorTrackingId: string, - monitorId: string, - schoolId: string, - groupId: string, - scheduleVisitDate: Date, - visitDate: Date, - page: number, - request: any - ) { - var axios = require("axios"); - let offset = 0; - - if (page > 1) { - offset = parseInt(limit) * (page - 1); - } - const searchData = { - monitorTrackingId, - monitorId, - schoolId, - groupId, - scheduleVisitDate, - visitDate, - }; - - let query = ""; - Object.keys(searchData).forEach((e) => { - if (searchData[e] && searchData[e] != "") { - query += `${e}:{_eq:"${searchData[e]}"}`; - } - }); - - var data = { - query: `query SearchMonitorTracking($offset:Int,$limit:Int) { - monitortracking_aggregate { - aggregate { - count - } - } - monitortracking(where:{ ${query}}, offset: $offset,limit: $limit) { - created_at - feedback - monitorTrackingId - scheduleVisitDate - status - schoolId - groupId - monitorId - updated_at - visitDate - lastVisited - } - }`, - variables: { limit: parseInt(limit), offset: offset }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - let result = await this.mappedResponse(response.data.data.monitortracking); - const count = - response?.data?.data?.monitortracking_aggregate?.aggregate?.count; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - totalCount: count, - data: result, - }); - } - - public async mappedResponse(result: any) { - const monitorResponse = result.map((obj: any) => { - const monitorMapping = { - id: obj?.monitorTrackingId ? `${obj.monitorTrackingId}` : "", - monitorTrackingId: obj?.monitorTrackingId - ? `${obj.monitorTrackingId}` - : "", - monitorId: obj?.monitorId ? `${obj.monitorId}` : "", - schoolId: obj?.schoolId ? `${obj.schoolId}` : "", - groupId: obj?.groupId ? `${obj.groupId}` : "", - scheduleVisitDate: obj?.scheduleVisitDate - ? `${obj.scheduleVisitDate}` - : "", - visitDate: obj?.visitDate ? `${obj.visitDate}` : "", - feedback: obj?.feedback ? `${obj.feedback}` : "", - status: obj?.status ? `${obj.status}` : "", - - lastVisited: obj?.lastVisited ? `${obj.lastVisited}` : "", - createdAt: obj?.created_at ? `${obj.created_at}` : "", - updatedAt: obj?.updated_at ? `${obj.updated_at}` : "", - }; - return new MonitorTrackingDto(monitorMapping); - }); - - return monitorResponse; - } -} diff --git a/src/adapters/hasura/question.adapter.ts b/src/adapters/hasura/question.adapter.ts deleted file mode 100644 index 8646dfba..00000000 --- a/src/adapters/hasura/question.adapter.ts +++ /dev/null @@ -1,464 +0,0 @@ -import { HttpService } from "@nestjs/axios"; -import { Injectable } from "@nestjs/common"; -import { SuccessResponse } from "src/success-response"; -import { QuestionDto } from "src/Question/dto/question.dto"; -import { IServicelocator } from "../questionservicelocator"; -export const HasuraQuestionToken = "HasuraQuestion"; -@Injectable() -export class QuestionService implements IServicelocator { - constructor(private httpService: HttpService) {} - - public async getQuestion(questionId: string, request: any) { - var axios = require("axios"); - - var data = { - query: `query GetQuestion($examQuestionId:uuid) { - question_by_pk(examQuestionId: $examQuestionId) { - answer - avgRating - avgTimeSpent - bloomsLevel - body - class - compatibilityLevel - created_at - examQuestionId - expectedDuration - feedback - hints - instructions - interactions - isTemplate - language - learningOutcome - maxScore - media - numAttempts - numCorrectAttempts - numInCorrectAttempts - numSkips - options - outcomeDeclaration - purpose - qlevel - questionId - qumlVersion - responseDeclaration - responseProcessing - scoringMode - solutionAvailable - source - subject - templateDeclaration - templateProcessing - topic - totalRatings - totalTimeSpent - type - updated_at - visibility - } - } - `, - variables: { examQuestionId: questionId }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - let result = [response.data.data.question_by_pk]; - const questionResponse = await this.mappedResponse(result); - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: questionResponse[0], - }); - } - public async createQuestion(request: any, questionDto: QuestionDto) { - var axios = require("axios"); - let query = ""; - Object.keys(questionDto).forEach((e) => { - if (questionDto[e] && questionDto[e] != "") { - if (Array.isArray(questionDto[e])) { - query += `${e}: ${JSON.stringify(questionDto[e])}, `; - } else { - query += `${e}: "${questionDto[e]}", `; - } - } - }); - - var data = { - query: `mutation CreateQuestion { - insert_question_one(object: {${query}}) { - examQuestionId - } - } - `, - variables: {}, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - const result = response.data.data.insert_question_one; - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async updateQuestion( - questionId: string, - request: any, - questionDto: QuestionDto - ) { - var axios = require("axios"); - - let query = ""; - Object.keys(questionDto).forEach((e) => { - if (questionDto[e] && questionDto[e] != "") { - if (Array.isArray(questionDto[e])) { - query += `${e}: ${JSON.stringify(questionDto[e])}, `; - } else { - query += `${e}: ${questionDto[e]}, `; - } - } - }); - - var data = { - query: `mutation UpdateQuestion($examQuestionId:uuid) { - update_question(where: {examQuestionId: {_eq: $examQuestionId}}, _set: {${query}}) { - affected_rows - } -}`, - variables: { - examQuestionId: questionId, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - const result = response.data.data; - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async filterQuestion( - limit: string, - body: string, - className: string, - maxScore: string, - questionId: string, - subject: string, - topic: string, - type: string, - page: number, - request: any - ) { - var axios = require("axios"); - - let offset = 0; - - if (page > 1) { - offset = parseInt(limit) * (page - 1); - } - - const searchData = { - body: body, - class: className, - maxScore: maxScore, - questionId: questionId, - subject: subject, - topic: topic, - type: type, - }; - - let query = ""; - Object.keys(searchData).forEach((e) => { - if (searchData[e] && searchData[e] != "") { - query += `${e}:{_eq:"${searchData[e]}"}`; - } - }); - - var data = { - query: `query SearchQuestion($limit:Int, $offset:Int) { - question_aggregate { - aggregate { - count - } - } - question(where:{ ${query}}, limit: $limit, offset: $offset,) { - answer - avgRating - avgTimeSpent - bloomsLevel - body - class - compatibilityLevel - created_at - examQuestionId - expectedDuration - feedback - hints - instructions - interactions - isTemplate - language - learningOutcome - maxScore - media - numAttempts - numCorrectAttempts - numInCorrectAttempts - numSkips - options - outcomeDeclaration - purpose - qlevel - questionId - qumlVersion - responseDeclaration - responseProcessing - scoringMode - solutionAvailable - source - subject - templateDeclaration - templateProcessing - topic - totalRatings - totalTimeSpent - type - updated_at - visibility - } - }`, - variables: { - limit: parseInt(limit), - offset: offset, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - let result = response.data.data.question; - const questionResponse = await this.mappedResponse(result); - const count = response?.data?.data?.question_aggregate?.aggregate?.count; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - totalCount: count, - data: questionResponse, - }); - } - - public async bulkImport(request: any, questionDto: [Object]) { - let axios = require("axios"); - const result = Promise.all( - questionDto.map(async (data: any) => { - let query = ""; - Object.keys(data).forEach((e) => { - if (data[e] && data[e] != "") { - if (Array.isArray(data[e])) { - query += `${e}: ${JSON.stringify(data[e])}, `; - } else { - query += `${e}: ${data[e]}, `; - } - } - }); - - var createQuery = { - query: `mutation CreateQuestion { - insert_question_one(object: {${query}}) { - examQuestionId - } - } - `, - variables: {}, - }; - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: createQuery, - }; - - const response = await axios(config); - return await response.data; - }) - ); - const responseArray = await result; - return new SuccessResponse({ - statusCode: 200, - message: " Ok.", - data: responseArray, - }); - } - - public async getAllQuestions( - questionType: string, - subject: [string], - limit: string, - language: string, - medium: string, - bloomsLevel: [string], - topic: [string], - className: [string], - request: any - ) {} - - public async getAllQuestionsByQuestionIds( - questionIds: [string], - request: any - ) {} - public async getSubjectList(gradeLevel: string) {} - public async getTopicsList(subject: string) {} - - public async getOneQuestion(questionId: string, request: any) {} - - public async getCompetenciesList( - subject: string, - limit: string, - request: any - ) {} - public async mappedResponse(result: any) { - const questionResponse = result.map((item: any) => { - const questionMapping = { - id: item?.examQuestionId, - - examQuestionId: item?.examQuestionId, - - body: item?.body, - - instructions: item?.instructions, - - feedback: item?.feedback, - - topic: item?.topic, - - subject: item?.subject, - - class: item?.class, - - questionId: item?.questionId, - - hints: item?.hints, - - options: item?.options, - - media: item?.media, - - responseDeclaration: item?.responseDeclaration, - - outcomeDeclaration: item?.outcomeDeclaration, - - templateDeclaration: item?.templateDeclaration, - - templateProcessing: item?.templateProcessing, - - responseProcessing: item?.responseProcessing, - - bloomsLevel: item?.bloomsLevel, - - qlevel: item?.qlevel, - - purpose: item?.purpose, - - expectedDuration: item?.expectedDuration, - - maxScore: item?.maxScore, - - type: item?.type, - - visibility: item?.visibility, - - isTemplate: item?.isTemplate, - - interactions: item?.interactions, - - solutionAvailable: item?.solutionAvailable, - - scoringMode: item?.scoringMode, - - qumlVersion: item?.qumlVersion, - - totalTimeSpent: item?.totalTimeSpent, - - avgTimeSpent: item?.avgTimeSpent, - - numAttempts: item?.numAttempts, - - numCorrectAttempts: item?.numCorrectAttempts, - - numInCorrectAttempts: item?.numInCorrectAttempts, - - numSkips: item?.numSkips, - - source: item?.source, - - answer: item?.answer, - - learningOutcome: item?.learningOutcome, - - compatibilityLevel: item?.compatibilityLevel, - - language: item?.language, - - avgRating: item?.avgRating, - - totalRatings: item?.totalRatings, - }; - return new QuestionDto(questionMapping); - }); - - return questionResponse; - } -} diff --git a/src/adapters/hasura/rbac/assignrole.adapter.ts b/src/adapters/hasura/rbac/assignrole.adapter.ts new file mode 100644 index 00000000..f98b94d9 --- /dev/null +++ b/src/adapters/hasura/rbac/assignrole.adapter.ts @@ -0,0 +1,12 @@ +import { HttpService } from "@nestjs/axios"; +import { Injectable } from "@nestjs/common"; +import { CreateAssignRoleDto } from "src/rbac/assign-role/dto/create-assign-role.dto"; + + +@Injectable() +export class HasuraAssignRoleService { + constructor(private httpService: HttpService) {} + public async createAssignRole(request: any, createAssignRoleDto:CreateAssignRoleDto){}; + public async getAssignedRole(request: any, createAssignRoleDto:CreateAssignRoleDto){}; + public async deleteAssignedRole(deleteAssignRoleDto){}; +} \ No newline at end of file diff --git a/src/adapters/hasura/rbac/privilege.adapter.ts b/src/adapters/hasura/rbac/privilege.adapter.ts new file mode 100644 index 00000000..aecc03ec --- /dev/null +++ b/src/adapters/hasura/rbac/privilege.adapter.ts @@ -0,0 +1,22 @@ +import { HttpService } from "@nestjs/axios"; +import { Injectable } from "@nestjs/common"; +import { + CreatePrivilegesDto, + PrivilegeDto, +} from "src/rbac/privilege/dto/privilege.dto"; + +@Injectable() +export class HasuraPrivilegeService { + constructor(private httpService: HttpService) {} + + public async createPrivilege( + loggedinUser: any, + createPrivileges: CreatePrivilegesDto + ) {} + public async getPrivilege(roleId: string, request: any) {} + // public async updatePrivilege(privilegeId, request, privilegeDto){} + public async getAllPrivilege(request){} + public async deletePrivilege(privilegeId){} + public async getPrivilegebyRoleId(tenantId, roleId,request){} + +} diff --git a/src/adapters/hasura/rbac/privilegerole.adapter.ts b/src/adapters/hasura/rbac/privilegerole.adapter.ts new file mode 100644 index 00000000..417151bb --- /dev/null +++ b/src/adapters/hasura/rbac/privilegerole.adapter.ts @@ -0,0 +1,13 @@ +import { HttpService } from "@nestjs/axios"; +import { Injectable } from "@nestjs/common"; +import { Response } from "express"; +import { CreatePrivilegeRoleDto } from "src/rbac/assign-privilege/dto/create-assign-privilege.dto"; + + +@Injectable() +export class HasuraAssignPrivilegeService { + constructor(private httpService: HttpService) {} + public async createPrivilegeRole(request: any, createAssignRoleDto:CreatePrivilegeRoleDto,response : Response){}; + public async getPrivilegeRole(request: any, createAssignRoleDto:CreatePrivilegeRoleDto, response:Response){}; + public async deletePrivilegeRole(userId){}; +} \ No newline at end of file diff --git a/src/adapters/hasura/rbac/role.adapter.ts b/src/adapters/hasura/rbac/role.adapter.ts new file mode 100644 index 00000000..ffed3234 --- /dev/null +++ b/src/adapters/hasura/rbac/role.adapter.ts @@ -0,0 +1,14 @@ +import { HttpService } from "@nestjs/axios"; +import { Injectable } from "@nestjs/common"; +import { CreateRolesDto, RoleDto } from "../../../rbac/role/dto/role.dto"; +import { RoleSearchDto } from "../../../rbac/role/dto/role-search.dto"; + +@Injectable() +export class HasuraRoleService { + constructor(private httpService: HttpService) {} + public async getRole(roleId: string, request: any) {} + public async createRole(request: any, createRolesDto: CreateRolesDto){} + public async deleteRole(roleId: string) {} + public async updateRole(roleId: string, request: any, roleDto: RoleDto) {} + public async searchRole(roleSearchDto: RoleSearchDto) {} +} diff --git a/src/adapters/hasura/role.adapter.ts b/src/adapters/hasura/role.adapter.ts deleted file mode 100644 index 0c4a2b21..00000000 --- a/src/adapters/hasura/role.adapter.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { HttpService } from "@nestjs/axios"; -import { Injectable } from "@nestjs/common"; -import { SuccessResponse } from "src/success-response"; -import { RoleDto } from "src/role/dto/role.dto"; -@Injectable() -export class RoleService { - constructor(private httpService: HttpService) {} - - public async getRole(title: string, request: any) { - var axios = require("axios"); - var data = { - query: `query getRole($title: String) { - role(where: {title: {_eq: $title}}) { - title - roleId, - status, - parentId - created_at - updated_at - } - }`, - variables: { title: title }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - let result = response.data.data.role; - const roleData = await this.mappedResponse(result); - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: roleData, - }); - } - - public async createRole(request: any, roleDto: RoleDto) { - var axios = require("axios"); - var data = { - query: `mutation createRole($title: String, $parentId: String, $status: String) { - insert_role_one(object: {title: $title, parentId: $parentId, status: $status}) { - roleId - } - }`, - variables: { - title: roleDto.title, - parentId: roleDto.parentId, - status: roleDto.status, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - const result = response.data.data.insert_role_one; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async updateRole(roleId: string, request: any, roleDto: RoleDto) { - var axios = require("axios"); - - const updateRoleData = { - title: roleDto.title, - parentId: roleDto.parentId, - status: roleDto.status, - }; - - let query = ""; - Object.keys(updateRoleData).forEach((e) => { - if (updateRoleData[e] && updateRoleData[e] != "") { - query += `${e}:${updateRoleData[e]} `; - } - }); - - var data = { - query: `mutation updateRole($roleId: uuid, $title: String, $parentId: String, $status: String) { - update_role(where: {roleId: {_eq: $roleId}}, _set: {${query}}) { - affected_rows - } - }`, - variables: { - roleId: roleId, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - const result = response.data.data; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async searchRole( - limit: string, - roleId: string, - title: string, - parentId: string, - status: string, - request: any - ) { - var axios = require("axios"); - - const searchData = { - roleId, - title, - parentId, - status, - }; - - let query = ""; - Object.keys(searchData).forEach((e) => { - if (searchData[e] && searchData[e] != "") { - query += `${e}:{_eq:"${searchData[e]}"}`; - } - }); - var data = { - query: `query searchRole($limit:Int) { - role_aggregate { - aggregate { - count - } - } - role(limit: $limit, where: {${query}}) { - title - roleId, - status, - parentId - created_at - updated_at - } -}`, - variables: { - limit: parseInt(limit), - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - let result = response.data.data.role; - const roleData = await this.mappedResponse(result); - const count = response?.data?.data?.role_aggregate?.aggregate?.count; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - totalCount: count, - data: roleData, - }); - } - - public async mappedResponse(result: any) { - const roleResponse = result.map((item: any) => { - const roleMapping = { - id: item?.roleId ? `${item.roleId}` : "", - roleId: item?.roleId ? `${item.roleId}` : "", - title: item?.title ? `${item.title}` : "", - parentId: item?.parentId ? `${item.parentId}` : "", - status: item?.status ? `${item.status}` : "", - createdAt: item?.created_at ? `${item.created_at}` : "", - updatedAt: item?.updated_at ? `${item.updated_at}` : "", - }; - return new RoleDto(roleMapping); - }); - - return roleResponse; - } -} diff --git a/src/adapters/hasura/school.adapter.ts b/src/adapters/hasura/school.adapter.ts deleted file mode 100644 index ca6188f6..00000000 --- a/src/adapters/hasura/school.adapter.ts +++ /dev/null @@ -1,286 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { HttpService } from "@nestjs/axios"; -import { SuccessResponse } from "src/success-response"; -import { SchoolDto } from "src/school/dto/school.dto"; -import { SchoolSearchDto } from "src/school/dto/school-search.dto"; -import { IServicelocator } from "../schoolservicelocator"; -export const HasuraSchoolToken = "HasuraSchool"; -@Injectable() -export class SchoolHasuraService implements IServicelocator { - constructor(private httpService: HttpService) {} - - public async createSchool(request: any, schoolDto: SchoolDto) { - var axios = require("axios"); - const schoolSchema = new SchoolDto(schoolDto); - let query = ""; - Object.keys(schoolDto).forEach((e) => { - if ( - schoolDto[e] && - schoolDto[e] != "" && - Object.keys(schoolSchema).includes(e) - ) { - if (Array.isArray(schoolDto[e])) { - query += `${e}: ${JSON.stringify(schoolDto[e])}, `; - } else { - query += `${e}: "${schoolDto[e]}", `; - } - } - }); - - var data = { - query: `mutation CreateSchool { - insert_school_one(object: {${query}}) { - schoolId - } - } - `, - variables: {}, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - const result = response.data.data.insert_school_one; - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async updateSchool(id: string, request: any, schoolDto: SchoolDto) { - var axios = require("axios"); - const schoolSchema = new SchoolDto(schoolDto); - let query = ""; - Object.keys(schoolDto).forEach((e) => { - if ( - schoolDto[e] && - schoolDto[e] != "" && - Object.keys(schoolSchema).includes(e) - ) { - if (Array.isArray(schoolDto[e])) { - query += `${e}: ${JSON.stringify(schoolDto[e])}, `; - } else { - query += `${e}: "${schoolDto[e]}", `; - } - } - }); - - var data = { - query: `mutation UpdateSchool($schoolId:uuid) { - update_school(where: {schoolId: {_eq: $schoolId}}, _set: {${query}}) { - affected_rows - }}`, - variables: { - schoolId: id, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - const result = response.data.data; - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async getSchool(schoolId: any, request: any) { - var axios = require("axios"); - - var data = { - query: `query GetSchool($schoolId:uuid!) { - school_by_pk(schoolId: $schoolId) { - address - block - created_at - deactivationReason - district - email - latitude - enrollCount - locationId - longitude - mediumOfInstruction - metaData - phoneNumber - updated_at - status - udise - stateId - schoolType - schoolName - schoolId - pincode - village - website - cluster - headMaster - board - } - } - `, - variables: { schoolId: schoolId }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - let result = [response.data.data.school_by_pk]; - const schoolDto = await this.mappedResponse(result); - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: schoolDto[0], - }); - } - public async searchSchool(request: any, schoolSearchDto: SchoolSearchDto) { - var axios = require("axios"); - - let offset = 0; - - if (schoolSearchDto.page > 1) { - offset = parseInt(schoolSearchDto.limit) * (schoolSearchDto.page - 1); - } - - let query = ""; - Object.keys(schoolSearchDto.filters).forEach((e) => { - if (schoolSearchDto.filters[e] && schoolSearchDto.filters[e] != "") { - query += `${e}:{_eq:"${schoolSearchDto.filters[e]}"}`; - } - }); - - var data = { - query: `query SearchSchool($limit:Int, $offset:Int) { - school_aggregate { - aggregate { - count - } - } - school(where:{ ${query}}, limit: $limit, offset: $offset,) { - address - block - created_at - deactivationReason - district - email - latitude - enrollCount - locationId - longitude - mediumOfInstruction - metaData - phoneNumber - updated_at - status - udise - stateId - schoolType - schoolName - schoolId - pincode - village - website - cluster - headMaster - board - } - }`, - variables: { - limit: parseInt(schoolSearchDto.limit), - offset: offset, - }, - }; - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - let result = response.data.data.school; - const schoolDto = await this.mappedResponse(result); - const count = response?.data?.data?.school_aggregate?.aggregate?.count; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - totalCount: count, - data: schoolDto, - }); - } - public async mappedResponse(result: any) { - const schoolResponse = result.map((item: any) => { - const schoolMapping = { - id: item?.schoolId ? `${item.schoolId}` : "", - schoolId: item?.schoolId ? `${item.schoolId}` : "", - schoolName: item?.schoolName ? `${item.schoolName}` : "", - email: item?.email ? `${item.email}` : "", - udise: item?.udise ? `${item.udise}` : "", - mediumOfInstruction: item?.mediumOfInstruction - ? item.mediumOfInstruction - : "", - phoneNumber: item?.phoneNumber ? item.phoneNumber : "", - address: item?.address ? item.address : "", - schoolType: item?.schoolType ? `${item.schoolType}` : "", - website: item?.website ? `${item.website}` : "", - headMaster: item?.headMaster ? `${item.headMaster}` : "", - board: item?.board ? `${item.board}` : "", - village: item?.village ? `${item.village}` : "", - block: item?.block ? `${item.block}` : "", - district: item?.district ? `${item.district}` : "", - stateId: item?.stateId ? `${item.stateId}` : "", - cluster: item?.cluster ? `${item.cluster}` : "", - pincode: item?.pincode ? item.pincode : "", - locationId: item?.locationId ? `${item.locationId}` : "", - enrollCount: item?.enrollCount ? `${item.enrollCount}` : "", - status: item?.status ? `${item.status}` : "", - latitude: item?.latitude ? item.latitude : "", - longitude: item?.longitude ? item.longitude : "", - metaData: item?.metaData ? item.metaData : [], - deactivationReason: item?.deactivationReason - ? `${item.deactivationReason}` - : "", - createdAt: item?.created_at ? `${item.created_at}` : "", - updatedAt: item?.updated_at ? `${item.updated_at}` : "", - }; - return new SchoolDto(schoolMapping); - }); - - return schoolResponse; - } -} diff --git a/src/adapters/hasura/services/fields.service.ts b/src/adapters/hasura/services/fields.service.ts index 14c450af..b9eaf139 100644 --- a/src/adapters/hasura/services/fields.service.ts +++ b/src/adapters/hasura/services/fields.service.ts @@ -11,10 +11,8 @@ export class FieldsService { constructor() {} //fields - async createFields(request: any, fieldsDto: FieldsDto) { + async createFields(request:any,fieldsDto: FieldsDto) { try{ - - const decoded: any = jwt_decode(request.headers.authorization); var axios = require("axios"); //add render json object @@ -56,8 +54,7 @@ export class FieldsService { const response = await axios(config); return response; - - } catch (e) { + }catch (e) { console.error(e); return new ErrorResponse({ errorCode: "400", @@ -127,107 +124,116 @@ export class FieldsService { } public async getFieldsContext( + request:any, tenantId: string, context: string, contextId: string ) { - var axios = require("axios"); + try{ + var axios = require("axios"); - var data = { - query: `query GetFields($context:String!, $contextId:uuid!, $tenantId:uuid!) { - Fields( - where:{ - _or:[ - { - tenantId:{ - _eq:$tenantId - } - context:{ - _eq:$context - } - contextId:{ - _is_null:true - } - }, - { - tenantId:{ - _eq:$tenantId - } - context:{ - _eq:$context + var data = { + query: `query GetFields($context:String!, $contextId:uuid!, $tenantId:uuid!) { + Fields( + where:{ + _or:[ + { + tenantId:{ + _eq:$tenantId + } + context:{ + _eq:$context + } + contextId:{ + _is_null:true + } + }, + { + tenantId:{ + _eq:$tenantId + } + context:{ + _eq:$context + } + contextId:{ + _eq:$contextId + } } - contextId:{ + ] + } + ){ + tenantId + fieldId + assetId + context + contextId + render + groupId + name + label + defaultValue + type + note + description + state + required + ordering + metadata + access + onlyUseInSubform + createdAt + updatedAt + createdBy + updatedBy + fieldValues: FieldValues( + where:{ + itemId:{ _eq:$contextId - } + }, } - ] + ){ + value + fieldValuesId + itemId + fieldId + createdAt + updatedAt + createdBy + updatedBy } - ){ - tenantId - fieldId - assetId - context - contextId - render - groupId - name - label - defaultValue - type - note - description - state - required - ordering - metadata - access - onlyUseInSubform - createdAt - updatedAt - createdBy - updatedBy - fieldValues: FieldValues( - where:{ - itemId:{ - _eq:$contextId - }, - } - ){ - value - fieldValuesId - itemId - fieldId - createdAt - updatedAt - createdBy - updatedBy } - } - }`, - variables: { - context: context, - contextId: contextId, - tenantId: tenantId, - }, - }; + }`, + variables: { + context: context, + contextId: contextId, + tenantId: tenantId, + }, + }; - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; + var config = { + method: "post", + url: process.env.REGISTRYHASURA, + headers: { + Authorization: request.headers.authorization, + "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, + "Content-Type": "application/json", + }, + data: data, + }; - const response = await axios(config); - return response; + const response = await axios(config); + return response; + }catch (e) { + console.error(e); + return new ErrorResponse({ + errorCode: "400", + errorMessage: e, + }); + } } - async searchFields(request:any, tenantId: string, fieldsSearchDto: FieldsSearchDto) { + async searchFields(request: any, tenantId: string, fieldsSearchDto: FieldsSearchDto) { try{ - const decoded: any = jwt_decode(request.headers.authorization); var axios = require("axios"); let offset = 0; @@ -292,15 +298,16 @@ export class FieldsService { }, data: data, }; - } catch (e) { + + const response = await axios(config); + return response; + }catch (e) { console.error(e); return new ErrorResponse({ errorCode: "400", errorMessage: e, }); } - const response = await axios(config); - return response; } async updateFields(fieldsId: string, fieldsDto: FieldsDto) { @@ -466,10 +473,8 @@ export class FieldsService { } //field values - async createFieldValues(request:any, fieldValuesDto: FieldValuesDto) { + async createFieldValues(request: any, fieldValuesDto: FieldValuesDto) { try{ - const decoded: any = jwt_decode(request.headers.authorization); - var axios = require("axios"); let query = ""; @@ -497,6 +502,7 @@ export class FieldsService { method: "post", url: process.env.REGISTRYHASURA, headers: { + Authorization: request.headers.authorization, "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, "Content-Type": "application/json", }, @@ -505,7 +511,7 @@ export class FieldsService { const response = await axios(config); return response; - } catch (e) { + }catch (e) { console.error(e); return new ErrorResponse({ errorCode: "400", @@ -513,7 +519,6 @@ export class FieldsService { }); } } - async createFieldValuesBulk(field_values: any) { var axios = require("axios"); @@ -634,8 +639,6 @@ export class FieldsService { async searchFieldValues(request:any, fieldValuesSearchDto: FieldValuesSearchDto) { try{ - const decoded: any = jwt_decode(request.headers.authorization); - var axios = require("axios"); let offset = 0; @@ -677,6 +680,7 @@ export class FieldsService { method: "post", url: process.env.REGISTRYHASURA, headers: { + Authorization: request.headers.authorization, "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, "Content-Type": "application/json", }, @@ -685,19 +689,17 @@ export class FieldsService { const response = await axios(config); return response; - } catch (e) { - console.error(e); - return new ErrorResponse({ - errorCode: "400", - errorMessage: e, - }); - } + }catch (e) { + console.error(e); + return new ErrorResponse({ + errorCode: "400", + errorMessage: e, + }); + } } - async searchFieldValuesFilter(request:any, filter: any) { + async searchFieldValuesFilter(request:any,filter: any) { try{ - const decoded: any = jwt_decode(request.headers.authorization); - let obj_filter = []; Object.keys(filter).forEach((item) => { Object.keys(filter[item]).forEach((e) => { @@ -734,16 +736,16 @@ export class FieldsService { }, data: data, }; + const response = await axios(config); return response; - } catch (e) { + }catch (e) { console.error(e); return new ErrorResponse({ errorCode: "400", errorMessage: e, }); } - } async updateFieldValues(id: string, fieldValuesDto: FieldValuesDto) { diff --git a/src/adapters/hasura/trackassessment.adapter.ts b/src/adapters/hasura/trackassessment.adapter.ts deleted file mode 100644 index 4d8be164..00000000 --- a/src/adapters/hasura/trackassessment.adapter.ts +++ /dev/null @@ -1,309 +0,0 @@ -import { HttpException, Injectable } from "@nestjs/common"; -import { HttpService } from "@nestjs/axios"; -import { AxiosResponse } from "axios"; -import { catchError, map } from "rxjs"; -import { SuccessResponse } from "src/success-response"; -import { TrackAssessmentDto } from "src/trackAssessment/dto/trackassessment.dto"; -import { ErrorResponse } from "src/error-response"; -import { Status } from "../../trackAssessment/enums/statuses.enum"; - -@Injectable() -export class TrackAssessmentService { - constructor(private httpService: HttpService) {} - - url = process.env.DIKSHADEVBASEAPIURL; - public async getAssessment(assessmentId: any, request: any) { - var axios = require("axios"); - try { - var data = { - query: `query GetTrackAssessment($trackAssessmentId:uuid) { - trackassessment(where: {trackAssessmentId: {_eq: $trackAssessmentId}}) { - answersheet - filter - created_at - updated_at - trackAssessmentId - questions - score - totalScore - source - studentId - teacherId - groupId - subject - date - type - status - } - }`, - variables: { - trackAssessmentId: assessmentId, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - const result = await this.mappedResponse( - response.data.data.trackassessment - ); - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } catch (e) { - return `${e}`; - } - } - - public async createAssessment( - request: any, - assessmentDto: TrackAssessmentDto - ) { - let variables: object; - try { - const axios = require("axios"); - if (!assessmentDto.status) { - // let's set it as "COMPLETED" by default - assessmentDto.status = Status.COMPLETED; - } - if (assessmentDto.status == Status.COMPLETED) { - const answer = JSON.stringify(assessmentDto.answersheet); - const jsonObj = JSON.parse(answer); - const params = JSON.parse(jsonObj); - - let sum = 0; - params.children.map((e: any) => { - sum += e.score; - return sum; - }); - assessmentDto.score = sum.toString(); - - const questionIds = assessmentDto.questions; - const totalScoreArray = []; - for (const value of questionIds) { - const config = { - method: "get", - url: `${this.url}/question/v1/read/${value}?fields=maxScore`, - }; - const response = await axios(config); - const data = response?.data; - const final = data.result.question; - - const scoreResponse = { - maxScore: final.maxScore, - }; - totalScoreArray.push(scoreResponse); - } - let totalScore = 0; - totalScoreArray.map((e: any) => { - totalScore += e.maxScore; - return totalScore; - }); - assessmentDto.totalScore = totalScore.toString(); - - variables = { - filter: assessmentDto.filter, - source: assessmentDto.source, - questions: assessmentDto.questions.toString(), - studentId: assessmentDto.studentId, - teacherId: assessmentDto.teacherId, - type: assessmentDto.type, - answersheet: assessmentDto.answersheet, - score: assessmentDto.score, - totalScore: assessmentDto.totalScore, - groupId: assessmentDto.groupId, - subject: assessmentDto.subject, - status: assessmentDto.status, - }; - } else { - variables = { - filter: null, - source: null, - questions: null, - studentId: assessmentDto.studentId, - teacherId: assessmentDto.teacherId, - type: assessmentDto.type, - answersheet: null, - score: null, - totalScore: null, - groupId: assessmentDto.groupId, - subject: null, - status: assessmentDto.status, - }; - } - - const data = { - query: `mutation CreateTrackAssessment($filter: String, $score: String, $totalScore:String, $source: String, $questions: String, $studentId: String, $teacherId: String, $type: String, $answersheet: String,$groupId:String, $subject:String, $status: String) { - insert_trackassessment_one(object:{filter: $filter, score: $score, totalScore:$totalScore, source: $source, questions: $questions, studentId: $studentId, teacherId: $teacherId, type: $type, answersheet: $answersheet,groupId:$groupId,subject:$subject, status: $status}) { - trackAssessmentId - } - }`, - variables: variables, - }; - - const config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: response.data, - }); - } catch (e) { - return `${e}`; - } - } - - public async searchAssessment( - fromDate: string, - toDate: string, - limit: string, - source: string, - studentId: string, - teacherId: string, - groupId: string, - subject: string, - page: number, - request: any - ) { - var axios = require("axios"); - - let offset = 0; - - if (page > 1) { - offset = parseInt(limit) * (page - 1); - } - let searchData = { - fromDate, - toDate, - source, - studentId, - teacherId, - groupId, - subject, - }; - - let query = ""; - if (searchData.fromDate && searchData.toDate) { - query += `date:{_gte: "${searchData.fromDate}"}, _and: {date: {_lte: "${searchData.toDate}"}} `; - } - const objectKeys = Object.keys(searchData); - objectKeys.forEach((e, index) => { - if ( - searchData[e] && - searchData[e] != "" && - !["fromDate", "toDate"].includes(e) - ) { - query += `${e}:{_eq:"${searchData[e]}"}`; - if (index !== objectKeys.length - 1) { - query += " "; - } - } - }); - - var data = { - query: `query searchTrackAssessment($offset:Int,$limit:Int) { - trackassessment_aggregate { - aggregate { - count - } - } - trackassessment(limit: $limit, offset: $offset, where: {${query}}) { - answersheet - filter - created_at - updated_at - trackAssessmentId - questions - score - totalScore - source - studentId - teacherId - groupId - subject - date - type - status - } -}`, - variables: { - limit: parseInt(limit), - offset: offset, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - const result = await this.mappedResponse( - response.data.data.trackassessment - ); - const count = - response?.data?.data?.trackassessment_aggregate?.aggregate?.count; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - totalCount: count, - data: result, - }); - } - - public async mappedResponse(result: any) { - const trackAssessmentResponse = result.map((obj: any) => { - const trackAssessmentMapping = { - id: obj?.trackAssessmentId ? `${obj.trackAssessmentId}` : "", - trackAssessmentId: obj?.trackAssessmentId - ? `${obj.trackAssessmentId}` - : "", - filter: obj?.filter ? `${obj.filter}` : "", - type: obj?.type ? `${obj.type}` : "", - questions: obj?.questions ? obj.questions : "", - source: obj?.source ? `${obj.source}` : "", - answersheet: obj?.answersheet ? `${obj.answersheet}` : "", - score: obj?.score ? `${obj.score}` : "", - totalScore: obj?.totalScore ? `${obj.totalScore}` : "", - studentId: obj?.studentId ? `${obj.studentId}` : "", - teacherId: obj?.teacherId ? `${obj.teacherId}` : "", - groupId: obj?.groupId ? `${obj.groupId}` : "", - subject: obj?.subject ? `${obj.subject}` : "", - date: obj?.date ? `${obj.date}` : "", - status: obj?.status ? `${obj.status}` : "", - createdAt: obj?.created_at ? `${obj.created_at}` : "", - updatedAt: obj?.updated_at ? `${obj.updated_at}` : "", - }; - return new TrackAssessmentDto(trackAssessmentMapping); - }); - - return trackAssessmentResponse; - } -} diff --git a/src/adapters/hasura/user.adapter.ts b/src/adapters/hasura/user.adapter.ts index b546dd17..0f31aa23 100644 --- a/src/adapters/hasura/user.adapter.ts +++ b/src/adapters/hasura/user.adapter.ts @@ -7,15 +7,17 @@ import jwt_decode from "jwt-decode"; import { UserSearchDto } from "src/user/dto/user-search.dto"; import { ErrorResponse } from "src/error-response"; import { FieldsService } from "./services/fields.service"; +import { UserData } from "src/user/user.controller"; // import { UserUpdateDto } from "src/user/dto/user-update.dto"; import { getUserRole, getKeycloakAdminToken, createUserInKeyCloak, checkIfUsernameExistsInKeycloak, -} from "./keycloak.adapter.util"; +} from "../../common/utils/keycloak.adapter.util"; import { UserCreateDto } from "src/user/dto/user-create.dto"; import { FieldValuesDto } from "src/fields/dto/field-values.dto"; +import { Response } from "express"; @Injectable() export class HasuraUserService implements IServicelocator { @@ -25,335 +27,149 @@ export class HasuraUserService implements IServicelocator { private httpService: HttpService, private fieldsService: FieldsService ) {} + checkUser(body: any, response: any) { + throw new Error("Method not implemented."); + } + public async findUserDetails(userID: any, username: String) { + + } + public async getUsersDetailsById(userData: UserData, response:any) {} + public async getUsersDetailsByCohortId(userData: Record, response:any) {} - public async getUser( - tenantId: string, - userId: string, - accessRole: string, - request: any, - res: any - ) { - try { - const decoded: any = jwt_decode(request.headers.authorization); - const userRoles = - decoded["https://hasura.io/jwt/claims"]["x-hasura-allowed-roles"]; + public async checkAndAddUser(request: any, userDto: UserCreateDto) { + // try { + // const decoded: any = jwt_decode(request.headers.authorization); + // const altUserRoles = + // decoded["https://hasura.io/jwt/claims"]["x-hasura-allowed-roles"]; + + // const userId = + // decoded["https://hasura.io/jwt/claims"]["x-hasura-user-id"]; + // userDto.createdBy = userId; + // userDto.updatedBy = userId; + + // if ( + // !userDto.username || + // !userDto.password || + // !userDto.role || + // !userDto.name + // ) { + // return new ErrorResponse({ + // errorCode: "400", + // errorMessage: "Name, username, password and role are required", + // }); + // } + + // const keycloakResponse = await getKeycloakAdminToken(); + // const token = keycloakResponse.data.access_token; + + // const usernameExistsInKeycloak = await checkIfUsernameExistsInKeycloak( + // userDto.username, + // token + // ); + // if (usernameExistsInKeycloak?.data[0]?.username) { + // const usernameExistsInDB: any = await this.getUserByUsername( + // usernameExistsInKeycloak?.data[0]?.username, + // request + // ); + // if (usernameExistsInDB?.statusCode === 200) { + // if (usernameExistsInDB?.data) { + // return usernameExistsInDB; + // } else { + // const resetPasswordRes: any = await this.resetKeycloakPassword( + // request, + // token, + // userDto.password, + // usernameExistsInKeycloak?.data[0]?.id + // ); + + // if (resetPasswordRes.statusCode !== 204) { + // return new ErrorResponse({ + // errorCode: "400", + // errorMessage: "Something went wrong in password reset", + // }); + // } + + // userDto.userId = usernameExistsInKeycloak?.data[0]?.id; + // const newlyCreatedDbUser = await this.createUserInDatabase( + // request, + // userDto + // ); + + // return newlyCreatedDbUser; + // } + // } else { + // return usernameExistsInDB; + // } + // } else { + // return await this.createUser(request, userDto); + // } + // } catch (e) { + // console.error(e); + // return e; + // } + } - const data = { - query: `query GetUser($userId: uuid!, $tenantId: uuid!, $context: String!, $contextId: uuid!, $access : String!) { - Users(where: {tenantId: {_eq: $tenantId}, userId: {_eq: $userId}}) { - username - userId - name - email - district - state - address - pincode - mobile - dob - role - tenantId - updatedAt - updatedBy - createdBy - createdAt - fields: UsersFieldsTenants(where: {_or: [{contextId: {_is_null: true}}, {contextId: {_eq: $contextId}}], context: {_eq: $context}, _and: {_or: [{access: {_is_null: true}}, {access: {_eq: $access}}]}}) { - tenantId - fieldId - assetId - context - contextId - groupId - name - label - defaultValue - type - note - description - state - required - ordering - metadata - access - onlyUseInSubform - updatedAt - updatedBy - createdAt - createdBy - fieldValues: FieldValues(where: {itemId: {_eq: $contextId}}) { - value - itemId - fieldId - fieldValuesId - updatedBy - updatedAt - createdBy - createdAt - } - } + public async createUser(request: any, userCreateDto: UserCreateDto) { + } + + async createUserInDatabase(request: any, userCreateDto: UserCreateDto) { + try{ + let query = ""; + Object.keys(userCreateDto).forEach((e) => { + if ( + userCreateDto[e] && + userCreateDto[e] !== "" && + e != "password" && + e != "fieldValues" + ) { + if (e === "role") { + query += `${e}: ${userCreateDto[e]},`; + } else if (Array.isArray(userCreateDto[e])) { + query += `${e}: ${JSON.stringify(userCreateDto[e])}, `; + } else { + query += `${e}: ${JSON.stringify(userCreateDto[e])}, `; } - }`, - variables: { - userId: userId, - tenantId: tenantId, - context: "Users", - contextId: userId, - access: accessRole, - }, + } + }); + + const data = { + query: `mutation CreateUser { + insert_Users_one(object: {${query}}) { + userId + } + } + `, + variables: {}, }; - const config = { + var config = { method: "post", url: process.env.REGISTRYHASURA, headers: { Authorization: request.headers.authorization, - "x-hasura-role": getUserRole(userRoles), + "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, "Content-Type": "application/json", }, data: data, }; - const response = await this.axios(config); - if (response?.data?.errors) { - return res.status(400).send({ - errorCode: response?.data?.errors[0]?.extensions?.code, - errorMessage: response?.data?.errors[0]?.message, - }); - } else { - const result = response.data.data.Users; - return res.status(200).send({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - } catch (e) { - console.error(e); - return new ErrorResponse({ - errorCode: "400", - errorMessage: e, - }); - } - } - - public async checkAndAddUser(request: any, userDto: UserCreateDto) { - try { - const decoded: any = jwt_decode(request.headers.authorization); - const altUserRoles = - decoded["https://hasura.io/jwt/claims"]["x-hasura-allowed-roles"]; - - const userId = - decoded["https://hasura.io/jwt/claims"]["x-hasura-user-id"]; - userDto.createdBy = userId; - userDto.updatedBy = userId; - - if ( - !userDto.username || - !userDto.password || - !userDto.role || - !userDto.name - ) { + if (response?.data?.errors || userCreateDto.userId == undefined) { return new ErrorResponse({ - errorCode: "400", - errorMessage: "Name, username, password and role are required", + errorCode: response.data.errors[0].extensions, + errorMessage: response.data.errors[0].message, }); - } - - const keycloakResponse = await getKeycloakAdminToken(); - const token = keycloakResponse.data.access_token; - - const usernameExistsInKeycloak = await checkIfUsernameExistsInKeycloak( - userDto.username, - token - ); - if (usernameExistsInKeycloak?.data[0]?.username) { - const usernameExistsInDB: any = await this.getUserByUsername( - usernameExistsInKeycloak?.data[0]?.username, - request - ); - if (usernameExistsInDB?.statusCode === 200) { - if (usernameExistsInDB?.data) { - return usernameExistsInDB; - } else { - const resetPasswordRes: any = await this.resetKeycloakPassword( - request, - token, - userDto.password, - usernameExistsInKeycloak?.data[0]?.id - ); - - if (resetPasswordRes.statusCode !== 204) { - return new ErrorResponse({ - errorCode: "400", - errorMessage: "Something went wrong in password reset", - }); - } - - userDto.userId = usernameExistsInKeycloak?.data[0]?.id; - const newlyCreatedDbUser = await this.createUserInDatabase( - request, - userDto - ); - - return newlyCreatedDbUser; - } - } else { - return usernameExistsInDB; - } } else { - return await this.createUser(request, userDto); } - } catch (e) { + }catch (e) { console.error(e); return e; } } - public async createUser(request: any, userCreateDto: UserCreateDto) { - // It is considered that if user is not present in keycloak it is not present in database as well - try { - const decoded: any = jwt_decode(request.headers.authorization); - const userRoles = - decoded["https://hasura.io/jwt/claims"]["x-hasura-allowed-roles"]; - - const userId = - decoded["https://hasura.io/jwt/claims"]["x-hasura-user-id"]; - userCreateDto.createdBy = userId; - userCreateDto.updatedBy = userId; - - userCreateDto.username = userCreateDto.username.toLocaleLowerCase(); - - const userSchema = new UserCreateDto(userCreateDto); - - let errKeycloak = ""; - let resKeycloak = ""; - - // if (altUserRoles.includes("systemAdmin")) { - - const keycloakResponse = await getKeycloakAdminToken(); - const token = keycloakResponse.data.access_token; - - resKeycloak = await createUserInKeyCloak(userSchema, token).catch( - (error) => { - errKeycloak = error.response?.data.errorMessage; - - return new ErrorResponse({ - errorCode: "500", - errorMessage: "Someting went wrong", - }); - } - ); - // } else { - // return new ErrorResponse({ - // errorCode: "401", - // errorMessage: "Unauthorized", - // }); - // } - - // Add userId created in keycloak as user Id of ALT user - userCreateDto.userId = resKeycloak; - return await this.createUserInDatabase(request, userCreateDto); - } catch (e) { - console.error(e); - return e; - } - } - - async createUserInDatabase(request: any, userCreateDto: UserCreateDto) { - let query = ""; - Object.keys(userCreateDto).forEach((e) => { - if ( - userCreateDto[e] && - userCreateDto[e] !== "" && - e != "password" && - e != "fieldValues" - ) { - if (e === "role") { - query += `${e}: ${userCreateDto[e]},`; - } else if (Array.isArray(userCreateDto[e])) { - query += `${e}: ${JSON.stringify(userCreateDto[e])}, `; - } else { - query += `${e}: ${JSON.stringify(userCreateDto[e])}, `; - } - } - }); - - const data = { - query: `mutation CreateUser { - insert_Users_one(object: {${query}}) { - userId - } - } - `, - variables: {}, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - const response = await this.axios(config); - - if (response?.data?.errors || userCreateDto.userId == undefined) { - return new ErrorResponse({ - errorCode: response.data.errors[0].extensions, - errorMessage: response.data.errors[0].message, - }); - } else { - const result = response.data.data.insert_Users_one; - - let fieldCreate = true; - let fieldError = null; - //create fields values - let userId = result?.userId; - let field_value_array = userCreateDto.fieldValues?.split("|"); - - if (field_value_array?.length > 0) { - let field_values = []; - for (let i = 0; i < field_value_array.length; i++) { - let fieldValues = field_value_array[i].split(":"); - field_values.push({ - value: fieldValues[1] ? fieldValues[1] : "", - itemId: userId, - fieldId: fieldValues[0] ? fieldValues[0] : "", - createdBy: userCreateDto?.createdBy, - updatedBy: userCreateDto?.updatedBy, - }); - } - - const response_field_values = - await this.fieldsService.createFieldValuesBulk(field_values); - if (response_field_values?.data?.errors) { - fieldCreate = false; - fieldError = response_field_values?.data; - } - } - - if (fieldCreate) { - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } else { - return new ErrorResponse({ - errorCode: fieldError?.errors[0]?.extensions?.code, - errorMessage: fieldError?.errors[0]?.message, - }); - } - } - } - public async updateUser( - userId: string, - request: any, - userUpdateDto: UserCreateDto + userUpdateDto: UserCreateDto, + userData ) { let query = ""; Object.keys(userUpdateDto).forEach((e) => { @@ -388,7 +204,7 @@ export class HasuraUserService implements IServicelocator { } `, variables: { - userId: userId, + userId: userUpdateDto.userId, }, }; @@ -410,42 +226,6 @@ export class HasuraUserService implements IServicelocator { errorMessage: response?.data?.errors[0]?.message, }); } else { - let result = response.data.update_Users_by_pk; - let fieldCreate = true; - let fieldError = []; - //update fields values - let field_value_array = userUpdateDto.fieldValues?.split("|"); - if (field_value_array?.length > 0) { - for (let i = 0; i < field_value_array.length; i++) { - let fieldValues = field_value_array[i].split(":"); - //update values - let fieldValuesUpdate = new FieldValuesDto({ - value: fieldValues[1] ? fieldValues[1] : "", - }); - - const response_field_values = - await this.fieldsService.updateFieldValues( - fieldValues[0] ? fieldValues[0] : "", - fieldValuesUpdate - ); - if (response_field_values?.data?.errors) { - fieldCreate = false; - fieldError.push(response_field_values?.data); - } - } - } - if (fieldCreate) { - return new SuccessResponse({ - statusCode: 200, - message: "User updated successfully ", - data: result, - }); - } else { - return new ErrorResponse({ - errorCode: "filed value update error", - errorMessage: JSON.stringify(fieldError), - }); - } } } @@ -466,6 +246,7 @@ export class HasuraUserService implements IServicelocator { if (fieldsFilter) { //apply filter on fields value + // searchfieldValuesFilter returns the contexts here userId that match the fieldId and value pair const responseFieldsValue = await this.fieldsService.searchFieldValuesFilter(request,fieldsFilter); @@ -522,7 +303,7 @@ export class HasuraUserService implements IServicelocator { const count = result.length; //get user fields value - let result_data = await this.searchUserFields(tenantId, userResponse); + let result_data = await this.searchUserFields(request,tenantId, userResponse); return response.status(200).send({ statusCode: 200, @@ -824,7 +605,7 @@ export class HasuraUserService implements IServicelocator { } } - public async searchUserFields(tenantId: string, users: any) { + public async searchUserFields(request:any, tenantId: string, users: any) { // function uses field service to get extra field and respective fieldValues for each user // ****Need extra field for access via role let userWithFields = []; @@ -833,6 +614,7 @@ export class HasuraUserService implements IServicelocator { let userId = new_obj["userId"]; //get fields let response = await this.fieldsService.getFieldsContext( + request, tenantId, "Users", userId @@ -857,7 +639,6 @@ export class HasuraUserService implements IServicelocator { ) { // function to search users within the user tables try { - const decoded: any = jwt_decode(request.headers.authorization); let offset = 0; if (userSearchDto.page > 1) { offset = parseInt(userSearchDto.limit) * (userSearchDto.page - 1); @@ -929,4 +710,7 @@ export class HasuraUserService implements IServicelocator { return e; } } -} + + public async deleteUserById(userId){} + +} \ No newline at end of file diff --git a/src/adapters/hasura/userTenantMapping.adapter.ts b/src/adapters/hasura/userTenantMapping.adapter.ts new file mode 100644 index 00000000..5188c0bf --- /dev/null +++ b/src/adapters/hasura/userTenantMapping.adapter.ts @@ -0,0 +1,18 @@ +import { BadRequestException, ConsoleLogger, HttpStatus, Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { In, Repository } from 'typeorm'; +import { UserTenantMapping } from 'src/userTenantMapping/entities/user-tenant-mapping.entity'; +import { UserTenantMappingDto } from "src/userTenantMapping/dto/user-tenant-mapping.dto"; +import { IServicelocatorAssignTenant } from '../usertenantmappinglocator'; + +@Injectable() +export class HasuraAssignTenantService implements IServicelocatorAssignTenant { + constructor( + @InjectRepository(UserTenantMapping) + private userTenantMappingRepository: Repository, + + ) { } + public async userTenantMapping(request: any, assignTenantMappingDto:UserTenantMappingDto) { + } + +} diff --git a/src/adapters/hasura/workhistory.adapter.ts b/src/adapters/hasura/workhistory.adapter.ts deleted file mode 100644 index 9e258984..00000000 --- a/src/adapters/hasura/workhistory.adapter.ts +++ /dev/null @@ -1,263 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { HttpService } from "@nestjs/axios"; -import { SuccessResponse } from "src/success-response"; -import { WorkHistoryDto } from "../../workHistory/dto/work-history.dto"; - -@Injectable() -export class WorkHistoryService { - constructor(private httpService: HttpService) {} - - public async createWorkHistory(request: any, workHistoryDto: WorkHistoryDto) { - var axios = require("axios"); - - let query = ""; - Object.keys(workHistoryDto).forEach((e) => { - if (workHistoryDto[e] && workHistoryDto[e] != "") { - query += `${e}: "${workHistoryDto[e]}", `; - } - }); - - var data = { - query: `mutation CreateWorkHistory { - insert_workhistory_one(object: {${query}}) { - workHistoryId - } - } - `, - variables: {}, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - const result = response.data.data.insert_workhistory_one; - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async updateWorkHistory( - id: string, - request: any, - workHistoryDto: WorkHistoryDto - ) { - var axios = require("axios"); - - let query = ""; - Object.keys(workHistoryDto).forEach((e) => { - if (workHistoryDto[e] && workHistoryDto[e] != "") { - query += `${e}: "${workHistoryDto[e]}", `; - } - }); - - var data = { - query: `mutation UpdateWorkHistory($workHistoryId:uuid) { - update_workhistory(where: {workHistoryId: {_eq: $worksHitoryId}}, _set: {${query}}) { - affected_rows - } -}`, - variables: { - workHistoryId: id, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - const result = response.data.data; - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async getWorkHistory(workHistoryId: any, request: any) { - var axios = require("axios"); - - var data = { - query: `query GetWorkHistory($workHistoryId:uuid) { - workhistory(where: {workHistoryId: {_eq: $workHistoryId}}) { - workHistoryId - cadre - created_at - dateOfJoining - dateOfOrder - dateOfRelieving - joiningDesignation - leavingDesignation - modeOfPosting - placeOfPosting - reason - remark - role - transferOrderNumber - updated_at - userId - organizationName - } - } - `, - variables: { workHistoryId: workHistoryId }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - let result = await this.mappedResponse(response?.data?.data?.workhistory); - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async searchWorkHistory( - limit: string, - workHistoryId: string, - userId: string, - dateOfJoining: string, - dateOfRelieving: string, - page: number, - request: any - ) { - var axios = require("axios"); - - let offset = 0; - if (page > 1) { - offset = parseInt(limit) * (page - 1); - } - - const searchData = { - workHistoryId: workHistoryId, - userId: userId, - dateOfJoining: dateOfJoining, - dateOfRelieving: dateOfRelieving, - }; - - let query = ""; - Object.keys(searchData).forEach((e) => { - if (searchData[e] && searchData[e] != "") { - query += `${e}:{_eq:"${searchData[e]}"}`; - } - }); - - var data = { - query: `query SearchWorkHistory($limit:Int, $offset:Int) { - workhistory_aggregate { - aggregate { - count - } - } - workhistory(where:{ ${query}}, limit: $limit, offset: $offset,) { - workHistoryId - cadre - created_at - dateOfJoining - dateOfOrder - dateOfRelieving - joiningDesignation - leavingDesignation - modeOfPosting - placeOfPosting - reason - remark - role - transferOrderNumber - updated_at - userId - organizationName - } - }`, - variables: { - limit: parseInt(limit), - offset: offset, - }, - }; - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - let result = await this.mappedResponse(response.data.data.workhistory); - const count = response?.data?.data?.workhistory_aggregate?.aggregate?.count; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - totalCount: count, - data: result, - }); - } - - public async mappedResponse(result: any) { - const workHistoryResponse = result.map((obj: any) => { - const workHistoryMapping = { - id: obj?.workHistoryId ? `${obj.workHistoryId}` : "", - workHistoryId: obj?.workHistoryId ? `${obj.workHistoryId}` : "", - userId: obj?.userId ? `${obj.userId}` : "", - role: obj?.role ? `${obj.role}` : "", - joiningDesignation: obj?.joiningDesignation - ? `${obj.joiningDesignation}` - : "", - leavingDesignation: obj?.leavingDesignation - ? `${obj.leavingDesignation}` - : "", - dateOfJoining: obj?.dateOfJoining ? obj.dateOfJoining : "", - dateOfRelieving: obj?.dateOfRelieving ? obj.dateOfRelieving : "", - reason: obj?.reason ? `${obj.reason}` : "", - remark: obj?.remark ? `${obj.remark}` : "", - cadre: obj?.cadre ? `${obj.cadre}` : "", - transferOrderNumber: obj?.transferOrderNumber - ? `${obj.transferOrderNumber}` - : "", - placeOfPosting: obj?.placeOfPosting ? `${obj.placeOfPosting}` : "", - dateOfOrder: obj?.dateOfOrder ? obj.dateOfOrder : "", - modeOfPosting: obj?.modeOfPosting ? `${obj.modeOfPosting}` : "", - organizationName: obj?.organizationName - ? `${obj.organizationName}` - : "", - createdAt: obj?.created_at ? `${obj.created_at}` : "", - updatedAt: obj?.updated_at ? `${obj.updated_at}` : "", - }; - return new WorkHistoryDto(workHistoryMapping); - }); - - return workHistoryResponse; - } -} diff --git a/src/adapters/hasura/worksheet.adapter.ts b/src/adapters/hasura/worksheet.adapter.ts deleted file mode 100644 index 4d9bad13..00000000 --- a/src/adapters/hasura/worksheet.adapter.ts +++ /dev/null @@ -1,613 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { HttpService } from "@nestjs/axios"; -import { SuccessResponse } from "src/success-response"; -import { WorksheetDto } from "src/worksheet/dto/worksheet.dto"; -import { WorksheetSearchDto } from "src/worksheet/dto/worksheet-search.dto"; -import { StudentDto } from "src/student/dto/student.dto"; -import { ErrorResponse } from "src/error-response"; -import { TemplateProcessDto } from "src/template/dto/template-process.dto"; - -@Injectable() -export class WorksheetService { - constructor(private httpService: HttpService) {} - questionurl = process.env.DIKSHADEVBASEAPIURL; - templateurl = process.env.TEMPLATERURL; - url = `${process.env.BASEAPIURL}`; - public async createWorksheet(request: any, worksheetDto: WorksheetDto) { - var axios = require("axios"); - const worksheetSchema = new WorksheetDto(worksheetDto); - let query = ""; - Object.keys(worksheetDto).forEach((e) => { - if ( - worksheetDto[e] && - worksheetDto[e] != "" && - Object.keys(worksheetSchema).includes(e) - ) { - if (Array.isArray(worksheetDto[e])) { - query += `${e}: ${JSON.stringify(worksheetDto[e])}, `; - } else { - query += `${e}: "${worksheetDto[e]}", `; - } - } - }); - - var data = { - query: `mutation CreateWorksheet { - insert_worksheet_one(object: {${query}}) { - worksheetId - } - } - `, - variables: {}, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - const result = response.data.data.insert_worksheet_one; - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async updateWorksheet( - id: string, - request: any, - worksheetDto: WorksheetDto - ) { - var axios = require("axios"); - const worksheetSchema = new WorksheetDto(worksheetDto); - let query = ""; - Object.keys(worksheetDto).forEach((e) => { - if ( - worksheetDto[e] && - worksheetDto[e] != "" && - Object.keys(worksheetSchema).includes(e) - ) { - if (Array.isArray(worksheetDto[e])) { - query += `${e}: ${JSON.stringify(worksheetDto[e])}, `; - } else { - query += `${e}: ${worksheetDto[e]}, `; - } - } - }); - - var data = { - query: `mutation UpdateWorksheet($worksheetId:uuid) { - update_worksheet(where: {worksheetId: {_eq: $worksheetId}}, _set: {${query}}) { - affected_rows - } -}`, - variables: { - worksheetId: id, - }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - const result = response.data.data; - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: result, - }); - } - - public async getWorksheet(worksheetId: any, request: any) { - var axios = require("axios"); - - var data = { - query: `query GetWorksheet($worksheetId:uuid!) { - worksheet_by_pk(worksheetId: $worksheetId) { - created_at - feedback - criteria - grade - hints - instructions - level - name - navigationMode - outcomeDeclaration - outcomeProcessing - purpose - questionSetType - questionSets - questions - qumlVersion - showHints - source - state - subject - timeLimits - topic - updated_at - usedFor - visibility - worksheetId - } - } - `, - variables: { worksheetId: worksheetId }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - let result = [response.data.data.worksheet_by_pk]; - const worksheetResponse = await this.mappedResponse(result); - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: worksheetResponse[0], - }); - } - - public async searchWorksheet( - worksheetSearchDto: WorksheetSearchDto, - request: any - ) { - var axios = require("axios"); - - let offset = 0; - if (worksheetSearchDto.page > 1) { - offset = - parseInt(worksheetSearchDto.limit) * (worksheetSearchDto.page - 1); - } - - let filters = worksheetSearchDto.filters; - - Object.keys(worksheetSearchDto.filters).forEach((item) => { - Object.keys(worksheetSearchDto.filters[item]).forEach((e) => { - if (!e.startsWith("_")) { - filters[item][`_${e}`] = filters[item][e]; - delete filters[item][e]; - } - }); - }); - - var data = { - query: `query SearchWorksheet($filters:worksheet_bool_exp,$limit:Int, $offset:Int) { - worksheet_aggregate { - aggregate { - count - } - } - worksheet(where:$filters, limit: $limit, offset: $offset,) { - created_at - feedback - criteria - grade - hints - instructions - level - name - navigationMode - outcomeDeclaration - outcomeProcessing - purpose - questionSetType - questionSets - questions - qumlVersion - showHints - source - state - subject - timeLimits - topic - updated_at - usedFor - visibility - worksheetId - } - }`, - variables: { - limit: parseInt(worksheetSearchDto.limit), - offset: offset, - filters: worksheetSearchDto.filters, - }, - }; - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - let result = response.data.data.worksheet; - const worksheetResponse = await this.mappedResponse(result); - const count = response?.data?.data?.worksheet_aggregate?.aggregate?.count; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - totalCount: count, - data: worksheetResponse, - }); - } - - public async downloadWorksheet( - worksheetId: any, - templateId: any, - request: any - ) { - var axios = require("axios"); - var template_id = parseInt(templateId); - - const templateDetail = await axios.get( - `${this.templateurl}${template_id}`, - { - headers: { - Authorization: request.headers.authorization, - }, - } - ); - - const templateData = templateDetail.data; - var templateTags = templateData.tag; - - var worksheetData = { - query: `query GetWorksheet($worksheetId:uuid) { - worksheet(where: {worksheetId: {_eq: $worksheetId}}) { - created_at - feedback - criteria - grade - hints - instructions - level - name - navigationMode - outcomeDeclaration - outcomeProcessing - purpose - questionSetType - questionSets - questions - qumlVersion - showHints - source - state - subject - timeLimits - topic - updated_at - usedFor - visibility - worksheetId - } - } - `, - variables: { worksheetId: worksheetId }, - }; - - var config = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: worksheetData, - }; - - const response = await axios(config); - - let resData = response.data.data.worksheet[0]; - - let questionIds = resData.questions; - - let questionsArray = []; - - for (let value of questionIds) { - let qData = { - method: "get", - url: `${this.questionurl}/question/v1/read/${value}?fields=body`, - }; - const response = await axios(qData); - const data = response?.data; - const final = data.result.question; - - if (templateTags.includes("with_answers")) { - questionsArray.push( - "
  • " + final.body + "
    Ans -


  • " - ); - } else { - questionsArray.push("
  • " + final.body + "
  • "); - } - } - - var data = { - config_id: 1, - data: { - title: resData.name, - grade: resData.grade, - subject: resData.subject, - questions: questionsArray, - }, - template_id: template_id, - }; - - const pdf = await axios.post( - `http://68.183.94.187:8000/generate/?plugin=pdf`, - data, - { - headers: { - "Content-Type": "application/json", - }, - } - ); - - const pdfUrl = pdf.data; - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: pdfUrl, - }); - } - - public async mappedResponse(result: any) { - const worksheetResponse = result.map((item: any) => { - const worksheetMapping = { - id: item?.worksheetId ? `${item.worksheetId}` : "", - worksheetId: item?.worksheetId ? `${item.worksheetId}` : "", - name: item?.name ? `${item.name}` : "", - state: item?.state ? `${item.state}` : "", - subject: item?.subject ? `${item.subject}` : "", - grade: item?.grade ? `${item.grade}` : "", - level: item?.level ? `${item.level}` : "", - instructions: item?.instructions ? `${item.instructions}` : "", - feedback: item?.feedback ? `${item.feedback}` : "", - hints: item?.hints ? `${item.hints}` : "", - navigationMode: item?.navigationMode ? `${item.navigationMode}` : "", - timeLimits: item?.timeLimits ? `${item.timeLimits}` : "", - showHints: item?.showHints ? item.showHints : "", - questions: item?.questions ? item.questions : "", - questionSets: item?.questionSets ? `${item.questionSets}` : "", - outcomeDeclaration: item?.outcomeDeclaration - ? `${item.outcomeDeclaration}` - : "", - outcomeProcessing: item?.outcomeProcessing - ? `${item.outcomeProcessing}` - : "", - questionSetType: item?.questionSetType ? `${item.questionSetType}` : "", - criteria: item?.criteria ? `${item.criteria}` : "", - usedFor: item?.usedFor ? `${item.usedFor}` : "", - purpose: item?.purpose ? `${item.purpose}` : "", - visibility: item?.visibility ? `${item.visibility}` : "", - qumlVersion: item?.qumlVersion ? `${item.qumlVersion}` : "", - topic: item?.topic ? item.topic : "", - source: item?.source ? `${item.source}` : "", - createdAt: item?.created_at ? `${item.created_at}` : "", - updatedAt: item?.updated_at ? `${item.updated_at}` : "", - }; - return new WorksheetDto(worksheetMapping); - }); - - return worksheetResponse; - } - - public async sendWorksheet( - studentIds: [string], - teacherId: string, - templateId: string, - link: string, - subject: string, - topic: string, - request: any - ) { - var axios = require("axios"); - const teacherResponse = await axios.get(`${this.url}User/${teacherId}`); - const teacher = teacherResponse.data; - const templateDetail = await axios.get(`${this.templateurl}${templateId}`); - const templateData = templateDetail.data; - - var getSchool = { - query: `query GetSchool($schoolId:uuid!) { - school_by_pk(schoolId: $schoolId) { - schoolName - } -}`, - variables: { - schoolId: teacher.schoolId, - }, - }; - - var schoolCall = { - method: "post", - url: process.env.REGISTRYHASURA, - headers: { - "x-hasura-admin-secret": process.env.REGISTRYHASURAADMINSECRET, - "Content-Type": "application/json", - }, - data: getSchool, - }; - - const schoolResponse = await axios(schoolCall); - - let schoolData = schoolResponse?.data?.data?.school_by_pk; - studentIds.map(async (studentId) => { - const student = await axios.get(`${this.url}Student/${studentId}`); - - const process = { - id: parseInt(templateId), - data: { - studentName: - (student?.data?.firstName ? student?.data?.firstName : "") + - " " + - (student?.data?.lastName ? student?.data?.lastName : ""), - subject: subject, - topic: topic, - teacherName: - (teacher?.firstName ? teacher?.firstName : "") + - " " + - (teacher?.lastName ? teacher?.lastName : ""), - schoolName: schoolData.schoolName, - link: link, - }, - }; - - var templateCall = { - method: "post", - url: `${this.templateurl}process`, - headers: { - Authorization: request.headers.authorization, - }, - data: process, - }; - const responseData = await axios(templateCall); - - const templateDataResponse = responseData.data; - var data = { - adapterId: templateData.user, - to: { - userID: student.data.studentPhoneNumber, - deviceType: "PHONE", - }, - payload: { - text: templateDataResponse.processed, - }, - }; - - var smsSend = { - method: "post", - url: "http://143.110.255.220:9090/message/send", - headers: { - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(smsSend); - - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: response.data, - }); - }); - } - - public async StudentMappedResponse(result: any) { - const studentResponse = result.map((item: any) => { - const studentMapping = { - studentId: item?.osid ? `${item.osid}` : "", - refId1: item?.admissionNo ? `${item.admissionNo}` : "", - refId2: item?.refId2 ? `${item.refId2}` : "", - aadhaar: item?.aadhaar ? `${item.aadhaar}` : "", - firstName: item?.firstName ? `${item.firstName}` : "", - middleName: item?.middleName ? `${item.middleName}` : "", - lastName: item?.lastName ? `${item.lastName}` : "", - groupId: item?.groupId ? `${item.groupId}` : "", - schoolId: item?.schoolId ? `${item.schoolId}` : "", - studentEmail: item?.studentEmail ? `${item.studentEmail}` : "", - studentPhoneNumber: item?.studentPhoneNumber - ? item.studentPhoneNumber - : "", - iscwsn: item?.iscwsn ? `${item.iscwsn}` : "", - gender: item?.gender ? `${item.gender}` : "", - socialCategory: item?.socialCategory ? `${item.socialCategory}` : "", - religion: item?.religion ? `${item.religion}` : "", - singleGirl: item?.singleGirl ? item.singleGirl : "", - weight: item?.weight ? `${item.weight}` : "", - height: item?.height ? `${item.height}` : "", - bloodGroup: item?.bloodGroup ? `${item.bloodGroup}` : "", - birthDate: item?.birthDate ? `${item.birthDate}` : "", - homeless: item?.homeless ? item.homeless : "", - bpl: item?.bpl ? item.bpl : "", - migrant: item?.migrant ? item.migrant : "", - status: item?.status ? `${item.status}` : "", - - fatherFirstName: item?.fatherFirstName ? `${item.fatherFirstName}` : "", - - fatherMiddleName: item?.fatherMiddleName - ? `${item.fatherMiddleName}` - : "", - - fatherLastName: item?.fatherLastName ? `${item.fatherLastName}` : "", - fatherPhoneNumber: item?.fatherPhoneNumber - ? item.fatherPhoneNumber - : "", - fatherEmail: item?.fatherEmail ? `${item.fatherEmail}` : "", - - motherFirstName: item?.motherFirstName ? `${item.motherFirstName}` : "", - motherMiddleName: item?.motherMiddleName - ? `${item.motherMiddleName}` - : "", - motherLastName: item?.motherLastName ? `${item.motherLastName}` : "", - motherPhoneNumber: item?.motherPhoneNumber - ? item.motherPhoneNumber - : "", - motherEmail: item?.motherEmail ? `${item.motherEmail}` : "", - - guardianFirstName: item?.guardianFirstName - ? `${item.guardianFirstName}` - : "", - guardianMiddleName: item?.guardianMiddleName - ? `${item.guardianMiddleName}` - : "", - guardianLastName: item?.guardianLastName - ? `${item.guardianLastName}` - : "", - guardianPhoneNumber: item?.guardianPhoneNumber - ? item.guardianPhoneNumber - : "", - guardianEmail: item?.guardianEmail ? `${item.guardianEmail}` : "", - image: item?.image ? `${item.image}` : "", - deactivationReason: item?.deactivationReason - ? `${item.deactivationReason}` - : "", - studentAddress: item?.studentAddress ? `${item.studentAddress}` : "", - village: item?.village ? `${item.village}` : "", - block: item?.block ? `${item.block}` : "", - district: item?.district ? `${item.district}` : "", - stateId: item?.stateId ? `${item.stateId}` : "", - pincode: item?.pincode ? item.pincode : "", - locationId: item?.locationId ? `${item.locationId}` : "", - metaData: item?.metaData ? item.metaData : [], - createdAt: item?.osCreatedAt ? `${item.osCreatedAt}` : "", - updatedAt: item?.osUpdatedAt ? `${item.osUpdatedAt}` : "", - createdBy: item?.osCreatedBy ? `${item.osCreatedBy}` : "", - updatedBy: item?.osUpdatedBy ? `${item.osUpdatedBy}` : "", - }; - return new StudentDto(studentMapping); - }); - - return studentResponse; - } -} diff --git a/src/adapters/holidayservicelocator.ts b/src/adapters/holidayservicelocator.ts deleted file mode 100644 index e6b0e687..00000000 --- a/src/adapters/holidayservicelocator.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { HolidaySearchDto } from "src/holiday/dto/holiday-search.dto"; -import { HolidayDto } from "src/holiday/dto/holiday.dto"; - -export interface IServicelocator { - getHoliday(holidayId: string, request: any); - createHoliday(request: any, holidayDto: HolidayDto); - updateHoliday(holidayId: string, request: any, holidayDto: HolidayDto); - searchHoliday(request: any, holidaySearchDto: HolidaySearchDto); - holidayFilter(fromDate: string, toDate: string, request: any); -} diff --git a/src/adapters/likeservicelocator.ts b/src/adapters/likeservicelocator.ts deleted file mode 100644 index d5728291..00000000 --- a/src/adapters/likeservicelocator.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { LikeSearchDto } from "src/like/dto/like-search.dto"; -import { LikeDto } from "src/like/dto/like.dto"; - -export interface IServicelocator { - getLike(likeId: string, request: any); - createLike(request: any, likeDto: LikeDto); - updateLike(likeId: string, request: any, likeDto: LikeDto); - searchLike(request: any, likeSearchDto: LikeSearchDto); - getCountLike(contextId: string, context: string, request: any); - deleteLike(likeId: string, request: any); -} diff --git a/src/adapters/postgres/attendance-adapter.ts b/src/adapters/postgres/attendance-adapter.ts new file mode 100644 index 00000000..7d246fcd --- /dev/null +++ b/src/adapters/postgres/attendance-adapter.ts @@ -0,0 +1,828 @@ +import { User } from '../../user/entities/user-entity'; +import { ConfigService } from '@nestjs/config'; +import { InjectRepository } from "@nestjs/typeorm"; +import { AttendanceEntity } from "../../attendance/entities/attendance.entity"; +import { Repository, Between, In, MoreThanOrEqual, LessThanOrEqual } from "typeorm"; +import { ConsoleLogger, HttpStatus, Injectable } from "@nestjs/common"; +import { AttendanceSearchDto } from "../../attendance/dto/attendance-search.dto"; +import { SuccessResponse } from 'src/success-response'; +import { AttendanceDto, BulkAttendanceDTO } from '../../attendance/dto/attendance.dto'; +import { AttendanceDateDto } from '../../attendance/dto/attendance-date.dto'; +import { AttendanceStatsDto } from '../../attendance/dto/attendance-stats.dto'; +import { ErrorResponseTypeOrm } from 'src/error-response-typeorm'; +import { CohortMembers } from 'src/cohortMembers/entities/cohort-member.entity'; +const moment = require('moment'); +const facetedSearch = require("in-memory-faceted-search"); + +@Injectable() +export class PostgresAttendanceService { + constructor(private configService: ConfigService, + @InjectRepository(AttendanceEntity) + private attendanceRepository: Repository, + @InjectRepository(User) + private userRepository: Repository, + @InjectRepository(CohortMembers) + private cohortMembersRepository: Repository + ) { } + + + + /* + Method to search attendance for all or for the key value pair provided in filter object + @body an object of details consisting of attendance details of user (attendance dto) + @return Attendance records from attendance table for provided filters + */ + + async searchAttendance(tenantId: string, request: any, attendanceSearchDto: AttendanceSearchDto) { + try { + let { limit, page, filters, facets, sort } = attendanceSearchDto; + // Set default limit to 0 if not provided + if (!limit) { + limit = 20; + } + + let offset = 0; + // Calculate offset based on page number + if (page > 1) { + offset = (limit) * (page - 1); + } + + // Get column names from metadata + const attendanceKeys = this.attendanceRepository.metadata.columns.map((column) => column.propertyName); + + let whereClause: any = { tenantId }; + // Default WHERE clause for filtering by tenantId + + if (filters && Object.keys(filters).length > 0) { + for (const [key, value] of Object.entries(filters)) { + if (attendanceKeys.includes(key)) { + if (key === "attendanceDate") { + // For attendanceDate, consider NULL values as well + whereClause[key] = In([value, null]); + } else { + whereClause[key] = value; + } + } else if (filters.fromDate && filters.toDate) { + // Convert fromDate and toDate strings to Date objects + const fromDate = new Date(filters.fromDate); + const toDate = new Date(filters.toDate); + + // Construct the whereClause with the date range using Between + whereClause["attendanceDate"] = Between(fromDate, toDate); + } else { + // If filter key is invalid, return a BadRequest response + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.BAD_REQUEST, + errorMessage: `${key} Invalid filter key`, + }); + } + } + } + + let attendanceList; + + if (!facets) { + let orderOption: any = {}; + if (sort && Array.isArray(sort) && sort.length === 2) { + const [column, order] = sort; + if (attendanceKeys.includes(column)) { + orderOption[column] = order.toUpperCase();; + } else { + // If sort key is invalid, return a BadRequest response + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.BAD_REQUEST, + errorMessage: `${column} Invalid sort key`, + }); + } + } + + attendanceList = await this.attendanceRepository.find({ + where: whereClause, + order: orderOption, // Apply sorting option + }); + const paginatedAttendanceList = attendanceList.slice(offset, offset + (limit)); + return new SuccessResponse({ + statusCode: HttpStatus.OK, + message: 'Ok.', + data: { + attendanceList: paginatedAttendanceList + }, + }); + } + + if (facets && facets.length > 0) { + attendanceList = await this.attendanceRepository.find({ + where: whereClause // Apply sorting option + }); + + let facetFields = []; + // Check for invalid facets + for (const facet of facets) { + if (!attendanceKeys.includes(facet)) { + // If facet is not present in attendanceKeys, return a BadRequest response + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.BAD_REQUEST, + errorMessage: `${facet} Invalid facet`, + }); + } + facetFields.push({ name: facet, field: facet }); + } + + let result = {}; + // Process the data to calculate counts based on facets + for (const facet of facetFields) { + const { field } = facet; + const tree = await this.facetedSearch({ data: attendanceList, facets: [facet], sort }); + + if (tree instanceof ErrorResponseTypeOrm) { + return tree + } + result[field] = tree[field]; + } + + // Return success response with counts + return new SuccessResponse({ + statusCode: HttpStatus.OK, + message: 'Ok.', + data: { + result: result, + }, + }); + } + + } catch (error) { + if (error.code === "22P02") { + // Handle invalid input value error + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.BAD_REQUEST, + errorMessage: `Invalid value entered for ${error.routine}`, + }); + } + // Handle other errors with internal server error response + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.INTERNAL_SERVER_ERROR, + errorMessage: error, // Use error message if available + }); + } + } + + + async facetedSearch({ data, facets, sort }) { + const tree = {}; + const attendanceKeys = new Set(); + + // Populate attendanceKeys with distinct attendance values + data.forEach(item => { + const attendanceValue = item.attendance; + if (attendanceValue) { + attendanceKeys.add(attendanceValue); + } + }); + + // Iterate over facets + for (const facet of facets) { + const { field } = facet; + + // Initialize main facet in tree + tree[field] = {}; + + // Iterate over data to count occurrences of each field value + for (const item of data) { + const value = item[field]; + const attendanceValue = item["attendance"]; + + // If contextId doesn't exist in the tree, initialize it with an empty object + if (!tree[field][value]) { + tree[field][value] = {}; + } + + // Increment count for attendanceValue + if (!tree[field][value][attendanceValue]) { + tree[field][value][attendanceValue] = 1; + } else { + tree[field][value][attendanceValue]++; + } + } + + // Calculate percentage for each value + for (const value in tree[field]) { + const counts = tree[field][value]; + const totalCount = Object.values(counts).reduce((acc: number, curr: unknown) => acc + (curr as number), 0); + + for (const key in counts) { + const count = counts[key]; + const percentage = (count / Number(totalCount)) * 100; // Convert totalCount to a number + counts[key + "_percentage"] = percentage.toFixed(2); // Round percentage to two decimal places + } + } + + // Assign default values for sorting + for (const value in tree[field]) { + for (const attendanceValue of attendanceKeys) { + const percentageKey = `${attendanceValue}_percentage`; + if (!tree[field][value].hasOwnProperty(percentageKey)) { + tree[field][value][percentageKey] = "0.00"; // Set default value internally + } + } + } + + // Validate sort keys + if (sort) { + const [sortField, sortOrder] = sort; + if (!attendanceKeys.has(sortField.replace("_percentage", "")) && sortField !== 'present_percentage' && sortField !== 'absent_percentage') { + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.BAD_REQUEST, + errorMessage: `Invalid sort[0]: ${sortField}`, + }); + } + + // Sort the tree based on the provided sort parameter + tree[field] = await this.sortTree(tree[field], sortField, sortOrder); + } + } + + // Remove default values from the response + for (const field in tree) { + for (const value in tree[field]) { + for (const attendanceValue of attendanceKeys) { + const percentageKey = `${attendanceValue}_percentage`; + if (tree[field][value][percentageKey] === "0.00") { + delete tree[field][value][percentageKey]; // Remove default value from response + } + } + } + } + + return tree; + } + + // Helper function to sort the tree based on the provided sortField and sortOrder + async sortTree(tree, sortField, sortOrder) { + const sortedTree = {}; + + // Convert the object keys (values of the sortField) into an array + const keys = Object.keys(tree); + + // Sort the keys based on the sortField and sortOrder + keys.sort((a, b) => { + const valueA = parseFloat(tree[a][sortField] || "0.00"); // Convert string to float + const valueB = parseFloat(tree[b][sortField] || "0.00"); // Convert string to float + + if (sortOrder === 'asc') { + return valueA - valueB; + } else { + return valueB - valueA; + } + }); + + // Populate the sortedTree with sorted data + keys.forEach(key => { + sortedTree[key] = tree[key]; + }); + + return sortedTree; + } + + + + + + + + + + + + + + async attendanceReport(attendanceStatsDto: AttendanceStatsDto) { + let { contextId, limit, offset, filters } = attendanceStatsDto; + try { + + let nameFilter = ''; + let userFilter = ''; + let dateFilter = ''; + let queryParams: any[] = [contextId]; + let subqueryParams: any[] = [contextId]; // Initialize query parameters array + let paramIndex = 1; // Initialize parameter index + + if (filters && filters.search) { + nameFilter = `AND u."name" ILIKE $${++paramIndex}`; // Increment paramIndex + queryParams.push(`%${filters.search.trim()}%`); + subqueryParams.push(`%${filters.search.trim()}%`); + } + if (filters && filters.userId) { + userFilter = ` AND u."userId" = $${++paramIndex}`; // Increment paramIndex + queryParams.push(filters.userId.trim()); + subqueryParams.push(filters.userId.trim()); + + } + if (filters && filters.fromDate && filters.toDate) { + dateFilter = `WHERE aa."attendanceDate" >= $${++paramIndex} AND aa."attendanceDate" <= $${++paramIndex}`; + queryParams.push(filters.fromDate); + queryParams.push(filters.toDate); + subqueryParams.push(filters.fromDate); + subqueryParams.push(filters.toDate); + } + + + let query = ` + SELECT + u."userId", + u."name", + CASE + WHEN aa_stats."total_attendance" = 0 THEN '-' + ELSE ROUND((aa_stats."present_count" * 100.0) / aa_stats."total_attendance", 0)::text + END AS attendance_percentage + FROM + public."Users" AS u + INNER JOIN + public."CohortMembers" AS cm ON cm."userId" = u."userId" + LEFT JOIN + ( + SELECT + aa."userId", + COUNT(*) AS "total_attendance", + COUNT(CASE WHEN aa."attendance" = 'present' THEN 1 END) AS "present_count" + FROM + public."Attendance" AS aa + ${dateFilter} + GROUP BY + aa."userId" + ) AS aa_stats ON cm."userId" = aa_stats."userId" + WHERE + cm."cohortId" = $1 + AND cm."role" = 'student' + ${nameFilter} + ${userFilter} + GROUP BY + u."userId", u."name", aa_stats."total_attendance", aa_stats."present_count" + `; + + if (filters) { + if (filters.nameOrder && (filters.nameOrder === "asc" || filters.nameOrder === "desc")) { + query += ` ORDER BY "name" ${filters.nameOrder}`; + } else if (filters.percentageOrder && (filters.percentageOrder === "asc" || filters.percentageOrder === "desc")) { + query += ` ORDER BY attendance_percentage ${filters.percentageOrder}`; + } + } + query += ` + LIMIT $${++paramIndex} + OFFSET $${++paramIndex}`; + + queryParams.push(limit); + queryParams.push(offset); + + + + const result = await this.attendanceRepository.query(query, queryParams); + + if (!filters || !filters?.userId) { + // We don't need average for single user + const countquery = ` + SELECT ROUND(AVG(attendance_percentage::NUMERIC), 2) AS average_attendance_percentage + FROM ( + SELECT + u."userId", + u."name", + CASE + WHEN aa_stats."total_attendance" = 0 THEN '-' + ELSE ROUND((aa_stats."present_count" * 100.0) / aa_stats."total_attendance", 0)::text + END AS attendance_percentage + FROM + public."Users" AS u + INNER JOIN + public."CohortMembers" AS cm ON cm."userId" = u."userId" + LEFT JOIN + ( + SELECT + aa."userId", + COUNT(*) AS "total_attendance", + COUNT(CASE WHEN aa."attendance" = 'present' THEN 1 END) AS "present_count" + FROM + public."Attendance" AS aa + ${dateFilter} + GROUP BY + aa."userId" + ) AS aa_stats ON cm."userId" = aa_stats."userId" + WHERE + cm."cohortId" = $1 + AND cm."role" = 'student' + ${nameFilter} + ${userFilter} + GROUP BY + u."userId", u."name", aa_stats."total_attendance", aa_stats."present_count" + ) AS subquery`; + + + const average = await this.attendanceRepository.query(countquery, subqueryParams); + const report = await this.mapResponseforReport(result); + const response = { + report, + average: average[0] + }; + return new SuccessResponse({ + statusCode: HttpStatus.OK, + message: "Ok.", + data: response + }); + } else { + + const response = await this.mapResponseforReport(result); + return new SuccessResponse({ + statusCode: HttpStatus.OK, + message: "Ok.", + data: response + }); + } + + + } catch (error) { + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.INTERNAL_SERVER_ERROR, + errorMessage: error + }); + } + } + + + + + + + + + public async mappedResponse(result: any) { + const attendanceResponse = result.map((item: any) => { + + const dateObject = new Date(item.attendanceDate); + const formattedDate = moment(dateObject).format('YYYY-MM-DD'); const attendanceMapping = { + tenantId: item?.tenantId ? `${item.tenantId}` : "", + attendanceId: item?.attendanceId ? `${item.attendanceId}` : "", + userId: item?.userId ? `${item.userId}` : "", + attendanceDate: item.attendanceDate ? formattedDate : null, + attendance: item?.attendance ? `${item.attendance}` : "", + remark: item?.remark ? `${item.remark}` : "", + latitude: item?.latitude ? item.latitude : 0, + longitude: item?.longitude ? item.longitude : 0, + image: item?.image ? `${item.image}` : "", + metaData: item?.metaData ? item.metaData : [], + syncTime: item?.syncTime ? `${item.syncTime}` : "", + session: item?.session ? `${item.session}` : "", + contextId: item?.contextId ? `${item.contextId}` : "", + contextType: item?.contextType ? `${item.contextType}` : "", + createdAt: item?.createdAt ? `${item.createdAt}` : "", + updatedAt: item?.updatedAt ? `${item.updatedAt}` : "", + createdBy: item?.createdBy ? `${item.createdBy}` : "", + updatedBy: item?.updatedBy ? `${item.updatedBy}` : "", + username: item?.username ? `${item.username}` : "", + role: item?.role ? `${item.role}` : "", + + }; + + + return new AttendanceDto(attendanceMapping); + }); + + return attendanceResponse; + } + + public async mapResponseforReport(result: any) { + const attendanceReport = result.map((item: any) => { + const attendanceReportMapping = { + name: item?.name ? `${item.name}` : "", + userId: item?.userId ? `${item.userId}` : "", + attendance_percentage: item?.attendance_percentage ? `${item.attendance_percentage}` : "", + }; + + return new AttendanceStatsDto(attendanceReportMapping); + }); + + return attendanceReport; + } + + public async mapAttendanceRecord(result: any) { + const attendanceRecords = result.map((item: any) => { + const dateObject = new Date(item.attendanceDate); + const formattedDate = moment(dateObject).format('YYYY-MM-DD'); + + let attendance = { + name: item?.name ? `${item.name}` : "", + userId: item?.userId ? `${item.userId}` : "", + attendance: item?.attendance ? `${item.attendance}` : "", + attendanceDate: item.attendanceDate ? formattedDate : null + + }; + return new AttendanceStatsDto(attendance); + }); + + return attendanceRecords; + } + /* + Method to create,update or add attendance for valid user in attendance table + @body an object of details consisting of attendance details of user (attendance dto) + @return updated details of attendance record + */ + + public async updateAttendanceRecord( + loginUserId, + attendanceDto: AttendanceDto + ) { + + + try { + + const Isvalid = await this.validateUserForCohort(attendanceDto.userId, attendanceDto.contextId) + if (!Isvalid) { + + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.BAD_REQUEST, + errorMessage: "Invalid combination of contextId and userId", + }); + + } + + const attendanceToSearch = new AttendanceSearchDto({}); + + attendanceToSearch.filters = { + attendanceDate: attendanceDto.attendanceDate, + userId: attendanceDto.userId, + }; + + const attendanceFound: any = await this.searchAttendance( + attendanceDto.tenantId, + loginUserId, + attendanceToSearch + ); + + if (attendanceFound instanceof ErrorResponseTypeOrm) { + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.INTERNAL_SERVER_ERROR, + errorMessage: attendanceFound?.errorMessage, + }); + } + + if ( + attendanceFound.data.attendanceList.length > 0 + ) { + + attendanceDto.updatedBy = loginUserId + return await this.updateAttendance( + attendanceFound.data.attendanceList[0].attendanceId, + loginUserId, + attendanceDto + ); + } else { + if (!attendanceDto.scope) { + attendanceDto.scope = "student" + } + attendanceDto.createdBy = loginUserId; + attendanceDto.updatedBy = loginUserId; + return await this.createAttendance(loginUserId, attendanceDto); + } + } catch (e) { + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.INTERNAL_SERVER_ERROR, + errorMessage: e, + }); + } + } + + /*Method to update attendance for userId + @body an object of details consisting of attendance details of user (attendance dto),attendanceId + @return updated attendance record based on attendanceId + */ + public async updateAttendance( + attendanceId: string, + request: any, + attendanceDto: AttendanceDto + ) { + try { + + + const attendanceRecord = await this.attendanceRepository.findOne({ + where: { attendanceId }, + }); + + + if (!attendanceRecord) { + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.BAD_REQUEST, + errorMessage: "Attendance record not found", + }); + } + + this.attendanceRepository.merge(attendanceRecord, attendanceDto); + + // Save the updated attendance record + const updatedAttendanceRecord = await this.attendanceRepository.save( + attendanceRecord + ); + + return new SuccessResponse({ + statusCode: HttpStatus.OK, + message: "Attendance record updated successfully", + data: updatedAttendanceRecord, + }); + } catch (error) { + + if (error.code == '23503') { + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.BAD_REQUEST, + errorMessage: "Please provide valid contextId", + }); + } + else { + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.INTERNAL_SERVER_ERROR, + errorMessage: error, + + }) + } + + } + } + + /*method to add attendance of new user in attendance + @body object containing details related to attendance details (AttendanceDto) + @return attendance record for newly added user in Attendance table + */ + public async createAttendance(request: any, attendanceDto: AttendanceDto) { + + try { + const attendance = this.attendanceRepository.create(attendanceDto); + const result = await this.attendanceRepository.save(attendance); + + return new SuccessResponse({ + statusCode: HttpStatus.CREATED, + message: "Attendance created successfully.", + data: result, + }); + } catch (error) { + + if (error.code == '23503') { + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.BAD_REQUEST, + errorMessage: "Please enter valid UserId and contextId", + }); + } else { + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.INTERNAL_SERVER_ERROR, + errorMessage: error, + }); + } + } + } + + /*Method to search attendance fromDate to toDate + @body object containing attendance date details for user (AttendanceDateDto) + @return attendance records from fromDate to toDate */ + + public async attendanceByDate( + tenantId: string, + request: any, + attendanceSearchDto: AttendanceDateDto + ) { + try { + + + let { limit, page } = attendanceSearchDto; + if (!limit) { + limit = '0'; + } + + let offset = 0; + if (page > 1) { + offset = parseInt(limit) * (page - 1); + } + + const fromDate = new Date(attendanceSearchDto.fromDate); + const toDate = new Date(attendanceSearchDto.toDate); + + let whereClause: any = { + tenantId: tenantId ? tenantId : '', + attendanceDate: Between(fromDate, toDate), + }; + + // Add additional filters if present + if (attendanceSearchDto.filters) { + Object.keys(attendanceSearchDto.filters).forEach((key) => { + whereClause[key] = attendanceSearchDto.filters[key]; + }); + } + + const [results, totalCount] = await this.attendanceRepository.findAndCount({ + where: whereClause, + take: parseInt(limit), + skip: offset, + }); + + const mappedResponse = await this.mappedResponse(results); + + return new SuccessResponse({ + statusCode: HttpStatus.OK, + message: "Ok", + totalCount: totalCount, + data: mappedResponse, + }); + } catch (e) { + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.INTERNAL_SERVER_ERROR, + errorMessage: e, + }); + } + } + + /*Method to add multiple attendance records in Attendance table + @body Array of objects containing attendance details of user (AttendanceDto) + */ + + public async multipleAttendance( + tenantId: string, + request: any, + attendanceData: BulkAttendanceDTO + ) { + + const loginUserId = request.user.userId + + + const responses = []; + const errors = []; + try { + let count = 1; + + for (let attendance of attendanceData.userAttendance) { + + + const userAttendance = new AttendanceDto({ + attendanceDate: attendanceData.attendanceDate, + contextId: attendanceData?.contextId, + scope: attendanceData?.scope, + attendance: attendance?.attendance, + userId: attendance?.userId, + tenantId: tenantId, + remark: attendance?.remark, + latitude: attendance?.latitude, + longitude: attendance?.longitude, + image: attendance?.image, + metaData: attendance?.metaData, + syncTime: attendance?.syncTime, + session: attendance?.session, + contextType: attendance?.contextType, + }) + + const attendanceRes: any = await this.updateAttendanceRecord( + loginUserId, + userAttendance + ); + + if (attendanceRes?.statusCode === 200 || attendanceRes?.statusCode === 201) { + responses.push(attendanceRes.data); + } + else { + errors.push({ + userId: attendance.userId, + attendanceRes, + }); + } + count++; + + + } + } catch (e) { + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.INTERNAL_SERVER_ERROR, + errorMessage: e, + }); + } + + + return { + statusCode: HttpStatus.OK, + totalCount: attendanceData.userAttendance.length, + successCount: responses.length, + errorCount: errors.length, + responses, + errors, + }; + } + + + + public async validateUserForCohort(userId, cohortId) { + const attendanceRecord = await this.cohortMembersRepository.findOne({ + where: { userId, cohortId } // Include cohortId in the where clause + }); + if (attendanceRecord) { + return true + } else { + return false + } + + } + +} + + + + diff --git a/src/adapters/postgres/cohort-adapter.ts b/src/adapters/postgres/cohort-adapter.ts new file mode 100644 index 00000000..434d1fb7 --- /dev/null +++ b/src/adapters/postgres/cohort-adapter.ts @@ -0,0 +1,701 @@ +import { ConsoleLogger, HttpStatus, Injectable } from "@nestjs/common"; +import { SuccessResponse } from "src/success-response"; +const resolvePath = require("object-resolve-path"); +import jwt_decode from "jwt-decode"; +import { CohortDto, ReturnResponseBody } from "src/cohort/dto/cohort.dto"; +import { CohortSearchDto } from "src/cohort/dto/cohort-search.dto"; +import { UserDto } from "src/user/dto/user.dto"; +import { CohortCreateDto } from "src/cohort/dto/cohort-create.dto"; +import { CohortUpdateDto } from "src/cohort/dto/cohort-update.dto"; +import { FieldValuesDto } from "src/fields/dto/field-values.dto"; +import { FieldValuesUpdateDto } from "src/fields/dto/field-values-update.dto"; +import { IsNull, Not, Repository, getConnection, getRepository } from "typeorm"; +import { Cohort } from "src/cohort/entities/cohort.entity"; +import { Fields } from "src/fields/entities/fields.entity"; +import { InjectRepository } from "@nestjs/typeorm"; +import { PostgresFieldsService } from "./fields-adapter" +import { FieldValues } from "../../fields/entities/fields-values.entity"; +import { CohortMembers } from "src/cohortMembers/entities/cohort-member.entity"; +import { ErrorResponseTypeOrm } from "src/error-response-typeorm"; +import { isUUID } from "class-validator"; +import { UserTenantMapping } from "src/userTenantMapping/entities/user-tenant-mapping.entity"; +import APIResponse from "src/common/responses/response"; +import { APIID } from "src/common/utils/api-id.config"; + +@Injectable() +export class PostgresCohortService { + + + constructor( + @InjectRepository(Cohort) + private cohortRepository: Repository, + @InjectRepository(CohortMembers) + private cohortMembersRepository: Repository, + @InjectRepository(FieldValues) + private fieldValuesRepository: Repository, + @InjectRepository(Fields) + private fieldsRepository: Repository, + @InjectRepository(UserTenantMapping) + private UserTenantMappingRepository: Repository, + private fieldsService: PostgresFieldsService, + ) { } + + public async getCohortList( + tenantId: string, + userId: string, + request: any, + res: any + ) { + const apiId = APIID.COHORT_LIST; + try { + let findCohortId = await this.findCohortName(userId); + let result = { + cohortData: [], + }; + + for (let data of findCohortId) { + let cohortData = { + cohortId: data.cohortId, + name: data.name, + parentId: data.parentId, + customField: {} + }; + const getDetails = await this.getCohortListDetails(data.cohortId); + cohortData.customField = getDetails + result.cohortData.push(cohortData); + } + + return APIResponse.success(res, apiId, result, (HttpStatus.OK), "Cohort list fetched successfully"); + + } catch (error) { + const errorMessage = error.message || 'Internal server error'; + return APIResponse.error(res, apiId, "Internal Server Error", errorMessage, (HttpStatus.INTERNAL_SERVER_ERROR)); + + } + } + + public async getCohortsDetails(cohortId: string, res) { + const apiId = APIID.COHORT_READ; + + try { + + if (!isUUID(cohortId)) { + return APIResponse.error( + res, + apiId, + `Please Enter valid (UUID)`, + 'Invalid cohortId', + (HttpStatus.BAD_REQUEST) + ) + } + const checkData = await this.checkAuthAndValidData(cohortId); + + if (checkData === true) { + const result = await this.getCohortDataWithCustomfield(cohortId); + return APIResponse.success(res, apiId, result, (HttpStatus.OK), "Cohort details fetched succcessfully."); + + } else { + return APIResponse.error( + res, + apiId, + `Cohort not found`, + 'Invalid cohortId', + (HttpStatus.NOT_FOUND) + ) + } + + } catch (error) { + const errorMessage = error.message || 'Internal server error'; + return APIResponse.error(res, apiId, "Internal Server Error", errorMessage, (HttpStatus.INTERNAL_SERVER_ERROR)); + } + } + + public async getCohortDataWithCustomfield(cohortId: string) { + const result = { + cohortData: {} + }; + + let customFieldsArray = []; + + const [filledValues, cohortDetails, customFields] = await Promise.all([ + this.findFilledValues(cohortId), + this.findCohortDetails(cohortId), + this.findCustomFields() + ]); + + + result.cohortData = cohortDetails; + const filledValuesMap = new Map(filledValues.map(item => [item.fieldId, item.value])); + for (let data of customFields) { + const fieldValue = filledValuesMap.get(data.fieldId); + const customField = { + fieldId: data.fieldId, + label: data.label, + value: fieldValue || '', + options: data?.fieldParams?.['options'] || {}, + type: data.type || '' + }; + customFieldsArray.push(customField); + } + result.cohortData['customFields'] = customFieldsArray; + return result + } + + + async findFilledValues(cohortId: string) { + let query = `SELECT C."cohortId",F."fieldId",F."value" FROM public."Cohort" C + LEFT JOIN public."FieldValues" F + ON C."cohortId" = F."itemId" where C."cohortId" =$1`; + let result = await this.cohortRepository.query(query, [cohortId]); + return result; + } + + async findCohortDetails(cohortId: string) { + let whereClause: any = { cohortId: cohortId }; + let cohortDetails = await this.cohortRepository.findOne({ + where: whereClause + }) + return new ReturnResponseBody(cohortDetails); + } + + async findCustomFields() { + let customFields = await this.fieldsRepository.find({ + where: { + context: 'COHORT', + contextType: 'COHORT' + } + }) + return customFields; + } + + public async findCohortName(userId: any) { + let query = `SELECT c."name",c."cohortId",c."parentId" + FROM public."CohortMembers" AS cm + LEFT JOIN public."Cohort" AS c ON cm."cohortId" = c."cohortId" + WHERE cm."userId"=$1 AND c.status=true`; + let result = await this.cohortMembersRepository.query(query, [userId]); + return result; + } + + public async getCohortListDetails(userId) { + let query = `SELECT DISTINCT f."label", fv."value", f."type", f."fieldParams" + FROM public."CohortMembers" cm + LEFT JOIN ( + SELECT DISTINCT ON (fv."fieldId", fv."itemId") fv.* + FROM public."FieldValues" fv + ) fv ON fv."itemId" = cm."cohortId" + INNER JOIN public."Fields" f ON fv."fieldId" = f."fieldId" + WHERE cm."cohortId" = $1;`; + let result = await this.cohortMembersRepository.query(query, [ + userId + ]); + return result; + } + public async validateFieldValues(field_value_array: string[]) { + let encounteredKeys = [] + for (const fieldValue of field_value_array) { + const [fieldId] = fieldValue.split(":").map(value => value.trim()); + if (encounteredKeys.includes(fieldId)) { + return { valid: false, fieldId: fieldId }; + } + encounteredKeys.push(fieldId) + } + return { valid: true, fieldId: "true" }; + }; + + + + public async createCohort(request: any, cohortCreateDto: CohortCreateDto, res) { + const apiId = APIID.COHORT_CREATE; + + try { + let field_value_array = cohortCreateDto.fieldValues.split("|"); + //Check duplicate field + let valid = await this.validateFieldValues(field_value_array); + if (valid && valid?.valid === false) { + return APIResponse.error( + res, + apiId, + `Duplicate fieldId '${valid.fieldId}' found in fieldValues.`, + `Duplicate fieldId`, + (HttpStatus.CONFLICT) + ) + } + const decoded: any = jwt_decode(request.headers.authorization); + cohortCreateDto.createdBy = decoded?.sub + cohortCreateDto.updatedBy = decoded?.sub + cohortCreateDto.status = true; + cohortCreateDto.attendanceCaptureImage = false; + + let response; + if (cohortCreateDto.name && cohortCreateDto.parentId) { + const existData = await this.cohortRepository.find({ + where: { name: cohortCreateDto.name, parentId: cohortCreateDto.parentId } + }) + if (existData.length == 0) { + response = await this.cohortRepository.save(cohortCreateDto); + } else { + if (existData[0].status == false) { + const updateData = { status: true }; + const cohortId = existData[0].cohortId; + await this.cohortRepository.update(cohortId, updateData); + const cohortData = await this.cohortRepository.find({ where: { cohortId: cohortId } }) + response = cohortData[0]; + } else { + return APIResponse.error( + res, + apiId, + `Cohort name already exist for this parent.`, + `Cohort already exists`, + (HttpStatus.CONFLICT) + ) + } + } + } else { + const existData = await this.cohortRepository.find({ + where: { name: cohortCreateDto.name } + }) + if (existData.length === 0) { + response = await this.cohortRepository.save(cohortCreateDto); + } else { + if (existData[0].status == false) { + const updateData = { status: true }; + const cohortId = existData[0].cohortId; + await this.cohortRepository.update(cohortId, updateData); + const cohortData = await this.cohortRepository.find({ where: { cohortId: cohortId } }) + response = cohortData[0]; + } else { + return APIResponse.error( + res, + apiId, + `Cohort name already exists`, + `Duplicate Cohort name`, + (HttpStatus.CONFLICT) + ) + } + } + } + + let cohortId = response?.cohortId; + + if (field_value_array.length > 0) { + let field_values = []; + + for (let i = 0; i < field_value_array.length; i++) { + let fieldValues = field_value_array[i].split(":"); + let fieldValueDto: FieldValuesDto = { + value: fieldValues[1] ? fieldValues[1].trim() : "", + itemId: cohortId, + fieldId: fieldValues[0] ? fieldValues[0].trim() : "", + createdBy: cohortCreateDto?.createdBy, + updatedBy: cohortCreateDto?.updatedBy, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }; + const fieldValue = await this.fieldsService.findAndSaveFieldValues(fieldValueDto); + } + } + + + response = new ReturnResponseBody(response); + return APIResponse.success(res, apiId, response, (HttpStatus.CREATED), "Cohort Created Successfully."); + + + } catch (error) { + const errorMessage = error.message || 'Internal server error'; + return APIResponse.error(res, apiId, "Internal Server Error", errorMessage, (HttpStatus.INTERNAL_SERVER_ERROR)); + } + } + + public async updateCohort( + cohortId: string, + request: any, + cohortUpdateDto: CohortUpdateDto, + res + ) { + const apiId = APIID.COHORT_UPDATE; + try { + + let field_value_array = cohortUpdateDto.fieldValues.split("|"); + let valid = await this.validateFieldValues(field_value_array); + if (valid && valid?.valid === false) { + return APIResponse.error( + res, + apiId, + `Duplicate fieldId '${valid.fieldId}' found in fieldValues`, + `Duplicate fieldId`, + (HttpStatus.CONFLICT) + ) + } + + const decoded: any = jwt_decode(request.headers.authorization); + cohortUpdateDto.updatedBy = decoded?.sub + cohortUpdateDto.createdBy = decoded?.sub + cohortUpdateDto.status = true; + let response; + + if (!isUUID(cohortId)) { + return APIResponse.error( + res, + apiId, + `Please Enter valid cohortId(UUID)`, + `Invalid cohortId`, + (HttpStatus.CONFLICT) + ) + } + + const checkData = await this.checkAuthAndValidData(cohortId); + + if (checkData === true) { + let updateData = {}; + let fieldValueData = {}; + + // Iterate over all keys in cohortUpdateDto + for (let key in cohortUpdateDto) { + if (cohortUpdateDto.hasOwnProperty(key) && cohortUpdateDto[key] !== null) { + if (key !== 'fieldValues') { + updateData[key] = cohortUpdateDto[key]; + } else { + fieldValueData[key] = cohortUpdateDto[key]; + } + } + } + + if (cohortUpdateDto.name && cohortUpdateDto.parentId) { + const existData = await this.cohortRepository.find({ + where: { name: cohortUpdateDto.name, parentId: cohortUpdateDto.parentId } + }) + + + + if (existData.length == 0) { + response = await this.cohortRepository.update(cohortId, updateData); + } else { + if (existData[0].status == false) { + const updateData = { status: true }; + const cohortId = existData[0].cohortId; + await this.cohortRepository.update(cohortId, updateData); + const cohortData = await this.cohortRepository.find({ where: { cohortId: cohortId } }) + response = cohortData[0]; + } else { + return APIResponse.error( + res, + apiId, + `Cohort name already exist for this parent please choose another name`, + `Duplicate cohort name`, + (HttpStatus.CONFLICT), + ) + } + } + } else { + const existData = await this.cohortRepository.find({ + where: { name: cohortUpdateDto.name } + }) + + if (existData.length == 0) { + response = await this.cohortRepository.update(cohortId, updateData); + } else { + if (existData[0].status == false) { + const updateData = { status: true }; + const cohortId = existData[0].cohortId; + await this.cohortRepository.update(cohortId, updateData); + const cohortData = await this.cohortRepository.find({ where: { cohortId: cohortId } }) + response = cohortData[0]; + } else { + return APIResponse.error( + res, + apiId, + `Cohort name already exists please choose another name`, + `Duplicate cohort name`, + (HttpStatus.CONFLICT) + ) + } + } + } + + if (fieldValueData['fieldValues']) { + if (field_value_array.length > 0) { + + for (let i = 0; i < field_value_array.length; i++) { + let fieldValues = field_value_array[i].split(":"); + let fieldId = fieldValues[0] ? fieldValues[0].trim() : ""; + try { + + const fieldVauesRowId = await this.fieldsService.searchFieldValueId(cohortId, fieldId) + const rowid = fieldVauesRowId.fieldValuesId; + + let fieldValueUpdateDto: FieldValuesUpdateDto = { + fieldValuesId: rowid, + value: fieldValues[1] ? fieldValues[1].trim() : "" + }; + await this.fieldsService.updateFieldValues(rowid, fieldValueUpdateDto); + } catch { + + let fieldValueDto: FieldValuesDto = { + value: fieldValues[1] ? fieldValues[1].trim() : "", + itemId: cohortId, + fieldId: fieldValues[0] ? fieldValues[0].trim() : "", + createdBy: cohortUpdateDto?.createdBy, + updatedBy: cohortUpdateDto?.updatedBy, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }; + await this.fieldsService.findAndSaveFieldValues(fieldValueDto); + } + } + } + } + + return APIResponse.success(res, apiId, response.affected, (HttpStatus.OK), "Cohort updated successfully."); + + } else { + return APIResponse.error( + res, + apiId, + `Cohort not found`, + `Cohort not found`, + (HttpStatus.NOT_FOUND) + ) + } + } catch (error) { + const errorMessage = error.message || 'Internal server error'; + return APIResponse.error(res, apiId, "Internal Server Error", errorMessage, (HttpStatus.INTERNAL_SERVER_ERROR)); + + } + } + + public async searchCohort( + tenantId: string, + request: any, + cohortSearchDto: CohortSearchDto, response + ) { + const apiId = APIID.COHORT_LIST; + try { + + let { limit, page, filters } = cohortSearchDto; + + let offset = 0; + if (limit > 0 && page > 0) { + offset = (limit) * (page - 1); + } + if (limit === 0) { limit = 200 } + const emptyValueKeys = {}; + let emptyKeysString = ''; + + const MAX_LIMIT = 200; + const PAGE_LIMIT = 10000000; + + // Validate the limit parameter + if (limit > MAX_LIMIT) { + return APIResponse.error( + response, + apiId, + `Limit exceeds maximum allowed value of ${MAX_LIMIT}`, + `Limit exceeded`, + (HttpStatus.BAD_REQUEST) + ) + } + + if (page > PAGE_LIMIT) { + return APIResponse.error( + response, + apiId, + `Page limit exceeds maximum allowed value of ${PAGE_LIMIT}`, + `Page limit exceeded`, + (HttpStatus.BAD_REQUEST) + ) + } + + const allowedKeys = ["userId", "cohortId", "name"]; + const whereClause = {}; + + if (filters && Object.keys(filters).length > 0) { + Object.entries(filters).forEach(([key, value]) => { + if (!allowedKeys.includes(key)) { + return APIResponse.error( + response, + apiId, + `${key} Invalid key`, + `Invalid filter key`, + (HttpStatus.BAD_REQUEST) + ) + } else { + if (value === '') { + emptyValueKeys[key] = value; + emptyKeysString += (emptyKeysString ? ', ' : '') + key; + } else { + whereClause[key] = value; + } + } + }); + } + + if (whereClause['userId'] && !isUUID(whereClause['userId'])) { + return APIResponse.error( + response, + apiId, + `Invalid User ID format. It must be a valid UUID`, + `Invalid userId`, + (HttpStatus.BAD_REQUEST) + ) + } + + if (whereClause['cohortId'] && !isUUID(whereClause['cohortId'])) { + return APIResponse.error( + response, + apiId, + `Invalid Cohort ID format. It must be a valid UUID`, + `Invalid cohortID`, + (HttpStatus.BAD_REQUEST) + ) + } + + let results = { + cohortDetails: [], + }; + + let count=0 + + if (whereClause['userId']) { + const additionalFields = Object.keys(whereClause).filter(key => key !== 'userId'); + if (additionalFields.length > 0) { + // Handle the case where userId is provided along with other fields + return APIResponse.error( + response, + apiId, + `When filtering by userId, do not include additional fields`, + 'Invalid filters', + (HttpStatus.BAD_REQUEST) + ) + } + + let userTenantMapExist = await this.UserTenantMappingRepository.find({ + where: { + tenantId: tenantId, + userId: whereClause['userId'] + } + }) + if (userTenantMapExist.length == 0) { + return APIResponse.error( + response, + apiId, + `User is not mapped for this tenant`, + 'Invalid combination of userId and tenantId', + (HttpStatus.BAD_REQUEST) + ) + } + const [data,totalCount] = await this.cohortMembersRepository.findAndCount({ + where: whereClause, + skip: offset, + }); + const cohortData = data.slice(offset, offset + (limit)); + count=totalCount + for (let data of cohortData) { + let cohortDetails = await this.getCohortDataWithCustomfield(data.cohortId); + results.cohortDetails.push(cohortDetails); + } + } else { + const [data,totalcount] = await this.cohortRepository.findAndCount({ + where: whereClause, + skip: offset + }); + const cohortData = data.slice(offset, offset + (limit)); + count=totalcount + + for (let data of cohortData) { + let cohortDetails = await this.getCohortDataWithCustomfield(data.cohortId); + results.cohortDetails.push(cohortDetails); + } + } + + if (results.cohortDetails.length > 0) { + const totalCount = results.cohortDetails.length + return APIResponse.success(response, apiId, {count,results}, (HttpStatus.OK), "Cohort details fetched successfully"); + + } else { + return APIResponse.error( + response, + apiId, + `No data found.`, + 'No data found.', + (HttpStatus.NOT_FOUND) + ) + } + + + } catch (error) { + const errorMessage = error.message || 'Internal server error'; + return APIResponse.error(response, apiId, "Internal Server Error", errorMessage, (HttpStatus.INTERNAL_SERVER_ERROR)); + } + } + + + public async updateCohortStatus( + cohortId: string, + request: any, + response + ) { + const apiId = APIID.COHORT_DELETE; + try { + const decoded: any = jwt_decode(request.headers.authorization); + // const createdBy = decoded?.sub; + const updatedBy = decoded?.sub + + if (!isUUID(cohortId)) { + return APIResponse.error( + response, + apiId, + `Invalid Cohort Id format. It must be a valid UUID`, + 'Invalid cohortId', + (HttpStatus.BAD_REQUEST) + ) + } + const checkData = await this.checkAuthAndValidData(cohortId); + + if (checkData === true) { + let query = `UPDATE public."Cohort" + SET "status" = false, + "updatedBy" = '${updatedBy}' + WHERE "cohortId" = $1`; + const affectedrows = await this.cohortRepository.query(query, [cohortId]); + await this.cohortMembersRepository.delete( + { cohortId: cohortId } + ); + await this.fieldValuesRepository.delete( + { itemId: cohortId } + ); + + + return APIResponse.success(response, apiId, affectedrows[1], (HttpStatus.OK), "Cohort Deleted Successfully."); + + } else { + return APIResponse.error( + response, + apiId, + `Cohort not found`, + 'Invalid cohortId', + (HttpStatus.BAD_REQUEST) + ) + } + } catch (error) { + const errorMessage = error.message || 'Internal server error'; + return APIResponse.error(response, apiId, "Internal Server Error", errorMessage, (HttpStatus.INTERNAL_SERVER_ERROR)); + } + } + + public async checkAuthAndValidData(id: any) { + + const existData = await this.cohortRepository.find({ + where: { + cohortId: id, + status: true + } + }) + if (existData.length !== 0) { + return true; + } else { + return false; + } + + } +} diff --git a/src/adapters/postgres/cohortMembers-adapter.ts b/src/adapters/postgres/cohortMembers-adapter.ts new file mode 100644 index 00000000..c0b83828 --- /dev/null +++ b/src/adapters/postgres/cohortMembers-adapter.ts @@ -0,0 +1,455 @@ +import { Injectable } from "@nestjs/common"; +import { HttpService } from "@nestjs/axios"; +import { SuccessResponse } from "src/success-response"; +import { ErrorResponse } from "src/error-response"; +const resolvePath = require("object-resolve-path"); +import { CohortMembersDto } from "src/cohortMembers/dto/cohortMembers.dto"; +import { CohortMembersSearchDto } from "src/cohortMembers/dto/cohortMembers-search.dto"; +import { CohortMembers } from "src/cohortMembers/entities/cohort-member.entity"; +import { InjectRepository } from "@nestjs/typeorm"; +import { IsNull, Not, Repository, getConnection, getRepository } from "typeorm"; +import { CohortDto } from "src/cohort/dto/cohort.dto"; + +import { HttpStatus } from "@nestjs/common"; +import response from "src/utils/response"; +import { User } from "src/user/entities/user-entity"; +import { CohortMembersUpdateDto } from "src/cohortMembers/dto/cohortMember-update.dto"; +import { ErrorResponseTypeOrm } from "src/error-response-typeorm"; +import { Fields } from "src/fields/entities/fields.entity"; +import { UUID } from "typeorm/driver/mongodb/bson.typings"; +import { isUUID } from "class-validator"; +import { Cohort } from "src/cohort/entities/cohort.entity"; +import APIResponse from "src/common/responses/response"; +import { Response } from "express"; +import { APIID } from 'src/common/utils/api-id.config'; +@Injectable() +export class PostgresCohortMembersService { + constructor( + @InjectRepository(CohortMembers) + private cohortMembersRepository: Repository, + @InjectRepository(Fields) + private fieldsRepository: Repository, + @InjectRepository(User) + private usersRepository: Repository, + @InjectRepository(Cohort) + private cohortRepository: Repository + ) { } + + //Get cohort member + async getCohortMembers(cohortId: any, tenantId: any, fieldvalue: any, res: Response) { + const apiId = APIID.COHORT_MEMBER_GET + try { + const fieldvalues = fieldvalue?.toLowerCase() + + if (!tenantId) { + return APIResponse.error(res, apiId, "Bad Request", `TenantId required`, HttpStatus.BAD_REQUEST); + } + + if (!isUUID(cohortId)) { + return APIResponse.error(res, apiId, "Bad Request", "Invalid input: CohortId must be a valid UUID.", HttpStatus.BAD_REQUEST); + } + + + const cohortTenantMap = await this.cohortRepository.find({ + where: { + cohortId: cohortId, + tenantId: tenantId + } + }) + if (!cohortTenantMap) { + return APIResponse.error(res, apiId, "Not Found", "Invalid input: Cohort not found for the provided tenant.", HttpStatus.NOT_FOUND); + } + const userDetails = await this.findcohortData(cohortId); + if (userDetails === true) { + let results = { + userDetails: [], + }; + + let cohortDetails = await this.getUserDetails( + cohortId, + "cohortId", + fieldvalues + ); + results.userDetails.push(cohortDetails); + + return APIResponse.success(res, apiId, results, HttpStatus.OK, "Cohort members details fetched successfully."); + } else { + return APIResponse.error(res, apiId, "Not Found", "Invalid input: Cohort Member not exist.", HttpStatus.NOT_FOUND); + } + } catch (e) { + const errorMessage = e.message || 'Internal server error'; + return APIResponse.error(res, apiId, "Internal Server Error", errorMessage, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + async getUserDetails(searchId: any, searchKey: any, fieldShowHide: any) { + let results = { + userDetails: [], + }; + + let getUserDetails = await this.findUserName(searchId, searchKey); + + for (let data of getUserDetails) { + let userDetails = { + userId: data?.userId, + userName: data?.userName, + name: data?.name, + role: data?.role, + district: data?.district, + state: data?.state, + mobile: data?.mobile + }; + + if (fieldShowHide === "true") { + const fieldValues = await this.getFieldandFieldValues(data.userId); + userDetails['customField'] = fieldValues; + results.userDetails.push(userDetails); + } else { + results.userDetails.push(userDetails); + } + } + + return results; + } + async findFilledValues(userId: string) { + let query = `SELECT U."userId",F."fieldId",F."value" FROM public."Users" U + LEFT JOIN public."FieldValues" F + ON U."userId" = F."itemId" where U."userId" =$1`; + let result = await this.usersRepository.query(query, [userId]); + return result; + } + async findcohortData(cohortId: any) { + let whereClause: any = { cohortId: cohortId }; + let userDetails = await this.cohortMembersRepository.find({ + where: whereClause, + }); + if (userDetails.length !== 0) { + return true; + } else { + return false; + } + } + async findCustomFields(role) { + let customFields = await this.fieldsRepository.find({ + where: { + context: "USERS", + contextType: role.toUpperCase(), + }, + }); + return customFields; + } + + async getFieldandFieldValues(userId: string) { + let query = `SELECT Fv."fieldId",F."label" AS FieldName,Fv."value" as FieldValues + FROM public."FieldValues" Fv + LEFT JOIN public."Fields" F + ON F."fieldId" = Fv."fieldId" + where Fv."itemId" =$1 `; + let result = await this.usersRepository.query(query, [userId]); + return result; + } + + async findUserName(searchData: string, searchKey: any) { + let whereCase; + if (searchKey == "cohortId") { + whereCase = `where CM."cohortId" =$1`; + } else { + whereCase = `where CM."userId" =$1`; + } + let query = `SELECT U."userId", U.username, U.name, U.district, U.state,U.mobile FROM public."CohortMembers" CM + LEFT JOIN public."Users" U + ON CM."userId" = U."userId" ${whereCase}`; + + let result = await this.usersRepository.query(query, [searchData]); + return result; + } + + public async searchCohortMembers( + cohortMembersSearchDto: CohortMembersSearchDto, + tenantId: string, + res: Response + ) { + const apiId = APIID.COHORT_MEMBER_SEARCH; + try { + if (!isUUID(tenantId)) { + return APIResponse.error(res, apiId, "Bad Request", "Invalid input: TenantId must be a valid UUID.", HttpStatus.BAD_REQUEST); + } + + let { limit, page, filters } = cohortMembersSearchDto; + let offset = 0; + if (page > 1) { + offset = limit * (page - 1); + } + + const whereClause = {}; + if (filters && Object.keys(filters).length > 0) { + Object.entries(filters).forEach(([key, value]) => { + whereClause[key] = value; + }); + } + + // Validate cohortId and userId format + if (whereClause["cohortId"] && !isUUID(whereClause["cohortId"])) { + return APIResponse.error(res, apiId, "Bad Request", "Invalid input: CohortId must be a valid UUID.", HttpStatus.BAD_REQUEST); + } + if (whereClause["userId"] && !isUUID(whereClause["userId"])) { + return APIResponse.error(res, apiId, "Bad Request", "Invalid input: UserId must be a valid UUID.", HttpStatus.BAD_REQUEST); + } + // Check if cohortId exists + if (whereClause["cohortId"]) { + const cohortExists = await this.cohortMembersRepository.findOne({ + where: { cohortId: whereClause["cohortId"] }, + }); + if (!cohortExists) { + return APIResponse.error(res, apiId, "Not Found", "Invalid input: No member found for this cohortId.", HttpStatus.NOT_FOUND); + } + } + + // Check if userId exists + if (whereClause["userId"]) { + const userExists = await this.cohortMembersRepository.findOne({ + where: { userId: whereClause["userId"] }, + }); + if (!userExists) { + return APIResponse.error(res, apiId, "Not Found", "Invalid input: No member found for this userId and cohort combination.", HttpStatus.NOT_FOUND); + } + } + + // console.log("USER DATA ",userData) + let results = {}; + let where = []; + if (whereClause["cohortId"]) { + where.push(["cohortId", whereClause["cohortId"]]); + } + if (whereClause["userId"]) { + where.push(["userId", whereClause["userId"]]); + } + if (whereClause["role"]) { + where.push(["role", whereClause["role"]]); + } + let options = []; + if (limit) { + options.push(['limit', limit]); + } + if (offset) { + options.push(['offset', offset]); + } + + results = await this.getCohortMemberUserDetails( + where, + "true", + options + ); + const totalCount = results['userDetails'].length; + + + if (results['userDetails'].length == 0) { + return APIResponse.error(res, apiId, "Not Found", "Invalid input: No data found.", HttpStatus.NOT_FOUND); + } + + return APIResponse.success(res, apiId, { totalCount, results }, HttpStatus.OK, "Cohort members details fetched successfully."); + + + } catch (e) { + const errorMessage = e.message || 'Internal server error'; + return APIResponse.error(res, apiId, "Internal Server Error", errorMessage, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + async getCohortMemberUserDetails(where: any, fieldShowHide: any, options: any) { + let results = { + userDetails: [], + }; + + let getUserDetails = await this.getUsers(where, options); + + for (let data of getUserDetails) { + let userDetails = { + userId: data?.userId, + userName: data?.userName, + name: data?.name, + role: data?.role, + district: data?.district, + state: data?.state, + mobile: data?.mobile + }; + + if (fieldShowHide === "false") { + results.userDetails.push(userDetails); + } else { + const fieldValues = await this.getFieldandFieldValues(data.userId); + userDetails['customField'] = fieldValues; + results.userDetails.push(userDetails); + } + } + + return results; + } + + public async createCohortMembers( + loginUser: any, + cohortMembers: CohortMembersDto, + res: Response, + tenantId: string + ) { + const apiId = APIID.COHORT_MEMBER_CREATE; + try { + cohortMembers.createdBy = loginUser; + cohortMembers.updatedBy = loginUser; + + const existCohort = await this.cohortRepository.find({ + where: { + cohortId: cohortMembers.cohortId + } + }) + if (existCohort.length == 0) { + return APIResponse.error(res, apiId, "Bad Request", "Invalid input: Cohort Id does not exist.", HttpStatus.BAD_REQUEST); + } + + const existUser = await this.usersRepository.find({ + where: { + userId: cohortMembers.userId, + } + }) + if (existUser.length == 0) { + return APIResponse.error(res, apiId, "Bad Request", "Invalid input: User Id does not exist.", HttpStatus.BAD_REQUEST); + } + + const existrole = await this.cohortMembersRepository.find({ + where: { + userId: cohortMembers.userId, + cohortId: cohortMembers.cohortId + } + }) + if (existrole.length > 0) { + return APIResponse.error(res, apiId, "CONFLICT", `User '${cohortMembers.userId}' is already assigned to cohort '${cohortMembers.cohortId}'.`, HttpStatus.CONFLICT); + } + + // Create a new CohortMembers entity and populate it with cohortMembers data + const savedCohortMember = await this.cohortMembersRepository.save( + cohortMembers + ); + + return APIResponse.success(res, apiId, savedCohortMember, HttpStatus.OK, "Cohort member has been successfully assigned."); + + } catch (e) { + const errorMessage = e.message || 'Internal server error'; + return APIResponse.error(res, apiId, "Internal Server Error", errorMessage, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + async getUsers(where: any, options: any) { + let query = ``; + let whereCase = ``; + let optionsCase = ``; + let isRoleCondition = 0; + if (where.length > 0) { + whereCase = `where `; + where.forEach((value, index) => { + if (value[0] == "role") { + isRoleCondition = 1; + whereCase += `R."name"='${value[1]}' `; + } else { + whereCase += `CM."${value[0]}"='${value[1]}' `; + } + if (index != (where.length - 1)) { + whereCase += ` AND ` + } + }) + } + + if (options.length > 0) { + options.forEach((value, index) => { + optionsCase = `${value[0]} ${value[1]} `; + }) + } + + if (isRoleCondition == 0) { + query = `SELECT U."userId", U.username, U.name, R.name AS role, U.district, U.state,U.mobile FROM public."CohortMembers" CM + INNER JOIN public."Users" U + ON CM."userId" = U."userId" + INNER JOIN public."UserRolesMapping" UR + ON UR."userId" = U."userId" + INNER JOIN public."Roles" R + ON R."roleId" = UR."roleId" ${whereCase} ${optionsCase}`; + } + else { + query = `SELECT U."userId", U.username, U.name, R.name AS role, U.district, U.state,U.mobile FROM public."CohortMembers" CM + INNER JOIN public."Users" U + ON CM."userId" = U."userId" + INNER JOIN public."UserRolesMapping" UR + ON UR."userId" = U."userId" + INNER JOIN public."Roles" R + ON R."roleId" = UR."roleId" ${whereCase} ${optionsCase}`; + } + let result = await this.usersRepository.query(query); + return result; + + } + + + public async updateCohortMembers( + cohortMembershipId: string, + loginUser: any, + cohortMembersUpdateDto: CohortMembersUpdateDto, + res: Response, + ) { + const apiId = APIID.COHORT_MEMBER_UPDATE; + + try { + cohortMembersUpdateDto.updatedBy = loginUser; + if (!isUUID(cohortMembershipId)) { + return APIResponse.error(res, apiId, "Bad Request", "Invalid input: Please Enter a valid UUID for cohortMemberId.", HttpStatus.BAD_REQUEST); + } + + const cohortMemberToUpdate = await this.cohortMembersRepository.findOne({ + where: { cohortMembershipId: cohortMembershipId }, + }); + + if (!cohortMemberToUpdate) { + return APIResponse.error(res, apiId, "Not Found", "Invalid input: Cohort member not found.", HttpStatus.NOT_FOUND); + } + + Object.assign(cohortMemberToUpdate, cohortMembersUpdateDto); + + const updatedCohortMember = await this.cohortMembersRepository.save( + cohortMemberToUpdate + ); + + return APIResponse.success(res, apiId, updatedCohortMember, HttpStatus.OK, "Cohort Member Updated successfully."); + + } catch (e) { + const errorMessage = e.message || 'Internal server error'; + return APIResponse.error(res, apiId, "Internal Server Error", errorMessage, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + + public async deleteCohortMemberById( + tenantid: any, + cohortMembershipId: any, + res: any + ) { + const apiId = APIID.COHORT_MEMBER_DELETE; + + try { + const cohortMember = await this.cohortMembersRepository.find({ + where: { + cohortMembershipId: cohortMembershipId, + }, + }); + + if (!cohortMember || cohortMember.length === 0) { + return APIResponse.error(res, apiId, "Not Found", "Invalid input: Cohort member not found.", HttpStatus.NOT_FOUND); + } + + const result = await this.cohortMembersRepository.delete( + cohortMembershipId + ); + + return APIResponse.success(res, apiId, result, HttpStatus.OK, "Cohort Member deleted Successfully."); + } catch (e) { + const errorMessage = e.message || 'Internal server error'; + return APIResponse.error(res, apiId, "Internal Server Error", errorMessage, HttpStatus.INTERNAL_SERVER_ERROR); + } + } +} diff --git a/src/adapters/postgres/fields-adapter.ts b/src/adapters/postgres/fields-adapter.ts new file mode 100644 index 00000000..9003bb93 --- /dev/null +++ b/src/adapters/postgres/fields-adapter.ts @@ -0,0 +1,305 @@ +import { HttpStatus, Injectable } from "@nestjs/common"; +import { FieldsDto } from "src/fields/dto/fields.dto"; +import { FieldsSearchDto } from "src/fields/dto/fields-search.dto"; +import { FieldValuesDto } from "src/fields/dto/field-values.dto"; +import { FieldValuesUpdateDto } from "src/fields/dto/field-values-update.dto"; +import { FieldValuesSearchDto } from "src/fields/dto/field-values-search.dto"; +import { ErrorResponse } from "src/error-response"; +import { Fields } from "../../fields/entities/fields.entity"; +import { FieldValues } from "../../fields/entities/fields-values.entity"; +import { InjectRepository } from "@nestjs/typeorm"; +import { Repository } from "typeorm"; +import { SuccessResponse } from "src/success-response"; +import { ErrorResponseTypeOrm } from "src/error-response-typeorm"; +import APIResponse from "src/common/responses/response"; +import { APIID } from "src/common/utils/api-id.config"; +import { IServicelocatorfields } from "../fieldsservicelocator"; +import { Response } from "express"; + +@Injectable() +export class PostgresFieldsService implements IServicelocatorfields { + constructor( + @InjectRepository(Fields) + private fieldsRepository: Repository, + @InjectRepository(FieldValues) + private fieldsValuesRepository: Repository, + ) { } + + //fields + async createFields(request: any, fieldsDto: FieldsDto, response: Response,) { + const apiId = APIID.FIELDS_CREATE; + try { + const fieldsData: any = {}; // Define an empty object to store field data + + Object.keys(fieldsDto).forEach((e) => { + if (fieldsDto[e] && fieldsDto[e] !== "") { + if (e === "render") { + fieldsData[e] = fieldsDto[e]; + } else if (Array.isArray(fieldsDto[e])) { + fieldsData[e] = JSON.stringify(fieldsDto[e]); + } else { + fieldsData[e] = fieldsDto[e]; + } + } + }); + + let result = await this.fieldsRepository.save(fieldsData); + + return await APIResponse.success(response, apiId, result, + HttpStatus.CREATED, 'Fields created successfully.') + + } catch (e) { + const errorMessage = e?.message || 'Something went wrong'; + return APIResponse.error(response, apiId, "Internal Server Error", `Error : ${errorMessage}`, HttpStatus.INTERNAL_SERVER_ERROR) + } + } + + async searchFields(tenantId: string, request: any, fieldsSearchDto: FieldsSearchDto,response :Response) { + const apiId = APIID.FIELDS_SEARCH; + try { + + const getConditionalData = await this.search(fieldsSearchDto) + const offset = getConditionalData.offset; + const limit = getConditionalData.limit; + const whereClause = getConditionalData.whereClause; + + const getFieldValue = await this.searchFieldData(offset, limit, whereClause) + + + const result = { + totalCount: getFieldValue.totalCount, + fields: getFieldValue.mappedResponse, + } + + return await APIResponse.success(response, apiId, result, + HttpStatus.OK, 'Fields fetched successfully.') + + + } catch (e) { + const errorMessage = e?.message || 'Something went wrong'; + return APIResponse.error(response, apiId, "Internal Server Error", `Error : ${errorMessage}`, HttpStatus.INTERNAL_SERVER_ERROR) + } + } + + async searchFieldData(offset: number, limit: string, searchData: any) { + let queryOptions: any = { + where: searchData, + }; + + if (offset !== undefined) { + queryOptions.skip = offset; + } + + if (limit !== undefined) { + queryOptions.take = parseInt(limit); + } + + + const [results, totalCount] = await this.fieldsRepository.findAndCount(queryOptions); + + const mappedResponse = await this.mappedResponseField(results); + return { mappedResponse, totalCount }; + } + + async createFieldValues(request: any, fieldValuesDto: FieldValuesDto,res:Response) { + const apiId = APIID.FIELDVALUES_CREATE; + + + try { + let result = await this.findAndSaveFieldValues(fieldValuesDto); + if(!result){ + APIResponse.error( + res, + apiId, + `Fields not found`, + `Fields not found`, + (HttpStatus.NOT_FOUND) + ) + + } + return APIResponse.success(res, apiId, result, (HttpStatus.CREATED), "Ok"); + + + } catch (error) { + const errorMessage = error.message || 'Internal server error'; + return APIResponse.error(res, apiId, "Internal Server Error",errorMessage, (HttpStatus.INTERNAL_SERVER_ERROR)); + + } + } + + async searchFieldValues(request: any, fieldValuesSearchDto: FieldValuesSearchDto, response: Response) { + const apiId = APIID.FIELDVALUES_SEARCH; + try { + const getConditionalData = await this.search(fieldValuesSearchDto) + const offset = getConditionalData.offset; + const limit = getConditionalData.limit; + const whereClause = getConditionalData.whereClause; + + const getFieldValue = await this.getSearchFieldValueData(offset, limit, whereClause) + + const result = { + totalCount: getFieldValue.totalCount, + fields: getFieldValue.mappedResponse, + } + + return await APIResponse.success(response, apiId, result, + HttpStatus.OK, 'Field Values fetched successfully.') + + } catch (e) { + const errorMessage = e?.message || 'Something went wrong'; + return APIResponse.error(response, apiId, "Internal Server Error", `Error : ${errorMessage}`, HttpStatus.INTERNAL_SERVER_ERROR) + } + } + + async getSearchFieldValueData(offset: number, limit: string, searchData: any) { + let queryOptions: any = { + where: searchData, + }; + + if (offset !== undefined) { + queryOptions.skip = offset; + } + + if (limit !== undefined) { + queryOptions.take = parseInt(limit); + } + + const [results, totalCount] = await this.fieldsValuesRepository.findAndCount(queryOptions); + const mappedResponse = await this.mappedResponse(results); + + return { mappedResponse, totalCount }; + + } + + async searchFieldValueId(itemId: string, fieldId: string) { + const response = await this.fieldsValuesRepository.findOne({ + where: { itemId: itemId, fieldId: fieldId }, + }); + return response; + } + + async updateFieldValues(id: string, fieldValuesUpdateDto: FieldValuesUpdateDto) { + + try { + const fieldsData: any = {}; + Object.keys(fieldValuesUpdateDto).forEach((e) => { + if (fieldValuesUpdateDto[e] && fieldValuesUpdateDto[e] != "") { + if (Array.isArray(fieldValuesUpdateDto[e])) { + fieldsData[e] = JSON.stringify(fieldValuesUpdateDto[e]); + } else { + fieldsData[e] = fieldValuesUpdateDto[e]; + } + } + }); + const response = await this.fieldsValuesRepository.update(id, fieldValuesUpdateDto); + + return response; + } catch (e) { + return new ErrorResponse({ + errorCode: "400", + errorMessage: e, + }); + } + } + + public async getFieldsAndFieldsValues(cohortId: string) { + let query = `SELECT FV."value",FV."itemId", FV."fieldId", F."name" AS fieldname, F."label", F."context",F."type", F."state", F."contextType", F."fieldParams" FROM public."FieldValues" FV + LEFT JOIN public."Fields" F + ON FV."fieldId" = F."fieldId" where FV."itemId" =$1`; + const results = await this.fieldsValuesRepository.query(query, [cohortId]); + return results; + } + + + public async mappedResponse(result: any) { + const fieldValueResponse = result.map((item: any) => { + const fieldValueMapping = { + value: item?.value ? `${item.value}` : "", + fieldValuesId: item?.fieldValuesId ? `${item.fieldValuesId}` : "", + itemId: item?.itemId ? `${item.itemId}` : "", + fieldId: item?.fieldId ? `${item.fieldId}` : "", + createdAt: item?.createdAt ? `${item.createdAt}` : "", + updatedAt: item?.updatedAt ? `${item.updatedAt}` : "", + createdBy: item?.createdBy ? `${item.createdBy}` : "", + updatedBy: item?.updatedBy ? `${item.updatedBy}` : "", + }; + + return new FieldValuesDto(fieldValueMapping); + }); + + return fieldValueResponse; + } + + public async mappedResponseField(result: any) { + const fieldResponse = result.map((item: any) => { + + const fieldMapping = { + fieldId: item?.fieldId ? `${item.fieldId}` : "", + assetId: item?.assetId ? `${item.assetId}` : "", + context: item?.context ? `${item.context}` : "", + groupId: item?.groupId ? `${item.groupId}` : "", + name: item?.name ? `${item.name}` : "", + label: item?.label ? `${item.label}` : "", + defaultValue: item?.defaultValue ? `${item.defaultValue}` : "", + type: item?.type ? `${item.type}` : "", + note: item?.note ? `${item.note}` : "", + description: item?.description ? `${item.description}` : "", + state: item?.state ? `${item.state}` : "", + required: item?.required ? `${item.required}` : "", + ordering: item?.ordering ? `${item.ordering}` : "", + metadata: item?.metadata ? `${item.metadata}` : "", + access: item?.access ? `${item.access}` : "", + onlyUseInSubform: item?.onlyUseInSubform ? `${item.onlyUseInSubform}` : "", + tenantId: item?.tenantId ? `${item.tenantId}` : "", + createdAt: item?.createdAt ? `${item.createdAt}` : "", + updatedAt: item?.updatedAt ? `${item.updatedAt}` : "", + createdBy: item?.createdBy ? `${item.createdBy}` : "", + updatedBy: item?.updatedBy ? `${item.updatedBy}` : "", + contextId: item?.contextId ? `${item.contextId}` : "", + render: item?.render ? `${item.render}` : "", + contextType: item?.contextType ? `${item.contextType}` : "", + fieldParams: item?.fieldParams ? JSON.stringify(item.fieldParams) : "" + }; + + return new FieldsDto(fieldMapping); + }); + + return fieldResponse; + } + + public async findAndSaveFieldValues(fieldValuesDto: FieldValuesDto){ + + const checkFieldValueExist = await this.fieldsValuesRepository.find({ + where: { itemId: fieldValuesDto.itemId, fieldId: fieldValuesDto.fieldId }, + }); + + if (checkFieldValueExist.length == 0) { + let result = await this.fieldsValuesRepository.save(fieldValuesDto); + return result + } + return false; + } + + + public async search(dtoFileName){ + let { limit, page, filters } = dtoFileName; + + let offset = 0; + if (page > 1) { + offset = parseInt(limit) * (page - 1); + } + + if (limit.trim() === '') { + limit = '0'; + } + + const whereClause = {}; + if (filters && Object.keys(filters).length > 0) { + Object.entries(filters).forEach(([key, value]) => { + whereClause[key] = value; + }); + } + return {offset,limit,whereClause}; + } + +} diff --git a/src/adapters/postgres/potsgres-module.ts b/src/adapters/postgres/potsgres-module.ts new file mode 100644 index 00000000..352f2be4 --- /dev/null +++ b/src/adapters/postgres/potsgres-module.ts @@ -0,0 +1,48 @@ +import { HttpModule } from "@nestjs/axios"; +import { Module } from "@nestjs/common"; +import { PostgresUserService } from "./user-adapter"; +import { FieldsService } from "src/fields/fields.service"; +import { TypeOrmModule } from "@nestjs/typeorm"; +import { User } from "src/user/entities/user-entity"; +import { CohortMembers } from "src/cohortMembers/entities/cohort-member.entity"; +import { Field } from "src/user/entities/field-entity"; +import { Fields } from "src/fields/entities/fields.entity"; +import { FieldValues } from "src/user/entities/field-value-entities"; +import { AttendanceEntity } from "src/attendance/entities/attendance.entity"; +import { PostgresAttendanceService } from "./attendance-adapter"; +import { PostgresFieldsService } from "./fields-adapter"; +import { Cohort } from "src/cohort/entities/cohort.entity"; +import { UserTenantMapping } from "src/userTenantMapping/entities/user-tenant-mapping.entity"; +import { Tenants } from "src/userTenantMapping/entities/tenant.entity"; +import { UserRoleMapping } from "src/rbac/assign-role/entities/assign-role.entity"; +import { Role } from "src/rbac/role/entities/role.entity"; + + +@Module({ + imports: [HttpModule, + TypeOrmModule.forFeature([ + User, + Field, + FieldValues, + CohortMembers, + AttendanceEntity, + Fields, + Cohort, + UserTenantMapping, + Tenants, + UserRoleMapping, + Role + ]) + ], + providers: [ + PostgresUserService, + PostgresAttendanceService, + PostgresFieldsService + ], + exports: [ + PostgresUserService, + PostgresAttendanceService, + PostgresFieldsService + ], +}) +export class PostgresModule { } diff --git a/src/adapters/postgres/rbac/assignrole-adapter.ts b/src/adapters/postgres/rbac/assignrole-adapter.ts new file mode 100644 index 00000000..758e7444 --- /dev/null +++ b/src/adapters/postgres/rbac/assignrole-adapter.ts @@ -0,0 +1,225 @@ +import { BadRequestException, ConsoleLogger, HttpStatus, Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { In, Repository } from 'typeorm'; +import { SuccessResponse } from 'src/success-response'; +import { ErrorResponseTypeOrm } from 'src/error-response-typeorm'; +import { CreateAssignRoleDto, ResponseAssignRoleDto } from 'src/rbac/assign-role/dto/create-assign-role.dto'; +import { UserRoleMapping } from 'src/rbac/assign-role/entities/assign-role.entity'; +import { Role } from "src/rbac/role/entities/role.entity"; +import { IsAlpha, IsUUID, isUUID } from 'class-validator'; +import { executionAsyncResource } from 'async_hooks'; +import jwt_decode from "jwt-decode"; +import { DeleteAssignRoleDto } from 'src/rbac/assign-role/dto/delete-assign-role.dto'; +import { Response } from 'express'; +import { APIID } from 'src/common/utils/api-id.config'; +import APIResponse from 'src/common/responses/response'; + +@Injectable() +export class PostgresAssignroleService { + constructor( + @InjectRepository(UserRoleMapping) + private userRoleMappingRepository: Repository, + @InjectRepository(Role) + private roleRepository: Repository + ) { } + public async createAssignRole(request: Request, createAssignRoleDto: CreateAssignRoleDto, response: Response) { + const apiId = APIID.USERROLE_CREATE + try { + // const decoded: any = jwt_decode(request.headers.authorization); + const userId = createAssignRoleDto.userId; + const roles = createAssignRoleDto.roleId; + const tenantId = createAssignRoleDto.tenantId; + + // Check if roles array is not empty + if (!roles || roles.length === 0) { + return APIResponse.error( + response, + apiId, + `Roles array cannot be empty.`, + 'empty array found', + HttpStatus.BAD_REQUEST + ) + } + let result = []; + let errors = []; + + for (const roleId of roles) { + // If role already exists for user, return error response + let findExistingRole = await this.userRoleMappingRepository.findOne({ + where: { + userId: userId, + roleId: roleId, + }, + }); + if (findExistingRole) { + errors.push({ + errorMessage: `Role ${roleId} is already assigned to this user.`, + }); + continue; + } + + //Rele is belong for this tenent + let roleExistforTenant = await this.roleRepository.findOne({ + where: { + roleId: roleId, + tenantId: tenantId, + }, + }); + if (!roleExistforTenant) { + errors.push({ + errorMessage: `Role ${roleId} is not exist for this tenant.`, + }); + continue; + } + + const data = await this.userRoleMappingRepository.save({ + userId: userId, + roleId: roleId, + tenantId: tenantId, + createdBy: request['user'].userId, + updatedBy: request['user'].userId + }) + result.push(new ResponseAssignRoleDto(data, `Role assigned successfully to the user in the specified tenant.`)); + } + + if (result.length == 0) { + return APIResponse.error( + response, + apiId, + `Please Enter Valid User ID`, + 'Invalid User ID', + HttpStatus.BAD_REQUEST + ) + } + return APIResponse.success(response, apiId, { successCount: result.length, errorCount: errors.length, result, errors }, + HttpStatus.CREATED, 'Role successfully Created') + } catch (error) { + if (error.code === '23503') { + return APIResponse.error( + response, + apiId, + `User Id or Role Id Doesn't Exist in Database`, + 'Not found', + HttpStatus.NOT_FOUND + ) + } + const errorMessage = error.message || 'Internal server error'; + return APIResponse.error(response, apiId, "Internal Server Error", errorMessage, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + public async getAssignedRole(userId: string, request: Request, response: Response) { + const apiId = APIID.USERROLE_GET; + try { + if (!isUUID(userId)) { + return APIResponse.error( + response, + apiId, + `Please Enter Valid User ID`, + 'Invalid User ID', + HttpStatus.BAD_REQUEST + ) + } + + let result = await this.checkExistingRole(userId); + if (!result) { + return APIResponse.error( + response, + apiId, + `User Id or Role Id Doesn't Exist in Database`, + 'Not found', + HttpStatus.NOT_FOUND + ) + } + return APIResponse.success(response, apiId, result, HttpStatus.OK, 'User role fetched successfully') + } catch (error) { + const errorMessage = error.message || 'Internal server error'; + return APIResponse.error(response, apiId, "Internal Server Error", errorMessage, HttpStatus.INTERNAL_SERVER_ERROR); + } + + } + + public async deleteAssignedRole(deleteAssignRoleDto: DeleteAssignRoleDto, res: Response) { + const apiId = APIID.USERROLE_DELETE; + try { + // Validate userId format + if (!isUUID(deleteAssignRoleDto.userId)) { + return APIResponse.error( + res, + apiId, + `Invalid userId format. Please provide a valid UUID.`, + 'Invalid UUID', + HttpStatus.BAD_REQUEST + ) + + } + // Validate roleId format + for (const roleId of deleteAssignRoleDto.roleId) { + if (!isUUID(roleId)) { + return APIResponse.error( + res, + apiId, + `Invalid roleId format. Please provide valid UUIDs`, + 'Invalid UUID', + HttpStatus.BAD_REQUEST + ) + } + } + // Check if the userId exists in userRoleMapping table + const userExists = await this.userRoleMappingRepository.findOne({ + where: { userId: deleteAssignRoleDto.userId }, + }); + if (!userExists) { + return APIResponse.error( + res, + apiId, + `User not found in userRoleMapping table`, + 'User not found', + HttpStatus.BAD_REQUEST + ) + } + // Check if all roleId(s) exist + const roleExists = await this.userRoleMappingRepository.find({ + where: { + userId: deleteAssignRoleDto.userId, + roleId: In(deleteAssignRoleDto.roleId), + }, + }); + // If any roleId(s) are missing, throw an error + if (roleExists.length !== deleteAssignRoleDto.roleId.length) { + return APIResponse.error( + res, + apiId, + `Roles not found for the user`, + 'Roles not found', + HttpStatus.BAD_REQUEST + ) + } + // If all validations pass, proceed with deletion + const response = await this.userRoleMappingRepository.delete({ + userId: deleteAssignRoleDto.userId, + roleId: In(deleteAssignRoleDto.roleId), + }); + return APIResponse.success(res, apiId, { rowCount: response.affected }, + HttpStatus.OK, 'Roles deleted successfully.') + } catch (e) { + const errorMessage = e.message || 'Internal server error'; + return APIResponse.error(res, apiId, "Internal Server Error", errorMessage, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + // async checkAndAddUserRole(data){ + // let existingUser = await this.checkExistingRole(data.userId) ; + // if(existingUser){ + // let update = + // } + // } + + async checkExistingRole(userId) { + const result = await this.userRoleMappingRepository.findOne({ + where: { userId }, + }) + return result; + } + +} diff --git a/src/adapters/postgres/rbac/privilege-adapter.ts b/src/adapters/postgres/rbac/privilege-adapter.ts new file mode 100644 index 00000000..0252d763 --- /dev/null +++ b/src/adapters/postgres/rbac/privilege-adapter.ts @@ -0,0 +1,270 @@ +import { HttpStatus, Injectable } from "@nestjs/common"; +import { InjectRepository } from "@nestjs/typeorm"; +import { Repository } from "typeorm"; +import { SuccessResponse } from "src/success-response"; +import { ErrorResponseTypeOrm } from "src/error-response-typeorm"; +import { Privilege } from "src/rbac/privilege/entities/privilege.entity"; +import { + CreatePrivilegesDto, + PrivilegeDto, + PrivilegeResponseDto, +} from "src/rbac/privilege/dto/privilege.dto"; +import { isUUID } from "class-validator"; +import { RolePrivilegeMapping } from "src/rbac/assign-privilege/entities/assign-privilege.entity"; +import { Role } from "src/rbac/role/entities/role.entity"; +import { Response } from "express"; +import APIResponse from "src/common/responses/response"; +import { APIID } from "src/common/utils/api-id.config"; +@Injectable() +export class PostgresPrivilegeService { + constructor( + @InjectRepository(Privilege) + private privilegeRepository: Repository, + @InjectRepository(RolePrivilegeMapping) + private rolePrivilegeMappingRepository: Repository, + @InjectRepository(Role) + private roleRepository: Repository, + ) { } + + public async createPrivilege( + loggedinUser: any, + createPrivilegesDto: CreatePrivilegesDto, + response: Response + ) { + const privileges = []; + const errors = []; + const apiId = APIID.PRIVILEGE_CREATE + try { + for (const privilegeDto of createPrivilegesDto.privileges) { + const code = privilegeDto.code; + + // Check if privilege with the same label already exists + const existingPrivilege = await this.checkExistingPrivilege(code); + + if (existingPrivilege) { + errors.push({ + errorMessage: `Privilege with the code '${privilegeDto.code}' already exists.`, + }); + continue; // Skip to the next privilege + } + + privilegeDto.createdBy = loggedinUser; + privilegeDto.updatedBy = loggedinUser; + + // Create new privilege + const privilege = this.privilegeRepository.create(privilegeDto); + const response = await this.privilegeRepository.save(privilege); + privileges.push(new PrivilegeResponseDto(response)); + } + } catch (e) { + const errorMessage = e.message || 'Internal server error'; + return APIResponse.error(response, apiId, "Internal Server Error", errorMessage, HttpStatus.INTERNAL_SERVER_ERROR); + } + return APIResponse.success(response, apiId, + { + successCount: privileges.length, + errorCount: errors.length, + privileges, + errors, + }, + HttpStatus.CREATED, 'Privileges successfully Created') + } + + public async checkExistingPrivilege(code) { + try { + const existingPrivilege = await this.privilegeRepository.findOne({ + where: { code }, + }); + return existingPrivilege; + } catch (error) { + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.INTERNAL_SERVER_ERROR, + errorMessage: error.message || "Internal server error", + }); + } + } + + public async getPrivilege(privilegeId: string, request: any, response: Response) { + const apiId = APIID.PRIVILEGE_BYPRIVILEGEID + try { + if (!isUUID(privilegeId)) { + return APIResponse.error( + response, + apiId, + `Please Enter valid PrivilegeId (UUID)`, + 'Invalid PrivilegeId (UUID)', + HttpStatus.BAD_REQUEST + ) + } + + const privilege = await this.privilegeRepository.findOne({ + where: { privilegeId }, + }); + if (!privilege) { + return APIResponse.error( + response, + apiId, + `Privilege not found`, + 'Not found', + HttpStatus.NOT_FOUND + ) + } + + return APIResponse.success(response, apiId, privilege, + HttpStatus.OK, 'Privilege fetched successfully') + } catch (e) { + const errorMessage = e.message || 'Internal server error'; + return APIResponse.error(response, apiId, "Internal Server Error", errorMessage, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + // public async updatePrivilege(privilegeId: string, request: any, privilegeDto: PrivilegeDto) { + // try { + // // Get the privilege using the getPrivilege method + // const existingPrivilegeResponse = await this.getPrivilege(privilegeId, request); + + // if (existingPrivilegeResponse instanceof ErrorResponseTypeOrm) { + // return existingPrivilegeResponse; + // } + + // // Cast the data property of the SuccessResponse to a Privilege object + // const existingPrivilege: Privilege = existingPrivilegeResponse.data as Privilege; + + // const newLabel = privilegeDto.privilegeName.split(' ').join(''); + + // const result = await this.checkExistingPrivilege(newLabel) + + // if (result) { + // return new ErrorResponseTypeOrm({ + // statusCode: HttpStatus.CONFLICT, + // errorMessage: "Privilege with the same name already exists.", + // }); + // } + // // Merge the updated data into the existing privilege + // const mergedPrivilege = this.privilegeRepository.merge(existingPrivilege, privilegeDto); + + // mergedPrivilege.label = newLabel; + // // Save the updated privilege record + // const updatedPrivilegeRecord = await this.privilegeRepository.save(mergedPrivilege); + + // return new SuccessResponse({ + // statusCode: HttpStatus.OK, + // message: "Privilege updated successfully", + // data: updatedPrivilegeRecord + // }); + // } catch (e) { + // return new ErrorResponseTypeOrm({ + // statusCode: HttpStatus.INTERNAL_SERVER_ERROR, + // errorMessage: "Internal server error", + // }); + // } + // } + + public async getAllPrivilege(request, response: Response) { + try { + const [privileges, totalCount] = await this.privilegeRepository.findAndCount(); + return APIResponse.success(response, APIID.ROLE_GET, { privileges, totalCount }, HttpStatus.OK, 'privileges fetched successfully') + } catch (e) { + const errorMessage = e.message || 'Internal server error'; + return APIResponse.error(response, APIID.PRIVILEGE_BYROLEID, "Internal Server Error", errorMessage, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + public async deletePrivilege(privilegeId: string, res: Response) { + const apiId = APIID.PRIVILEGE_DELETE + try { + const privilegeToDelete = await this.privilegeRepository.findOne({ + where: { privilegeId: privilegeId }, + }); + if (!privilegeToDelete) { + return APIResponse.error( + res, + apiId, + `Privilege not found`, + 'Not found', + HttpStatus.NOT_FOUND + ) + } + // Delete the privilege + const response = await this.privilegeRepository.delete(privilegeId); + + // Delete entries from RolePrivilegesMapping table associated with the privilegeId + const rolePrivilegesDeleteResponse = + await this.rolePrivilegeMappingRepository.delete({ + privilegeId: privilegeId, + }); + return APIResponse.success(res, APIID.PRIVILEGE_DELETE, { rowCount: response.affected }, HttpStatus.OK, 'Privilege deleted successfully.') + + } catch (e) { + const errorMessage = e.message || 'Internal server error'; + return APIResponse.error(res, APIID.PRIVILEGE_DELETE, "Internal Server Error", errorMessage, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + public async getPrivilegebyRoleId(tenantId, roleId, request, response: Response) { + const apiId = APIID.PRIVILEGE_BYROLEID + if (!isUUID(tenantId) || !isUUID(roleId)) { + return APIResponse.error( + response, + apiId, + `Please Enter valid tenantId and roleId (UUID)`, + 'Invalid Tenant Id or Role Id', + HttpStatus.BAD_REQUEST + ) + } + + try { + const valid = await this.checkValidTenantIdRoleIdCombination(tenantId, roleId) + if (!valid) { + return APIResponse.error( + response, + apiId, + `Invalid combination of roleId and tenantId`, + 'Invalid roleId or tenantId ', + HttpStatus.BAD_REQUEST + ) + } + + let query = `SELECT r.*, u.* + FROM public."RolePrivilegesMapping" AS r + inner JOIN public."Privileges" AS u ON r."privilegeId" = u."privilegeId" + where r."roleId"=$1` + + const result = await this.privilegeRepository.query(query, [roleId]); + const privilegeResponseArray: PrivilegeResponseDto[] = result.map((item: any) => { + const privilegeDto = new PrivilegeDto(item); + privilegeDto.title = item.name + return new PrivilegeResponseDto(privilegeDto); + }); + + if (!privilegeResponseArray.length) { + APIResponse.error( + response, + apiId, + `No privileges assigned to the role`, + 'Not found', + HttpStatus.NOT_FOUND + ) + } + return APIResponse.success(response, apiId, privilegeResponseArray, HttpStatus.OK, 'privilege fetched successfully by Role Id') + } + catch (error) { + const errorMessage = error.message || 'Internal server error'; + return APIResponse.error(response, apiId, "Internal Server Error", errorMessage, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + public async checkValidTenantIdRoleIdCombination(tenantId, roleId) { + try { + const ValidTenantIdRoleCombination = await this.roleRepository.findOne({ where: { tenantId: tenantId, roleId: roleId } }); + return ValidTenantIdRoleCombination; + } + catch (error) { + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.INTERNAL_SERVER_ERROR, + errorMessage: error.message || "Internal server error", + }); + } + } + +} diff --git a/src/adapters/postgres/rbac/privilegerole.adapter.ts b/src/adapters/postgres/rbac/privilegerole.adapter.ts new file mode 100644 index 00000000..02707a18 --- /dev/null +++ b/src/adapters/postgres/rbac/privilegerole.adapter.ts @@ -0,0 +1,89 @@ +import {HttpStatus, Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { In, Repository } from 'typeorm'; +import { CreatePrivilegeRoleDto } from 'src/rbac/assign-privilege/dto/create-assign-privilege.dto'; +import { RolePrivilegeMapping } from 'src/rbac/assign-privilege/entities/assign-privilege.entity'; +import { isUUID } from 'class-validator'; +import APIResponse from 'src/common/responses/response'; +import { Response } from 'express'; +import { APIID } from 'src/common/utils/api-id.config'; + +@Injectable() +export class PostgresAssignPrivilegeService { + constructor( + @InjectRepository(RolePrivilegeMapping) + private rolePrivilegeMappingRepository: Repository + ){} + public async createPrivilegeRole(request: Request,createPrivilegeRoleDto:CreatePrivilegeRoleDto,response:Response){ + const apiId = APIID.ASSIGNPRIVILEGE_CREATE; + try { + let result ; + if (createPrivilegeRoleDto.deleteOld) { + await this.deleteByRoleId(createPrivilegeRoleDto.roleId); + } + const privilegeRoles = createPrivilegeRoleDto.privilegeId.map(privilegeId => ({ + roleId: createPrivilegeRoleDto.roleId, + privilegeId + })); + const existingPrivileges = await this.rolePrivilegeMappingRepository.find({ + where: { + roleId: createPrivilegeRoleDto.roleId, + privilegeId: In(createPrivilegeRoleDto.privilegeId) + } + }); + + const newPrivileges = privilegeRoles.filter(privilegeRole => { + return !existingPrivileges.some(existing => existing.privilegeId === privilegeRole.privilegeId); + }); + + for (let data of newPrivileges) { + result = await this.rolePrivilegeMappingRepository.save(data); + } + + return await APIResponse.success(response, apiId, result,HttpStatus.CREATED, "Privileges assigned successfully.") + } catch (error) { + if(error.code === '23503') { + return APIResponse.error(response, apiId, "Not Found",`Privilege Id or Role Id Doesn't Exist in Database.`, HttpStatus.NOT_FOUND); + } + + return APIResponse.error(response, apiId, "Not Found",`Error is: ${error}.`, HttpStatus.NOT_FOUND); + } + } + + public async deleteByRoleId(roleId: string) { + try { + await this.rolePrivilegeMappingRepository.delete({ roleId }); + } catch (error) { + throw error; + } +} + + + public async getPrivilegeRole(roleId:string,request: Request,response:Response){ + const apiId = APIID.ASSIGNPRIVILEGE_GET; + try { + if (!isUUID(roleId)) { + return APIResponse.error(response, apiId, "Bad Request","Please Enter Valid User ID.", HttpStatus.BAD_REQUEST); + } + let result = await this.checkExistingRole(roleId); + + if(!result){ + return APIResponse.error(response, apiId, "Not Found","No Role Found.", HttpStatus.NOT_FOUND); + } + + return await APIResponse.success(response, apiId, result,HttpStatus.OK, "Privileges for role fetched successfully.") + + } catch (error) { + return APIResponse.error(response, apiId, "Internal Server Error",`Something went wrong.`, HttpStatus.INTERNAL_SERVER_ERROR); + } + + } + + async checkExistingRole(roleId){ + const result= await this.rolePrivilegeMappingRepository.find({ + where: { roleId } + }) + return result; + } + +} diff --git a/src/adapters/postgres/rbac/role-adapter.ts b/src/adapters/postgres/rbac/role-adapter.ts new file mode 100644 index 00000000..e229884d --- /dev/null +++ b/src/adapters/postgres/rbac/role-adapter.ts @@ -0,0 +1,290 @@ +import { HttpStatus, Injectable } from "@nestjs/common"; +import { Role } from "src/rbac/role/entities/role.entity"; +import { RolePrivilegeMapping } from "src/rbac/assign-privilege/entities/assign-privilege.entity"; +import { InjectRepository } from "@nestjs/typeorm"; +import { Repository } from "typeorm"; +import { + CreateRolesDto, + RoleDto, + RolesResponseDto, +} from "../../../rbac/role/dto/role.dto"; +import { RoleSearchDto } from "../../../rbac/role/dto/role-search.dto"; +import { UserRoleMapping } from "src/rbac/assign-role/entities/assign-role.entity"; +import { Privilege } from "src/rbac/privilege/entities/privilege.entity"; +import { isUUID } from "class-validator"; +import APIResponse from "src/common/responses/response"; +import { Response } from 'express'; +import { APIID } from 'src/common/utils/api-id.config' + +@Injectable() +export class PostgresRoleService { + constructor( + @InjectRepository(Role) + private roleRepository: Repository, + @InjectRepository(UserRoleMapping) + private readonly userRoleMappingRepository: Repository, + @InjectRepository(RolePrivilegeMapping) + private readonly roleprivilegeMappingRepository: Repository + ) { } + public async createRole(request: any, createRolesDto: CreateRolesDto, response: Response) { + const apiId = APIID.ROLE_CREATE + const tenant = await this.checkTenantID(createRolesDto.tenantId) + if (!tenant) { + return APIResponse.error( + response, + apiId, + `Please enter valid tenantId`, + 'Invalid Tenant Id', + HttpStatus.BAD_REQUEST + ) + } + const roles = []; + const errors = [] + try { + + // Convert role name to lowercase + for (const roleDto of createRolesDto.roles) { + const tenantId = createRolesDto.tenantId; + const code = roleDto.title.toLowerCase().replace(/\s+/g, '_'); + + // Check if role name already exists + const existingRole = await this.roleRepository.findOne({ where: { code: code, tenantId: tenantId } }) + if (existingRole) { + errors.push({ + errorMessage: `Combination of this tenantId and the code '${code}' already exists.`, + }); + continue; + } + + const newRoleDto = new RoleDto({ + ...roleDto, + code, + createdAt: new Date(), + updatedAt: new Date(), + createdBy: request.user.userId, // Assuming you have a user object in the request + updatedBy: request.user.userId, + tenantId, // Add the tenantId to the RoleDto + }); + // Convert roleDto to lowercase + // const response = await this.roleRepository.save(roleDto); + const roleEntity = this.roleRepository.create(newRoleDto); + + // Save the role entity to the database + const response = await this.roleRepository.save(roleEntity); + roles.push(new RolesResponseDto(response)); + } + } catch (e) { + const errorMessage = e.message || 'Internal server error'; + return APIResponse.error(response, apiId, "Internal Server Error", errorMessage, HttpStatus.INTERNAL_SERVER_ERROR); + } + return APIResponse.success(response, apiId, { successCount: roles.length, errorCount: errors.length, roles, errors }, + HttpStatus.OK, 'Role successfully Created') + } + + public async getRole(roleId: string, request: any, response: Response) { + const apiId = APIID.ROLE_GET + try { + const [roles, totalCount] = await this.roleRepository.findAndCount({ + where: { roleId }, + }); + return APIResponse.success(response, apiId, { roles, totalCount }, HttpStatus.OK, 'Roles fetched successfully') + } catch (e) { + const errorMessage = e.message || 'Internal server error'; + return APIResponse.error(response, apiId, "Internal Server Error", errorMessage, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + public async updateRole(roleId: string, request: any, roleDto: RoleDto, response: Response) { + const apiId = APIID.ROLE_UPDATE + try { + const code = roleDto.title.toLowerCase().replace(/\s+/g, '_'); + roleDto.code = code; + const result = await this.roleRepository.update(roleId, roleDto); + return APIResponse.success(response, apiId, { rowCount: result.affected, }, HttpStatus.OK, 'Roles Updated successful') + } catch (e) { + const errorMessage = e.message || 'Internal server error'; + return APIResponse.error(response, apiId, "Internal Server Error", errorMessage, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + public async searchRole(roleSearchDto: RoleSearchDto, response: Response) { + const apiId = APIID.ROLE_SEARCH + try { + let { limit, page, filters } = roleSearchDto; + + let offset = 0; + if (page > 1) { + offset = parseInt(limit) * (page - 1); + } + + if (limit.trim() === "") { + limit = "0"; + } + + let whereClause: any = {}; + if (filters && Object.keys(filters).length > 0) { + Object.entries(filters).forEach(([key, value]) => { + whereClause[key] = value; + }); + } + if (whereClause.userId && !whereClause.tenantId) { + return APIResponse.error( + response, + APIID.ROLE_SEARCH, + `Please Enter Tenenat id or Valid Filter`, + 'Invalid Tenant Id or Valid Filter', + HttpStatus.BAD_REQUEST + ) + } + if (whereClause.field && !["Privilege"].includes(whereClause?.field)) { + return APIResponse.error( + response, + APIID.ROLE_SEARCH, + `Please Enter valid field value.`, + 'Invalid field value.', + HttpStatus.BAD_REQUEST + ) + } + if (whereClause.userId && whereClause.tenantId && whereClause.field === "Privilege") { + const userRoleMappingData = await this.findUserRoleData(whereClause.userId, whereClause.tenantId); + const roleIds = userRoleMappingData.map(data => data.roleid); + + const result = await this.findPrivilegeByRoleId(roleIds); + + const roles = userRoleMappingData.map(data => { + const roleResult = result.find(privilegeData => privilegeData.roleid === data.roleid); + return { + roleId: data.roleid, + title: data.title, + code: data.code, + privileges: roleResult ? roleResult : [] + }; + }); + return APIResponse.success(response, apiId, roles, HttpStatus.OK, 'Role For User with Privileges fetched successfully.') + } else if (whereClause.userId && whereClause.tenantId && !whereClause.field) { + const data = await this.findUserRoleData(whereClause.userId, whereClause.tenantId) + return APIResponse.success(response, apiId, data, HttpStatus.OK, 'Role For User Id fetched successfully.') + } + else if (whereClause.tenantId && whereClause.field === "Privilege") { + const userRoleData = await this.findRoleData(whereClause.tenantId); + const result = await this.findPrivilegeByRoleId(userRoleData.map(data => data.roleId)); + const roles = userRoleData.map(data => { + const roleResult = result.find(privilegeData => privilegeData.roleid === data.roleId); + return { + roleId: data.roleId, + title: data.title, + code: data.code, + privileges: roleResult ? roleResult : [] + }; + }); + return APIResponse.success(response, apiId, roles, HttpStatus.OK, 'Role For Tenant with Privileges fetched successfully.') + } else if (whereClause.tenantId && !whereClause.field) { + const data = await this.findRoleData(whereClause.tenantId); + return APIResponse.success(response, apiId, data, HttpStatus.OK, 'Role For Tenant fetched successfully.') + } else { + return APIResponse.error( + response, + APIID.ROLE_SEARCH, + `Please Enter Valid Filter`, + 'Invalid Filter', + HttpStatus.BAD_REQUEST + ) + } + } catch (e) { + const errorMessage = e.message || 'Internal server error'; + return APIResponse.error(response, apiId, "Internal Server Error", errorMessage, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + public async deleteRole(roleId: string, res: Response) { + const apiId = APIID.ROLE_DELETE + try { + if (!isUUID(roleId)) { + return APIResponse.error( + res, + apiId, + `Please Enter valid (UUID)`, + 'Invalid UUID', + HttpStatus.BAD_REQUEST + ) + } + + const roleToDelete = await this.roleRepository.findOne({ + where: { roleId: roleId }, + }); + + if (!roleToDelete) { + return APIResponse.error( + res, + apiId, + `Role not found`, + 'Not found', + HttpStatus.NOT_FOUND + ) + } + // Delete the role + const response = await this.roleRepository.delete(roleId); + + // Delete entries from RolePrivilegesMapping table associated with the roleId + const rolePrivilegesDeleteResponse = + await this.roleprivilegeMappingRepository.delete({ + roleId: roleId, + }); + + const userRoleDeleteResponse = + await this.userRoleMappingRepository.delete({ + roleId: roleId, + }); + return APIResponse.success(res, apiId, { rowCount: response.affected }, HttpStatus.OK, 'Role deleted successfully.') + } catch (e) { + const errorMessage = e.message || 'Internal server error'; + return APIResponse.error(res, apiId, "Internal Server Error", errorMessage, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + + public async findRoleData(id: string) { + const data = await this.roleRepository.find({ + where: { + tenantId: id, + }, + select: ["roleId", "title", "code"], + }); + return data; + } + + public async findUserRoleData(userId: string, tenantId: string) { + let userRoleData = await this.userRoleMappingRepository.createQueryBuilder('urp'). + innerJoin(Role, 'r', 'r.roleId=urp.roleId'). + select(['urp.roleId as roleid', 'r.title as title', 'r.code as code']). + where("urp.userId= :userId", { userId }). + andWhere("urp.tenantId=:tenantId", { tenantId }) + .getRawMany() + return userRoleData; + } + + public async findPrivilegeByRoleId(roleIds: string[]) { + const privileges = await this.roleprivilegeMappingRepository + .createQueryBuilder("rpm") + .innerJoin(Privilege, "p", "p.privilegeId=rpm.privilegeId") + .select([ + "p.privilegeId as privilegeId", + "p.name as name", + "p.code as code", + "rpm.roleId as roleId" + ]) + .where("rpm.roleId IN (:...roleIds)", { roleIds }) + .getRawMany(); + return privileges; + } + + public async checkTenantID(tenantId) { + let query = `SELECT "tenantId" FROM public."Tenants" + where "tenantId"= $1 `; + let response = await this.roleRepository.query(query, [tenantId]); + if (response.length > 0) { + return true; + } + return false; + } +} diff --git a/src/adapters/postgres/user-adapter.ts b/src/adapters/postgres/user-adapter.ts new file mode 100644 index 00000000..b23a3c13 --- /dev/null +++ b/src/adapters/postgres/user-adapter.ts @@ -0,0 +1,829 @@ +import { HttpStatus, Injectable } from '@nestjs/common'; +import { User } from '../../user/entities/user-entity' +import { FieldValues } from '../../user/entities/field-value-entities'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { UserCreateDto } from '../../user/dto/user-create.dto'; +import jwt_decode from "jwt-decode"; +import { + getKeycloakAdminToken, + createUserInKeyCloak, + checkIfUsernameExistsInKeycloak, + checkIfEmailExistsInKeycloak +} from "../../common/utils/keycloak.adapter.util" +import { ErrorResponse } from 'src/error-response'; +import { SuccessResponse } from 'src/success-response'; +import { Field } from '../../user/entities/field-entity'; +import { CohortMembers } from 'src/cohortMembers/entities/cohort-member.entity'; +import { ErrorResponseTypeOrm } from 'src/error-response-typeorm'; +import { isUUID } from 'class-validator'; +import { UserSearchDto } from 'src/user/dto/user-search.dto'; +import { UserTenantMapping } from "src/userTenantMapping/entities/user-tenant-mapping.entity"; +import { UserRoleMapping } from "src/rbac/assign-role/entities/assign-role.entity"; +import { Tenants } from "src/userTenantMapping/entities/tenant.entity"; +import { Cohort } from "src/cohort/entities/cohort.entity"; +import { Role } from "src/rbac/role/entities/role.entity"; +import { UserData } from 'src/user/user.controller'; +import APIResponse from 'src/common/responses/response'; +import { Response } from 'express'; +import { APIID } from 'src/common/utils/api-id.config'; +import { IServicelocator } from '../userservicelocator'; + +@Injectable() +export class PostgresUserService implements IServicelocator { + axios = require("axios"); + + constructor( + // private axiosInstance: AxiosInstance, + @InjectRepository(User) + private usersRepository: Repository, + @InjectRepository(FieldValues) + private fieldsValueRepository: Repository, + @InjectRepository(Field) + private fieldsRepository: Repository, + @InjectRepository(CohortMembers) + private cohortMemberRepository: Repository, + @InjectRepository(UserTenantMapping) + private userTenantMappingRepository: Repository, + @InjectRepository(Tenants) + private tenantsRepository: Repository, + @InjectRepository(UserRoleMapping) + private userRoleMappingRepository: Repository, + @InjectRepository(Cohort) + private cohortRepository: Repository, + @InjectRepository(Role) + private roleRepository: Repository, + ) { } + + async searchUser(tenantId: string, + request: any, + response: any, + userSearchDto: UserSearchDto) { + const apiId = APIID.USER_LIST; + try { + let findData = await this.findAllUserDetails(userSearchDto); + if (!findData.length) { + return APIResponse.error(response, apiId, "Bad request", `Either Filter is wrong or No Data Found For the User`, HttpStatus.BAD_REQUEST); + } + return await APIResponse.success(response, apiId, findData, + HttpStatus.OK, 'User List fetched.') + } catch (e) { + return APIResponse.error(response, apiId, "Internal Server Error", "Something went wrong", HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + async findAllUserDetails(userSearchDto) { + let { limit, page, filters } = userSearchDto; + + let offset = 0; + if (page > 1) { + offset = parseInt(limit) * (page - 1); + } + + if (limit.trim() === '') { + limit = '0'; + } + + const whereClause = {}; + if (filters && Object.keys(filters).length > 0) { + Object.entries(filters).forEach(([key, value]) => { + whereClause[key] = value; + }); + } + const results = await this.usersRepository.find({ + where: whereClause, + skip: offset, + take: parseInt(limit), + }); + return results; + } + + async getUsersDetailsById(userData: UserData, response: any) { + const apiId = APIID.USER_GET; + try { + if (!isUUID(userData.userId)) { + return APIResponse.error(response, apiId, "Bad request", `Please Enter Valid UUID`, HttpStatus.BAD_REQUEST); + } + const checkExistUser = await this.usersRepository.find({ + where: { + userId: userData.userId + } + }) + + if (checkExistUser.length == 0) { + return APIResponse.error(response, apiId, "Not Found", `User Id '${userData.userId}' does not exist.`, HttpStatus.NOT_FOUND); + } + + const result = { + userData: { + } + }; + let filledValues: any; + let customFieldsArray = []; + + let [userDetails, userRole] = await Promise.all([ + this.findUserDetails(userData.userId), + this.findUserRoles(userData.userId, userData.tenantId) + ]); + const roleInUpper = (userRole.title).toUpperCase(); + if (userData?.fieldValue) { + filledValues = await this.findFilledValues(userData.userId, roleInUpper) + } + + if (userRole) { + userDetails['role'] = userRole.title; + } + + if (!userDetails) { + return APIResponse.error(response, apiId, "Not Found", `User Not Found`, HttpStatus.NOT_FOUND); + } + if (!userData.fieldValue) { + return await APIResponse.success(response, apiId, { userData: userDetails }, + HttpStatus.OK, 'User details Fetched Successfully.') + } + const customFields = await this.findCustomFields(userData, roleInUpper) + result.userData = userDetails; + const filledValuesMap = new Map(filledValues.map(item => [item.fieldId, item.value])); + + for (let data of customFields) { + let fieldValue: any = filledValuesMap.get(data.fieldId); + + if (fieldValue) { + fieldValue = fieldValue.split(',') + } + + const customField = { + fieldId: data.fieldId, + name: data?.name, + label: data.label, + order: data.ordering, + value: fieldValue || '', + isRequired: data.fieldAttributes ? data.fieldAttributes['isRequired'] : '', + isEditable: data.fieldAttributes ? data.fieldAttributes['isEditable'] : '', + options: data?.fieldParams?.['options'] || {}, + type: data.type || '' + }; + customFieldsArray.push(customField); + } + + result.userData['customFields'] = customFieldsArray; + return await APIResponse.success(response, apiId, { ...result }, + HttpStatus.OK, 'User details Fetched Successfully.') + } catch (e) { + ; + return APIResponse.error(response, apiId, "Internal Server Error", "Something went wrong", HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + async getUsersDetailsByCohortId(userData: Record, response: any) { + let apiId = 'api.users.getAllUsersDetails' + try { + if (userData?.fieldValue) { + let getUserDetails = await this.findUserName(userData.cohortId, userData.contextType) + let result = { + userDetails: [], + }; + + for (let data of getUserDetails) { + let userDetails = { + userId: data.userId, + userName: data.userName, + name: data.name, + role: data.role, + district: data.district, + state: data.state, + mobile: data.mobile, + } + result.userDetails.push(userDetails); + } + + return new SuccessResponse({ + statusCode: HttpStatus.OK, + message: 'Ok.', + data: result, + }); + + } else { + let getUserDetails = await this.findUserName(userData.cohortId, userData.contextType) + let result = { + userDetails: [], + }; + + for (let data of getUserDetails) { + let userDetails = { + userId: data.userId, + userName: data.userName, + name: data.name, + role: data.role, + district: data.district, + state: data.state, + mobile: data.mobile, + customField: [], + } + const fieldValues = await this.getFieldandFieldValues(data.userId) + userDetails.customField.push(fieldValues); + + result.userDetails.push(userDetails); + } + + return new SuccessResponse({ + statusCode: HttpStatus.OK, + message: 'Ok.', + data: result, + }); + + } + } catch (e) { + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.INTERNAL_SERVER_ERROR, + errorMessage: e, + }); + } + } + + async findUserName(cohortId: string, role: string) { + let query = `SELECT U."userId", U.username, U.name, U.role, U.district, U.state,U.mobile FROM public."CohortMembers" CM + LEFT JOIN public."Users" U + ON CM."userId" = U."userId" + where CM."cohortId" =$1 ` + if (role !== null) { + query += ` AND U."role" = $2`; + } + let result: any[]; + if (role !== null) { + result = await this.usersRepository.query(query, [cohortId, role]); + } else { + result = await this.usersRepository.query(query, [cohortId]); + } + return result; + } + + async getFieldandFieldValues(userId: string) { + let query = `SELECT Fv."fieldId",F."label" AS FieldName,Fv."value" as FieldValues + FROM public."FieldValues" Fv + LEFT JOIN public."Fields" F + ON F."fieldId" = Fv."fieldId" + where Fv."itemId" =$1 ` + let result = await this.usersRepository.query(query, [userId]); + return result + } + + async findUserRoles(userId: string, tenantId: string) { + + const getRole = await this.userRoleMappingRepository.findOne({ + where: { + userId: userId, + tenantId: tenantId + } + }) + if (!getRole) { + return false; + } + let role; + role = await this.roleRepository.findOne({ + where: { + roleId: getRole.roleId, + }, + select: ["title"] + }) + return role + } + + async findUserDetails(userId, username?: any) { + let whereClause: any = { userId: userId }; + if (username && userId === null) { + delete whereClause.userId; + whereClause.username = username; + } + let userDetails = await this.usersRepository.findOne({ + where: whereClause, + select: ["userId", "username", "name", "district", "state", "mobile"] + }) + if (!userDetails) { + return false; + } + const tenentDetails = await this.allUsersTenent(userDetails.userId) + userDetails['tenantData'] = tenentDetails; + return userDetails; + } + async allUsersTenent(userId: string) { + const query = ` + SELECT T.name AS tenantName, T."tenantId", UTM."Id" AS userTenantMappingId + FROM public."UserTenantMapping" UTM + LEFT JOIN public."Tenants" T + ON T."tenantId" = UTM."tenantId" + WHERE UTM."userId" = $1`; + const result = await this.usersRepository.query(query, [userId]); + return result; + } + async findCustomFields(userData, role) { + let customFields = await this.fieldsRepository.find({ + where: { + context: userData.context, + contextType: role, + } + }) + return customFields; + } + async findFilledValues(userId: string, role: string) { + let query = `SELECT U."userId",FV."fieldId",FV."value", F."fieldAttributes" FROM public."Users" U + LEFT JOIN public."FieldValues" FV + ON U."userId" = FV."itemId" + LEFT JOIN public."Fields" F + ON F."fieldId" = FV."fieldId" + where U."userId" =$1 AND F."contextType" = $2`; + + let result = await this.usersRepository.query(query, [userId, role]); + return result; + } + + async updateUser(userDto, response: Response) { + const apiId = APIID.USER_UPDATE; + try { + let updatedData = {}; + let errorMessage; + + if (userDto.userData) { + await this.updateBasicUserDetails(userDto.userId, userDto.userData); + updatedData['basicDetails'] = userDto.userData; + } + + if (userDto?.customFields?.length > 0) { + const getFieldsAttributesQuery = ` + SELECT * + FROM "public"."Fields" + WHERE "fieldAttributes"->>'isEditable' = $1 + `; + const getFieldsAttributesParams = ['true']; + const getFieldsAttributes = await this.fieldsRepository.query(getFieldsAttributesQuery, getFieldsAttributesParams); + + let isEditableFieldId = []; + for (let fieldDetails of getFieldsAttributes) { + isEditableFieldId.push(fieldDetails.fieldId); + } + + // let errorMessage = []; + let unEditableIdes = []; + for (let data of userDto.customFields) { + if (isEditableFieldId.includes(data.fieldId)) { + const result = await this.updateCustomFields(userDto.userId, data); + if (result) { + if (!updatedData['customFields']) + updatedData['customFields'] = []; + updatedData['customFields'].push(result); + } + } else { + unEditableIdes.push(data.fieldId) + } + } + if (unEditableIdes.length > 0) { + errorMessage = `Uneditable fields: ${unEditableIdes.join(', ')}` + } + } + return await APIResponse.success(response, apiId, updatedData, + HttpStatus.OK, "User has been updated successfully.") + } catch (e) { + return APIResponse.error(response, apiId, "Internal Server Error", "Something went wrong", HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + async updateBasicUserDetails(userId, userData: Partial): Promise { + const user = await this.usersRepository.findOne({ where: { userId: userId } }); + if (!user) { + return null; + } + Object.assign(user, userData); + + return this.usersRepository.save(user); + } + + + async updateCustomFields(itemId, data) { + + if (Array.isArray(data.value) === true) { + let dataArray = []; + for (let value of data.value) { + dataArray.push(value); + } + data.value = dataArray.join(','); + } + let result = await this.fieldsValueRepository.update({ itemId, fieldId: data.fieldId }, { value: data.value }); + let newResult; + + if (result.affected === 0) { + newResult = await this.fieldsValueRepository.save({ + itemId, + fieldId: data.fieldId, + value: data.value + }); + } + Object.assign(result, newResult); + return result; + } + + async createUser(request: any, userCreateDto: UserCreateDto, response: Response) { + const apiId = APIID.USER_CREATE; + // It is considered that if user is not present in keycloak it is not present in database as well + try { + const decoded: any = jwt_decode(request.headers.authorization); + userCreateDto.createdBy = decoded?.sub + userCreateDto.updatedBy = decoded?.sub + + //Check duplicate field entry + if (userCreateDto.fieldValues) { + let field_values = userCreateDto.fieldValues; + const validateField = await this.validateFieldValues(field_values); + + if (validateField == false) { + return APIResponse.error(response, apiId, "Conflict", `Duplicate fieldId found in fieldValues.`, HttpStatus.CONFLICT); + } + } + + // check and validate all fields + let validateBodyFields = await this.validateBodyFields(userCreateDto) + + if (validateBodyFields == true) { + userCreateDto.username = userCreateDto.username.toLocaleLowerCase(); + const userSchema = new UserCreateDto(userCreateDto); + + let errKeycloak = ""; + let resKeycloak = ""; + + const keycloakResponse = await getKeycloakAdminToken(); + const token = keycloakResponse.data.access_token; + let checkUserinKeyCloakandDb = await this.checkUserinKeyCloakandDb(userCreateDto) + // let checkUserinDb = await this.checkUserinKeyCloakandDb(userCreateDto.username); + if (checkUserinKeyCloakandDb) { + return APIResponse.error(response, apiId, "Forbidden", `User Already Exist`, HttpStatus.FORBIDDEN); + } + resKeycloak = await createUserInKeyCloak(userSchema, token).catch( + (error) => { + errKeycloak = error.response?.data.errorMessage; + return APIResponse.error(response, apiId, "Internal Server Error", `${errKeycloak}`, HttpStatus.INTERNAL_SERVER_ERROR); + } + ); + userCreateDto.userId = resKeycloak; + + let result = await this.createUserInDatabase(request, userCreateDto); + + let fieldData = {}; + if (userCreateDto.fieldValues) { + + if (result && userCreateDto.fieldValues?.length > 0) { + let userId = result?.userId; + for (let fieldValues of userCreateDto.fieldValues) { + + fieldData = { + fieldId: fieldValues['fieldId'], + value: fieldValues['value'] + } + let result = await this.updateCustomFields(userId, fieldData); + if (!result) { + return APIResponse.error(response, apiId, "Internal Server Error", `Error is ${result}`, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + } + } + + APIResponse.success(response, apiId, { userData: result }, + HttpStatus.CREATED, "User has been created successfully.") + } + } catch (e) { + if (e instanceof ErrorResponseTypeOrm) { + return e; + } else { + return APIResponse.error(response, apiId, "Internal Server Error", "Something went wrong", HttpStatus.INTERNAL_SERVER_ERROR); + } + } + } + + async validateBodyFields(userCreateDto) { + for (const tenantCohortRoleMapping of userCreateDto.tenantCohortRoleMapping) { + + const { tenantId, cohortId, roleId } = tenantCohortRoleMapping; + + const [tenantExists, cohortExists, roleExists] = await Promise.all([ + this.tenantsRepository.find({ where: { tenantId } }), + this.cohortRepository.find({ where: { tenantId, cohortId } }), + this.roleRepository.find({ where: { roleId } }) + ]); + + if (tenantExists.length === 0) { + throw new ErrorResponseTypeOrm({ + statusCode: HttpStatus.BAD_REQUEST, + errorMessage: `Tenant Id '${tenantId}' does not exist.`, + }); + } + + if (cohortExists.length === 0) { + throw new ErrorResponseTypeOrm({ + statusCode: HttpStatus.BAD_REQUEST, + errorMessage: `Cohort Id '${cohortId}' does not exist for this tenant '${tenantId}'.`, + }); + } + + if (roleExists.length === 0) { + throw new ErrorResponseTypeOrm({ + statusCode: HttpStatus.BAD_REQUEST, + errorMessage: `Role Id '${roleId}' does not exist.`, + }); + } + } + return true; + } + + async checkUser(body) { + let checkUserinKeyCloakandDb = await this.checkUserinKeyCloakandDb(body); + if (checkUserinKeyCloakandDb) { + return new SuccessResponse({ + statusCode: 200, + message: "User Exists. Proceed with Sending Email ", + data: { data: true }, + }); + } + return new SuccessResponse({ + statusCode: HttpStatus.BAD_REQUEST, + message: "Invalid Username Or Email", + data: { data: false }, + }); + } + + // Can be Implemeneted after we know what are the unique entties + async checkUserinKeyCloakandDb(userDto) { + const keycloakResponse = await getKeycloakAdminToken(); + const token = keycloakResponse.data.access_token; + if (userDto?.username) { + const usernameExistsInKeycloak = await checkIfUsernameExistsInKeycloak( + userDto?.username, + token + ); + if (usernameExistsInKeycloak?.data?.length > 0) { + return usernameExistsInKeycloak; + } + return false; + } else { + const usernameExistsInKeycloak = await checkIfEmailExistsInKeycloak( + userDto?.email, + token + ); + if (usernameExistsInKeycloak.data.length > 0) { + return usernameExistsInKeycloak; + } + return false; + } + } + + + async createUserInDatabase(request: any, userCreateDto: UserCreateDto) { + const user = new User() + user.username = userCreateDto?.username + user.name = userCreateDto?.name + user.email = userCreateDto?.email + user.mobile = Number(userCreateDto?.mobile) || null, + user.createdBy = userCreateDto?.createdBy + user.updatedBy = userCreateDto?.updatedBy + user.userId = userCreateDto?.userId, + user.state = userCreateDto?.state, + user.district = userCreateDto?.district, + user.address = userCreateDto?.address, + user.pincode = userCreateDto?.pincode + + if (userCreateDto?.dob) { + user.dob = new Date(userCreateDto.dob); + } + + let result = await this.usersRepository.save(user); + + if (result) { + for (let mapData of userCreateDto.tenantCohortRoleMapping) { + let cohortData = { + userId: result?.userId, + cohortId: mapData?.cohortId + } + + await this.addCohortMember(cohortData); + + let tenantRoleMappingData = { + userId: result?.userId, + tenantRoleMapping: mapData, + } + await this.assignUserToTenant(tenantRoleMappingData, request); + } + } + return result; + } + + async assignUserToTenant(tenantsData, request) { + try { + const tenantId = tenantsData?.tenantRoleMapping?.tenantId; + const userId = tenantsData?.userId; + const roleId = tenantsData?.tenantRoleMapping?.roleId; + + if (roleId) { + const data = await this.userRoleMappingRepository.save({ + userId: userId, + tenantId: tenantId, + roleId: roleId, + createdBy: request['user'].userId, + updatedBy: request['user'].userId + }) + } + + const data = await this.userTenantMappingRepository.save({ + userId: userId, + tenantId: tenantId, + createdBy: request['user'].userId, + updatedBy: request['user'].userId + }) + + + } catch (error) { + throw new Error(error) + } + } + + public async validateUserTenantMapping(userId: string, tenantId: string) { + // check if tenant exists + const tenantExist = await this.tenantsRepository.findOne({ where: { tenantId: tenantId } }); + if (!tenantExist) { + return false + } else { + return true + } + } + + async addCohortMember(cohortData) { + try { + let result = await this.cohortMemberRepository.insert(cohortData); + return result;; + } catch (error) { + throw new Error(error) + } + } + + public async resetUserPassword( + request: any, + username: string, + newPassword: string, + response: Response + ) { + const apiId = APIID.USER_RESET_PASSWORD; + try { + const userData: any = await this.findUserDetails(null, username); + let userId; + + if (userData?.userId) { + userId = userData?.userId; + } else { + return APIResponse.error(response, apiId, "Not Found", `User with given username not found`, HttpStatus.NOT_FOUND); + } + + // const data = JSON.stringify({ + // temporary: "false", + // type: "password", + // value: newPassword, + // }); + + const keycloakResponse = await getKeycloakAdminToken(); + const resToken = keycloakResponse.data.access_token; + let apiResponse; + + try { + apiResponse = await this.resetKeycloakPassword( + request, + resToken, + newPassword, + userId + ); + } catch (e) { + return APIResponse.error(response, apiId, "Internal Server Error", `Error : ${e?.response?.data.error}`, HttpStatus.INTERNAL_SERVER_ERROR); + } + + if (apiResponse.statusCode === 204) { + return await APIResponse.success(response, apiId, {}, + HttpStatus.NO_CONTENT, 'User Password Updated Successfully.') + } else { + return APIResponse.error(response, apiId, "Bad Request", `Error : ${apiResponse?.errors}`, HttpStatus.BAD_REQUEST); + } + } catch (e) { + // return e; + return APIResponse.error(response, apiId, "Internal Server Error", `Error : ${e?.response?.data.error}`, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + public async resetKeycloakPassword( + request: any, + token: string, + newPassword: string, + userId: string + ) { + const data = JSON.stringify({ + temporary: "false", + type: "password", + value: newPassword, + }); + + if (!token) { + const response = await getKeycloakAdminToken(); + token = response.data.access_token; + } + + let apiResponse; + + const config = { + method: "put", + url: + process.env.KEYCLOAK + + process.env.KEYCLOAK_ADMIN + + "/" + + userId + + "/reset-password", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + token, + }, + data: data, + }; + + try { + apiResponse = await this.axios(config); + } catch (e) { + return new ErrorResponse({ + errorCode: `${e.response.status}`, + errorMessage: e.response.data.error, + }); + } + + if (apiResponse.status === 204) { + return new SuccessResponse({ + statusCode: apiResponse.status, + message: apiResponse.statusText, + data: { msg: "Password reset successful!" }, + }); + } else { + return new ErrorResponse({ + errorCode: "400", + errorMessage: apiResponse.errors, + }); + } + } + + public async validateFieldValues(field_values) { + let encounteredKeys = [] + for (const fieldValue of field_values) { + const fieldId = fieldValue['fieldId']; + // const [fieldId] = fieldValue.split(":").map(value => value.trim()); + if (encounteredKeys.includes(fieldId)) { + return false + } + encounteredKeys.push(fieldId); + }; + } + + public async deleteUserById(userId: string, response: Response) { + const apiId = APIID.USER_DELETE; + const { KEYCLOAK, KEYCLOAK_ADMIN } = process.env; + // Validate userId format + if (!isUUID(userId)) { + return APIResponse.error(response, apiId, "Bad request", `Please Enter Valid UUID for userId`, HttpStatus.BAD_REQUEST); + } + + try { + // Check if user exists in usersRepository + const user = await this.usersRepository.findOne({ where: { userId: userId } }); + if (!user) { + return APIResponse.error(response, apiId, "Not Found", `User not found in user table.`, HttpStatus.NOT_FOUND); + } + + + // Delete from User table + const userResult = await this.usersRepository.delete(userId); + + // Delete from CohortMembers table + const cohortMembersResult = await this.cohortMemberRepository.delete({ userId: userId }); + + // Delete from UserTenantMapping table + const userTenantMappingResult = await this.userTenantMappingRepository.delete({ userId: userId }); + + // Delete from UserRoleMapping table + const userRoleMappingResult = await this.userRoleMappingRepository.delete({ userId: userId }); + + // Delete from FieldValues table where ItemId matches userId + const fieldValuesResult = await this.fieldsValueRepository.delete({ itemId: userId }); + + const keycloakResponse = await getKeycloakAdminToken(); + const token = keycloakResponse.data.access_token; + + await this.axios.delete(`${KEYCLOAK}${KEYCLOAK_ADMIN}/${userId}`, { + headers: { + 'Authorization': `Bearer ${token}` + } + }); + + return await APIResponse.success(response, apiId, userResult, + HttpStatus.OK, "User and related entries deleted Successfully.") + } catch (e) { + return APIResponse.error(response, apiId, "Internal Server Error", `Error : ${e?.response?.data.error}`, HttpStatus.INTERNAL_SERVER_ERROR); + } + } +} diff --git a/src/adapters/postgres/userTenantMapping-adapter.ts b/src/adapters/postgres/userTenantMapping-adapter.ts new file mode 100644 index 00000000..00261ec5 --- /dev/null +++ b/src/adapters/postgres/userTenantMapping-adapter.ts @@ -0,0 +1,102 @@ +import { HttpStatus, Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { In, Repository } from 'typeorm'; +import { UserTenantMapping } from 'src/userTenantMapping/entities/user-tenant-mapping.entity'; +import { UserTenantMappingDto,ResponseAssignTenantDto } from "src/userTenantMapping/dto/user-tenant-mapping.dto"; +import { ErrorResponseTypeOrm } from 'src/error-response-typeorm'; +import { SuccessResponse } from 'src/success-response'; +import { User } from "src/user/entities/user-entity"; +import { Tenants } from "src/userTenantMapping/entities/tenant.entity"; +import { IServicelocatorAssignTenant } from '../usertenantmappinglocator'; +import APIResponse from 'src/common/responses/response'; +import { Response } from 'express'; +import { APIID } from 'src/common/utils/api-id.config'; + +@Injectable() +export class PostgresAssignTenantService implements IServicelocatorAssignTenant { + constructor( + @InjectRepository(UserTenantMapping) + private userTenantMappingRepository: Repository, + @InjectRepository(User) + private userRepository: Repository, + @InjectRepository(Tenants) + private tenantsRepository: Repository, + ) { } + + public async validateUserTenantMapping(userId: string, tenantId: string, errors: any[]) { + // check if user tenant mapping exists. + const existingMapping = await this.userTenantMappingRepository.findOne({ + where: { userId, tenantId }, + }); + if (existingMapping) { + errors.push({ + errorMessage: `User already exists in Tenant ${tenantId}.`, + }); + return false; + } + + // check if user exists + const userExist = await this.userRepository.findOne({ where: { userId } }); + if (!userExist) { + errors.push({ errorMessage: `User ${userId} does not exist.` }); + return false; + } + + // check if tenant exists + const tenantExist = await this.tenantsRepository.findOne({ where: { tenantId } }); + if (!tenantExist) { + errors.push({ errorMessage: `Tenant ${tenantId} does not exist.` }); + return false; + } + + return true; + } + + public async userTenantMapping(request: any, assignTenantMappingDto: UserTenantMappingDto, response: Response) { + const apiId = APIID.ASSIGN_TENANT_CREATE; + try { + const userId = assignTenantMappingDto.userId; + const tenantIds = assignTenantMappingDto.tenantId; + + // Check if tenant array is not empty + if (!tenantIds || tenantIds.length === 0) { + return APIResponse.error(response, apiId, "Bad Request", "Please provide at least one tenant Id", HttpStatus.BAD_REQUEST); + } + + let result = []; + let errors = []; + + for (const tenantId of tenantIds) { + const isValid = await this.validateUserTenantMapping(userId, tenantId, errors); + + if (!isValid) { + continue; + } + + const data = await this.userTenantMappingRepository.save({ + userId: userId, + tenantId: tenantId, + createdBy: request['user'].userId, + updatedBy: request['user'].userId + }) + + result.push(new ResponseAssignTenantDto(data, `User is successfully added to the Tenants.`)); + } + + if (result.length == 0) { + return APIResponse.error(response, apiId, "Bad Request", `User not added to tenants ${JSON.stringify(errors)}`, HttpStatus.BAD_REQUEST); + } + const res = { + successCount: result.length, + errorCount: errors.length, + data: result, + errors, + }; + + return await APIResponse.success(response, apiId, res ,HttpStatus.OK, "User added to tenants successfully.") + } catch (error) { + const errorMessage = error?.message || 'Something went wrong'; + return APIResponse.error(response, apiId, "Internal Server Error", `Error : ${errorMessage}`, HttpStatus.INTERNAL_SERVER_ERROR) + } + } +} diff --git a/src/adapters/privilegeservicelocator.ts b/src/adapters/privilegeservicelocator.ts new file mode 100644 index 00000000..09cdec306 --- /dev/null +++ b/src/adapters/privilegeservicelocator.ts @@ -0,0 +1,19 @@ +import { + CreatePrivilegesDto, + PrivilegeDto, +} from "src/rbac/privilege/dto/privilege.dto"; +import { Response } from "express"; + +export interface IServicelocator { + createPrivilege(loggedinUser: any, createPrivileges: CreatePrivilegesDto, response?: Response); + getPrivilege( + privilegeId?: string, + request?: any, + response?: Response + ); + // updatePrivilege(privilegeId, request, privilegeDto) + getAllPrivilege(request: any, response?: Response) + getPrivilegebyRoleId(tenantId: string, roleId: string, request: any, response?: Response) + // updatePrivilege(privilegeId, request, privilegeDto) + deletePrivilege(privilegeId, response?: Response); +} diff --git a/src/adapters/rbacservicelocator.ts b/src/adapters/rbacservicelocator.ts new file mode 100644 index 00000000..4860642d --- /dev/null +++ b/src/adapters/rbacservicelocator.ts @@ -0,0 +1,15 @@ +import { RoleSearchDto } from "../rbac/role/dto/role-search.dto"; +import { CreateRolesDto, RoleDto } from "../rbac/role/dto/role.dto"; +import { Response } from "express"; + +export interface IServicelocatorRbac { + getRole( + roleId?: string, + request?: any, + response?: Response + ); + updateRole(id?: string, request?: any, userDto?: any, response?: Response); + createRole(request: any, createRolesDto: CreateRolesDto, response?: Response); + searchRole(roleSearchDto: RoleSearchDto, response: Response); + deleteRole(roleId?: string, response?: Response); +} diff --git a/src/adapters/schoolservicelocator.ts b/src/adapters/schoolservicelocator.ts deleted file mode 100644 index fae79c28..00000000 --- a/src/adapters/schoolservicelocator.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { SchoolSearchDto } from "src/school/dto/school-search.dto"; -import { SchoolDto } from "src/school/dto/school.dto"; - -export interface IServicelocator { - searchSchool(request: any, schoolSearchDto: SchoolSearchDto); - createSchool(request: any, schoolDto: SchoolDto); - updateSchool(id: string, request: any, schoolDto: SchoolDto); - getSchool(schoolId: any, request: any); -} diff --git a/src/adapters/studentservicelocator.ts b/src/adapters/studentservicelocator.ts deleted file mode 100644 index 0912b59f..00000000 --- a/src/adapters/studentservicelocator.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { StudentSearchDto } from "src/student/dto/student-search.dto"; -import { StudentDto } from "src/student/dto/student.dto"; - -export interface IServicelocator { - getStudent(studentId: any, request: any); - createStudent(request: any, studentDto: StudentDto); - updateStudent(id: string, request: any, studentDto: StudentDto); - searchStudent(request: any, studentSearchDto: StudentSearchDto); -} diff --git a/src/adapters/sunbirdrc/adminForm.adapter.ts b/src/adapters/sunbirdrc/adminForm.adapter.ts deleted file mode 100644 index 79fd7b30..00000000 --- a/src/adapters/sunbirdrc/adminForm.adapter.ts +++ /dev/null @@ -1,174 +0,0 @@ -import { HttpService } from "@nestjs/axios"; -import { Injectable, HttpException } from "@nestjs/common"; -const resolvePath = require("object-resolve-path"); -import { AxiosResponse } from "axios"; -import { AdminFormDto } from "src/adminForm/dto/adminForm.dto"; -import { first, map, Observable } from "rxjs"; -import { SuccessResponse } from "src/success-response"; -import { catchError } from "rxjs/operators"; -import { ErrorResponse } from "src/error-response"; -import { AdminFormSearchDto } from "src/adminForm/dto/adminForm-search.dto"; -@Injectable() -export class AdminFormService { - constructor(private httpService: HttpService) {} - url = `${process.env.BASEAPIURL}/AdminForm`; - - public async getAdminForm(adminFormId: string, request: any) { - return this.httpService - .get(`${this.url}/${adminFormId}`, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (axiosResponse: AxiosResponse) => { - let data = [axiosResponse.data]; - - const adminFormDto = await this.mappedResponse(data); - return new SuccessResponse({ - statusCode: 200, - message: "ok.", - data: adminFormDto[0], - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response?.status, - errorMessage: e.response?.data?.params?.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async createAdminForm(request: any, adminFormDto: AdminFormDto) { - return this.httpService - .post(`${this.url}`, adminFormDto, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map((axiosResponse: AxiosResponse) => { - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: axiosResponse.data, - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response?.status, - errorMessage: e.response?.data?.params?.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async updateAdminForm( - adminFormId: string, - request: any, - adminFormDto: AdminFormDto - ) { - var axios = require("axios"); - var data = adminFormDto; - - var config = { - method: "put", - url: `${this.url}/${adminFormId}`, - headers: { - Authorization: request.headers.authorization, - }, - data: data, - }; - const response = await axios(config); - return new SuccessResponse({ - statusCode: 200, - message: " Ok.", - data: response.data, - }); - } - - public async searchAdminForm( - request: any, - adminFormSearchDto: AdminFormSearchDto - ) { - return this.httpService - .post(`${this.url}/search`, adminFormSearchDto, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (response) => { - const responsedata = await this.mappedResponse(response.data); - return new SuccessResponse({ - statusCode: response.status, - message: "Ok.", - data: responsedata, - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response.status, - errorMessage: e.response.data.params.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async adminFormFilter(fromDate: string, toDate: string, request: any) { - let axios = require("axios"); - let filters = { - fromDate, - toDate, - }; - const filterArray = Object.keys(filters).filter( - (value, key) => filters[value] && filters[value] !== "" - ); - let data = { date: { between: [] } }; - filterArray.forEach((value, key) => { - if (["fromDate", "toDate"].includes(value)) { - data["date"].between.push(filters[value]); - } - }); - - let config = { - method: "post", - url: `${this.url}/search`, - headers: { - Authorization: request.headers.authorization, - }, - data: { filters: data }, - }; - - const response = await axios(config); - - let result = response?.data && response.data; - const responsedata = await this.mappedResponse(result); - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: responsedata, - }); - } - - public async mappedResponse(result: any) { - const adminFormResponse = result.map((obj: any) => { - const adminFormMapping = { - adminFormId: obj?.osid ? `${obj.osid}` : "", - moduleId: obj?.moduleId ? `${obj.moduleId}` : "", - formSchema: obj?.formSchema ? `${obj.formSchema}` : "", - createdAt: obj?.osCreatedAt ? `${obj.osCreatedAt}` : "", - updatedAt: obj?.osUpdatedAt ? `${obj.osUpdatedAt}` : "", - createdBy: obj?.osCreatedBy ? `${obj.osCreatedBy}` : "", - updatedBy: obj?.osUpdatedBy ? `${obj.osUpdatedBy}` : "", - }; - return new AdminFormDto(adminFormMapping); - }); - - return adminFormResponse; - } -} diff --git a/src/adapters/sunbirdrc/attendance.adapter.ts b/src/adapters/sunbirdrc/attendance.adapter.ts deleted file mode 100644 index 9bfe90de..00000000 --- a/src/adapters/sunbirdrc/attendance.adapter.ts +++ /dev/null @@ -1,766 +0,0 @@ -import { Injectable, HttpException } from "@nestjs/common"; -import { HttpService } from "@nestjs/axios"; -import { AxiosResponse } from "axios"; -import { map } from "rxjs"; -import { AttendanceDto } from "src/attendance/dto/attendance.dto"; -import { SuccessResponse } from "src/success-response"; -import { ErrorResponse } from "src/error-response"; -import { catchError } from "rxjs/operators"; -import { AttendanceSearchDto } from "src/attendance/dto/attendance-search.dto"; -import { SegmentDto } from "src/common-dto/userSegment.dto"; -import moment from "moment"; - -import { IServicelocator } from "../attendanceservicelocator"; -import { StudentDto } from "src/student/dto/student.dto"; -import { AttendanceDateDto } from "src/attendance/dto/attendance-date.dto"; -export const SunbirdAttendanceToken = "SunbirdAttendance"; - -@Injectable() -export class AttendanceService implements IServicelocator { - constructor(private httpService: HttpService) {} - checkAndAddAttendance(request: Request, attendanceDto: AttendanceDto): unknown { - throw new Error("Method not implemented."); - } - attendanceByDate(tenantId: string, request: any, attendanceSearchDto: AttendanceDateDto) { - throw new Error("Method not implemented."); - } - url = `${process.env.BASEAPIURL}/Attendance`; - studentAPIUrl = `${process.env.BASEAPIURL}/Student`; - baseUrl = process.env.BASEAPIURL; - public async getAttendance( - tenantId: string, - attendanceId: any, - request: any - ) { - return this.httpService - .get(`${this.url}/${attendanceId}`, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (axiosResponse: AxiosResponse) => { - const data = axiosResponse.data; - const result = [data]; - const mappedResponse = await this.mappedResponse(result); - return new SuccessResponse({ - statusCode: 200, - message: "ok.", - data: mappedResponse[0], - }); - }) - ); - } - - public async updateAttendance( - attendanceId: string, - request: any, - attendanceDto: AttendanceDto - ) { - var axios = require("axios"); - var data = attendanceDto; - - var config = { - method: "put", - url: `${this.url}/${attendanceId}`, - headers: { - Authorization: request.headers.authorization, - }, - data: data, - }; - const response = await axios(config); - return new SuccessResponse({ - statusCode: 200, - message: " Ok.", - data: response.data, - }); - } - - public async searchAttendance( - tenantId: string, - request: any, - attendanceSearchDto: AttendanceSearchDto - ) { - return this.httpService - .post(`${this.url}/search`, attendanceSearchDto, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (response) => { - const responsedata = response.data; - const mappedResponse = await this.mappedResponse(responsedata); - return new SuccessResponse({ - statusCode: response.status, - message: "Ok.", - data: mappedResponse, - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response.status, - errorMessage: e.response.data.params.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async userSegment( - groupId: string, - attendance: string, - date: string, - request: any - ) { - let axios = require("axios"); - - let data: any = { - filters: { - attendance: { - eq: `${attendance}`, - }, - groupId: { - eq: `${groupId}`, - }, - }, - }; - switch (date) { - case "today": - data.filters = { - ...data.filters, - attendanceDate: { - eq: `${moment().format("Y-MM-DD")}`, - }, - }; - break; - - case "yesterday": - data.filters = { - ...data.filters, - attendanceDate: { - eq: `${moment().add(-1, "days").format("Y-MM-DD")}`, - }, - }; - break; - - case "lastthreedays": - data.filters = { - ...data.filters, - attendanceDate: { - eq: `${moment().add(-3, "days").format("Y-MM-DD")}`, - }, - }; - break; - - case "thisweek": - data.filters = { - ...data.filters, - attendanceDate: { - between: [ - moment().startOf("week").format("Y-MM-DD"), - moment().endOf("week").format("Y-MM-DD"), - ], - }, - }; - break; - - case "lastweek": - data.filters = { - ...data.filters, - attendanceDate: { - between: [ - moment() - .subtract(1, "weeks") - .startOf("week") - .format("YYYY-MM-DD"), - moment().subtract(1, "weeks").endOf("week").format("YYYY-MM-DD"), - ], - }, - }; - - break; - - case "thismonth": - data.filters = { - ...data.filters, - attendanceDate: { - between: [ - moment().startOf("month").format("Y-MM-DD"), - moment().endOf("month").format("Y-MM-DD"), - ], - }, - }; - break; - - case "lastmonth": - data.filters = { - ...data.filters, - attendanceDate: { - between: [ - moment() - .subtract(1, "months") - .startOf("month") - .format("YYYY-MM-DD"), - moment() - .subtract(1, "months") - .endOf("month") - .format("YYYY-MM-DD"), - ], - }, - }; - - break; - } - - let config = { - method: "post", - url: `${this.url}/search`, - - data: data, - }; - - let startDates: any; - let endDates: any; - - if (data.filters.attendanceDate.between === undefined) { - startDates = ""; - endDates = ""; - } else { - startDates = data.filters.attendanceDate?.between[0] - ? data.filters.attendanceDate.between[0] - : ""; - endDates = data.filters.attendanceDate?.between[1] - ? data.filters.attendanceDate.between[1] - : ""; - } - - const response = await axios(config); - let resData = response?.data; - - let dateData = resData.map((e: any) => { - return e.attendanceDate; - }); - - const groupData = await axios.get(`${this.baseUrl}/Class/${groupId}`); - - const teacherData = await axios.get( - `${this.baseUrl}/User/${groupData.data.teacherId}` - ); - - const schoolData = await axios.get( - `${this.baseUrl}/School/${groupData.data.schoolId}` - ); - - let arrayIds = resData.map((e: any) => { - return e.userId; - }); - - let studentArray = []; - for (let value of arrayIds) { - let config = { - method: "get", - url: `${this.studentAPIUrl}/${value}`, - }; - const response = await axios(config); - const data = response?.data; - - const date = new Date(dateData[0]); - const month = date.toLocaleString("default", { month: "long" }); - - const studentDto = { - id: data.osid, - name: data?.firstName + " " + data?.lastName, - phoneNo: data.guardianPhoneNumber, - parentName: data?.guardianFirstName + " " + data?.guardianLastName, - attendanceDate: dateData[0], - month: month, - teacherName: - teacherData.data.firstName + " " + teacherData.data.lastName, - schoolName: schoolData.data.schoolName, - startDate: startDates, - endDate: endDates, - }; - let studentDtoData = new SegmentDto(studentDto); - studentArray.push(studentDtoData); - } - - return new SuccessResponse({ - data: studentArray, - }); - } - - public async attendanceFilter( - fromDate: string, - toDate: string, - userId: string, - userType: string, - attendance: string, - groupId: string, - schoolId: string, - eventId: string, - topicId: string, - request: any - ) { - let axios = require("axios"); - let filters = { - fromDate, - toDate, - userId, - userType, - attendance, - groupId, - schoolId, - eventId, - topicId, - }; - const filterArray = Object.keys(filters).filter( - (value, key) => filters[value] && filters[value] !== "" - ); - let data = { attendanceDate: { between: [] } }; - filterArray.forEach((value, key) => { - if (["fromDate", "toDate"].includes(value)) { - data["attendanceDate"].between.push(filters[value]); - } else { - data[value] = { eq: filters[value] }; - } - }); - - let config = { - method: "post", - url: `${this.url}/search`, - headers: { - Authorization: request.headers.authorization, - }, - data: { filters: data }, - }; - - const response = await axios(config); - let result = response?.data && response.data; - const mappedResponse = await this.mappedResponse(result); - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: mappedResponse, - }); - } - - public async createAttendance(request: any, attendanceDto: AttendanceDto) { - let axios = require("axios"); - let data = { - filters: { - userId: { - eq: `${attendanceDto.userId}`, - }, - attendanceDate: { - eq: `${attendanceDto.attendanceDate}`, - }, - }, - }; - - let attendanceCreate = { - method: "post", - url: `${this.url}/search`, - - data: data, - }; - - const response = await axios(attendanceCreate); - let resData = response?.data; - let result = await this.mappedResponse(resData); - - let attendanceId = result.map(function (AttendanceDto) { - return AttendanceDto.attendanceId; - }); - - if (resData.length > 0) { - var updateData = attendanceDto; - var updateAttendance = { - method: "put", - url: `${this.url}/${attendanceId}`, - headers: { - Authorization: request.headers.authorization, - }, - data: updateData, - }; - - const response = await axios(updateAttendance); - return new SuccessResponse({ - statusCode: 200, - message: " Ok.", - data: response.data, - }); - } else { - var createAttendance = attendanceDto; - var create = { - method: "post", - url: `${this.url}`, - headers: { - Authorization: request.headers.authorization, - }, - data: createAttendance, - }; - - const response = await axios(create); - - return new SuccessResponse({ - statusCode: 200, - message: " Ok.", - data: response.data, - }); - } - } - public async multipleAttendance( - tenantId: string, - request: any, - attendanceData: [Object] - ) { - let attendeeData = attendanceData["attendanceData"]; - const result = Promise.all( - attendeeData.map(async (data: any) => { - data["schoolId"] = attendanceData["schoolId"] - ? attendanceData["schoolId"] - : ""; - data["userType"] = attendanceData["userType"] - ? attendanceData["userType"] - : ""; - data["groupId"] = attendanceData["groupId"] - ? attendanceData["groupId"] - : ""; - data["topicId"] = attendanceData["topicId"] - ? attendanceData["topicId"] - : ""; - data["eventId"] = attendanceData["eventId"] - ? attendanceData["eventId"] - : ""; - data["attendanceDate"] = attendanceData["attendanceDate"] - ? attendanceData["attendanceDate"] - : ""; - data["latitude"] = attendanceData["latitude"] - ? attendanceData["latitude"] - : 0; - data["longitude"] = attendanceData["longitude"] - ? attendanceData["longitude"] - : 0; - data["image"] = attendanceData["image"] ? attendanceData["image"] : ""; - data["metaData"] = attendanceData["metaData"] - ? attendanceData["metaData"] - : []; - data["syncTime"] = attendanceData["syncTime"] - ? attendanceData["syncTime"] - : ""; - - let attendanceDto = data; - let axios = require("axios"); - let dataSearch = { - filters: { - userId: { - eq: `${attendanceDto.userId}`, - }, - attendanceDate: { - eq: `${attendanceDto.attendanceDate}`, - }, - }, - }; - - let attendanceCreate = { - method: "post", - url: `${this.url}/search`, - - data: dataSearch, - }; - - const response = await axios(attendanceCreate); - let resData = response?.data; - let result = await this.mappedResponse(resData); - - let attendanceId = result.map(function (AttendanceDto) { - return AttendanceDto.attendanceId; - }); - - if (resData.length > 0) { - var updateData = attendanceDto; - var updateAttendance = { - method: "put", - url: `${this.url}/${attendanceId}`, - headers: { - Authorization: request.headers.authorization, - }, - data: updateData, - }; - - const response = await axios(updateAttendance); - return await response.data; - } else { - var createAttendance = attendanceDto; - var create = { - method: "post", - url: `${this.url}`, - headers: { - Authorization: request.headers.authorization, - }, - data: createAttendance, - }; - - const response = await axios(create); - return await response.data; - } - }) - ); - const responseArray = await result; - return new SuccessResponse({ - statusCode: 200, - message: " Ok.", - data: responseArray, - }); - } - - public async studentAttendanceByGroup( - date: string, - groupId: string, - request: any - ) { - let axios = require("axios"); - try { - let studentArray = []; - let data = { - filters: { - groupId: { - eq: `${groupId}`, - }, - attendanceDate: { - eq: `${date}`, - }, - }, - }; - let config = { - method: "post", - url: `${this.url}/search`, - headers: { - Authorization: request.headers.authorization, - }, - data: data, - }; - - const response = await axios(config); - - if (response.data.length > 0) { - const studentIds = response.data.map((e: any) => { - return e.userId; - }); - - for (let studentId of studentIds) { - const studentData = await axios.get( - `${this.studentAPIUrl}/${studentId}`, - { - headers: { - Authorization: request.headers.authorization, - }, - } - ); - - let responseData = await this.StudentMappedResponse([ - studentData.data, - ]); - let result = responseData[0]; - const updatedStudent = { - ...result, - attendance: response.data[0].attendance, - attendanceDate: response.data[0].attendanceDate, - }; - studentArray.push(updatedStudent); - } - - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: studentArray, - }); - } else { - return new SuccessResponse({ - statusCode: 200, - message: "Attendance not marked for this class yet", - data: [], - }); - } - } catch (e) { - return new ErrorResponse({ - errorCode: e.response.status, - errorMessage: e.response.data.params.errmsg, - }); - } - } - - public async studentAttendanceByUserId( - date: string, - userId: string, - request: any - ) { - let axios = require("axios"); - try { - let data = { - filters: { - userId: { - eq: `${userId}`, - }, - attendanceDate: { - eq: `${date}`, - }, - }, - }; - let config = { - method: "post", - url: `${this.url}/search`, - headers: { - Authorization: request.headers.authorization, - }, - data: data, - }; - - const response = await axios(config); - const studentId = response.data[0].userId; - const studentData = await axios.get( - `${this.studentAPIUrl}/${studentId}`, - { - headers: { - Authorization: request.headers.authorization, - }, - } - ); - - let responseData = await this.StudentMappedResponse([studentData.data]); - let result = responseData[0]; - const updatedStudent = { - ...result, - attendance: response.data[0].attendance, - attendanceDate: response.data[0].attendanceDate, - }; - - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: updatedStudent, - }); - } catch (e) { - return `${e}`; - } - } - - public async mappedResponse(result: any) { - const attendanceResponse = result.map((item: any) => { - const attendanceMapping = { - attendanceId: item?.osid ? `${item.osid}` : "", - schoolId: item?.schoolId ? `${item.schoolId}` : "", - userType: item?.userType ? `${item.userType}` : "", - userId: item?.userId ? `${item.userId}` : "", - groupId: item?.groupId ? `${item.groupId}` : "", - topicId: item?.topicId ? `${item.topicId}` : "", - eventId: item?.eventId ? `${item.eventId}` : "", - remark: item?.remark ? `${item.remark}` : "", - attendance: item?.attendance ? `${item.attendance}` : "", - attendanceDate: item?.attendanceDate ? `${item.attendanceDate}` : "", - latitude: item?.latitude ? item.latitude : 0, - longitude: item?.longitude ? item.longitude : 0, - image: item?.image ? `${item.image}` : "", - syncTime: item?.syncTime ? `${item.syncTime}` : "", - metaData: item?.metaData ? item.metaData : [], - createdAt: item?.osCreatedAt ? `${item.osCreatedAt}` : "", - updatedAt: item?.osUpdatedAt ? `${item.osUpdatedAt}` : "", - createdBy: item?.osCreatedBy ? `${item.osCreatedBy}` : "", - updatedBy: item?.osUpdatedBy ? `${item.osUpdatedBy}` : "", - }; - - return new AttendanceDto(attendanceMapping); - }); - - return attendanceResponse; - } - - public async StudentMappedResponse(result: any) { - const studentResponse = result.map((item: any) => { - const studentMapping = { - studentId: item?.osid ? `${item.osid}` : "", - refId1: item?.admissionNo ? `${item.admissionNo}` : "", - refId2: item?.refId2 ? `${item.refId2}` : "", - aadhaar: item?.aadhaar ? `${item.aadhaar}` : "", - firstName: item?.firstName ? `${item.firstName}` : "", - middleName: item?.middleName ? `${item.middleName}` : "", - lastName: item?.lastName ? `${item.lastName}` : "", - groupId: item?.groupId ? `${item.groupId}` : "", - schoolId: item?.schoolId ? `${item.schoolId}` : "", - studentEmail: item?.studentEmail ? `${item.studentEmail}` : "", - studentPhoneNumber: item?.studentPhoneNumber - ? item.studentPhoneNumber - : "", - iscwsn: item?.iscwsn ? `${item.iscwsn}` : "", - gender: item?.gender ? `${item.gender}` : "", - socialCategory: item?.socialCategory ? `${item.socialCategory}` : "", - religion: item?.religion ? `${item.religion}` : "", - singleGirl: item?.singleGirl ? item.singleGirl : "", - weight: item?.weight ? `${item.weight}` : "", - height: item?.height ? `${item.height}` : "", - bloodGroup: item?.bloodGroup ? `${item.bloodGroup}` : "", - birthDate: item?.birthDate ? `${item.birthDate}` : "", - homeless: item?.homeless ? item.homeless : "", - bpl: item?.bpl ? item.bpl : "", - migrant: item?.migrant ? item.migrant : "", - status: item?.status ? `${item.status}` : "", - - fatherFirstName: item?.fatherFirstName ? `${item.fatherFirstName}` : "", - - fatherMiddleName: item?.fatherMiddleName - ? `${item.fatherMiddleName}` - : "", - - fatherLastName: item?.fatherLastName ? `${item.fatherLastName}` : "", - fatherPhoneNumber: item?.fatherPhoneNumber - ? item.fatherPhoneNumber - : "", - fatherEmail: item?.fatherEmail ? `${item.fatherEmail}` : "", - - motherFirstName: item?.motherFirstName ? `${item.motherFirstName}` : "", - motherMiddleName: item?.motherMiddleName - ? `${item.motherMiddleName}` - : "", - motherLastName: item?.motherLastName ? `${item.motherLastName}` : "", - motherPhoneNumber: item?.motherPhoneNumber - ? item.motherPhoneNumber - : "", - motherEmail: item?.motherEmail ? `${item.motherEmail}` : "", - - guardianFirstName: item?.guardianFirstName - ? `${item.guardianFirstName}` - : "", - guardianMiddleName: item?.guardianMiddleName - ? `${item.guardianMiddleName}` - : "", - guardianLastName: item?.guardianLastName - ? `${item.guardianLastName}` - : "", - guardianPhoneNumber: item?.guardianPhoneNumber - ? item.guardianPhoneNumber - : "", - guardianEmail: item?.guardianEmail ? `${item.guardianEmail}` : "", - image: item?.image ? `${item.image}` : "", - deactivationReason: item?.deactivationReason - ? `${item.deactivationReason}` - : "", - studentAddress: item?.studentAddress ? `${item.studentAddress}` : "", - village: item?.village ? `${item.village}` : "", - block: item?.block ? `${item.block}` : "", - district: item?.district ? `${item.district}` : "", - stateId: item?.stateId ? `${item.stateId}` : "", - pincode: item?.pincode ? item.pincode : "", - locationId: item?.locationId ? `${item.locationId}` : "", - metaData: item?.metaData ? item.metaData : [], - createdAt: item?.osCreatedAt ? `${item.osCreatedAt}` : "", - updatedAt: item?.osUpdatedAt ? `${item.osUpdatedAt}` : "", - createdBy: item?.osCreatedBy ? `${item.osCreatedBy}` : "", - updatedBy: item?.osUpdatedBy ? `${item.osUpdatedBy}` : "", - }; - return new StudentDto(studentMapping); - }); - - return studentResponse; - } -} diff --git a/src/adapters/sunbirdrc/comment.adapter.ts b/src/adapters/sunbirdrc/comment.adapter.ts deleted file mode 100644 index cec0e14a..00000000 --- a/src/adapters/sunbirdrc/comment.adapter.ts +++ /dev/null @@ -1,186 +0,0 @@ -import { HttpService } from "@nestjs/axios"; -import { Injectable, HttpException } from "@nestjs/common"; -import { AxiosResponse } from "axios"; -import { map } from "rxjs"; -import { SuccessResponse } from "src/success-response"; -import { catchError } from "rxjs/operators"; -import { ErrorResponse } from "src/error-response"; -import { CommentDto } from "src/comment/dto/comment.dto"; -import { CommentSearchDto } from "src/comment/dto/comment-search.dto"; -import jwt_decode from "jwt-decode"; -import { IServicelocator } from "../commentservicelocator"; -export const SunbirdCommentToken = "SunbirdComment"; -@Injectable() -export class SunbirdCommentService implements IServicelocator { - constructor(private httpService: HttpService) {} - url = `${process.env.BASEAPIURL}/Comment`; - userUrl = `${process.env.BASEAPIURL}/User`; - - public async getComment(commentId: string, request: any) { - return this.httpService - .get(`${this.url}/${commentId}`, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (axiosResponse: AxiosResponse) => { - let data = [axiosResponse.data]; - - const commentDto = await this.mappedResponse(data); - return new SuccessResponse({ - statusCode: 200, - message: "ok.", - data: commentDto[0], - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response?.status, - errorMessage: e.response?.data?.params?.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - public async createComment(request: any, commentDto: CommentDto) { - const authToken = request.headers.authorization; - const decoded: any = jwt_decode(authToken); - let email = decoded.email; - let axios = require("axios"); - let data = { - filters: { - email: { - eq: `${email}`, - }, - }, - }; - let config = { - method: "post", - url: `${this.userUrl}/search`, - headers: { - Authorization: request.headers.authorization, - }, - data: data, - }; - const response = await axios(config); - const result = response.data[0]; - commentDto.userId = result.osid; - - return this.httpService - .post(`${this.url}`, commentDto, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map((axiosResponse: AxiosResponse) => { - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: axiosResponse.data, - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response?.status, - errorMessage: e.response?.data?.params?.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async updateComment( - commentId: string, - request: any, - commentDto: CommentDto - ) { - var axios = require("axios"); - var data = commentDto; - const authToken = request.headers.authorization; - const decoded: any = jwt_decode(authToken); - let email = decoded.email; - let updateData = { - filters: { - email: { - eq: `${email}`, - }, - }, - }; - let configData = { - method: "post", - url: `${this.userUrl}/search`, - headers: { - Authorization: request.headers.authorization, - }, - data: updateData, - }; - const userResponse = await axios(configData); - const result = userResponse.data[0]; - commentDto.userId = result.osid; - - var config = { - method: "put", - url: `${this.url}/${commentId}`, - headers: { - Authorization: request.headers.authorization, - }, - data: data, - }; - const response = await axios(config); - return new SuccessResponse({ - statusCode: 200, - message: " Ok.", - data: response.data, - }); - } - - public async searchComment(request: any, commentSearchDto: CommentSearchDto) { - return this.httpService - .post(`${this.url}/search`, commentSearchDto, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (response) => { - const responsedata = response.data; - const commentDto = await this.mappedResponse(responsedata); - return new SuccessResponse({ - statusCode: response.status, - message: "Ok.", - data: commentDto, - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response.status, - errorMessage: e.response.data.params.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - public async mappedResponse(result: any) { - const commentResponse = result.map((obj: any) => { - const commentMapping = { - commentId: obj?.osid ? `${obj.osid}` : "", - contextId: obj?.contextId ? `${obj.contextId}` : "", - context: obj?.context ? `${obj.context}` : "", - userId: obj?.userId ? `${obj.userId}` : "", - comment: obj?.comment ? `${obj.comment}` : "", - privacy: obj?.privacy ? `${obj.privacy}` : "", - parentId: obj?.parentId ? `${obj.parentId}` : "", - status: obj?.status ? `${obj.status}` : "", - createdAt: obj?.osCreatedAt ? `${obj.osCreatedAt}` : "", - updatedAt: obj?.osUpdatedAt ? `${obj.osUpdatedAt}` : "", - createdBy: obj?.osCreatedBy ? `${obj.osCreatedBy}` : "", - updatedBy: obj?.osUpdatedBy ? `${obj.osUpdatedBy}` : "", - }; - return new CommentDto(commentMapping); - }); - - return commentResponse; - } -} diff --git a/src/adapters/sunbirdrc/config.adapter.ts b/src/adapters/sunbirdrc/config.adapter.ts deleted file mode 100644 index 5d1f7d7b..00000000 --- a/src/adapters/sunbirdrc/config.adapter.ts +++ /dev/null @@ -1,267 +0,0 @@ -import { HttpService } from "@nestjs/axios"; -import { Injectable, HttpException } from "@nestjs/common"; -const resolvePath = require("object-resolve-path"); -import { AxiosResponse } from "axios"; -import { ConfigDto } from "src/configs/dto/config.dto"; -import { first, map, Observable } from "rxjs"; -import { SuccessResponse } from "src/success-response"; -import jwt_decode from "jwt-decode"; -import { UserDto } from "../../user/dto/user.dto"; -import { IServicelocator } from "../configservicelocator"; -export const SunbirdConfigToken = "SunbirdConfig"; -@Injectable() -export class SunbirdConfigService implements IServicelocator { - constructor(private httpService: HttpService) {} - url = `${process.env.BASEAPIURL}config`; - - public async createConfig(request: any, configDto: ConfigDto) { - let axios = require("axios"); - let data = { - filters: { - module: { - eq: `${configDto.module}`, - }, - key: { - eq: `${configDto.key}`, - }, - }, - }; - - let config = { - method: "post", - url: `${this.url}/search`, - - data: data, - }; - - const response = await axios(config); - let resData = response?.data; - let result = await this.mappedResponse(resData); - let configId = result.map(function (ConfigDto) { - return ConfigDto.configId; - }); - - if (resData.length > 0) { - var udateData = configDto; - var updateConfig = { - method: "put", - url: `${this.url}/${configId}`, - headers: { - Authorization: request.headers.authorization, - }, - data: udateData, - }; - const response = await axios(updateConfig); - return new SuccessResponse({ - statusCode: 200, - message: " Ok.", - data: response.data, - }); - } else { - var createData = configDto; - var createConfig = { - method: "post", - url: `${this.url}`, - headers: { - Authorization: request.headers.authorization, - }, - data: createData, - }; - - const response = await axios(createConfig); - - return new SuccessResponse({ - statusCode: 200, - message: " Ok.", - data: response.data, - }); - } - } - - public async getConfig(request: any) { - let axios = require("axios"); - let data = { - filters: {}, - }; - let globalConfig = { - method: "post", - url: `${this.url}/search`, - headers: { - Authorization: request.headers.authorization, - }, - data: data, - }; - - const globalConfigData = await axios(globalConfig); - let gobalConfigResult = await this.mappedResponse( - globalConfigData?.data && globalConfigData.data - ); - // get Logged In user data - const authToken = request.headers.authorization; - const decoded: any = jwt_decode(authToken); - let email = decoded.email; - let teacherData = { - filters: { - email: { - eq: `${email}`, - }, - }, - }; - let config = { - method: "post", - url: `${process.env.BASEAPIURL}/User/search`, - headers: { - Authorization: request.headers.authorization, - }, - data: teacherData, - }; - const response = await axios(config); - - let teacherProfileData = await this.userMappedResponse( - response?.data && response.data - ); - - let schoolId = teacherProfileData.map(function (UserDto) { - return UserDto.schoolId; - }); - - let teacherConfig = { - filters: { - contextId: { - eq: `${schoolId}`, - }, - }, - }; - - let final = { - method: "post", - url: `${this.url}/search`, - headers: { - Authorization: request.headers.authorization, - }, - data: teacherConfig, - }; - const confifResponse = await axios(final); - - let overridenResult = await this.mappedResponse( - confifResponse?.data && confifResponse.data - ); - var result = gobalConfigResult.filter((obj) => obj.contextId == ""); - - for (let i = 0; i < result.length; i++) { - let overridenData = overridenResult.filter( - (obj) => obj.key == result[i].key && obj.module == result[i].module - ); - if (overridenData.length > 0) { - result[i] = overridenData[0]; - } - } - - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: result, - }); - } - - public async createModuleConfigs(request: any, configAllData: [Object]) { - configAllData.forEach((element) => { - element["data"].forEach((data) => { - data["module"] = element["module"]; - data["context"] = element["context"] ? element["context"] : ""; - data["contextId"] = element["contextId"] ? element["contextId"] : ""; - data["key"] = data["key"] ? data["key"] : ""; - data["value"] = data["value"] ? data["value"] : []; - data["isPublic"] = data["isPublic"] ? data["isPublic"] : true; - data["canOverride"] = data["canOverride"] ? data["canOverride"] : true; - data["overrideBy"] = data["overrideBy"] ? data["overrideBy"] : ""; - - this.createConfig(request, data); - }); - }); - } - - public async mappedResponse(result: any) { - const configResponse = result.map((item: any) => { - const configMapping = { - configId: item?.osid ? `${item.osid}` : "", - module: item?.module ? `${item.module}` : "", - key: item?.key ? `${item.key}` : "", - value: item?.value, - context: item?.context ? `${item.context}` : "", - contextId: item?.contextId ? `${item.contextId}` : "", - canOverride: item?.canOverride, - overrideBy: item?.overrideBy ? `${item.overrideBy}` : "", - isPublic: item?.isPublic, - createdAt: item?.osCreatedAt ? `${item.osCreatedAt}` : "", - updatedAt: item?.osUpdatedAt ? `${item.osUpdatedAt}` : "", - createdBy: item?.osCreatedBy ? `${item.osCreatedBy}` : "", - updatedBy: item?.osUpdatedBy ? `${item.osUpdatedBy}` : "", - }; - return new ConfigDto(configMapping); - }); - - return configResponse; - } - - public async userMappedResponse(result: any) { - const userResponse = result.map((item: any) => { - const userMapping = { - userId: item?.osid ? `${item.osid}` : "", - refId1: item?.refId1 ? `${item.refId1}` : "", - refId2: item?.refId2 ? `${item.refId2}` : "", - refId3: item?.refId3 ? `${item.refId3}` : "", - firstName: item?.firstName ? `${item.firstName}` : "", - middleName: item?.middleName ? `${item.middleName}` : "", - lastName: item?.lastName ? `${item.lastName}` : "", - phoneNumber: item?.phoneNumber ? `${item.phoneNumber}` : "", - email: item?.email ? `${item.email}` : "", - aadhaar: item?.aadhaar ? `${item.aadhaar}` : "", - gender: item?.gender ? `${item.gender}` : "", - socialCategory: item?.socialCategory ? `${item.socialCategory}` : "", - birthDate: item?.birthDate ? `${item.birthDate}` : "", - designation: item?.designation ? `${item.designation}` : "", - cadre: item?.cadre ? `${item.cadre}` : "", - profQualification: item?.profQualification - ? `${item.profQualification}` - : "", - joiningDate: item?.joiningDate ? `${item.joiningDate}` : "", - subjectIds: item.subjectIds ? item.subjectIds : [], - bloodGroup: item?.bloodGroup ? `${item.bloodGroup}` : "", - maritalStatus: item?.maritalStatus ? `${item.maritalStatus}` : "", - compSkills: item?.compSkills ? `${item.compSkills}` : "", - disability: item?.disability ? `${item.disability}` : "", - religion: item?.religion ? `${item.religion}` : "", - homeDistance: item?.homeDistance ? `${item.homeDistance}` : "", - employmentType: item?.employmentType ? `${item.employmentType}` : "", - schoolId: item?.schoolId ? `${item.schoolId}` : "", - address: item?.address ? `${item.address}` : "", - village: item?.village ? `${item.village}` : "", - block: item?.block ? `${item.block}` : "", - district: item?.district ? `${item.district}` : "", - stateId: item?.stateId ? `${item.stateId}` : "", - pincode: item?.pincode ? item.pincode : "", - locationId: item?.locationId ? `${item.locationId}` : "", - image: item?.image ? `${item.image}` : "", - status: item?.status ? `${item.status}` : "", - deactivationReason: item?.deactivationReason - ? `${item.deactivationReason}` - : "", - reportsTo: item?.reportsTo ? `${item.reportsTo}` : "", - retirementDate: item?.retirementDate ? `${item.retirementDate}` : "", - workingStatus: item?.workingStatus ? `${item.workingStatus}` : "", - fcmToken: item?.fcmToken ? `${item.fcmToken}` : "", - role: item?.role ? `${item.role}` : "", - employeeCode: item?.employeeCode ? `${item.employeeCode}` : "", - metaData: item?.metaData ? item.metaData : [], - createdAt: item?.osCreatedAt ? `${item.osCreatedAt}` : "", - updatedAt: item?.osUpdatedAt ? `${item.osUpdatedAt}` : "", - createdBy: item?.osCreatedBy ? `${item.osCreatedBy}` : "", - updatedBy: item?.osUpdatedBy ? `${item.osUpdatedBy}` : "", - }; - return new UserDto(userMapping); - }); - - return userResponse; - } -} diff --git a/src/adapters/sunbirdrc/group.adapter.ts b/src/adapters/sunbirdrc/group.adapter.ts deleted file mode 100644 index 457b42e3..00000000 --- a/src/adapters/sunbirdrc/group.adapter.ts +++ /dev/null @@ -1,431 +0,0 @@ -import { Injectable, HttpException } from "@nestjs/common"; -import { GroupInterface } from "../../group/interfaces/group.interface"; -import { HttpService } from "@nestjs/axios"; -import { AxiosResponse } from "axios"; -import { first, map, Observable } from "rxjs"; -import { response } from "express"; -import { SuccessResponse } from "src/success-response"; -const resolvePath = require("object-resolve-path"); -import { GroupDto } from "src/group/dto/group.dto"; -import { catchError } from "rxjs/operators"; -import { ErrorResponse } from "src/error-response"; -import { GroupSearchDto } from "src/group/dto/group-search.dto"; -import { IServicelocatorgroup } from "../groupservicelocator"; -import { StudentDto } from "src/student/dto/student.dto"; -import { UserDto } from "src/user/dto/user.dto"; -export const SunbirdGroupToken = "SunbirdGroup"; -@Injectable() -export class SunbirdGroupService implements IServicelocatorgroup { - private group: GroupInterface; - - constructor(private httpService: HttpService) {} - - url = `${process.env.BASEAPIURL}`; - - public async getGroup(groupId: any, request: any) { - return this.httpService - .get(`${this.url}/Class/${groupId}`, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (axiosResponse: AxiosResponse) => { - let data = axiosResponse.data; - const groupDto = [data]; - const groupResponse = await this.mappedResponse(groupDto); - return new SuccessResponse({ - statusCode: 200, - message: "Ok..", - data: groupResponse[0], - }); - }) - ); - } - - public async createGroup(request: any, groupDto: GroupDto) { - return this.httpService - .post(`${this.url}/Class`, groupDto, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map((axiosResponse: AxiosResponse) => { - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: axiosResponse.data, - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response?.status, - errorMessage: e.response?.data?.params?.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - public async updateGroup(groupId: string, request: any, groupDto: GroupDto) { - var axios = require("axios"); - var data = groupDto; - - var config = { - method: "put", - url: `${this.url}/Class/${groupId}`, - headers: { - Authorization: request.headers.authorization, - }, - data: data, - }; - const response = await axios(config); - return new SuccessResponse({ - statusCode: 200, - message: " Ok.", - data: response.data, - }); - } - - public async searchGroup(request: any, groupSearchDto: GroupSearchDto) { - return this.httpService - .post(`${this.url}/Class/search`, groupSearchDto, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (response) => { - const responsedata = response.data; - const groupResponse = await this.mappedResponse(responsedata); - return new SuccessResponse({ - statusCode: response.status, - message: "Ok.", - data: groupResponse, - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response.status, - errorMessage: e.response.data.params.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - public async findMembersOfGroup(id: string, role: string, request: any) { - if (role == "Student") { - let axios = require("axios"); - let data = { - filters: { - currentClassId: { - eq: `${id}`, - }, - }, - }; - - let config = { - method: "post", - url: `${this.url}/Student/search`, - headers: { - Authorization: request.headers.authorization, - }, - data: data, - }; - - const response = await axios(config); - let result = await this.StudentMappedResponse( - response?.data && response.data - ); - - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: result, - }); - } else if (role == "Teacher") { - let axios = require("axios"); - - let final = { - method: "get", - url: `${this.url}/Class/${id}`, - headers: { - Authorization: request.headers.authorization, - }, - }; - - const response = await axios(final); - let classObj = response?.data; - let resData = []; - if (classObj?.teacherId) { - let classFinal = { - method: "get", - url: `${this.url}/User/${classObj.teacherId}`, - headers: { - Authorization: request.headers.authorization, - }, - }; - - const responseData = await axios(classFinal); - - let response = await this.userMappedResponse([responseData.data]); - resData = response; - } - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: resData, - }); - } else { - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: { msg: "Unable to get data !!" }, - }); - } - } - - public async findGroupsByUserId(id: string, role: string, request: any) { - let responseData = []; - - if (role === "Teacher") { - let axios = require("axios"); - let data = { - filters: { - teacherId: { - eq: `${id}`, - }, - }, - }; - - let final = { - method: "post", - url: `${this.url}/Class/search`, - headers: { - Authorization: request.headers.authorization, - }, - data: data, - }; - - const response = await axios(final); - responseData = response.data; - } else if (role === "Student") { - let axios = require("axios"); - const config = { - method: "get", - url: `${this.url}/Student/${id}`, - headers: { - Authorization: request.headers.authorization, - }, - }; - - const response = await axios(config); - let studentObj = response?.data; - - if (studentObj?.groupId) { - let studentFinal = { - method: "get", - url: `${this.url}/Class/${studentObj.groupId}`, - headers: { - Authorization: request.headers.authorization, - }, - }; - const resData = await axios(studentFinal); - - responseData = resData?.data ? [resData.data] : []; - } - } - - const groupResponse = await this.mappedResponse(responseData); - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: groupResponse, - }); - } - public async findMembersOfChildGroup( - groupId: string, - role: string, - request: any - ) {} - - public async mappedResponse(result: any) { - const groupResponse = result.map((item: any) => { - const groupMapping = { - groupId: item?.osid ? `${item.osid}` : "", - schoolId: item?.schoolId ? `${item.schoolId}` : "", - name: item?.name ? `${item.name}` : "", - type: item?.type ? `${item.type}` : "", - section: item?.section ? `${item.section}` : "", - status: item?.status ? `${item.status}` : "", - deactivationReason: item?.deactivationReason - ? `${item.deactivationReason}` - : "", - mediumOfInstruction: item?.mediumOfInstruction - ? `${item.mediumOfInstruction}` - : "", - teacherId: item?.teacherId ? `${item.teacherId}` : "", - parentId: item?.parentId ? `${item.parentId}` : "", - image: item?.image ? `${item.image}` : "", - metaData: item?.metaData ? item.metaData : [], - option: item?.option ? item.option : [], - gradeLevel: item?.gradeLevel ? `${item.gradeLevel}` : "", - createdAt: item?.osCreatedAt ? `${item.osCreatedAt}` : "", - updatedAt: item?.osUpdatedAt ? `${item.osUpdatedAt}` : "", - createdBy: item?.osCreatedBy ? `${item.osCreatedBy}` : "", - updatedBy: item?.osUpdatedBy ? `${item.osUpdatedBy}` : "", - }; - return new GroupDto(groupMapping); - }); - - return groupResponse; - } - public async StudentMappedResponse(result: any) { - const studentResponse = result.map((item: any) => { - const studentMapping = { - studentId: item?.osid ? `${item.osid}` : "", - refId1: item?.admissionNo ? `${item.admissionNo}` : "", - refId2: item?.refId2 ? `${item.refId2}` : "", - aadhaar: item?.aadhaar ? `${item.aadhaar}` : "", - firstName: item?.firstName ? `${item.firstName}` : "", - middleName: item?.middleName ? `${item.middleName}` : "", - lastName: item?.lastName ? `${item.lastName}` : "", - groupId: item?.groupId ? `${item.groupId}` : "", - schoolId: item?.schoolId ? `${item.schoolId}` : "", - studentEmail: item?.studentEmail ? `${item.studentEmail}` : "", - studentPhoneNumber: item?.studentPhoneNumber - ? item.studentPhoneNumber - : "", - iscwsn: item?.iscwsn ? `${item.iscwsn}` : "", - gender: item?.gender ? `${item.gender}` : "", - socialCategory: item?.socialCategory ? `${item.socialCategory}` : "", - religion: item?.religion ? `${item.religion}` : "", - singleGirl: item?.singleGirl ? item.singleGirl : "", - weight: item?.weight ? `${item.weight}` : "", - height: item?.height ? `${item.height}` : "", - bloodGroup: item?.bloodGroup ? `${item.bloodGroup}` : "", - birthDate: item?.birthDate ? `${item.birthDate}` : "", - homeless: item?.homeless ? item.homeless : "", - bpl: item?.bpl ? item.bpl : "", - migrant: item?.migrant ? item.migrant : "", - status: item?.status ? `${item.status}` : "", - - fatherFirstName: item?.fatherFirstName ? `${item.fatherFirstName}` : "", - - fatherMiddleName: item?.fatherMiddleName - ? `${item.fatherMiddleName}` - : "", - - fatherLastName: item?.fatherLastName ? `${item.fatherLastName}` : "", - fatherPhoneNumber: item?.fatherPhoneNumber - ? item.fatherPhoneNumber - : "", - fatherEmail: item?.fatherEmail ? `${item.fatherEmail}` : "", - - motherFirstName: item?.motherFirstName ? `${item.motherFirstName}` : "", - motherMiddleName: item?.motherMiddleName - ? `${item.motherMiddleName}` - : "", - motherLastName: item?.motherLastName ? `${item.motherLastName}` : "", - motherPhoneNumber: item?.motherPhoneNumber - ? item.motherPhoneNumber - : "", - motherEmail: item?.motherEmail ? `${item.motherEmail}` : "", - - guardianFirstName: item?.guardianFirstName - ? `${item.guardianFirstName}` - : "", - guardianMiddleName: item?.guardianMiddleName - ? `${item.guardianMiddleName}` - : "", - guardianLastName: item?.guardianLastName - ? `${item.guardianLastName}` - : "", - guardianPhoneNumber: item?.guardianPhoneNumber - ? item.guardianPhoneNumber - : "", - guardianEmail: item?.guardianEmail ? `${item.guardianEmail}` : "", - image: item?.image ? `${item.image}` : "", - deactivationReason: item?.deactivationReason - ? `${item.deactivationReason}` - : "", - studentAddress: item?.studentAddress ? `${item.studentAddress}` : "", - village: item?.village ? `${item.village}` : "", - block: item?.block ? `${item.block}` : "", - district: item?.district ? `${item.district}` : "", - stateId: item?.stateId ? `${item.stateId}` : "", - pincode: item?.pincode ? item.pincode : "", - locationId: item?.locationId ? `${item.locationId}` : "", - metaData: item?.metaData ? item.metaData : [], - createdAt: item?.osCreatedAt ? `${item.osCreatedAt}` : "", - updatedAt: item?.osUpdatedAt ? `${item.osUpdatedAt}` : "", - createdBy: item?.osCreatedBy ? `${item.osCreatedBy}` : "", - updatedBy: item?.osUpdatedBy ? `${item.osUpdatedBy}` : "", - }; - return new StudentDto(studentMapping); - }); - - return studentResponse; - } - - public async userMappedResponse(result: any) { - const userResponse = result.map((item: any) => { - const userMapping = { - userId: item?.osid ? `${item.osid}` : "", - refId1: item?.refId1 ? `${item.refId1}` : "", - refId2: item?.refId2 ? `${item.refId2}` : "", - refId3: item?.refId3 ? `${item.refId3}` : "", - firstName: item?.firstName ? `${item.firstName}` : "", - middleName: item?.middleName ? `${item.middleName}` : "", - lastName: item?.lastName ? `${item.lastName}` : "", - phoneNumber: item?.phoneNumber ? `${item.phoneNumber}` : "", - email: item?.email ? `${item.email}` : "", - aadhaar: item?.aadhaar ? `${item.aadhaar}` : "", - gender: item?.gender ? `${item.gender}` : "", - socialCategory: item?.socialCategory ? `${item.socialCategory}` : "", - birthDate: item?.birthDate ? `${item.birthDate}` : "", - designation: item?.designation ? `${item.designation}` : "", - cadre: item?.cadre ? `${item.cadre}` : "", - profQualification: item?.profQualification - ? `${item.profQualification}` - : "", - joiningDate: item?.joiningDate ? `${item.joiningDate}` : "", - subjectIds: item.subjectIds ? item.subjectIds : [], - bloodGroup: item?.bloodGroup ? `${item.bloodGroup}` : "", - maritalStatus: item?.maritalStatus ? `${item.maritalStatus}` : "", - compSkills: item?.compSkills ? `${item.compSkills}` : "", - disability: item?.disability ? `${item.disability}` : "", - religion: item?.religion ? `${item.religion}` : "", - homeDistance: item?.homeDistance ? `${item.homeDistance}` : "", - employmentType: item?.employmentType ? `${item.employmentType}` : "", - schoolId: item?.schoolId ? `${item.schoolId}` : "", - address: item?.address ? `${item.address}` : "", - village: item?.village ? `${item.village}` : "", - block: item?.block ? `${item.block}` : "", - district: item?.district ? `${item.district}` : "", - stateId: item?.stateId ? `${item.stateId}` : "", - pincode: item?.pincode ? item.pincode : "", - locationId: item?.locationId ? `${item.locationId}` : "", - image: item?.image ? `${item.image}` : "", - status: item?.status ? `${item.status}` : "", - deactivationReason: item?.deactivationReason - ? `${item.deactivationReason}` - : "", - reportsTo: item?.reportsTo ? `${item.reportsTo}` : "", - retirementDate: item?.retirementDate ? `${item.retirementDate}` : "", - workingStatus: item?.workingStatus ? `${item.workingStatus}` : "", - fcmToken: item?.fcmToken ? `${item.fcmToken}` : "", - role: item?.role ? `${item.role}` : "", - employeeCode: item?.employeeCode ? `${item.employeeCode}` : "", - metaData: item?.metaData ? item.metaData : [], - createdAt: item?.osCreatedAt ? `${item.osCreatedAt}` : "", - updatedAt: item?.osUpdatedAt ? `${item.osUpdatedAt}` : "", - createdBy: item?.osCreatedBy ? `${item.osCreatedBy}` : "", - updatedBy: item?.osUpdatedBy ? `${item.osUpdatedBy}` : "", - }; - return new UserDto(userMapping); - }); - - return userResponse; - } -} diff --git a/src/adapters/sunbirdrc/holiday.adapter.ts b/src/adapters/sunbirdrc/holiday.adapter.ts deleted file mode 100644 index edc5cfbe..00000000 --- a/src/adapters/sunbirdrc/holiday.adapter.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { HttpService } from "@nestjs/axios"; -import { Injectable, HttpException } from "@nestjs/common"; -const resolvePath = require("object-resolve-path"); -import { AxiosResponse } from "axios"; -import { HolidayDto } from "src/holiday/dto/holiday.dto"; -import { first, map, Observable } from "rxjs"; -import { SuccessResponse } from "src/success-response"; -import { catchError } from "rxjs/operators"; -import { ErrorResponse } from "src/error-response"; -import { HolidaySearchDto } from "src/holiday/dto/holiday-search.dto"; -import { IServicelocator } from "../holidayservicelocator"; -export const SunbirdHolidayToken = "SunbirdHoliday"; -@Injectable() -export class SunbirdHolidayService implements IServicelocator { - constructor(private httpService: HttpService) {} - url = `${process.env.BASEAPIURL}/Holiday`; - - public async getHoliday(holidayId: string, request: any) { - return this.httpService - .get(`${this.url}/${holidayId}`, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (axiosResponse: AxiosResponse) => { - let data = [axiosResponse.data]; - const holidayData = await this.mappedResponse(data); - return new SuccessResponse({ - statusCode: 200, - message: "ok.", - data: holidayData[0], - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response?.status, - errorMessage: e.response?.data?.params?.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - public async createHoliday(request: any, holidayDto: HolidayDto) { - return this.httpService - .post(`${this.url}`, holidayDto, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map((axiosResponse: AxiosResponse) => { - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: axiosResponse.data, - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response?.status, - errorMessage: e.response?.data?.params?.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async updateHoliday( - holidayId: string, - request: any, - holidayDto: HolidayDto - ) { - var axios = require("axios"); - var data = holidayDto; - - var config = { - method: "put", - url: `${this.url}/${holidayId}`, - headers: { - Authorization: request.headers.authorization, - }, - data: data, - }; - const response = await axios(config); - return new SuccessResponse({ - statusCode: 200, - message: " Ok.", - data: response.data, - }); - } - - public async searchHoliday(request: any, holidaySearchDto: HolidaySearchDto) { - return this.httpService - .post(`${this.url}/search`, holidaySearchDto, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (response) => { - const responsedata = response.data; - const holidayData = await this.mappedResponse(responsedata); - return new SuccessResponse({ - statusCode: response.status, - message: "Ok.", - data: holidayData, - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response.status, - errorMessage: e.response.data.params.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async holidayFilter(fromDate: string, toDate: string, request: any) { - let axios = require("axios"); - let filters = { - fromDate, - toDate, - }; - const filterArray = Object.keys(filters).filter( - (value, key) => filters[value] && filters[value] !== "" - ); - let data = { date: { between: [] } }; - filterArray.forEach((value, key) => { - if (["fromDate", "toDate"].includes(value)) { - data["date"].between.push(filters[value]); - } - }); - - let config = { - method: "post", - url: `${this.url}/search`, - headers: { - Authorization: request.headers.authorization, - }, - data: { filters: data }, - }; - - const response = await axios(config); - - let result = response?.data && response.data; - const holidayData = await this.mappedResponse(result); - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: holidayData, - }); - } - - public async mappedResponse(result: any) { - const holidayResponse = result.map((item: any) => { - const holidayMapping = { - holidayId: item?.osid ? `${item.osid}` : "", - date: item?.remark ? item.remark : "", - remark: item?.remark ? `${item.remark}` : "", - year: item?.year ? item.year : "", - context: item?.context ? `${item.context}` : "", - contextId: item?.contextId ? `${item.contextId}` : "", - createdAt: item?.osCreatedAt ? `${item.osCreatedAt}` : "", - updatedAt: item?.osUpdatedAt ? `${item.osUpdatedAt}` : "", - createdBy: item?.osCreatedBy ? `${item.osCreatedBy}` : "", - updatedBy: item?.osUpdatedBy ? `${item.osUpdatedBy}` : "", - }; - return new HolidayDto(holidayMapping); - }); - - return holidayResponse; - } -} diff --git a/src/adapters/sunbirdrc/inAppNotification.adapter.ts b/src/adapters/sunbirdrc/inAppNotification.adapter.ts deleted file mode 100644 index a8f02dec..00000000 --- a/src/adapters/sunbirdrc/inAppNotification.adapter.ts +++ /dev/null @@ -1,250 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { HttpService } from "@nestjs/axios"; -import { SuccessResponse } from "src/success-response"; -import { GroupDto } from "src/group/dto/group.dto"; -import moment from "moment"; - -@Injectable() -export class InAppNotificationService { - constructor(private httpService: HttpService) {} - url = process.env.HISTORYAPIURL; - UCIURL = process.env.UCIAPI; - templaterURL = process.env.TEMPLATERURL; - groupURL = `${process.env.BASEAPIURL}/Class`; - public async userHistoryNotification( - userId: string, - provider: string, - startDate: string, - endDate: string, - request: any - ) { - var axios = require("axios"); - - var config = { - method: "get", - url: `${this.url}history?userId=${userId}&provider=${provider}&endDate=${endDate}&startDate=${startDate}`, - headers: {}, - }; - - const response = await axios(config); - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: response.data.result, - }); - } - - public async botHistoryNotification( - botId: string, - provider: string, - startDate: string, - endDate: string, - request: any - ) { - var axios = require("axios"); - - var config = { - method: "get", - url: `${this.url}history/dump?provider=${provider}&botId=${botId}&endDate=${endDate}&startDate=${startDate}`, - headers: {}, - }; - - const response = await axios(config); - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: response.data.result, - }); - } - - public async inAppNotification( - module: string, - groupId: string, - request: any, - templateId: string - ) { - try { - var axios = require("axios"); - const result = Math.random().toString(27).substring(1, 8); - var confi = { - method: "get", - url: `${this.templaterURL}${templateId}`, - headers: { - Authorization: request.headers.authorization, - }, - }; - - const getContent = await axios(confi); - const contentData = getContent.data; - - // Conversation Logic - var conversationData = { - data: { - name: `Firebase Broadcast ${result}`, - transformers: [ - { - id: process.env.TRANSFORMERSID, - meta: { - body: contentData.body, - type: contentData.type, - params: ["name", "phoneNo"], - }, - type: "broadcast", - }, - ], - adapter: contentData.user, - }, - }; - - const conversation = await axios.post( - `${this.UCIURL}/conversationLogic/create`, - conversationData, - { - headers: { - "admin-token": process.env.UCIADMINTOKEN, - "Content-Type": "application/json", - }, - } - ); - - const resData = conversation.data; - const consversationLogicID = resData.result.data.id; - - var data = { - filters: { - osid: { - eq: groupId, - }, - }, - }; - - const responseData = await axios.post(`${this.groupURL}/search`, data, { - headers: { - Authorization: request.headers.authorization, - }, - }); - - const dtoResponse = await this.GroupMappedResponse(responseData.data); - - const filterObj = dtoResponse.filter((e: any) => e); - let option = filterObj[0].option; - let optionStr = JSON.stringify(option); - var jsonObj = JSON.parse(optionStr); - let params = JSON.parse(jsonObj); - - var botData = { - data: { - startingMessage: `Hi Shiksha Firebase Broadcast ${result}`, - name: `Shiksha Firebase Broadcast ${result}`, - users: [params.inAppNotification.teacherSegment], - logic: [consversationLogicID], - status: "enabled", - startDate: moment().format("Y-MM-DD"), - endDate: moment().format("Y-MM-DD"), - }, - }; - - const botResponse = await axios.post( - `${this.UCIURL}/bot/create`, - botData, - { - headers: { - "admin-token": process.env.UCIADMINTOKEN, - "Content-Type": "application/json", - }, - } - ); - - const botResData = botResponse.data; - const botCreateID = botResData.result.data.id; - - var configs = { - method: "get", - url: `${process.env.BOTCALL}${botCreateID}`, - headers: {}, - }; - - const botres = await axios(configs); - - const sendData = botres.data; - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: sendData, - }); - } catch (e) { - return { data: e.response.data }; - } - } - - public async readReceipt( - eventType: string, - externalId: string, - destAdd: string, - fcmDestAdd: string, - messageId: string, - text: string, - from: string, - request: any - ) { - var axios = require("axios"); - var data = { - text: text, - from: from, - messageId: messageId, - eventType: eventType, - report: { - externalId: externalId, - destAdd: destAdd, - fcmDestAdd: fcmDestAdd, - }, - }; - - var config = { - method: "post", - url: "http://139.59.14.252:9080/firebase/web", - headers: { - "Content-Type": "application/json", - }, - data: data, - }; - - const response = await axios(config); - - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: response.data, - }); - } - public async GroupMappedResponse(result: any) { - const groupResponse = result.map((item: any) => { - const groupMapping = { - groupId: item?.groupId ? `${item.groupId}` : "", - schoolId: item?.schoolId ? `${item.schoolId}` : "", - name: item?.name ? `${item.name}` : "", - type: item?.type ? `${item.type}` : "", - section: item?.section ? `${item.section}` : "", - status: item?.status ? `${item.status}` : "", - deactivationReason: item?.deactivationReason - ? `${item.deactivationReason}` - : "", - mediumOfInstruction: item?.mediumOfInstruction - ? `${item.mediumOfInstruction}` - : "", - teacherId: item?.teacherId ? `${item.teacherId}` : "", - parentId: item?.parentId ? `${item.parentId}` : "", - image: item?.image ? `${item.image}` : "", - metaData: item?.metaData ? item.metaData : [], - option: item?.option ? item.option : [], - gradeLevel: item?.gradeLevel ? `${item.gradeLevel}` : "", - createdAt: item?.created_at ? `${item.created_at}` : "", - updatedAt: item?.updated_at ? `${item.updated_at}` : "", - }; - return new GroupDto(groupMapping); - }); - - return groupResponse; - } -} diff --git a/src/adapters/sunbirdrc/lessonPlan.adapter.ts b/src/adapters/sunbirdrc/lessonPlan.adapter.ts deleted file mode 100644 index d31f62f2..00000000 --- a/src/adapters/sunbirdrc/lessonPlan.adapter.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { HttpService } from "@nestjs/axios"; -import { Injectable, HttpException } from "@nestjs/common"; -const resolvePath = require("object-resolve-path"); -import { AxiosResponse } from "axios"; -import { LessonPlanDto } from "src/lessonPlan/dto/lessonPlan.dto"; -import { map } from "rxjs"; -import { SuccessResponse } from "src/success-response"; -import { catchError } from "rxjs/operators"; -import { ErrorResponse } from "src/error-response"; -import { LessonPlanSearchDto } from "src/lessonPlan/dto/lessonPlan.search.dto"; -@Injectable() -export class LessonPlanService { - constructor(private httpService: HttpService) {} - url = `${process.env.BASEAPIURL}/Content`; - - public async getLessonPlan(lessonPlanId: string, request: any) { - return this.httpService - .get(`${this.url}/${lessonPlanId}`, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (axiosResponse: AxiosResponse) => { - let data = [axiosResponse.data]; - const lessonPlanDto = await this.mappedResponse(data); - return new SuccessResponse({ - statusCode: 200, - message: "ok.", - data: lessonPlanDto[0], - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response?.status, - errorMessage: e.response?.data?.params?.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - public async createLessonPlan(request: any, lessonPlanDto: LessonPlanDto) { - return this.httpService - .post(`${this.url}`, lessonPlanDto, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map((axiosResponse: AxiosResponse) => { - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: axiosResponse.data, - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response?.status, - errorMessage: e.response?.data?.params?.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async updateLessonPlan( - lessonPlanId: string, - request: any, - lessonPlanDto: LessonPlanDto - ) { - var axios = require("axios"); - var data = lessonPlanDto; - - var config = { - method: "put", - url: `${this.url}/${lessonPlanId}`, - headers: { - Authorization: request.headers.authorization, - }, - data: data, - }; - const response = await axios(config); - return new SuccessResponse({ - statusCode: 200, - message: " Ok.", - data: response.data, - }); - } - - public async searchLessonPlan( - request: any, - lessonPlanSearchDto: LessonPlanSearchDto - ) { - return this.httpService - .post(`${this.url}/search`, lessonPlanSearchDto, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (response) => { - const responsedata = await this.mappedResponse(response.data); - return new SuccessResponse({ - statusCode: response.status, - message: "Ok.", - data: responsedata, - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response.status, - errorMessage: e.response.data.params.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async mappedResponse(result: any) { - const lessonPlanResponse = result.map((obj: any) => { - const lessonPlanMapping = { - contentId: obj?.osid ? `${obj.osid}` : "", - name: `${obj.name}`, - code: obj?.code ? `${obj.code}` : "", - status: obj?.status ? `${obj.status}` : "", - channel: obj?.channel ? `${obj.channel}` : "", - mediaType: obj?.mediaType ? `${obj.mediaType}` : "", - compatibilityLevel: obj?.compatibilityLevel - ? `${obj.compatibilityLevel}` - : "", - audience: obj?.audience ? obj.audience : [], - posterImage: obj?.posterImage ? `${obj.posterImage}` : "", - duration: obj?.duration ? `${obj.duration}` : "", - downloadUrl: obj?.downloadUrl ? `${obj.downloadUrl}` : "", - previewUrl: obj?.previewUrl ? `${obj.previewUrl}` : "", - author: obj?.author ? `${obj.author}` : "", - languageCode: obj?.languageCode ? obj.languageCode : [], - language: obj?.language ? obj.language : [], - ageGroup: obj?.ageGroup ? obj.ageGroup : [], - contentType: obj?.contentType ? `${obj.contentType}` : "", - category: obj?.category ? obj.category : [], - teachingMode: obj?.teachingMode ? `${obj.teachingMode}` : "", - skills: obj?.skills ? obj.skills : [], - keywords: obj?.keywords ? obj.keywords : [], - description: obj?.osid ? `${obj.osid}` : "", - instructions: obj?.osid ? `${obj.osid}` : "", - body: obj?.body ? obj.body : "", - learningObjective: obj?.learningObjective ? obj.learningObjective : [], - creator: obj?.creator ? `${obj.creator}` : "", - reviewer: obj?.reviewer ? `${obj.reviewer}` : "", - lastSubmittedBy: obj?.lastSubmittedBy ? `${obj.lastSubmittedBy}` : "", - lastSubmittedOn: obj?.lastSubmittedOn ? `${obj.lastSubmittedOn}` : "", - lastPublishedBy: obj?.lastPublishedBy ? `${obj.lastPublishedBy}` : "", - lastPublishedOn: obj?.astPublishedOn ? `${obj.astPublishedOn}` : "", - subject: obj?.subject ? obj.subject : [], - questionCategories: obj?.questionCategories - ? obj.questionCategories - : [], - medium: obj?.medium ? obj.medium : [], - gradeLevel: obj?.gradeLevel ? obj.gradeLevel : [], - topic: obj?.topic ? obj.topic : [], - subjectCodes: obj?.subjectCodes ? obj.subjectCodes : [], - difficultyLevel: obj?.difficultyLevel ? `${obj.difficultyLevel}` : "", - board: obj?.board ? `${obj.board}` : "", - primaryCategory: obj?.primaryCategory ? `${obj.primaryCategory}` : "", - accessibility: obj?.accessibility ? obj.accessibility : [], - createdAt: obj?.osCreatedAt ? `${obj.osCreatedAt}` : "", - updatedAt: obj?.osUpdatedAt ? `${obj.osUpdatedAt}` : "", - createdBy: obj?.osCreatedBy ? `${obj.osCreatedBy}` : "", - updatedBy: obj?.osUpdatedBy ? `${obj.osUpdatedBy}` : "", - }; - return new LessonPlanDto(lessonPlanMapping); - }); - - return lessonPlanResponse; - } -} diff --git a/src/adapters/sunbirdrc/like.adapter.ts b/src/adapters/sunbirdrc/like.adapter.ts deleted file mode 100644 index 18c86965..00000000 --- a/src/adapters/sunbirdrc/like.adapter.ts +++ /dev/null @@ -1,235 +0,0 @@ -import { HttpService } from "@nestjs/axios"; -import { Injectable, HttpException } from "@nestjs/common"; -import { AxiosResponse } from "axios"; -import { map } from "rxjs"; -import { SuccessResponse } from "src/success-response"; -import { catchError } from "rxjs/operators"; -import { ErrorResponse } from "src/error-response"; -import { LikeDto } from "src/like/dto/like.dto"; -import { LikeSearchDto } from "src/like/dto/like-search.dto"; -import jwt_decode from "jwt-decode"; -import { IServicelocator } from "../likeservicelocator"; -export const SunbirdLikeToken = "SunbirdLike"; -@Injectable() -export class SunbirdLikeService implements IServicelocator { - constructor(private httpService: HttpService) {} - url = `${process.env.BASEAPIURL}/Like`; - userUrl = `${process.env.BASEAPIURL}/User`; - - public async getLike(likeId: string, request: any) { - return this.httpService - .get(`${this.url}/${likeId}`, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (axiosResponse: AxiosResponse) => { - let data = [axiosResponse.data]; - const likeDto = await this.mappedResponse(data); - return new SuccessResponse({ - statusCode: 200, - message: "ok.", - data: likeDto[0], - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response?.status, - errorMessage: e.response?.data?.params?.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - public async createLike(request: any, likeDto: LikeDto) { - const authToken = request.headers.authorization; - const decoded: any = jwt_decode(authToken); - let email = decoded.email; - let axios = require("axios"); - let data = { - filters: { - email: { - eq: `${email}`, - }, - }, - }; - let config = { - method: "post", - url: `${this.userUrl}/search`, - headers: { - Authorization: request.headers.authorization, - }, - data: data, - }; - const response = await axios(config); - const result = response.data[0]; - likeDto.userId = result.osid; - return this.httpService - .post(`${this.url}`, likeDto, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map((axiosResponse: AxiosResponse) => { - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: axiosResponse.data, - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response?.status, - errorMessage: e.response?.data?.params?.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async updateLike(likeId: string, request: any, likeDto: LikeDto) { - var axios = require("axios"); - var data = likeDto; - const authToken = request.headers.authorization; - const decoded: any = jwt_decode(authToken); - let email = decoded.email; - let updateData = { - filters: { - email: { - eq: `${email}`, - }, - }, - }; - let configData = { - method: "post", - url: `${this.userUrl}/search`, - headers: { - Authorization: request.headers.authorization, - }, - data: updateData, - }; - const userResponse = await axios(configData); - const result = userResponse.data[0]; - data.userId = result.osid; - - var config = { - method: "put", - url: `${this.url}/${likeId}`, - headers: { - Authorization: request.headers.authorization, - }, - data: data, - }; - const response = await axios(config); - return new SuccessResponse({ - statusCode: 200, - message: " Ok.", - data: response.data, - }); - } - - public async searchLike(request: any, likeSearchDto: LikeSearchDto) { - return this.httpService - .post(`${this.url}/search`, likeSearchDto, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (response) => { - const responsedata = response.data; - const likeDto = await this.mappedResponse(responsedata); - return new SuccessResponse({ - statusCode: response.status, - message: "Ok.", - data: likeDto, - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response.status, - errorMessage: e.response.data.params.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async getCountLike(contextId: string, context: string, request: any) { - let axios = require("axios"); - let data = { - filters: { - contextId: { - eq: `${contextId}`, - }, - context: { - eq: `${context}`, - }, - }, - }; - - let config = { - method: "post", - url: `${this.url}/search`, - - data: data, - }; - - const response = await axios(config); - let resData = response?.data; - const likeDto = await this.mappedResponse(resData); - return new SuccessResponse({ - statusCode: 200, - message: " Ok.", - data: likeDto.length, - }); - } - - public async deleteLike(likeId: string, request: any) { - return this.httpService - .delete(`${this.url}/${likeId}`, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map((axiosResponse: AxiosResponse) => { - let data = axiosResponse.data; - - return new SuccessResponse({ - statusCode: 200, - message: "ok.", - data: data, - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response?.status, - errorMessage: e.response?.data?.params?.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async mappedResponse(result: any) { - const likeResponse = result.map((obj: any) => { - const likeMapping = { - likeId: obj?.osid ? `${obj.osid}` : "", - contextId: obj?.contextId ? `${obj.contextId}` : "", - context: obj?.context ? `${obj.context}` : "", - userId: obj?.userId ? `${obj.userId}` : "", - type: obj?.type ? `${obj.type}` : "", - createdAt: obj?.osCreatedAt ? `${obj.osCreatedAt}` : "", - updatedAt: obj?.osUpdatedAt ? `${obj.osUpdatedAt}` : "", - createdBy: obj?.osCreatedBy ? `${obj.osCreatedBy}` : "", - updatedBy: obj?.osUpdatedBy ? `${obj.osUpdatedBy}` : "", - }; - return new LikeDto(likeMapping); - }); - - return likeResponse; - } -} diff --git a/src/adapters/sunbirdrc/notification.adapter.ts b/src/adapters/sunbirdrc/notification.adapter.ts deleted file mode 100644 index 2dd05ad1..00000000 --- a/src/adapters/sunbirdrc/notification.adapter.ts +++ /dev/null @@ -1,618 +0,0 @@ -import { Injectable, HttpException, Param } from "@nestjs/common"; -import { HttpService } from "@nestjs/axios"; -import axios, { AxiosResponse } from "axios"; -import { map } from "rxjs"; -import { SuccessResponse } from "src/success-response"; -import { catchError, takeLast } from "rxjs/operators"; -import { ErrorResponse } from "src/error-response"; -import { NotificationLogDto } from "src/notification/dto/notification.dto"; -import { NotificationSearchDto } from "src/notification/dto/notification-search.dto"; -import moment from "moment"; -import { GroupDto } from "src/group/dto/group.dto"; -import { CronJob } from "cron"; -import { SchedulerRegistry } from "@nestjs/schedule"; -import jwt_decode from "jwt-decode"; - -@Injectable() -export class NotificationService { - constructor( - private httpService: HttpService, - private schedulerRegistry: SchedulerRegistry - ) {} - baseURL = `${process.env.BASEAPIURL}`; - UCIURL = `${process.env.UCIAPI}`; - url = process.env.TEMPLATERURL; - groupURL = `${process.env.BASEAPIURL}/Class`; - - public async instantSendNotification( - module: any, - eventTrigger: string, - templateId: string, - senderId: string, - groupId: string, - channel: string, - request: any - ) { - try { - var axios = require("axios"); - const result = Math.random().toString(27).substring(1, 8); - var confi = { - method: "get", - url: `${this.url}${templateId}`, - headers: { - Authorization: request.headers.authorization, - }, - }; - - const getContent = await axios(confi); - const contentData = getContent.data; - - var data = { - filters: { - osid: { - eq: groupId, - }, - }, - }; - - const responseData = await axios.post(`${this.groupURL}/search`, data, { - headers: { - Authorization: request.headers.authorization, - }, - }); - - const dtoResponse = await this.groupResponse(responseData.data); - const filterObj = dtoResponse.filter((e: any) => e); - let option = filterObj[0].option; - let optionStr = JSON.stringify(option); - var jsonObj = JSON.parse(optionStr); - let params = JSON.parse(jsonObj); - var notificationModule = params.filter( - (obj: any) => obj.module === module - ); - const triggers = notificationModule[0].eventTriggers; - var notificationTrigger = triggers.filter( - (obj: any) => obj.name === eventTrigger - ); - let botIdChecked = notificationTrigger[0].botId; - - let botCreateID: any; - - if (botIdChecked.length > 0) { - botCreateID = notificationTrigger[0].botId; - } else { - // Conversation Logic - var conversationData = { - data: { - name: `Shiksha ${channel} Broadcast ${result}`, - transformers: [ - { - id: process.env.TRANSFORMERSID, - meta: { - body: contentData.body, - type: contentData.type, - user: process.env.TRANSFORMERSUSER, - }, - type: "broadcast", - }, - ], - adapter: contentData.user, - }, - }; - - const conversation = await axios.post( - `${this.UCIURL}/conversationLogic/create`, - conversationData, - { - headers: { - "admin-token": process.env.UCIADMINTOKEN, - "Content-Type": "application/json", - }, - } - ); - - const resData = conversation.data; - const consversationLogicID = resData.result.data.id; - - var botData = { - data: { - startingMessage: `Hi Shiksha ${channel} Broadcast ${result}`, - name: `Shiksha Notification Broadcast ${result}`, - users: [notificationTrigger[0].userSegment], - logic: [consversationLogicID], - status: "enabled", - startDate: moment().format("Y-MM-DD"), - endDate: moment().format("Y-MM-DD"), - }, - }; - - const botResponse = await axios.post( - `${this.UCIURL}/bot/create`, - botData, - { - headers: { - "admin-token": process.env.UCIADMINTOKEN, - "Content-Type": "application/json", - }, - } - ); - - const botResData = botResponse.data; - botCreateID = botResData.result.data.id; - } - - var configs = { - method: "get", - url: `${process.env.BOTCALL}${botCreateID}`, - headers: {}, - }; - - const botres = await axios(configs); - - const sendData = botres.data; - // Notification Log - - var notificationData = { - medium: channel, - templateId: templateId, - recepients: [notificationTrigger[0].userSegment], - sentDate: new Date(), - sentBy: senderId, - module: notificationModule[0].module, - options: "", - content: contentData.body, - }; - - const logRes = await axios.post( - `${this.baseURL}Notificationlog`, - notificationData, - { - headers: { - Authorization: request.headers.authorization, - }, - } - ); - const logResponse = logRes.data; - return new SuccessResponse({ - statusCode: 200, - message: "ok.", - data: logResponse, - }); - } catch (e) { - return e.response.data; - } - } - - //Notificationschedule - public async scheduleSendNotification( - module: any, - eventTrigger: string, - templateId: string, - senderId: string, - groupId: string, - channel: string, - month: string, - date: string, - hours: string, - minutes: String, - jobName: string, - request: any - ) { - try { - var axios = require("axios"); - - //content data api - var confi = { - method: "get", - url: `${this.url}${templateId}`, - headers: { - Authorization: request.headers.authorization, - }, - }; - const getContent = await axios(confi); - const contentData = getContent.data; - - //params data - var axios = require("axios"); - var data = { - filters: { - osid: { - eq: groupId, - }, - }, - }; - - var getSegment = { - method: "post", - url: `${this.groupURL}/search`, - headers: { - Authorization: request.headers.authorization, - }, - data: data, - }; - const responseData = await axios(getSegment); - - const dtoResponse = await this.groupResponse(responseData.data); - - const filterObj = dtoResponse.filter((e: any) => e); - - let option = filterObj[0].option; - - let optionStr = JSON.stringify(option); - var jsonObj = JSON.parse(optionStr); - let params = JSON.parse(jsonObj); - var notificationModule = params.filter( - (obj: any) => obj.module === module - ); - const triggers = notificationModule[0].eventTriggers; - var notificationTrigger = triggers.filter( - (obj: any) => obj.name === eventTrigger - ); - let botIdChecked = notificationTrigger[0].botId; - let botCreateID: any; - - //save notification - let notificationScheduleData = { - medium: channel, - templateId: templateId, - recepients: [notificationTrigger[0].userSegment], - sentDate: date, - sentBy: senderId, - module: notificationModule[0].module, - options: "", - content: contentData.body, - scheduleDate: date, - hours, - minutes, - month, - }; - - var logConfig = { - method: "post", - url: `${this.baseURL}Notificationschedule`, - headers: { - Authorization: request.headers.authorization, - }, - data: notificationScheduleData, - }; - - const logRes = await axios(logConfig); - const logResponse = logRes.data; - - //cronJob logic - - let osid = logResponse.result.Notificationschedule.osid; - - var yy = date.slice(0, 4); - let year = parseInt(yy); - - var dd = date.slice(-2); - let d = parseInt(dd); - - var mm = date.slice(5, 7); - let mon = parseInt(mm); - mon = mon - 1; - - let hrs = parseInt(hours); - let mins = +minutes; - - let ist = new Date(year, mon, d, hrs, mins); - let utc = moment.utc(ist).format("YYYY-MM-DD HH:mm:ss "); - let utcMin = utc.slice(14, 16); - let utcHrs = utc.slice(11, 13); - let utcDay = utc.slice(8, 11); - let utcMon = utc.slice(5, 7); - const job = new CronJob( - // `0 ${utcMin} ${utcHrs} ${utcDay} ${utcMon} *`, - `0 ${mins} ${hrs} ${d} ${mon} *`, - async () => { - try { - var axios = require("axios"); - const result = Math.random().toString(27).substring(1, 8); - - if (botIdChecked.length > 0) { - botCreateID = notificationTrigger[0].botId; - } else { - // Conversation Logic - var conversationData = { - data: { - name: `Shiksha ${channel} Broadcast ${result}`, - transformers: [ - { - id: process.env.TRANSFORMERSID, - meta: { - body: contentData.body, - type: contentData.type, - user: process.env.TRANSFORMERSUSER, - }, - type: "broadcast", - }, - ], - adapter: contentData.user, - }, - }; - - const conversation = await axios.post( - `${this.UCIURL}/conversationLogic/create`, - conversationData, - { - headers: { - "admin-token": process.env.UCIADMINTOKEN, - "Content-Type": "application/json", - }, - } - ); - - const resData = conversation.data; - const consversationLogicID = resData.result.data.id; - - var botData = { - data: { - startingMessage: `Hi Shiksha ${channel} Broadcast ${result}`, - name: `Shiksha Notification Broadcast ${result}`, - users: [notificationTrigger[0].userSegment], - logic: [consversationLogicID], - status: "enabled", - startDate: moment().format("Y-MM-DD"), - endDate: moment().format("Y-MM-DD"), - }, - }; - - const botResponse = await axios.post( - `${this.UCIURL}/bot/create`, - botData, - { - headers: { - "admin-token": process.env.UCIADMINTOKEN, - "Content-Type": "application/json", - }, - } - ); - - const botResData = botResponse.data; - botCreateID = botResData.result.data.id; - } - - var configs = { - method: "get", - url: `${process.env.BOTCALL}${botCreateID}`, - headers: {}, - }; - const botres = await axios(configs); - - const sendData = botres.data; - // Notification Log - - var notificationData = { - medium: channel, - templateId: templateId, - recepients: [notificationTrigger[0].userSegment], - sentDate: date, - sentBy: senderId, - module: module, - options: "", - content: contentData.body, - scheduleDate: date, - hours, - minutes, - month, - }; - const logRes = await axios.post( - `${this.baseURL}Notificationlog`, - notificationData, - { - headers: { - Authorization: request.headers.authorization, - }, - } - ); - const logResponse = logRes.data; - - var deleteCron = { - method: "delete", - url: `${this.baseURL}Notificationschedule/${osid}`, - headers: { - Authorization: request.headers.authorization, - }, - }; - const deletedNotification = await axios(deleteCron); - - job.stop(); - } catch (e) { - return e.response.data; - } - } - ); - - this.schedulerRegistry.addCronJob(jobName, job); - job.start(); - - return `SMS set for EOD at ${hours}:${minutes} `; - } catch (e) { - return `${e}`; - } - } - - public async getNotification(notificationId: string, request: any) { - return this.httpService - .get(`${this.baseURL}/Notificationlog/${notificationId}`, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (axiosResponse: AxiosResponse) => { - let notificationData = [axiosResponse.data]; - const NotificationLogDto = await this.notificationLog( - notificationData - ); - - return new SuccessResponse({ - statusCode: 200, - message: "ok.", - data: NotificationLogDto[0], - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response?.status, - errorMessage: e.response?.data?.params?.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async searchNotification( - request: any, - notificationSearchDto: NotificationSearchDto - ) { - return this.httpService - .post(`${this.baseURL}/Notificationlog/search`, notificationSearchDto, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (response) => { - const responsedata = await this.notificationLog(response.data); - return new SuccessResponse({ - statusCode: response.status, - message: "Ok.", - data: responsedata, - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response.status, - errorMessage: e.response.data.params.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - //schedule - public async searchSchedulehNotification( - request: any, - notificationSearchDto: NotificationSearchDto - ) { - return this.httpService - .post( - `${this.baseURL}/Notificationschedule/search`, - notificationSearchDto, - { - headers: { - Authorization: request.headers.authorization, - }, - } - ) - .pipe( - map(async (response) => { - const responsedata = await this.notificationLog(response.data); - return new SuccessResponse({ - statusCode: response.status, - message: "Ok.", - data: responsedata, - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response.status, - errorMessage: e.response.data.params.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async getScheduleNotification( - ScheduleNotificationid: string, - request: any - ) { - return this.httpService - .get(`${this.baseURL}/Notificationschedule/${ScheduleNotificationid}`, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (axiosResponse: AxiosResponse) => { - let scheduleNotificationData = [axiosResponse.data]; - const responsedata = await this.notificationLog( - scheduleNotificationData - ); - - return new SuccessResponse({ - statusCode: 200, - message: "ok.", - data: responsedata[0], - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response?.status, - errorMessage: e.response?.data?.params?.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - public async notificationLog(result: any) { - const notificationLog = result.map((item: any) => { - const notificationLogMapping = { - notificationLogId: item?.osid ? `${item.osid}` : "", - content: item?.content ? `${item.content}` : "", - recepients: item?.recepients ? item.recepients : [], - module: item?.module ? `${item.module}` : "", - templateContentId: item?.templateContentId - ? `${item.templateContentId}` - : "", - templateId: item?.templateId ? `${item.templateId}` : "", - medium: item?.medium ? `${item.medium}` : "", - sentDate: item?.sentDate ? `${item.sentDate}` : "", - sentBy: item?.sentBy ? `${item.sentBy}` : "", - scheduleDate: item?.scheduleDate ? `${item.scheduleDate}` : "", - options: item?.options ? item.options : "", - createdAt: item?.osCreatedAt ? `${item.osCreatedAt}` : "", - updatedAt: item?.osUpdatedAt ? `${item.osUpdatedAt}` : "", - createdBy: item?.osCreatedBy ? `${item.osCreatedBy}` : "", - updatedBy: item?.osUpdatedBy ? `${item.osUpdatedBy}` : "", - }; - return new NotificationLogDto(notificationLogMapping); - }); - - return notificationLog; - } - public async groupResponse(result: any) { - const groupResponse = result.map((item: any) => { - const groupMapping = { - groupId: item?.osid ? `${item.osid}` : "", - schoolId: item?.schoolId ? `${item.schoolId}` : "", - name: item?.name ? `${item.name}` : "", - type: item?.type ? `${item.type}` : "", - section: item?.section ? `${item.section}` : "", - status: item?.status ? `${item.status}` : "", - deactivationReason: item?.deactivationReason - ? `${item.deactivationReason}` - : "", - mediumOfInstruction: item?.mediumOfInstruction - ? `${item.mediumOfInstruction}` - : "", - teacherId: item?.teacherId ? `${item.teacherId}` : "", - parentId: item?.parentId ? `${item.parentId}` : "", - image: item?.image ? `${item.image}` : "", - metaData: item?.metaData ? item.metaData : [], - option: item?.option ? item.option : [], - gradeLevel: item?.gradeLevel ? `${item.gradeLevel}` : "", - createdAt: item?.osCreatedAt ? `${item.osCreatedAt}` : "", - updatedAt: item?.osUpdatedAt ? `${item.osUpdatedAt}` : "", - createdBy: item?.osCreatedBy ? `${item.osCreatedBy}` : "", - updatedBy: item?.osUpdatedBy ? `${item.osUpdatedBy}` : "", - }; - return new GroupDto(groupMapping); - }); - - return groupResponse; - } -} diff --git a/src/adapters/sunbirdrc/school.adapter.ts b/src/adapters/sunbirdrc/school.adapter.ts deleted file mode 100644 index 2e73cf31..00000000 --- a/src/adapters/sunbirdrc/school.adapter.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { HttpException, Injectable } from "@nestjs/common"; -import { HttpService } from "@nestjs/axios"; -import { AxiosResponse } from "axios"; -import { catchError, map } from "rxjs"; -import { SchoolDto } from "src/school/dto/school.dto"; -import { SchoolSearchDto } from "src/school/dto/school-search.dto"; -import { SuccessResponse } from "src/success-response"; -import { ErrorResponse } from "src/error-response"; -import { IServicelocator } from "../schoolservicelocator"; -export const SunbirdSchoolToken = "SunbirdSchool"; -@Injectable() -export class SchoolService implements IServicelocator { - constructor(private httpService: HttpService) {} - url = `${process.env.BASEAPIURL}/School`; - - public async getSchool(id: any, request: any) { - return this.httpService - .get(`${this.url}/${id}`, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (axiosResponse: AxiosResponse) => { - const data = axiosResponse.data; - const schoolDto = await this.mappedResponse(data); - return new SuccessResponse({ - statusCode: 200, - message: " Ok.", - data: schoolDto[0], - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response?.status, - errorMessage: e.response?.data?.params?.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async createSchool(request: any, schoolDto: SchoolDto) { - return this.httpService - .post(`${this.url}`, schoolDto, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map((axiosResponse: AxiosResponse) => { - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: axiosResponse.data, - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response?.status, - errorMessage: e.response?.data?.params?.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async updateSchool(id: string, request: any, schoolDto: SchoolDto) { - var axios = require("axios"); - var data = schoolDto; - - var config = { - method: "put", - url: `${this.url}/${id}`, - headers: { - Authorization: request.headers.authorization, - }, - data: data, - }; - const response = await axios(config); - return new SuccessResponse({ - statusCode: 200, - message: " Ok.", - data: response.data, - }); - } - - public async searchSchool(request: any, schoolSearchDto: SchoolSearchDto) { - return this.httpService - .post(`${this.url}/search`, schoolSearchDto, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (response) => { - const responsedata = response.data; - const schoolDto = await this.mappedResponse(responsedata); - return new SuccessResponse({ - statusCode: response.status, - message: "Ok.", - data: schoolDto, - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response.status, - errorMessage: e.response.data.params.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async mappedResponse(result: any) { - const schoolResponse = result.map((item: any) => { - const schoolMapping = { - schoolId: item?.osid ? `${item.osid}` : "", - schoolName: item?.schoolName ? `${item.schoolName}` : "", - email: item?.email ? `${item.email}` : "", - udise: item?.udise ? `${item.udise}` : "", - mediumOfInstruction: item?.mediumOfInstruction - ? item.mediumOfInstruction - : "", - phoneNumber: item?.phoneNumber ? item.phoneNumber : "", - address: item?.address ? item.address : "", - schoolType: item?.schoolType ? `${item.schoolType}` : "", - website: item?.website ? `${item.website}` : "", - headMaster: item?.headMaster ? `${item.headMaster}` : "", - board: item?.board ? `${item.board}` : "", - village: item?.village ? `${item.village}` : "", - block: item?.block ? `${item.block}` : "", - district: item?.district ? `${item.district}` : "", - stateId: item?.stateId ? `${item.stateId}` : "", - cluster: item?.cluster ? `${item.cluster}` : "", - pincode: item?.pincode ? item.pincode : "", - locationId: item?.locationId ? `${item.locationId}` : "", - enrollCount: item?.enrollCount ? `${item.enrollCount}` : "", - status: item?.status ? `${item.status}` : "", - latitude: item?.latitude ? item.latitude : "", - longitude: item?.longitude ? item.longitude : "", - metaData: item?.metaData ? item.metaData : [], - deactivationReason: item?.deactivationReason - ? `${item.deactivationReason}` - : "", - createdAt: item?.osCreatedAt ? `${item.osCreatedAt}` : "", - updatedAt: item?.osUpdatedAt ? `${item.osUpdatedAt}` : "", - createdBy: item?.osCreatedBy ? `${item.osCreatedBy}` : "", - updatedBy: item?.osUpdatedBy ? `${item.osUpdatedBy}` : "", - }; - return new SchoolDto(schoolMapping); - }); - - return schoolResponse; - } -} diff --git a/src/adapters/sunbirdrc/student.adapter.ts b/src/adapters/sunbirdrc/student.adapter.ts deleted file mode 100644 index 12c4fd22..00000000 --- a/src/adapters/sunbirdrc/student.adapter.ts +++ /dev/null @@ -1,226 +0,0 @@ -import { Injectable, HttpException } from "@nestjs/common"; -import { StudentDto } from "../../student/dto/student.dto"; -import { HttpService } from "@nestjs/axios"; -import { AxiosResponse } from "axios"; -import { map } from "rxjs"; -import { SuccessResponse } from "src/success-response"; -import { catchError } from "rxjs/operators"; -import { ErrorResponse } from "src/error-response"; -import { StudentSearchDto } from "src/student/dto/student-search.dto"; -import { IServicelocator } from "../studentservicelocator"; -export const SunbirdStudentToken = "SunbirdStudent"; -@Injectable() -export class StudentService implements IServicelocator { - private student: StudentDto; - - constructor(private httpService: HttpService) {} - url = `${process.env.BASEAPIURL}/Student`; - - public async getStudent(studentId: any, request: any) { - return this.httpService - .get(`${this.url}/${studentId}`, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (axiosResponse: AxiosResponse) => { - let data = axiosResponse.data; - - const studentDto = [data]; - const studentResponse = await this.mappedResponse(studentDto); - return new SuccessResponse({ - statusCode: 200, - message: "student found Successfully", - data: studentResponse[0], - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response?.status, - errorMessage: e.response?.data?.params?.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async createStudent(request: any, studentDto: StudentDto) { - return this.httpService - .post(`${this.url}`, studentDto, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map((axiosResponse: AxiosResponse) => { - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: axiosResponse.data, - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response?.status, - errorMessage: e.response?.data?.params?.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async updateStudent(id: string, request: any, studentDto: StudentDto) { - var axios = require("axios"); - var data = studentDto; - - var config = { - method: "put", - url: `${this.url}/${id}`, - headers: { - Authorization: request.headers.authorization, - }, - data: data, - }; - const response = await axios(config); - return new SuccessResponse({ - statusCode: 200, - message: " Ok.", - data: response.data, - }); - } - - public async searchStudent(request: any, studentSearchDto: StudentSearchDto) { - let studentSearchData = studentSearchDto; - - let searchData: any; - Object.keys(studentSearchData).forEach((e) => { - if (studentSearchData[e].studentId) { - searchData = { - filters: { - osid: { - eq: `${studentSearchData[e].studentId.eq}`, - }, - ...studentSearchData.filters, - }, - }; - - delete searchData.filters.studentId; - } else { - searchData = studentSearchData; - } - }); - return this.httpService - .post(`${this.url}/search`, studentSearchDto, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (response) => { - const responsedata = response.data; - const studentResponse = await this.mappedResponse(responsedata); - return new SuccessResponse({ - statusCode: response.status, - message: "Ok.", - data: studentResponse, - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response.status, - errorMessage: e.response.data.params.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async mappedResponse(result: any) { - const studentResponse = result.map((item: any) => { - const studentMapping = { - studentId: item?.osid ? `${item.osid}` : "", - refId1: item?.admissionNo ? `${item.admissionNo}` : "", - refId2: item?.refId2 ? `${item.refId2}` : "", - aadhaar: item?.aadhaar ? `${item.aadhaar}` : "", - firstName: item?.firstName ? `${item.firstName}` : "", - middleName: item?.middleName ? `${item.middleName}` : "", - lastName: item?.lastName ? `${item.lastName}` : "", - groupId: item?.groupId ? `${item.groupId}` : "", - schoolId: item?.schoolId ? `${item.schoolId}` : "", - studentEmail: item?.studentEmail ? `${item.studentEmail}` : "", - studentPhoneNumber: item?.studentPhoneNumber - ? item.studentPhoneNumber - : "", - iscwsn: item?.iscwsn ? `${item.iscwsn}` : "", - gender: item?.gender ? `${item.gender}` : "", - socialCategory: item?.socialCategory ? `${item.socialCategory}` : "", - religion: item?.religion ? `${item.religion}` : "", - singleGirl: item?.singleGirl ? item.singleGirl : "", - weight: item?.weight ? `${item.weight}` : "", - height: item?.height ? `${item.height}` : "", - bloodGroup: item?.bloodGroup ? `${item.bloodGroup}` : "", - birthDate: item?.birthDate ? `${item.birthDate}` : "", - homeless: item?.homeless ? item.homeless : "", - bpl: item?.bpl ? item.bpl : "", - migrant: item?.migrant ? item.migrant : "", - status: item?.status ? `${item.status}` : "", - - fatherFirstName: item?.fatherFirstName ? `${item.fatherFirstName}` : "", - - fatherMiddleName: item?.fatherMiddleName - ? `${item.fatherMiddleName}` - : "", - - fatherLastName: item?.fatherLastName ? `${item.fatherLastName}` : "", - fatherPhoneNumber: item?.fatherPhoneNumber - ? item.fatherPhoneNumber - : "", - fatherEmail: item?.fatherEmail ? `${item.fatherEmail}` : "", - - motherFirstName: item?.motherFirstName ? `${item.motherFirstName}` : "", - motherMiddleName: item?.motherMiddleName - ? `${item.motherMiddleName}` - : "", - motherLastName: item?.motherLastName ? `${item.motherLastName}` : "", - motherPhoneNumber: item?.motherPhoneNumber - ? item.motherPhoneNumber - : "", - motherEmail: item?.motherEmail ? `${item.motherEmail}` : "", - - guardianFirstName: item?.guardianFirstName - ? `${item.guardianFirstName}` - : "", - guardianMiddleName: item?.guardianMiddleName - ? `${item.guardianMiddleName}` - : "", - guardianLastName: item?.guardianLastName - ? `${item.guardianLastName}` - : "", - guardianPhoneNumber: item?.guardianPhoneNumber - ? item.guardianPhoneNumber - : "", - guardianEmail: item?.guardianEmail ? `${item.guardianEmail}` : "", - image: item?.image ? `${item.image}` : "", - deactivationReason: item?.deactivationReason - ? `${item.deactivationReason}` - : "", - studentAddress: item?.studentAddress ? `${item.studentAddress}` : "", - village: item?.village ? `${item.village}` : "", - block: item?.block ? `${item.block}` : "", - district: item?.district ? `${item.district}` : "", - stateId: item?.stateId ? `${item.stateId}` : "", - pincode: item?.pincode ? item.pincode : "", - locationId: item?.locationId ? `${item.locationId}` : "", - metaData: item?.metaData ? item.metaData : [], - createdAt: item?.osCreatedAt ? `${item.osCreatedAt}` : "", - updatedAt: item?.osUpdatedAt ? `${item.osUpdatedAt}` : "", - createdBy: item?.osCreatedBy ? `${item.osCreatedBy}` : "", - updatedBy: item?.osUpdatedBy ? `${item.osUpdatedBy}` : "", - }; - return new StudentDto(studentMapping); - }); - - return studentResponse; - } -} diff --git a/src/adapters/sunbirdrc/subnbird.module.ts b/src/adapters/sunbirdrc/subnbird.module.ts deleted file mode 100644 index 004a7725..00000000 --- a/src/adapters/sunbirdrc/subnbird.module.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { HttpModule } from "@nestjs/axios"; -import { Module } from "@nestjs/common"; -import { AttendanceService } from "./attendance.adapter"; -import { SunbirdCommentService } from "./comment.adapter"; -import { SunbirdConfigService } from "./config.adapter"; -import { SunbirdGroupService } from "./group.adapter"; -import { SunbirdHolidayService } from "./holiday.adapter"; -import { SunbirdLikeService } from "./like.adapter"; -import { SchoolService } from "./school.adapter"; -import { StudentService } from "./student.adapter"; -import { UserService } from "./user.adapter"; - -@Module({ - imports: [HttpModule], - providers: [ - AttendanceService, - StudentService, - UserService, - SchoolService, - SunbirdGroupService, - SunbirdCommentService, - SunbirdConfigService, - SunbirdLikeService, - SunbirdHolidayService, - ], - exports: [ - AttendanceService, - StudentService, - UserService, - SchoolService, - SunbirdGroupService, - SunbirdCommentService, - SunbirdConfigService, - SunbirdLikeService, - SunbirdHolidayService, - ], -}) -export class SunbirdModule {} diff --git a/src/adapters/sunbirdrc/template.adapter.ts b/src/adapters/sunbirdrc/template.adapter.ts deleted file mode 100644 index 6645335f..00000000 --- a/src/adapters/sunbirdrc/template.adapter.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { Injectable, HttpException } from "@nestjs/common"; -import { HttpService } from "@nestjs/axios"; -import { AxiosResponse } from "axios"; -import { map } from "rxjs"; -import { SuccessResponse } from "src/success-response"; -import { catchError } from "rxjs/operators"; -import { ErrorResponse } from "src/error-response"; -import { TemplateProcessDto } from "src/template/dto/template-process.dto"; -import { TemplateCreateDto } from "src/template/dto/template-create.dto"; - -@Injectable() -export class TemplateService { - constructor(private httpService: HttpService) {} - url = process.env.TEMPLATERURL; - - public async createTemplate(request: any, templateDto: TemplateCreateDto) { - var axios = require("axios"); - - var config = { - method: "post", - url: this.url, - headers: { - Authorization: request.headers.authorization, - }, - data: templateDto, - }; - - const response = await axios(config); - const responseData = response.data; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: responseData, - }); - } - - public async getTemplate(id: any, request: any) { - return this.httpService - .get(`${this.url}${id}`, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (axiosResponse: AxiosResponse) => { - let data = [axiosResponse.data]; - - const templateDto = await this.templateCreate(data); - - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: templateDto[0], - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response?.status, - errorMessage: e.response?.data?.params?.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - public async processTemplate( - request: any, - templateProcessDto: TemplateProcessDto - ) { - var axios = require("axios"); - - var config = { - method: "post", - url: `${this.url}process`, - headers: { - Authorization: request.headers.authorization, - }, - data: templateProcessDto, - }; - - const response = await axios(config); - const responseData = response.data; - return new SuccessResponse({ - statusCode: 200, - message: "Ok.", - data: responseData, - }); - } - public async getTemplateByTag(tag: string, request: any) { - var axios = require("axios"); - - let config = { - method: "get", - url: `${this.url}search/tag?queryString=${tag}`, - }; - - const response = await axios(config); - const data = response?.data; - - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: data, - }); - } - - public async templateCreate(result: any) { - const templateCreateResponse = result.map((item: any) => { - const templateMapping = { - body: item?.body ? `${item.body}` : "", - type: item?.type ? `${item.type}` : "", - user: item?.user ? `${item.user}` : "", - tag: item?.tag, - createdAt: item?.createdAt ? `${item.createdAt}` : "", - updatedAt: item?.updatedAt ? `${item.updatedAt}` : "", - }; - return new TemplateCreateDto(templateMapping); - }); - - return templateCreateResponse; - } - - public async templateProcess(result: any) { - const templateProcessResponse = result.map((item: any) => { - const templateMapping = { - id: item?.id ? item.id : "", - data: item?.data ? item.data : "", - }; - return new TemplateCreateDto(templateMapping); - }); - - return templateProcessResponse; - } -} diff --git a/src/adapters/sunbirdrc/user.adapter.ts b/src/adapters/sunbirdrc/user.adapter.ts deleted file mode 100644 index e1016be5..00000000 --- a/src/adapters/sunbirdrc/user.adapter.ts +++ /dev/null @@ -1,294 +0,0 @@ -import { Injectable, HttpException } from "@nestjs/common"; -import { HttpService } from "@nestjs/axios"; -import { AxiosResponse } from "axios"; -import { map } from "rxjs"; -import { catchError } from "rxjs/operators"; -import { SuccessResponse } from "src/success-response"; -import { ErrorResponse } from "src/error-response"; -import { UserSearchDto } from "src/user/dto/user-search.dto"; -import { UserDto } from "../../user/dto/user.dto"; -import jwt_decode from "jwt-decode"; -import { IServicelocator } from "../userservicelocator"; -import { UserSegmentDto } from "src/user/dto/user-segment.dto"; -import { UserCreateDto } from "src/user/dto/user-create.dto"; -export const SunbirdUserToken = "SunbirdUser"; -@Injectable() -export class UserService implements IServicelocator { - constructor(private httpService: HttpService) {} - checkAndAddUser(request: any, userDto: UserCreateDto) { - throw new Error("Method not implemented."); - } - url = `${process.env.BASEAPIURL}/User`; - templaterURL = process.env.TEMPLATERURL; - - public async getUser(tenantId: string, id: any, request: any) { - return this.httpService - .get(`${this.url}/${id}`, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (axiosResponse: AxiosResponse) => { - let data = [axiosResponse.data]; - - const teacherResponse = await this.mappedResponse(data); - return new SuccessResponse({ - statusCode: 200, - message: "User found Successfully", - data: teacherResponse[0], - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response?.status, - errorMessage: e.response?.data?.params?.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async createUser(request: any, teacherDto: UserDto) { - return this.httpService - .post(`${this.url}`, teacherDto, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map((axiosResponse: AxiosResponse) => { - return new SuccessResponse({ - statusCode: 200, - message: "User created Successfully", - data: axiosResponse.data, - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response?.status, - errorMessage: e.response?.data?.params?.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async updateUser(id: string, request: any, teacherDto: UserDto) { - var axios = require("axios"); - var data = teacherDto; - - var config = { - method: "put", - url: `${this.url}/${id}`, - headers: { - Authorization: request.headers.authorization, - }, - data: data, - }; - const response = await axios(config); - return new SuccessResponse({ - statusCode: 200, - message: "User updated Successfully", - data: response.data, - }); - } - public async searchUser( - tenantId: string, - request: any, - teacherSearchDto: UserSearchDto - ) { - let userSearchData = teacherSearchDto; - - let searchData: any; - Object.keys(userSearchData).forEach((e) => { - if (userSearchData[e].userId) { - searchData = { - filters: { - osid: { - eq: `${userSearchData[e].userId.eq}`, - }, - ...userSearchData.filters, - }, - }; - - delete searchData.filters.userId; - } else { - searchData = userSearchData; - } - }); - - return this.httpService - .post(`${this.url}/search`, searchData, { - headers: { - Authorization: request.headers.authorization, - }, - }) - .pipe( - map(async (response) => { - const responsedata = response.data; - const teacherResponse = await this.mappedResponse(responsedata); - return new SuccessResponse({ - statusCode: response.status, - message: "Ok", - data: teacherResponse, - }); - }), - catchError((e) => { - var error = new ErrorResponse({ - errorCode: e.response.status, - errorMessage: e.response.data.params.errmsg, - }); - throw new HttpException(error, e.response.status); - }) - ); - } - - public async getUserByAuth(tenantId: string, request: any) { - const authToken = request.headers.authorization; - const decoded: any = jwt_decode(authToken); - let email = decoded.email; - - let axios = require("axios"); - let data = { - filters: { - email: { - eq: `${email}`, - }, - }, - }; - let config = { - method: "post", - url: `${this.url}/search`, - headers: { - Authorization: request.headers.authorization, - }, - data: data, - }; - const response = await axios(config); - let result = response?.data && response.data; - const teacherResponse = await this.mappedResponse(result); - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: teacherResponse, - }); - } - - public async teacherSegment( - schoolId: string, - templateId: string, - request: any - ) { - const teacherSearchDto = { - filters: { - schoolId: { - eq: `${schoolId}`, - }, - }, - }; - var axios = require("axios"); - var getTemplate = { - method: "get", - url: `${this.templaterURL}${templateId}`, - }; - const getfcmClickActionUrl = await axios(getTemplate); - const fcmClickActionUrlData = getfcmClickActionUrl.data; - let tagString = JSON.stringify(fcmClickActionUrlData.tag[0]); - var jsonToObj = JSON.parse(tagString); - let fcmClickActionUrl = JSON.parse(jsonToObj); - - let config = { - method: "post", - url: `${this.url}/search`, - - data: teacherSearchDto, - }; - const response = await axios(config); - let responseData = response.data.map((item: any) => { - const userSegment = { - fcmToken: item?.fcmToken ? `${item.fcmToken}` : "", - phoneNo: item?.phoneNumber ? `${item.phoneNumber}` : "", - name: item?.firstName ? `${item.firstName}` : "", - fcmClickActionUrl: item?.fcmClickActionUrl - ? `${item.fcmClickActionUrl}` - : "", - }; - return new UserSegmentDto(userSegment); - }); - - const teachersegment = responseData.map((obj: any) => { - return { ...obj, fcmClickActionUrl: fcmClickActionUrl.attendance }; - }); - - return new SuccessResponse({ - statusCode: 200, - message: "ok", - data: teachersegment, - }); - } - public async mappedResponse(result: any) { - const userResponse = result.map((item: any) => { - const userMapping = { - userId: item?.osid ? `${item.osid}` : "", - refId1: item?.refId1 ? `${item.refId1}` : "", - refId2: item?.refId2 ? `${item.refId2}` : "", - refId3: item?.refId3 ? `${item.refId3}` : "", - firstName: item?.firstName ? `${item.firstName}` : "", - middleName: item?.middleName ? `${item.middleName}` : "", - lastName: item?.lastName ? `${item.lastName}` : "", - phoneNumber: item?.phoneNumber ? `${item.phoneNumber}` : "", - email: item?.email ? `${item.email}` : "", - aadhaar: item?.aadhaar ? `${item.aadhaar}` : "", - gender: item?.gender ? `${item.gender}` : "", - socialCategory: item?.socialCategory ? `${item.socialCategory}` : "", - birthDate: item?.birthDate ? `${item.birthDate}` : "", - designation: item?.designation ? `${item.designation}` : "", - cadre: item?.cadre ? `${item.cadre}` : "", - profQualification: item?.profQualification - ? `${item.profQualification}` - : "", - joiningDate: item?.joiningDate ? `${item.joiningDate}` : "", - subjectIds: item.subjectIds ? item.subjectIds : [], - bloodGroup: item?.bloodGroup ? `${item.bloodGroup}` : "", - maritalStatus: item?.maritalStatus ? `${item.maritalStatus}` : "", - compSkills: item?.compSkills ? `${item.compSkills}` : "", - disability: item?.disability ? `${item.disability}` : "", - religion: item?.religion ? `${item.religion}` : "", - homeDistance: item?.homeDistance ? `${item.homeDistance}` : "", - employmentType: item?.employmentType ? `${item.employmentType}` : "", - schoolId: item?.schoolId ? `${item.schoolId}` : "", - address: item?.address ? `${item.address}` : "", - village: item?.village ? `${item.village}` : "", - block: item?.block ? `${item.block}` : "", - district: item?.district ? `${item.district}` : "", - stateId: item?.stateId ? `${item.stateId}` : "", - pincode: item?.pincode ? item.pincode : "", - locationId: item?.locationId ? `${item.locationId}` : "", - image: item?.image ? `${item.image}` : "", - status: item?.status ? `${item.status}` : "", - deactivationReason: item?.deactivationReason - ? `${item.deactivationReason}` - : "", - reportsTo: item?.reportsTo ? `${item.reportsTo}` : "", - retirementDate: item?.retirementDate ? `${item.retirementDate}` : "", - workingStatus: item?.workingStatus ? `${item.workingStatus}` : "", - fcmToken: item?.fcmToken ? `${item.fcmToken}` : "", - role: item?.role ? `${item.role}` : "", - employeeCode: item?.employeeCode ? `${item.employeeCode}` : "", - metaData: item?.metaData ? item.metaData : [], - createdAt: item?.osCreatedAt ? `${item.osCreatedAt}` : "", - updatedAt: item?.osUpdatedAt ? `${item.osUpdatedAt}` : "", - createdBy: item?.osCreatedBy ? `${item.osCreatedBy}` : "", - updatedBy: item?.osUpdatedBy ? `${item.osUpdatedBy}` : "", - }; - return new UserDto(userMapping); - }); - - return userResponse; - } - - resetUserPassword(request: any, username: string, newPassword: string) { - throw new Error("Method not implemented."); - } -} diff --git a/src/adapters/userservicelocator.ts b/src/adapters/userservicelocator.ts index be916987..aca47bb3 100644 --- a/src/adapters/userservicelocator.ts +++ b/src/adapters/userservicelocator.ts @@ -1,24 +1,30 @@ +import { Response } from "express"; import { UserCreateDto } from "src/user/dto/user-create.dto"; import { UserSearchDto } from "src/user/dto/user-search.dto"; -import { UserDto } from "src/user/dto/user.dto"; +import { UserData } from "src/user/user.controller"; export interface IServicelocator { - getUser( - tenantId: string, - id: any, - accessRole: string, - request: any, - response: any - ); - getUserByAuth(tenantId: string, request: any); - checkAndAddUser(request: any, userDto: UserCreateDto); - createUser(request: any, userDto: UserCreateDto); - updateUser(id: string, request: any, userDto: UserCreateDto); + // getUser( + // userId?:Record, + // response?: any, + // tenantId?: string, + // id?: any, + // accessRole?: string, + // request?: any, + // ); + getUsersDetailsById(userData: UserData, response:any); + getUsersDetailsByCohortId(userData: Record, response:any); + updateUser(userDto?: any,response?: any); + createUser(request: any, userDto: UserCreateDto, response: Response); + findUserDetails(userID:any,username:String) searchUser( tenantId: string, request: any, response: any, userSearchDto: UserSearchDto ); - resetUserPassword(request: any, username: string, newPassword: string); + resetUserPassword(request: any, username: string, newPassword: string, response: Response); + checkUser(body:any,response); + deleteUserById(userId: string, response: Response): Promise; + } diff --git a/src/adapters/usertenantmappinglocator.ts b/src/adapters/usertenantmappinglocator.ts new file mode 100644 index 00000000..adc30d46 --- /dev/null +++ b/src/adapters/usertenantmappinglocator.ts @@ -0,0 +1,5 @@ +import { Response } from "express"; +import { UserTenantMappingDto } from "src/userTenantMapping/dto/user-tenant-mapping.dto"; +export interface IServicelocatorAssignTenant { + userTenantMapping(request: any, assignTenantMappingDto: UserTenantMappingDto,response: Response); +} \ No newline at end of file diff --git a/src/adminForm/adminForm.controller.spec.ts b/src/adminForm/adminForm.controller.spec.ts deleted file mode 100644 index d82053df..00000000 --- a/src/adminForm/adminForm.controller.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from "@nestjs/testing"; -import { AdminFormController } from "./adminForm.controller"; - -describe("AdminFormController", () => { - let controller: AdminFormController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [AdminFormController], - }).compile(); - - controller = module.get(AdminFormController); - }); - - it("should be defined", () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/src/adminForm/adminForm.controller.ts b/src/adminForm/adminForm.controller.ts deleted file mode 100644 index 9cbc19b8..00000000 --- a/src/adminForm/adminForm.controller.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { AdminFormService } from "../adapters/sunbirdrc/adminForm.adapter"; - -import { - CacheInterceptor, - CACHE_MANAGER, - Inject, - Request, -} from "@nestjs/common"; -import { - ApiTags, - ApiBody, - ApiOkResponse, - ApiForbiddenResponse, - ApiCreatedResponse, - ApiBasicAuth, -} from "@nestjs/swagger"; -import { - Controller, - Get, - Post, - Body, - Put, - Patch, - Param, - UseInterceptors, - ClassSerializerInterceptor, - SerializeOptions, - Req, -} from "@nestjs/common"; -import { AdminFormDto } from "./dto/adminForm.dto"; -import { AdminFormSearchDto } from "./dto/adminForm-search.dto"; -@ApiTags("AdminForm") -@Controller("adminForm") -export class AdminFormController { - constructor( - private service: AdminFormService, - @Inject(CACHE_MANAGER) private cacheManager - ) {} - - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "AdminForm detail." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @SerializeOptions({ - strategy: "excludeAll", - }) - getAdminForm(@Param("id") adminFormId: string, @Req() request: Request) { - return this.service.getAdminForm(adminFormId, request); - } - - @Post() - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "AdminForm has been created successfully.", - }) - @ApiBody({ type: AdminFormDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async createAdminForm( - @Req() request: Request, - @Body() adminFormDto: AdminFormDto - ) { - return this.service.createAdminForm(request, adminFormDto); - } - - @Put("/:id") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "AdminForm has been updated successfully.", - }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async updateAdminForm( - @Param("id") id: string, - @Req() request: Request, - @Body() adminFormDto: AdminFormDto - ) { - return await this.service.updateAdminForm(id, request, adminFormDto); - } - - @Post("/search") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "AdminForm list." }) - @ApiBody({ type: AdminFormSearchDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async searchAdminForm( - @Req() request: Request, - @Body() adminFormSearchDto: AdminFormSearchDto - ) { - return await this.service.searchAdminForm(request, adminFormSearchDto); - } -} diff --git a/src/adminForm/adminForm.module.ts b/src/adminForm/adminForm.module.ts deleted file mode 100644 index 1aa9cfcb..00000000 --- a/src/adminForm/adminForm.module.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { CacheModule, Module } from "@nestjs/common"; -import { AdminFormController } from "./adminForm.controller"; -import { AdminFormService } from "../adapters/sunbirdrc/adminForm.adapter"; -import { HttpModule } from "@nestjs/axios"; -const ttl = process.env.TTL as never; -@Module({ - imports: [ - HttpModule, - CacheModule.register({ - ttl: ttl, - }), - ], - controllers: [AdminFormController], - providers: [AdminFormService], -}) -export class AdminFormModule {} diff --git a/src/adminForm/dto/adminForm-search.dto.ts b/src/adminForm/dto/adminForm-search.dto.ts deleted file mode 100644 index 41c69074..00000000 --- a/src/adminForm/dto/adminForm-search.dto.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Exclude, Expose } from "class-transformer"; -import { - MaxLength, - IsNotEmpty, - IsEmail, - IsString, - IsNumber, -} from "class-validator"; -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; - -export class AdminFormSearchDto { - @ApiProperty({ - type: String, - description: "Limit", - }) - limit: string; - - @ApiProperty({ - type: Object, - description: "Filters", - }) - @ApiPropertyOptional() - filters: object; - - constructor(partial: Partial) { - Object.assign(this, partial); - } -} diff --git a/src/adminForm/dto/adminForm.dto.ts b/src/adminForm/dto/adminForm.dto.ts deleted file mode 100644 index 856693d2..00000000 --- a/src/adminForm/dto/adminForm.dto.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; -import { Exclude, Expose } from "class-transformer"; - -export class AdminFormDto { - @Expose() - adminFormId: string; - @ApiProperty() - @Expose() - moduleId: string; - @ApiProperty() - @Expose() - formSchema: string; - - @Expose() - createdAt: string; - - @Expose() - updatedAt: string; - - @Expose() - createdBy: string; - - @Expose() - updatedBy: string; - - constructor(obj: any) { - Object.assign(this, obj); - } -} diff --git a/src/announcements/announcements.controller.spec.ts b/src/announcements/announcements.controller.spec.ts deleted file mode 100644 index 25f0980d..00000000 --- a/src/announcements/announcements.controller.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from "@nestjs/testing"; -import { AnnouncementsController } from "./announcements.controller"; - -describe("AnnouncementsController", () => { - let controller: AnnouncementsController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [AnnouncementsController], - }).compile(); - - controller = module.get(AnnouncementsController); - }); - - it("should be defined", () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/src/announcements/announcements.controller.ts b/src/announcements/announcements.controller.ts deleted file mode 100644 index 05b3f4ad..00000000 --- a/src/announcements/announcements.controller.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { - Body, - CacheInterceptor, - ClassSerializerInterceptor, - Controller, - Delete, - Get, - Inject, - Param, - Post, - Put, - Query, - Req, - Request, - SerializeOptions, - UseInterceptors, -} from "@nestjs/common"; -import { FileInterceptor } from "@nestjs/platform-express"; -import { - ApiBasicAuth, - ApiBody, - ApiConsumes, - ApiCreatedResponse, - ApiForbiddenResponse, - ApiOkResponse, -} from "@nestjs/swagger"; -import { IServicelocator } from "src/adapters/announcementsservicelocator"; -import { - AnnouncementsService, - AnnouncementsToken, -} from "src/adapters/hasura/announcements.adapter"; -import { AnnouncementsFilterDto } from "./dto/announcements-filter.dto"; -import { AnnouncementsDto } from "./dto/announcements.dto"; - -@Controller("announcements") -export class AnnouncementsController { - constructor( - private hasuraService: AnnouncementsService, - @Inject(AnnouncementsToken) private provider: IServicelocator - ) {} - - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Get announcement detail" }) - @ApiForbiddenResponse({ description: "Forbidden" }) - public async getAnnouncement( - @Param("id") announcementId: string, - @Req() request: Request - ) { - return this.hasuraService.getAnnouncement(announcementId, request); - } - - @Get("") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Get announcements" }) - @ApiForbiddenResponse({ description: "Forbidden" }) - public async getAnnouncementSet( - @Query() query: AnnouncementsFilterDto, - @Req() request: Request - ) { - return this.hasuraService.getAnnouncementSet(request, query); - } - - @Put("/:id") - @ApiConsumes("multipart/form-data") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Announcement has been Updated successfully.", - }) - @ApiBody({ type: AnnouncementsDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async updateAnnouncement( - @Param("id") announcementId: string, - @Req() request: Request, - @Body() announcementData: any - ) { - const updatedData = JSON.parse(announcementData?.data); - return this.hasuraService.updateAnnouncement( - announcementId, - request, - updatedData - ); - } - - @Post() - @ApiConsumes("multipart/form-data") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Announcement has been created successfully.", - }) - @ApiBody({ type: AnnouncementsDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - public async createAnnouncement( - @Req() request: Request, - @Body() announcementData: AnnouncementsDto - ) { - return this.hasuraService.createAnnouncement(request, announcementData); - } - - @Delete("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "Deleted the announcement " }) - public async deleteAnnouncement( - @Param("id") announcementId: string, - @Req() request: Request - ) { - return this.hasuraService.deleteAnnouncement(announcementId, request); - } -} diff --git a/src/announcements/announcements.module.ts b/src/announcements/announcements.module.ts deleted file mode 100644 index 8c052fcb..00000000 --- a/src/announcements/announcements.module.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { CacheModule, Module } from "@nestjs/common"; -import { HttpModule } from "@nestjs/axios"; -import { ScheduleModule } from "@nestjs/schedule"; -import { - AnnouncementsService, - AnnouncementsToken, -} from "../adapters/hasura/announcements.adapter"; -import { AnnouncementsController } from "./announcements.controller"; - -const ttl = process.env.TTL as never; -@Module({ - imports: [ - HttpModule, - CacheModule.register({ - ttl: ttl, - }), - ScheduleModule.forRoot(), - ], - providers: [ - AnnouncementsService, - { - provide: AnnouncementsToken, - useClass: AnnouncementsService, - }, - ], - controllers: [AnnouncementsController], -}) -export class AnnouncementsModule {} diff --git a/src/announcements/dto/announcements-filter.dto.ts b/src/announcements/dto/announcements-filter.dto.ts deleted file mode 100644 index d02a58f9..00000000 --- a/src/announcements/dto/announcements-filter.dto.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { Expose } from "class-transformer"; -import { ApiPropertyOptional } from "@nestjs/swagger"; - -export class AnnouncementsFilterDto { - @ApiPropertyOptional({ - type: String, - description: "Page size(limit) for the announcement set", - }) - @Expose() - pageSize: string; - - @ApiPropertyOptional({ - type: String, - description: "Page index(offset) for the announcement", - }) - @Expose() - pageIndex: string; - - @ApiPropertyOptional({ - type: String, - description: "The title of announcement", - default: "", - }) - @Expose() - title: string; - - @ApiPropertyOptional({ - type: Array, - description: "The author of announcement", - default: "", - }) - @Expose() - author: string[]; - - @ApiPropertyOptional({ - type: Boolean, - description: "Whether announcement is pinned or not", - default: false, - }) - @Expose() - isPinned: boolean; - - @ApiPropertyOptional({ - type: Object, - description: "Additional properties for pinned announcements", - default: {}, - }) - @Expose() - pinnedAnnouncementProperties: any; - - @ApiPropertyOptional({ - type: String, - description: "The status of the announcement-draft or published", - default: "", - }) - @Expose() - status: string; - - @ApiPropertyOptional({ - type: Array, - description: "The type of announcement", - default: "", - }) - @Expose() - announcementType: string[]; - - @ApiPropertyOptional({ - type: String, - description: "Start date for filtering announcement", - }) - @Expose() - startDate: string; - - @ApiPropertyOptional({ - type: String, - description: "End date for filtering announcement", - }) - @Expose() - endDate: string; - - constructor(partial: Partial) { - Object.assign(this, partial); - } -} diff --git a/src/announcements/dto/announcements.dto.ts b/src/announcements/dto/announcements.dto.ts deleted file mode 100644 index d1f334f7..00000000 --- a/src/announcements/dto/announcements.dto.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Expose } from "class-transformer"; -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; - -export class AnnouncementsDto { - @ApiProperty({ - type: String, - description: "The id of the announcement ", - default: "", - }) - @Expose() - announcementId: string; - - @ApiPropertyOptional({ - type: String, - description: "The data of the announcement", - default: "", - }) - @Expose() - data: string; - - @ApiProperty({ - type: String, - description: "The type of announcement", - default: "", - }) - @Expose() - announcementType: string; - - @ApiProperty({ - type: String, - description: "The title of announcement", - default: "", - }) - @Expose() - title: string; - - @ApiProperty({ - type: String, - description: "The date modified", - default: "", - }) - @Expose() - dateModified: string; - - @ApiPropertyOptional({ - type: Array, - description: "Additional properties of an announcement", - default: "", - }) - @Expose() - additionalTags: string[]; - - @ApiProperty({ - type: Boolean, - description: "Whether announcement is pinned or not", - default: false, - }) - @Expose() - isPinned: boolean; - - @ApiProperty({ - type: String, - description: "The status of the announcement-draft or published", - default: "", - }) - @Expose() - status: string; - - @ApiProperty({ - type: String, - description: "The author of the announcement", - default: "", - }) - @Expose() - author: string; - - @ApiPropertyOptional({ - type: Object, - description: "Additional properties for pinned announcements", - default: {}, - }) - @Expose() - pinnedAnnouncementProperties: any; - - constructor(obj: any) { - Object.assign(this, obj); - } -} diff --git a/src/app.controller.ts b/src/app.controller.ts index 42e665b1..3b70c990 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -1,12 +1,27 @@ -import { Controller, Get, Param, Res } from "@nestjs/common"; +import { + Controller, + Get, + Param, + Res, + UseGuards, + Headers, +} from "@nestjs/common"; import { AppService } from "./app.service"; +import { JwtAuthGuard } from "./common/guards/keycloak.guard"; +import { ApiBasicAuth, ApiHeader } from "@nestjs/swagger"; +import { RbacAuthGuard } from "./common/guards/rbac.guard"; @Controller() export class AppController { constructor(private readonly appService: AppService) {} @Get() - getHello(): string { + @UseGuards(RbacAuthGuard, JwtAuthGuard) + @ApiHeader({ + name: "rbac_token", + }) + @ApiBasicAuth("access-token") + getHello(@Headers() headers): object { return this.appService.getHello(); } diff --git a/src/app.module.ts b/src/app.module.ts index 34f9cefd..96f2222d 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,73 +1,41 @@ import { Module } from "@nestjs/common"; +import { ConfigModule } from "@nestjs/config"; import { AppController } from "./app.controller"; import { AppService } from "./app.service"; -import { UserModule } from "./user/user.module"; -import { SchoolModule } from "./school/school.module"; -import { AttendanceModule } from "./attendance/attendance.module"; -import { HolidayModule } from "./holiday/holiday.module"; +// import { MulterModule } from "@nestjs/platform-express/multer"; +// Below modules not in use for Shiksha 2.0 + +/* import { ConfigurationModule } from "./configs/configuration.module"; -import { ConfigModule } from "@nestjs/config"; -import { NotificationModule } from "./notification/notification.module"; -import { TemplateModule } from "./template/template.module"; -import { WorksheetModule } from "./worksheet/worksheet.module"; -import { MulterModule } from "@nestjs/platform-express/multer"; -import { QuestionModule } from "./Question/question.module"; -import { LessonPlanModule } from "./lessonPlan/lessonPlan.module"; -import { AdminFormModule } from "./adminForm/adminForm.module"; -import { LikeModule } from "./like/like.module"; -import { CommentModule } from "./comment/comment.module"; -import { TrackAssessmentModule } from "./trackAssessment/trackassessment.module"; -import { AssessmentSetModule } from "./assessmentset/assessmentset.module"; -import { InAppNotificationModule } from "./inAppNotification/inAppNotification.module"; -import { MentorTrackingModule } from "./mentorTracking/mentorTracking.module"; -import { MonitorTrackingModule } from "./monitorTracking/monitorTracking.module"; -import { CourseModule } from "./course/course.module"; -import { CourseTrackingModule } from "./courseTracking/courseTracking.module"; -import { AnnouncementsModule } from "./announcements/announcements.module"; -import { RoleModule } from "./role/role.module"; -import { WorkHistoryModule } from "./workHistory/workHistory.module"; +*/ +// In use for Shiksha 2.0 +import { DatabaseModule } from "./common/database.module"; +import { AuthModule } from "./auth/auth.module"; +import { AuthRbacModule } from "./authRbac/authRbac.module"; import { CohortModule } from "./cohort/cohort.module"; import { CohortMembersModule } from "./cohortMembers/cohortMembers.module"; import { FieldsModule } from "./fields/fields.module"; -import { AuthModule } from "./auth/auth.module"; -// Below modules no longer required in Shiksha 2.0 -// import { GroupModule } from "./group/group.module"; -// import { GroupMembershipModule } from "./groupMembership/groupMembership.module"; -// import { StudentModule } from "./student/student.module"; +import { AttendanceModule } from "./attendance/attendance.module"; +import { UserModule } from "./user/user.module"; +import { RbacModule } from "./rbac/rbac.module"; +import { AssignTenantModule } from './userTenantMapping/user-tenant-mapping.module'; @Module({ imports: [ - ConfigModule.forRoot(), - MulterModule.register({ - dest: "./uploads", - }), + RbacModule, + ConfigModule.forRoot({ isGlobal: true }), + // MulterModule.register({ + // dest: "./uploads", + // }), UserModule, - SchoolModule, - RoleModule, AttendanceModule, - HolidayModule, - ConfigurationModule, - TemplateModule, - NotificationModule, - WorksheetModule, - QuestionModule, - LessonPlanModule, - AdminFormModule, - LikeModule, - CommentModule, - TrackAssessmentModule, - AssessmentSetModule, - InAppNotificationModule, - MentorTrackingModule, - MonitorTrackingModule, - CourseModule, - CourseTrackingModule, - AnnouncementsModule, - WorkHistoryModule, CohortModule, CohortMembersModule, + AssignTenantModule, FieldsModule, - AuthModule + AuthModule, + AuthRbacModule, + DatabaseModule, ], controllers: [AppController], providers: [AppService], diff --git a/src/app.service.ts b/src/app.service.ts index 92f4b485..3743f6df 100644 --- a/src/app.service.ts +++ b/src/app.service.ts @@ -3,7 +3,7 @@ require("dotenv").config(); @Injectable() export class AppService { - getHello(): string { - return process.env.ADAPTERSOURCE; + getHello(): object { + return { msg: "Welcome to Shiksha Backend" }; } } diff --git a/src/assessmentset/assessmentset.controller.ts b/src/assessmentset/assessmentset.controller.ts deleted file mode 100644 index c6d91a9d..00000000 --- a/src/assessmentset/assessmentset.controller.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { - ApiTags, - ApiForbiddenResponse, - ApiCreatedResponse, - ApiBasicAuth, - ApiBody, - ApiQuery, -} from "@nestjs/swagger"; -import { - Controller, - Get, - Param, - UseInterceptors, - ClassSerializerInterceptor, - SerializeOptions, - Req, - Request, - CacheInterceptor, - Post, - Body, - Query, -} from "@nestjs/common"; -import { AssessmentSetSearchDto } from "./dto/assessmentset-search-dto"; -import { AssessmentsetDto } from "./dto/assessmentset.dto"; -import { AssessmentsetService } from "src/adapters/hasura/assessmentset.adapter"; - -@ApiTags("Assessmentset") -@Controller("assessmentset") -export class AssessmentsetController { - constructor(private service: AssessmentsetService) {} - - @Post("/assessmentset") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Assessment set has been created successfully.", - }) - @ApiBody({ type: AssessmentsetDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async createAssessmentSet( - @Req() request: Request, - @Body() assessmentsetDto: AssessmentsetDto - ) { - return this.service.createAssessmentSet(request, assessmentsetDto); - } - @Get("assessmentset/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Assessment set detail" }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async getAssessmentset( - @Param("id") assessmentsetId: string, - @Req() request: Request - ) { - return this.service.getAssessmentset(assessmentsetId, request); - } - - @Post("assessmentset/search") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Assessment set list." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @SerializeOptions({ - strategy: "excludeAll", - }) - @ApiQuery({ name: "limit", required: false }) - @ApiQuery({ name: "assessmentsetId", required: false }) - @ApiQuery({ name: "type", required: false }) - @ApiQuery({ name: "title", required: false }) - @ApiQuery({ name: "gradeType", required: false }) - public async searchAssessmentset( - @Req() request: Request, - @Query("limit") limit: string, - @Query("assessmentsetId") assessmentsetId: string, - @Query("type") type: string, - @Query("title") title: string, - @Query("gradeType") gradeType: string - ) { - return await this.service.searchAssessmentset( - limit, - assessmentsetId, - type, - title, - gradeType, - request - ); - } -} diff --git a/src/assessmentset/assessmentset.module.ts b/src/assessmentset/assessmentset.module.ts deleted file mode 100644 index 1e4f18af..00000000 --- a/src/assessmentset/assessmentset.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { CacheModule, Module } from "@nestjs/common"; -import { HttpModule } from "@nestjs/axios"; -import { ScheduleModule } from "@nestjs/schedule"; -import { AssessmentsetController } from "./assessmentset.controller"; -import { AssessmentsetService } from "src/adapters/hasura/assessmentset.adapter"; -const ttl = process.env.TTL as never; -@Module({ - imports: [ - HttpModule, - CacheModule.register({ - ttl: ttl, - }), - ScheduleModule.forRoot(), - ], - controllers: [AssessmentsetController], - providers: [AssessmentsetService], -}) -export class AssessmentSetModule {} diff --git a/src/assessmentset/dto/assessmentset-search-dto.ts b/src/assessmentset/dto/assessmentset-search-dto.ts deleted file mode 100644 index 5fa957d3..00000000 --- a/src/assessmentset/dto/assessmentset-search-dto.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; - -export class AssessmentSetSearchDto { - @ApiProperty({ - type: String, - description: "Limit", - }) - limit: string; - - @ApiProperty({ - type: Object, - description: "Filters", - }) - @ApiPropertyOptional() - filters: object; - - constructor(partial: Partial) { - Object.assign(this, partial); - } -} diff --git a/src/assessmentset/dto/assessmentset.dto.ts b/src/assessmentset/dto/assessmentset.dto.ts deleted file mode 100644 index a18acbee..00000000 --- a/src/assessmentset/dto/assessmentset.dto.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Expose } from "class-transformer"; -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; - -export class AssessmentsetDto { - @Expose() - id: string; - - @Expose() - assessmentsetId: string; - - @ApiProperty({ - description: "Assessment set title ", - }) - @Expose() - title: string; - - @ApiPropertyOptional({ - description: "Assessment set Type", - }) - @Expose() - type: string; - - @ApiProperty({ - description: "Assessmentset type, Ex - marks, boolean, grade", - }) - @Expose() - typeDetails: string; - - @ApiProperty({ - description: "Assessmentset type details", - }) - @Expose() - gradeType: string; - - @ApiProperty({ - description: "Extra data against assessment sent", - }) - @Expose() - options: string; - - @Expose() - createdAt: string; - - @Expose() - updatedAt: string; - - constructor(obj: any) { - Object.assign(this, obj); - } -} diff --git a/src/attendance/attendance.controller.ts b/src/attendance/attendance.controller.ts index 9bef3129..ca226b9a 100644 --- a/src/attendance/attendance.controller.ts +++ b/src/attendance/attendance.controller.ts @@ -6,8 +6,9 @@ import { ApiCreatedResponse, ApiBasicAuth, ApiConsumes, - ApiQuery, ApiHeader, + ApiBadRequestResponse, + ApiInternalServerErrorResponse, } from "@nestjs/swagger"; import { Controller, @@ -22,51 +23,55 @@ import { Req, Request, UploadedFile, - CacheInterceptor, - Query, Headers, + UsePipes, + ValidationPipe, + Res, + UseGuards, + HttpStatus, } from "@nestjs/common"; -import { AttendanceDto } from "./dto/attendance.dto"; +import { AttendanceDto, BulkAttendanceDTO } from "./dto/attendance.dto"; import { FileInterceptor } from "@nestjs/platform-express"; import { diskStorage } from "multer"; import { editFileName, imageFileFilter } from "./utils/file-upload.utils"; import { AttendanceSearchDto } from "./dto/attendance-search.dto"; -import { AttendanceHasuraService } from "src/adapters/hasura/attendance.adapter"; import { AttendaceAdapter } from "./attendanceadapter"; import { AttendanceDateDto } from "./dto/attendance-date.dto"; +import { AttendanceStatsDto } from "./dto/attendance-stats.dto"; +import { Response } from "express"; +import { JwtAuthGuard } from "src/common/guards/keycloak.guard"; @ApiTags("Attendance") @Controller("attendance") +@UseGuards(JwtAuthGuard) export class AttendanceController { constructor( - private service: AttendanceHasuraService, - private attendaceAdapter: AttendaceAdapter + private attendaceAdapter: AttendaceAdapter, ) {} - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Attendance detail" }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @SerializeOptions({ - strategy: "excludeAll", - }) - @ApiHeader({ - name: "tenantid", - }) - public async getAttendance( - @Headers() headers, - @Param("id") attendanceId: string, - @Req() request: Request - ) { - let tenantid = headers["tenantid"]; - return await this.attendaceAdapter - .buildAttenceAdapter() - .getAttendance(tenantid, attendanceId, request); - } + // @Get("/:id") + // @UseInterceptors(ClassSerializerInterceptor) + // @ApiBasicAuth("access-token") + // @ApiCreatedResponse({ description: "Attendance detail" }) + // @ApiForbiddenResponse({ description: "Forbidden" }) + // @SerializeOptions({ + // strategy: "excludeAll", + // }) + // @ApiHeader({ + // name: "tenantid", + // }) + // public async getAttendance( + // @Headers() headers, + // @Param("id") attendanceId: string, + // @Req() request: Request + // ) { + // let tenantid = headers["tenantid"]; + // return await this.attendaceAdapter + // .buildAttenceAdapter() + // .getAttendance(tenantid, attendanceId, request); + // } @Post() - @ApiConsumes("multipart/form-data") @ApiBasicAuth("access-token") @ApiCreatedResponse({ description: "Attendance has been created successfully.", @@ -81,138 +86,92 @@ export class AttendanceController { }) ) @ApiBody({ type: AttendanceDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) + // @UseInterceptors(ClassSerializerInterceptor) @ApiHeader({ - name: "tenantid", + name: "tenantid" }) + @UsePipes(ValidationPipe) public async createAttendace( @Headers() headers, - @Req() request: Request, + @Req() request, @Body() attendanceDto: AttendanceDto, + @Res() response: Response, @UploadedFile() image ) { attendanceDto.tenantId = headers["tenantid"]; attendanceDto.image = image?.filename; - return this.attendaceAdapter - .buildAttenceAdapter() - .checkAndAddAttendance(request, attendanceDto); + const result = await this.attendaceAdapter.buildAttenceAdapter().updateAttendanceRecord( + request.user.userId, + attendanceDto + ); + return response.status(result.statusCode).json(result); } - @Put("/:id") - @ApiConsumes("multipart/form-data") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Attendance has been Updated successfully.", - }) - @UseInterceptors( - FileInterceptor("image", { - storage: diskStorage({ - destination: "./uploads", - filename: editFileName, - }), - fileFilter: imageFileFilter, - }) - ) - @ApiBody({ type: AttendanceDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async updateAttendace( - @Param("id") attendanceId: string, - @Req() request: Request, - @Body() attendanceDto: AttendanceDto, - @UploadedFile() image - ) { - const response = { - image: image?.filename, - }; - Object.assign(attendanceDto, response); - return this.attendaceAdapter - .buildAttenceAdapter() - .updateAttendance(attendanceId, request, attendanceDto); - } - @Post("/search") + + + @Post("/list") @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Attendance list." }) + @ApiOkResponse({ description: "Attendance List" }) + @ApiBadRequestResponse({ description: "Bad Request" }) + @ApiInternalServerErrorResponse({ description: "Internal Server Error" }) @ApiBody({ type: AttendanceSearchDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) + // @UseInterceptors(ClassSerializerInterceptor) + @UsePipes(ValidationPipe) @SerializeOptions({ strategy: "excludeAll", }) @ApiHeader({ name: "tenantid", }) - public async searchAttendance( + public async searchAttendanceNew( @Headers() headers, @Req() request: Request, - @Body() studentSearchDto: AttendanceSearchDto + @Body() studentSearchDto: AttendanceSearchDto, + @Res() response: Response ) { let tenantid = headers["tenantid"]; - return this.attendaceAdapter - .buildAttenceAdapter() - .searchAttendance(tenantid, request, studentSearchDto); - } + if (!tenantid) { + return response.status(HttpStatus.BAD_REQUEST).json({ + statusCode: HttpStatus.BAD_REQUEST, + errorMessage: "tenantId is missing in headers", + }); + } - @Post("/bydate") - @UseInterceptors(ClassSerializerInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: " Ok." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @ApiHeader({ - name: "tenantid", - }) - public async attendanceFilter( - @Headers() headers, - @Req() request: Request, - @Body() attendanceDateDto: AttendanceDateDto - ) { - const tenantId = headers["tenantid"]; - return this.attendaceAdapter - .buildAttenceAdapter() - .attendanceByDate(tenantId, request, attendanceDateDto); + const result = await this.attendaceAdapter.buildAttenceAdapter().searchAttendance( + tenantid, + request, + studentSearchDto + ); + return response.status(result.statusCode).json(result); } + + @Post("bulkAttendance") @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Attendance has been created successfully.", - }) - @ApiBody({ type: [AttendanceDto] }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) + @ApiCreatedResponse({description: "Attendance has been created successfully."}) + @ApiBadRequestResponse({description: "Bad Request",}) + @ApiOkResponse({description: "Attendance updated successfully"}) + @ApiInternalServerErrorResponse({description: "Internal server error"}) + @ApiBody({ type: BulkAttendanceDTO }) @ApiHeader({ name: "tenantid", }) + @UsePipes(ValidationPipe) public async multipleAttendance( @Headers() headers, @Req() request: Request, - @Body() attendanceDtos: [AttendanceDto] + @Res() response: Response, + @Body() attendanceDtos: BulkAttendanceDTO ) { - let tenantid = headers["tenantid"]; - return this.attendaceAdapter - .buildAttenceAdapter() - .multipleAttendance(tenantid, request, attendanceDtos); + let tenantId = headers["tenantid"]; + const result = await this.attendaceAdapter.buildAttenceAdapter().multipleAttendance( + tenantId, + request, + attendanceDtos + ); + return response.status(result.statusCode).json(result); } - /** No longer required in Shiksha 2.0 */ - /* - @Get("usersegment/:attendance") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - // @ApiBasicAuth("access-token") - @ApiOkResponse({ description: " Ok." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @ApiQuery({ name: "groupId", required: false }) - @ApiQuery({ name: "date" }) - public async userSegment( - @Query("groupId") groupId: string, - @Param("attendance") attendance: string, - @Query("date") date: string, - @Req() request: Request - ) { - return await this.service.userSegment(groupId, attendance, date, request); - } - */ - } diff --git a/src/attendance/attendance.module.ts b/src/attendance/attendance.module.ts index 75ef9385..323c84e3 100644 --- a/src/attendance/attendance.module.ts +++ b/src/attendance/attendance.module.ts @@ -1,21 +1,21 @@ -import { CacheModule, Module } from "@nestjs/common"; +import { Module } from "@nestjs/common"; import { AttendanceController } from "./attendance.controller"; import { ScheduleModule } from "@nestjs/schedule"; import { AttendaceAdapter } from "./attendanceadapter"; import { HasuraModule } from "src/adapters/hasura/hasura.module"; -import { SunbirdModule } from "src/adapters/sunbirdrc/subnbird.module"; +import { TypeOrmModule } from "@nestjs/typeorm"; +import { AttendanceEntity } from "./entities/attendance.entity"; +import { Repository } from "typeorm"; +import { PostgresModule } from "src/adapters/postgres/potsgres-module"; -const ttl = process.env.TTL as never; @Module({ imports: [ - SunbirdModule, + TypeOrmModule.forFeature([AttendanceEntity]), HasuraModule, - CacheModule.register({ - ttl: ttl, - }), + PostgresModule, ScheduleModule.forRoot(), ], controllers: [AttendanceController], - providers: [AttendaceAdapter], + providers: [AttendaceAdapter, Repository], }) export class AttendanceModule {} diff --git a/src/attendance/attendanceadapter.ts b/src/attendance/attendanceadapter.ts index 1db4b36f..29f3a6e6 100644 --- a/src/attendance/attendanceadapter.ts +++ b/src/attendance/attendanceadapter.ts @@ -1,14 +1,11 @@ import { Inject, Injectable } from "@nestjs/common"; import { IServicelocator } from "src/adapters/attendanceservicelocator"; import { AttendanceHasuraService } from "src/adapters/hasura/attendance.adapter"; -import { AttendanceService } from "src/adapters/sunbirdrc/attendance.adapter"; +import { PostgresAttendanceService } from "src/adapters/postgres/attendance-adapter"; @Injectable() export class AttendaceAdapter { - constructor( - private hasuraProvider: AttendanceHasuraService, - private sunbirdProvider: AttendanceService - ) {} + constructor(private hasuraProvider: AttendanceHasuraService,private postgresProvider:PostgresAttendanceService) {} buildAttenceAdapter(): IServicelocator { let adapter: IServicelocator; @@ -16,8 +13,8 @@ export class AttendaceAdapter { case "hasura": adapter = this.hasuraProvider; break; - case "sunbird": - adapter = this.sunbirdProvider; + case "postgres": + adapter = this.postgresProvider; break; } return adapter; diff --git a/src/attendance/dto/attendance-date.dto.ts b/src/attendance/dto/attendance-date.dto.ts index 07edc8af..6b085991 100644 --- a/src/attendance/dto/attendance-date.dto.ts +++ b/src/attendance/dto/attendance-date.dto.ts @@ -1,17 +1,39 @@ import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; +import { IsDate, IsNotEmpty, Matches, Validate, ValidateIf, ValidationArguments, ValidatorConstraint, ValidatorConstraintInterface } from "class-validator"; +import { isAfter, isBefore, isSameDay } from "date-fns"; + + +@ValidatorConstraint({ name: 'isNotAfterFromDate', async: false }) +export class IsFromDateBeforeToDateConstraint implements ValidatorConstraintInterface { + validate(fromDate: Date, args: ValidationArguments) { + const toDate = args.object[args.constraints[0]]; + const res = isSameDay(fromDate, toDate) || isBefore(fromDate, toDate); + return res + } + + defaultMessage(args: ValidationArguments) { + return 'From Date must be before or equal to To Date'; + } +} + export class AttendanceDateDto { @ApiProperty({ - type: String, + type: Date, description: "From Date", }) - fromDate: string; + @IsNotEmpty() + @Matches(/^\d{4}-\d{2}-\d{2}$/, { message: 'Please provide a valid date in the format yyyy-mm-dd' }) + @Validate(IsFromDateBeforeToDateConstraint, ['toDate']) + fromDate: Date; @ApiProperty({ - type: String, + type: Date, description: "To Date", }) - toDate: string; + @Matches(/^\d{4}-\d{2}-\d{2}$/, { message: 'Please provide a valid date in the format yyyy-mm-dd' }) + @IsNotEmpty() + toDate: Date; @ApiProperty({ type: String, diff --git a/src/attendance/dto/attendance-search.dto.ts b/src/attendance/dto/attendance-search.dto.ts index 45c66fd5..8f4de7f8 100644 --- a/src/attendance/dto/attendance-search.dto.ts +++ b/src/attendance/dto/attendance-search.dto.ts @@ -1,11 +1,101 @@ -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; +import { ApiAcceptedResponse, ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; +import { ArrayMaxSize, ArrayMinSize, IsArray, IsEnum, IsNotEmpty, IsNumber, IsOptional, IsString, IsUUID, Matches, Validate, ValidateIf, ValidateNested, ValidationArguments, ValidatorConstraint, ValidatorConstraintInterface, isNotEmpty } from "class-validator"; +import { isBefore, isSameDay } from "date-fns"; +import { UserDto } from "src/user/dto/user.dto"; +import { AttendanceDto } from "./attendance.dto"; +import { CohortMembersDto } from "src/cohortMembers/dto/cohortMembers.dto"; +import { Type } from "class-transformer"; -export class AttendanceSearchDto { - @ApiProperty({ + +@ValidatorConstraint({ name: 'isNotAfterFromDate', async: false }) +export class IsFromDateBeforeToDateConstraint implements ValidatorConstraintInterface { + validate(fromDate: Date, args: ValidationArguments) { + const toDate = args.object[args.constraints[0]]; + const res = isSameDay(fromDate, toDate) || isBefore(fromDate, toDate); + return res + } + + defaultMessage(args: ValidationArguments) { + return 'From Date must be before or equal to To Date'; + } +} + + +enum SortDirection { + ASC = 'asc', + DESC = 'desc', +} + +export class AttendanceFiltersDto { + @ApiPropertyOptional({ default: "yyyy-mm-dd" }) + @IsOptional() + @Matches(/^\d{4}-\d{2}-\d{2}$/, { message: 'Please provide a valid date in the format yyyy-mm-dd' }) + @Validate(IsFromDateBeforeToDateConstraint, ['toDate']) + fromDate?: Date; + + @ApiPropertyOptional({ default: "yyyy-mm-dd" }) + @Matches(/^\d{4}-\d{2}-\d{2}$/, { message: 'Please provide a valid date in the format yyyy-mm-dd' }) + @IsOptional() + toDate?: Date; + + @ApiPropertyOptional() + @IsUUID() + @IsOptional() + contextId?: string + + @ApiPropertyOptional() + scope?: string + + @ApiPropertyOptional({ type: String, + description: "The date of the attendance in format yyyy-mm-dd", + default: "yyyy-mm-dd" + }) + @IsOptional() + @Matches(/^\d{4}-\d{2}-\d{2}$/, { message: 'Please provide a valid date in the format yyyy-mm-dd' }) + attendanceDate?: Date + + + @ApiPropertyOptional() + @IsUUID() + @IsOptional() + userId?: string + + // @ApiPropertyOptional() + + + [key: string]: any; // Allows additional dynamic keys +} + +@ValidatorConstraint({ name: "validDateRange", async: false }) +export class ValidDateRangeConstraint implements ValidatorConstraintInterface { + validate(value: any, args: ValidationArguments) { + const { toDate, fromDate } = value; + + // If either fromDate or toDate is not provided, validation passes + if (!fromDate || !toDate) { + return true; + } + + return isBefore(new Date(fromDate), new Date(toDate)) || isSameDay(new Date(fromDate), new Date(toDate)); + } + + defaultMessage(args: ValidationArguments) { + return "Invalid date range. 'toDate' must be after or equal to 'fromDate'."; + } +} + + + +export class AttendanceSearchDto { + @ApiPropertyOptional({ + type: Number, description: "Limit", }) - limit: string; + @IsOptional() + @IsNotEmpty() + @IsNumber({}, { message: 'Limit must be a number' }) + limit: number; @ApiProperty({ type: Number, @@ -13,12 +103,39 @@ export class AttendanceSearchDto { }) page: number; - @ApiProperty({ - type: Object, + @ApiPropertyOptional({ + type: AttendanceFiltersDto, description: "Filters", }) - @ApiPropertyOptional() - filters: object; + @ValidateNested({ each: true }) + @Type(() => AttendanceFiltersDto) + // @Validate(ValidDateRangeConstraint, { message: "Invalid date range." }) + filters: AttendanceFiltersDto + + + @ApiPropertyOptional({ + description: "Facets", + example: ["contextId", "userId", "scope"] + }) + facets?: string[]; + + @ApiPropertyOptional({ + description: "Sort", + example: ["attendanceDate", "asc"] + }) + @IsArray() + @IsOptional() + @ArrayMinSize(2, { message: 'Sort array must contain exactly two elements' }) + @ArrayMaxSize(2, { message: 'Sort array must contain exactly two elements' }) + sort: [string, string]; + + @ValidateIf((o) => o.sort !== undefined) + @IsEnum(SortDirection, { each: true, message: 'Sort[1] must be either asc or desc' }) + get sortDirection(): string | undefined { + return this.sort ? this.sort[1] : undefined; + } + + constructor(partial: Partial) { Object.assign(this, partial); diff --git a/src/attendance/dto/attendance-stats.dto.ts b/src/attendance/dto/attendance-stats.dto.ts new file mode 100644 index 00000000..8850fae4 --- /dev/null +++ b/src/attendance/dto/attendance-stats.dto.ts @@ -0,0 +1,128 @@ +import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; +import { Expose, Transform, Type } from "class-transformer"; +import { IsEnum, IsNotEmpty, IsNumberString, IsOptional, IsString, IsUUID, Matches, Validate, ValidateNested, ValidationArguments, ValidatorConstraint, ValidatorConstraintInterface } from 'class-validator'; +import { isBefore, isSameDay } from "date-fns"; +enum Order { + ASC = 'asc', + DESC = 'desc', +} + +@ValidatorConstraint({ name: 'isNotAfterFromDate', async: false }) +export class IsFromDateBeforeToDateConstraint implements ValidatorConstraintInterface { + validate(fromDate: Date, args: ValidationArguments) { + const toDate = args.object[args.constraints[0]]; + const res = isSameDay(fromDate, toDate) || isBefore(fromDate, toDate); + return res + } + + defaultMessage(args: ValidationArguments) { + return 'From Date must be before or equal to To Date'; + } +} + + + +class FiltersDto { + @IsEnum(Order,{ message: "nameOrder must be a valid enum value asc or desc" }) + @IsOptional() + @ApiPropertyOptional() + nameOrder: Order; + + @IsEnum(Order,{ message: "nameOrder must be a valid enum value asc or desc" }) + @IsOptional() + @ApiPropertyOptional() + percentageOrder: Order; + + @IsString() + @IsOptional() + @ApiPropertyOptional() + search: string; + + @IsString() + @IsOptional() + @ApiPropertyOptional() + userId: string; + + @ApiPropertyOptional({ + type: Date, + description: "From Date", + }) + @IsOptional() + @Matches(/^\d{4}-\d{2}-\d{2}$/, { message: 'Please provide a valid date in the format yyyy-mm-dd' }) + @Validate(IsFromDateBeforeToDateConstraint, ['toDate']) + fromDate: Date; + + @IsOptional() + @ApiProperty({ + type: Date, + description: "To Date", + }) + @Matches(/^\d{4}-\d{2}-\d{2}$/, { message: 'Please provide a valid date in the format yyyy-mm-dd' }) + + toDate: Date; + +} + +export class AttendanceStatsDto { + + @Expose() + name: string; + + @ApiProperty({ + type: String, + description: "The name of the person", + }) + @IsNotEmpty() + @IsUUID() + @Expose() + contextId: string; + + + @Expose() + attendance_percentage: string; + + + @Expose() + userId: string; + + + @Expose() + attendanceDate: Date; + + + @Expose() + attendance: string; + + + + @ApiProperty({ + type: Number, + description: "limit", + }) + @IsNotEmpty() + @Expose() + limit: number; + + @ApiPropertyOptional({ + type: Number, + description: "offset", + }) + @Expose() + offset: number; + + @ApiProperty({ + type: FiltersDto, + description: "Filters", + }) + @ApiPropertyOptional() + @ValidateNested({ each: true }) + @Type(() => FiltersDto) + + filters: FiltersDto + + constructor(obj: Partial) { + Object.assign(this, obj); + } +} + + diff --git a/src/attendance/dto/attendance.dto.ts b/src/attendance/dto/attendance.dto.ts index 85655e9e..6442f8dd 100644 --- a/src/attendance/dto/attendance.dto.ts +++ b/src/attendance/dto/attendance.dto.ts @@ -1,6 +1,37 @@ -import { Exclude, Expose } from "class-transformer"; +import { ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm'; +import { IsDate, IsDateString, IsDefined, IsEnum, IsUUID, Matches, Validate, ValidateIf, ValidateNested, ValidationArguments, ValidatorConstraint, ValidatorConstraintInterface } from 'class-validator'; +import { Exclude, Expose, Transform, Type } from "class-transformer"; import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; +import { IsNotEmpty, IsString, IsObject } from 'class-validator'; +import { User } from 'src/user/entities/user-entity'; +import { addHours, format, isAfter, isBefore, isValid } from 'date-fns'; // Import isAfter function from date-fns +import { HttpException, HttpStatus } from '@nestjs/common'; +import { Cohort } from 'src/cohort/entities/cohort.entity'; + +//for student valid enum are[present,absent] +//for teacher valid enum are[present,on-leave,half-day] +enum Attendance { + present = "present", + absent = "absent", + onLeave = "on-leave" +} + +enum Scope { + self = 'self', + student = 'student', +} +@ValidatorConstraint({ name: 'isNotAfterToday', async: false }) +export class IsNotAfterToday implements ValidatorConstraintInterface { + validate(date: Date, args: ValidationArguments) { + const currentDateIST = addHours(new Date(), 5.5); + return isBefore(date, currentDateIST); + } + + defaultMessage(args: ValidationArguments) { + return 'Attendance date must not be after today'; + } +} export class AttendanceDto { @Expose() attendanceId: string; @@ -13,16 +44,28 @@ export class AttendanceDto { description: "The userid of the attendance", default: "", }) + @IsDefined() + @IsNotEmpty() + @IsUUID() @Expose() userId: string; + @ManyToOne(() => User, { nullable: true }) + @JoinColumn({ name: 'userId' }) + user: User; + @ApiProperty({ type: String, description: "The date of the attendance in format yyyy-mm-dd", - default: new Date(), + default: new Date() + }) + @IsNotEmpty() + @Matches(/^\d{4}-\d{2}-\d{2}$/, { message: 'Please provide a valid date in the format yyyy-mm-dd' }) + @Validate(IsNotAfterToday, { + message: 'Attendance date must not be after today', }) @Expose() - attendanceDate: string; + attendanceDate: Date; @ApiProperty({ type: String, @@ -30,41 +73,26 @@ export class AttendanceDto { default: "", }) @Expose() + @IsNotEmpty() + @IsEnum(Attendance, { message: "Please enter valid enum values for attendance [present, absent,on-leave]" }) attendance: string; - @ApiProperty({ - type: String, - description: "The remark of the attendance", - default: "", - }) + @Expose() @ApiPropertyOptional() remark: string; - @ApiProperty({ - type: String, - description: "The latitude of the attendance", - default: 0, - }) + @Expose() @ApiPropertyOptional() - latitude: Number; + latitude: number; - @ApiProperty({ - type: String, - description: "The longitude of the attendance", - default: 0, - }) + @Expose() @ApiPropertyOptional() - longitude: Number; + longitude: number; - @ApiProperty({ - type: "string", - format: "binary", - description: "The image of person", - default: "NA", - }) + @Expose() @ApiPropertyOptional() image: string; @@ -89,17 +117,27 @@ export class AttendanceDto { @Expose() contextType: string; - @ApiPropertyOptional({ + @ApiProperty({ type: String, description: "The contextId of the attendance", default: "", }) + @IsNotEmpty() + @IsUUID() @Expose() + @IsDefined() contextId: string; + @ManyToOne(() => Cohort, { nullable: true }) // Define the ManyToOne relationship with Cohort entity + @JoinColumn({ name: "contextId", referencedColumnName: "cohortId" }) // Map contextId to cohortId column in Cohort table + cohort: Cohort; + + + @CreateDateColumn() @Expose() createdAt: string; + @UpdateDateColumn() @Expose() updatedAt: string; @@ -109,6 +147,107 @@ export class AttendanceDto { @Expose() updatedBy: string; + @ApiPropertyOptional() + @ValidateIf(o => o.scope !== undefined && o.scope !== null) @IsEnum(Scope, { message: "Please enter valid enum values for scope [self, student]" }) + scope: string + + constructor(obj: any) { + Object.assign(this, obj); + } +} + + +export class UserAttendanceDTO { + @IsUUID() + @IsNotEmpty() + @ApiProperty() + @IsUUID() + userId: string; + + @ApiProperty({ + type: String, + description: "The attendance of the attendance", + default: "", + }) + @Expose() + @IsNotEmpty() + @IsEnum(Attendance, { message: "Please enter valid enum values for attendance [present, absent,on-leave, half-day]" }) + attendance: string; + + + @Expose() + @ApiPropertyOptional() + remark: string; + + @Expose() + @ApiPropertyOptional() + latitude: number; + + @Expose() + @ApiPropertyOptional() + longitude: number; + + + @Expose() + @ApiPropertyOptional() + image: string; + + @ApiPropertyOptional() + @Expose() + metaData: string; + + @ApiPropertyOptional() + @Expose() + syncTime: string; + + @ApiPropertyOptional() + @Expose() + session: string; + + @ApiPropertyOptional({ + type: String, + description: "The contextType of the attendance", + default: "", + }) + @Expose() + contextType: string; +} + +export class BulkAttendanceDTO { + @ApiProperty({ + type: String, + description: "The date of the attendance in format yyyy-mm-dd", + }) + @IsNotEmpty() + @Matches(/^\d{4}-\d{2}-\d{2}$/, { message: 'Please provide a valid date in the format yyyy-mm-dd' }) + @Validate(IsNotAfterToday, { + message: 'Attendance date must not be after today', + }) + @Expose() + attendanceDate: Date; + + @IsUUID() + @Expose() + @IsNotEmpty() + @ApiProperty() + + contextId: string; + + @ApiPropertyOptional() + @ValidateIf(o => o.scope !== undefined && o.scope !== null) + @IsEnum(Scope, { message: "Please enter valid enum values for scope [self, student]" }) + scope: string + + + @ApiProperty({ + type: [UserAttendanceDTO], // Specify the type of userAttendance as an array of UserAttendanceDTO + description: 'List of user attendance details', + }) + @ValidateNested({ each: true }) + @Type(() => UserAttendanceDTO) + // Adjust the max size according to your requirements + userAttendance: UserAttendanceDTO[]; + constructor(obj: any) { Object.assign(this, obj); } diff --git a/src/attendance/entities/attendance.entity.ts b/src/attendance/entities/attendance.entity.ts new file mode 100644 index 00000000..82c75827 --- /dev/null +++ b/src/attendance/entities/attendance.entity.ts @@ -0,0 +1,72 @@ +import { Cohort } from 'src/cohort/entities/cohort.entity'; +import { User } from 'src/user/entities/user-entity'; +import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm'; + +@Entity({name:"Attendance"}) +export class AttendanceEntity { + @PrimaryGeneratedColumn('uuid') + attendanceId: string; + + @Column() + tenantId: string; + + @Column() + userId: string; + + @ManyToOne(() => User, {nullable:true}) + @JoinColumn({ name: 'userId' }) + user: User; + + @Column({ type: 'date' }) + attendanceDate: Date; + + @Column() + attendance: string; + + @Column({ nullable: true }) + remark: string; + + @Column({ type: 'numeric', nullable: true }) + latitude: number; + + @Column({ type: 'numeric', nullable: true }) + longitude: number; + + @Column({ nullable: true }) + image: string; + + @Column({ nullable: true }) + metaData: string; + + @Column({ nullable: true }) + syncTime: string; + + @Column({ nullable: true }) + session: string; + + @Column({ nullable: true }) + contextType: string; + + @Column({ nullable: true }) + contextId: string; + + // @Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' }) + @CreateDateColumn({ type: "timestamp with time zone", default: () => "CURRENT_TIMESTAMP" }) + createdAt: Date; + + @UpdateDateColumn({ type: "timestamp with time zone", default: () => "CURRENT_TIMESTAMP" }) + updatedAt: Date; + + @Column() + createdBy: string; + + @Column() + updatedBy: string; + + @Column() + scope: string; + + constructor(obj: Partial) { + Object.assign(this, obj); + } +} diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index a8821917..e564b26d 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -3,45 +3,82 @@ import { ApiBody, ApiForbiddenResponse, ApiHeader, + ApiBasicAuth, + ApiOkResponse, } from "@nestjs/swagger"; import { Controller, Get, Post, Body, - Put, - Param, - UseInterceptors, - ClassSerializerInterceptor, SerializeOptions, Req, Res, - Request, - Response, - Headers, + HttpStatus, + HttpCode, + UsePipes, + ValidationPipe, + UseGuards, + UseFilters, } from "@nestjs/common"; -import { HasuraAuthService } from "src/adapters/auth/auth.adapter"; -import { AuthDto } from "./dto/auth-dto"; +import { + AuthDto, + RefreshTokenRequestBody, + LogoutRequestBody, +} from "./dto/auth-dto"; +import { AuthService } from "./auth.service"; +import { JwtAuthGuard } from "src/common/guards/keycloak.guard"; +import { APIID } from "src/common/utils/api-id.config"; +import { AllExceptionsFilter } from "src/common/filters/exception.filter"; +import { Response } from "express"; @ApiTags("Auth") @Controller("auth") export class AuthController { - constructor( - private authService: HasuraAuthService - ) {} + constructor(private authService: AuthService) {} + @UseFilters(new AllExceptionsFilter(APIID.LOGIN)) @Post("/login") @ApiBody({ type: AuthDto }) + @UsePipes(ValidationPipe) + @HttpCode(HttpStatus.OK) + @ApiForbiddenResponse({ description: "Forbidden" }) + public async login(@Body() authDto: AuthDto, @Res() response: Response) { + return this.authService.login(authDto,response); + } + + @UseFilters(new AllExceptionsFilter(APIID.USER_AUTH)) + @Get("/") + @UseGuards(JwtAuthGuard) + @ApiBasicAuth("access-token") + @ApiOkResponse({ description: "User detail." }) @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) @SerializeOptions({ strategy: "excludeAll", }) - public async login( - @Req() request: Request, - @Res() response: Response, - @Body() authDto: AuthDto - ) { - return this.authService.login(request, response, authDto); + public async getUserByAuth(@Req() request, @Res() response: Response) { + return this.authService.getUserByAuth(request, response); + } + + @UseFilters(new AllExceptionsFilter(APIID.REFRESH)) + @Post("/refresh") + @HttpCode(HttpStatus.OK) + @ApiBody({ type: RefreshTokenRequestBody }) + @UsePipes(ValidationPipe) + refreshToken(@Body() body: RefreshTokenRequestBody, @Res() response: Response) { + const { refresh_token: refreshToken } = body; + + return this.authService.refreshToken(refreshToken,response); + } + + @UseFilters(new AllExceptionsFilter(APIID.LOGOUT)) + @Post("/logout") + @UsePipes(ValidationPipe) + @HttpCode(HttpStatus.OK) + @ApiBody({ type: LogoutRequestBody }) + async logout(@Body() body: LogoutRequestBody, @Res() response: Response) { + const { refresh_token: refreshToken } = body; + + await this.authService.logout(refreshToken,response); } } diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts index d64776fe..8ff41fbb 100644 --- a/src/auth/auth.module.ts +++ b/src/auth/auth.module.ts @@ -1,17 +1,36 @@ -import { CacheModule, Module } from "@nestjs/common"; +import { Module } from "@nestjs/common"; import { HttpModule } from "@nestjs/axios"; import { AuthController } from "./auth.controller"; -import { HasuraAuthService } from "src/adapters/auth/auth.adapter"; +import { AuthService } from "./auth.service"; +import { JwtStrategy } from "src/common/guards/keycloak.strategy"; +import { RbacJwtStrategy } from "src/common/guards/rbac.strategy"; +import { UserAdapter } from "../user/useradapter"; +import { TypeOrmModule } from "@nestjs/typeorm"; +import { User } from "../user/entities/user-entity"; +import { FieldValues } from "../user/entities/field-value-entities"; +import { Field } from "src/user/entities/field-entity"; +import { CohortMembers } from "src/cohortMembers/entities/cohort-member.entity"; +import { KeycloakService } from "src/common/utils/keycloak.service"; +import { HasuraUserService } from "src/adapters/hasura/user.adapter"; +import { PostgresUserService } from "src/adapters/postgres/user-adapter"; +import { FieldsService } from "src/adapters/hasura/services/fields.service"; +import { HasuraModule } from "src/adapters/hasura/hasura.module"; +import { PostgresModule } from "src/adapters/postgres/potsgres-module"; -const ttl = process.env.TTL as never; @Module({ imports: [ + TypeOrmModule.forFeature([User, FieldValues, Field, CohortMembers]), HttpModule, - CacheModule.register({ - ttl: ttl, - }), + HasuraModule, + PostgresModule, ], controllers: [AuthController], - providers: [HasuraAuthService], + providers: [ + AuthService, + JwtStrategy, + RbacJwtStrategy, + KeycloakService, + UserAdapter, + ], }) export class AuthModule {} diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts new file mode 100644 index 00000000..6022aad8 --- /dev/null +++ b/src/auth/auth.service.ts @@ -0,0 +1,106 @@ +import { HttpStatus, Injectable, NotFoundException, UnauthorizedException } from "@nestjs/common"; +import { UserAdapter } from "src/user/useradapter"; +import axios from "axios"; +import jwt_decode from "jwt-decode"; +import APIResponse from "src/common/responses/response"; +import { KeycloakService } from "src/common/utils/keycloak.service"; +import { APIID } from "src/common/utils/api-id.config"; +import { Response } from "express"; + + +type LoginResponse = { + access_token: string; + refresh_token: string; + expires_in: number; + refresh_expires_in: number; +}; +@Injectable() +export class AuthService { + + constructor( + private readonly useradapter: UserAdapter, + private readonly keycloakService: KeycloakService + ) {} + + async login(authDto,response: Response) { + const apiId = APIID.LOGIN; + const { username, password } = authDto; + try { + const { + access_token, + expires_in, + refresh_token, + refresh_expires_in, + token_type + } = await this.keycloakService.login(username, password); + + const res = { + access_token, + refresh_token, + expires_in, + refresh_expires_in, + token_type + }; + + return APIResponse.success(response, apiId, res, + HttpStatus.OK, "Auth Token fetched Successfully.") + } catch (error) { + if (error.response && error.response.status === 401) { + throw new NotFoundException("Invalid username or password"); + } else { + const errorMessage = error?.message || 'Something went wrong'; + return APIResponse.error(response, apiId, "Internal Server Error", `Error : ${errorMessage}`, HttpStatus.INTERNAL_SERVER_ERROR) + } + } + } + + + + public async getUserByAuth(request: any, response:Response) { + let apiId = APIID.USER_AUTH; + try { + const decoded: any = jwt_decode(request.headers.authorization); + const username = decoded.preferred_username; + const data = await this.useradapter.buildUserAdapter().findUserDetails(null, username); + + return APIResponse.success(response, apiId, data, + HttpStatus.OK, "User fetched by auth token Successfully.") + } catch (e) { + const errorMessage = e?.message || 'Something went wrong'; + return APIResponse.error(response, apiId, "Internal Server Error", `Error : ${errorMessage}`, HttpStatus.INTERNAL_SERVER_ERROR) + } + } + + async refreshToken(refreshToken: string, response: Response): Promise { + const apiId = APIID.REFRESH; + const { access_token, expires_in, refresh_token, refresh_expires_in } = + await this.keycloakService.refreshToken(refreshToken).catch(() => { + throw new UnauthorizedException(); + }); + + const res = { + access_token, + refresh_token, + expires_in, + refresh_expires_in, + }; + return APIResponse.success(response, apiId, res, + HttpStatus.OK, "Refresh Token fetched Successfully.") + } + + async logout(refreshToken: string, response: Response) { + const apiId = APIID.LOGOUT; + try { + const logout = await this.keycloakService.logout(refreshToken); + return APIResponse.success(response, apiId, logout, + HttpStatus.OK, "Logged Out Successfully.") + } catch (error) { + if (error.response && error.response.status === 400) { + throw new UnauthorizedException(); + } else { + const errorMessage = error?.message || 'Something went wrong'; + return APIResponse.error(response, apiId, "Internal Server Error", `Error : ${errorMessage}`, HttpStatus.INTERNAL_SERVER_ERROR) + } + } + } +} diff --git a/src/auth/dto/auth-dto.ts b/src/auth/dto/auth-dto.ts index 0d998644..f6bbaebf 100644 --- a/src/auth/dto/auth-dto.ts +++ b/src/auth/dto/auth-dto.ts @@ -1,19 +1,52 @@ import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; +import { IsString, IsNotEmpty } from "class-validator"; export class AuthDto { @ApiProperty({ type: String, description: "username", }) + @IsString() + @IsNotEmpty() username: string; @ApiProperty({ type: String, description: "password", }) + @IsString() + @IsNotEmpty() password: string; - constructor(partial: Partial) { + constructor(partial: AuthDto) { + Object.assign(this, partial); + } +} + +export class RefreshTokenRequestBody { + @ApiProperty({ + type: String, + description: "token", + }) + @IsString() + @IsNotEmpty() + refresh_token: string; + + constructor(partial: RefreshTokenRequestBody) { + Object.assign(this, partial); + } +} + +export class LogoutRequestBody { + @ApiProperty({ + type: String, + description: "token", + }) + @IsString() + @IsNotEmpty() + refresh_token: string; + + constructor(partial: LogoutRequestBody) { Object.assign(this, partial); } } diff --git a/src/authRbac/authRbac.controller.ts b/src/authRbac/authRbac.controller.ts new file mode 100644 index 00000000..04568afc --- /dev/null +++ b/src/authRbac/authRbac.controller.ts @@ -0,0 +1,36 @@ +import { + Controller, + HttpCode, + HttpStatus, + Get, + UseGuards, + Req, + BadRequestException, +} from "@nestjs/common"; +import { AuthRbacService } from "./authRbac.service"; +import { ApiBasicAuth, ApiHeader, ApiTags } from "@nestjs/swagger"; +import { JwtAuthGuard } from "src/common/guards/keycloak.guard"; +import { isUUID } from "class-validator"; + +@ApiTags("AuthRbac") +@Controller("auth/rbac") +export class AuthRbacController { + constructor(private authService: AuthRbacService) {} + + @HttpCode(HttpStatus.OK) + @Get("/token") + @ApiHeader({ + name: "tenantid", + required: true, + description: "Tenant Id", + }) + @ApiBasicAuth("access-token") + @UseGuards(JwtAuthGuard) + signInRbac(@Req() req) { + const tenantId = req.headers["tenantid"]; + if (!isUUID(tenantId)) { + throw new BadRequestException("Please add valid Tenant ID"); + } + return this.authService.signInRbac(req.user.username, tenantId); + } +} diff --git a/src/authRbac/authRbac.module.ts b/src/authRbac/authRbac.module.ts new file mode 100644 index 00000000..f939f703 --- /dev/null +++ b/src/authRbac/authRbac.module.ts @@ -0,0 +1,35 @@ +import { Module } from "@nestjs/common"; +import { JwtModule } from "@nestjs/jwt"; +import { ConfigModule, ConfigService } from "@nestjs/config"; +import { AuthRbacService } from "./authRbac.service"; +import { AuthRbacController } from "./authRbac.controller"; +import { UserAdapter } from "src/user/useradapter"; +import { HasuraModule } from "src/adapters/hasura/hasura.module"; +import { PostgresModule } from "src/adapters/postgres/potsgres-module"; +import { PostgresRoleService } from "src/adapters/postgres/rbac/role-adapter"; +import { Role } from "src/rbac/role/entities/role.entity"; +import { TypeOrmModule } from "@nestjs/typeorm"; +import { UserRoleMapping } from "src/rbac/assign-role/entities/assign-role.entity"; +import { RolePrivilegeMapping } from "src/rbac/assign-privilege/entities/assign-privilege.entity"; + +@Module({ + imports: [ + TypeOrmModule.forFeature([Role, UserRoleMapping, RolePrivilegeMapping]), + JwtModule.registerAsync({ + imports: [ConfigModule], + useFactory: async (configService: ConfigService) => ({ + global: true, + secret: configService.get("RBAC_JWT_SECRET"), + signOptions: { + expiresIn: configService.get("RBAC_JWT_EXPIRES_IN"), + }, + }), + inject: [ConfigService], + }), + HasuraModule, + PostgresModule, + ], + providers: [AuthRbacService, UserAdapter, PostgresRoleService], + controllers: [AuthRbacController], +}) +export class AuthRbacModule {} diff --git a/src/authRbac/authRbac.service.ts b/src/authRbac/authRbac.service.ts new file mode 100644 index 00000000..f758bb10 --- /dev/null +++ b/src/authRbac/authRbac.service.ts @@ -0,0 +1,88 @@ +import { + BadRequestException, + Injectable, + UnauthorizedException, +} from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { JwtService } from "@nestjs/jwt"; +import { PostgresRoleService } from "src/adapters/postgres/rbac/role-adapter"; +import { UserAdapter } from "src/user/useradapter"; + +@Injectable() +export class AuthRbacService { + issuer: string; + audience: string; + jwt_expires_In: any; + jwt_secret: any; + constructor( + private jwtService: JwtService, + private configService: ConfigService, + private readonly userAdapter: UserAdapter, + private readonly postgresRoleService: PostgresRoleService + ) { + this.issuer = this.configService.get("ISSUER"); + this.audience = this.configService.get("AUDIENCE"); + this.jwt_expires_In = this.configService.get("RBAC_JWT_EXPIRES_IN"); + this.jwt_secret = this.configService.get("RBAC_JWT_SECRET"); + } + + async generateToken(payload) { + const plainObject = JSON.parse(JSON.stringify(payload)); + const token = await this.jwtService.signAsync(plainObject, { + secret: this.jwt_secret, + expiresIn: this.jwt_expires_In, + }); + return token; + } + + async signInRbac(username: string, tenantId: string): Promise { + let userData = await this.userAdapter + .buildUserAdapter() + .findUserDetails(null, username); + + if (!userData || !tenantId) { + throw new BadRequestException( + "User details or tenant not found for user" + ); + } + + const userRoles = await this.postgresRoleService.findUserRoleData( + userData?.userId, + tenantId + ); + + if (!userRoles?.length) { + throw new BadRequestException("Roles not found for user"); + } + + userData["roles"] = userRoles.map(({ code }) => code); + userData["privileges"] = await this.getPrivileges(userRoles); + userData["tenantId"] = tenantId; + + const issuer = this.issuer; + const audience = this.audience; + + const payload = { + userData, + iss: issuer, + aud: audience, + }; + + return { + access_token: await this.generateToken(payload), + }; + } + + async getPrivileges(userRoleData) { + const roleIds = userRoleData.map(({ roleid }) => roleid); + if (!roleIds.length) { + return []; + } + const privilegesData = await this.postgresRoleService.findPrivilegeByRoleId( + roleIds + ); + + const privileges = privilegesData.map(({ code }) => code); + return privileges; + } +} diff --git a/src/cohort/cohort.controller.ts b/src/cohort/cohort.controller.ts index 71b2afcb..0907e458 100644 --- a/src/cohort/cohort.controller.ts +++ b/src/cohort/cohort.controller.ts @@ -1,12 +1,15 @@ import { ApiTags, ApiBody, - ApiOkResponse, - ApiForbiddenResponse, ApiCreatedResponse, ApiBasicAuth, ApiConsumes, ApiHeader, + ApiBadRequestResponse, + ApiInternalServerErrorResponse, + ApiOkResponse, + ApiNotFoundResponse, + ApiConflictResponse, } from "@nestjs/swagger"; import { Controller, @@ -16,36 +19,66 @@ import { Put, Param, UseInterceptors, - ClassSerializerInterceptor, SerializeOptions, Req, - Query, - CacheInterceptor, UploadedFile, Res, Headers, + Delete, + UseGuards, + ValidationPipe, + UsePipes, + BadRequestException, + UseFilters, } from "@nestjs/common"; import { CohortSearchDto } from "./dto/cohort-search.dto"; import { Request } from "@nestjs/common"; -import { CohortDto } from "./dto/cohort.dto"; import { FileInterceptor } from "@nestjs/platform-express"; import { editFileName, imageFileFilter } from "./utils/file-upload.utils"; import { diskStorage } from "multer"; import { Response } from "express"; - import { CohortAdapter } from "./cohortadapter"; import { CohortCreateDto } from "./dto/cohort-create.dto"; +import { CohortUpdateDto } from "./dto/cohort-update.dto"; +import { JwtAuthGuard } from "src/common/guards/keycloak.guard"; +import { AllExceptionsFilter } from "src/common/filters/exception.filter"; +import { APIID } from "src/common/utils/api-id.config"; @ApiTags("Cohort") @Controller("cohort") +@UseGuards(JwtAuthGuard) export class CohortController { - constructor(private cohortAdapter: CohortAdapter) {} + constructor(private readonly cohortAdapter: CohortAdapter) { } - //create cohort - @Post() + @UseFilters(new AllExceptionsFilter(APIID.COHORT_READ)) + @Get("/read/:cohortId") + @ApiBasicAuth("access-token") + @ApiOkResponse({ description: "Cohort details Fetched Successfully" }) + @ApiNotFoundResponse({ description: "Cohort Not Found" }) + @ApiInternalServerErrorResponse({ description: "Internal Server Error." }) + @ApiBadRequestResponse({ description: "Bad Request" }) + @SerializeOptions({ strategy: "excludeAll", }) + @ApiHeader({ name: "tenantid", }) + public async getCohortsDetails( + @Headers() headers, + @Param("cohortId") cohortId: string, + @Req() request: Request, + @Res() response: Response + ) { + // const tenantId = headers["tenantid"]; Can be Used In future + return await this.cohortAdapter.buildCohortAdapter().getCohortsDetails(cohortId, response); + + } + + @UseFilters(new AllExceptionsFilter(APIID.COHORT_CREATE)) + @Post("/create") @ApiConsumes("multipart/form-data") @ApiBasicAuth("access-token") @ApiCreatedResponse({ description: "Cohort has been created successfully." }) + @ApiBadRequestResponse({ description: "Bad request." }) + @ApiInternalServerErrorResponse({ description: "Internal Server Error." }) + @ApiConflictResponse({ description: "Cohort already exists." }) + @UseInterceptors( FileInterceptor("image", { storage: diskStorage({ @@ -55,9 +88,8 @@ export class CohortController { fileFilter: imageFileFilter, }) ) + @UsePipes(new ValidationPipe()) @ApiBody({ type: CohortCreateDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) @ApiHeader({ name: "tenantid", }) @@ -65,7 +97,8 @@ export class CohortController { @Headers() headers, @Req() request: Request, @Body() cohortCreateDto: CohortCreateDto, - @UploadedFile() image + @UploadedFile() image, + @Res() response: Response ) { let tenantid = headers["tenantid"]; const payload = { @@ -73,43 +106,22 @@ export class CohortController { tenantId: tenantid, }; Object.assign(cohortCreateDto, payload); - - return this.cohortAdapter - .buildCohortAdapter() - .createCohort(request, cohortCreateDto); + return await this.cohortAdapter.buildCohortAdapter().createCohort( + request, + cohortCreateDto, + response + ); } - //get cohort - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Cohort detail" }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @SerializeOptions({ - strategy: "excludeAll", - }) - @ApiHeader({ - name: "tenantid", - }) - public async getCohort( - @Headers() headers, - @Param("id") cohortId: string, - @Req() request: Request, - @Res() res: Response - ) { - let tenantid = headers["tenantid"]; - return this.cohortAdapter - .buildCohortAdapter() - .getCohort(tenantid, cohortId, request, res); - } - //search + @UseFilters(new AllExceptionsFilter(APIID.COHORT_LIST)) @Post("/search") @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Cohort list." }) @ApiBody({ type: CohortSearchDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) + @ApiOkResponse({ description: "Cohort list" }) + @ApiBadRequestResponse({ description: "Bad request." }) + @ApiInternalServerErrorResponse({ description: "Internal Server Error." }) + @UsePipes(new ValidationPipe()) @SerializeOptions({ strategy: "excludeAll", }) @@ -120,19 +132,21 @@ export class CohortController { @Headers() headers, @Req() request: Request, @Body() cohortSearchDto: CohortSearchDto, - @Res() res: Response + @Res() response: Response ) { let tenantid = headers["tenantid"]; - return this.cohortAdapter - .buildCohortAdapter() - .searchCohort(tenantid, request, cohortSearchDto, res); + return await this.cohortAdapter.buildCohortAdapter().searchCohort( + tenantid, + request, + cohortSearchDto, + response + ); } - //update - @Put("/:id") + @UseFilters(new AllExceptionsFilter(APIID.COHORT_UPDATE)) + @Put("/update/:cohortId") @ApiConsumes("multipart/form-data") @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Cohort has been updated successfully." }) @UseInterceptors( FileInterceptor("image", { storage: diskStorage({ @@ -142,22 +156,38 @@ export class CohortController { fileFilter: imageFileFilter, }) ) - @ApiBody({ type: CohortCreateDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) + @ApiBody({ type: CohortUpdateDto }) + @ApiOkResponse({ description: "Cohort has been updated successfully" }) + @ApiBadRequestResponse({ description: "Bad request." }) + @ApiInternalServerErrorResponse({ description: "Internal Server Error." }) + public async updateCohort( - @Param("id") cohortId: string, + @Param("cohortId") cohortId: string, @Req() request: Request, - @Body() cohortCreateDto: CohortCreateDto, - @UploadedFile() image + @Body() cohortUpdateDto: CohortUpdateDto, + @UploadedFile() image, + @Res() response: Response ) { - const response = { - image: image?.filename, - }; - Object.assign(cohortCreateDto, response); + return await this.cohortAdapter.buildCohortAdapter().updateCohort( + cohortId, + request, + cohortUpdateDto, + response + ); + } - return this.cohortAdapter - .buildCohortAdapter() - .updateCohort(cohortId, request, cohortCreateDto); + + @UseFilters(new AllExceptionsFilter(APIID.COHORT_DELETE)) + @Delete("/delete/:cohortId") + @ApiBasicAuth("access-token") + @ApiOkResponse({ description: "Cohort has been deleted successfully." }) + @ApiBadRequestResponse({ description: "Bad request." }) + @ApiInternalServerErrorResponse({ description: "Internal Server Error." }) + public async updateCohortStatus( + @Param("cohortId") cohortId: string, + @Req() request: Request, + @Res() response: Response + ) { + return await this.cohortAdapter.buildCohortAdapter().updateCohortStatus(cohortId, request, response); } } diff --git a/src/cohort/cohort.module.ts b/src/cohort/cohort.module.ts index 26c696e4..e8dddc71 100644 --- a/src/cohort/cohort.module.ts +++ b/src/cohort/cohort.module.ts @@ -1,18 +1,26 @@ -import { CacheModule, Module } from "@nestjs/common"; +import { Module } from "@nestjs/common"; import { CohortController } from "./cohort.controller"; import { HttpModule } from "@nestjs/axios"; import { CohortAdapter } from "./cohortadapter"; import { HasuraModule } from "src/adapters/hasura/hasura.module"; -const ttl = process.env.TTL as never; +import { TypeOrmModule } from "@nestjs/typeorm"; +import { Cohort } from "./entities/cohort.entity"; +import { FieldsService } from "../fields/fields.service"; +import { Fields } from "../fields/entities/fields.entity"; +import { FieldValues } from "../fields/entities/fields-values.entity"; +import { CohortMembers } from "src/cohortMembers/entities/cohort-member.entity"; +import { PostgresModule } from "src/adapters/postgres/potsgres-module"; +import { PostgresCohortService } from "src/adapters/postgres/cohort-adapter"; +import { UserTenantMapping } from "src/userTenantMapping/entities/user-tenant-mapping.entity"; + @Module({ imports: [ + TypeOrmModule.forFeature([Cohort, FieldValues, Fields, CohortMembers, UserTenantMapping]), HttpModule, HasuraModule, - CacheModule.register({ - ttl: ttl, - }), + PostgresModule ], controllers: [CohortController], - providers: [CohortAdapter], + providers: [CohortAdapter, FieldsService,PostgresCohortService], }) export class CohortModule {} diff --git a/src/cohort/cohort.service.ts b/src/cohort/cohort.service.ts new file mode 100644 index 00000000..90ac4069 --- /dev/null +++ b/src/cohort/cohort.service.ts @@ -0,0 +1,332 @@ +import { ConsoleLogger, HttpStatus, Injectable } from "@nestjs/common"; +import { CohortInterface } from "./interfaces/cohort.interface"; +import { HttpService } from "@nestjs/axios"; +import { SuccessResponse } from "src/success-response"; +import { ErrorResponse } from "src/error-response"; +const resolvePath = require("object-resolve-path"); +import jwt_decode from "jwt-decode"; +import { CohortDto } from "src/cohort/dto/cohort.dto"; +import { CohortSearchDto } from "src/cohort/dto/cohort-search.dto"; +import { UserDto } from "src/user/dto/user.dto"; +import { CohortCreateDto } from "src/cohort/dto/cohort-create.dto"; +import { FieldValuesDto } from "src/fields/dto/field-values.dto"; +import { FieldValuesSearchDto } from "src/fields/dto/field-values-search.dto"; +import { IsNull, Not, Repository, getConnection, getRepository } from "typeorm"; +import { Cohort } from "src/cohort/entities/cohort.entity"; +import { InjectRepository } from "@nestjs/typeorm"; +import { FieldsService } from "../fields/fields.service"; +// import { FieldValues } from "src/fields/entities/field-values.entity"; +import { response } from "express"; +import APIResponse from "src/utils/response"; +import { FieldValues } from "../fields/entities/fields-values.entity"; +import { v4 as uuidv4 } from "uuid"; +import { CohortMembers } from "src/cohortMembers/entities/cohort-member.entity"; +import { ErrorResponseTypeOrm } from "src/error-response-typeorm"; + +@Injectable() +export class CohortService { + private cohort: CohortInterface; + + constructor( + @InjectRepository(Cohort) + private cohortRepository: Repository, + @InjectRepository(CohortMembers) + private cohortMembersRepository: Repository, + private fieldsService: FieldsService + ) {} + + public async getCohortsDetails(userData, request: any, response: any) { + let apiId = "api.concept.getCohortDetails"; + try { + if (userData.name === "user") { + let findCohortId = await this.findCohortName(userData?.id); + let result = { + cohortData: [], + }; + for (let data of findCohortId) { + let cohortData = { + cohortId: data?.cohortId, + name: data.name, + parentId: data?.parentId, + customField: {}, + }; + const getDetails = await this.getCohortListDetails(data?.cohortId); + cohortData.customField = getDetails; + result.cohortData.push(cohortData); + } + return response + .status(HttpStatus.OK) + .send(APIResponse.success(apiId, result, "OK")); + } else { + let cohortName = await this.cohortRepository.findOne({ + where: { cohortId: userData?.id }, + select: ["name", "parentId"], + }); + let cohortData = { + cohortId: userData?.id, + name: cohortName?.name, + parentId: cohortName?.parentId, + customField: {}, + }; + const getDetails = await this.getCohortListDetails(userData?.id); + cohortData.customField = getDetails; + return response + .status(HttpStatus.OK) + .send(APIResponse.success(apiId, cohortData, "OK")); + } + } catch (error) { + return response + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .send( + APIResponse.error( + apiId, + "Something went wrong", + `Failure Retrieving Cohort Member. Error is: ${error}`, + "INTERNAL_SERVER_ERROR" + ) + ); + } + } + + public async findCohortName(userId: any) { + let query = `SELECT c."name",c."cohortId",c."parentId" + FROM public."CohortMembers" AS cm + LEFT JOIN public."Cohort" AS c ON cm."cohortId" = c."cohortId" + WHERE cm."userId"=$1 AND c.status=true`; + let result = await this.cohortMembersRepository.query(query, [userId]); + // if(!result.length){ + // return null; + // } + return result; + } + + public async getCohortListDetails(userId) { + let query = `SELECT DISTINCT f."label", fv."value", f."type", f."fieldParams" + FROM public."CohortMembers" cm + LEFT JOIN ( + SELECT DISTINCT ON (fv."fieldId", fv."itemId") fv.* + FROM public."FieldValues" fv + ) fv ON fv."itemId" = cm."cohortId" + INNER JOIN public."Fields" f ON fv."fieldId" = f."fieldId" + WHERE cm."cohortId" = $1;`; + let result = await this.cohortMembersRepository.query(query, [userId]); + return result; + } + public async createCohort(request: any, cohortCreateDto: CohortCreateDto) { + try { + const cohortData: any = {}; + cohortCreateDto.cohortId = uuidv4(); + Object.keys(cohortCreateDto).forEach((e) => { + if ( + cohortCreateDto[e] && + cohortCreateDto[e] != "" && + e != "fieldValues" + ) { + if (Array.isArray(cohortCreateDto[e])) { + cohortData[e] = JSON.stringify(cohortCreateDto[e]); + } else { + cohortData[e] = cohortCreateDto[e]; + } + } + }); + const response = await this.cohortRepository.save(cohortData); + let cohortId = response?.cohortId; + + let field_value_array = cohortCreateDto.fieldValues.split("|"); + + if (field_value_array.length > 0) { + let field_values = []; + for (let i = 0; i < field_value_array.length; i++) { + let fieldValues = field_value_array[i].split(":"); + let fieldValueDto: FieldValuesDto = { + value: fieldValues[1] ? fieldValues[1].trim() : "", + itemId: cohortId, + fieldId: fieldValues[0] ? fieldValues[0].trim() : "", + createdBy: cohortCreateDto?.createdBy, + updatedBy: cohortCreateDto?.updatedBy, + createdAt: new Date().toISOString(), // Provide appropriate values for createdAt and updatedAt + updatedAt: new Date().toISOString(), + }; + + await this.fieldsService.createFieldValues(request, fieldValueDto); + } + } + + return new SuccessResponse({ + statusCode: HttpStatus.CREATED, + message: "Ok.", + data: response, + }); + } catch (e) { + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.INTERNAL_SERVER_ERROR, + errorMessage: e, + }); + } + } + + public async updateCohort( + cohortId: string, + request: any, + cohortUpdateDto: CohortCreateDto + ) { + try { + const cohortUpdateData: any = {}; + + Object.keys(cohortUpdateDto).forEach((e) => { + if ( + cohortUpdateDto[e] && + cohortUpdateDto[e] != "" && + e != "fieldValues" + ) { + if (Array.isArray(cohortUpdateDto[e])) { + cohortUpdateData[e] = JSON.stringify(cohortUpdateDto[e]); + } else { + cohortUpdateData[e] = cohortUpdateDto[e]; + } + } + }); + + const response = await this.cohortRepository.update( + cohortId, + cohortUpdateData + ); + + let field_value_array = cohortUpdateDto.fieldValues.split("|"); + + if (field_value_array.length > 0) { + let field_values = []; + for (let i = 0; i < field_value_array.length; i++) { + let fieldValues = field_value_array[i].split(":"); + let fieldId = fieldValues[0] ? fieldValues[0].trim() : ""; + try { + const fieldVauesRowId = await this.fieldsService.searchFieldValueId( + cohortId, + fieldId + ); + const rowid = fieldVauesRowId.fieldValuesId; + + let fieldValueDto: FieldValuesDto = { + value: fieldValues[1] ? fieldValues[1].trim() : "", + itemId: cohortId, + fieldId: fieldValues[0] ? fieldValues[0].trim() : "", + createdBy: cohortUpdateDto?.createdBy, + updatedBy: cohortUpdateDto?.updatedBy, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }; + await this.fieldsService.updateFieldValues(rowid, fieldValueDto); + } catch { + // let fieldValueDto: FieldValuesDto = { + // fieldValuesId: null, + // value: fieldValues[1] ? fieldValues[1].trim() : "", + // itemId: cohortId, + // fieldId: fieldValues[0] ? fieldValues[0].trim() : "", + // createdBy: cohortUpdateDto?.createdBy, + // updatedBy: cohortUpdateDto?.updatedBy, + // createdAt: new Date().toISOString(), + // updatedAt: new Date().toISOString(), + // }; + // await this.fieldsService.createFieldValues(request, fieldValueDto); + } + } + } + + return new SuccessResponse({ + statusCode: HttpStatus.OK, + message: "Ok.", + data: { + rowCount: response.affected, + }, + }); + } catch (e) { + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.INTERNAL_SERVER_ERROR, + errorMessage: e, + }); + } + } + + public async searchCohort( + tenantId: string, + request: any, + cohortSearchDto: CohortSearchDto + ) { + try { + let { limit, page, filters } = cohortSearchDto; + + let offset = 0; + if (page > 1) { + offset = limit * (page - 1); + } + + + const whereClause = {}; + if (filters && Object.keys(filters).length > 0) { + Object.entries(filters).forEach(([key, value]) => { + whereClause[key] = value; + }); + } + const [results, totalCount] = await this.cohortRepository.findAndCount({ + where: whereClause, + skip: offset, + take: limit, + }); + + const mappedResponse = await this.mappedResponse(results); + + return new SuccessResponse({ + statusCode: HttpStatus.OK, + message: "Ok.", + totalCount, + data: mappedResponse, + }); + } catch (e) { + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.INTERNAL_SERVER_ERROR, + errorMessage: e, + }); + } + } + + public async mappedResponse(result: any) { + const cohortValueResponse = result.map((item: any) => { + const cohortMapping = { + tenantId: item?.tenantId ? `${item.tenantId}` : "", + programId: item?.programId ? `${item.programId}` : "", + cohortId: item?.cohortId ? `${item.cohortId}` : "", + parentId: item?.parentId ? `${item.parentId}` : "", + name: item?.name ? `${item.name}` : "", + type: item?.type ? `${item.type}` : "", + status: item?.status ? `${item.status}` : "", + image: item?.image ? `${item.image}` : "", + createdAt: item?.createdAt ? `${item.createdAt}` : "", + updatedAt: item?.updatedAt ? `${item.updatedAt}` : "", + createdBy: item?.createdBy ? `${item.createdBy}` : "", + updatedBy: item?.updatedBy ? `${item.updatedBy}` : "", + referenceId: item?.referenceId ? `${item.referenceId}` : "", + metadata: item?.metadata ? `${item.metadata}` : "", + }; + return new CohortDto(cohortMapping); + }); + return cohortValueResponse; + } + + public async updateCohortStatus(cohortId: string) { + try { + let query = `UPDATE public."Cohort" + SET "status" = false + WHERE "cohortId" = $1`; + const results = await this.cohortRepository.query(query, [cohortId]); + return new SuccessResponse({ + statusCode: HttpStatus.OK, + message: "Cohort Deleted Successfully.", + }); + } catch (e) { + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.INTERNAL_SERVER_ERROR, + errorMessage: e, + }); + } + } +} diff --git a/src/cohort/cohortadapter.ts b/src/cohort/cohortadapter.ts index 62988ddb..0ed45cf6 100644 --- a/src/cohort/cohortadapter.ts +++ b/src/cohort/cohortadapter.ts @@ -1,10 +1,11 @@ import { Injectable } from "@nestjs/common"; import { IServicelocatorcohort } from "src/adapters/cohortservicelocator"; import { HasuraCohortService } from "src/adapters/hasura/cohort.adapter"; +import { PostgresCohortService } from "src/adapters/postgres/cohort-adapter"; @Injectable() export class CohortAdapter { - constructor(private hasuraProvider: HasuraCohortService) {} + constructor(private hasuraProvider: HasuraCohortService,private postgresProvider:PostgresCohortService) {} buildCohortAdapter(): IServicelocatorcohort { let adapter: IServicelocatorcohort; @@ -12,6 +13,9 @@ export class CohortAdapter { case "hasura": adapter = this.hasuraProvider; break; + case "postgres": + adapter = this.postgresProvider; + break; } return adapter; } diff --git a/src/cohort/dto/cohort-create.dto.ts b/src/cohort/dto/cohort-create.dto.ts index b3790979..d7974d2e 100644 --- a/src/cohort/dto/cohort-create.dto.ts +++ b/src/cohort/dto/cohort-create.dto.ts @@ -1,22 +1,22 @@ import { Exclude, Expose } from "class-transformer"; import { - MaxLength, IsNotEmpty, - IsEmail, IsString, - IsNumber, + IsOptional } from "class-validator"; import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; import { FieldValuesCreateDto } from "src/fields/dto/field-values-create.dto"; export class CohortCreateDto { - //generated fields - @Expose() - tenantId: string; @Expose() cohortId: string; + + @Expose() + tenantId: string; + @Expose() createdAt: string; + @Expose() updatedAt: string; @@ -39,11 +39,6 @@ export class CohortCreateDto { parentId: string; //referenceId - @ApiPropertyOptional({ - type: String, - description: "The referenceId of the cohort", - default: "", - }) @Expose() referenceId: string; @@ -54,6 +49,7 @@ export class CohortCreateDto { default: "", }) @Expose() + @IsNotEmpty() name: string; //type @@ -63,58 +59,48 @@ export class CohortCreateDto { default: "", }) @Expose() + @IsNotEmpty() type: string; //status - @ApiPropertyOptional({ - type: String, - description: "The status of the cohort", - default: "publish", - }) @Expose() - status: string; + status: boolean; - //image + //attendanceCaptureImage @Expose() - @ApiPropertyOptional({ type: "string", format: "binary" }) - image: string; + attendanceCaptureImage: boolean; + + //image need for future + // @Expose() + // @ApiPropertyOptional({ type: "string", format: "binary" }) + // image: string; //metadata - @ApiPropertyOptional({ - type: String, - description: "The metadata of cohort", - default: "", - }) @Expose() metadata: string; //createdBy @Expose() - @ApiProperty({ - type: String, - description: "The cohort is createdBy", - default: "", - }) createdBy: string; //updatedBy - @ApiProperty({ - type: String, - description: "The cohort is updatedBy", - default: "", - }) @Expose() updatedBy: string; //fieldValues - @ApiProperty({ + @ApiPropertyOptional({ type: String, description: "The fieldValues Object", }) + @IsString() + @IsOptional() @Expose() - fieldValues: string; + fieldValues?: string; + - constructor(partial: Partial) { - Object.assign(this, partial); + constructor(obj?: Partial) { + if (obj) { + Object.assign(this, obj); + } } } diff --git a/src/cohort/dto/cohort-search.dto.ts b/src/cohort/dto/cohort-search.dto.ts index b1a76b6f..f6be8bc2 100644 --- a/src/cohort/dto/cohort-search.dto.ts +++ b/src/cohort/dto/cohort-search.dto.ts @@ -1,24 +1,67 @@ import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; +import { IsBoolean, IsNotEmpty, IsNumber, IsNumberString, IsObject, IsOptional, IsString, IsUUID, ValidationArguments, ValidationOptions, registerDecorator } from "class-validator"; +import { CohortDto } from "./cohort.dto"; +import { Expose } from "class-transformer"; -export class CohortSearchDto { +export class setFilters { + //userIdBy + @ApiProperty({ + type: String, + description: "The cohort is createdBy", + default: "", + }) + @Expose() + @IsOptional() + @IsUUID() + @IsNotEmpty() + userId?: string; + + //cohortIdBy @ApiProperty({ type: String, + description: "The cohort is createdBy", + default: "", + }) + @Expose() + @IsOptional() + @IsUUID() + @IsNotEmpty() + cohortId?: string; + + //name + @ApiProperty({ + type: String, + description: "The name of the cohort", + default: "", + }) + @Expose() + @IsOptional() + @IsString() + @IsNotEmpty() + name?: string; +} + +export class CohortSearchDto { + @ApiProperty({ + type: Number, description: "Limit", }) - limit: string; + @IsNumber() + limit: number; @ApiProperty({ type: Number, - description: "number", + description: "Page", }) + @IsNumber() page: number; @ApiProperty({ - type: Object, + type: setFilters, description: "Filters", }) - @ApiPropertyOptional() - filters: object; + @IsObject() + filters: setFilters; constructor(partial: Partial) { Object.assign(this, partial); diff --git a/src/cohort/dto/cohort-update.dto.ts b/src/cohort/dto/cohort-update.dto.ts new file mode 100644 index 00000000..0c7a8d88 --- /dev/null +++ b/src/cohort/dto/cohort-update.dto.ts @@ -0,0 +1,98 @@ +import { Exclude, Expose } from "class-transformer"; +import { + IsNotEmpty, + IsString, + IsOptional +} from "class-validator"; +import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; + + +export class CohortUpdateDto { + @Expose() + cohortId: string; + + @Expose() + tenantId: string; + + @Expose() + createdAt: string; + + @Expose() + updatedAt: string; + + //programId + @ApiPropertyOptional({ + type: String, + description: "The programId of the cohort", + }) + @Expose() + programId: string; + + //parentId + @ApiPropertyOptional({ + type: String, + description: "The parentId of the cohort", + }) + @Expose() + parentId: string; + + //referenceId + @Expose() + referenceId: string; + + //name + @ApiPropertyOptional({ + type: String, + description: "The name of the cohort", + }) + @Expose() + name: string; + + //type + @ApiPropertyOptional({ + type: String, + description: "The type of the cohort", + }) + @Expose() + type: string; + + //status + @Expose() + status: boolean; + + //attendanceCaptureImage + @Expose() + attendanceCaptureImage: boolean; + + //image need for future + // @Expose() + // @ApiPropertyOptional({ type: "string", format: "binary" }) + // image: string; + + //metadata + @Expose() + metadata: string; + + //createdBy + @Expose() + createdBy: string; + + //updatedBy + @Expose() + updatedBy: string; + + //fieldValues + @ApiPropertyOptional({ + type: String, + description: "The fieldValues Object", + }) + @Expose() + fieldValues: string; + + + constructor(obj?: Partial) { + if (obj) { + Object.assign(this, obj); + } + } +} diff --git a/src/cohort/dto/cohort.dto.ts b/src/cohort/dto/cohort.dto.ts index 463152db..31dce917 100644 --- a/src/cohort/dto/cohort.dto.ts +++ b/src/cohort/dto/cohort.dto.ts @@ -15,9 +15,9 @@ export class CohortDto { @Expose() cohortId: string; @Expose() - createdAt: string; + createdAt: Date; @Expose() - updatedAt: string; + updatedAt: Date; //programId @ApiPropertyOptional({ @@ -64,20 +64,30 @@ export class CohortDto { @Expose() type: string; + //status @ApiPropertyOptional({ - type: String, + type: Boolean, description: "The status of the cohort", - default: "publish", + default: true, }) @Expose() - status: string; + status: boolean; //image @Expose() @ApiPropertyOptional({ type: "string", format: "binary" }) image: string; + //attendanceCaptureImage + @ApiProperty({ + type: Boolean, + description: "Capture image while marking the attendance", + default: false, + }) + @Expose() + attendanceCaptureImage: Boolean; + //metadata @ApiPropertyOptional({ type: String, @@ -109,3 +119,27 @@ export class CohortDto { Object.assign(this, obj); } } + +export class ReturnResponseBody{ + @Expose() + cohortId: string; + @Expose() + parentId: string; + @Expose() + name: string; + @Expose() + type: string; + @Expose() + status: boolean; + @Expose() + tenantId: string; + + constructor(cohortDto: CohortDto) { + this.cohortId = cohortDto.cohortId; + this.parentId = cohortDto.parentId; + this.name = cohortDto.name; + this.type = cohortDto.type; + this.status = cohortDto.status; + this.tenantId = cohortDto.tenantId; + } +} diff --git a/src/cohort/dto/query-params.dto.ts b/src/cohort/dto/query-params.dto.ts new file mode 100644 index 00000000..072d97ba --- /dev/null +++ b/src/cohort/dto/query-params.dto.ts @@ -0,0 +1,11 @@ +import { IsNotEmpty, IsString, IsInt } from 'class-validator'; + +export class QueryParamsDto { + @IsNotEmpty() + @IsString() + name: string; + + @IsNotEmpty() + @IsString() + id: string; +} \ No newline at end of file diff --git a/src/cohort/entities/cohort.entity.ts b/src/cohort/entities/cohort.entity.ts new file mode 100644 index 00000000..29362080 --- /dev/null +++ b/src/cohort/entities/cohort.entity.ts @@ -0,0 +1,64 @@ +import { + Entity, + PrimaryColumn, + Column, + CreateDateColumn, + UpdateDateColumn, + ManyToMany, + PrimaryGeneratedColumn, +} from "typeorm"; + +@Entity({ name: "Cohort" }) +export class Cohort { + @PrimaryGeneratedColumn('uuid') + cohortId: string; + + @Column({ nullable: true }) + parentId: string; + + @Column({ nullable: true }) + name: string; + + @Column({ nullable: true }) + type: string; + + @Column({ nullable: true }) + status: boolean; + + @Column({ nullable: true }) + image: string; + + @Column({ nullable: true }) + referenceId: string; + + @Column({ nullable: true }) + metadata: string; + + @Column({ nullable: true }) + tenantId: string; + + @Column({ nullable: true }) + programId: string; + + @Column() + attendanceCaptureImage: boolean; + + @CreateDateColumn({ + type: "timestamp with time zone", + default: () => "CURRENT_TIMESTAMP", + }) + createdAt: Date; + + @UpdateDateColumn({ + type: "timestamp with time zone", + default: () => "CURRENT_TIMESTAMP", + }) + updatedAt: Date; + + @Column() + createdBy: string; + + @Column() + updatedBy: string; + +} diff --git a/src/cohortMembers/cohortMembers.controller.ts b/src/cohortMembers/cohortMembers.controller.ts index 1abdffd1..4beecf18 100644 --- a/src/cohortMembers/cohortMembers.controller.ts +++ b/src/cohortMembers/cohortMembers.controller.ts @@ -5,7 +5,10 @@ import { ApiCreatedResponse, ApiBasicAuth, ApiHeader, - ApiConsumes, + ApiOkResponse, + ApiQuery, + ApiNotFoundResponse, + ApiBadRequestResponse, } from "@nestjs/swagger"; import { Controller, @@ -13,82 +16,97 @@ import { Post, Body, Put, + Delete, Param, - UseInterceptors, - ClassSerializerInterceptor, SerializeOptions, Req, - CacheInterceptor, Headers, + Res, + UseGuards, + UsePipes, + ValidationPipe, + Query, + UseFilters, } from "@nestjs/common"; import { CohortMembersSearchDto } from "./dto/cohortMembers-search.dto"; import { Request } from "@nestjs/common"; import { CohortMembersDto } from "./dto/cohortMembers.dto"; import { CohortMembersAdapter } from "./cohortMembersadapter"; +import { CohortMembersUpdateDto } from "./dto/cohortMember-update.dto"; +import { JwtAuthGuard } from "src/common/guards/keycloak.guard"; +import { Response } from "express"; +import { AllExceptionsFilter } from "src/common/filters/exception.filter"; +import { APIID } from "src/common/utils/api-id.config"; -@ApiTags("Cohort Members") -@Controller("cohortmembers") +@ApiTags("Cohort Member") +@Controller("cohortmember") +@UseGuards(JwtAuthGuard) export class CohortMembersController { - constructor(private cohortMembersAdapter: CohortMembersAdapter) {} + constructor( + private readonly cohortMemberAdapter: CohortMembersAdapter + ) { } //create cohort members - @Post() + @UseFilters(new AllExceptionsFilter(APIID.COHORT_MEMBER_CREATE)) + @Post("/create") + @UsePipes(new ValidationPipe()) @ApiBasicAuth("access-token") @ApiCreatedResponse({ - description: "Cohort Members has been created successfully.", + description: "Cohort Member has been created successfully.", }) @ApiBody({ type: CohortMembersDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) @ApiHeader({ name: "tenantid", }) public async createCohortMembers( @Headers() headers, - @Req() request: Request, - @Body() cohortMembersDto: CohortMembersDto + @Req() request, + @Body() cohortMembersDto: CohortMembersDto, + @Res() response: Response ) { - let tenantid = headers["tenantid"]; - const payload = { - tenantId: tenantid, - }; - Object.assign(cohortMembersDto, payload); - - return this.cohortMembersAdapter + const loginUser = request.user.userId; + const tenantId = headers["tenantid"]; + const result = await this.cohortMemberAdapter .buildCohortMembersAdapter() - .createCohortMembers(request, cohortMembersDto); + .createCohortMembers(loginUser, cohortMembersDto, response, tenantId); + return response.status(result.statusCode).json(result); } //get cohort members - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) + @UseFilters(new AllExceptionsFilter(APIID.COHORT_MEMBER_GET)) + @Get("/read/:cohortId") @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Cohort Members detail" }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @SerializeOptions({ - strategy: "excludeAll", - }) - @ApiHeader({ - name: "tenantid", + @ApiCreatedResponse({ description: "Cohort Member detail" }) + @ApiNotFoundResponse({ description: "Data not found" }) + @ApiBadRequestResponse({ description: "Bad request" }) + @SerializeOptions({ strategy: "excludeAll" }) + @ApiHeader({ name: "tenantid" }) + @ApiQuery({ + name: "fieldvalue", + description: "Send True to Fetch Custom Field of User", + required: false, }) public async getCohortMembers( @Headers() headers, - @Param("id") cohortMembersId: string, - @Req() request: Request + @Param("cohortId") cohortId: string, + @Req() request: Request, + @Res() response: Response, + @Query("fieldvalue") fieldvalue: string | null = null ) { - let tenantid = headers["tenantid"]; - return this.cohortMembersAdapter + const tenantId = headers["tenantid"]; + const result = await this.cohortMemberAdapter .buildCohortMembersAdapter() - .getCohortMembers(tenantid, cohortMembersId, request); + .getCohortMembers(cohortId, tenantId, fieldvalue, response); } - //search - @Post("/search") + // search; + @UseFilters(new AllExceptionsFilter(APIID.COHORT_MEMBER_SEARCH)) + @Post("/list") @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Cohort Members list." }) + @ApiCreatedResponse({ description: "Cohort Member list." }) + @ApiNotFoundResponse({ description: "Data not found" }) + @ApiBadRequestResponse({ description: "Bad request" }) @ApiBody({ type: CohortMembersSearchDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) @SerializeOptions({ strategy: "excludeAll", }) @@ -98,32 +116,66 @@ export class CohortMembersController { public async searchCohortMembers( @Headers() headers, @Req() request: Request, + @Res() response: Response, @Body() cohortMembersSearchDto: CohortMembersSearchDto ) { - let tenantid = headers["tenantid"]; - return this.cohortMembersAdapter + const tenantId = headers["tenantid"]; + + const result = await this.cohortMemberAdapter .buildCohortMembersAdapter() - .searchCohortMembers(tenantid, request, cohortMembersSearchDto); + .searchCohortMembers(cohortMembersSearchDto, tenantId, response); } //update - @Put("/:id") + @UseFilters(new AllExceptionsFilter(APIID.COHORT_MEMBER_UPDATE)) + @Put("/update/:id") @ApiBasicAuth("access-token") @ApiCreatedResponse({ - description: "Cohort Members has been updated successfully.", + description: "Cohort Member has been updated successfully.", }) - @ApiBody({ type: CohortMembersDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) + @ApiNotFoundResponse({ description: "Data not found" }) + @ApiBadRequestResponse({ description: "Bad request" }) + @ApiBody({ type: CohortMembersUpdateDto }) public async updateCohortMembers( @Param("id") cohortMembersId: string, + @Req() request, + @Body() cohortMemberUpdateDto: CohortMembersUpdateDto, + @Res() response: Response + ) { + const loginUser = request.user.userId; + + const result = await this.cohortMemberAdapter + .buildCohortMembersAdapter() + .updateCohortMembers( + cohortMembersId, + loginUser, + cohortMemberUpdateDto, + response + ); + } + + //delete + @UseFilters(new AllExceptionsFilter(APIID.COHORT_MEMBER_DELETE)) + @Delete("/delete/:id") + @ApiBasicAuth("access-token") + @ApiCreatedResponse({ description: "Cohort member deleted successfully" }) + @ApiNotFoundResponse({ description: "Data not found" }) + @SerializeOptions({ + strategy: "excludeAll", + }) + @ApiHeader({ + name: "tenantid", + }) + public async deleteCohortMember( + @Headers() headers, + @Param("id") cohortMembershipId: string, @Req() request: Request, - @Body() cohortMembersipDto: CohortMembersDto + @Res() response: Response ) { - return this.cohortMembersAdapter.buildCohortMembersAdapter().updateCohortMembers( - cohortMembersId, - request, - cohortMembersipDto - ); + let tenantid = headers["tenantid"]; + + const result = await this.cohortMemberAdapter + .buildCohortMembersAdapter() + .deleteCohortMemberById(tenantid, cohortMembershipId, response); } -} +} \ No newline at end of file diff --git a/src/cohortMembers/cohortMembers.module.ts b/src/cohortMembers/cohortMembers.module.ts index eb0f145f..3e175da0 100644 --- a/src/cohortMembers/cohortMembers.module.ts +++ b/src/cohortMembers/cohortMembers.module.ts @@ -1,18 +1,29 @@ -import { CacheModule, Module } from "@nestjs/common"; +import { Module } from "@nestjs/common"; import { CohortMembersController } from "./cohortMembers.controller"; import { HttpModule } from "@nestjs/axios"; import { CohortMembersAdapter } from "./cohortMembersadapter"; import { HasuraModule } from "src/adapters/hasura/hasura.module"; -const ttl = process.env.TTL as never; +import { TypeOrmModule } from "@nestjs/typeorm"; +import { CohortMembers } from "./entities/cohort-member.entity"; +import { PostgresModule } from "src/adapters/postgres/potsgres-module"; +import { PostgresCohortMembersService } from "src/adapters/postgres/cohortMembers-adapter"; +import { HasuraCohortMembersService } from "src/adapters/hasura/cohortMembers.adapter"; +import { Fields } from "src/fields/entities/fields.entity"; +import { User } from "src/user/entities/user-entity"; +import { Cohort } from "src/cohort/entities/cohort.entity"; + @Module({ imports: [ + TypeOrmModule.forFeature([CohortMembers, Fields, User, Cohort]), HttpModule, HasuraModule, - CacheModule.register({ - ttl: ttl, - }), + PostgresModule, ], controllers: [CohortMembersController], - providers: [CohortMembersAdapter], + providers: [ + CohortMembersAdapter, + PostgresCohortMembersService, + HasuraCohortMembersService, + ], }) -export class CohortMembersModule {} +export class CohortMembersModule { } \ No newline at end of file diff --git a/src/cohortMembers/cohortMembersadapter.ts b/src/cohortMembers/cohortMembersadapter.ts index 6d4d96ae..8748ba74 100644 --- a/src/cohortMembers/cohortMembersadapter.ts +++ b/src/cohortMembers/cohortMembersadapter.ts @@ -1,10 +1,14 @@ import { Injectable } from "@nestjs/common"; import { IServicelocatorcohortMembers } from "src/adapters/cohortMembersservicelocator"; import { HasuraCohortMembersService } from "src/adapters/hasura/cohortMembers.adapter"; +import { PostgresCohortMembersService } from "src/adapters/postgres/cohortMembers-adapter"; @Injectable() export class CohortMembersAdapter { - constructor(private hasuraProvider: HasuraCohortMembersService) {} + constructor( + private hasuraProvider: HasuraCohortMembersService, + private postgresProvider: PostgresCohortMembersService + ) {} buildCohortMembersAdapter(): IServicelocatorcohortMembers { let adapter: IServicelocatorcohortMembers; @@ -12,6 +16,8 @@ export class CohortMembersAdapter { case "hasura": adapter = this.hasuraProvider; break; + case "postgres": + adapter = this.postgresProvider; } return adapter; } diff --git a/src/cohortMembers/dto/cohortMember-update.dto.ts b/src/cohortMembers/dto/cohortMember-update.dto.ts new file mode 100644 index 00000000..522057e3 --- /dev/null +++ b/src/cohortMembers/dto/cohortMember-update.dto.ts @@ -0,0 +1,67 @@ +import { Exclude, Expose } from "class-transformer"; +import { ApiProperty } from "@nestjs/swagger"; +import { IsOptional } from "class-validator"; + +export class CohortMembersUpdateDto { + @Expose() + tenantId: string; + + @Expose() + cohortMembershipId: string; + + @Expose() + @IsOptional() + createdAt: string; + + @Expose() + @IsOptional() + updatedAt: string; + + @ApiProperty({ + type: String, + description: "The cohortId of the cohort members", + default: "", + }) + @Expose() + @IsOptional() // Marking as optional + cohortId?: string; // Making it optional by adding '?' after the type + + @ApiProperty({ + type: String, + description: "The userId of the cohort members", + default: "", + }) + @Expose() + @IsOptional() + userId?: string; + + @ApiProperty({ + type: String, + description: "The role of the cohort members", + default: "", + }) + @Expose() + role: string; + + @ApiProperty({ + type: String, + description: "The createdBy of the cohort members", + default: "", + }) + @Expose() + @IsOptional() + createdBy?: string; + + @ApiProperty({ + type: String, + description: "The updatedBy of the cohort members", + default: "", + }) + @Expose() + @IsOptional() + updatedBy?: string; + + constructor(obj: any) { + Object.assign(this, obj); + } +} diff --git a/src/cohortMembers/dto/cohortMembers-search.dto.ts b/src/cohortMembers/dto/cohortMembers-search.dto.ts index c2a49786..bf6250af 100644 --- a/src/cohortMembers/dto/cohortMembers-search.dto.ts +++ b/src/cohortMembers/dto/cohortMembers-search.dto.ts @@ -2,10 +2,10 @@ import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; export class CohortMembersSearchDto { @ApiProperty({ - type: String, + type: Number, description: "Limit", }) - limit: string; + limit: number; @ApiProperty({ type: Number, @@ -16,9 +16,10 @@ export class CohortMembersSearchDto { @ApiProperty({ type: Object, description: "Filters", + example: { cohortId: "", userId: "", role:"" }, // Adding example for Swagger }) @ApiPropertyOptional() - filters: object; + filters: { cohortId?: string; userId?: string; role?: string }; // Define cohortId and userId properties constructor(partial: Partial) { Object.assign(this, partial); diff --git a/src/cohortMembers/dto/cohortMembers.dto.ts b/src/cohortMembers/dto/cohortMembers.dto.ts index c4b8d371..9abe39ad 100644 --- a/src/cohortMembers/dto/cohortMembers.dto.ts +++ b/src/cohortMembers/dto/cohortMembers.dto.ts @@ -1,5 +1,6 @@ import { Exclude, Expose } from "class-transformer"; -import { ApiProperty } from "@nestjs/swagger"; +import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; +import { IsNotEmpty, IsUUID } from "class-validator"; export class CohortMembersDto { //generated fields @@ -11,6 +12,10 @@ export class CohortMembersDto { createdAt: string; @Expose() updatedAt: string; + @Expose() + createdBy: string; + @Expose() + updatedBy: string; //cohortId @ApiProperty({ @@ -19,6 +24,8 @@ export class CohortMembersDto { default: "", }) @Expose() + @IsNotEmpty() + @IsUUID(undefined, { message: 'Cohort Id must be a valid UUID' }) cohortId: string; //userId @@ -28,35 +35,10 @@ export class CohortMembersDto { default: "", }) @Expose() + @IsNotEmpty() + @IsUUID(undefined, { message: 'User Id must be a valid UUID' }) userId: string; - //role - @ApiProperty({ - type: String, - description: "The role of the cohort members", - default: "", - }) - @Expose() - role: string; - - //createdBy - @ApiProperty({ - type: String, - description: "The createdBy of the cohort members", - default: "", - }) - @Expose() - createdBy: string; - - //updatedBy - @ApiProperty({ - type: String, - description: "The updatedBy of the cohort members", - default: "", - }) - @Expose() - updatedBy: string; - constructor(obj: any) { Object.assign(this, obj); } diff --git a/src/cohortMembers/entities/cohort-member.entity.ts b/src/cohortMembers/entities/cohort-member.entity.ts new file mode 100644 index 00000000..d3ba1759 --- /dev/null +++ b/src/cohortMembers/entities/cohort-member.entity.ts @@ -0,0 +1,40 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, + ManyToMany, + JoinTable, +} from "typeorm"; + +@Entity({ name: "CohortMembers" }) +export class CohortMembers { + @PrimaryGeneratedColumn("uuid") + cohortMembershipId: string; + + @Column({ type: "uuid", nullable: true }) + cohortId: string | null; + + @Column({ type: "uuid" }) + userId: string; + + @CreateDateColumn({ + type: "timestamp with time zone", + default: () => "CURRENT_TIMESTAMP", + }) + createdAt: Date; + + @UpdateDateColumn({ + type: "timestamp with time zone", + default: () => "CURRENT_TIMESTAMP", + }) + updatedAt: Date; + + @Column({}) + createdBy: string; + + @Column({}) + updatedBy: string; + +} diff --git a/src/comment/comment.controller.ts b/src/comment/comment.controller.ts deleted file mode 100644 index e197a797..00000000 --- a/src/comment/comment.controller.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { - Controller, - Get, - Post, - Body, - Put, - Param, - UseInterceptors, - ClassSerializerInterceptor, - SerializeOptions, - Req, - CacheInterceptor, -} from "@nestjs/common"; -import { - ApiTags, - ApiBody, - ApiOkResponse, - ApiForbiddenResponse, - ApiCreatedResponse, - ApiBasicAuth, -} from "@nestjs/swagger"; -import { Request } from "@nestjs/common"; -import { CommentDto } from "./dto/comment.dto"; -import { CommentSearchDto } from "./dto/comment-search.dto"; -import { IServicelocator } from "src/adapters/commentservicelocator"; -import { CommentAdapter } from "./commentadapter"; -@ApiTags("Comment") -@Controller("comment") -export class CommentController implements IServicelocator { - constructor(private commentAdapter: CommentAdapter) {} - - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "Comment detail." }) - @SerializeOptions({ - strategy: "excludeAll", - }) - getComment(@Param("id") commentId: string, @Req() request: Request) { - return this.commentAdapter - .buildCommentAdapter() - .getComment(commentId, request); - } - - @Post() - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Comment has been created successfully.", - }) - @ApiBody({ type: CommentDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async createComment( - @Req() request: Request, - @Body() commentDto: CommentDto - ) { - return this.commentAdapter - .buildCommentAdapter() - .createComment(request, commentDto); - } - - @Put("/:id") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Comment has been updated successfully.", - }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async updateComment( - @Param("id") commentId: string, - @Req() request: Request, - @Body() commentDto: CommentDto - ) { - return this.commentAdapter - .buildCommentAdapter() - .updateComment(commentId, request, commentDto); - } - - @Post("/search") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Comment list." }) - @ApiBody({ type: CommentSearchDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async searchComment( - @Req() request: Request, - @Body() CommentSearchDto: CommentSearchDto - ) { - return this.commentAdapter - .buildCommentAdapter() - .searchComment(request, CommentSearchDto); - } -} diff --git a/src/comment/comment.module.ts b/src/comment/comment.module.ts deleted file mode 100644 index 233e7666..00000000 --- a/src/comment/comment.module.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { HttpModule } from "@nestjs/axios"; -import { CacheModule, Module } from "@nestjs/common"; -import { HasuraModule } from "src/adapters/hasura/hasura.module"; -import { SunbirdModule } from "src/adapters/sunbirdrc/subnbird.module"; -import { CommentController } from "./comment.controller"; -import { CommentAdapter } from "./commentadapter"; -const ttl = process.env.TTL as never; -@Module({ - imports: [ - SunbirdModule, - HasuraModule, - HttpModule, - CacheModule.register({ - ttl: ttl, - }), - ], - controllers: [CommentController], - providers: [CommentAdapter], -}) -export class CommentModule {} diff --git a/src/comment/commentadapter.ts b/src/comment/commentadapter.ts deleted file mode 100644 index c0f43a55..00000000 --- a/src/comment/commentadapter.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { IServicelocator } from "src/adapters/commentservicelocator"; -import { HasuraCommentService } from "src/adapters/hasura/comment.adapter"; -import { SunbirdCommentService } from "src/adapters/sunbirdrc/comment.adapter"; - -@Injectable() -export class CommentAdapter { - constructor( - private sunbirdProvider: SunbirdCommentService, - private hasuraProvider: HasuraCommentService - ) {} - buildCommentAdapter(): IServicelocator { - let adapter: IServicelocator; - - switch (process.env.ADAPTERSOURCE) { - case "sunbird": - adapter = this.sunbirdProvider; - break; - case "hasura": - adapter = this.hasuraProvider; - break; - } - return adapter; - } -} diff --git a/src/comment/dto/comment-search.dto.ts b/src/comment/dto/comment-search.dto.ts deleted file mode 100644 index 0cb5dd7b..00000000 --- a/src/comment/dto/comment-search.dto.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; - -export class CommentSearchDto { - @ApiProperty({ - type: String, - description: "Limit", - }) - limit: string; - - @ApiProperty({ - type: Number, - description: "Page", - }) - page: number; - - @ApiProperty({ - type: Object, - description: "Filters", - }) - @ApiPropertyOptional() - filters: object; - - constructor(partial: Partial) { - Object.assign(this, partial); - } -} diff --git a/src/comment/dto/comment.dto.ts b/src/comment/dto/comment.dto.ts deleted file mode 100644 index 0782fa3d..00000000 --- a/src/comment/dto/comment.dto.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Expose } from "class-transformer"; -import { ApiProperty } from "@nestjs/swagger"; - -export class CommentDto { - @Expose() - id: string; - - @Expose() - commentId: string; - - @ApiProperty({}) - @Expose() - contextId: string; - - @ApiProperty({}) - @Expose() - context: string; - - @Expose() - userId: string; - - @ApiProperty({}) - @Expose() - comment: string; - - @ApiProperty({}) - @Expose() - privacy: string; - - @ApiProperty({}) - @Expose() - parentId: string; - - @ApiProperty({}) - @Expose() - status: string; - - @Expose() - createdAt: string; - - @Expose() - updatedAt: string; - - constructor(obj: any) { - Object.assign(this, obj); - } -} diff --git a/src/common/database.module.ts b/src/common/database.module.ts new file mode 100644 index 00000000..8cf44fc5 --- /dev/null +++ b/src/common/database.module.ts @@ -0,0 +1,26 @@ +import { Module } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { User } from 'src/user/entities/user-entity'; + +@Module({ + imports: [ + TypeOrmModule.forRootAsync({ + useFactory: (configService: ConfigService) => ({ + type: 'postgres', + host: configService.get('POSTGRES_HOST'), + port: configService.get('POSTGRES_PORT'), + database: configService.get('POSTGRES_DATABASE'), + username: configService.get('POSTGRES_USERNAME'), + password: configService.get('POSTGRES_PASSWORD'), + // entities: [ + // User + // ], + autoLoadEntities: true, + }), + inject: [ConfigService], + }), + ], + providers: [ConfigService], +}) +export class DatabaseModule {} diff --git a/src/common/decorators/permission.config.ts b/src/common/decorators/permission.config.ts new file mode 100644 index 00000000..0861cdfd --- /dev/null +++ b/src/common/decorators/permission.config.ts @@ -0,0 +1,22 @@ +/** +List of permssions in the system +*/ + +export const PERMISSIONS = { + USERS_CREATE: "users.create", + USERS_READ: "users.read", + USERS_UPDATE: "users.update", + USERS_DELETE: "users.delete", + ATTENDANCE_CREATE: "attendance.create", + ATTENDANCE_READ: "attendance.read", + ATTENDANCE_UPDATE: "attendance.update", + ATTENDANCE_DELETE: "attendance.delete", + COHORT_CREATE: "cohort.create", + COHORT_READ: "cohort.read", + COHORT_UPDATE: "cohort.update", + COHORT_DELETE: "cohort.delete", + COHORTMEMBERS_CREATE: "cohortmembers.create", + COHORTMEMBERS_READ: "cohortmembers.read", + COHORTMEMBERS_UPDATE: "cohortmembers.update", + COHORTMEMBERS_DELETE: "cohortmembers.delete", +}; diff --git a/src/common/decorators/permission.decorator.ts b/src/common/decorators/permission.decorator.ts new file mode 100644 index 00000000..ecc191a0 --- /dev/null +++ b/src/common/decorators/permission.decorator.ts @@ -0,0 +1,12 @@ +import { SetMetadata, createParamDecorator, ExecutionContext } from '@nestjs/common'; + +// Define a custom decorator to set permission metadata +export const Permissions = (...permissions: string[]) => SetMetadata('permissions', permissions); + +// Create a custom decorator to access permissions metadata +export const PermissionsDecorator = createParamDecorator( + (data: unknown, ctx: ExecutionContext) => { + const request = ctx.switchToHttp().getRequest(); + return request.permissions; + }, +); diff --git a/src/common/filters/exception.filter.ts b/src/common/filters/exception.filter.ts new file mode 100644 index 00000000..1ca84985 --- /dev/null +++ b/src/common/filters/exception.filter.ts @@ -0,0 +1,27 @@ +import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common'; +import { Response, Request } from 'express'; +import APIResponse from '../responses/response'; + +@Catch() +export class AllExceptionsFilter implements ExceptionFilter { + constructor(private readonly apiId?: string) { } + + catch(exception: unknown, host: ArgumentsHost) { + const ctx = host.switchToHttp(); + const response = ctx.getResponse(); + const status = exception instanceof HttpException ? exception.getStatus() : 500; + const exceptionResponse = exception instanceof HttpException ? exception.getResponse() : null; + const errorMessage = + exception instanceof HttpException + ? (exceptionResponse as any).message || exception.message + : 'Internal server error'; + const detailedErrorMessage = `${errorMessage}`; + APIResponse.error( + response, + this.apiId, + detailedErrorMessage, + exception instanceof HttpException ? exception.name : 'Internal Server Error', // error + status + ); + } +} \ No newline at end of file diff --git a/src/common/guards/keycloak.guard.ts b/src/common/guards/keycloak.guard.ts new file mode 100644 index 00000000..768afc54 --- /dev/null +++ b/src/common/guards/keycloak.guard.ts @@ -0,0 +1,5 @@ +import { Injectable } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; + +@Injectable() +export class JwtAuthGuard extends AuthGuard('jwt-keycloak') {} diff --git a/src/common/guards/keycloak.strategy.ts b/src/common/guards/keycloak.strategy.ts new file mode 100644 index 00000000..a2d83467 --- /dev/null +++ b/src/common/guards/keycloak.strategy.ts @@ -0,0 +1,28 @@ +import { ExtractJwt, Strategy } from "passport-jwt"; +import { PassportStrategy } from "@nestjs/passport"; +import { Injectable } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; + +@Injectable() +export class JwtStrategy extends PassportStrategy(Strategy, "jwt-keycloak") { + constructor(configService: ConfigService) { + super({ + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + ignoreExpiration: false, + secretOrKey: configService.get("KEYCLOAK_REALM_RSA_PUBLIC_KEY"), + }); + } + + async validate(payload: any) { + /** + * This can be obtained via req.user in the Controllers + * This is where we validate that the user is valid and delimit the payload returned to req.user + */ + // console.log(payload, "payload"); + return { + userId: payload.sub, + name: payload.name, + username: payload.preferred_username, + }; + } +} diff --git a/src/common/guards/rbac.guard.ts b/src/common/guards/rbac.guard.ts new file mode 100644 index 00000000..b8e6a586 --- /dev/null +++ b/src/common/guards/rbac.guard.ts @@ -0,0 +1,31 @@ +import { ExecutionContext, Injectable } from "@nestjs/common"; +import { Reflector } from "@nestjs/core"; +import { AuthGuard } from "@nestjs/passport"; +import { Observable } from "rxjs"; + +@Injectable() +export class RbacAuthGuard extends AuthGuard("jwt-rbac") { + constructor(private readonly reflector: Reflector) { + super(); + } + + canActivate( + context: ExecutionContext + ): boolean | Promise | Observable { + // Required permissions come from permission decorator for each API end point + const requiredPermissions = this.reflector.get( + "permissions", + context.getHandler() + ); + + const payload = super.getRequest(context).user; + + if (!requiredPermissions) { + return true; // No permissions required, allow access + } + payload.requiredPermissions = requiredPermissions; + // const request = context.switchToHttp().getRequest(); + // request.requiredPermissions = requiredPermissions; + return super.canActivate(context); + } +} diff --git a/src/common/guards/rbac.strategy.ts b/src/common/guards/rbac.strategy.ts new file mode 100644 index 00000000..5a790f81 --- /dev/null +++ b/src/common/guards/rbac.strategy.ts @@ -0,0 +1,47 @@ +import { ExtractJwt, Strategy } from "passport-jwt"; +import { PassportStrategy } from "@nestjs/passport"; +import { Injectable, UnauthorizedException } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; + +@Injectable() +export class RbacJwtStrategy extends PassportStrategy(Strategy, "jwt-rbac") { + constructor(private configService: ConfigService) { + super({ + jwtFromRequest: ExtractJwt.fromHeader("rbac_token"), + ignoreExpiration: false, + secretOrKey: configService.get("RBAC_JWT_SECRET"), + passReqToCallback: true, + }); + } + + async validate(request: any, payload: any) { + const requiredPermissions = request.user.requiredPermissions; + + const userPermissions = payload.userData.privileges; + const roles = payload.userData.roles; + + if (roles.includes("admin")) { + return payload; + } + + if ( + !( + payload.iss === this.configService.get("ISSUER") && + payload.aud === this.configService.get("AUDIENCE") && + userPermissions.length > 0 + ) + ) { + throw new UnauthorizedException(); + } + + const isAuthorized = requiredPermissions.every((permission: string) => + userPermissions.includes(permission) + ); + + if (isAuthorized) { + return payload; + } + + throw new UnauthorizedException(); + } +} diff --git a/src/common/responses/response-interface.ts b/src/common/responses/response-interface.ts new file mode 100644 index 00000000..159932e9 --- /dev/null +++ b/src/common/responses/response-interface.ts @@ -0,0 +1,25 @@ +import { IsOptional } from "class-validator"; +// structure for server responses +export interface ServerResponse { + // api id + id: string; + // response param + params: Params; + // response code + responseCode: number; + //server result + result: any; + // time stamp + ts: string; + // api version + ver: string; + headers?: any; + response: any; +} +export interface Params { + resmsgid: string; + err?: any; + status: string; + errmsg?: any; + successmessage?: string; +} diff --git a/src/common/responses/response.ts b/src/common/responses/response.ts new file mode 100644 index 00000000..26a76389 --- /dev/null +++ b/src/common/responses/response.ts @@ -0,0 +1,60 @@ +import { v4 } from 'uuid'; +import { Params } from './response-interface'; +import { Response } from 'express'; +export default class APIResponse { + public static success( + response: Response, + id: string, + result: Type, + statusCode: number, + successmessage: string + ) { + try { + const params: Params = { + resmsgid: v4(), + status: 'successful', + err: null, + errmsg: null, + successmessage: successmessage + }; + const resObj = { + id, + ver: '1.0', + ts: new Date().toISOString(), + params, + responseCode: statusCode, + result, + }; + return response.status(statusCode).json(resObj); + } catch (e) { + return e; + } + } + public static error( + response: Response, + id: string, + errmsg: string, + error: string, + statusCode: number, + ) { + try { + const params: Params = { + resmsgid: v4(), + status: 'failed', + err: error, + errmsg: errmsg, + }; + const resObj = { + id, + ver: '1.0', + ts: new Date().toISOString(), + params, + responseCode: statusCode, + result: {}, + }; + return response.status(statusCode).json(resObj); + } catch (e) { + return e; + } + } +} diff --git a/src/common/utils/api-id.config.ts b/src/common/utils/api-id.config.ts new file mode 100644 index 00000000..400cfe23 --- /dev/null +++ b/src/common/utils/api-id.config.ts @@ -0,0 +1,41 @@ +export const APIID = { + USER_GET: "api.user.get", + USER_CREATE: "api.user.create", + USER_UPDATE: "api.user.update", + USER_LIST: "api.user.list", + USER_RESET_PASSWORD: "api.user.resetPassword", + USER_DELETE: "api.user.delete", + ROLE_GET: "api.role.get", + ROLE_CREATE: "api.role.create", + ROLE_UPDATE: "api.role.update", + ROLE_SEARCH: "api.role.search", + ROLE_DELETE: "api.role.delete", + PRIVILEGE_BYROLEID: "api.privilegebyRoleId.get", + PRIVILEGE_BYPRIVILEGEID: 'api.privilegebyPrivilegeId.get', + PRIVILEGE_CREATE: 'api.privilege.create', + PRIVILEGE_DELETE: 'api.privilege.delete', + USERROLE_CREATE: "api.userRole.create", + USERROLE_GET: "api.userRole.get", + USERROLE_DELETE: "api.userRole.delete", + COHORT_MEMBER_GET: "api.cohortmember.get", + COHORT_MEMBER_CREATE: "api.cohortmember.create", + COHORT_MEMBER_UPDATE: "api.cohortmember.update", + COHORT_MEMBER_SEARCH: "api.cohortmember.list", + COHORT_MEMBER_DELETE: "api.cohortmember.delete", + ASSIGNPRIVILEGE_CREATE: "api.assignprivilege.create", + ASSIGNPRIVILEGE_GET: "api.assignprivilege.get", + COHORT_CREATE: "api.cohort.create", + COHORT_LIST: "api.cohort.list", + COHORT_READ: "api.cohort.read", + COHORT_UPDATE: "api.cohort.update", + COHORT_DELETE: "api.cohort.delete", + ASSIGN_TENANT_CREATE:"api.assigntenant.create", + FIELDS_CREATE: "api.fields.create", + FIELDS_SEARCH:"api.fields.search", + FIELDVALUES_CREATE: "api.fieldValues.create", + FIELDVALUES_SEARCH: "api.fieldValues.search", + LOGIN: "api.login", + LOGOUT: "api.logout", + REFRESH: "api.refresh", + USER_AUTH: "api.user.auth" +} diff --git a/src/adapters/hasura/keycloak.adapter.util.ts b/src/common/utils/keycloak.adapter.util.ts similarity index 84% rename from src/adapters/hasura/keycloak.adapter.util.ts rename to src/common/utils/keycloak.adapter.util.ts index f2e3d18b..c2dfabfb 100644 --- a/src/adapters/hasura/keycloak.adapter.util.ts +++ b/src/common/utils/keycloak.adapter.util.ts @@ -68,7 +68,7 @@ async function createUserInKeyCloak(query, token) { lastName: lname, enabled: "true", username: query.username, - groups: [getUserGroup(query.role)], + // groups: [getUserGroup(query.role)], credentials: [ { temporary: "false", @@ -87,20 +87,25 @@ async function createUserInKeyCloak(query, token) { }, data: data, }; - let userResponse; + // try { + // userResponse = await axios(config); + // } catch (e) { + // console.log(e.response, "Keycloak Creation error"); + // return e; + // } + + // const userString = userResponse.headers.location; + // const index = userString.lastIndexOf("/"); + // const userId = userString.substring(index + 1); + + // return userId; try { - userResponse = await axios(config); - } catch (e) { - console.log(e.response, "Keycloak Creation error"); - return e; + const userResponse = await axios(config); + return userResponse.headers.location.split("/").pop(); + } catch (error) { + return "Error creating user: " + error.response.data.error; } - - const userString = userResponse.headers.location; - const index = userString.lastIndexOf("/"); - const userId = userString.substring(index + 1); - - return userId; } async function checkIfEmailExistsInKeycloak(email, token) { @@ -133,7 +138,7 @@ async function checkIfUsernameExistsInKeycloak(username, token) { url: process.env.KEYCLOAK + process.env.KEYCLOAK_ADMIN + - `?username=${username}`, + `?username=${username}&exact=true`, headers: { "Content-Type": "application/json", Authorization: "Bearer " + token, diff --git a/src/common/utils/keycloak.service.ts b/src/common/utils/keycloak.service.ts new file mode 100644 index 00000000..b291f3b3 --- /dev/null +++ b/src/common/utils/keycloak.service.ts @@ -0,0 +1,136 @@ +import { HttpService } from "@nestjs/axios"; +import { Injectable } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import axios from "axios"; +import qs from "qs"; + +type LoginResponse = { + access_token: string; + scope: string; + refresh_token: string; + token_type: string; + session_state: string; + refresh_expires_in: number; + expires_in: number; +}; + +type UserInfoResponse = { + sub: string; + email_verified: boolean; + preferred_username: string; +}; + +@Injectable() +export class KeycloakService { + private baseURL: string; + private realm: string; + private clientId: string; + private clientSecret: string; + private axios; + userToken: any; + constructor(private readonly configService: ConfigService) { + this.baseURL = this.configService.get("KEYCLOAK"); + this.userToken = this.configService.get("KEYCLOAK_USER_TOKEN"); + this.realm = this.configService.get("KEYCLOAK_REALM"); + this.clientId = this.configService.get("KEYCLOAK_CLIENT_ID"); + this.clientSecret = this.configService.get("KEYCLOAK_CLIENT_SECRET"); + this.axios = axios.create(); + } + + async login(username: string, password: string): Promise { + const data = qs.stringify({ + client_id: this.clientId, + client_secret: this.clientSecret, + grant_type: "password", + username, + password, + }); + + const axiosConfig = { + method: "post", + url: `${this.baseURL}realms/${this.realm}/protocol/openid-connect/token`, + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + data: data, + }; + const res = await this.axios(axiosConfig); + return res.data; + } + + async getUserInfo(accessToken: string): Promise { + // const { data } = await firstValueFrom( + // this.httpService.get( + // `${this.baseURL}/auth/realms/${this.realm}/protocol/openid-connect/userinfo`, + // { + // headers: { + // Authorization: `Bearer ${accessToken}`, + // }, + // } + // ) + // ); + + // const data = qs.stringify({ + // client_id: this.clientId, + // client_secret: this.clientSecret, + // grant_type: "refresh_token", + // refresh_token: refreshToken, + // }); + + const axiosConfig = { + method: "post", + url: `${this.baseURL}realms/${this.realm}/protocol/openid-connect/userinfo`, + headers: { + "Content-Type": "application/x-www-form-urlencoded", + Authorization: `Bearer ${accessToken}`, + }, + }; + + const res = await this.axios(axiosConfig); + + return res.data; + } + + async refreshToken(refreshToken: string): Promise { + const data = qs.stringify({ + client_id: this.clientId, + client_secret: this.clientSecret, + grant_type: "refresh_token", + refresh_token: refreshToken, + }); + + const axiosConfig = { + method: "post", + url: `${this.baseURL}realms/${this.realm}/protocol/openid-connect/token`, + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + data: data, + }; + + const res = await this.axios(axiosConfig); + + return res.data; + } + + async logout(refreshToken: string) { + const data = qs.stringify({ + client_id: this.clientId, + client_secret: this.clientSecret, + refresh_token: refreshToken, + }); + + const axiosConfig = { + method: "post", + url: `${this.baseURL}realms/${this.realm}/protocol/openid-connect/logout`, + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + data: data, + }; + + const res = await this.axios(axiosConfig); + + return res.data; + } +} diff --git a/src/configs/config.controller.ts b/src/configs/config.controller.ts index deca0ff6..e67f244a 100644 --- a/src/configs/config.controller.ts +++ b/src/configs/config.controller.ts @@ -4,6 +4,7 @@ import { ApiForbiddenResponse, ApiCreatedResponse, ApiBasicAuth, + ApiExcludeController, } from "@nestjs/swagger"; import { Controller, @@ -19,7 +20,8 @@ import { Request } from "@nestjs/common"; import { ConfigDto } from "./dto/config.dto"; import { ConfigsAdapter } from "./configsadapter"; -@ApiTags("Config") +// @ApiTags("Config") +@ApiExcludeController() @Controller("config") export class ConfigController { constructor(private configsAdapter: ConfigsAdapter) {} @@ -27,8 +29,8 @@ export class ConfigController { @Get(":module/all") @ApiBasicAuth("access-token") @UseInterceptors(ClassSerializerInterceptor) - @ApiCreatedResponse({ description: "Config detail" }) - @ApiForbiddenResponse({ description: "Forbidden" }) + // @ApiCreatedResponse({ description: "Config detail" }) + // @ApiForbiddenResponse({ description: "Forbidden" }) @SerializeOptions({ strategy: "excludeAll", }) @@ -37,10 +39,10 @@ export class ConfigController { } @Post("") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Config has been created successfully." }) - @ApiBody({ type: ConfigDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) + // @ApiBasicAuth("access-token") + // @ApiCreatedResponse({ description: "Config has been created successfully." }) + // @ApiBody({ type: ConfigDto }) + // @ApiForbiddenResponse({ description: "Forbidden" }) @UseInterceptors(ClassSerializerInterceptor) public async createConfig( @Req() request: Request, @@ -52,9 +54,9 @@ export class ConfigController { } @Post(":multipleConfigs") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Config has been created successfully." }) - @ApiForbiddenResponse({ description: "Forbidden" }) + // @ApiBasicAuth("access-token") + // @ApiCreatedResponse({ description: "Config has been created successfully." }) + // @ApiForbiddenResponse({ description: "Forbidden" }) @UseInterceptors(ClassSerializerInterceptor) public async createModuleConfigs( @Req() request: Request, diff --git a/src/configs/configsadapter.ts b/src/configs/configsadapter.ts index 0fb6acc4..37f38ffd 100644 --- a/src/configs/configsadapter.ts +++ b/src/configs/configsadapter.ts @@ -1,21 +1,14 @@ import { Injectable } from "@nestjs/common"; import { IServicelocator } from "src/adapters/configservicelocator"; import { HasuraConfigService } from "src/adapters/hasura/config.adapter"; -import { SunbirdConfigService } from "src/adapters/sunbirdrc/config.adapter"; @Injectable() export class ConfigsAdapter { - constructor( - private sunbirdProvider: SunbirdConfigService, - private hasuraProvider: HasuraConfigService - ) {} + constructor(private hasuraProvider: HasuraConfigService) {} buildConfigsAdapter(): IServicelocator { let adapter: IServicelocator; switch (process.env.ADAPTERSOURCE) { - case "sunbird": - adapter = this.sunbirdProvider; - break; case "hasura": adapter = this.hasuraProvider; break; diff --git a/src/configs/configuration.module.ts b/src/configs/configuration.module.ts index a1736b24..b883bbd7 100644 --- a/src/configs/configuration.module.ts +++ b/src/configs/configuration.module.ts @@ -1,9 +1,5 @@ import { CacheModule, Module } from "@nestjs/common"; import { ConfigController } from "./config.controller"; -import { - SunbirdConfigService, - SunbirdConfigToken, -} from "../adapters/sunbirdrc/config.adapter"; import { HttpModule } from "@nestjs/axios"; import { HasuraConfigService, @@ -11,17 +7,9 @@ import { } from "src/adapters/hasura/config.adapter"; import { ConfigsAdapter } from "./configsadapter"; import { HasuraModule } from "src/adapters/hasura/hasura.module"; -import { SunbirdModule } from "src/adapters/sunbirdrc/subnbird.module"; -const ttl = process.env.TTL as never; + @Module({ - imports: [ - SunbirdModule, - HasuraModule, - HttpModule, - CacheModule.register({ - ttl: ttl, - }), - ], + imports: [HasuraModule, HttpModule], controllers: [ConfigController], providers: [ConfigsAdapter], }) diff --git a/src/course/course.controller.ts b/src/course/course.controller.ts index 3e24859f..257c6018 100644 --- a/src/course/course.controller.ts +++ b/src/course/course.controller.ts @@ -1,11 +1,11 @@ import { + ApiExcludeController, ApiForbiddenResponse, ApiOkResponse, ApiQuery, ApiTags, } from "@nestjs/swagger"; import { - CacheInterceptor, ClassSerializerInterceptor, Controller, Get, @@ -20,15 +20,16 @@ import { import { DikshaCourseToken } from "src/adapters/diksha/dikshaCourse.adapter"; import { IServicelocator } from "src/adapters/courseservicelocator"; -@ApiTags("Course") +// @ApiTags("Course") +@ApiExcludeController() @Controller("course") export class CourseController { constructor( - @Inject(DikshaCourseToken) private dikshaProvider: IServicelocator, + @Inject(DikshaCourseToken) private dikshaProvider: IServicelocator ) {} @Get(":adapter/search") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) + @UseInterceptors(ClassSerializerInterceptor) @ApiOkResponse({ description: "Get all Course detail." }) @ApiForbiddenResponse({ description: "Forbidden" }) @ApiQuery({ name: "subject", required: false }) @@ -58,7 +59,7 @@ export class CourseController { } @Get(":adapter/courseIds") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) + @UseInterceptors(ClassSerializerInterceptor) @ApiOkResponse({ description: "Get all Course detail." }) @ApiForbiddenResponse({ description: "Forbidden" }) @ApiQuery({ name: "courseIds", required: false }) @@ -72,7 +73,7 @@ export class CourseController { } } @Get(":adapter/hierarchy/courseid") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) + @UseInterceptors(ClassSerializerInterceptor) @ApiOkResponse({ description: "Get Course detail." }) @ApiForbiddenResponse({ description: "Forbidden" }) @ApiQuery({ name: "courseId", required: false }) @@ -87,7 +88,7 @@ export class CourseController { } @Get(":adapter/content/courseid") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) + @UseInterceptors(ClassSerializerInterceptor) @ApiOkResponse({ description: "Get Course detail." }) @ApiForbiddenResponse({ description: "Forbidden" }) @ApiQuery({ name: "courseId", required: false }) diff --git a/src/course/course.module.ts b/src/course/course.module.ts index 67325a8a..64eb291c 100644 --- a/src/course/course.module.ts +++ b/src/course/course.module.ts @@ -6,14 +6,9 @@ import { DikshaCourseService, DikshaCourseToken, } from "src/adapters/diksha/dikshaCourse.adapter"; -const ttl = process.env.TTL as never; + @Module({ - imports: [ - HttpModule, - CacheModule.register({ - ttl: ttl, - }), - ], + imports: [HttpModule], controllers: [CourseController], providers: [{ provide: DikshaCourseToken, useClass: DikshaCourseService }], }) diff --git a/src/courseTracking/courseTracking.controller.ts b/src/courseTracking/courseTracking.controller.ts index 0cc586bd..db1a2bf2 100644 --- a/src/courseTracking/courseTracking.controller.ts +++ b/src/courseTracking/courseTracking.controller.ts @@ -3,9 +3,9 @@ import { ApiForbiddenResponse, ApiCreatedResponse, ApiBasicAuth, - ApiBody, ApiQuery, ApiOkResponse, + ApiExcludeController, } from "@nestjs/swagger"; import { Controller, @@ -16,36 +16,35 @@ import { SerializeOptions, Req, Request, - CacheInterceptor, Post, - Body, Query, Put, } from "@nestjs/common"; import { CourseTrackingService } from "src/adapters/hasura/courseTracking.adapter"; import { CourseTrackingDto } from "./dto/courseTracking.dto"; -@ApiTags("Course Tracking") +// @ApiTags("Course Tracking") +@ApiExcludeController() @Controller("coursetracking") export class CourseTrackingController { constructor(private service: CourseTrackingService) {} @Post() - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Course Tracking has been created successfully.", - }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @ApiQuery({ name: "progressDetail", required: false }) - @ApiQuery({ name: "courseId", required: false }) - @ApiQuery({ name: "userId", required: false }) - @ApiQuery({ name: "contentIds", required: false }) - @ApiQuery({ name: "startTime", required: false }) - @ApiQuery({ name: "endTime", required: false }) - @ApiQuery({ name: "certificate", required: false }) - @ApiQuery({ name: "status", required: false }) - @ApiQuery({ name: "source", required: false }) + // @ApiBasicAuth("access-token") + // @ApiCreatedResponse({ + // description: "Course Tracking has been created successfully.", + // }) + // @ApiForbiddenResponse({ description: "Forbidden" }) + // @UseInterceptors(ClassSerializerInterceptor) + // @ApiQuery({ name: "progressDetail", required: false }) + // @ApiQuery({ name: "courseId", required: false }) + // @ApiQuery({ name: "userId", required: false }) + // @ApiQuery({ name: "contentIds", required: false }) + // @ApiQuery({ name: "startTime", required: false }) + // @ApiQuery({ name: "endTime", required: false }) + // @ApiQuery({ name: "certificate", required: false }) + // @ApiQuery({ name: "status", required: false }) + // @ApiQuery({ name: "source", required: false }) public async createCourse( @Req() request: Request, @Query("progressDetail") progressDetail: string, @@ -73,7 +72,7 @@ export class CourseTrackingController { } @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) + @UseInterceptors(ClassSerializerInterceptor) @ApiBasicAuth("access-token") @ApiCreatedResponse({ description: "Course Tracking detail" }) @ApiForbiddenResponse({ description: "Forbidden" }) @@ -88,21 +87,21 @@ export class CourseTrackingController { } @Put("/:id") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Course Tracking has been updated successfully.", - }) - @ApiForbiddenResponse({ description: "Forbidden" }) + // @ApiBasicAuth("access-token") + // @ApiCreatedResponse({ + // description: "Course Tracking has been updated successfully.", + // }) + // @ApiForbiddenResponse({ description: "Forbidden" }) @UseInterceptors(ClassSerializerInterceptor) - @ApiQuery({ name: "progressDetail", required: false }) - @ApiQuery({ name: "courseId", required: false }) - @ApiQuery({ name: "userId", required: false }) - @ApiQuery({ name: "contentIds", required: false }) - @ApiQuery({ name: "startTime", required: false }) - @ApiQuery({ name: "endTime", required: false }) - @ApiQuery({ name: "certificate", required: false }) - @ApiQuery({ name: "status", required: false }) - @ApiQuery({ name: "source", required: false }) + // @ApiQuery({ name: "progressDetail", required: false }) + // @ApiQuery({ name: "courseId", required: false }) + // @ApiQuery({ name: "userId", required: false }) + // @ApiQuery({ name: "contentIds", required: false }) + // @ApiQuery({ name: "startTime", required: false }) + // @ApiQuery({ name: "endTime", required: false }) + // @ApiQuery({ name: "certificate", required: false }) + // @ApiQuery({ name: "status", required: false }) + // @ApiQuery({ name: "source", required: false }) public async updateTracking( @Param("id") courseTrackingId: string, @Req() request: Request, @@ -132,19 +131,19 @@ export class CourseTrackingController { } @Post("/search") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Course Tracking list." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @SerializeOptions({ - strategy: "excludeAll", - }) - @ApiQuery({ name: "limit", required: false }) - @ApiQuery({ name: "courseId", required: false }) - @ApiQuery({ name: "userId", required: false }) - @ApiQuery({ name: "status", required: false }) - @ApiQuery({ name: "page", required: false }) - @ApiQuery({ name: "source", required: false }) + // @ApiBasicAuth("access-token") + // @ApiCreatedResponse({ description: "Course Tracking list." }) + // @ApiForbiddenResponse({ description: "Forbidden" }) + // @UseInterceptors(ClassSerializerInterceptor) + // @SerializeOptions({ + // strategy: "excludeAll", + // }) + // @ApiQuery({ name: "limit", required: false }) + // @ApiQuery({ name: "courseId", required: false }) + // @ApiQuery({ name: "userId", required: false }) + // @ApiQuery({ name: "status", required: false }) + // @ApiQuery({ name: "page", required: false }) + // @ApiQuery({ name: "source", required: false }) public async searchCourseTracking( @Query("limit") limit: string, @Query("courseId") courseId: string, diff --git a/src/courseTracking/courseTracking.module.ts b/src/courseTracking/courseTracking.module.ts index 0efb90cd..462d9712 100644 --- a/src/courseTracking/courseTracking.module.ts +++ b/src/courseTracking/courseTracking.module.ts @@ -1,15 +1,12 @@ -import { CacheModule, Module } from "@nestjs/common"; +import { Module } from "@nestjs/common"; import { HttpModule } from "@nestjs/axios"; import { ScheduleModule } from "@nestjs/schedule"; import { CourseTrackingService } from "src/adapters/hasura/courseTracking.adapter"; import { CourseTrackingController } from "./courseTracking.controller"; -const ttl = process.env.TTL as never; + @Module({ imports: [ HttpModule, - CacheModule.register({ - ttl: ttl, - }), ScheduleModule.forRoot(), ], controllers: [CourseTrackingController], diff --git a/src/error-response-typeorm.ts b/src/error-response-typeorm.ts new file mode 100644 index 00000000..2655aed0 --- /dev/null +++ b/src/error-response-typeorm.ts @@ -0,0 +1,13 @@ +import { Expose } from "class-transformer"; + +export class ErrorResponseTypeOrm { + @Expose() + statusCode: number; + + @Expose() + errorMessage: string; + + constructor(partial: Partial) { + Object.assign(this, partial); + } +} diff --git a/src/fields/dto/field-values-create.dto.ts b/src/fields/dto/field-values-create.dto.ts index b356f5bd..152c2b6f 100644 --- a/src/fields/dto/field-values-create.dto.ts +++ b/src/fields/dto/field-values-create.dto.ts @@ -10,20 +10,10 @@ import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; export class FieldValuesCreateDto { //fieldId - @ApiProperty({ - type: String, - description: "The fieldId of the field values", - default: "", - }) @Expose() fieldId: string; //value - @ApiProperty({ - type: String, - description: "The value of the field values", - default: "", - }) @Expose() value: string; diff --git a/src/fields/dto/field-values.dto.ts b/src/fields/dto/field-values.dto.ts index 766e21fc..61af23db 100644 --- a/src/fields/dto/field-values.dto.ts +++ b/src/fields/dto/field-values.dto.ts @@ -9,56 +9,30 @@ import { import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; export class FieldValuesDto { - //generated fields - @Expose() - fieldValuesId: string; + @Expose() createdAt: string; + @Expose() updatedAt: string; //fieldId - @ApiProperty({ - type: String, - description: "The fieldId of the field values", - default: "", - }) @Expose() fieldId: string; //value - @ApiProperty({ - type: String, - description: "The value of the field values", - default: "", - }) @Expose() value: string; //itemId - @ApiProperty({ - type: String, - description: "The itemId of the field values", - default: "", - }) @Expose() itemId: string; //createdBy - @ApiProperty({ - type: String, - description: "The createdBy of the field values", - default: "", - }) @Expose() createdBy: string; //updatedBy - @ApiProperty({ - type: String, - description: "The updatedBy of the field values", - default: "", - }) @Expose() updatedBy: string; diff --git a/src/fields/dto/fields.dto.ts b/src/fields/dto/fields.dto.ts index 460bda36..d36c0fc6 100644 --- a/src/fields/dto/fields.dto.ts +++ b/src/fields/dto/fields.dto.ts @@ -11,13 +11,7 @@ import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; export class FieldsDto { //generated fields @Expose() - tenantId: string; - @Expose() fieldId: string; - @Expose() - createdAt: string; - @Expose() - updatedAt: string; //assetId @ApiProperty({ @@ -37,23 +31,6 @@ export class FieldsDto { @Expose() context: string; - //contextId - @ApiProperty({ - type: String, - description: "The contextId of the fields", - default: "", - }) - @Expose() - contextId: string; - - //render - @ApiProperty({ - type: Object, - description: "The form render json of the fields", - }) - @Expose() - render: any; - //groupId @ApiProperty({ type: String, @@ -171,6 +148,15 @@ export class FieldsDto { @Expose() onlyUseInSubform: Boolean; + @Expose() + tenantId: string; + + @Expose() + createdAt: string; + + @Expose() + updatedAt: string; + //createdBy @ApiProperty({ type: String, @@ -189,6 +175,41 @@ export class FieldsDto { @Expose() updatedBy: string; + //contextId + @ApiProperty({ + type: String, + description: "The contextId of the fields", + default: "", + }) + @Expose() + contextId: string; + + //render + @ApiProperty({ + type: Object, + description: "The form render json of the fields", + }) + @Expose() + render: any; + + //contexType + @ApiProperty({ + type: String, + description: "The contexType of the fields", + default: "", + }) + @Expose() + contexType: string; + + //contexType + @ApiProperty({ + type: Object, + description: "The fieldParams of the fields", + default: "", + }) + @Expose() + fieldParams: object; + constructor(obj: any) { Object.assign(this, obj); } diff --git a/src/fields/entities/fields-values.entity.ts b/src/fields/entities/fields-values.entity.ts new file mode 100644 index 00000000..122e8ec0 --- /dev/null +++ b/src/fields/entities/fields-values.entity.ts @@ -0,0 +1,40 @@ +import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm'; +@Entity({ name: 'FieldValues' }) +export class FieldValues { + + @Column({ type: 'text', nullable: false }) + value: string; + + @PrimaryGeneratedColumn('uuid') + fieldValuesId: string; + + @Column({ type: 'uuid', nullable: false, default: () => 'gen_random_uuid()' }) + itemId: string; + + @Column({ type: 'uuid', nullable: false, name: 'fieldId' }) + fieldId: string; + + @CreateDateColumn({ + type: "timestamp with time zone", + default: () => "CURRENT_TIMESTAMP", + }) + createdAt: Date; + + @UpdateDateColumn({ + type: "timestamp with time zone", + default: () => "CURRENT_TIMESTAMP", + }) + updatedAt: Date; + + @CreateDateColumn({ + type: "timestamp with time zone", + default: () => "CURRENT_TIMESTAMP", + }) + createdBy: Date; + + @UpdateDateColumn({ + type: "timestamp with time zone", + default: () => "CURRENT_TIMESTAMP", + }) + updatedBy: Date; +} diff --git a/src/fields/entities/fields.entity.ts b/src/fields/entities/fields.entity.ts new file mode 100644 index 00000000..13a2799f --- /dev/null +++ b/src/fields/entities/fields.entity.ts @@ -0,0 +1,98 @@ +import { + Entity, + Column, + PrimaryColumn, + CreateDateColumn, + UpdateDateColumn, +} from 'typeorm'; + +@Entity({ name: 'Fields' }) +export class Fields { + + @PrimaryColumn({ type: "uuid" }) + fieldId: string; + + @Column({ type: 'varchar' }) + assetId: string; + + @Column({ type: 'varchar' }) + context: string; + + @Column({ type: 'varchar' }) + groupId: string; + + @Column({ type: 'varchar' }) + name: string; + + @Column({ type: 'varchar' }) + label: string; + + @Column({ type: 'varchar' }) + defaultValue: string; + + @Column({ type: 'varchar' }) + type: string; + + @Column({ type: 'text' }) + note: string; + + @Column({ type: 'text' }) + description: string; + + @Column({ type: 'text' }) + state: string; + + @Column({ type: 'boolean' }) + required: boolean; + + @Column({ type: 'int' }) + ordering: number; + + @Column({ type: 'text' }) + metadata: string; + + @Column({ type: 'varchar' }) + access: string; + + @Column({ type: 'boolean' }) + onlyUseInSubform: boolean; + + @Column({ type: 'uuid' }) + tenantId: string; + + @CreateDateColumn({ + type: "timestamp with time zone", + default: () => "CURRENT_TIMESTAMP", + }) + createdAt: Date; + + @UpdateDateColumn({ + type: "timestamp with time zone", + default: () => "CURRENT_TIMESTAMP", + }) + updatedAt: Date; + + @CreateDateColumn({ + type: "timestamp with time zone", + default: () => "CURRENT_TIMESTAMP", + }) + createdBy: Date; + + @UpdateDateColumn({ + type: "timestamp with time zone", + default: () => "CURRENT_TIMESTAMP", + }) + updatedBy: Date; + + @Column({ type: 'uuid' }) + contextId: string; + + @Column({ type: 'varchar' }) + render: string; + + @Column({ type: 'varchar' }) + contextType: string; + + @Column({ type: 'jsonb' }) + fieldParams: object; +} diff --git a/src/fields/fields.controller.ts b/src/fields/fields.controller.ts index 9e57c896..676c725e 100644 --- a/src/fields/fields.controller.ts +++ b/src/fields/fields.controller.ts @@ -1,92 +1,68 @@ import { ApiTags, ApiBody, - ApiOkResponse, ApiForbiddenResponse, ApiCreatedResponse, ApiBasicAuth, - ApiConsumes, ApiHeader, } from "@nestjs/swagger"; import { Controller, - Get, Post, Body, - Put, - Param, - UseInterceptors, - ClassSerializerInterceptor, SerializeOptions, Req, - Query, - CacheInterceptor, - UploadedFile, Headers, + UseGuards, + Res, + UseFilters, } from "@nestjs/common"; import { FieldsSearchDto } from "./dto/fields-search.dto"; import { Request } from "@nestjs/common"; import { FieldsDto } from "./dto/fields.dto"; import { FileInterceptor } from "@nestjs/platform-express"; import { diskStorage } from "multer"; - +import { Response } from "express"; import { FieldsAdapter } from "./fieldsadapter"; import { FieldValuesDto } from "./dto/field-values.dto"; import { FieldValuesSearchDto } from "./dto/field-values-search.dto"; +import { FieldsService } from "./fields.service"; +import { JwtAuthGuard } from "src/common/guards/keycloak.guard"; +import { AllExceptionsFilter } from "src/common/filters/exception.filter"; +import { APIID } from "src/common/utils/api-id.config"; @ApiTags("Fields") @Controller("fields") +@UseGuards(JwtAuthGuard) export class FieldsController { - constructor(private fieldsAdapter: FieldsAdapter) {} + constructor( + private fieldsAdapter: FieldsAdapter, + private readonly fieldsService: FieldsService + ) {} //fields //create fields - @Post() + @Post("/create") @ApiBasicAuth("access-token") @ApiCreatedResponse({ description: "Fields has been created successfully." }) @ApiBody({ type: FieldsDto }) @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) + // @UseInterceptors(ClassSerializerInterceptor) @ApiHeader({ name: "tenantid", }) public async createFields( @Headers() headers, @Req() request: Request, - @Body() fieldsDto: FieldsDto + @Body() fieldsDto: FieldsDto, + @Res() response: Response ) { let tenantid = headers["tenantid"]; const payload = { tenantId: tenantid, }; Object.assign(fieldsDto, payload); - - return this.fieldsAdapter - .buildFieldsAdapter() - .createFields(request, fieldsDto); - } - - //get fields - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Fields detail" }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @SerializeOptions({ - strategy: "excludeAll", - }) - @ApiHeader({ - name: "tenantid", - }) - public async getFields( - @Headers() headers, - @Param("id") fieldsId: string, - @Req() request: Request - ) { - let tenantid = headers["tenantid"]; - return this.fieldsAdapter - .buildFieldsAdapter() - .getFields(tenantid, fieldsId, request); + return await this.fieldsAdapter.buildFieldsAdapter().createFields(request, fieldsDto, response); } //search @@ -95,7 +71,7 @@ export class FieldsController { @ApiCreatedResponse({ description: "Fields list." }) @ApiBody({ type: FieldsSearchDto }) @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) + // @UseInterceptors(ClassSerializerInterceptor) @SerializeOptions({ strategy: "excludeAll", }) @@ -105,67 +81,39 @@ export class FieldsController { public async searchFields( @Headers() headers, @Req() request: Request, - @Body() fieldsSearchDto: FieldsSearchDto + @Body() fieldsSearchDto: FieldsSearchDto, + @Res() response: Response ) { let tenantid = headers["tenantid"]; - return this.fieldsAdapter - .buildFieldsAdapter() - .searchFields(tenantid, request, fieldsSearchDto); - } - - //update - @Put("/:id") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Fields has been updated successfully." }) - @ApiBody({ type: FieldsDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async updateFields( - @Param("id") fieldsId: string, - @Req() request: Request, - @Body() fieldsDto: FieldsDto - ) { - return this.fieldsAdapter - .buildFieldsAdapter() - .updateFields(fieldsId, request, fieldsDto); + return await this.fieldsAdapter.buildFieldsAdapter().searchFields( + tenantid, + request, + fieldsSearchDto, + response + ); } //field values //create fields values - @Post("/values") + @UseFilters(new AllExceptionsFilter(APIID.FIELDVALUES_CREATE)) + @Post("/values/create") @ApiBasicAuth("access-token") @ApiCreatedResponse({ description: "Fields Values has been created successfully.", }) @ApiBody({ type: FieldValuesDto }) @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) + // @UseInterceptors(ClassSerializerInterceptor) public async createFieldValues( @Req() request: Request, - @Body() fieldValuesDto: FieldValuesDto + @Body() fieldValuesDto: FieldValuesDto, + @Res() response: Response ) { - return this.fieldsAdapter - .buildFieldsAdapter() - .createFieldValues(request, fieldValuesDto); - } - - //get fields values - @Get("/values/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Fields Values detail" }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async getFieldValues( - @Param("id") id: string, - @Req() request: Request - ) { - return this.fieldsAdapter.buildFieldsAdapter().getFieldValues(id, request); + return await this.fieldsAdapter.buildFieldsAdapter().createFieldValues( + request, + fieldValuesDto, + response + ); } //search fields values @@ -174,38 +122,19 @@ export class FieldsController { @ApiCreatedResponse({ description: "Fields Values list." }) @ApiBody({ type: FieldValuesSearchDto }) @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) + // @UseInterceptors(ClassSerializerInterceptor) @SerializeOptions({ strategy: "excludeAll", }) public async searchFieldValues( @Req() request: Request, - @Body() fieldValuesSearchDto: FieldValuesSearchDto - ) { - return this.fieldsAdapter - .buildFieldsAdapter() - .searchFieldValues(request, fieldValuesSearchDto); - } - - //update - @Put("/values/:id") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Fields Values has been updated successfully.", - }) - @ApiBody({ type: FieldValuesDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async updateFieldValues( - @Param("id") id: string, - @Req() request: Request, - @Body() fieldValuesDto: FieldValuesDto + @Body() fieldValuesSearchDto: FieldValuesSearchDto, + @Res() response: Response ) { - return this.fieldsAdapter - .buildFieldsAdapter() - .updateFieldValues(id, request, fieldValuesDto); + return await this.fieldsAdapter.buildFieldsAdapter().searchFieldValues( + request, + fieldValuesSearchDto, + response + ); } } diff --git a/src/fields/fields.module.ts b/src/fields/fields.module.ts index a46da0e6..dd16fe2c 100644 --- a/src/fields/fields.module.ts +++ b/src/fields/fields.module.ts @@ -3,16 +3,21 @@ import { FieldsController } from "./fields.controller"; import { HttpModule } from "@nestjs/axios"; import { FieldsAdapter } from "./fieldsadapter"; import { HasuraModule } from "src/adapters/hasura/hasura.module"; -const ttl = process.env.TTL as never; +import { Fields } from "./entities/fields.entity"; +import { FieldValues } from "./entities/fields-values.entity"; +import { TypeOrmModule } from "@nestjs/typeorm"; +import { FieldsService } from "./fields.service"; +import { PostgresModule } from "src/adapters/postgres/potsgres-module"; + @Module({ imports: [ + TypeOrmModule.forFeature([Fields]), + TypeOrmModule.forFeature([FieldValues]), HttpModule, HasuraModule, - CacheModule.register({ - ttl: ttl, - }), + PostgresModule ], controllers: [FieldsController], - providers: [FieldsAdapter], + providers: [FieldsAdapter, FieldsService], }) export class FieldsModule {} diff --git a/src/fields/fields.service.ts b/src/fields/fields.service.ts new file mode 100644 index 00000000..a170f465 --- /dev/null +++ b/src/fields/fields.service.ts @@ -0,0 +1,275 @@ +import { HttpStatus, Injectable } from "@nestjs/common"; +import { FieldsDto } from "src/fields/dto/fields.dto"; +import { FieldsSearchDto } from "src/fields/dto/fields-search.dto"; +import { FieldValuesDto } from "src/fields/dto/field-values.dto"; +import { FieldValuesSearchDto } from "src/fields/dto/field-values-search.dto"; +import jwt_decode from "jwt-decode"; +import { ErrorResponse } from "src/error-response"; +import { Fields } from "./entities/fields.entity"; +import { FieldValues } from "./entities/fields-values.entity"; +import { InjectRepository } from "@nestjs/typeorm"; +import { IsNull, Not, Repository, getConnection, getRepository } from "typeorm"; +import { SuccessResponse } from "src/success-response"; +import { off } from "process"; +import APIResponse from "src/utils/response"; +import { log } from "util"; +import { ErrorResponseTypeOrm } from "src/error-response-typeorm"; + +@Injectable() +export class FieldsService { + constructor( + @InjectRepository(Fields) + private fieldsRepository: Repository, + @InjectRepository(FieldValues) + private fieldsValuesRepository: Repository, + ) { } + + //fields + async createFields(request: any, fieldsDto: FieldsDto) { + try { + + const fieldsData: any = {}; // Define an empty object to store field data + + Object.keys(fieldsDto).forEach((e) => { + if (fieldsDto[e] && fieldsDto[e] !== "") { + if (e === "render") { + fieldsData[e] = fieldsDto[e]; + } else if (Array.isArray(fieldsDto[e])) { + fieldsData[e] = JSON.stringify(fieldsDto[e]); + } else { + fieldsData[e] = fieldsDto[e]; + } + } + }); + + let result = await this.fieldsRepository.save(fieldsData); + return new SuccessResponse({ + statusCode: HttpStatus.CREATED, + message: "Ok.", + data: result, + }); + + } catch (e) { + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.INTERNAL_SERVER_ERROR, + errorMessage: e, + }); + } + } + + async searchFields(tenantId: string, request: any, fieldsSearchDto: FieldsSearchDto) { + try { + + const getConditionalData = APIResponse.search(fieldsSearchDto) + const offset = getConditionalData.offset ; + const limit = getConditionalData.limit ; + const whereClause = getConditionalData.whereClause ; + + const getFieldValue = await this.searchFieldData(offset, limit, whereClause) + + + return new SuccessResponse({ + statusCode: HttpStatus.OK, + message: 'Ok.', + totalCount : getFieldValue.totalCount, + data: getFieldValue.mappedResponse, + }); + + } catch (e) { + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.INTERNAL_SERVER_ERROR, + errorMessage: e, + }); + } + } + + async searchFieldData(offset: number, limit: string, searchData:any){ + let queryOptions: any = { + where: searchData, + }; + + if (offset !== undefined) { + queryOptions.skip = offset; + } + + if (limit !== undefined) { + queryOptions.take = parseInt(limit); + } + + + const [results, totalCount] = await this.fieldsRepository.findAndCount(queryOptions); + + const mappedResponse = await this.mappedResponseField(results); + return {mappedResponse, totalCount}; + } + + async createFieldValues(request: any, fieldValuesDto: FieldValuesDto) { + try { + + const fieldsData: any = {}; + Object.keys(fieldValuesDto).forEach((e) => { + if (fieldValuesDto[e] && fieldValuesDto[e] != "") { + if (Array.isArray(fieldValuesDto[e])) { + fieldsData[e] = JSON.stringify(fieldValuesDto[e]); + } else { + fieldsData[e] = fieldValuesDto[e]; + } + } + }); + + let result = await this.fieldsValuesRepository.save(fieldsData); + return new SuccessResponse({ + statusCode: HttpStatus.CREATED, + message: "Ok.", + data: result, + }); + + } catch (e) { + return new ErrorResponseTypeOrm({ + statusCode:HttpStatus.INTERNAL_SERVER_ERROR, + errorMessage: e, + }); + } + } + + async searchFieldValues(request: any, fieldValuesSearchDto: FieldValuesSearchDto) { + try { + const getConditionalData = APIResponse.search(fieldValuesSearchDto) + const offset = getConditionalData.offset ; + const limit = getConditionalData.limit ; + const whereClause = getConditionalData.whereClause ; + + const getFieldValue = await this.getSearchFieldValueData(offset, limit, whereClause) + + return new SuccessResponse({ + statusCode: HttpStatus.OK, + message: 'Ok.', + totalCount: getFieldValue.totalCount, + data: getFieldValue.mappedResponse, + }); + + } catch (e) { + return new ErrorResponseTypeOrm({ + statusCode: HttpStatus.INTERNAL_SERVER_ERROR, + errorMessage: e, + }); + } + } + + async getSearchFieldValueData(offset: number, limit: string, searchData:any){ + let queryOptions: any = { + where: searchData, + }; + + if (offset !== undefined) { + queryOptions.skip = offset; + } + + if (limit !== undefined) { + queryOptions.take = parseInt(limit); + } + + const [results, totalCount] = await this.fieldsValuesRepository.findAndCount(queryOptions); + const mappedResponse = await this.mappedResponse(results); + + return {mappedResponse, totalCount}; + + } + + async searchFieldValueId(cohortId: string, fieldId: string){ + const response = await this.fieldsValuesRepository.findOne({ + where: { itemId: cohortId, fieldId: fieldId }, + }); + return response; + } + + async updateFieldValues(id: string, fieldValuesDto: FieldValuesDto) { + + try { + const fieldsData: any = {}; + Object.keys(fieldValuesDto).forEach((e) => { + if (fieldValuesDto[e] && fieldValuesDto[e] != "") { + if (Array.isArray(fieldValuesDto[e])) { + fieldsData[e] = JSON.stringify(fieldValuesDto[e]); + } else { + fieldsData[e] = fieldValuesDto[e]; + } + } + }); + const response = await this.fieldsValuesRepository.update(id, fieldValuesDto); + + return response; + } catch (e) { + return new ErrorResponse({ + errorCode: "400", + errorMessage: e, + }); + } + } + + public async getFieldsAndFieldsValues(cohortId:string){ + let query = `SELECT FV."value",FV."itemId", FV."fieldId", F."name" AS fieldname, F."label", F."context",F."type", F."state", F."contextType", F."fieldParams" FROM public."FieldValues" FV + LEFT JOIN public."Fields" F + ON FV."fieldId" = F."fieldId" where FV."itemId" =$1`; + const results = await this.fieldsValuesRepository.query(query, [cohortId]); + return results; + } + + + public async mappedResponse(result: any) { + const fieldValueResponse = result.map((item: any) => { + const fieldValueMapping = { + value: item?.value ? `${item.value}` : "", + fieldValuesId: item?.fieldValuesId ? `${item.fieldValuesId}` : "", + itemId: item?.itemId ? `${item.itemId}` : "", + fieldId: item?.fieldId ? `${item.fieldId}` : "", + createdAt: item?.createdAt ? `${item.createdAt}` : "", + updatedAt: item?.updatedAt ? `${item.updatedAt}` : "", + createdBy: item?.createdBy ? `${item.createdBy}` : "", + updatedBy: item?.updatedBy ? `${item.updatedBy}` : "", + }; + + return new FieldValuesDto(fieldValueMapping); + }); + + return fieldValueResponse; + } + + public async mappedResponseField(result: any) { + const fieldResponse = result.map((item: any) => { + + const fieldMapping = { + fieldId: item?.fieldId ? `${item.fieldId}` : "", + assetId: item?.assetId ? `${item.assetId}` : "", + context: item?.context ? `${item.context}` : "", + groupId: item?.groupId ? `${item.groupId}` : "", + name: item?.name ? `${item.name}` : "", + label: item?.label ? `${item.label}` : "", + defaultValue: item?.defaultValue ? `${item.defaultValue}` : "", + type: item?.type ? `${item.type}` : "", + note: item?.note ? `${item.note}` : "", + description: item?.description ? `${item.description}` : "", + state: item?.state ? `${item.state}` : "", + required: item?.required ? `${item.required}` : "", + ordering: item?.ordering ? `${item.ordering}` : "", + metadata: item?.metadata ? `${item.metadata}` : "", + access: item?.access ? `${item.access}` : "", + onlyUseInSubform: item?.onlyUseInSubform ? `${item.onlyUseInSubform}` : "", + tenantId: item?.tenantId ? `${item.tenantId}` : "", + createdAt: item?.createdAt ? `${item.createdAt}` : "", + updatedAt: item?.updatedAt ? `${item.updatedAt}` : "", + createdBy: item?.createdBy ? `${item.createdBy}` : "", + updatedBy: item?.updatedBy ? `${item.updatedBy}` : "", + contextId: item?.contextId ? `${item.contextId}` : "", + render: item?.render ? `${item.render}` : "", + contextType: item?.contextType ? `${item.contextType}` : "", + fieldParams: item?.fieldParams ? JSON.stringify(item.fieldParams) : "" + }; + + return new FieldsDto(fieldMapping); + }); + + return fieldResponse; + } + +} diff --git a/src/fields/fieldsadapter.ts b/src/fields/fieldsadapter.ts index bd99ac12..b02836b9 100644 --- a/src/fields/fieldsadapter.ts +++ b/src/fields/fieldsadapter.ts @@ -1,10 +1,11 @@ import { Injectable } from "@nestjs/common"; import { IServicelocatorfields } from "src/adapters/fieldsservicelocator"; import { HasuraFieldsService } from "src/adapters/hasura/fields.adapter"; +import { PostgresFieldsService } from "src/adapters/postgres/fields-adapter"; @Injectable() export class FieldsAdapter { - constructor(private hasuraProvider: HasuraFieldsService) {} + constructor(private hasuraProvider: HasuraFieldsService,private postgresProvider:PostgresFieldsService) {} buildFieldsAdapter(): IServicelocatorfields { let adapter: IServicelocatorfields; @@ -12,6 +13,9 @@ export class FieldsAdapter { case "hasura": adapter = this.hasuraProvider; break; + case "postgres": + adapter = this.postgresProvider; + break; } return adapter; } diff --git a/src/group/dto/group-response.dto.ts b/src/group/dto/group-response.dto.ts deleted file mode 100644 index dd7f5688..00000000 --- a/src/group/dto/group-response.dto.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface GroupResponseDto { - groupId: string; - name: string; - type: string; - status: string; -} diff --git a/src/group/dto/group.dto.ts b/src/group/dto/group.dto.ts deleted file mode 100644 index c56d28d9..00000000 --- a/src/group/dto/group.dto.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { Exclude, Expose } from "class-transformer"; -import { - MaxLength, - IsNotEmpty, - IsEmail, - IsString, - IsNumber, -} from "class-validator"; -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; - -export class GroupDto { - @Expose() - id: string; - - @Expose() - groupId: string; - - @ApiPropertyOptional({ - type: String, - description: "The schoolId of the group", - }) - @Expose() - schoolId: string; - - @ApiPropertyOptional({ - type: String, - description: "The name of the group", - }) - @Expose() - name: string; - - @ApiPropertyOptional({ - type: String, - description: "The type of the group", - }) - @Expose() - type: string; - - @ApiPropertyOptional({ - type: String, - description: "The section of the group", - }) - @Expose() - section: string; - - @ApiPropertyOptional({ - type: String, - description: "The status of the group", - }) - @Expose() - status: string; - - @ApiPropertyOptional({ - type: String, - description: "Teacher Id of Group", - }) - @Expose() - teacherId: string; - - @ApiPropertyOptional({ - type: String, - description: "Parent Id of Group", - }) - @Expose() - parentId: string; - - @ApiPropertyOptional() - @Expose() - deactivationReason: string; - - @ApiPropertyOptional({ - type: String, - description: "The mediumOfInstruction of the group", - }) - @Expose() - mediumOfInstruction: string; - - @ApiPropertyOptional({ type: "string", format: "binary" }) - @Expose() - image: string; - - @ApiPropertyOptional() - @Expose() - metaData: [string]; - - @ApiPropertyOptional() - @Expose() - option: [string]; - - @ApiPropertyOptional({ - description: "Grade against group", - }) - @Expose() - gradeLevel: string; - - @Expose() - createdAt: string; - - @Expose() - updatedAt: string; - - constructor(obj: any) { - Object.assign(this, obj); - } -} diff --git a/src/group/dto/studentGroupMembership.dto.ts b/src/group/dto/studentGroupMembership.dto.ts deleted file mode 100644 index 9611aecc..00000000 --- a/src/group/dto/studentGroupMembership.dto.ts +++ /dev/null @@ -1,293 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; -import { Exclude, Expose } from "class-transformer"; - -export class StudentGroupMembershipDto { - @Expose() - studentId: string; - - @ApiProperty() - @Expose() - refId1: string; - - @ApiProperty() - @Expose() - refId2: string; - - @ApiPropertyOptional() - @Expose() - aadhaar: string; - - @ApiProperty() - @Expose() - firstName: string; - - @ApiProperty() - @Expose() - middleName: string; - - @ApiProperty() - @Expose() - lastName: string; - - @ApiProperty() - @Expose() - schoolId: string; - - @ApiProperty() - @Expose() - studentPhoneNumber: Number; - - @ApiPropertyOptional() - @Expose() - studentEmail: string; - - @ApiProperty() - @Expose() - gender: string; - - @ApiProperty() - @Expose() - groupId: string; - - @ApiPropertyOptional() - @Expose() - socialCategory: string; - - @ApiPropertyOptional() - @Expose() - iscwsn: string; - - @ApiPropertyOptional() - @Expose() - religion: string; - - @ApiPropertyOptional() - @Expose() - singleGirl: Boolean; - - @ApiPropertyOptional() - @Expose() - weight: string; - - @ApiPropertyOptional() - @Expose() - height: string; - - @ApiPropertyOptional() - @Expose() - bloodGroup: string; - - @ApiProperty() - @Expose() - birthDate: string; - - @ApiPropertyOptional() - @Expose() - homeless: Boolean; - - @ApiProperty() - @Expose() - bpl: Boolean; - - @ApiProperty() - @Expose() - migrant: Boolean; - - @ApiProperty() - @Expose() - status: string; - - @ApiPropertyOptional() - @Expose() - fatherFirstName: string; - - @ApiPropertyOptional() - @Expose() - fatherMiddleName: string; - - @ApiPropertyOptional() - @Expose() - fatherLastName: string; - - @ApiPropertyOptional() - @Expose() - fatherPhoneNumber: Number; - - @ApiPropertyOptional() - @Expose() - fatherEmail: string; - - @ApiPropertyOptional() - @Expose() - motherFirstName: string; - - @ApiPropertyOptional() - @Expose() - motherMiddleName: string; - - @ApiPropertyOptional() - @Expose() - motherLastName: string; - - @ApiPropertyOptional() - @Expose() - motherPhoneNumber: Number; - - @ApiPropertyOptional() - @Expose() - motherEmail: string; - - @ApiPropertyOptional() - @Expose() - guardianFirstName: string; - - @ApiPropertyOptional() - @Expose() - guardianMiddleName: string; - - @ApiPropertyOptional() - @Expose() - guardianLastName: string; - - @ApiPropertyOptional() - @Expose() - guardianPhoneNumber: Number; - - @ApiPropertyOptional() - @Expose() - guardianEmail: string; - - @ApiPropertyOptional({ - type: "string", - format: "binary", - }) - @Expose() - image: string; - - @ApiPropertyOptional() - @Expose() - studentAddress: string; - - @ApiProperty() - @Expose() - village: string; - - @ApiProperty() - @Expose() - block: string; - - @ApiProperty() - @Expose() - district: string; - - @ApiProperty() - @Expose() - stateId: string; - - @ApiProperty() - @Expose() - pincode: Number; - - @ApiProperty() - @Expose() - locationId: string; - - @ApiPropertyOptional() - @Expose() - deactivationReason: string; - - @ApiPropertyOptional() - @Expose() - metaData: [string]; - - @Expose() - createdAt: string; - - @Expose() - updatedAt: string; - - @Expose() - createdBy: string; - - @Expose() - updatedBy: string; - - constructor(obj: any) { - this.studentId = obj?.id ? `${obj.id}` : ""; - this.refId1 = obj?.admission_number ? `${obj.admission_number}` : ""; - this.refId2 = obj?.ref_student_id ? `${obj.ref_student_id}` : ""; - this.aadhaar = obj?.aadhaar ? `${obj.aadhaar}` : ""; - this.firstName = obj?.name ? `${obj.name}` : ""; - this.middleName = obj?.middleName ? `${obj.middleName}` : ""; - this.lastName = obj?.lastName ? `${obj.lastName}` : ""; - this.groupId = obj?.grade_number ? `${obj.grade_number}` : ""; - this.schoolId = obj?.school_id ? `${obj.school_id}` : ""; - this.studentEmail = obj?.studentEmail ? `${obj.studentEmail}` : ""; - this.studentPhoneNumber = obj?.phone ? obj.phone : ""; - this.iscwsn = obj?.is_cwsn ? `${obj.is_cwsn}` : ""; - this.gender = obj?.gender ? `${obj.gender}` : ""; - this.socialCategory = obj?.socialCategory ? `${obj.socialCategory}` : ""; - this.religion = obj?.religion ? `${obj.religion}` : ""; - this.singleGirl = obj?.singleGirl ? obj.singleGirl : ""; - this.weight = obj?.weight ? `${obj.weight}` : ""; - this.height = obj?.height ? `${obj.height}` : ""; - this.bloodGroup = obj?.bloodGroup ? `${obj.bloodGroup}` : ""; - this.birthDate = obj?.dob ? `${obj.dob}` : ""; - this.homeless = obj?.homeless ? obj.homeless : ""; - this.bpl = obj?.is_bpl ? obj.is_bpl : ""; - this.migrant = obj?.is_migrant ? obj.is_migrant : ""; - this.status = obj?.status ? `${obj.status}` : ""; - - this.fatherFirstName = obj?.fatherFirstName ? `${obj.fatherFirstName}` : ""; - - this.fatherMiddleName = obj?.fatherMiddleName - ? `${obj.fatherMiddleName}` - : ""; - - this.fatherLastName = obj?.father_name ? `${obj.father_name}` : ""; - this.fatherPhoneNumber = obj?.fatherPhoneNumber - ? obj.fatherPhoneNumber - : ""; - this.fatherEmail = obj?.fatherEmail ? `${obj.fatherEmail}` : ""; - - this.motherFirstName = obj?.mother_name ? `${obj.mother_name}` : ""; - this.motherMiddleName = obj?.motherMiddleName - ? `${obj.motherMiddleName}` - : ""; - this.motherLastName = obj?.motherLastName ? `${obj.motherLastName}` : ""; - this.motherPhoneNumber = obj?.motherPhoneNumber - ? obj.motherPhoneNumber - : ""; - this.motherEmail = obj?.motherEmail ? `${obj.motherEmail}` : ""; - - this.guardianFirstName = obj?.guardianFirstName - ? `${obj.guardianFirstName}` - : ""; - this.guardianMiddleName = obj?.guardianMiddleName - ? `${obj.guardianMiddleName}` - : ""; - this.guardianLastName = obj?.guardianLastName - ? `${obj.guardianLastName}` - : ""; - this.guardianPhoneNumber = obj?.guardianPhoneNumber - ? obj.guardianPhoneNumber - : ""; - this.guardianEmail = obj?.guardianEmail ? `${obj.guardianEmail}` : ""; - this.image = obj?.image ? `${obj.image}` : ""; - this.deactivationReason = obj?.deactivationReason - ? `${obj.deactivationReason}` - : ""; - this.studentAddress = obj?.studentAddress ? `${obj.studentAddress}` : ""; - this.village = obj?.village ? `${obj.village}` : ""; - this.block = obj?.block ? `${obj.block}` : ""; - this.district = obj?.district ? `${obj.district}` : ""; - this.stateId = obj?.stateId ? `${obj.stateId}` : ""; - this.pincode = obj?.pincode ? obj.pincode : ""; - this.locationId = obj?.locationId ? `${obj.locationId}` : ""; - this.metaData = obj?.metaData ? obj.metaData : []; - this.createdAt = obj?.created ? `${obj.created}` : ""; - this.updatedAt = obj?.updated ? `${obj.updated}` : ""; - this.createdBy = obj?.osCreatedBy ? `${obj.osCreatedBy}` : ""; - this.updatedBy = obj?.osUpdatedBy ? `${obj.osUpdatedBy}` : ""; - } -} diff --git a/src/group/group.controller.spec.ts b/src/group/group.controller.spec.ts deleted file mode 100644 index 511d14dd..00000000 --- a/src/group/group.controller.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from "@nestjs/testing"; -import { GroupController } from "./group.controller"; - -describe("GroupController", () => { - let controller: GroupController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [GroupController], - }).compile(); - - controller = module.get(GroupController); - }); - - it("should be defined", () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/src/group/group.controller.ts b/src/group/group.controller.ts deleted file mode 100644 index 9257bdb8..00000000 --- a/src/group/group.controller.ts +++ /dev/null @@ -1,174 +0,0 @@ -import { - ApiTags, - ApiBody, - ApiOkResponse, - ApiForbiddenResponse, - ApiCreatedResponse, - ApiBasicAuth, - ApiConsumes, -} from "@nestjs/swagger"; -import { - Controller, - Get, - Post, - Body, - Put, - Param, - UseInterceptors, - ClassSerializerInterceptor, - SerializeOptions, - Req, - Query, - CacheInterceptor, - UploadedFile, -} from "@nestjs/common"; -import { GroupSearchDto } from "./dto/group-search.dto"; -import { Request } from "@nestjs/common"; -import { GroupDto } from "./dto/group.dto"; -import { FileInterceptor } from "@nestjs/platform-express"; -import { editFileName, imageFileFilter } from "./utils/file-upload.utils"; -import { diskStorage } from "multer"; - -import { GroupAdapter } from "./groupadapter"; - -@ApiTags("Group") -@Controller("group") -export class GroupController { - constructor(private groupAdapter: GroupAdapter) {} - - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Group detail" }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async getGroup(@Param("id") groupId: string, @Req() request: Request) { - return this.groupAdapter.buildGroupAdapter().getGroup(groupId, request); - } - - @Post() - @ApiConsumes("multipart/form-data") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Group has been created successfully." }) - @UseInterceptors( - FileInterceptor("image", { - storage: diskStorage({ - destination: process.env.IMAGEPATH, - filename: editFileName, - }), - fileFilter: imageFileFilter, - }) - ) - @ApiBody({ type: GroupDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async createGroup( - @Req() request: Request, - @Body() groupDto: GroupDto, - @UploadedFile() image - ) { - const response = { - image: image?.filename, - }; - Object.assign(groupDto, response); - - return this.groupAdapter.buildGroupAdapter().createGroup(request, groupDto); - } - - @Put("/:id") - @ApiConsumes("multipart/form-data") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Group has been updated successfully." }) - @UseInterceptors( - FileInterceptor("image", { - storage: diskStorage({ - destination: process.env.IMAGEPATH, - filename: editFileName, - }), - fileFilter: imageFileFilter, - }) - ) - @ApiBody({ type: GroupDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async updateGroup( - @Param("id") groupId: string, - @Req() request: Request, - @Body() groupDto: GroupDto, - @UploadedFile() image - ) { - const response = { - image: image?.filename, - }; - Object.assign(groupDto, response); - - return this.groupAdapter - .buildGroupAdapter() - .updateGroup(groupId, request, groupDto); - } - - @Post("/search") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Group list." }) - @ApiBody({ type: GroupSearchDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async searchGroup( - @Req() request: Request, - @Body() groupSearchDto: GroupSearchDto - ) { - return this.groupAdapter - .buildGroupAdapter() - .searchGroup(request, groupSearchDto); - } - - @Get(":groupId/participants") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "Group detail." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - public async findMembersOfGroup( - @Param("groupId") id: string, - @Query("role") role: string, - @Req() request: Request - ) { - return this.groupAdapter - .buildGroupAdapter() - .findMembersOfGroup(id, role, request); - } - - @Get("participant/:userId") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "Group detail." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - public async getGroupsByUserId( - @Param("userId") id: string, - @Query("role") role: string, - @Req() request: Request - ) { - return this.groupAdapter - .buildGroupAdapter() - .findGroupsByUserId(id, role, request); - } - - @Get(":groupId/child") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "Group detail." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - public async findMembersOfChildGroup( - @Param("groupId") id: string, - @Query("role") role: string, - @Req() request: Request - ) { - return this.groupAdapter - .buildGroupAdapter() - .findMembersOfChildGroup(id, role, request); - } -} diff --git a/src/group/group.module.ts b/src/group/group.module.ts deleted file mode 100644 index e067b124..00000000 --- a/src/group/group.module.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { CacheModule, Module } from "@nestjs/common"; -import { GroupController } from "./group.controller"; -import { HttpModule } from "@nestjs/axios"; -import { GroupAdapter } from "./groupadapter"; -import { HasuraModule } from "src/adapters/hasura/hasura.module"; -import { SunbirdModule } from "src/adapters/sunbirdrc/subnbird.module"; -const ttl = process.env.TTL as never; -@Module({ - imports: [ - HttpModule, - SunbirdModule, - HasuraModule, - CacheModule.register({ - ttl: ttl, - }), - ], - controllers: [GroupController], - providers: [GroupAdapter], -}) -export class GroupModule {} diff --git a/src/group/groupadapter.ts b/src/group/groupadapter.ts deleted file mode 100644 index d1ca91b3..00000000 --- a/src/group/groupadapter.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { IServicelocatorgroup } from "src/adapters/groupservicelocator"; -import { HasuraGroupService } from "src/adapters/hasura/group.adapter"; -import { SunbirdGroupService } from "src/adapters/sunbirdrc/group.adapter"; - -@Injectable() -export class GroupAdapter { - constructor( - private sunbirdProvider: SunbirdGroupService, - private hasuraProvider: HasuraGroupService - ) {} - buildGroupAdapter(): IServicelocatorgroup { - let adapter: IServicelocatorgroup; - - switch (process.env.ADAPTERSOURCE) { - case "sunbird": - adapter = this.sunbirdProvider; - break; - case "hasura": - adapter = this.hasuraProvider; - break; - } - return adapter; - } -} diff --git a/src/group/interfaces/group.interface.ts b/src/group/interfaces/group.interface.ts deleted file mode 100644 index 328b6f2a..00000000 --- a/src/group/interfaces/group.interface.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface GroupInterface { - groupId?: string; - name?: string; - type?: string; - status?: string; -} diff --git a/src/group/utils/file-upload.utils.ts b/src/group/utils/file-upload.utils.ts deleted file mode 100644 index a6ccb3cb..00000000 --- a/src/group/utils/file-upload.utils.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { extname } from "path"; - -export const imageFileFilter = (req, file, callback) => { - if (!file.originalname.match(/.(jpg|jpeg|png|gif)$/)) { - return callback(new Error("Only image files are allowed!"), false); - } - callback(null, true); -}; - -export const editFileName = (req, file, callback) => { - const name = file.originalname.split(".")[0]; - const fileExtName = extname(file.originalname); - const randomName = Array(4) - .fill(null) - .map(() => Math.round(Math.random() * 16).toString(16)) - .join(""); - callback(null, `${name}-${randomName}${fileExtName}`); -}; diff --git a/src/groupMembership/dto/groupMembership-search.dto.ts b/src/groupMembership/dto/groupMembership-search.dto.ts deleted file mode 100644 index 9c0b1557..00000000 --- a/src/groupMembership/dto/groupMembership-search.dto.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; - -export class GroupMembershipSearchDto { - @ApiProperty({ - type: String, - description: "Limit", - }) - limit: string; - - @ApiProperty({ - type: Number, - description: "Page", - }) - page: number; - - @ApiProperty({ - type: Object, - description: "Filters", - }) - @ApiPropertyOptional() - filters: object; - - constructor(partial: Partial) { - Object.assign(this, partial); - } -} diff --git a/src/groupMembership/dto/groupMembership.dto.ts b/src/groupMembership/dto/groupMembership.dto.ts deleted file mode 100644 index 1148f3bf..00000000 --- a/src/groupMembership/dto/groupMembership.dto.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Exclude, Expose } from "class-transformer"; -import { ApiProperty } from "@nestjs/swagger"; - -export class GroupMembershipDto { - @Expose() - id: string; - - @Expose() - groupMembershipId: string; - - @ApiProperty() - @Expose() - groupId: string; - - @ApiProperty() - @Expose() - schoolId: string; - - @ApiProperty() - @Expose() - userId: string; - - @ApiProperty() - @Expose() - role: string; - - @Expose() - created_at: string; - - @Expose() - updated_at: string; - - constructor(obj: any) { - Object.assign(this, obj); - } -} diff --git a/src/groupMembership/groupMembership.controller.spec.ts b/src/groupMembership/groupMembership.controller.spec.ts deleted file mode 100644 index 6690fc44..00000000 --- a/src/groupMembership/groupMembership.controller.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Test, TestingModule } from "@nestjs/testing"; -import { GroupMembershipService } from "src/adapters/hasura/groupMembership.adapter"; -import { GroupMembershipController } from "./groupMembership.controller"; - -describe("GroupMembershipController", () => { - let controller: GroupMembershipController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [GroupMembershipController], - providers: [GroupMembershipService], - }).compile(); - - controller = module.get( - GroupMembershipController - ); - }); - - it("should be defined", () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/src/groupMembership/groupMembership.controller.ts b/src/groupMembership/groupMembership.controller.ts deleted file mode 100644 index e0fe4ff2..00000000 --- a/src/groupMembership/groupMembership.controller.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { - ApiTags, - ApiBody, - ApiForbiddenResponse, - ApiCreatedResponse, - ApiBasicAuth, -} from "@nestjs/swagger"; -import { - Controller, - Get, - Post, - Body, - Put, - Param, - UseInterceptors, - ClassSerializerInterceptor, - SerializeOptions, - Req, - CacheInterceptor, - Request, -} from "@nestjs/common"; - -import { GroupMembershipDto } from "./dto/groupMembership.dto"; -import { GroupMembershipSearchDto } from "./dto/groupMembership-search.dto"; -import { GroupMembershipService } from "src/adapters/hasura/groupMembership.adapter"; - -@ApiTags("Group Membership") -@Controller("groupmembership") -export class GroupMembershipController { - constructor(private service: GroupMembershipService) {} - - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Group Membership detail" }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async getGroupMembership( - @Param("id") groupMembershipId: string, - @Req() request: Request - ) { - return this.service.getGroupMembership(groupMembershipId, request); - } - - @Post() - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Group Membership has been created successfully.", - }) - @ApiBody({ type: GroupMembershipDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async createGroupMembership( - @Req() request: Request, - @Body() groupMembershipDto: GroupMembershipDto - ) { - return this.service.createGroupMembership(request, groupMembershipDto); - } - - @Put("/:id") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Group Membership has been updated successfully.", - }) - @ApiBody({ type: GroupMembershipDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async updateGroupMembership( - @Param("id") groupMembershipId: string, - @Req() request: Request, - @Body() groupMembersipDto: GroupMembershipDto - ) { - return this.service.updateGroupMembership( - groupMembershipId, - request, - groupMembersipDto - ); - } - - @Post("/search") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Group Membership list." }) - @ApiBody({ type: GroupMembershipSearchDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async searchGroupMembership( - @Req() request: Request, - @Body() groupMembershipSearchDto: GroupMembershipSearchDto - ) { - return this.service.searchGroupMembership( - request, - groupMembershipSearchDto - ); - } -} diff --git a/src/groupMembership/groupMembership.module.ts b/src/groupMembership/groupMembership.module.ts deleted file mode 100644 index 2782aeb0..00000000 --- a/src/groupMembership/groupMembership.module.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { CacheModule, Module } from "@nestjs/common"; -import { HttpModule } from "@nestjs/axios"; - -import { GroupMembershipController } from "./groupMembership.controller"; -import { GroupMembershipService } from "src/adapters/hasura/groupMembership.adapter"; -const ttl = process.env.TTL as never; -@Module({ - imports: [ - HttpModule, - CacheModule.register({ - ttl: ttl, - }), - ], - controllers: [GroupMembershipController], - providers: [GroupMembershipService], -}) -export class GroupMembershipModule {} diff --git a/src/holiday/dto/holiday-search.dto.ts b/src/holiday/dto/holiday-search.dto.ts deleted file mode 100644 index c8ffbe39..00000000 --- a/src/holiday/dto/holiday-search.dto.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; - -export class HolidaySearchDto { - @ApiProperty({ - type: String, - description: "Limit", - }) - limit: string; - - @ApiProperty({ - type: Number, - description: "Page", - }) - page: number; - - @ApiProperty({ - type: Object, - description: "Filters", - }) - @ApiPropertyOptional() - filters: object; - - constructor(partial: Partial) { - Object.assign(this, partial); - } -} diff --git a/src/holiday/dto/holiday.dto.ts b/src/holiday/dto/holiday.dto.ts deleted file mode 100644 index 344f42a7..00000000 --- a/src/holiday/dto/holiday.dto.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Expose } from "class-transformer"; -import { ApiProperty } from "@nestjs/swagger"; - -export class HolidayDto { - @Expose() - id: string; - - @Expose() - holidayId: string; - - @ApiProperty({ - type: String, - description: "The date of the holiday", - default: `eg. ${new Date().toISOString().split("T")[0]}`, - }) - @Expose() - date: Date; - - @ApiProperty({ - type: String, - description: "The remark of the holiday", - }) - @Expose() - remark: string; - - @ApiProperty({ - type: String, - description: "The year of the holiday", - default: `eg. ${new Date().toISOString().split("T")[0]}`, - }) - @Expose() - year: Date; - - @ApiProperty({ - type: String, - description: "The context of the holiday", - }) - @Expose() - context: string; - - @ApiProperty({ - type: String, - description: "The context id of the holiday", - }) - @Expose() - contextId: string; - - @Expose() - createdAt: string; - - @Expose() - updatedAt: string; - - constructor(obj: any) { - Object.assign(this, obj); - } -} diff --git a/src/holiday/holiday.controller.spec.ts b/src/holiday/holiday.controller.spec.ts deleted file mode 100644 index d7734f6d..00000000 --- a/src/holiday/holiday.controller.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from "@nestjs/testing"; -import { HolidayController } from "./holiday.controller"; - -describe("HolidayController", () => { - let controller: HolidayController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [HolidayController], - }).compile(); - - controller = module.get(HolidayController); - }); - - it("should be defined", () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/src/holiday/holiday.controller.ts b/src/holiday/holiday.controller.ts deleted file mode 100644 index 52fda8b4..00000000 --- a/src/holiday/holiday.controller.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { - Controller, - Get, - Post, - Body, - Put, - Param, - UseInterceptors, - ClassSerializerInterceptor, - SerializeOptions, - Req, - CacheInterceptor, - Query, -} from "@nestjs/common"; -import { - ApiTags, - ApiBody, - ApiOkResponse, - ApiForbiddenResponse, - ApiCreatedResponse, - ApiBasicAuth, - ApiQuery, -} from "@nestjs/swagger"; -import { HolidayDto } from "./dto/holiday.dto"; -import { HolidaySearchDto } from "./dto/holiday-search.dto"; -import { Request } from "@nestjs/common"; -import { HolidayAdapter } from "./holidayadapter"; -@ApiTags("Holiday") -@Controller("holiday") -export class HolidayController { - constructor(private holidayProvider: HolidayAdapter) {} - - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "Holiday detail." }) - @SerializeOptions({ - strategy: "excludeAll", - }) - getHolidays(@Param("id") holidayId: string, @Req() request: Request) { - return this.holidayProvider - .buildHolidayAdapter() - .getHoliday(holidayId, request); - } - - @Post() - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Holiday has been created successfully." }) - @ApiBody({ type: HolidayDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async createHoliday( - @Req() request: Request, - @Body() holidayDto: HolidayDto - ) { - return await this.holidayProvider - .buildHolidayAdapter() - .createHoliday(request, holidayDto); - } - - @Put("/:id") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Holiday has been updated successfully." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async updateHoliday( - @Param("id") holidayId: string, - @Req() request: Request, - @Body() holidayDto: HolidayDto - ) { - return await this.holidayProvider - .buildHolidayAdapter() - .updateHoliday(holidayId, request, holidayDto); - } - - @Post("/search") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Holiday list." }) - @ApiBody({ type: HolidaySearchDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async searchHoliday( - @Req() request: Request, - @Body() holidaySearchDto: HolidaySearchDto - ) { - return await this.holidayProvider - .buildHolidayAdapter() - .searchHoliday(request, holidaySearchDto); - } - - @Get("") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: " Ok." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @ApiQuery({ name: "fromDate" }) - @ApiQuery({ name: "toDate" }) - public async holidayFilter( - @Query("fromDate") date: string, - @Query("toDate") toDate: string, - @Req() request: Request - ) { - return await this.holidayProvider - .buildHolidayAdapter() - .holidayFilter(date, toDate, request); - } -} diff --git a/src/holiday/holiday.module.ts b/src/holiday/holiday.module.ts deleted file mode 100644 index dd601931..00000000 --- a/src/holiday/holiday.module.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { HttpModule } from "@nestjs/axios"; -import { CacheModule, Module } from "@nestjs/common"; -import { HasuraModule } from "src/adapters/hasura/hasura.module"; -import { SunbirdModule } from "src/adapters/sunbirdrc/subnbird.module"; -import { HolidayController } from "./holiday.controller"; -import { HolidayAdapter } from "./holidayadapter"; -const ttl = process.env.TTL as never; -@Module({ - imports: [ - SunbirdModule, - HasuraModule, - HttpModule, - CacheModule.register({ - ttl: ttl, - }), - ], - controllers: [HolidayController], - providers: [HolidayAdapter], -}) -export class HolidayModule {} diff --git a/src/holiday/holidayadapter.ts b/src/holiday/holidayadapter.ts deleted file mode 100644 index 290603d4..00000000 --- a/src/holiday/holidayadapter.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { HasuraHolidayService } from "src/adapters/hasura/holiday.adapter"; -import { IServicelocator } from "src/adapters/holidayservicelocator"; -import { SunbirdHolidayService } from "src/adapters/sunbirdrc/holiday.adapter"; - -@Injectable() -export class HolidayAdapter { - constructor( - private sunbirdProvider: SunbirdHolidayService, - private hasuraProvider: HasuraHolidayService - ) {} - buildHolidayAdapter(): IServicelocator { - let adapter: IServicelocator; - - switch (process.env.ADAPTERSOURCE) { - case "sunbird": - adapter = this.sunbirdProvider; - break; - case "hasura": - adapter = this.hasuraProvider; - break; - } - return adapter; - } -} diff --git a/src/inAppNotification/inAppNotification.controller.ts b/src/inAppNotification/inAppNotification.controller.ts deleted file mode 100644 index b0c77f32..00000000 --- a/src/inAppNotification/inAppNotification.controller.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { - ApiTags, - ApiForbiddenResponse, - ApiCreatedResponse, - ApiBasicAuth, - ApiQuery, -} from "@nestjs/swagger"; -import { - Controller, - Post, - UseInterceptors, - ClassSerializerInterceptor, - Req, - Request, - Query, - Get, -} from "@nestjs/common"; -import { InAppNotificationService } from "src/adapters/sunbirdrc/inAppNotification.adapter"; - -@ApiTags("In App Notification") -@Controller("inappnotification") -export class InAppNotificationController { - constructor(private service: InAppNotificationService) {} - - @Post("inappnotification") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Notification has been sent successfully.", - }) - @UseInterceptors(ClassSerializerInterceptor) - @ApiForbiddenResponse({ description: "Forbidden" }) - @ApiQuery({ name: "module" }) - @ApiQuery({ name: "groupId" }) - @ApiQuery({ name: "templateId" }) - public async inAppNotification( - @Query("module") module: string, - @Query("groupId") groupId: string, - @Query("templateId") templateId: string, - @Req() request: Request - ) { - return this.service.inAppNotification(module, groupId, request, templateId); - } - - @Get("/userhistory") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "User Notification History.", - }) - @UseInterceptors(ClassSerializerInterceptor) - @ApiForbiddenResponse({ description: "Forbidden" }) - @ApiQuery({ name: "userId" }) - @ApiQuery({ name: "provider" }) - @ApiQuery({ name: "startDate" }) - @ApiQuery({ name: "endDate" }) - public async userHistoryNotification( - @Query("userId") userId: string, - @Query("provider") provider: string, - @Query("startDate") startDate: string, - @Query("endDate") endDate: string, - @Req() request: Request - ) { - return this.service.userHistoryNotification( - userId, - provider, - startDate, - endDate, - request - ); - } - - @Get("/bothistory") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Bot Notification History.", - }) - @UseInterceptors(ClassSerializerInterceptor) - @ApiForbiddenResponse({ description: "Forbidden" }) - @ApiQuery({ name: "botId" }) - @ApiQuery({ name: "provider" }) - @ApiQuery({ name: "startDate" }) - @ApiQuery({ name: "endDate" }) - public async botHistoryNotification( - @Query("botId") botId: string, - @Query("provider") provider: string, - @Query("startDate") startDate: string, - @Query("endDate") endDate: string, - @Req() request: Request - ) { - return this.service.botHistoryNotification( - botId, - provider, - startDate, - endDate, - request - ); - } - - @Post("readreceipt") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Read Receipt.", - }) - @UseInterceptors(ClassSerializerInterceptor) - @ApiForbiddenResponse({ description: "Forbidden" }) - @ApiQuery({ name: "eventType", required: false }) - @ApiQuery({ name: "externalId", required: false }) - @ApiQuery({ name: "destAdd", required: false }) - @ApiQuery({ name: "fcmDestAdd", required: false }) - @ApiQuery({ name: "messageId", required: false }) - @ApiQuery({ name: "text", required: false }) - @ApiQuery({ name: "from", required: false }) - public async readReceipt( - @Query("eventType") eventType: string, - @Query("externalId") externalId: string, - @Query("destAdd") destAdd: string, - @Query("fcmDestAdd") fcmDestAdd: string, - @Query("messageId") messageId: string, - @Query("text") text: string, - @Query("from") from: string, - @Req() request: Request - ) { - return this.service.readReceipt( - eventType, - externalId, - destAdd, - fcmDestAdd, - messageId, - text, - from, - request - ); - } -} diff --git a/src/inAppNotification/inAppNotification.module.ts b/src/inAppNotification/inAppNotification.module.ts deleted file mode 100644 index b63978c7..00000000 --- a/src/inAppNotification/inAppNotification.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { CacheModule, Module } from "@nestjs/common"; -import { HttpModule } from "@nestjs/axios"; -import { ScheduleModule } from "@nestjs/schedule"; -import { InAppNotificationController } from "./inAppNotification.controller"; -import { InAppNotificationService } from "src/adapters/sunbirdrc/inAppNotification.adapter"; -const ttl = process.env.TTL as never; -@Module({ - imports: [ - HttpModule, - CacheModule.register({ - ttl: ttl, - }), - ScheduleModule.forRoot(), - ], - controllers: [InAppNotificationController], - providers: [InAppNotificationService], -}) -export class InAppNotificationModule {} diff --git a/src/interfaces/entities/IStudent.ts b/src/interfaces/entities/IStudent.ts deleted file mode 100644 index 8cd815e1..00000000 --- a/src/interfaces/entities/IStudent.ts +++ /dev/null @@ -1,22 +0,0 @@ -interface IStudent { - studentId: string; - refId: string; - aadhaar: string; - firstName: string; - lastName: string; - schoolId: string; - currentClassId: string; - gender: string; - socialCategory: string; - iscwsn: string; - religion: string; - singleGirl: string; - weight: string; - height: string; - bloodGroup: string; - birthDate: string; - homeless: string; - bpl: string; - migrant: string; - status: string; -} diff --git a/src/lessonPlan/dto/lessonPlan.dto.ts b/src/lessonPlan/dto/lessonPlan.dto.ts deleted file mode 100644 index 34eb5ff1..00000000 --- a/src/lessonPlan/dto/lessonPlan.dto.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { Expose } from "class-transformer"; -import { ApiProperty } from "@nestjs/swagger"; - -export class LessonPlanDto { - @Expose() - contentId: string; - - @ApiProperty({}) - @Expose() - name: string; - - @ApiProperty({}) - @Expose() - code: string; - - @ApiProperty({}) - @Expose() - status: string; - - @ApiProperty({}) - @Expose() - channel: string; - - @ApiProperty({}) - @Expose() - mediaType: string; - - @ApiProperty({}) - @Expose() - compatibilityLevel: string; - - @ApiProperty({}) - @Expose() - audience: [string]; - - @ApiProperty({}) - @Expose() - posterImage: string; - - @ApiProperty({}) - @Expose() - duration: string; - - @ApiProperty({}) - @Expose() - downloadUrl: string; - - @ApiProperty({}) - @Expose() - previewUrl: string; - - @ApiProperty({}) - @Expose() - author: string; - - @ApiProperty({}) - @Expose() - languageCode: [string]; - - @ApiProperty({}) - @Expose() - language: [string]; - - @ApiProperty({}) - @Expose() - ageGroup: [string]; - - @ApiProperty({}) - @Expose() - contentType: string; - - @ApiProperty({}) - @Expose() - category: [string]; - - @ApiProperty({}) - @Expose() - teachingMode: string; - - @ApiProperty({}) - @Expose() - skills: [string]; - - @ApiProperty({}) - @Expose() - keywords: [string]; - - @ApiProperty({}) - @Expose() - description: string; - - @ApiProperty({}) - @Expose() - instructions: string; - - @ApiProperty({}) - @Expose() - body: string; - - @ApiProperty({}) - @Expose() - learningObjective: [string]; - - @ApiProperty({}) - @Expose() - creator: string; - - @ApiProperty({}) - @Expose() - reviewer: string; - - @ApiProperty({}) - @Expose() - lastSubmittedBy: string; - - @ApiProperty({}) - @Expose() - lastSubmittedOn: string; - - @ApiProperty({}) - @Expose() - lastPublishedBy: string; - - @ApiProperty({}) - @Expose() - lastPublishedOn: string; - - @ApiProperty({}) - @Expose() - subject: [string]; - - @ApiProperty({}) - @Expose() - questionCategories: [string]; - - @ApiProperty({}) - @Expose() - medium: [string]; - - @ApiProperty({}) - @Expose() - gradeLevel: [string]; - - @ApiProperty({}) - @Expose() - topic: [string]; - - @ApiProperty({}) - @Expose() - subjectCodes: [string]; - - @ApiProperty({}) - @Expose() - difficultyLevel: string; - - @ApiProperty({}) - @Expose() - board: string; - - @ApiProperty({}) - @Expose() - primaryCategory: string; - - @ApiProperty({}) - @Expose() - accessibility: [string]; - - @Expose() - createdAt: string; - - @Expose() - updatedAt: string; - - @Expose() - createdBy: string; - - @Expose() - updatedBy: string; - constructor(obj: any) { - Object.assign(this, obj); - } -} diff --git a/src/lessonPlan/dto/lessonPlan.search.dto.ts b/src/lessonPlan/dto/lessonPlan.search.dto.ts deleted file mode 100644 index 58ae35b2..00000000 --- a/src/lessonPlan/dto/lessonPlan.search.dto.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; - -export class LessonPlanSearchDto { - @ApiProperty({ - type: String, - description: "Limit", - }) - limit: string; - - @ApiProperty({ - type: Object, - description: "Filters", - }) - @ApiPropertyOptional() - filters: object; - - constructor(partial: Partial) { - Object.assign(this, partial); - } -} diff --git a/src/lessonPlan/lessonPlan.controller.ts b/src/lessonPlan/lessonPlan.controller.ts deleted file mode 100644 index 792c9861..00000000 --- a/src/lessonPlan/lessonPlan.controller.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { - Controller, - Get, - Post, - Body, - Put, - Param, - UseInterceptors, - ClassSerializerInterceptor, - SerializeOptions, - Req, - CacheInterceptor, -} from "@nestjs/common"; -import { - ApiTags, - ApiBody, - ApiOkResponse, - ApiForbiddenResponse, - ApiCreatedResponse, - ApiBasicAuth, -} from "@nestjs/swagger"; -import { Request } from "@nestjs/common"; -import { LessonPlanDto } from "./dto/lessonPlan.dto"; -import { LessonPlanSearchDto } from "./dto/lessonPlan.search.dto"; -import { LessonPlanService } from "src/adapters/sunbirdrc/lessonPlan.adapter"; -@ApiTags("Lesson Plan") -@Controller("lessonPlan") -export class LessonPlanController { - constructor(private readonly service: LessonPlanService) {} - - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "LessonPlan detail." }) - @SerializeOptions({ - strategy: "excludeAll", - }) - getLessonPlans(@Param("id") lessonPlanId: string, @Req() request: Request) { - return this.service.getLessonPlan(lessonPlanId, request); - } - - @Post() - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "LessonPlan has been created successfully.", - }) - @ApiBody({ type: LessonPlanDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async createLessonPlan( - @Req() request: Request, - @Body() lessonPlanDto: LessonPlanDto - ) { - return this.service.createLessonPlan(request, lessonPlanDto); - } - - @Put("/:id") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "LessonPlan has been updated successfully.", - }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async updateLessonPlan( - @Param("id") lessonPlanId: string, - @Req() request: Request, - @Body() lessonPlanDto: LessonPlanDto - ) { - return await this.service.updateLessonPlan( - lessonPlanId, - request, - lessonPlanDto - ); - } - - @Post("/search") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "LessonPlan list." }) - @ApiBody({ type: LessonPlanSearchDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async searchLessonPlan( - @Req() request: Request, - @Body() lessonPlanSearchDto: LessonPlanSearchDto - ) { - return await this.service.searchLessonPlan(request, lessonPlanSearchDto); - } -} diff --git a/src/lessonPlan/lessonPlan.module.ts b/src/lessonPlan/lessonPlan.module.ts deleted file mode 100644 index 3b764661..00000000 --- a/src/lessonPlan/lessonPlan.module.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { HttpModule } from "@nestjs/axios"; -import { CacheModule, Module } from "@nestjs/common"; -import { LessonPlanController } from "./lessonPlan.controller"; -import { LessonPlanService } from "src/adapters/sunbirdrc/lessonPlan.adapter"; -const ttl = process.env.TTL as never; -@Module({ - imports: [ - HttpModule, - CacheModule.register({ - ttl: ttl, - }), - ], - controllers: [LessonPlanController], - providers: [LessonPlanService], -}) -export class LessonPlanModule {} diff --git a/src/like/dto/like-search.dto.ts b/src/like/dto/like-search.dto.ts deleted file mode 100644 index 8991eb27..00000000 --- a/src/like/dto/like-search.dto.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; - -export class LikeSearchDto { - @ApiProperty({ - type: String, - description: "Limit", - }) - limit: string; - - @ApiProperty({ - type: Number, - description: "Page", - }) - page: number; - - @ApiProperty({ - type: Object, - description: "Filters", - }) - @ApiPropertyOptional() - filters: object; - - constructor(partial: Partial) { - Object.assign(this, partial); - } -} diff --git a/src/like/dto/like.dto.ts b/src/like/dto/like.dto.ts deleted file mode 100644 index 5b68ae13..00000000 --- a/src/like/dto/like.dto.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Expose } from "class-transformer"; -import { ApiProperty } from "@nestjs/swagger"; - -export class LikeDto { - @Expose() - id: string; - - @Expose() - likeId: string; - - @ApiProperty({}) - @Expose() - contextId: string; - - @ApiProperty({}) - @Expose() - context: string; - - @Expose() - userId: string; - - @ApiProperty({}) - @Expose() - type: string; - - @Expose() - createdAt: string; - - @Expose() - updatedAt: string; - - constructor(obj: any) { - Object.assign(this, obj); - } -} diff --git a/src/like/like.controller.ts b/src/like/like.controller.ts deleted file mode 100644 index 67f4a14a..00000000 --- a/src/like/like.controller.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { - Controller, - Get, - Post, - Body, - Put, - Param, - UseInterceptors, - ClassSerializerInterceptor, - SerializeOptions, - Req, - CacheInterceptor, - Query, - Delete, -} from "@nestjs/common"; -import { - ApiTags, - ApiBody, - ApiOkResponse, - ApiForbiddenResponse, - ApiCreatedResponse, - ApiBasicAuth, - ApiQuery, -} from "@nestjs/swagger"; -import { Request } from "@nestjs/common"; -import { LikeDto } from "./dto/like.dto"; -import { LikeSearchDto } from "./dto/like-search.dto"; -import { LikeAdapter } from "./likeadapter"; -@ApiTags("Like") -@Controller("like") -export class LikeController { - constructor(private likeAdapter: LikeAdapter) {} - - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "Like detail." }) - @SerializeOptions({ - strategy: "excludeAll", - }) - getLike(@Param("id") likeId: string, @Req() request: Request) { - return this.likeAdapter.buildLikeAdapter().getLike(likeId, request); - } - - @Post() - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Like has been created successfully.", - }) - @ApiBody({ type: LikeDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async createLike(@Req() request: Request, @Body() likeDto: LikeDto) { - return this.likeAdapter.buildLikeAdapter().createLike(request, likeDto); - } - - @Put("/:id") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Like has been updated successfully.", - }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async updateLike( - @Param("id") likeId: string, - @Req() request: Request, - @Body() likeDto: LikeDto - ) { - return await this.likeAdapter - .buildLikeAdapter() - .updateLike(likeId, request, likeDto); - } - - @Post("/search") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Like list." }) - @ApiBody({ type: LikeSearchDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async searchLike( - @Req() request: Request, - @Body() likeSearchDto: LikeSearchDto - ) { - return await this.likeAdapter - .buildLikeAdapter() - .searchLike(request, likeSearchDto); - } - - @Post("/getAllLikes") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "All Like." }) - @ApiQuery({ name: "contextId" }) - @ApiQuery({ name: "context" }) - public async getCountLike( - @Query("contextId") contextId: string, - @Query("context") context: string, - @Req() request: Request - ) { - return await this.likeAdapter - .buildLikeAdapter() - .getCountLike(contextId, context, request); - } - - @Delete("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "Delete like. " }) - public async deleteLike( - @Param("id") likeId: string, - @Req() request: Request - ) { - return await this.likeAdapter - .buildLikeAdapter() - .deleteLike(likeId, request); - } -} diff --git a/src/like/like.module.ts b/src/like/like.module.ts deleted file mode 100644 index 40b40103..00000000 --- a/src/like/like.module.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { HttpModule } from "@nestjs/axios"; -import { CacheModule, Module } from "@nestjs/common"; -import { HasuraModule } from "src/adapters/hasura/hasura.module"; -import { SunbirdModule } from "src/adapters/sunbirdrc/subnbird.module"; -import { LikeController } from "./like.controller"; -import { LikeAdapter } from "./likeadapter"; -const ttl = process.env.TTL as never; -@Module({ - imports: [ - SunbirdModule, - HasuraModule, - HttpModule, - CacheModule.register({ - ttl: ttl, - }), - ], - controllers: [LikeController], - providers: [LikeAdapter], -}) -export class LikeModule {} diff --git a/src/like/likeadapter.ts b/src/like/likeadapter.ts deleted file mode 100644 index ace6540f..00000000 --- a/src/like/likeadapter.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { HasuraLikeService } from "src/adapters/hasura/like.adapter"; -import { IServicelocator } from "src/adapters/likeservicelocator"; -import { SunbirdLikeService } from "src/adapters/sunbirdrc/like.adapter"; - -@Injectable() -export class LikeAdapter { - constructor( - private sunbirdProvider: SunbirdLikeService, - private hasuraProvider: HasuraLikeService - ) {} - buildLikeAdapter(): IServicelocator { - let adapter: IServicelocator; - - switch (process.env.ADAPTERSOURCE) { - case "sunbird": - adapter = this.sunbirdProvider; - break; - case "hasura": - adapter = this.hasuraProvider; - break; - } - return adapter; - } -} diff --git a/src/main.ts b/src/main.ts index 8cf49963..c102f9c3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,16 +1,49 @@ +// import { NestFactory } from "@nestjs/core"; +// import { AppModule } from "./app.module"; +// import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger"; +// import { RequestMethod } from "@nestjs/common"; +// import { join } from "path"; +// import express = require("express"); +// async function bootstrap() { +// const app = await NestFactory.create(AppModule); +// app.use( +// process.env.IMAGEPATH, +// express.static(join(__dirname, "..", "uploads")) +// ); +// app.setGlobalPrefix("api/v1", { +// exclude: [{ path: "health", method: RequestMethod.GET }], +// }); + +// // const config = new DocumentBuilder() +// // .setTitle("Shiksha Platform") +// // .setDescription("CRUD API") +// // .setVersion("1.0") +// // .addTag("V1") +// // .addApiKey( +// // { type: "apiKey", scheme: "bearer", bearerFormat: "JWT", name: "Authorization", in: "header" }, +// // "access-token" +// // ).build(); +// // const document = SwaggerModule.createDocument(app, config); +// // SwaggerModule.setup("api/swagger-docs", app, document); +// app.enableCors(); +// await app.listen(3000); +// } +// bootstrap(); import { NestFactory } from "@nestjs/core"; import { AppModule } from "./app.module"; import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger"; import { RequestMethod } from "@nestjs/common"; import { join } from "path"; import express = require("express"); +import { AllExceptionsFilter } from "./common/filters/exception.filter"; + async function bootstrap() { const app = await NestFactory.create(AppModule); app.use( process.env.IMAGEPATH, express.static(join(__dirname, "..", "uploads")) ); - app.setGlobalPrefix("api/v1", { + app.setGlobalPrefix("user/v1", { exclude: [{ path: "health", method: RequestMethod.GET }], }); @@ -20,13 +53,13 @@ async function bootstrap() { .setVersion("1.0") .addTag("V1") .addApiKey( - { type: "apiKey", scheme: "bearer", bearerFormat: "JWT", name: "Authorization", in: "header" }, + { type: "apiKey", name: "Authorization", in: "header" }, "access-token" ) - .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup("api/swagger-docs", app, document); + app.useGlobalFilters(new AllExceptionsFilter()) app.enableCors(); await app.listen(3000); } diff --git a/src/mentorTracking/dto/feedback-create.dto.ts b/src/mentorTracking/dto/feedback-create.dto.ts deleted file mode 100644 index 2707c62a..00000000 --- a/src/mentorTracking/dto/feedback-create.dto.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; -import * as FormData from "form-data"; -export class FeedbackCreateDto { - @ApiProperty({}) - feedback: FormData; - - @ApiProperty({ type: "string", format: "binary" }) - image: string; - - constructor(obj: any) { - Object.assign(this, obj); - } -} diff --git a/src/mentorTracking/dto/mentorTracking.dto.ts b/src/mentorTracking/dto/mentorTracking.dto.ts deleted file mode 100644 index 49461ad5..00000000 --- a/src/mentorTracking/dto/mentorTracking.dto.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { Expose } from "class-transformer"; -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; -import { IsEnum, IsIn, IsNotEmpty, IsString } from "class-validator"; -import { VisitStatus } from "./visitStatus.enum"; - -export class MentorTrackingDto { - @Expose() - id: string; - - @Expose() - mentorTrackingId: string; - - @ApiProperty({}) - @Expose() - mentorId: string; - - @ApiProperty({}) - @Expose() - teacherId: string; - - @ApiProperty({}) - @Expose() - schoolId: string; - - @ApiProperty({ - default: new Date().toISOString().split("T")[0], - }) - @Expose() - scheduleVisitDate: string; - - @ApiProperty({ - default: new Date().toISOString().split("T")[0], - }) - @Expose() - visitDate: string; - - @ApiProperty({}) - @Expose() - feedback: string; - - @IsString() - @IsNotEmpty() - @IsIn([VisitStatus.pending, VisitStatus.visited]) - @IsEnum(VisitStatus) - @ApiPropertyOptional({ - enum: [VisitStatus.pending, VisitStatus.visited], - }) - @Expose() - status: string; - - @ApiProperty({ default: new Date().toISOString().split("T")[0] }) - @Expose() - lastVisited: string; - - @Expose() - createdAt: string; - - @Expose() - updatedAt: string; - - @Expose() - createdBy: string; - - @Expose() - updatedBy: string; - - constructor(obj: any) { - Object.assign(this, obj); - } -} diff --git a/src/mentorTracking/dto/visitStatus.enum.ts b/src/mentorTracking/dto/visitStatus.enum.ts deleted file mode 100644 index 6a8dc426..00000000 --- a/src/mentorTracking/dto/visitStatus.enum.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum VisitStatus { - pending = "pending", - visited = "visited", -} diff --git a/src/mentorTracking/mentorTracking.controller.ts b/src/mentorTracking/mentorTracking.controller.ts deleted file mode 100644 index 7ffa394e..00000000 --- a/src/mentorTracking/mentorTracking.controller.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { - Controller, - Get, - Post, - Body, - Put, - Param, - UseInterceptors, - ClassSerializerInterceptor, - SerializeOptions, - Req, - CacheInterceptor, - Query, - ValidationPipe, - UsePipes, - Header, - UploadedFile, -} from "@nestjs/common"; -import { - ApiTags, - ApiBody, - ApiOkResponse, - ApiForbiddenResponse, - ApiCreatedResponse, - ApiBasicAuth, - ApiQuery, - ApiConsumes, -} from "@nestjs/swagger"; -import { Request } from "@nestjs/common"; -import { MentorTrackingDto } from "./dto/mentorTracking.dto"; -import { MentorTrackingService } from "src/adapters/hasura/mentorTracking.adapter"; -import { FileInterceptor } from "@nestjs/platform-express"; -import { editFileName, imageFileFilter } from "./utils/file-upload.utils"; -import { diskStorage } from "multer"; -import { FeedbackCreateDto } from "./dto/feedback-create.dto"; -@ApiTags("Mentor Tracking") -@Controller("mentortracking") -export class MentorTrackingController { - constructor(private readonly service: MentorTrackingService) {} - - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "Mentor Tracking detail." }) - @SerializeOptions({ - strategy: "excludeAll", - }) - getMentor(@Param("id") mentorId: string, @Req() request: Request) { - return this.service.getMentorTracking(mentorId, request); - } - - @Post() - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Mentor Tracking has been created successfully.", - }) - @ApiBody({ type: MentorTrackingDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @UsePipes(new ValidationPipe({})) - public async createMentor( - @Req() request: Request, - @Body() mentorDto: MentorTrackingDto - ) { - return this.service.createMentorTracking(request, mentorDto); - } - - @Put("/:id") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Mentor Tracking has been updated successfully.", - }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @UsePipes(new ValidationPipe({})) - public async updateMentor( - @Param("id") mentorTrackingId: string, - @Req() request: Request, - @Body() mentorTrackingDto: MentorTrackingDto - ) { - return await this.service.updateMentorTracking( - mentorTrackingId, - request, - mentorTrackingDto - ); - } - - @Put("feedback/:id") - @ApiConsumes("multipart/form-data") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Group has been updated successfully." }) - @UseInterceptors( - FileInterceptor("image", { - storage: diskStorage({ - destination: process.env.IMAGEPATH, - filename: editFileName, - }), - fileFilter: imageFileFilter, - }) - ) - @ApiBody({ type: FeedbackCreateDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async feedback( - @Param("id") mentorTrackingId: string, - @Req() request: Request, - @Body() feedbackCreateDto: FeedbackCreateDto, - @UploadedFile() image - ) { - const response = { - image: image?.filename, - }; - Object.assign(feedbackCreateDto, response); - - return await this.service.feedback( - mentorTrackingId, - feedbackCreateDto, - request - ); - } - - @Post("/search") - @UseInterceptors(ClassSerializerInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: " Ok." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @ApiQuery({ name: "limit", required: false }) - @ApiQuery({ name: "mentorTrackingId", required: false }) - @ApiQuery({ name: "mentorId", required: false }) - @ApiQuery({ name: "teacherId", required: false }) - @ApiQuery({ name: "schoolId", required: false }) - @ApiQuery({ name: "scheduleVisitDate", required: false }) - @ApiQuery({ name: "visitDate", required: false }) - @ApiQuery({ name: "page", required: false }) - @ApiQuery({ name: "status", required: false }) - public async searchMentorTracking( - @Query("limit") limit: string, - @Query("mentorTrackingId") mentorTrackingId: string, - @Query("mentorId") mentorId: string, - @Query("teacherId") teacherId: string, - @Query("schoolId") schoolId: string, - @Query("scheduleVisitDate") scheduleVisitDate: Date, - @Query("visitDate") visitDate: Date, - @Query("page") page: number, - @Query("status") status: string, - @Req() request: Request - ) { - return this.service.searchMentorTracking( - limit, - mentorTrackingId, - mentorId, - teacherId, - schoolId, - scheduleVisitDate, - visitDate, - page, - status, - request - ); - } -} diff --git a/src/mentorTracking/mentorTracking.module.ts b/src/mentorTracking/mentorTracking.module.ts deleted file mode 100644 index f6363b93..00000000 --- a/src/mentorTracking/mentorTracking.module.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { HttpModule } from "@nestjs/axios"; -import { CacheModule, Module } from "@nestjs/common"; -import { MentorTrackingService } from "src/adapters/hasura/mentorTracking.adapter"; -import { MentorTrackingController } from "./mentorTracking.controller"; -const ttl = process.env.TTL as never; -@Module({ - imports: [ - HttpModule, - CacheModule.register({ - ttl: ttl, - }), - ], - controllers: [MentorTrackingController], - providers: [MentorTrackingService], -}) -export class MentorTrackingModule {} diff --git a/src/mentorTracking/utils/file-upload.utils.ts b/src/mentorTracking/utils/file-upload.utils.ts deleted file mode 100644 index 6bd794ca..00000000 --- a/src/mentorTracking/utils/file-upload.utils.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { extname } from "path"; - -export const imageFileFilter = (req, file, callback) => { - if (!file.originalname.match(/.(jpg|jpeg|png|gif|pdf)$/)) { - return callback(new Error("!"), false); - } - callback(null, true); -}; - -export const editFileName = (req, file, callback) => { - const name = file.originalname.split(".")[0]; - const fileExtName = extname(file.originalname); - const randomName = Array(4) - .fill(null) - .map(() => Math.round(Math.random() * 16).toString(16)) - .join(""); - callback(null, `${name}-${randomName}${fileExtName}`); -}; diff --git a/src/monitorTracking/dto/monitorTracking.dto.ts b/src/monitorTracking/dto/monitorTracking.dto.ts deleted file mode 100644 index 79e03261..00000000 --- a/src/monitorTracking/dto/monitorTracking.dto.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { Expose } from "class-transformer"; -import { ApiProperty } from "@nestjs/swagger"; -import { VisitStatus } from "./visitStatus.enum"; -import { IsEnum, IsIn, IsNotEmpty, IsString } from "class-validator"; - -export class MonitorTrackingDto { - @Expose() - id: string; - - @Expose() - monitorTrackingId: string; - - @ApiProperty({}) - @Expose() - monitorId: string; - - @ApiProperty({}) - @Expose() - schoolId: string; - - @ApiProperty({ description: "Group ID (class ID)" }) - @Expose() - groupId: string; - - @ApiProperty({ - default: new Date().toISOString().split("T")[0], - }) - @Expose() - scheduleVisitDate: string; - - @ApiProperty({ - default: new Date().toISOString().split("T")[0], - }) - @Expose() - visitDate: string; - - @ApiProperty({}) - @Expose() - feedback: string; - - @IsString() - @IsNotEmpty() - @IsIn([VisitStatus.pending, VisitStatus.visited]) - @IsEnum(VisitStatus) - @ApiProperty({ - enum: [VisitStatus.pending, VisitStatus.visited], - }) - @Expose() - status: string; - - @ApiProperty({ default: new Date().toISOString().split("T")[0] }) - @Expose() - lastVisited: string; - - @Expose() - createdAt: string; - - @Expose() - updatedAt: string; - - @Expose() - createdBy: string; - - @Expose() - updatedBy: string; - - constructor(obj: any) { - Object.assign(this, obj); - } -} diff --git a/src/monitorTracking/dto/visitStatus.enum.ts b/src/monitorTracking/dto/visitStatus.enum.ts deleted file mode 100644 index 6a8dc426..00000000 --- a/src/monitorTracking/dto/visitStatus.enum.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum VisitStatus { - pending = "pending", - visited = "visited", -} diff --git a/src/monitorTracking/monitorTracking.controller.ts b/src/monitorTracking/monitorTracking.controller.ts deleted file mode 100644 index cbb4dc59..00000000 --- a/src/monitorTracking/monitorTracking.controller.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { - Controller, - Get, - Post, - Body, - Put, - Param, - UseInterceptors, - ClassSerializerInterceptor, - SerializeOptions, - Req, - CacheInterceptor, - Query, - ValidationPipe, - UsePipes, -} from "@nestjs/common"; -import { - ApiTags, - ApiBody, - ApiOkResponse, - ApiForbiddenResponse, - ApiCreatedResponse, - ApiBasicAuth, - ApiQuery, -} from "@nestjs/swagger"; -import { Request } from "@nestjs/common"; -import { MonitorTrackingDto } from "./dto/monitorTracking.dto"; -import { MonitorTrackingService } from "src/adapters/hasura/monitorTracking.adapter"; - -@ApiTags("Monitor Tracking") -@Controller("monitortracking") -export class MonitorTrackingController { - constructor(private readonly service: MonitorTrackingService) {} - - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "Monitor Tracking detail." }) - @SerializeOptions({ - strategy: "excludeAll", - }) - getMonitor(@Param("id") monitorTrackingId: string, @Req() request: Request) { - return this.service.getMonitorTracking(monitorTrackingId, request); - } - - @Post() - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Monitor Tracking has been created successfully.", - }) - @ApiBody({ type: MonitorTrackingDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @UsePipes(new ValidationPipe({})) - public async createMonitor( - @Req() request: Request, - @Body() monitorDto: MonitorTrackingDto - ) { - return this.service.createMonitorTracking(request, monitorDto); - } - - @Put("/:id") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Monitor Tracking has been updated successfully.", - }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @UsePipes(new ValidationPipe({})) - public async updateMonitor( - @Param("id") monitorTrackingId: string, - @Req() request: Request, - @Body() monitorDto: MonitorTrackingDto - ) { - return await this.service.updateMonitorTracking( - monitorTrackingId, - request, - monitorDto - ); - } - - @Post("/search") - @UseInterceptors(ClassSerializerInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: " Ok." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @ApiQuery({ name: "limit", required: false }) - @ApiQuery({ name: "monitorTrackingId", required: false }) - @ApiQuery({ name: "monitorId", required: false }) - @ApiQuery({ name: "schoolId", required: false }) - @ApiQuery({ name: "groupId", required: false }) - @ApiQuery({ name: "scheduleVisitDate", required: false }) - @ApiQuery({ name: "visitDate", required: false }) - @ApiQuery({ name: "page", required: false }) - public async searchMonitorTracking( - @Query("limit") limit: string, - @Query("monitorTrackingId") monitorTrackingId: string, - @Query("monitorId") monitorId: string, - @Query("schoolId") schoolId: string, - @Query("groupId") groupId: string, - @Query("scheduleVisitDate") scheduleVisitDate: Date, - @Query("visitDate") visitDate: Date, - @Query("page") page: number, - @Req() request: Request - ) { - return this.service.searchMonitorTracking( - limit, - monitorTrackingId, - monitorId, - schoolId, - groupId, - scheduleVisitDate, - visitDate, - page, - request - ); - } -} diff --git a/src/monitorTracking/monitorTracking.module.ts b/src/monitorTracking/monitorTracking.module.ts deleted file mode 100644 index 46378788..00000000 --- a/src/monitorTracking/monitorTracking.module.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { HttpModule } from "@nestjs/axios"; -import { CacheModule, Module } from "@nestjs/common"; -import { MonitorTrackingService } from "src/adapters/hasura/monitorTracking.adapter"; - -import { MonitorTrackingController } from "./monitorTracking.controller"; -const ttl = process.env.TTL as never; -@Module({ - imports: [ - HttpModule, - CacheModule.register({ - ttl: ttl, - }), - ], - controllers: [MonitorTrackingController], - providers: [MonitorTrackingService], -}) -export class MonitorTrackingModule {} diff --git a/src/notification/dto/notification-search.dto.ts b/src/notification/dto/notification-search.dto.ts deleted file mode 100644 index 5fc7d630..00000000 --- a/src/notification/dto/notification-search.dto.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; - -export class NotificationSearchDto { - @ApiProperty({ - type: String, - description: "Limit", - }) - limit: string; - - @ApiProperty({ - type: Object, - description: "Filters", - }) - @ApiPropertyOptional() - filters: object; - - constructor(partial: Partial) { - Object.assign(this, partial); - } -} diff --git a/src/notification/dto/notification.dto.ts b/src/notification/dto/notification.dto.ts deleted file mode 100644 index 094d9319..00000000 --- a/src/notification/dto/notification.dto.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { Expose } from "class-transformer"; -import { ApiProperty } from "@nestjs/swagger"; - -export class NotificationLogDto { - @Expose() - notificationLogId: string; - @ApiProperty({ - description: "Content of notification", - }) - @Expose() - content: string; - - @ApiProperty({ - description: "Recepients of notification", - }) - @Expose() - recepients: []; - - @ApiProperty({ - description: "Module of notification", - }) - @Expose() - module: string; - - @ApiProperty({ - description: "Template content Id", - }) - @Expose() - templateContentId: string; - - @ApiProperty({ - description: "medium of notification", - }) - @Expose() - medium: string; - - @ApiProperty({ - description: "Sent date of notification", - }) - @Expose() - sentDate: string; - - @Expose() - sentBy: string; - - @ApiProperty({ - description: "options of notification", - }) - @Expose() - options: string; - - @Expose() - createdAt: string; - - @Expose() - updatedAt: string; - - @Expose() - createdBy: string; - - @Expose() - updatedBy: string; - - @Expose() - templateId: string; - - @Expose() - scheduleDate: string; - - constructor(obj: any) { - Object.assign(this, obj); - } -} diff --git a/src/notification/instantNotification.controller.ts b/src/notification/instantNotification.controller.ts deleted file mode 100644 index f87660cf..00000000 --- a/src/notification/instantNotification.controller.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { - ApiTags, - ApiBody, - ApiOkResponse, - ApiForbiddenResponse, - ApiCreatedResponse, - ApiBasicAuth, - ApiQuery, -} from "@nestjs/swagger"; -import { - Controller, - Get, - Post, - Body, - Param, - UseInterceptors, - ClassSerializerInterceptor, - SerializeOptions, - Req, - Request, - CacheInterceptor, - Query, -} from "@nestjs/common"; - -import { NotificationService } from "src/adapters/sunbirdrc/notification.adapter"; -import { NotificationSearchDto } from "./dto/notification-search.dto"; -@ApiTags("Instant Notification") -@Controller("instantNotification") -export class instantNotificationController { - constructor(private service: NotificationService) {} - - @Post("instantSend") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Notification has been sent successfully.", - }) - @UseInterceptors(ClassSerializerInterceptor) - @ApiForbiddenResponse({ description: "Forbidden" }) - @ApiQuery({ name: "module" }) - @ApiQuery({ name: "eventTrigger" }) - @ApiQuery({ name: "templateId" }) - @ApiQuery({ name: "senderId" }) - @ApiQuery({ name: "groupId" }) - @ApiQuery({ name: "channel" }) - public async instantSendNotification( - @Query("module") module: string, - @Query("eventTrigger") eventTrigger: string, - @Query("templateId") templateId: string, - @Query("senderId") senderId: string, - @Query("groupId") groupId: string, - @Query("channel") channel: string, - @Req() request: Request - ) { - return this.service.instantSendNotification( - module, - eventTrigger, - templateId, - senderId, - groupId, - channel, - request - ); - } - - @Get("log/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "Notification Log detail." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async getNotification( - @Param("id") id: string, - @Req() request: Request - ) { - return this.service.getNotification(id, request); - } - - @Post("log/search") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Notification log list." }) - @ApiBody({ type: NotificationSearchDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async searchStudent( - @Req() request: Request, - @Body() notificationSearchDto: NotificationSearchDto - ) { - return await this.service.searchNotification( - request, - notificationSearchDto - ); - } -} diff --git a/src/notification/notification.module.ts b/src/notification/notification.module.ts deleted file mode 100644 index 11d7aa07..00000000 --- a/src/notification/notification.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { CacheModule, Module } from "@nestjs/common"; -import { HttpModule } from "@nestjs/axios"; -import { NotificationService } from "src/adapters/sunbirdrc/notification.adapter"; -import { instantNotificationController } from "./instantNotification.controller"; -import { scheduleNotificationController } from "./scheduleNotification.controller"; -import { ScheduleModule } from "@nestjs/schedule"; -const ttl = process.env.TTL as never; -@Module({ - imports: [ - HttpModule, - CacheModule.register({ - ttl: ttl, - }), - ScheduleModule.forRoot(), - ], - controllers: [instantNotificationController, scheduleNotificationController], - providers: [NotificationService], -}) -export class NotificationModule {} diff --git a/src/notification/scheduleNotification.controller.ts b/src/notification/scheduleNotification.controller.ts deleted file mode 100644 index ef5f1aa2..00000000 --- a/src/notification/scheduleNotification.controller.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { - ApiTags, - ApiBody, - ApiOkResponse, - ApiForbiddenResponse, - ApiCreatedResponse, - ApiBasicAuth, - ApiQuery, -} from "@nestjs/swagger"; -import { - Controller, - Get, - Post, - Body, - Param, - UseInterceptors, - ClassSerializerInterceptor, - SerializeOptions, - Req, - Request, - CacheInterceptor, - Query, -} from "@nestjs/common"; - -import { NotificationService } from "src/adapters/sunbirdrc/notification.adapter"; -import { NotificationSearchDto } from "./dto/notification-search.dto"; - -@ApiTags("Schedule Notification") -@Controller("scheduleNotification") -export class scheduleNotificationController { - constructor(private service: NotificationService) {} - - @Post("scheduledSend") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Notification has been sent successfully.", - }) - @UseInterceptors(ClassSerializerInterceptor) - @ApiForbiddenResponse({ description: "Forbidden" }) - @ApiQuery({ name: "module" }) - @ApiQuery({ name: "eventTrigger" }) - @ApiQuery({ name: "templateId" }) - @ApiQuery({ name: "senderId" }) - @ApiQuery({ name: "groupId" }) - @ApiQuery({ name: "channel" }) - @ApiQuery({ name: "month" }) - @ApiQuery({ name: "date" }) - @ApiQuery({ name: "hours" }) - @ApiQuery({ name: "minutes" }) - @ApiQuery({ name: "taskName" }) - public async scheduledSendNotification( - @Query("module") module: string, - @Query("eventTrigger") eventTrigger: string, - @Query("templateId") templateId: string, - @Query("senderId") senderId: string, - @Query("groupId") groupId: string, - @Query("channel") channel: string, - @Query("month") month: string, - @Query("date") date: string, - @Query("hours") hours: string, - @Query("minutes") minutes: string, - @Query("taskName") taskName: string, - @Req() request: Request - ) { - return this.service.scheduleSendNotification( - module, - eventTrigger, - templateId, - senderId, - groupId, - channel, - month, - date, - hours, - minutes, - taskName, - request - ); - } - - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "Notification Log detail." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async getNotification( - @Param("id") id: string, - @Req() request: Request - ) { - return this.service.getScheduleNotification(id, request); - } - - @Post("/search") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Notification log list." }) - @ApiBody({ type: NotificationSearchDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async searchStudent( - @Req() request: Request, - @Body() notificationSearchDto: NotificationSearchDto - ) { - return await this.service.searchSchedulehNotification( - request, - notificationSearchDto - ); - } -} diff --git a/src/rbac/assign-privilege/assign-privilege.apater.ts b/src/rbac/assign-privilege/assign-privilege.apater.ts new file mode 100644 index 00000000..84625e6a --- /dev/null +++ b/src/rbac/assign-privilege/assign-privilege.apater.ts @@ -0,0 +1,26 @@ +import { Injectable } from "@nestjs/common"; +import { PostgresAssignPrivilegeService } from "src/adapters/postgres/rbac/privilegerole.adapter"; +import { HasuraAssignPrivilegeService } from "src/adapters/hasura/rbac/privilegerole.adapter"; +import { IServicelocatorassignRole } from "src/adapters/assignroleservicelocater"; +import { IServicelocatorprivilegeRole } from "src/adapters/assignprivilegelocater"; + +@Injectable() +export class AssignPrivilegeAdapter { + constructor(private hasuraProvider: HasuraAssignPrivilegeService, + private postgresProvider:PostgresAssignPrivilegeService) {} + buildPrivilegeRoleAdapter(): IServicelocatorprivilegeRole { + let adapter: IServicelocatorprivilegeRole; + + switch (process.env.ADAPTERSOURCE) { + case "hasura": + adapter = this.hasuraProvider; + break; + case "postgres": + adapter = this.postgresProvider; + break; + default: + throw new Error("Invalid ADAPTERSOURCE environment variable. Please specify either 'hasura' or 'postgres'."); + } + return adapter; + } +} diff --git a/src/rbac/assign-privilege/assign-privilege.controller.ts b/src/rbac/assign-privilege/assign-privilege.controller.ts new file mode 100644 index 00000000..545b6df0 --- /dev/null +++ b/src/rbac/assign-privilege/assign-privilege.controller.ts @@ -0,0 +1,53 @@ +import { Controller, Get, Post, Body, Patch, Param, Delete, UsePipes, ValidationPipe, Req, Res, SerializeOptions, UseGuards } from '@nestjs/common'; +import { AssignPrivilegeAdapter } from './assign-privilege.apater'; +import { CreatePrivilegeRoleDto } from './dto/create-assign-privilege.dto'; +import { Response, Request} from "express"; +import { ApiBasicAuth, ApiCreatedResponse, ApiBody, ApiForbiddenResponse, ApiHeader, ApiOkResponse, ApiTags } from '@nestjs/swagger'; +import { JwtAuthGuard } from 'src/common/guards/keycloak.guard'; + +@ApiTags('rbac') +@Controller('assignprivilege') +@UseGuards(JwtAuthGuard) +export class AssignPrivilegeController { + constructor(private readonly assignPrivilegeAdpater: AssignPrivilegeAdapter) {} + + @Post() + @UsePipes(new ValidationPipe()) + @ApiBasicAuth("access-token") + @ApiCreatedResponse({ description: "Privilege has been Assigned successfully." }) + @ApiBody({ type: CreatePrivilegeRoleDto }) + @ApiForbiddenResponse({ description: "Forbidden" }) + @ApiHeader({ name: "tenantid" }) + public async create(@Req() request: Request, + @Body() createAssignPrivilegeDto:CreatePrivilegeRoleDto , + @Res() response: Response) { + return await this.assignPrivilegeAdpater.buildPrivilegeRoleAdapter().createPrivilegeRole(request,createAssignPrivilegeDto,response); + } + + @Get("/:roleid") + @ApiBasicAuth("access-token") + @ApiOkResponse({ description: "Privilege Details." }) + @ApiHeader({ name: "tenantid" }) + @ApiForbiddenResponse({ description: "Forbidden" }) + @SerializeOptions({strategy: "excludeAll",}) + public async getRole( + @Param("roleid") roleId: string, + @Req() request: Request, + @Res() response: Response + ) { + return await this.assignPrivilegeAdpater.buildPrivilegeRoleAdapter().getPrivilegeRole(roleId, request, response); + } + + // @Delete("/:id") + // @ApiBasicAuth("access-token") + // @ApiCreatedResponse({ description: "Assigend Privililege has been deleted successfully." }) + // @ApiForbiddenResponse({ description: "Forbidden" }) + // public async deletePrivilegeRole( + // @Param("id") userId: string, + // @Res() response: Response + // ) { + // const result = await this.assignPrivilegeAdpater.buildPrivilegeRoleAdapter().deletePrivilegeRole(userId); + // return response.status(result.statusCode).json(result); + // } + +} diff --git a/src/rbac/assign-privilege/assign-privilege.module.ts b/src/rbac/assign-privilege/assign-privilege.module.ts new file mode 100644 index 00000000..5daef2a9 --- /dev/null +++ b/src/rbac/assign-privilege/assign-privilege.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common'; +import { AssignPrivilegeAdapter } from './assign-privilege.apater'; +import { AssignPrivilegeController } from './assign-privilege.controller'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { PostgresAssignPrivilegeService } from 'src/adapters/postgres/rbac/privilegerole.adapter'; +import { HasuraAssignPrivilegeService } from 'src/adapters/hasura/rbac/privilegerole.adapter'; +import { HttpModule } from '@nestjs/axios'; +import { RolePrivilegeMapping } from './entities/assign-privilege.entity'; + + +@Module({ + imports:[TypeOrmModule.forFeature([RolePrivilegeMapping]),HttpModule], + controllers: [AssignPrivilegeController], + providers: [AssignPrivilegeAdapter,HasuraAssignPrivilegeService,PostgresAssignPrivilegeService] +}) +export class AssignPrivilegeModule {} diff --git a/src/rbac/assign-privilege/dto/create-assign-privilege.dto.ts b/src/rbac/assign-privilege/dto/create-assign-privilege.dto.ts new file mode 100644 index 00000000..b05f09c7 --- /dev/null +++ b/src/rbac/assign-privilege/dto/create-assign-privilege.dto.ts @@ -0,0 +1,38 @@ +import { Expose } from "class-transformer"; +import { ApiProperty } from "@nestjs/swagger"; +import {IsNotEmpty,IsString, IsUUID} from "class-validator" + +export class CreatePrivilegeRoleDto { + @ApiProperty({ + type: String, + description: "Privilege Id", + default: "", + }) + @Expose() + privilegeId: string[]; + + @ApiProperty({ + type: String, + description: "Role Id", + default: "", + }) + @Expose() + @IsUUID() + @IsNotEmpty() + roleId: string; + + @ApiProperty({ + type: String, + description: "Boolean to Delete Previous Privileges", + default: "", + }) + @Expose() + @IsNotEmpty() + deleteOld:boolean; + + + + constructor(obj: any) { + Object.assign(this, obj); + } +} diff --git a/src/rbac/assign-privilege/entities/assign-privilege.entity.ts b/src/rbac/assign-privilege/entities/assign-privilege.entity.ts new file mode 100644 index 00000000..40054dbe --- /dev/null +++ b/src/rbac/assign-privilege/entities/assign-privilege.entity.ts @@ -0,0 +1,28 @@ +import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, BaseEntity } from 'typeorm'; + +@Entity({ name: 'RolePrivilegesMapping' }) +export class RolePrivilegeMapping { + @PrimaryGeneratedColumn('uuid', { name: 'rolePrivilegesId' }) + rolePrivilegesId: string; + + @Column('uuid', { name: 'roleId' }) + roleId: string; + + @Column('uuid', { name: 'createdBy', nullable: true }) + createdBy: string | null; + + @Column('uuid', { name: 'updatedBy', nullable: true }) + updatedBy: string | null; + + @CreateDateColumn({ name: 'createdAt', type: 'timestamp with time zone', default: () => 'CURRENT_TIMESTAMP' }) + createdAt: Date; + + @UpdateDateColumn({ name: 'updatedAt', type: 'timestamp with time zone', default: () => 'CURRENT_TIMESTAMP' }) + updatedAt: Date; + + @Column('uuid', { name: 'privilegeId', nullable: true }) + privilegeId: string | null; +} + + + diff --git a/src/rbac/assign-role/assign-role.apater.ts b/src/rbac/assign-role/assign-role.apater.ts new file mode 100644 index 00000000..da7ac3d0 --- /dev/null +++ b/src/rbac/assign-role/assign-role.apater.ts @@ -0,0 +1,25 @@ +import { Injectable } from "@nestjs/common"; +import { PostgresAssignroleService } from "src/adapters/postgres/rbac/assignrole-adapter"; +import { HasuraAssignRoleService } from "src/adapters/hasura/rbac/assignrole.adapter"; +import { IServicelocatorassignRole } from "src/adapters/assignroleservicelocater"; + +@Injectable() +export class AssignRoleAdapter { + constructor(private hasuraProvider: HasuraAssignRoleService, + private postgresProvider:PostgresAssignroleService) {} + buildassignroleAdapter(): IServicelocatorassignRole { + let adapter: IServicelocatorassignRole; + + switch (process.env.ADAPTERSOURCE) { + case "hasura": + adapter = this.hasuraProvider; + break; + case "postgres": + adapter = this.postgresProvider; + break; + default: + throw new Error("Invalid ADAPTERSOURCE environment variable. Please specify either 'hasura' or 'postgres'."); + } + return adapter; + } +} diff --git a/src/rbac/assign-role/assign-role.controller.ts b/src/rbac/assign-role/assign-role.controller.ts new file mode 100644 index 00000000..cf7cd6da --- /dev/null +++ b/src/rbac/assign-role/assign-role.controller.ts @@ -0,0 +1,71 @@ +import { Controller, Get, Post, Body, Patch, Param, Delete, UsePipes, ValidationPipe, Req, Res, SerializeOptions, Headers, UseGuards, UseFilters } from '@nestjs/common'; +import { AssignRoleAdapter } from './assign-role.apater'; +import { CreateAssignRoleDto } from './dto/create-assign-role.dto'; +import { Response, Request } from "express"; +import { ApiBasicAuth, ApiCreatedResponse, ApiBody, ApiForbiddenResponse, ApiHeader, ApiOkResponse, ApiTags, ApiBadRequestResponse, ApiInternalServerErrorResponse, ApiConflictResponse, ApiNotFoundResponse } from '@nestjs/swagger'; +import { JwtAuthGuard } from "src/common/guards/keycloak.guard"; +import { DeleteAssignRoleDto } from './dto/delete-assign-role.dto'; +import { AllExceptionsFilter } from 'src/common/filters/exception.filter'; +import { APIID } from 'src/common/utils/api-id.config'; + + +@ApiTags('rbac') +@Controller('rbac/usersRoles') +@UseGuards(JwtAuthGuard) +export class AssignRoleController { + constructor(private readonly assignRoleAdpater: AssignRoleAdapter) { } + + @UseFilters(new AllExceptionsFilter(APIID.USERROLE_CREATE)) + @Post() + @UsePipes(new ValidationPipe()) + @ApiBasicAuth("access-token") + @ApiCreatedResponse({ description: "Role assigned successfully to the user in the specified tenant." }) + @ApiBadRequestResponse({ description: "Bad request." }) + @ApiInternalServerErrorResponse({ description: "Internal Server Error." }) + @ApiConflictResponse({ description: "Role is already assigned to this user." }) + @ApiBody({ type: CreateAssignRoleDto }) + // @ApiHeader({ name: "tenantid" }) + public async create( + @Req() request: Request, + @Body() createAssignRoleDto: CreateAssignRoleDto, + @Res() response: Response, + @Headers() headers, + ) { + return await this.assignRoleAdpater.buildassignroleAdapter().createAssignRole(request, createAssignRoleDto, response); + // return response.status(result.statusCode).json(result); + } + + @UseFilters(new AllExceptionsFilter(APIID.USERROLE_GET)) + @Get("/:userId") + @ApiBasicAuth("access-token") + @ApiOkResponse({ description: "Role Detail." }) + @ApiHeader({ name: "tenantid" }) + @ApiForbiddenResponse({ description: "Forbidden" }) + @SerializeOptions({ strategy: "excludeAll", }) + public async getRole( + @Param("userId") userId: string, + @Req() request: Request, + @Res() response: Response + ) { + return await this.assignRoleAdpater.buildassignroleAdapter().getAssignedRole(userId, request, response); + // return response.status(result.statusCode).json(result); + } + + @UseFilters(new AllExceptionsFilter(APIID.USERROLE_DELETE)) + @Delete("/:userId") + @ApiBasicAuth("access-token") + @ApiHeader({ name: "tenantid" }) + @ApiOkResponse({ description: "Role deleted successfully." }) + @ApiNotFoundResponse({ description: "Data not found" }) + @ApiBadRequestResponse({ description: "Bad request" }) + public async deleteRole( + @Body() deleteAssignRoleDto: DeleteAssignRoleDto, // Modify this line to accept DeleteAssignRoleDto + @Res() response: Response + ) { + return await this.assignRoleAdpater + .buildassignroleAdapter() + .deleteAssignedRole(deleteAssignRoleDto, response); + // return response.status(result.statusCode).json(result); + } + +} diff --git a/src/rbac/assign-role/assign-role.module.ts b/src/rbac/assign-role/assign-role.module.ts new file mode 100644 index 00000000..04ce19e2 --- /dev/null +++ b/src/rbac/assign-role/assign-role.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common'; +import { AssignRoleAdapter } from './assign-role.apater'; +import { AssignRoleController } from './assign-role.controller'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { UserRoleMapping } from './entities/assign-role.entity'; +import { Role } from "src/rbac/role/entities/role.entity"; +import { PostgresAssignroleService } from 'src/adapters/postgres/rbac/assignrole-adapter'; +import { HasuraAssignRoleService } from 'src/adapters/hasura/rbac/assignrole.adapter'; +import { HttpModule } from '@nestjs/axios'; + +@Module({ + imports:[TypeOrmModule.forFeature([UserRoleMapping,Role]),HttpModule], + controllers: [AssignRoleController], + providers: [AssignRoleAdapter,HasuraAssignRoleService,PostgresAssignroleService] +}) +export class AssignRoleModule {} diff --git a/src/rbac/assign-role/dto/create-assign-role.dto.ts b/src/rbac/assign-role/dto/create-assign-role.dto.ts new file mode 100644 index 00000000..be516b76 --- /dev/null +++ b/src/rbac/assign-role/dto/create-assign-role.dto.ts @@ -0,0 +1,61 @@ +import { Expose } from "class-transformer"; +import { ApiProperty } from "@nestjs/swagger"; +import { IsNotEmpty, IsString, IsUUID, IsArray } from "class-validator"; + +export class CreateAssignRoleDto { + @ApiProperty({ + type: String, + description: "Tenant Id", + default: "", + }) + @Expose() + @IsUUID() + tenantId: string; + + @ApiProperty({ + type: String, + description: "User Id of User", + default: "", + }) + @Expose() + @IsUUID() + userId: string; + + @ApiProperty({ + type: [String], + description: "Assigned Role Ids", + default: [], + }) + @Expose() + @IsArray() + @IsUUID(undefined, { each: true }) // Validate each item in the array to be a UUID + @IsNotEmpty({ each: true }) // Ensure each item in the array is not empty + roleId: string[]; + + constructor(obj: any) { + Object.assign(this, obj); + } + +} + +export class ResponseAssignRoleDto { + @Expose() + userId: string; + + @Expose() + roleId: string; + + @Expose() + tenantId: string; + + @Expose() + message: string; + + constructor(data: { userId: string; roleId: string; tenantId: string }, message: string) { + this.userId = data.userId; + this.roleId = data.roleId; + this.tenantId = data.tenantId; + this.message = message; + } +} + diff --git a/src/rbac/assign-role/dto/delete-assign-role.dto.ts b/src/rbac/assign-role/dto/delete-assign-role.dto.ts new file mode 100644 index 00000000..2dec6bba --- /dev/null +++ b/src/rbac/assign-role/dto/delete-assign-role.dto.ts @@ -0,0 +1,28 @@ +import { Expose } from "class-transformer"; +import { ApiProperty } from "@nestjs/swagger"; +import { IsArray, IsNotEmpty, IsString, IsUUID } from "class-validator"; + +export class DeleteAssignRoleDto { + @ApiProperty({ + type: String, + description: "User Id of User", + default: "", + }) + @Expose() + // @IsUUID() + userId: string; + + @ApiProperty({ + type: [String], // Define roleId as an array of strings + description: "Assigned Role Id", + default: [], + }) + @IsArray() + @IsString({ each: true }) // Validate each string in the array + // @IsUUID(4, { each: true }) // Specify the UUID version (4) and validate each UUID string in the array + roleId: string[]; +} + +// constructor(obj: any) { +// Object.assign(this, obj); +// } \ No newline at end of file diff --git a/src/rbac/assign-role/entities/assign-role.entity.ts b/src/rbac/assign-role/entities/assign-role.entity.ts new file mode 100644 index 00000000..82695f6e --- /dev/null +++ b/src/rbac/assign-role/entities/assign-role.entity.ts @@ -0,0 +1,51 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + ManyToOne, + JoinColumn, + CreateDateColumn, + UpdateDateColumn, +} from "typeorm"; +import { User } from "src/user/entities/user-entity"; +import { Role } from "../../role/entities/role.entity"; +@Entity({ name: "UserRolesMapping" }) +export class UserRoleMapping { + @PrimaryGeneratedColumn("uuid") + userRolesId: string; + + @Column("uuid") + userId: string; + + @Column("uuid") + tenantId: string; + + @Column("uuid") + roleId: string; + + @CreateDateColumn({ + type: "timestamp with time zone", + default: () => "CURRENT_TIMESTAMP", + }) + createdAt: Date; + + @UpdateDateColumn({ + type: "timestamp with time zone", + default: () => "CURRENT_TIMESTAMP", + }) + updatedAt: Date; + + @Column() + createdBy: string; + + @Column() + updatedBy: string; + + // @ManyToOne(() => User, (user) => user.userRoleMappings) + // @JoinColumn({ name: "userId" }) + // user: User; + + // @ManyToOne(() => Role) + // @JoinColumn({ name: "roleId" }) + // role: Role; +} diff --git a/src/rbac/privilege/dto/privilege.dto.ts b/src/rbac/privilege/dto/privilege.dto.ts new file mode 100644 index 00000000..3db03d8d --- /dev/null +++ b/src/rbac/privilege/dto/privilege.dto.ts @@ -0,0 +1,71 @@ +import { Expose, Type } from "class-transformer"; +import { ApiProperty } from "@nestjs/swagger"; +import {IsNotEmpty,IsString, IsUUID, Matches, ValidateNested} from "class-validator" + +export class PrivilegeDto { + @Expose() + privilegeId: string; + + @ApiProperty({ + type: String, + description: "Privilege title", + default: "", + }) + @Expose() + @IsNotEmpty() + title: string; + + + @ApiProperty({ + type: String, + description: "Privilege title", + default: "", + }) + @IsNotEmpty() + @Expose() + code: string; + + @Expose() + createdAt: Date; + + @Expose() + updatedAt: Date; + + @Expose() + createdBy: string; + + @Expose() + updatedBy: string; + + constructor(obj: any) { + Object.assign(this, obj); + } +} + + + +export class CreatePrivilegesDto { + @ApiProperty({type:[PrivilegeDto]}) + @ValidateNested({ each: true }) + @Type(() => PrivilegeDto) + privileges: PrivilegeDto[]; +} + + +export class PrivilegeResponseDto { + @Expose() + privilegeId: string; + + @Expose() + title: string; + + @Expose() + code: string; + + constructor(privilegeDto: PrivilegeDto) { + this.privilegeId = privilegeDto.privilegeId; + this.title = privilegeDto.title; + this.code = privilegeDto.code; + + } +} diff --git a/src/rbac/privilege/dto/update-privilege.dto.ts b/src/rbac/privilege/dto/update-privilege.dto.ts new file mode 100644 index 00000000..7aff7bd6 --- /dev/null +++ b/src/rbac/privilege/dto/update-privilege.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { PrivilegeDto } from './privilege.dto'; + +export class UpdatePrivilegeDto extends PartialType(PrivilegeDto) {} diff --git a/src/rbac/privilege/entities/privilege.entity.ts b/src/rbac/privilege/entities/privilege.entity.ts new file mode 100644 index 00000000..e11638bf --- /dev/null +++ b/src/rbac/privilege/entities/privilege.entity.ts @@ -0,0 +1,28 @@ +// export class Privilege {} +import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm'; + +@Entity({ name: "Privileges" }) +export class Privilege { + @PrimaryGeneratedColumn('uuid') + privilegeId: string; + + @Column({name:"name"}) + title: string; + + @Column() + code:string + + @CreateDateColumn({ type: "timestamp with time zone", default: () => "CURRENT_TIMESTAMP" }) + createdAt: Date; + + @UpdateDateColumn({ type: "timestamp with time zone", default: () => "CURRENT_TIMESTAMP" }) + updatedAt: Date; + + @Column() + createdBy: string; + + @Column() + updatedBy: string; + + +} diff --git a/src/rbac/privilege/privilege.controller.ts b/src/rbac/privilege/privilege.controller.ts new file mode 100644 index 00000000..b5e09266 --- /dev/null +++ b/src/rbac/privilege/privilege.controller.ts @@ -0,0 +1,159 @@ +import { CreatePrivilegesDto, PrivilegeDto } from "./dto/privilege.dto"; +import { UpdatePrivilegeDto } from "./dto/update-privilege.dto"; +import { + Controller, + Get, + Post, + Body, + Put, + Param, + SerializeOptions, + Req, + UsePipes, + ValidationPipe, + Res, + Headers, + Delete, + UseGuards, + Query, + UseFilters, + ParseUUIDPipe, +} from "@nestjs/common"; +import { + ApiTags, + ApiBody, + ApiOkResponse, + ApiForbiddenResponse, + ApiCreatedResponse, + ApiBasicAuth, + ApiHeader, + ApiBadRequestResponse, + ApiInternalServerErrorResponse, + ApiConflictResponse, + ApiNotFoundResponse, +} from "@nestjs/swagger"; +import { Request } from "@nestjs/common"; +import { Response, response } from "express"; +import { JwtAuthGuard } from "src/common/guards/keycloak.guard"; +import { PrivilegeAdapter } from "./privilegeadapter"; +import { v4 as uuidv4 } from "uuid"; +import { AllExceptionsFilter } from "src/common/filters/exception.filter"; +import { APIID } from "src/common/utils/api-id.config"; + +@UseGuards(JwtAuthGuard) +@ApiTags("rbac") +@Controller('rbac/privileges') +export class PrivilegeController { + constructor(private readonly privilegeAdapter: PrivilegeAdapter) { } + + @UseFilters(new AllExceptionsFilter(APIID.PRIVILEGE_BYROLEID)) + @Get() + @ApiBasicAuth("access-token") + @ApiOkResponse({ description: "Privilege Detail." }) + @ApiBadRequestResponse({ description: "Bad Request" }) + @ApiInternalServerErrorResponse({ description: "Internal Server Error" }) + @SerializeOptions({ strategy: "excludeAll", }) + public async getPrivilegebyRoleId( + @Query("tenantId") tenantId: string, + @Query("roleId") roleId: string, + @Req() request: Request, + @Res() response: Response + ) { + return await this.privilegeAdapter.buildPrivilegeAdapter().getPrivilegebyRoleId(tenantId, roleId, request, response); + } + + + @UseFilters(new AllExceptionsFilter(APIID.PRIVILEGE_BYPRIVILEGEID)) + @Get("/:privilegeId") + @ApiBasicAuth("access-token") + @ApiOkResponse({ description: "Privilege Detail." }) + @ApiBadRequestResponse({ description: "Bad Request" }) + @ApiInternalServerErrorResponse({ description: "Internal Server Error" }) + @ApiHeader({ name: "tenantid" }) + @SerializeOptions({ strategy: "excludeAll" }) + public async getPrivilege( + @Param("privilegeId") privilegeId: string, + @Req() request: Request, + @Res() response: Response + ) { + return await this.privilegeAdapter + .buildPrivilegeAdapter() + .getPrivilege(privilegeId, request, response); + } + + + + + @UseFilters(new AllExceptionsFilter(APIID.PRIVILEGE_CREATE)) + @Post("/create") + @UsePipes(new ValidationPipe()) + @ApiBasicAuth("access-token") + @ApiCreatedResponse({ + description: "Privilege has been created successfully.", + }) + @ApiBadRequestResponse({ description: "Bad Request" }) + @ApiInternalServerErrorResponse({ description: "Internal Server Error" }) + @ApiConflictResponse({ description: "Privilege Already Exists" }) + @ApiBody({ type: CreatePrivilegesDto }) + @ApiHeader({ name: "tenantid" }) + public async createPrivilege( + @Req() request, + @Body() createPrivilegesDto: CreatePrivilegesDto, + @Res() response: Response + ) { + return await this.privilegeAdapter + .buildPrivilegeAdapter() + .createPrivilege(request.user.userId, createPrivilegesDto, response); + } + + // @Put("/:id") + // @ApiBasicAuth("access-token") + // @ApiOkResponse({ description: "Role updated successfully." }) + // @ApiBadRequestResponse({ description: "Bad Request" }) + // @ApiInternalServerErrorResponse({ description: "Internal Server Error" }) + // @ApiConflictResponse({ description: "Privilege Already Exists" }) + // @ApiBody({ type:PrivilegeDto }) + // @ApiForbiddenResponse({ description: "Forbidden" }) + // @ApiHeader({ name: "tenantid", }) + // public async updatePrivilege( + // @Param("id") privilegeId: string, + // @Req() request: Request, + // @Body() privilegeDto: PrivilegeDto, + // @Res() response: Response + // ) { + // const result = await this.privilegeAdapter.buildPrivilegeAdapter().updatePrivilege(privilegeId, request, privilegeDto); + // return response.status(result.statusCode).json(result); + // } + + // @Get() + // @ApiBasicAuth("access-token") + // @ApiOkResponse({ description: "Privilege Detail." }) + // @ApiHeader({ name: "tenantid" }) + // @ApiOkResponse({ description: "Privileges List" }) + // @ApiBadRequestResponse({ description: "Bad Request" }) + // @ApiInternalServerErrorResponse({ description: "Internal Server Error" }) + // @SerializeOptions({strategy: "excludeAll",}) + // public async getAllPrivilege( + // @Req() request: Request, + // @Res() response: Response + // ) { + // const result = await this.privilegeAdapter.buildPrivilegeAdapter().getAllPrivilege(request); + // return response.status(result.statusCode).json(result); + // } + + @UseFilters(new AllExceptionsFilter(APIID.PRIVILEGE_DELETE)) + @Delete("/:privilegeId") + @ApiBasicAuth("access-token") + @ApiHeader({ name: "tenantid" }) + @ApiOkResponse({ description: "Role deleted successfully." }) + @ApiNotFoundResponse({ description: "Data not found" }) + @ApiBadRequestResponse({ description: "Bad request" }) + public async deleteRole( + @Param("privilegeId", ParseUUIDPipe) privilegeId: string, + @Res() response: Response + ) { + return await this.privilegeAdapter + .buildPrivilegeAdapter() + .deletePrivilege(privilegeId, response); + } +} diff --git a/src/rbac/privilege/privilege.module.ts b/src/rbac/privilege/privilege.module.ts new file mode 100644 index 00000000..3f020762 --- /dev/null +++ b/src/rbac/privilege/privilege.module.ts @@ -0,0 +1,29 @@ +import { Module } from '@nestjs/common'; +import { PrivilegeController } from './privilege.controller'; +import { Privilege } from './entities/privilege.entity'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { HttpModule } from '@nestjs/axios'; +import { PostgresModule } from 'src/adapters/postgres/potsgres-module'; +import { HasuraModule } from 'src/adapters/hasura/hasura.module'; +import { PrivilegeAdapter } from './privilegeadapter'; +import { HasuraPrivilegeService } from 'src/adapters/hasura/rbac/privilege.adapter'; +import { PostgresRoleService } from 'src/adapters/postgres/rbac/role-adapter'; +import { PostgresPrivilegeService } from 'src/adapters/postgres/rbac/privilege-adapter'; +import { HasuraRoleService } from 'src/adapters/hasura/rbac/role.adapter'; +import { Role } from '../role/entities/role.entity'; +import { Repository } from 'typeorm'; +import { RolePrivilegeMapping } from '../assign-privilege/entities/assign-privilege.entity'; +import { UserRoleMapping } from '../assign-role/entities/assign-role.entity'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([Privilege,Role,UserRoleMapping,RolePrivilegeMapping]), + TypeOrmModule.forFeature([Privilege, Role, RolePrivilegeMapping]), + HttpModule, + PostgresModule, + HasuraModule, + ], + controllers: [PrivilegeController], + providers: [PrivilegeAdapter,HasuraPrivilegeService,PostgresPrivilegeService,HasuraRoleService,PostgresRoleService,Repository] +}) +export class PrivilegeModule {} diff --git a/src/rbac/privilege/privilegeadapter.ts b/src/rbac/privilege/privilegeadapter.ts new file mode 100644 index 00000000..f12253ef --- /dev/null +++ b/src/rbac/privilege/privilegeadapter.ts @@ -0,0 +1,22 @@ +import { Injectable } from "@nestjs/common"; +import { HasuraPrivilegeService } from "src/adapters/hasura/rbac/privilege.adapter"; +import { IServicelocator } from "src/adapters/privilegeservicelocator"; +import { PostgresPrivilegeService } from "src/adapters/postgres/rbac/privilege-adapter"; + +@Injectable() +export class PrivilegeAdapter { + constructor(private hasuraProvider: HasuraPrivilegeService, + private postgresProvider:PostgresPrivilegeService) {} + buildPrivilegeAdapter(): IServicelocator { + let adapter: IServicelocator; + + switch (process.env.ADAPTERSOURCE) { + case "hasura": + adapter = this.hasuraProvider; + break; + case "postgres": + adapter = this.postgresProvider; + } + return adapter; + } +} diff --git a/src/rbac/rbac.module.ts b/src/rbac/rbac.module.ts new file mode 100644 index 00000000..2fb2744d --- /dev/null +++ b/src/rbac/rbac.module.ts @@ -0,0 +1,19 @@ +import { Module } from "@nestjs/common"; +import { RoleModule } from "./role/role.module"; +import { PrivilegeModule } from './privilege/privilege.module'; +import { AssignRoleModule } from './assign-role/assign-role.module'; +import { AssignPrivilegeModule } from "./assign-privilege/assign-privilege.module"; +import { RolePrivilegeMapping } from "./assign-privilege/entities/assign-privilege.entity"; +import { UserRoleMapping } from "./assign-role/entities/assign-role.entity"; + +@Module({ + imports: [ + RoleModule, + PrivilegeModule, + AssignRoleModule, + AssignPrivilegeModule, + UserRoleMapping, + RolePrivilegeMapping + ], +}) +export class RbacModule {} diff --git a/src/group/dto/group-search.dto.ts b/src/rbac/role/dto/role-search.dto.ts similarity index 72% rename from src/group/dto/group-search.dto.ts rename to src/rbac/role/dto/role-search.dto.ts index a9e10f69..41ee1a8d 100644 --- a/src/group/dto/group-search.dto.ts +++ b/src/rbac/role/dto/role-search.dto.ts @@ -1,10 +1,13 @@ import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; +import { IsNumberString } from "class-validator"; -export class GroupSearchDto { + +export class RoleSearchDto { @ApiProperty({ type: String, description: "Limit", }) + @IsNumberString() limit: string; @ApiProperty({ @@ -20,7 +23,7 @@ export class GroupSearchDto { @ApiPropertyOptional() filters: object; - constructor(partial: Partial) { + constructor(partial: Partial) { Object.assign(this, partial); } } diff --git a/src/rbac/role/dto/role.dto.ts b/src/rbac/role/dto/role.dto.ts new file mode 100644 index 00000000..25902d32 --- /dev/null +++ b/src/rbac/role/dto/role.dto.ts @@ -0,0 +1,81 @@ +import { Expose, Type } from "class-transformer"; +import { ApiProperty } from "@nestjs/swagger"; +import {IsNotEmpty,IsString, IsUUID, ValidateNested, isUUID} from "class-validator" + +export class RoleDto { + + @Expose() + roleId: string; + + @ApiProperty({ + type: String, + description: "The name of the role", + default: "", + }) + @Expose() + @IsNotEmpty() + title: string; + + @Expose() + code: string; + + @Expose() + createdAt: Date; + + @Expose() + updatedAt: Date; + + @Expose() + createdBy: string; + + @Expose() + updatedBy: string; + + + + constructor(obj: any) { + Object.assign(this, obj); + } +} + + +export class CreateRolesDto { + + @ApiProperty({ + type: String, + description: "Tenant" + }) + @Expose() + @IsNotEmpty() + @IsUUID() + tenantId: string; + + + @ApiProperty( {type: [RoleDto]} ) + @ValidateNested({ each: true }) + @Type(() => RoleDto) + roles: RoleDto[]; + + constructor(obj: any) { + Object.assign(this, obj); + } +} + + +export class RolesResponseDto { + @Expose() + roleId: string; + + @Expose() + title: string; + + @Expose() + code: string; + + constructor(roleDto: RoleDto) { + this.roleId = roleDto.roleId; + this.title = roleDto.title; + this.code = roleDto.code; + + } +} diff --git a/src/rbac/role/entities/role.entity.ts b/src/rbac/role/entities/role.entity.ts new file mode 100644 index 00000000..759e53b6 --- /dev/null +++ b/src/rbac/role/entities/role.entity.ts @@ -0,0 +1,29 @@ +import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToOne } from 'typeorm'; + +@Entity({ name: "Roles" }) +export class Role { + @PrimaryGeneratedColumn('uuid') + roleId: string; + + @Column({name:"name"}) + title: string; + + @Column() + code: string; + + @Column('uuid') + tenantId: string; + + @CreateDateColumn({ type: "timestamp with time zone", default: () => "CURRENT_TIMESTAMP" }) + createdAt: Date; + + @UpdateDateColumn({ type: "timestamp with time zone", default: () => "CURRENT_TIMESTAMP" }) + updatedAt: Date; + + @Column() + createdBy: string; + + @Column() + updatedBy: string; + +} diff --git a/src/rbac/role/role.controller.ts b/src/rbac/role/role.controller.ts new file mode 100644 index 00000000..66b09a06 --- /dev/null +++ b/src/rbac/role/role.controller.ts @@ -0,0 +1,128 @@ +import { + Controller, + Get, + Post, + Body, + Put, + Param, + SerializeOptions, + Req, + UsePipes, + ValidationPipe, + Res, + Headers, + Delete, + UseGuards, + UseFilters, + ParseUUIDPipe, +} from "@nestjs/common"; +import { + ApiTags, + ApiBody, + ApiOkResponse, + ApiForbiddenResponse, + ApiCreatedResponse, + ApiBasicAuth, + ApiHeader, + ApiBadRequestResponse, + ApiNotFoundResponse, +} from "@nestjs/swagger"; +import { Request } from "@nestjs/common"; +import { CreateRolesDto, RoleDto } from "./dto/role.dto"; +import { RoleSearchDto } from "./dto/role-search.dto"; +import { Response } from "express"; +import { JwtAuthGuard } from "src/common/guards/keycloak.guard"; +import { RoleAdapter } from "./roleadapter" +import { AllExceptionsFilter } from "src/common/filters/exception.filter"; +import { APIID } from 'src/common/utils/api-id.config'; +@ApiTags("rbac") +@Controller("rbac/roles") +@UseGuards(JwtAuthGuard) +export class RoleController { + constructor(private readonly roleAdapter: RoleAdapter) { } + + //Get role + @UseFilters(new AllExceptionsFilter(APIID.ROLE_GET)) + @Get("read/:id") + @ApiBasicAuth("access-token") + @ApiOkResponse({ description: "Role Detail." }) + @ApiHeader({ name: "tenantid" }) + @ApiForbiddenResponse({ description: "Forbidden" }) + @SerializeOptions({ strategy: "excludeAll", }) + public async getRole( + @Param("id", ParseUUIDPipe) roleId: string, + @Req() request: Request, + @Res() response: Response + ) { + return await this.roleAdapter.buildRbacAdapter().getRole(roleId, request, response); + } + + //Create role + @UseFilters(new AllExceptionsFilter(APIID.ROLE_CREATE)) + @Post("/create") + @UsePipes(new ValidationPipe()) + @ApiBasicAuth("access-token") + @ApiCreatedResponse({ description: "Role has been created successfully." }) + @ApiBody({ type: CreateRolesDto }) + @ApiForbiddenResponse({ description: "Forbidden" }) + @ApiHeader({ name: "tenantid" }) + public async createRole( + @Req() request: Request, + @Body() createRolesDto: CreateRolesDto, + @Res() response: Response + ) { + return await this.roleAdapter.buildRbacAdapter().createRole(request, createRolesDto, response); + } + + //Update Role + @UseFilters(new AllExceptionsFilter(APIID.ROLE_UPDATE)) + @Put("update/:id") + @ApiBasicAuth("access-token") + @ApiCreatedResponse({ description: "Role updated successfully." }) + @ApiBody({ type: RoleDto }) + @ApiForbiddenResponse({ description: "Forbidden" }) + @ApiHeader({ name: "tenantid", }) + public async updateRole( + @Param("id") roleId: string, + @Req() request: Request, + @Body() roleDto: RoleDto, + @Res() response: Response + ) { + return await this.roleAdapter.buildRbacAdapter().updateRole(roleId, request, roleDto, response) + } + + // search Role + @UseFilters(new AllExceptionsFilter(APIID.ROLE_SEARCH)) + @Post("list/roles") + @ApiBasicAuth("access-token") + @ApiCreatedResponse({ description: "Role List." }) + @ApiBody({ type: RoleSearchDto }) + @ApiForbiddenResponse({ description: "Forbidden" }) + // @UsePipes(ValidationPipe) + @SerializeOptions({ strategy: "excludeAll", }) + @ApiHeader({ name: "tenantid" }) + public async searchRole( + @Headers() headers, + @Req() request: Request, + @Body() roleSearchDto: RoleSearchDto, + @Res() response: Response + ) { + // let tenantid = headers["tenantid"]; + return await this.roleAdapter.buildRbacAdapter().searchRole(roleSearchDto, response); + } + + //delete role + @UseFilters(new AllExceptionsFilter(APIID.ROLE_DELETE)) + @Delete("delete/:roleId") + @ApiBasicAuth("access-token") + @ApiHeader({ name: "tenantid" }) + @ApiOkResponse({ description: "Role deleted successfully." }) + @ApiNotFoundResponse({ description: "Data not found" }) + @ApiBadRequestResponse({ description: "Bad request" }) + public async deleteRole( + @Param("roleId") roleId: string, + @Res() response: Response + ) { + return await this.roleAdapter.buildRbacAdapter().deleteRole(roleId, response); + } +} diff --git a/src/rbac/role/role.module.ts b/src/rbac/role/role.module.ts new file mode 100644 index 00000000..08252c59 --- /dev/null +++ b/src/rbac/role/role.module.ts @@ -0,0 +1,24 @@ +import { Module } from "@nestjs/common"; +import { RoleController } from "./role.controller"; +import { TypeOrmModule } from "@nestjs/typeorm"; +import { Role } from "./entities/role.entity"; +import { HasuraModule } from "src/adapters/hasura/hasura.module"; +import { PostgresModule } from "src/adapters/postgres/potsgres-module"; +import { PostgresRoleService } from "src/adapters/postgres/rbac/role-adapter"; +import { HasuraRoleService } from "src/adapters/hasura/rbac/role.adapter"; +import { HttpModule } from "@nestjs/axios"; +import { RoleAdapter } from "./roleadapter"; +import { UserRoleMapping } from "../assign-role/entities/assign-role.entity"; +import { RolePrivilegeMapping } from "../assign-privilege/entities/assign-privilege.entity"; + +@Module({ + imports: [ + TypeOrmModule.forFeature([Role, UserRoleMapping, RolePrivilegeMapping]), + HttpModule, + PostgresModule, + HasuraModule, + ], + controllers: [RoleController], + providers: [RoleAdapter, HasuraRoleService, PostgresRoleService], +}) +export class RoleModule {} diff --git a/src/rbac/role/roleadapter.ts b/src/rbac/role/roleadapter.ts new file mode 100644 index 00000000..6275db0a --- /dev/null +++ b/src/rbac/role/roleadapter.ts @@ -0,0 +1,22 @@ +import { Injectable } from "@nestjs/common"; +import { IServicelocatorRbac } from "../../adapters/rbacservicelocator"; +import { HasuraRoleService } from "../../adapters/hasura/rbac/role.adapter"; +import { PostgresRoleService } from "../../adapters/postgres/rbac/role-adapter"; + +@Injectable() +export class RoleAdapter { + constructor(private hasuraProvider: HasuraRoleService, + private postgresProvider:PostgresRoleService) {} + buildRbacAdapter(): IServicelocatorRbac { + let adapter: IServicelocatorRbac; + + switch (process.env.ADAPTERSOURCE) { + case "hasura": + adapter = this.hasuraProvider; + break; + case "postgres": + adapter = this.postgresProvider; + } + return adapter; + } +} diff --git a/src/role/dto/role.dto.ts b/src/role/dto/role.dto.ts deleted file mode 100644 index 1b5e7338..00000000 --- a/src/role/dto/role.dto.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Expose } from "class-transformer"; -import { ApiProperty } from "@nestjs/swagger"; - -export class RoleDto { - @Expose() - id: string; - - @Expose() - roleId: string; - - @ApiProperty({}) - @Expose() - title: string; - - @ApiProperty({}) - @Expose() - parentId: string; - - @ApiProperty({}) - @Expose() - status: string; - - @Expose() - createdAt: string; - - @Expose() - updatedAt: string; - - @Expose() - createdBy: string; - - @Expose() - updatedBy: string; - - constructor(obj: any) { - Object.assign(this, obj); - } -} diff --git a/src/role/role.controller.ts b/src/role/role.controller.ts deleted file mode 100644 index c6832b8e..00000000 --- a/src/role/role.controller.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { - Controller, - Get, - Post, - Body, - Put, - Param, - UseInterceptors, - ClassSerializerInterceptor, - SerializeOptions, - Req, - CacheInterceptor, - Query, -} from "@nestjs/common"; -import { - ApiTags, - ApiBody, - ApiOkResponse, - ApiForbiddenResponse, - ApiCreatedResponse, - ApiBasicAuth, - ApiQuery, -} from "@nestjs/swagger"; -import { Request } from "@nestjs/common"; -import { RoleDto } from "./dto/role.dto"; -import { RoleService } from "src/adapters/hasura/role.adapter"; - -@ApiTags("Role") -@Controller("role") -export class RoleController { - constructor(private readonly service: RoleService) {} - - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "role detail." }) - @SerializeOptions({ - strategy: "excludeAll", - }) - getRole(@Param("id") roleId: string, @Req() request: Request) { - return this.service.getRole(roleId, request); - } - - @Post() - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Role has been created successfully.", - }) - @ApiBody({ type: RoleDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async creatRole(@Req() request: Request, @Body() roleDto: RoleDto) { - return this.service.createRole(request, roleDto); - } - - @Put("/:id") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Role has been updated successfully.", - }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async updateRole( - @Param("id") roleId: string, - @Req() request: Request, - @Body() roleDto: RoleDto - ) { - return await this.service.updateRole(roleId, request, roleDto); - } - - @Post("/search") - @UseInterceptors(ClassSerializerInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: " Ok." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @ApiQuery({ name: "limit", required: false }) - @ApiQuery({ name: "roleId", required: false }) - @ApiQuery({ name: "title", required: false }) - @ApiQuery({ name: "parentId", required: false }) - @ApiQuery({ name: "status", required: false }) - public async searchRole( - @Query("limit") limit: string, - @Query("roleId") roleId: string, - @Query("title") title: string, - @Query("parentId") parentId: string, - @Query("status") status: string, - @Req() request: Request - ) { - return this.service.searchRole( - limit, - roleId, - title, - parentId, - status, - request - ); - } -} diff --git a/src/role/role.module.ts b/src/role/role.module.ts deleted file mode 100644 index 566a68db..00000000 --- a/src/role/role.module.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { HttpModule } from "@nestjs/axios"; -import { CacheModule, Module } from "@nestjs/common"; -import { RoleService } from "src/adapters/hasura/role.adapter"; -import { RoleController } from "./role.controller"; -const ttl = process.env.TTL as never; -@Module({ - imports: [ - HttpModule, - CacheModule.register({ - ttl: ttl, - }), - ], - controllers: [RoleController], - providers: [RoleService], -}) -export class RoleModule {} diff --git a/src/school/dto/school-response.dto.ts b/src/school/dto/school-response.dto.ts deleted file mode 100644 index 8435e097..00000000 --- a/src/school/dto/school-response.dto.ts +++ /dev/null @@ -1,15 +0,0 @@ -export interface SchoolResponseDto { - schoolId: string; - schoolName: string; - email: string; - schoolRefId: string; - instituteManagement: string; - address: string; - schoolType: string; - website: string; - street: string; - city: string; - district: string; - state: string; - pincode: string; -} diff --git a/src/school/dto/school-search.dto.ts b/src/school/dto/school-search.dto.ts deleted file mode 100644 index b7f1cd9c..00000000 --- a/src/school/dto/school-search.dto.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; - -export class SchoolSearchDto { - @ApiProperty({ - type: String, - description: "Limit", - }) - limit: string; - - @ApiProperty({ - type: Number, - description: "Page", - }) - page: number; - - @ApiProperty({ - type: Object, - description: "Filters", - }) - @ApiPropertyOptional() - filters: object; - - constructor(partial: Partial) { - Object.assign(this, partial); - } -} diff --git a/src/school/dto/school.dto.ts b/src/school/dto/school.dto.ts deleted file mode 100644 index 03c278f9..00000000 --- a/src/school/dto/school.dto.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { Exclude, Expose } from "class-transformer"; -import { - MaxLength, - IsNotEmpty, - IsEmail, - IsString, - IsNumber, -} from "class-validator"; -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; - -export class SchoolDto { - @Expose() - id: string; - - @Expose() - schoolId: string; - - @ApiProperty({ - type: String, - description: "The schoolName of the school", - }) - @Expose() - schoolName: string; - - @ApiProperty({ - type: String, - description: "The email of the school", - }) - @IsEmail() - @Expose() - email: string; - - @ApiProperty({ - type: String, - description: "The udise of the school", - }) - @Expose() - udise: string; - - @ApiProperty({ - type: [String], - description: "The medium of instruction of the school", - }) - @Expose() - mediumOfInstruction: [string]; - - @ApiProperty({ - type: Number, - description: "The phone number of the school", - }) - @IsNumber() - @Expose() - phoneNumber: Number; - - @ApiProperty({ - type: String, - description: "The address of the school", - }) - @Expose() - address: string; - - @ApiProperty({ - type: String, - description: "The schoolType of the school", - }) - @Expose() - schoolType: string; - - @ApiProperty({ - type: String, - description: "The website of the school", - }) - @Expose() - website: string; - - @ApiProperty({ - type: String, - description: "The Head master of the school", - }) - @Expose() - headMaster: string; - - @ApiProperty({ - type: String, - description: "The Board of the school", - }) - @Expose() - board: string; - - @ApiProperty({ - type: String, - description: "The village of the school", - }) - @Expose() - village: string; - - @ApiProperty({ - type: String, - description: "The block of the school", - }) - @Expose() - block: string; - - @ApiProperty({ - type: String, - description: "The district of the school", - }) - @Expose() - district: string; - - @ApiProperty({ - type: String, - description: "The stateId of the school", - }) - @Expose() - stateId: string; - - @ApiProperty({ - type: Number, - description: "The pincode of the school", - }) - @Expose() - pincode: Number; - - @ApiProperty({ - type: String, - description: "The cluster of the school", - }) - @Expose() - cluster: string; - - @ApiProperty({ - type: String, - description: "The locationId of the school", - }) - @Expose() - locationId: string; - - @ApiProperty({ - type: String, - description: "The enrollCount of the school", - }) - @Expose() - enrollCount: string; - - @ApiProperty({ - type: String, - description: "The status of the school", - }) - @Expose() - status: string; - - @ApiProperty({ - type: Number, - description: "The latitude of the school", - }) - @Expose() - latitude: Number; - - @ApiProperty({ - type: Number, - description: "The longitude of the school", - }) - @Expose() - longitude: Number; - - @ApiPropertyOptional() - @Expose() - metaData: [string]; - - @ApiPropertyOptional({}) - @Expose() - deactivationReason: string; - - @Expose() - createdAt: string; - - @Expose() - updatedAt: string; - - constructor(obj: any) { - Object.assign(this, obj); - } -} diff --git a/src/school/interfaces/school.interface.ts b/src/school/interfaces/school.interface.ts deleted file mode 100644 index aea9e0a0..00000000 --- a/src/school/interfaces/school.interface.ts +++ /dev/null @@ -1,15 +0,0 @@ -export interface SchoolInterface { - schoolId?: string; - schoolName?: string; - email?: string; - schoolRefId?: string; - instituteManagement?: string; - address?: string; - schoolType?: string; - website?: string; - street?: string; - city?: string; - district?: string; - state?: string; - pincode?: string; -} diff --git a/src/school/school.controller.spec.ts b/src/school/school.controller.spec.ts deleted file mode 100644 index 6c749dfb..00000000 --- a/src/school/school.controller.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from "@nestjs/testing"; -import { SchoolController } from "./school.controller"; - -describe("SchoolController", () => { - let controller: SchoolController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [SchoolController], - }).compile(); - - controller = module.get(SchoolController); - }); - - it("should be defined", () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/src/school/school.controller.ts b/src/school/school.controller.ts deleted file mode 100644 index e78633db..00000000 --- a/src/school/school.controller.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { - Controller, - Get, - Post, - Put, - Param, - Body, - UseInterceptors, - ClassSerializerInterceptor, - SerializeOptions, - Req, - Request, - CacheInterceptor, -} from "@nestjs/common"; -import { SchoolDto } from "./dto/school.dto"; -import { - ApiTags, - ApiBody, - ApiOkResponse, - ApiForbiddenResponse, - ApiCreatedResponse, - ApiBasicAuth, -} from "@nestjs/swagger"; -import { SchoolSearchDto } from "./dto/school-search.dto"; -import { SchoolAdapter } from "./schooladapter"; -@ApiTags("School") -@Controller("school") -export class SchoolController { - constructor(private schoolAdapter: SchoolAdapter) {} - - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "School detail." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async getSchool(@Param("id") id: string, @Req() request: Request) { - return this.schoolAdapter.buildSchoolAdapter().getSchool(id, request); - } - - @Post() - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "School has been created successfully." }) - @ApiBody({ type: SchoolDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async createSchool( - @Req() request: Request, - @Body() schoolDto: SchoolDto - ) { - return this.schoolAdapter - .buildSchoolAdapter() - .createSchool(request, schoolDto); - } - - @Put("/:id") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "School has been updated successfully." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async updateSchool( - @Param("id") id: string, - @Req() request: Request, - @Body() schoolDto: SchoolDto - ) { - return this.schoolAdapter - .buildSchoolAdapter() - .updateSchool(id, request, schoolDto); - } - @Post("/search") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "School list." }) - @ApiBody({ type: SchoolSearchDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async searchSchool( - @Req() request: Request, - @Body() schoolSearchDto: SchoolSearchDto - ) { - return this.schoolAdapter - .buildSchoolAdapter() - .searchSchool(request, schoolSearchDto); - } -} diff --git a/src/school/school.module.ts b/src/school/school.module.ts deleted file mode 100644 index 0a8da4c8..00000000 --- a/src/school/school.module.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { CacheModule, Module } from "@nestjs/common"; -import { SchoolController } from "./school.controller"; -import { HttpModule } from "@nestjs/axios"; -import { SchoolAdapter } from "./schooladapter"; -import { HasuraModule } from "src/adapters/hasura/hasura.module"; -import { SunbirdModule } from "src/adapters/sunbirdrc/subnbird.module"; -const ttl = process.env.TTL as never; -@Module({ - imports: [ - HttpModule, - SunbirdModule, - HasuraModule, - CacheModule.register({ - ttl: ttl, - }), - ], - controllers: [SchoolController], - providers: [SchoolAdapter], -}) -export class SchoolModule {} diff --git a/src/school/schooladapter.ts b/src/school/schooladapter.ts deleted file mode 100644 index 24e1cdcb..00000000 --- a/src/school/schooladapter.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { SchoolHasuraService } from "src/adapters/hasura/school.adapter"; -import { IServicelocator } from "src/adapters/schoolservicelocator"; -import { SchoolService } from "src/adapters/sunbirdrc/school.adapter"; - -@Injectable() -export class SchoolAdapter { - constructor( - private sunbirdProvider: SchoolService, - private hasuraProvider: SchoolHasuraService - ) {} - buildSchoolAdapter(): IServicelocator { - let adapter: IServicelocator; - - switch (process.env.ADAPTERSOURCE) { - case "sunbird": - adapter = this.sunbirdProvider; - break; - case "hasura": - adapter = this.hasuraProvider; - break; - } - return adapter; - } -} diff --git a/src/student/dto/student-detail.dto.ts b/src/student/dto/student-detail.dto.ts deleted file mode 100644 index 36c63184..00000000 --- a/src/student/dto/student-detail.dto.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; -import { Exclude, Expose } from "class-transformer"; - -export class StudentDetailDto { - @Expose() - studentId: string; - - @Expose() - refId: string; - - @Expose() - aadhaar: string; - - @Expose() - firstName: string; - - @Expose() - lastName: string; - - @Expose() - schoolId: string; - - @Expose() - currentClassId: string; - - @Expose() - gender: string; - - @Expose() - socialCategory: string; - - @Expose() - iscwsn: string; - - @Expose() - religion: string; - - @Expose() - singleGirl: string; - - @Expose() - weight: string; - - @Expose() - height: string; - - @Expose() - bloodGroup: string; - - @Expose() - birthDate: string; - - @Expose() - homeless: string; - - @Expose() - bpl: string; - - @Expose() - migrant: string; - - @Expose() - status: string; - - @Expose() - email: string; - - @Expose() - fullName: string; - - @Expose() - fatherName: string; - - @Expose() - phoneNumber: string; - - @Expose() - admissionNo: string; - - @Expose() - address: string; - - @Expose() - createdAt: string; - - @Expose() - updatedAt: string; - - @Expose() - createdBy: string; - - @Expose() - updatedBy: string; - - constructor(obj: object = {}) { - Object.keys(obj).forEach((key) => (obj[key] === "" ? delete obj[key] : {})); - Object.assign(this, obj); - } -} diff --git a/src/student/dto/student-search.dto.ts b/src/student/dto/student-search.dto.ts deleted file mode 100644 index baaf8675..00000000 --- a/src/student/dto/student-search.dto.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Exclude, Expose } from "class-transformer"; -import { - MaxLength, - IsNotEmpty, - IsEmail, - IsString, - IsNumber, -} from "class-validator"; -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; - -export class StudentSearchDto { - @ApiProperty({ - type: String, - description: "Limit", - }) - limit: string; - - @ApiProperty({ - type: Object, - description: "Filters", - }) - @ApiPropertyOptional() - filters: object; - - constructor(partial: Partial) { - Object.assign(this, partial); - } -} diff --git a/src/student/dto/student.dto.ts b/src/student/dto/student.dto.ts deleted file mode 100644 index 3fb5f953..00000000 --- a/src/student/dto/student.dto.ts +++ /dev/null @@ -1,218 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; -import { Exclude, Expose } from "class-transformer"; - -export class StudentDto { - @Expose() - studentId: string; - - @ApiProperty() - @Expose() - refId1: string; - - @ApiProperty() - @Expose() - refId2: string; - - @ApiPropertyOptional() - @Expose() - aadhaar: string; - - @ApiProperty() - @Expose() - firstName: string; - - @ApiProperty() - @Expose() - middleName: string; - - @ApiProperty() - @Expose() - lastName: string; - - @ApiProperty() - @Expose() - schoolId: string; - - @ApiProperty() - @Expose() - studentPhoneNumber: Number; - - @ApiPropertyOptional() - @Expose() - studentEmail: string; - - @ApiProperty() - @Expose() - gender: string; - - @ApiProperty() - @Expose() - groupId: string; - - @ApiPropertyOptional() - @Expose() - socialCategory: string; - - @ApiPropertyOptional() - @Expose() - iscwsn: string; - - @ApiPropertyOptional() - @Expose() - religion: string; - - @ApiPropertyOptional() - @Expose() - singleGirl: Boolean; - - @ApiPropertyOptional() - @Expose() - weight: string; - - @ApiPropertyOptional() - @Expose() - height: string; - - @ApiPropertyOptional() - @Expose() - bloodGroup: string; - - @ApiProperty() - @Expose() - birthDate: string; - - @ApiPropertyOptional() - @Expose() - homeless: Boolean; - - @ApiProperty() - @Expose() - bpl: Boolean; - - @ApiProperty() - @Expose() - migrant: Boolean; - - @ApiProperty() - @Expose() - status: string; - - @ApiPropertyOptional() - @Expose() - fatherFirstName: string; - - @ApiPropertyOptional() - @Expose() - fatherMiddleName: string; - - @ApiPropertyOptional() - @Expose() - fatherLastName: string; - - @ApiPropertyOptional() - @Expose() - fatherPhoneNumber: Number; - - @ApiPropertyOptional() - @Expose() - fatherEmail: string; - - @ApiPropertyOptional() - @Expose() - motherFirstName: string; - - @ApiPropertyOptional() - @Expose() - motherMiddleName: string; - - @ApiPropertyOptional() - @Expose() - motherLastName: string; - - @ApiPropertyOptional() - @Expose() - motherPhoneNumber: Number; - - @ApiPropertyOptional() - @Expose() - motherEmail: string; - - @ApiPropertyOptional() - @Expose() - guardianFirstName: string; - - @ApiPropertyOptional() - @Expose() - guardianMiddleName: string; - - @ApiPropertyOptional() - @Expose() - guardianLastName: string; - - @ApiPropertyOptional() - @Expose() - guardianPhoneNumber: Number; - - @ApiPropertyOptional() - @Expose() - guardianEmail: string; - - @ApiPropertyOptional({ - type: "string", - format: "binary", - }) - @Expose() - image: string; - - @ApiPropertyOptional() - @Expose() - studentAddress: string; - - @ApiProperty() - @Expose() - village: string; - - @ApiProperty() - @Expose() - block: string; - - @ApiProperty() - @Expose() - district: string; - - @ApiProperty() - @Expose() - stateId: string; - - @ApiProperty() - @Expose() - pincode: Number; - - @ApiProperty() - @Expose() - locationId: string; - - @ApiPropertyOptional() - @Expose() - deactivationReason: string; - - @ApiPropertyOptional() - @Expose() - metaData: [string]; - - @Expose() - createdAt: string; - - @Expose() - updatedAt: string; - - @Expose() - createdBy: string; - - @Expose() - updatedBy: string; - - constructor(obj: any) { - Object.assign(this, obj); - } -} diff --git a/src/student/interfaces/student.interface.ts b/src/student/interfaces/student.interface.ts deleted file mode 100644 index db0d9903..00000000 --- a/src/student/interfaces/student.interface.ts +++ /dev/null @@ -1,23 +0,0 @@ -export interface StudentInterface { - studentId?: string; - refId?: string; - aadhaar?: string; - firstName?: string; - lastName?: string; - schoolId?: string; - currentClassId?: string; - gender?: string; - socialCategory?: string; - iscwsn?: string; - religion?: string; - singleGirl?: string; - weight?: string; - height?: string; - bloodGroup?: string; - birthDate?: string; - homeless?: string; - bpl?: string; - migrant?: string; - status?: string; - email?: string; -} diff --git a/src/student/student.controller.spec.ts b/src/student/student.controller.spec.ts deleted file mode 100644 index 52ad318f..00000000 --- a/src/student/student.controller.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from "@nestjs/testing"; -import { StudentController } from "./student.controller"; - -describe("StudentController", () => { - let controller: StudentController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [StudentController], - }).compile(); - - controller = module.get(StudentController); - }); - - it("should be defined", () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/src/student/student.controller.ts b/src/student/student.controller.ts deleted file mode 100644 index 25deb27f..00000000 --- a/src/student/student.controller.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { StudentInterface } from "./interfaces/student.interface"; -import { - StudentService, - SunbirdStudentToken, -} from "../adapters/sunbirdrc/student.adapter"; - -import { - CacheInterceptor, - CACHE_MANAGER, - Inject, - Request, -} from "@nestjs/common"; -import { - ApiTags, - ApiBody, - ApiOkResponse, - ApiForbiddenResponse, - ApiCreatedResponse, - ApiBasicAuth, -} from "@nestjs/swagger"; -import { - Controller, - Get, - Post, - Body, - Put, - Patch, - Param, - UseInterceptors, - ClassSerializerInterceptor, - SerializeOptions, - Req, -} from "@nestjs/common"; -import { StudentDto } from "./dto/student.dto"; -import { StudentSearchDto } from "./dto/student-search.dto"; -import { IServicelocator } from "src/adapters/studentservicelocator"; -import { StudentAdapter } from "./studentadapter"; -@ApiTags("Student") -@Controller("student") -export class StudentController { - constructor( - private service: StudentService, - @Inject(CACHE_MANAGER) private cacheManager, - - private studentAdapter: StudentAdapter - ) {} - - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "Student detail." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @SerializeOptions({ - strategy: "excludeAll", - }) - getStudent(@Param("id") studentId: string, @Req() request: Request) { - return this.studentAdapter - .buildStudentAdapter() - .getStudent(studentId, request); - } - - @Post() - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Student has been created successfully." }) - @ApiBody({ type: StudentDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async createStudent( - @Req() request: Request, - @Body() studentDto: StudentDto - ) { - return this.studentAdapter - .buildStudentAdapter() - .createStudent(request, studentDto); - } - - @Put("/:id") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Student has been updated successfully." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async updateStudent( - @Param("id") id: string, - @Req() request: Request, - @Body() studentDto: StudentDto - ) { - return await this.studentAdapter - .buildStudentAdapter() - .updateStudent(id, request, studentDto); - } - - @Post("/search") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Student list." }) - @ApiBody({ type: StudentSearchDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async searchStudent( - @Req() request: Request, - @Body() studentSearchDto: StudentSearchDto - ) { - return this.studentAdapter - .buildStudentAdapter() - .searchStudent(request, studentSearchDto); - } -} diff --git a/src/student/student.module.ts b/src/student/student.module.ts deleted file mode 100644 index cd148368..00000000 --- a/src/student/student.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { CacheModule, Module } from "@nestjs/common"; -import { StudentController } from "./student.controller"; -import { HttpModule } from "@nestjs/axios"; -import { StudentAdapter } from "./studentadapter"; -import { SunbirdModule } from "src/adapters/sunbirdrc/subnbird.module"; -const ttl = process.env.TTL as never; -@Module({ - imports: [ - HttpModule, - SunbirdModule, - CacheModule.register({ - ttl: ttl, - }), - ], - controllers: [StudentController], - providers: [StudentAdapter], -}) -export class StudentModule {} diff --git a/src/student/studentadapter.ts b/src/student/studentadapter.ts deleted file mode 100644 index b6879936..00000000 --- a/src/student/studentadapter.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { IServicelocator } from "src/adapters/studentservicelocator"; -import { StudentService } from "src/adapters/sunbirdrc/student.adapter"; - -@Injectable() -export class StudentAdapter { - constructor( - private sunbirdProvider: StudentService - ) {} - buildStudentAdapter(): IServicelocator { - let adapter: IServicelocator; - - switch (process.env.ADAPTERSOURCE) { - case "sunbird": - adapter = this.sunbirdProvider; - break; - } - return adapter; - } -} diff --git a/src/template/dto/template-create.dto.ts b/src/template/dto/template-create.dto.ts deleted file mode 100644 index eabc620d..00000000 --- a/src/template/dto/template-create.dto.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { ApiProperty } from "@nestjs/swagger"; -import { Expose } from "class-transformer"; - -export class TemplateCreateDto { - @ApiProperty({ - description: "Body of worksheet Template", - }) - @Expose() - body: string; - - @ApiProperty({ - description: "Type of the template", - }) - @Expose() - type: string; - - @ApiProperty({ - description: "tags for the template", - }) - @Expose() - tag: []; - - @ApiProperty({ - description: "User id", - }) - @Expose() - user: string; - - @Expose() - createdAt: string; - - @Expose() - updatedAt: string; - constructor(obj: any) { - Object.assign(this, obj); - } -} diff --git a/src/template/dto/template-process.dto.ts b/src/template/dto/template-process.dto.ts deleted file mode 100644 index 13298eb5..00000000 --- a/src/template/dto/template-process.dto.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; - -export class TemplateProcessDto { - @ApiProperty({ - description: "Id of created template", - }) - id: number; - - @ApiProperty({ - description: "Data to update", - }) - @ApiPropertyOptional() - data: object; - - constructor(obj: any) { - Object.assign(this, obj); - } -} diff --git a/src/template/dto/template-search.dto.ts b/src/template/dto/template-search.dto.ts deleted file mode 100644 index 0f5eaa08..00000000 --- a/src/template/dto/template-search.dto.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; - -export class TemplateSearchDto { - @ApiProperty({ - type: String, - description: "Limit", - }) - limit: string; - - @ApiProperty({ - type: Object, - description: "Filters", - }) - @ApiPropertyOptional() - filters: object; - - constructor(partial: Partial) { - Object.assign(this, partial); - } -} diff --git a/src/template/template.controller.ts b/src/template/template.controller.ts deleted file mode 100644 index 3903c30b..00000000 --- a/src/template/template.controller.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { - ApiBasicAuth, - ApiBody, - ApiQuery, - ApiCreatedResponse, - ApiForbiddenResponse, - ApiOkResponse, - ApiTags, -} from "@nestjs/swagger"; -import { - Body, - CacheInterceptor, - ClassSerializerInterceptor, - Controller, - Get, - Query, - Param, - Post, - Req, - SerializeOptions, - UseInterceptors, - Request, -} from "@nestjs/common"; -import { TemplateService } from "src/adapters/sunbirdrc/template.adapter"; -import { TemplateProcessDto } from "./dto/template-process.dto"; -import { TemplateCreateDto } from "./dto/template-create.dto"; - -@ApiTags("Template") -@Controller("template") -export class TemplateController { - constructor(private service: TemplateService) {} - - @Post("create") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: " Template has been created successfully.", - }) - @ApiBody({ type: TemplateCreateDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async createTemplate( - @Req() request: Request, - @Body() TemplateCreateDto: TemplateCreateDto - ) { - return this.service.createTemplate(request, TemplateCreateDto); - } - - @Post("process") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: " template process." }) - @ApiBody({ type: TemplateProcessDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async processTemplate( - @Req() request: Request, - @Body() TemplateProcessDto: TemplateProcessDto - ) { - return await this.service.processTemplate(request, TemplateProcessDto); - } - - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: " template detail." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async getTemplate(@Param("id") id: string, @Req() request: Request) { - return this.service.getTemplate(id, request); - } - @Get(":/searchByTag") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - //@ApiBasicAuth("access-token") - @ApiOkResponse({ description: "Get all Questions detail." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @ApiQuery({ name: "tag", required: true }) - public async getTemplates( - @Query("tag") tag: string, - @Req() request: Request - ) { - return this.service.getTemplateByTag(tag, request); - } -} diff --git a/src/template/template.module.ts b/src/template/template.module.ts deleted file mode 100644 index f248e32d..00000000 --- a/src/template/template.module.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { CacheModule, Module } from "@nestjs/common"; -import { HttpModule } from "@nestjs/axios"; -import { TemplateService } from "src/adapters/sunbirdrc/template.adapter"; -import { TemplateController } from "./template.controller"; -const ttl = process.env.TTL as never; -@Module({ - imports: [ - HttpModule, - CacheModule.register({ - ttl: ttl, - }), - ], - controllers: [TemplateController], - providers: [TemplateService], -}) -export class TemplateModule {} diff --git a/src/trackAssessment/dto/trackassessment.dto.ts b/src/trackAssessment/dto/trackassessment.dto.ts deleted file mode 100644 index add9eb94..00000000 --- a/src/trackAssessment/dto/trackassessment.dto.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { Expose } from "class-transformer"; -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; -import { IsIn, IsNotEmpty, IsString } from "class-validator"; -import { Status } from "../enums/statuses.enum"; - -export class TrackAssessmentDto { - @Expose() - id: string; - - @Expose() - trackAssessmentId: string; - - @ApiProperty({ - description: "JSON string of filters selected for the assessment ", - }) - @Expose() - filter: string; - - @ApiProperty({ - description: "Assessment type, spot assessment or exam", - }) - @Expose() - type: string; - - @ApiPropertyOptional({ - description: "Array of question Id's against the assessment is given", - }) - @Expose() - questions: [string]; - - @ApiPropertyOptional({ - description: "Assessment questions source", - }) - @Expose() - source: string; - - @ApiProperty({ - description: - "JSON encoded QUML player response against the given questions of the assessment", - }) - @Expose() - answersheet: string; - - @Expose() - score: string; - - @ApiProperty({ - description: "student Id who has given assessment", - }) - @Expose() - studentId: string; - - @ApiProperty({ - description: "Teacher Id who has assigned the assessment", - }) - @Expose() - teacherId: string; - - @ApiProperty({ - description: "GroupId of teacher", - }) - @Expose() - groupId: string; - - @ApiProperty({ - description: "subject", - }) - @Expose() - subject: string; - - @Expose() - totalScore: string; - - @Expose() - date: string; - - @IsString() - @IsIn([Status.NONE, Status.COMPLETED, Status.ABSENT]) - @ApiProperty({ - description: - "Assessment Status - whether student was absent or he has completed the assessment.", - enum: [Status.COMPLETED, Status.ABSENT], - required: true, - }) - @Expose() - status: string; - - @Expose() - createdAt: string; - - @Expose() - updatedAt: string; - - constructor(obj: any) { - Object.assign(this, obj); - } -} diff --git a/src/trackAssessment/enums/statuses.enum.ts b/src/trackAssessment/enums/statuses.enum.ts deleted file mode 100644 index fe0033f8..00000000 --- a/src/trackAssessment/enums/statuses.enum.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum Status { - NONE = "", // can be empty to maintain backward compatibility - COMPLETED = "COMPLETED", - ABSENT = "ABSENT", -} diff --git a/src/trackAssessment/trackassessment.controller.ts b/src/trackAssessment/trackassessment.controller.ts deleted file mode 100644 index c5e3df3d..00000000 --- a/src/trackAssessment/trackassessment.controller.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { - ApiTags, - ApiForbiddenResponse, - ApiCreatedResponse, - ApiBasicAuth, - ApiBody, - ApiQuery, - ApiOkResponse, -} from "@nestjs/swagger"; -import { - Controller, - Get, - Param, - UseInterceptors, - ClassSerializerInterceptor, - SerializeOptions, - Req, - Request, - CacheInterceptor, - Post, - Body, - Query, - UsePipes, - ValidationPipe, -} from "@nestjs/common"; -import { TrackAssessmentService } from "src/adapters/hasura/trackassessment.adapter"; -import { TrackAssessmentDto } from "./dto/trackassessment.dto"; -import { isUUID } from "class-validator"; - -@ApiTags("Track Assessment") -@Controller("trackassessment") -export class AssessmentController { - constructor(private service: TrackAssessmentService) {} - - @Post() - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Track Assessment has been created successfully.", - }) - @ApiBody({ type: TrackAssessmentDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @UsePipes(new ValidationPipe({})) - public async createAssessment( - @Req() request: Request, - @Body() assessmentDto: TrackAssessmentDto - ) { - return this.service.createAssessment(request, assessmentDto); - } - - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Track Assessment detail" }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async getAssessment( - @Param("id") assessmentId: string, - @Req() request: Request - ) { - return this.service.getAssessment(assessmentId, request); - } - - @Post("/search") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Track Assessment list." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @SerializeOptions({ - strategy: "excludeAll", - }) - @ApiQuery({ name: "fromDate", required: false }) - @ApiQuery({ name: "toDate", required: false }) - @ApiQuery({ name: "limit", required: false }) - @ApiQuery({ name: "source", required: false }) - @ApiQuery({ name: "studentId", required: false }) - @ApiQuery({ name: "teacherId", required: false }) - @ApiQuery({ name: "groupId", required: false }) - @ApiQuery({ name: "subject", required: false }) - @ApiQuery({ name: "page", required: false }) - public async searchAssessment( - @Query("fromDate") date: string, - @Query("toDate") toDate: string, - @Query("limit") limit: string, - @Query("source") source: string, - @Query("studentId") studentId: string, - @Query("teacherId") teacherId: string, - @Query("groupId") groupId: string, - @Query("subject") subject: string, - @Query("page") page: number, - @Req() request: Request - ) { - return await this.service.searchAssessment( - date, - toDate, - limit, - source, - studentId, - teacherId, - groupId, - subject, - page, - request - ); - } -} diff --git a/src/trackAssessment/trackassessment.module.ts b/src/trackAssessment/trackassessment.module.ts deleted file mode 100644 index e24671f9..00000000 --- a/src/trackAssessment/trackassessment.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { CacheModule, Module } from "@nestjs/common"; -import { HttpModule } from "@nestjs/axios"; -import { ScheduleModule } from "@nestjs/schedule"; -import { TrackAssessmentService } from "src/adapters/hasura/trackassessment.adapter"; -import { AssessmentController } from "./trackassessment.controller"; -const ttl = process.env.TTL as never; -@Module({ - imports: [ - HttpModule, - CacheModule.register({ - ttl: ttl, - }), - ScheduleModule.forRoot(), - ], - controllers: [AssessmentController], - providers: [TrackAssessmentService], -}) -export class TrackAssessmentModule {} diff --git a/src/user/dto/user-create.dto.ts b/src/user/dto/user-create.dto.ts index 225de48a..aa6ffac0 100644 --- a/src/user/dto/user-create.dto.ts +++ b/src/user/dto/user-create.dto.ts @@ -1,40 +1,76 @@ -import { Exclude, Expose } from "class-transformer"; +import { Expose, Type } from "class-transformer"; import { MaxLength, IsNotEmpty, IsEmail, IsString, IsNumber, + IsArray, + IsUUID, + ValidateNested, + IsOptional, } from "class-validator"; +import { User } from "../entities/user-entity"; import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; -export class UserCreateDto { +export class tenantRoleMappingDto { + @ApiProperty({ + type: String, + description: "Tenant Id", + }) @Expose() + @IsUUID(undefined, { message: 'Tenant Id must be a valid UUID' }) + @IsNotEmpty() tenantId: string; + @ApiPropertyOptional({ + type: String, + description: "The cohort id of the user", + }) @Expose() - userId: string; + @IsUUID(undefined, { message: 'Cohort Id must be a valid UUID' }) + @IsNotEmpty() + cohortId: string; - @ApiProperty({ + @ApiPropertyOptional({ type: String, - description: "The username of the user", + description: "User Role", }) + @IsOptional() @Expose() - username: string; + @IsUUID(undefined, { message: 'Role Id must be a valid UUID' }) + roleId: string; +} - @ApiProperty({ +export class FieldValuesDto { + @ApiPropertyOptional({ type: String, - description: "The name of the user", + description: "Field Id", }) @Expose() - name: string; + @IsUUID(undefined, { message: 'Field Id must be a valid UUID' }) + fieldId: string; - @ApiProperty({ + @ApiPropertyOptional({ type: String, - description: "The role of the user", + description: "Field values", }) @Expose() - role: string; + value: string; +} + +export class UserCreateDto { + @Expose() + userId: string; + + @ApiProperty({ type: () => User }) + @Expose() + @IsNotEmpty() + username: string; + + @ApiProperty({ type: () => String }) + @Expose() + name: string; @ApiPropertyOptional({ type: String, @@ -55,13 +91,13 @@ export class UserCreateDto { description: "The email of the user", }) @Expose() - @IsEmail() email: string; @ApiProperty({ type: String, description: "The password of the user", }) + @IsNotEmpty() @Expose() password: string; @@ -106,14 +142,26 @@ export class UserCreateDto { updatedBy: string; //fieldValues - @ApiProperty({ - type: String, + @ApiPropertyOptional({ + type: [FieldValuesDto], description: "The fieldValues Object", }) - @Expose() - fieldValues: string; + @ValidateNested({ each: true }) + @Type(() => FieldValuesDto) + fieldValues: FieldValuesDto[]; + + @ApiProperty({ + type: [tenantRoleMappingDto], + description: 'List of user attendance details', + }) + @ValidateNested({ each: true }) + @Type(() => tenantRoleMappingDto) + tenantCohortRoleMapping: tenantRoleMappingDto[]; constructor(partial: Partial) { Object.assign(this, partial); } } + + + diff --git a/src/user/dto/user-search.dto.ts b/src/user/dto/user-search.dto.ts index b9247ea2..7193420a 100644 --- a/src/user/dto/user-search.dto.ts +++ b/src/user/dto/user-search.dto.ts @@ -7,12 +7,10 @@ import { IsNumber, } from "class-validator"; import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; +import { User } from "../entities/user-entity"; export class UserSearchDto { - @ApiProperty({ - type: String, - description: "Limit", - }) + @ApiProperty({ type: () => User }) limit: string; @ApiProperty({ diff --git a/src/user/dto/user-update.dto.ts b/src/user/dto/user-update.dto.ts new file mode 100644 index 00000000..17742143 --- /dev/null +++ b/src/user/dto/user-update.dto.ts @@ -0,0 +1,95 @@ +import { IsString, IsOptional, IsArray, ValidateNested, IsNotEmpty, IsEnum, ValidateIf } from 'class-validator'; +import { Expose, Type } from 'class-transformer'; + +class UserDataDTO { + + @IsString() + @IsOptional() + username: string; + + @IsString() + @IsNotEmpty() + name: string; + + @IsString() + @IsOptional() + role: string; + + @IsOptional() + @IsString() + dob: string | null; + + @IsOptional() + @IsString() + email: string | null; + + @IsOptional() + @IsString() + district: string | null; + + @IsOptional() + @IsString() + state: string | null; + + @IsOptional() + @IsString() + address: string | null; + + @IsOptional() + @IsString() + pincode: string | null; + + @IsString() + @IsOptional() + createdAt: string; + + @IsString() + @IsOptional() + updatedAt: string; + + @IsString() + @IsOptional() + createdBy: string; + + @IsString() + @IsOptional() + updatedBy: string; + + @IsString() + @IsOptional() + tenantId: string; + + @IsString() + @IsOptional() + status: string; +} +class CustomFieldDTO { + + @IsString() + @Expose() + @IsNotEmpty() + fieldId: string; + + @ValidateIf(o => o.value !== '') + @IsNotEmpty() + @Expose() + value: string | string[]; +} + +export class UserUpdateDTO { + + userId: string; + + @Expose() + @ValidateNested() + @IsNotEmpty() + @Type(() => UserDataDTO) + userData: UserDataDTO; + + @IsArray() + @IsOptional() + @ValidateNested({ each: true }) + @Type(() => CustomFieldDTO) + @Expose() + customFields: CustomFieldDTO[]; +} diff --git a/src/user/entities/field-entity.ts b/src/user/entities/field-entity.ts new file mode 100644 index 00000000..77df953a --- /dev/null +++ b/src/user/entities/field-entity.ts @@ -0,0 +1,82 @@ +import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm'; + +@Entity({ name: 'Fields', schema: 'public' }) +export class Field { + @PrimaryGeneratedColumn('uuid') + fieldId: string; + + @Column({ type: 'varchar', nullable: true }) + assetId: string; + + @Column({ type: 'varchar' }) + context: string; + + @Column({ type: 'varchar', nullable: true }) + groupId: string; + + @Column({ type: 'varchar' }) + name: string; + + @Column({ type: 'varchar' }) + label: string; + + @Column({ type: 'varchar', nullable: true }) + defaultValue: string; + + @Column({ type: 'varchar' }) + type: string; + + @Column({ type: 'text', nullable: true }) + note: string; + + @Column({ type: 'text', nullable: true }) + description: string; + + @Column({ type: 'text' }) + state: string; + + @Column({ type: 'boolean' }) + required: boolean; + + @Column({ type: 'integer' }) + ordering: number; + + @Column({ type: 'text' }) + metadata: string; + + @Column({ type: 'varchar', nullable: true }) + access: string; + + @Column({ type: 'boolean' }) + onlyUseInSubform: boolean; + + @Column({ type: 'uuid' }) + tenantId: string; + + @CreateDateColumn({ type: 'timestamp with time zone', default: () => 'CURRENT_TIMESTAMP' }) + createdAt: Date; + + @UpdateDateColumn({ type: 'timestamp with time zone', default: () => 'CURRENT_TIMESTAMP' }) + updatedAt: Date; + + @Column({ type: 'varchar', nullable: true }) + createdBy: string; + + @Column({ type: 'varchar', nullable: true }) + updatedBy: string; + + @Column({ type: 'uuid', nullable: true }) + contextId: string; + + @Column({ type: 'varchar', nullable: true }) + render: string; + + @Column({ type: 'varchar', nullable: true }) + contextType: string; + + @Column({ type: 'jsonb', nullable: true }) + fieldParams: object; + + @Column({ type: 'jsonb', nullable: true }) + fieldAttributes: object; +} diff --git a/src/user/entities/field-value-entities.ts b/src/user/entities/field-value-entities.ts new file mode 100644 index 00000000..ff8ff7eb --- /dev/null +++ b/src/user/entities/field-value-entities.ts @@ -0,0 +1,33 @@ +import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToOne, JoinColumn } from 'typeorm'; +// import { Field } from './Field'; // Assuming you have a Field entity defined + +@Entity({ name: 'FieldValues' }) +export class FieldValues { + @PrimaryGeneratedColumn('uuid', { name: 'fieldValuesId' }) + fieldValuesId: string; + + @Column({ type: 'text', nullable: false }) + value: string; + + @Column({ type: 'uuid', nullable: false}) + itemId: string; + + @Column({ type: 'uuid', nullable: false, name: 'fieldId' }) + fieldId: string; + + // @ManyToOne(() => Field, field => field.fieldValues) + // @JoinColumn({ name: 'fieldId' }) + // field: Field; + + @CreateDateColumn({ name: 'createdAt', type: 'timestamp with time zone' }) + createdAt: Date; + + @UpdateDateColumn({ name: 'updatedAt', type: 'timestamp with time zone' }) + updatedAt: Date; + + @Column({ type: 'text', nullable: true, name: 'createdBy' }) + createdBy: string; + + @Column({ type: 'text', nullable: true, name: 'updatedBy' }) + updatedBy: string; +} diff --git a/src/user/entities/user-entity.ts b/src/user/entities/user-entity.ts new file mode 100644 index 00000000..1f108b1b --- /dev/null +++ b/src/user/entities/user-entity.ts @@ -0,0 +1,59 @@ +import { Entity, PrimaryColumn, Column, CreateDateColumn, UpdateDateColumn, OneToMany } from "typeorm"; +import { UserTenantMapping } from "src/userTenantMapping/entities/user-tenant-mapping.entity"; + +@Entity({ name: "Users" }) +export class User { + @PrimaryColumn({ type: "uuid" }) + userId: string; + + @Column({ unique: true }) + username: string; + + @Column() + name: string; + + @Column({ type: "date", nullable: true }) + dob: Date; + + @Column({ nullable: true }) + email: string; + + @Column({ nullable: true }) + district: string; + + @Column({ nullable: true }) + state: string; + + @Column({ nullable: true }) + address: string; + + @Column({ nullable: true }) + pincode: string; + + @CreateDateColumn({ type: "timestamp with time zone", default: () => "CURRENT_TIMESTAMP" }) + createdAt: Date; + + @UpdateDateColumn({ type: "timestamp with time zone", default: () => "CURRENT_TIMESTAMP" }) + updatedAt: Date; + + @Column() + mobile: number; + + @Column({ nullable: true }) + createdBy: string; + + @Column({ nullable: true }) + updatedBy: string; + + @Column({ default: "active" }) + status: string; + userRoleMappings: User; + + + // @OneToMany(() => CohortMembers, cohortMember => cohortMember.cohort) + // cohortMembers: CohortMembers[]; + + @OneToMany(() => UserTenantMapping, userTenantMapping => userTenantMapping.user) + userTenantMapping: UserTenantMapping[]; + +} diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index e59f6b18..e8457ff7 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -3,23 +3,21 @@ import { Get, Post, Body, - Put, Param, - UseInterceptors, - ClassSerializerInterceptor, SerializeOptions, Req, - CacheInterceptor, - Inject, - Query, Headers, Res, + Patch, + UseGuards, + Query, + UsePipes, + ValidationPipe, + Delete, + ParseUUIDPipe, + UseFilters, } from "@nestjs/common"; -import { - SunbirdUserToken, - UserService, -} from "../adapters/sunbirdrc/user.adapter"; -import { Request, Response } from "@nestjs/common"; + import { ApiTags, ApiBody, @@ -28,88 +26,99 @@ import { ApiCreatedResponse, ApiBasicAuth, ApiQuery, - ApiConsumes, ApiHeader, + ApiNotFoundResponse, + ApiInternalServerErrorResponse, + ApiBadRequestResponse, + ApiConflictResponse, } from "@nestjs/swagger"; -import { UserDto } from "./dto/user.dto"; import { UserSearchDto } from "./dto/user-search.dto"; import { UserAdapter } from "./useradapter"; import { UserCreateDto } from "./dto/user-create.dto"; +import { UserUpdateDTO } from "./dto/user-update.dto"; +import { JwtAuthGuard } from "src/common/guards/keycloak.guard"; +import { Request, Response } from "express"; +import { AllExceptionsFilter } from "src/common/filters/exception.filter"; +import { APIID } from "src/common/utils/api-id.config"; +export interface UserData { + context: string; + tenantId: string; + userId: string; + fieldValue: boolean; +} + @ApiTags("User") -@Controller("user") +@Controller() export class UserController { constructor( - private readonly service: UserService, - private userAdapter: UserAdapter + private userAdapter: UserAdapter, ) {} - @Get("/:userid") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) + @UseFilters(new AllExceptionsFilter(APIID.USER_GET)) + @Get('read/:userId') + @UseGuards(JwtAuthGuard) @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "User detail." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @SerializeOptions({ - strategy: "excludeAll", - }) - @ApiHeader({ - name: "tenantid", - }) - @ApiQuery({ name: "accessrole" }) + @ApiOkResponse({ description: "User details Fetched Successfully" }) + @ApiNotFoundResponse({ description: "User Not Found" }) + @ApiInternalServerErrorResponse({description:"Internal Server Error" }) + @ApiBadRequestResponse({description:"Bad Request"}) + @SerializeOptions({ strategy: "excludeAll", }) + @ApiHeader({ name: "tenantid", }) + @ApiQuery({ name: 'fieldvalue', description: 'Send True to Fetch Custom Field of User', required: false }) public async getUser( @Headers() headers, - @Param("userid") userId: string, - @Query("accessrole") accessRole: string, @Req() request: Request, - @Res() response: Response + @Res() response: Response, + @Param('userId', ParseUUIDPipe) userId: string, + @Query("fieldvalue") fieldvalue: string | null = null ) { - const tenantId = headers["tenantid"]; - return this.userAdapter - .buildUserAdapter() - .getUser(tenantId, userId, accessRole, request, response); - } - - @Get() - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "User detail." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @SerializeOptions({ - strategy: "excludeAll", - }) - @ApiHeader({ - name: "tenantid", - }) - public async getUserByAuth(@Headers() headers, @Req() request: Request) { - const tenantId = headers["tenantid"]; - return this.userAdapter.buildUserAdapter().getUserByAuth(tenantId, request); + const tenantId = headers["tenantid"]; + if(!tenantId){ + return response.status(400).json({ "statusCode": 400, error: "Please provide a tenantId." }); + } + const fieldValueBoolean = fieldvalue === 'true'; + // Context and ContextType can be taken from .env later + let userData:UserData = { + context: "USERS", + tenantId: tenantId, + userId: userId, + fieldValue: fieldValueBoolean + } + let result; + result = await this.userAdapter.buildUserAdapter().getUsersDetailsById(userData, response); + + return response.status(result.statusCode).json(result); } - @Post() + @UseFilters(new AllExceptionsFilter(APIID.USER_CREATE)) + @Post("/create") + @UseGuards(JwtAuthGuard) + @UsePipes(new ValidationPipe()) @ApiBasicAuth("access-token") @ApiCreatedResponse({ description: "User has been created successfully." }) @ApiBody({ type: UserCreateDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @ApiHeader({ - name: "tenantid", - }) - public async createUser( + @ApiForbiddenResponse({ description: "User Already Exists"}) + @ApiInternalServerErrorResponse({ description: "Internal Server Error" }) + @ApiConflictResponse({ description: "Duplicate data." }) + async createUser( @Headers() headers, @Req() request: Request, - @Body() userCreateDto: UserCreateDto + @Body() userCreateDto: UserCreateDto, + @Res() response: Response ) { - userCreateDto.tenantId = headers["tenantid"]; - return this.userAdapter - .buildUserAdapter() - .checkAndAddUser(request, userCreateDto); + return await this.userAdapter.buildUserAdapter().createUser(request, userCreateDto, response); + } - @Put("/:userid") + @UseFilters(new AllExceptionsFilter(APIID.USER_UPDATE)) + @Patch("update/:userid") + @UsePipes(new ValidationPipe()) + @UseGuards(JwtAuthGuard) @ApiBasicAuth("access-token") + @ApiBody({ type: UserUpdateDTO }) @ApiCreatedResponse({ description: "User has been updated successfully." }) @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) @ApiHeader({ name: "tenantid", }) @@ -117,20 +126,20 @@ export class UserController { @Headers() headers, @Param("userid") userId: string, @Req() request: Request, - @Body() userDto: UserCreateDto + @Body() userUpdateDto: UserUpdateDTO, + @Res() response: Response ) { - userDto.tenantId = headers["tenantid"]; - return await this.userAdapter - .buildUserAdapter() - .updateUser(userId, request, userDto); + // userDto.tenantId = headers["tenantid"]; + userUpdateDto.userId = userId; + return await this.userAdapter.buildUserAdapter().updateUser(userUpdateDto,response); } - @Post("/search") + @UseFilters(new AllExceptionsFilter(APIID.USER_LIST)) + @Post("/list") + @UseGuards(JwtAuthGuard) @ApiBasicAuth("access-token") @ApiCreatedResponse({ description: "User list." }) @ApiBody({ type: UserSearchDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) @SerializeOptions({ strategy: "excludeAll", }) @@ -144,19 +153,19 @@ export class UserController { @Body() userSearchDto: UserSearchDto ) { const tenantId = headers["tenantid"]; - return await this.userAdapter - .buildUserAdapter() - .searchUser(tenantId, request, response, userSearchDto); + return await this.userAdapter.buildUserAdapter().searchUser(tenantId,request,response,userSearchDto); } + @UseFilters(new AllExceptionsFilter(APIID.USER_RESET_PASSWORD)) @Post("/reset-password") + @UseGuards(JwtAuthGuard) @ApiBasicAuth("access-token") @ApiOkResponse({ description: "Password reset successfully." }) @ApiForbiddenResponse({ description: "Forbidden" }) @ApiBody({ type: Object }) - @UseInterceptors(ClassSerializerInterceptor) public async resetUserPassword( @Req() request: Request, + @Res() response: Response, @Body() reqBody: { username: string; @@ -165,6 +174,37 @@ export class UserController { ) { return await this.userAdapter .buildUserAdapter() - .resetUserPassword(request, reqBody.username, reqBody.newPassword); + .resetUserPassword(request, reqBody.username, reqBody.newPassword, response); + } + + // required for FTL + @Post("/check") + async checkUser( + @Body() body, + @Res() response: Response + ) { + const result = await this.userAdapter.buildUserAdapter().checkUser(body,response) + return response.status(result.statusCode).json(result); } + + //delete + @UseFilters(new AllExceptionsFilter(APIID.USER_DELETE)) + @Delete("delete/:userId") + @UseGuards(JwtAuthGuard) + @ApiBasicAuth("access-token") + @ApiOkResponse({ description: "User deleted successfully" }) + @ApiNotFoundResponse({ description: "Data not found" }) + @SerializeOptions({ + strategy: "excludeAll", + }) + public async deleteUserById( + @Headers() headers, + @Param("userId") userId: string, + @Req() request: Request, + @Res() response: Response + ) { + return await this.userAdapter + .buildUserAdapter() + .deleteUserById(userId,response); + } } diff --git a/src/user/user.module.ts b/src/user/user.module.ts index 51bb2f69..8d38a697 100644 --- a/src/user/user.module.ts +++ b/src/user/user.module.ts @@ -2,17 +2,26 @@ import { CacheModule, Module } from "@nestjs/common"; import { UserController } from "./user.controller"; import { HttpModule } from "@nestjs/axios"; import { UserAdapter } from "./useradapter"; -import { SunbirdModule } from "src/adapters/sunbirdrc/subnbird.module"; import { HasuraModule } from "src/adapters/hasura/hasura.module"; -const ttl = process.env.TTL as never; +import { PostgresModule } from "src/adapters/postgres/potsgres-module"; +import { TypeOrmModule } from "@nestjs/typeorm"; +import { User } from "./entities/user-entity"; +import { FieldValues } from "./entities/field-value-entities"; +import { Field } from "./entities/field-entity"; +import { CohortMembers } from "src/cohortMembers/entities/cohort-member.entity"; +import { UserTenantMapping } from "src/userTenantMapping/entities/user-tenant-mapping.entity"; +import { Tenants } from "src/userTenantMapping/entities/tenant.entity"; +import { UserRoleMapping } from "src/rbac/assign-role/entities/assign-role.entity"; +import { Cohort } from "src/cohort/entities/cohort.entity"; +import { Role } from "src/rbac/role/entities/role.entity"; + + @Module({ imports: [ + TypeOrmModule.forFeature([User, FieldValues, Field, CohortMembers,UserTenantMapping,Tenants,UserRoleMapping,Cohort,Role]), HttpModule, - SunbirdModule, HasuraModule, - CacheModule.register({ - ttl: ttl, - }), + PostgresModule, ], controllers: [UserController], providers: [UserAdapter], diff --git a/src/user/useradapter.ts b/src/user/useradapter.ts index de7e88a8..2cc4543f 100644 --- a/src/user/useradapter.ts +++ b/src/user/useradapter.ts @@ -1,24 +1,21 @@ import { Injectable } from "@nestjs/common"; -import { UserService } from "src/adapters/sunbirdrc/user.adapter"; import { HasuraUserService } from "src/adapters/hasura/user.adapter"; import { IServicelocator } from "src/adapters/userservicelocator"; +import { PostgresUserService } from "src/adapters/postgres/user-adapter"; @Injectable() export class UserAdapter { - constructor( - private sunbirdProvider: UserService, - private hasuraProvider: HasuraUserService - ) {} + constructor(private hasuraProvider: HasuraUserService, + private postgresProvider:PostgresUserService) {} buildUserAdapter(): IServicelocator { let adapter: IServicelocator; switch (process.env.ADAPTERSOURCE) { - case "sunbird": - adapter = this.sunbirdProvider; - break; case "hasura": adapter = this.hasuraProvider; break; + case "postgres": + adapter = this.postgresProvider; } return adapter; } diff --git a/src/userTenantMapping/dto/user-tenant-mapping.dto.ts b/src/userTenantMapping/dto/user-tenant-mapping.dto.ts new file mode 100644 index 00000000..d1ee71c6 --- /dev/null +++ b/src/userTenantMapping/dto/user-tenant-mapping.dto.ts @@ -0,0 +1,50 @@ +import { Expose } from "class-transformer"; +import { ApiProperty } from "@nestjs/swagger"; +import { IsNotEmpty, IsString, IsUUID, IsArray } from "class-validator"; + +export class UserTenantMappingDto { + @ApiProperty({ + type: String, + description: "User Id of User", + default: "", + }) + @Expose() + @IsNotEmpty() + @IsUUID() + userId: string; + + + @ApiProperty({ + type: String, + description: "Tenant Id", + default: [], + }) + @Expose() + @IsArray() + @IsUUID(undefined, { each: true }) + @IsNotEmpty({ each: true }) + tenantId: string[]; + + constructor(obj: any) { + Object.assign(this, obj); + } + +} + +export class ResponseAssignTenantDto { + @Expose() + userId: string; + + @Expose() + tenantId: string; + + @Expose() + message: string; + + constructor(data: { userId: string; tenantId: string }, message: string) { + this.userId = data.userId; + this.tenantId = data.tenantId; + this.message = message; + } +} + diff --git a/src/userTenantMapping/entities/tenant.entity.ts b/src/userTenantMapping/entities/tenant.entity.ts new file mode 100644 index 00000000..d7312eb6 --- /dev/null +++ b/src/userTenantMapping/entities/tenant.entity.ts @@ -0,0 +1,32 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, + } from "typeorm"; + + @Entity({ name: "Tenants" }) + export class Tenants { + @PrimaryGeneratedColumn("uuid") + tenantId: string; + + @Column() + name: string; + + @Column() + domain: string; + + @CreateDateColumn({ + type: "timestamp with time zone", + default: () => "CURRENT_TIMESTAMP", + }) + createdAt: Date; + + @UpdateDateColumn({ + type: "timestamp with time zone", + default: () => "CURRENT_TIMESTAMP", + }) + updatedAt: Date; + } + \ No newline at end of file diff --git a/src/userTenantMapping/entities/user-tenant-mapping.entity.ts b/src/userTenantMapping/entities/user-tenant-mapping.entity.ts new file mode 100644 index 00000000..f2bf29a8 --- /dev/null +++ b/src/userTenantMapping/entities/user-tenant-mapping.entity.ts @@ -0,0 +1,48 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, + ManyToOne, + JoinColumn, + } from "typeorm"; + import { User } from "src/user/entities/user-entity"; + + @Entity({ name: "UserTenantMapping" }) + export class UserTenantMapping { + @PrimaryGeneratedColumn("uuid") + Id: string; + + @Column("uuid") + userId: string; + + @Column("uuid") + tenantId: string; + + @CreateDateColumn({ + type: "timestamp with time zone", + default: () => "CURRENT_TIMESTAMP", + }) + createdAt: Date; + + @UpdateDateColumn({ + type: "timestamp with time zone", + default: () => "CURRENT_TIMESTAMP", + }) + updatedAt: Date; + + @Column() + createdBy: string; + + @Column() + updatedBy: string; + + + + @ManyToOne(() => User, user => user.userTenantMapping) + @JoinColumn({ name: 'userId' }) + user: User; + + } + \ No newline at end of file diff --git a/src/userTenantMapping/user-tenant-mapping.adapter.ts b/src/userTenantMapping/user-tenant-mapping.adapter.ts new file mode 100644 index 00000000..1bb9ef7e --- /dev/null +++ b/src/userTenantMapping/user-tenant-mapping.adapter.ts @@ -0,0 +1,25 @@ +import { Injectable } from "@nestjs/common"; +import { PostgresAssignTenantService } from "src/adapters/postgres/userTenantMapping-adapter"; +import { HasuraAssignTenantService } from "src/adapters/hasura/userTenantMapping.adapter"; +import { IServicelocatorAssignTenant } from "src/adapters/usertenantmappinglocator"; + +@Injectable() +export class AssignTenantAdapter { + constructor(private hasuraProvider: HasuraAssignTenantService, + private postgresProvider:PostgresAssignTenantService) {} + buildAssignTenantAdapter(): IServicelocatorAssignTenant { + let adapter: IServicelocatorAssignTenant; + + switch (process.env.ADAPTERSOURCE) { + case "hasura": + adapter = this.hasuraProvider; + break; + case "postgres": + adapter = this.postgresProvider; + break; + default: + throw new Error("Invalid ADAPTERSOURCE environment variable. Please specify either 'hasura' or 'postgres'."); + } + return adapter; + } +} diff --git a/src/userTenantMapping/user-tenant-mapping.controller.spec.ts b/src/userTenantMapping/user-tenant-mapping.controller.spec.ts new file mode 100644 index 00000000..91860489 --- /dev/null +++ b/src/userTenantMapping/user-tenant-mapping.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AssignTenantController } from './user-tenant-mapping.controller'; + +describe('AssignTenantController', () => { + let controller: AssignTenantController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [AssignTenantController], + }).compile(); + + controller = module.get(AssignTenantController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/userTenantMapping/user-tenant-mapping.controller.ts b/src/userTenantMapping/user-tenant-mapping.controller.ts new file mode 100644 index 00000000..11907845 --- /dev/null +++ b/src/userTenantMapping/user-tenant-mapping.controller.ts @@ -0,0 +1,65 @@ +import { + ApiTags, + ApiBody, + ApiCreatedResponse, + ApiBasicAuth, + ApiConsumes, + ApiHeader, + ApiBadRequestResponse, + ApiInternalServerErrorResponse, + ApiOkResponse, + ApiConflictResponse, +} from "@nestjs/swagger"; +import { + Controller, + Get, + Post, + Body, + Put, + Param, + UseInterceptors, + Req, + UploadedFile, + Res, + Headers, + UseGuards, + ValidationPipe, + UsePipes, + UseFilters, +} from "@nestjs/common"; +import { Request } from "@nestjs/common"; +import { Response, response } from "express"; +import { AssignTenantAdapter } from "./user-tenant-mapping.adapter"; +import { UserTenantMappingDto } from "./dto/user-tenant-mapping.dto"; +import { JwtAuthGuard } from "src/common/guards/keycloak.guard"; +import { AllExceptionsFilter } from "src/common/filters/exception.filter"; +import { APIID } from "src/common/utils/api-id.config"; + +@ApiTags("AssignTenant") +@Controller("assign-tenant") +@UseGuards(JwtAuthGuard) +export class AssignTenantController { + constructor(private readonly assignTenantAdapter: AssignTenantAdapter) { } + + @UseFilters(new AllExceptionsFilter(APIID.ASSIGN_TENANT_CREATE)) + @Post() + @ApiBasicAuth("access-token") + @ApiCreatedResponse({ description: "Tenant assigned successfully to the user." }) + @ApiBadRequestResponse({ description: "Bad request." }) + @ApiInternalServerErrorResponse({ description: "Internal Server Error." }) + @ApiConflictResponse({ description: "Tenant is already assigned to this user." }) + @UsePipes(new ValidationPipe()) + @ApiBody({ type: UserTenantMappingDto }) + public async createUserTenantMapping( + @Headers() headers, + @Req() request: Request, + @Body() userTenantMappingDto: UserTenantMappingDto, + @Res() response: Response + ) { + return await this.assignTenantAdapter.buildAssignTenantAdapter().userTenantMapping( + request, + userTenantMappingDto, + response + ); + } +} diff --git a/src/userTenantMapping/user-tenant-mapping.module.ts b/src/userTenantMapping/user-tenant-mapping.module.ts new file mode 100644 index 00000000..df4362a7 --- /dev/null +++ b/src/userTenantMapping/user-tenant-mapping.module.ts @@ -0,0 +1,36 @@ +import { HttpModule, Module } from '@nestjs/common'; +import { AssignTenantController } from './user-tenant-mapping.controller'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { UserTenantMapping } from "./entities/user-tenant-mapping.entity"; +import { AssignTenantAdapter } from "./user-tenant-mapping.adapter"; +import { PostgresAssignTenantService } from "src/adapters/postgres/userTenantMapping-adapter"; +import { HasuraAssignTenantService } from "src/adapters/hasura/userTenantMapping.adapter"; +import { User } from "src/user/entities/user-entity"; +import { Tenants } from "src/userTenantMapping/entities/tenant.entity"; + + +@Module({ + imports:[TypeOrmModule.forFeature([UserTenantMapping,User,Tenants]),HttpModule], + controllers: [AssignTenantController], + providers: [AssignTenantAdapter,HasuraAssignTenantService,PostgresAssignTenantService] + +}) +export class AssignTenantModule {} + + +// import { Module } from '@nestjs/common'; +// import { AssignRoleAdapter } from './assign-role.apater'; +// import { AssignRoleController } from './assign-role.controller'; +// import { TypeOrmModule } from '@nestjs/typeorm'; +// import { UserRoleMapping } from './entities/assign-role.entity'; +// import { Role } from "src/rbac/role/entities/role.entity"; +// import { PostgresAssignroleService } from 'src/adapters/postgres/rbac/assignrole-adapter'; +// import { HasuraAssignRoleService } from 'src/adapters/hasura/rbac/assignrole.adapter'; +// import { HttpModule } from '@nestjs/axios'; + +// @Module({ +// imports:[TypeOrmModule.forFeature([UserRoleMapping,Role]),HttpModule], +// controllers: [AssignRoleController], +// providers: [AssignRoleAdapter,HasuraAssignRoleService,PostgresAssignroleService] +// }) +// export class AssignRoleModule {} diff --git a/src/utils/response-interface.ts b/src/utils/response-interface.ts new file mode 100644 index 00000000..bfbb9269 --- /dev/null +++ b/src/utils/response-interface.ts @@ -0,0 +1,30 @@ +// structure for server responses +export interface ServerResponse { + // api id + id: string; + + // response param + params: Params; + + // response code + responseCode: string; + + //server result + result: any; + + // time stamp + ts: string; + + // api version + ver: string; + + headers?: any; + } + + export interface Params { + resmsgid: string; + err?: any; + status: string; + errmsg?: any; + } + \ No newline at end of file diff --git a/src/utils/response.ts b/src/utils/response.ts new file mode 100644 index 00000000..7aa5abc1 --- /dev/null +++ b/src/utils/response.ts @@ -0,0 +1,91 @@ +import { v4 } from 'uuid'; +import { ServerResponse, Params } from './response-interface'; + +export default class APIResponse { + public static success( + id: string, + result: Type, + statusCode: string, + ): ServerResponse { + try { + const params: Params = { + resmsgid: v4(), + status: 'successful', + err: null, + errmsg: null, + }; + + const resObj: ServerResponse = { + id, + ver: '1.0', + ts: new Date().toISOString(), + params, + responseCode: statusCode, + result, + }; + return resObj; + } catch (e) { + return e; + } + } + + public static error( + id: string, + errmsg: string, + error: string, + statusCode: string, + ): ServerResponse { + try { + const params: Params = { + resmsgid: v4(), + status: 'failed', + err: error, + errmsg: errmsg, + }; + + const resObj: ServerResponse = { + id, + ver: '1.0', + ts: new Date().toISOString(), + params, + responseCode: statusCode, + result: { success: false }, + }; + return resObj; + } catch (e) { + return e; + } + } + + public static search(dtoFileName){ + let { limit, page, filters } = dtoFileName; + + let offset = 0; + if (page > 1) { + offset = parseInt(limit) * (page - 1); + } + + if (limit.trim() === '') { + limit = '0'; + } + + const whereClause = {}; + if (filters && Object.keys(filters).length > 0) { + Object.entries(filters).forEach(([key, value]) => { + whereClause[key] = value; + }); + } + return {offset,limit,whereClause}; + } + +// public static handleBadRequests( +// response: Response, +// apiId: string, +// errmsg: string, +// error: string, +// ) { +// return response +// .status(HttpStatus.BAD_REQUEST) +// .json(APIResponse.error(apiId, errmsg, error, 'BAD_REQUEST')); +// } +} diff --git a/src/workHistory/dto/user-work-history.dto.ts b/src/workHistory/dto/user-work-history.dto.ts deleted file mode 100644 index 9886c824..00000000 --- a/src/workHistory/dto/user-work-history.dto.ts +++ /dev/null @@ -1,354 +0,0 @@ -import { Expose } from "class-transformer"; -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; -import { IsEmail } from "class-validator"; - -export class UserWorkHistoryDto { - @Expose() - workHistoryId: string; - - @ApiProperty({ - type: String, - description: "ID of the user for which posting info is stored", - }) - @Expose() - userId: string; - - @ApiProperty({ - type: String, - description: "role of the user ", - }) - @Expose() - role: string; - - @ApiProperty({ - type: String, - description: "Designation of the user at time of joining that posting", - }) - @Expose() - joiningDesignation: string; - - @ApiProperty({ - type: String, - description: "Designation of the user while leaving that posting", - }) - @Expose() - leavingDesignation: string; - - @ApiProperty({ - type: String, - description: "Date of joining in that posting", - default: new Date().toISOString().split("T")[0], - }) - @Expose() - dateOfJoining: Date; - - @ApiProperty({ - type: String, - description: "Date of relieving from that posting", - default: new Date().toISOString().split("T")[0], - }) - @Expose() - dateOfRelieving: Date; - - @ApiProperty({ - type: String, - description: "Reason of closure of that posting", - }) - @Expose() - reason: string; - - @ApiProperty({ - type: String, - description: "any remark", - }) - @Expose() - remark: string; - - @ApiProperty({}) - @Expose() - cadre: string; - - @ApiProperty({}) - @Expose() - transferOrderNumber: string; - - @ApiProperty({}) - @Expose() - placeOfPosting: string; - - @ApiProperty({ - default: new Date().toISOString().split("T")[0], - }) - @Expose() - dateOfOrder: string; - - @ApiProperty({}) - @Expose() - modeOfPosting: string; - - @ApiProperty() - @Expose() - refId1: string; - - @ApiProperty() - @Expose() - refId2: string; - - @ApiProperty() - @Expose() - refId3: string; - - @ApiProperty({ - type: String, - description: "The first name of the user", - }) - @Expose() - firstName: string; - - @ApiProperty({ - type: String, - description: "The middle name of the user", - }) - @Expose() - middleName: string; - - @ApiProperty({ - type: String, - description: "The lastname of the user", - }) - @Expose() - lastName: string; - - @ApiProperty({ - type: String, - description: "The contact number of the user", - }) - @Expose() - phoneNumber: string; - - @ApiProperty({ - type: String, - description: "The email of the user", - }) - @Expose() - @IsEmail() - email: string; - - @ApiProperty() - @Expose() - aadhaar: string; - - @ApiProperty({ - type: String, - description: "The gender of the user", - }) - @Expose() - gender: string; - - @ApiProperty({ - type: String, - description: "The socialCategory of the user", - }) - @Expose() - socialCategory: string; - - @ApiProperty({ - type: String, - description: "The birthDate of the user", - }) - @Expose() - birthDate: string; - - @ApiProperty({ - type: String, - description: "The designation of the user", - }) - @Expose() - designation: string; - - @ApiProperty({ - type: String, - description: "The profQualification of the user", - }) - @Expose() - profQualification: string; - - @ApiProperty({ - type: String, - description: "The joiningDate of the user", - }) - @Expose() - joiningDate: string; - - @ApiProperty({ - type: [String], - description: "The subjectId of the user", - }) - @Expose() - subjectIds: [string]; - - @ApiProperty({ - type: String, - description: "The bloodGroup of the user", - }) - @Expose() - bloodGroup: string; - - @ApiProperty({ - type: String, - description: "The maritalStatus of the user", - }) - @Expose() - maritalStatus: string; - - @ApiProperty({ - type: String, - description: "The compSkills of the user", - }) - @Expose() - compSkills: string; - - @ApiProperty({ - type: String, - description: "The disability of the user", - }) - @Expose() - disability: string; - - @ApiProperty({ - type: String, - description: "The religion of the user", - }) - @Expose() - religion: string; - - @ApiProperty({ - type: String, - description: "The homeDistance of the user", - }) - @Expose() - homeDistance: string; - - @ApiProperty({ - type: String, - description: "The schoolId of the user", - }) - @Expose() - schoolId: string; - - @ApiPropertyOptional() - @Expose() - address: string; - - @ApiProperty() - @Expose() - village: string; - - @ApiProperty() - @Expose() - block: string; - - @ApiProperty() - @Expose() - district: string; - - @ApiProperty() - @Expose() - stateId: string; - - @ApiProperty() - @Expose() - pincode: string; - - @ApiProperty() - @Expose() - locationId: string; - - @ApiProperty({ - type: String, - description: "The retirementDate of the user", - }) - @Expose() - retirementDate: string; - - @ApiProperty({ - type: String, - description: "The workingStatus of the user", - }) - @Expose() - workingStatus: string; - - @ApiProperty({ - type: String, - description: "The image of the user", - }) - @Expose() - image: string; - - @ApiProperty({ - type: String, - description: "The employmentType of the user", - }) - @Expose() - employmentType: string; - - @ApiProperty({ - type: String, - description: "The status of the user", - }) - @Expose() - status: string; - - @ApiProperty({ - type: String, - description: "The deactivation reason of the user", - }) - @Expose() - deactivationReason: string; - - @Expose() - workHistory: string; - - @ApiProperty() - @Expose() - reportsTo: string; - - @ApiPropertyOptional() - @Expose() - metaData: [string]; - - @ApiPropertyOptional() - @Expose() - fcmToken: string; - - @Expose() - createdAt: string; - - @Expose() - updatedAt: string; - - constructor(obj: any) { - this.workHistoryId = obj?.workHistoryId ? `${obj.workHistoryId}` : ""; - this.userId = obj?.userId ? `${obj.userId}` : ""; - this.role = obj?.role ? `${obj.role}` : ""; - this.joiningDesignation = obj?.joiningDesignation - ? `${obj.joiningDesignation}` - : ""; - this.leavingDesignation = obj?.leavingDesignation - ? `${obj.leavingDesignation}` - : ""; - this.dateOfJoining = obj?.dateOfJoining ? obj.dateOfJoining : ""; - this.dateOfRelieving = obj?.dateOfRelieving ? obj.dateOfRelieving : ""; - this.reason = obj?.reason ? `${obj.reason}` : ""; - this.remark = obj?.remark ? `${obj.remark}` : ""; - this.cadre = obj?.cadre ? `${obj.cadre}` : ""; - this.transferOrderNumber = obj?.transferOrderNumber - ? `${obj.transferOrderNumber}` - : ""; - this.placeOfPosting = obj?.placeOfPosting ? `${obj.placeOfPosting}` : ""; - this.dateOfOrder = obj?.dateOfOrder ? obj.dateOfOrder : ""; - this.modeOfPosting = obj?.modeOfPosting ? `${obj.modeOfPosting}` : ""; - this.createdAt = obj?.created_at ? `${obj.created_at}` : ""; - this.updatedAt = obj?.updated_at ? `${obj.updated_at}` : ""; - } -} diff --git a/src/workHistory/dto/work-history.dto.ts b/src/workHistory/dto/work-history.dto.ts deleted file mode 100644 index dd4ae301..00000000 --- a/src/workHistory/dto/work-history.dto.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { Expose } from "class-transformer"; -import { ApiProperty } from "@nestjs/swagger"; - -export class WorkHistoryDto { - @Expose() - workHistoryId: string; - - @Expose() - id: string; - - @ApiProperty({ - type: String, - description: "ID of the user for which posting info is stored", - }) - @Expose() - userId: string; - - @ApiProperty({ - type: String, - description: "role of the user ", - }) - @Expose() - role: string; - - @ApiProperty({ - type: String, - description: "Designation of the user at time of joining that posting", - }) - @Expose() - joiningDesignation: string; - - @ApiProperty({ - type: String, - description: "Designation of the user while leaving that posting", - }) - @Expose() - leavingDesignation: string; - - @ApiProperty({ - type: String, - description: "Date of joining in that posting", - default: new Date().toISOString().split("T")[0], - }) - @Expose() - dateOfJoining: Date; - - @ApiProperty({ - type: String, - description: "Date of relieving from that posting", - default: new Date().toISOString().split("T")[0], - }) - @Expose() - dateOfRelieving: Date; - - @ApiProperty({ - type: String, - description: "Reason of closure of that posting", - }) - @Expose() - reason: string; - - @ApiProperty({ - type: String, - description: "any remark", - }) - @Expose() - remark: string; - - @ApiProperty({}) - @Expose() - cadre: string; - - @ApiProperty({}) - @Expose() - transferOrderNumber: string; - - @ApiProperty({}) - @Expose() - placeOfPosting: string; - - @ApiProperty({ - default: new Date().toISOString().split("T")[0], - }) - @Expose() - dateOfOrder: string; - - @ApiProperty({}) - @Expose() - modeOfPosting: string; - - @ApiProperty({}) - @Expose() - organizationName: string; - - @Expose() - createdAt: string; - - @Expose() - updatedAt: string; - - constructor(obj: any) { - Object.assign(this, obj); - } -} diff --git a/src/workHistory/workHistory.controller.ts b/src/workHistory/workHistory.controller.ts deleted file mode 100644 index b82889c5..00000000 --- a/src/workHistory/workHistory.controller.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { - ApiBasicAuth, - ApiBody, - ApiCreatedResponse, - ApiForbiddenResponse, - ApiOkResponse, - ApiQuery, - ApiTags, -} from "@nestjs/swagger"; -import { - Body, - CacheInterceptor, - ClassSerializerInterceptor, - Controller, - Get, - Param, - Query, - Post, - Put, - Req, - SerializeOptions, - UseInterceptors, - Request, -} from "@nestjs/common"; - -import { WorkHistoryService } from "../adapters/hasura/workhistory.adapter"; -import { WorkHistoryDto } from "./dto/work-history.dto"; - -@ApiTags("Work History") -@Controller("workhistory") -export class WorkHistoryController { - constructor(private service: WorkHistoryService) {} - - @Post() - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Work History has been created successfully.", - }) - @ApiBody({ type: WorkHistoryDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async createWorkHistory( - @Req() request: Request, - @Body() workHistoryDto: WorkHistoryDto - ) { - return this.service.createWorkHistory(request, workHistoryDto); - } - - @Put("/:id") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Work History has been updated successfully.", - }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async updateWorkHistory( - @Param("id") id: string, - @Req() request: Request, - @Body() workHistoryDto: WorkHistoryDto - ) { - return await this.service.updateWorkHistory(id, request, workHistoryDto); - } - - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "Work History detail." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async getWorkHistory( - @Param("id") workHistoryId: string, - @Req() request: Request - ) { - return await this.service.getWorkHistory(workHistoryId, request); - } - - @Post("/search") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Work History list." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @ApiQuery({ name: "limit", required: false }) - @ApiQuery({ name: "workHistoryId", required: false }) - @ApiQuery({ name: "userId", required: false }) - @ApiQuery({ name: "dateOfJoining", required: false }) - @ApiQuery({ name: "dateOfRelieving", required: false }) - @ApiQuery({ name: "page", required: false }) - public async searchWorkHistory( - @Query("limit") limit: string, - @Query("workHistoryId") workHistoryId: string, - @Query("userId") userId: string, - @Query("dateOfJoining") dateOfJoining: string, - @Query("dateOfRelieving") dateOfRelieving: string, - @Query("page") page: number, - @Req() request: Request - ) { - return await this.service.searchWorkHistory( - limit, - workHistoryId, - userId, - dateOfJoining, - dateOfRelieving, - page, - request - ); - } -} diff --git a/src/workHistory/workHistory.module.ts b/src/workHistory/workHistory.module.ts deleted file mode 100644 index c366bc42..00000000 --- a/src/workHistory/workHistory.module.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { CacheModule, Module } from "@nestjs/common"; -import { HttpModule } from "@nestjs/axios"; -import { WorkHistoryService } from "../adapters/hasura/workhistory.adapter"; -import { WorkHistoryController } from "./workHistory.controller"; - -const ttl = process.env.TTL as never; -@Module({ - imports: [ - HttpModule, - CacheModule.register({ - ttl: ttl, - }), - ], - controllers: [WorkHistoryController], - providers: [WorkHistoryService], -}) -export class WorkHistoryModule {} diff --git a/src/worksheet/dto/worksheet-search.dto.ts b/src/worksheet/dto/worksheet-search.dto.ts deleted file mode 100644 index ee2fdf8d..00000000 --- a/src/worksheet/dto/worksheet-search.dto.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; - -export class WorksheetSearchDto { - @ApiProperty({ - type: String, - description: "Limit", - }) - limit: string; - - @ApiProperty({ - type: Number, - description: "Page", - }) - page: number; - @ApiProperty({ - type: Object, - description: "Filters", - }) - @ApiPropertyOptional() - filters: object; - - constructor(partial: Partial) { - Object.assign(this, partial); - } -} diff --git a/src/worksheet/dto/worksheet.dto.ts b/src/worksheet/dto/worksheet.dto.ts deleted file mode 100644 index 196cd1cd..00000000 --- a/src/worksheet/dto/worksheet.dto.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { ApiProperty } from "@nestjs/swagger"; -import { Expose } from "class-transformer"; - -export class WorksheetDto { - @Expose() - id: string; - - @Expose() - worksheetId: string; - - @ApiProperty({ - description: - "Instructions on how to understand, attempt or how the question set will be evaluated.", - }) - @Expose() - name: string; - - @ApiProperty({ - description: "Worksheet Title", - }) - @Expose() - state: string; - @ApiProperty({ - description: "Worksheet state", - }) - @Expose() - subject: string; - @ApiProperty({ - description: "Worksheet subject", - }) - @Expose() - grade: string; - @ApiProperty({ - description: "Worksheet grade level", - }) - @Expose() - level: string; - @ApiProperty({ - description: "Worksheet level", - }) - @Expose() - instructions: string; - - @ApiProperty({ - description: "Feedback shown to the students after outcome processing.", - }) - @Expose() - feedback: any; - - @ApiProperty({ - description: - "Hints are shown to the students after outcome processing or when the student requests for hints.", - }) - @Expose() - hints: any; - - @ApiProperty({ - description: - "Determines the general paths that the student may take during the test session. Applicable only when questions data is present.", - }) - @Expose() - navigationMode: string; - - @ApiProperty({ - description: - "Time limits for the complete set and/or for each question in the question set.", - }) - @Expose() - timeLimits: string; - - @ApiProperty({ - description: - "Configuration to enable/disable hints for the student while using the question set.", - }) - @Expose() - showHints: string; - - @ApiProperty({ - description: " learning outcome", - }) - @Expose() - questions: [string]; - - @ApiProperty({ - description: "Question Sets associated with the current set.", - }) - @Expose() - questionSets: any; - - @ApiProperty({ - description: - "Information about the outcome variables of the question set, i.e the values that are output of a question set session.", - }) - @Expose() - outcomeDeclaration: any; - - @ApiProperty({ - description: - "Rules to assign values to outcome variables based on the student's reponses.", - }) - @Expose() - outcomeProcessing: any; - - @ApiProperty({ - description: - "A question set can be comprised of a materialized list of questions, or can also be dynamically built at runtime by using a criteria to select member questions.", - }) - @Expose() - questionSetType: string; - - @ApiProperty({ - description: "Criteria to be used when the set type is dynamic.", - }) - @Expose() - criteria: string; - - @ApiProperty({ - description: " ", - }) - @Expose() - usedFor: string; - - @ApiProperty({ - description: "Purpose served by the question.", - }) - @Expose() - purpose: string; - - @ApiProperty({ - description: "Visibility of the question set.", - }) - @Expose() - visibility: string; - - @ApiProperty({ - description: - "Version of the QuML specification using which the question set is created.", - }) - @Expose() - qumlVersion: string; - - @ApiProperty({ - description: "Array of topic.", - }) - @Expose() - topic: [string]; - - @ApiProperty({ - description: "source of worksheet", - }) - @Expose() - source: string; - - @Expose() - createdAt: string; - - @Expose() - updatedAt: string; - - @Expose() - createdBy: string; - - @Expose() - updatedBy: string; - - constructor(obj: any) { - Object.assign(this, obj); - } -} diff --git a/src/worksheet/worksheet.controller.ts b/src/worksheet/worksheet.controller.ts deleted file mode 100644 index 95872038..00000000 --- a/src/worksheet/worksheet.controller.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { - ApiBasicAuth, - ApiBody, - ApiCreatedResponse, - ApiForbiddenResponse, - ApiOkResponse, - ApiQuery, - ApiTags, -} from "@nestjs/swagger"; -import { - Body, - CacheInterceptor, - ClassSerializerInterceptor, - Controller, - Get, - Param, - Query, - Post, - Put, - Req, - SerializeOptions, - UseInterceptors, - Request, -} from "@nestjs/common"; -import { WorksheetService } from "src/adapters/hasura/worksheet.adapter"; -import { WorksheetDto } from "./dto/worksheet.dto"; -import { WorksheetSearchDto } from "./dto/worksheet-search.dto"; - -@ApiTags("Worksheet") -@Controller("worksheet") -export class WorksheetController { - constructor(private service: WorksheetService) {} - - @Post() - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Worksheet has been created successfully.", - }) - @ApiBody({ type: WorksheetDto }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async createWorksheet( - @Req() request: Request, - @Body() worksheetDto: WorksheetDto - ) { - return this.service.createWorksheet(request, worksheetDto); - } - - @Put("/:id") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ - description: "Worksheet has been updated successfully.", - }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - public async updateWorksheet( - @Param("id") id: string, - @Req() request: Request, - @Body() worksheetDto: WorksheetDto - ) { - return await this.service.updateWorksheet(id, request, worksheetDto); - } - - @Get("/:id") - @UseInterceptors(ClassSerializerInterceptor, CacheInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: "Worksheet detail." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async getWorksheet( - @Param("id") worksheetId: string, - @Req() request: Request - ) { - return this.service.getWorksheet(worksheetId, request); - } - - @Post("/search") - @ApiBasicAuth("access-token") - @ApiCreatedResponse({ description: "Worksheet list." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @UseInterceptors(ClassSerializerInterceptor) - @SerializeOptions({ - strategy: "excludeAll", - }) - public async searchWorksheet( - @Body() worksheetSearchDto: WorksheetSearchDto, - @Req() request: Request - ) { - return await this.service.searchWorksheet(worksheetSearchDto, request); - } - - @Post(":worksheet/pdf") - @UseInterceptors(ClassSerializerInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: " Ok." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @ApiQuery({ name: "worksheetId", required: true }) - @ApiQuery({ name: "templateId", required: true }) - public async getWorksheetPdf( - @Query("worksheetId") worksheetId: string, - @Query("templateId") templateId: number, - @Req() request: Request - ) { - return this.service.downloadWorksheet(worksheetId, templateId, request); - } - - @Post("/share") - @UseInterceptors(ClassSerializerInterceptor) - @ApiBasicAuth("access-token") - @ApiOkResponse({ description: " Ok." }) - @ApiForbiddenResponse({ description: "Forbidden" }) - @ApiQuery({ name: "studentIds", required: true }) - @ApiQuery({ name: "teacherId", required: true }) - @ApiQuery({ name: "templateId", required: true }) - @ApiQuery({ name: "link", required: true }) - @ApiQuery({ name: "subject", required: true }) - @ApiQuery({ name: "topic", required: true }) - public async sendWorksheet( - @Query("studentIds") studentIds: [string], - @Query("teacherId") teacherId: string, - @Query("templateId") templateId: string, - @Query("link") link: string, - @Query("subject") subject: string, - @Query("topic") topic: string, - - @Req() - request: Request - ) { - return this.service.sendWorksheet( - studentIds, - teacherId, - templateId, - link, - subject, - topic, - request - ); - } -} diff --git a/src/worksheet/worksheet.module.ts b/src/worksheet/worksheet.module.ts deleted file mode 100644 index 5d60fad4..00000000 --- a/src/worksheet/worksheet.module.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { CacheModule, Module } from "@nestjs/common"; -import { HttpModule } from "@nestjs/axios"; -import { WorksheetController } from "./worksheet.controller"; -import { WorksheetService } from "src/adapters/hasura/worksheet.adapter"; -import { TemplateService } from "src/adapters/sunbirdrc/template.adapter"; -const ttl = process.env.TTL as never; -@Module({ - imports: [ - HttpModule, - CacheModule.register({ - ttl: ttl, - }), - ], - controllers: [WorksheetController], - providers: [WorksheetService, TemplateService], -}) -export class WorksheetModule {}