diff --git a/README.md b/README.md index 73769431c..02437da5e 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Elevate user services can be setup in local using two methods: ### Local Dependencies Steps -1. Update dependency (Mongo, Kafka etc) IP addresses in .env with "**host.docker.internal**". +1. Update dependency (Mongo v4.1.4, Kafka etc) IP addresses in .env with "**host.docker.internal**". Eg: @@ -102,7 +102,7 @@ Elevate user services can be setup in local using two methods: ### Remote Dependencies Steps -1. Update dependency (Mongo, Kafka etc) Ip addresses in .env with respective remote server IPs. +1. Update dependency (Mongo v4.1.4, Kafka etc) Ip addresses in .env with respective remote server IPs. Eg: @@ -170,9 +170,6 @@ Elevate user services can be setup in local using two methods: # Database connectivity url MONGODB_URL = mongodb://localhost:27017/db-name - # Number of rounds for encryption - SALT_ROUNDS = 10 - # Token secret to generate access token ACCESS_TOKEN_SECRET = 'access-token-secret' @@ -230,12 +227,6 @@ Elevate user services can be setup in local using two methods: # Internal access token for communicationcation between services via network call INTERNAL_ACCESS_TOKEN = 'internal-access-token' - # Mentor screct code for registering - MENTOR_SECRET_CODE = 'secret-code' - - #Enable logging of network request - ENABLE_LOG = true - # JWT Access Token expiry In Days ACCESS_TOKEN_EXPIRY = '1' @@ -279,7 +270,7 @@ Elevate user services can be setup in local using two methods: - Node - 16.0.0 - Kafka - 3.1.0 - Jest - 28.1.1 -- MongoDB - 4.4.14 +- MongoDB - 4.1.4 - Redis - 7.0.0 # Migrations Commands @@ -405,4 +396,3 @@ Several open source dependencies that have aided user service development: - diff --git a/deployment/ansible.yml b/deployment/ansible.yml index 9313ec21c..db29f885c 100644 --- a/deployment/ansible.yml +++ b/deployment/ansible.yml @@ -57,10 +57,6 @@ # src={{ release_path }} # dest={{ current_path }} # state=link - - - name: Delete old pm2 process - command: pm2 delete elevate-user - ignore_errors: yes - name: set permission shell: chmod 744 {{ current_path }}/src/scripts/json2env.sh @@ -78,6 +74,14 @@ register: apiDocResponse - debug: msg=" Api-doc {{ apiDocResponse }} " + + - name: Run Migrations + command: "chdir={{current_path}}/src npx sequelize-cli db:migrate" + + - name: Delete old pm2 process + command: pm2 delete elevate-user + ignore_errors: yes + - name: Delete release folder shell: rm -rf {{ release_path }} diff --git a/dev-ops/docker-compose.yml b/dev-ops/docker-compose.yml index bce024b7f..2e1f0b20a 100644 --- a/dev-ops/docker-compose.yml +++ b/dev-ops/docker-compose.yml @@ -1,35 +1,39 @@ version: '3' services: zookeeper: - image: 'bitnami/zookeeper:3.8.0' + image: 'confluentinc/cp-zookeeper:7.3.0' + ports: + - '2181:2181' environment: - ALLOW_ANONYMOUS_LOGIN=yes + - ZOOKEEPER_CLIENT_PORT=2181 + - ZOOKEEPER_TICK_TIME=2000 networks: - elevate_net logging: - driver: none + driver: json-file kafka: - image: 'bitnami/kafka:3.1.0' + image: 'confluentinc/cp-kafka:7.3.0' + ports: + - '9092:9092' environment: - - KAFKA_BROKER_ID=1 - - KAFKA_CFG_LISTENERS=CLIENT://:9092,EXTERNAL://:9093 - - KAFKA_CFG_ADVERTISED_LISTENERS=CLIENT://kafka:9092,EXTERNAL://localhost:9093 - - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181 - - ALLOW_PLAINTEXT_LISTENER=yes - - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CLIENT:PLAINTEXT,EXTERNAL:PLAINTEXT - - KAFKA_CFG_INTER_BROKER_LISTENER_NAME=CLIENT + KAFKA_BROKER_ID: 1 + KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:9093 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT + KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 depends_on: - zookeeper networks: - elevate_net - logging: - driver: none - mongo: - image: 'mongo:4.4.14' + driver: json-file + postgres: + image: 'postgres:16' restart: 'always' ports: - - '27017:27017' + - '5432:5432' networks: - elevate_net logging: @@ -42,24 +46,23 @@ services: networks: - elevate_net logging: - driver: none - user_test: + driver: json-file + user_elevate_2.5: build: '../' - image: elevate/user:1.0 + # image: elevate/user:1.0 volumes: - ../src/:/var/src ports: - - '3001:3001' + - '5001:5001' command: [ 'nodemon', 'app.js' ] environment: - - MONGODB_URL=mongodb://mongo:27017/elevate-mentoring - KAFKA_URL=kafka:9092 - REDIS_HOST=redis://redis:6379 env_file: - integration_test.env depends_on: - kafka - - mongo + - postgres - redis networks: - elevate_net diff --git a/dev-ops/integration_test.env b/dev-ops/integration_test.env index 2365ba6d1..29e1dbee6 100644 --- a/dev-ops/integration_test.env +++ b/dev-ops/integration_test.env @@ -1,127 +1,98 @@ -#User Service Config - -# Port on which service runs -APPLICATION_PORT = 3001 - -# Service environment -APPLICATION_ENV = development - -# Database connectivity url -MONGODB_URL = mongodb://mongo:27017/elevate-mentoring - - -# Number of rounds for encryption -SALT_ROUNDS = 10 - -# Token secret to generate access token -ACCESS_TOKEN_SECRET = 'test' - -# Token secret to generate refresh token -REFRESH_TOKEN_SECRET = 'refresh-token-secret' - -# Kafka hosted server url -KAFKA_URL = localhost:9092 - -# Kafka group to which consumer belongs -KAFKA_GROUP_ID = userservice - -# Kafka topic to consume data from -KAFKA_TOPIC = 'testTopic' - -# Kafka topic to push notification data -NOTIFICATION_KAFKA_TOPIC = 'testTopic' - -# Any one of three features available for cloud storage -CLOUD_STORAGE = 'AWS' - -# Gcp json config file path -GCP_PATH = 'gcp.json' - -# Gcp bucket name which stores files -DEFAULT_GCP_BUCKET_NAME = 'gcp-bucket-storage-name' - -# Gcp project id -GCP_PROJECT_ID = 'project-id' - -# Aws access key id -AWS_ACCESS_KEY_ID = 'aws-access-key-id' - -# Aws secret access key -AWS_SECRET_ACCESS_KEY = 'aws-secret-access-key' - -# Aws region where bucket will be located -AWS_BUCKET_REGION = 'ap-south-1' - -# Aws end point -AWS_BUCKET_ENDPOINT = 's3.ap-south-1.amazonaws.com' - -# Aws bucket name which stores files -DEFAULT_AWS_BUCKET_NAME = 'aws-bucket-storage-name' - -# Azure storage account name -AZURE_ACCOUNT_NAME = 'account-name' - -# Azure storage account key -AZURE_ACCOUNT_KEY = 'azure-account-key' - -# Azure storage container which stores files -DEFAULT_AZURE_CONTAINER_NAME = 'azure-container-storage-name' - -# Internal access token for communicationcation between services via network call -INTERNAL_ACCESS_TOKEN = 'internal-access-token' - -# Mentor screct code for registering -MENTOR_SECRET_CODE = 'secret-code' - -#Enable logging of network request -ENABLE_LOG = true - -# JWT Access Token expiry In Days -ACCESS_TOKEN_EXPIRY = '1' - -# JWT Refresh Token expiry In Days -REFRESH_TOKEN_EXPIRY = '183' - -# Redis Host connectivity url -REDIS_HOST = 'redis://localhost:6379' - -# Otp expiration time for forgetpassword or registration process -OTP_EXP_TIME = 86400 - -# Enable email based otp verification for registration process -ENABLE_EMAIL_OTP_VERIFICATION = false - -# Api doc url -API_DOC_URL = '/api-doc' - -#Enable email for reported issue. -ENABLE_EMAIL_FOR_REPORT_ISSUE = true - -#Email id of the support team. -SUPPORT_EMAIL_ID = 'support@xyz.com,team@xyz.com' - -#Email template code for reported issue. -REPORT_ISSUE_EMAIL_TEMPLATE_CODE = 'user_issue_reported' -#kafka rating topic -RATING_KAFKA_TOPIC = 'Rating' - -#Internal cache expiry time -INTERNAL_CACHE_EXP_TIME = 86400 - -#Kafka internal communicationcation -CLEAR_INTERNAL_CACHE = 'userInternal' - -APP_NAME = 'user' - -REGISTRATION_EMAIL_TEMPLATE_CODE = 'code' - -OTP_EMAIL_TEMPLATE_CODE = 'ssss' -REDIS_CACHE_EXP_TIME = 11 - -KEY="g5MQ7HG/r5gPCPQQCwfBBEduAt72ewJIY/gWc0RNoak=" -IV="2lIctRkqzYMWbwlW1jCC9A==" - -ERROR_LOG_LEVEL='silly' -DISABLE_LOG=false - -ADMIN_SECRET_CODE=W5bF7gesuS0xsNWmpsKy \ No newline at end of file +ACCESS_TOKEN_EXPIRY=1 +ACCESS_TOKEN_SECRET=bsj82AHBxahusub12yexlashsbxAXADHBlaj +API_DOC_URL = /user/api-doc +APP_NAME=MentorED +APPLICATION_ENV=development + +#APPLICATION_PORT=3001 + +#CLOUD SERVICE DETAILS +AWS_ACCESS_KEY_ID= "test" +AWS_BUCKET_ENDPOINT= "s3.ap-south-1.amazonaws.com" +AWS_BUCKET_REGION= "ap-south-1" +AWS_SECRET_ACCESS_KEY= "/0UfS/0ooVQAwQMcwaW9yi" + +AZURE_ACCOUNT_KEY=test +AZURE_ACCOUNT_NAME=mentoring +CLEAR_INTERNAL_CACHE=userinternal +CLOUD_STORAGE=AWS + +DEFAULT_AWS_BUCKET_NAME=mentoring-dev-storage +DEFAULT_AZURE_CONTAINER_NAME=mentoring-images +DEFAULT_GCP_BUCKET_NAME=mentoring-dev-storage +ENABLE_EMAIL_OTP_VERIFICATION=false + +GCP_PATH=gcp.json +GCP_PROJECT_ID=sl-dev-project +INTERNAL_ACCESS_TOKEN=internal_access_token +INTERNAL_CACHE_EXP_TIME=86400 +IV=LHYOA5YnTonqcgrm15k3/Q== + +#KAFKA +KAFKA_GROUP_ID=mentoring +KAFKA_TOPIC= mentoring +#KAFKA_URL= localhost:9092 +KEY=E/m3RD/aM3Ed3lLfYVcKizakG9R+bFybAPZSLjIP2hY= + +NOTIFICATION_KAFKA_TOPIC=dev.notifications +OTP_EMAIL_TEMPLATE_CODE=emailotp +OTP_EXP_TIME=86400 + +#REDIS_HOST=redis://localhost:6379 + +#refresh tokens deatils +REFRESH_TOKEN_EXPIRY=183 +REFRESH_TOKEN_SECRET=371hkjkjady2y3ihdkajshdkiq23iuekw71yekhaskdvkvegavy23t78veqwexqvxveit6ttxyeeytt62tx236vv + +#EMAIL TEMPLATES +REGISTRATION_EMAIL_TEMPLATE_CODE=registration +REGISTRATION_OTP_EMAIL_TEMPLATE_CODE=registrationotp + + +DEFAULT_OCI_BUCKET_NAME= dev-mentoring +OCI_ACCESS_KEY_ID= 23b90f80de10cc226f7a4d617e0f4012a544d01d +OCI_BUCKET_ENDPOINT= https://axgqmpkrpmt5.compat.objectstorage.ap-hyderabad-1.oraclecloud.com +OCI_BUCKET_REGION= ap-hyderabad-1 +OCI_SECRET_ACCESS_KEY= 22levMw5Cinz6/NsAKXXakZMpzCet9yu6nn4dVeSmNE= + + +ERROR_LOG_LEVEL= silly +DISABLE_LOG= false +DEFAULT_ORGANISATION_CODE=default_code + +#new +DEV_DATABASE_URL=postgres://postgres:postgres@localhost:5432/user-local +ADMIN_SECRET_CODE=W5bF7gesuS0xsNWmpsKy +MENTORING_SERVICE_URL= test +DEFAULT_QUEUE="test" + +INVITEE_EMAIL_TEMPLATE_CODE='test' +ADMIN_INVITEE_UPLOAD_EMAIL_TEMPLATE_CODE='test' +MENTOR_INVITATION_EMAIL_TEMPLATE_CODE="test" +MENTEE_INVITATION_EMAIL_TEMPLATE_CODE="test" +#Default role +DEFAULT_ROLE="mentee" + +#sample file upload path +SAMPLE_CSV_FILE_PATH=sample/bulk_user_creation.csv + +#Email template for org admin invitation +ORG_ADMIN_INVITATION_EMAIL_TEMPLATE_CODE=invite_org_admin +DEFAULT_ORG_ID=1 +MENTORING_SERVICE_URL=http://mentoring:3002 +#Email template for mentor role request accepted +MENTOR_REQUEST_ACCEPTED_EMAIL_TEMPLATE_CODE=mentor_request_accepted + +#Email template for mentor role request rejected +MENTOR_REQUEST_REJECTED_EMAIL_TEMPLATE_CODE=mentor_request_rejected +DEFAULT_ROLE=mentee +PORTAL_URL = 'https://mentored.shikshalokam.org/auth/login' + +#Host name for scheduler service +SCHEDULER_SERVICE_HOST="http://localhost:4000" + +#base URL for scheduler service +SCHEDULER_SERVICE_BASE_URL= /scheduler/ + +#Refresh interval for materialized views +REFRESH_VIEW_INTERVAL= 540000 diff --git a/docker-compose.yml b/docker-compose.yml index 207e38280..2bcb865eb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,7 +30,7 @@ services: logging: driver: none mongo: - image: 'mongo:4.4.14' + image: 'mongo:4.1.4' restart: 'always' ports: - '27017:27017' diff --git a/release-notes/2.6.0.md b/release-notes/2.6.0.md new file mode 100644 index 000000000..feca2dcd9 --- /dev/null +++ b/release-notes/2.6.0.md @@ -0,0 +1,59 @@ +# Elevate-User Service Release Note + +## Version: 2.6.0 + +### New Features: + +1. **Encrypted storage of Email Ids in Database:** + + - All emails will be stored in an encrypted format with aes-256-cbc encryption algorithm. + - Affected tables: **users**, **users_credentials** and **organization_user_invites**. + - Added migration to automatically encrypt existing email Ids in deployed databases. + - Added script to encrypt/decrypt emails without running migrations. + + **Steps to enable email Id encryption:** + + - Navigate to the src/scripts directory. + - Run the generateEncryptionKeys.js script + + ``` + src/scripts$ node generateEncyrptionKeys.js + Email Id Encryption Key: f3f079bf7f3d215805722cc43131ddf7b25bbe0d6a455945fb10cfc90f9c1e2b + Email Id Encryption IV: 0deef78e20868f278af00903236227e5 + ``` + + - Add following env keys to the .env file (source the keys from script output as shown above) + + ``` + EMAIL_ID_ENCRYPTION_KEY=f3f079bf7f3d215805722cc43131ddf7b25bbe0d6a455945fb10cfc90f9c1e2b + EMAIL_ID_ENCRYPTION_IV=0deef78e20868f278af00903236227e5 + EMAIL_ID_ENCRYPTION_ALGORITHM='aes-256-cbc' + ``` + + - Encryption + + - Using migration + ``` + src$ npx sequelize-cli db:migrate + ``` + - Using standalone script + - Navigate to the scripts directory and run the following command + ``` + src/scripts$ node encryptDecryptEmails.js encrypt + ``` + - In-order to decrypt the email Ids, run: + ``` + src/scripts$ node encryptDecryptEmails.js decrypt + ``` + +### Enhancements: + +### Bug Fixes: + +### Infrastructure: + +### Documentation: + +### Deprecation Notice: + +### Known Issues: diff --git a/src/.env.sample b/src/.env.sample index da4e42b3e..3f3e71554 100644 --- a/src/.env.sample +++ b/src/.env.sample @@ -9,9 +9,6 @@ APPLICATION_ENV = development # Database connectivity url MONGODB_URL = mongodb://localhost:27017/db-name -# Number of rounds for encryption -SALT_ROUNDS = 10 - # Token secret to generate access token ACCESS_TOKEN_SECRET = 'access-token-secret' @@ -84,9 +81,6 @@ OCI_BUCKET_REGION= 'ap-hyderabad-1' # Internal access token for communicationcation between services via network call INTERNAL_ACCESS_TOKEN = 'internal-access-token' -# Mentor screct code for registering -MENTOR_SECRET_CODE = 'secret-code' - # JWT Access Token expiry In Days ACCESS_TOKEN_EXPIRY = '1' @@ -105,9 +99,6 @@ ENABLE_EMAIL_OTP_VERIFICATION = true # Api doc url API_DOC_URL = '/api-doc' -#kafka rating topic -RATING_KAFKA_TOPIC = 'Rating' - #Internal cache expiry time INTERNAL_CACHE_EXP_TIME = 86400 @@ -144,12 +135,6 @@ MENTORING_SERVICE_URL=http://localhost:3000 #Default queue for process invitee upload DEFAULT_QUEUE=user-queue -#Email template for mentor invitation -MENTOR_INVITATION_EMAIL_TEMPLATE_CODE=invite_mentor - -#Email template for mentee invitation -MENTEE_INVITATION_EMAIL_TEMPLATE_CODE=invite_mentee - #Email template for mentor role request accepted MENTOR_REQUEST_ACCEPTED_EMAIL_TEMPLATE_CODE=mentor_request_accepted @@ -182,3 +167,6 @@ SCHEDULER_SERVICE_BASE_URL= '/scheduler/' #Refresh interval for materialized views REFRESH_VIEW_INTERVAL= 540000 + +#Generic Email template for new users +GENERIC_INVITATION_EMAIL_TEMPLATE_CODE=generic_invite diff --git a/src/.husky/pre-commit b/src/.husky/pre-commit index 3c75af6d0..51e5d5230 100755 --- a/src/.husky/pre-commit +++ b/src/.husky/pre-commit @@ -3,4 +3,3 @@ cd src npx lint-staged -npm test diff --git a/src/api-doc/MentorED-Users.postman_collection.json b/src/api-doc/MentorED-Users.postman_collection.json index 305fcedb2..4e61e8434 100644 --- a/src/api-doc/MentorED-Users.postman_collection.json +++ b/src/api-doc/MentorED-Users.postman_collection.json @@ -1,9 +1,10 @@ { "info": { - "_postman_id": "615444c7-583f-44b9-9b96-8b92f9cb0df1", - "name": "MentorED-Users DEV new", + "_postman_id": "4b5d5720-bd48-4145-afb9-8661f40c17e5", + "name": "MentorED-Users", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "_exporter_id": "21498549" + "_exporter_id": "24204448", + "_collection_link": "https://dark-astronaut-764227.postman.co/workspace/Elevate-Workspace~39b977d8-9e5b-4232-87f3-3da4bed3a67c/collection/24204448-4b5d5720-bd48-4145-afb9-8661f40c17e5?action=share&source=collection_link&creator=24204448" }, "item": [ { @@ -77,7 +78,8 @@ { "key": "email", "value": "nevil@tunerlabs.com", - "type": "text" + "type": "text", + "disabled": true }, { "key": "password", @@ -94,6 +96,17 @@ { "key": "password", "value": "testing", + "type": "text", + "disabled": true + }, + { + "key": "email", + "value": "suman.v@pacewisdom.com", + "type": "text" + }, + { + "key": "password", + "value": "password", "type": "text" } ] @@ -461,37 +474,6 @@ } ] }, - { - "name": "User Role", - "item": [ - { - "name": "List User Roles", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [ - { - "key": "X-auth-token", - "value": "bearer {{token}}", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{UserDevBaseUrl}}user/v1/userRole/list", - "host": ["{{UserDevBaseUrl}}user"], - "path": ["v1", "userRole", "list"] - } - }, - "response": [] - } - ] - }, { "name": "Form", "item": [ @@ -771,7 +753,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"email\": \"nevil@tunerlabs.com\",\n \"organization_id\": 1\n}", + "raw": "{\n \"email\": \"suman@cc.com\",\n \"organization_id\": 1\n}", "options": { "raw": { "language": "json" @@ -1638,6 +1620,203 @@ "response": [] } ] + }, + { + "name": "User Role", + "item": [ + { + "name": "List User Roles", + "request": { + "method": "GET", + "header": [ + { + "key": "X-auth-token", + "value": "bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{UserDevBaseUrl}}user/v1/userRole/list?page=1&limit=10&search=system", + "host": ["{{UserDevBaseUrl}}user"], + "path": ["v1", "userRole", "list"], + "query": [ + { + "key": "page", + "value": "1" + }, + { + "key": "limit", + "value": "10" + }, + { + "key": "search", + "value": "system" + }, + { + "key": "organization_id", + "value": "1" + }, + { + "key": "user_type", + "value": "2", + "disabled": true + }, + { + "key": "visibility", + "value": "PUBLIC", + "disabled": true + }, + { + "key": "status", + "value": "ACTIVE", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "Default List User Roles", + "request": { + "method": "GET", + "header": [ + { + "key": "X-auth-token", + "value": "bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{UserDevBaseUrl}}user/v1/userRole/default?page=1&limit=10&search=a", + "host": ["{{UserDevBaseUrl}}user"], + "path": ["v1", "userRole", "default"], + "query": [ + { + "key": "page", + "value": "1" + }, + { + "key": "limit", + "value": "10" + }, + { + "key": "search", + "value": "a" + }, + { + "key": "organization_id", + "value": "1", + "disabled": true + }, + { + "key": "user_type", + "value": "2", + "disabled": true + }, + { + "key": "visibility", + "value": "PUBLIC", + "disabled": true + }, + { + "key": "status", + "value": "ACTIVE", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "Create Roles", + "request": { + "method": "POST", + "header": [ + { + "key": "X-auth-token", + "value": "bearer {{token}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{ \n\"title\":\"system_admins\", \n\"user_type\": 1, \n\"status\":\"ACTIVE\", \n\"visibility\":\"PUBLIC\"\n} ", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{UserDevBaseUrl}}user/v1/userRole/create", + "host": ["{{UserDevBaseUrl}}user"], + "path": ["v1", "userRole", "create"] + } + }, + "response": [] + }, + { + "name": "Update Roles", + "request": { + "method": "POST", + "header": [ + { + "key": "X-auth-token", + "value": "bearer {{token}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{ \n\"title\":\"system_adminstrations\", \n\"user_type\": 1, \n\"status\":\"ACTIVE\", \n\"visibility\":\"PUBLIC\"\n} ", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{UserDevBaseUrl}}user/v1/userRole/update/:id", + "host": ["{{UserDevBaseUrl}}user"], + "path": ["v1", "userRole", "update", ":id"], + "variable": [ + { + "key": "id", + "value": "9" + } + ] + } + }, + "response": [] + }, + { + "name": "Delete Roles", + "request": { + "method": "DELETE", + "header": [ + { + "key": "X-auth-token", + "value": "bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{UserDevBaseUrl}}user/v1/userRole/delete/:id", + "host": ["{{UserDevBaseUrl}}user"], + "path": ["v1", "userRole", "delete", ":id"], + "variable": [ + { + "key": "id", + "value": "9" + } + ] + } + }, + "response": [] + } + ] } ] } diff --git a/src/api-doc/api-doc.yaml b/src/api-doc/api-doc.yaml index ba2e8e1d6..06a2752cb 100644 --- a/src/api-doc/api-doc.yaml +++ b/src/api-doc/api-doc.yaml @@ -1,14 +1,14 @@ ---- openapi: 3.0.0 info: title: Elevate User version: 1.0.0 - termsOfService: 'https://github.com/project-sunbird/sunbird-commons/blob/master/LICENSE' + termsOfService: https://github.com/project-sunbird/sunbird-commons/blob/master/LICENSE description: >- - - The Users Service is a centralized Service to support other services. Apis perform operations related to mentoring entities notification etc + - The Users Service is a centralized Service to support other services. Apis + perform operations related to mentoring entities notification etc - - The URL for Users API(s) is `{context}/user/v1` - - Note: These resources can be used in other services + - The URL for Users API(s) is `{context}/user/v1` - Note: These + resources can be used in other services contact: email: info@sunbird.org servers: @@ -16,17 +16,18 @@ servers: description: local dev - url: https://dev.elevate-apis.shikshalokam.org description: dev server - paths: - '/user/v1/account/create': + /user/v1/account/create: post: summary: Create user tags: - User APIs - description: "This Api is associated with Create User on mentoring \n - Then - Endpoint for creating user `/user/v1/account/create` \n - It is mandatory to - provide values for parameter marked with `required` \n - Mandatory parameter - cannot be empty or null \n " + description: |- + This Api is associated with Create User on mentoring + - Then Endpoint for creating user `/user/v1/account/create` + - It is mandatory to provide values for parameter marked with `required` + - Mandatory parameter cannot be empty or null + requestBody: required: true description: Request body consist of metadata and accepts payload as JSON @@ -40,20 +41,20 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/user/userCreateUserResponse' + $ref: '#/components/schemas/user/userCreateUserResponse' '400': description: Bad Request. OTP is invalid content: application/json: schema: - '$ref': '#/components/schemas/user/userCreateUser400Response' + $ref: '#/components/schemas/user/userCreateUser400Response' '406': description: Not Acceptable. User already exists. content: application/json: schema: - '$ref': '#/components/schemas/user/userCreateUser406Exist' - '/user/v1/account/login': + $ref: '#/components/schemas/user/userCreateUser406Exist' + /user/v1/account/login: post: summary: Login tags: @@ -68,33 +69,37 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/user/userLoginRequest' + $ref: '#/components/schemas/user/userLoginRequest' responses: '200': description: OK. User successfully logged in. content: application/json: schema: - '$ref': '#/components/schemas/user/userLoginResponse' + $ref: '#/components/schemas/user/userLoginResponse' '400': description: Bad Request. Invalid email or password. content: application/json: schema: - '$ref': '#/components/schemas/user/userLoginResponse400' - '/user/v1/account/acceptTermsAndCondition': + $ref: '#/components/schemas/user/userLoginResponse400' + /user/v1/account/acceptTermsAndCondition: patch: summary: Terms & Condition tags: - Depreciated APIs - description: | - This API sets the 'email_verified' field to true using only the X-auth-token. + description: > + This API sets the 'email_verified' field to true using only the + X-auth-token. + - Endpoint: `/user/v1/account/acceptTermsAndCondition` + - Requires a valid X-auth-token from the login API response. parameters: - name: X-auth-token in: header - description: To make use of the API, you require X-auth-token. This is Available + description: >- + To make use of the API, you require X-auth-token. This is Available in login API Response. required: true schema: @@ -105,27 +110,33 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/user/acceptTermsAndConditionResponse' + $ref: '#/components/schemas/user/acceptTermsAndConditionResponse' '400': description: Bad Request. User does not exist or invalid input. content: application/json: schema: - '$ref': '#/components/schemas/user/acceptTermsAndConditionResponse400' - '/user/v1/account/generateToken': + $ref: '#/components/schemas/user/acceptTermsAndConditionResponse400' + /user/v1/account/generateToken: post: summary: Generate Access Token tags: - User APIs - description: | - This API is for re-login and generating an access token from a refresh token. + description: > + This API is for re-login and generating an access token from a refresh + token. + - Endpoint for generating token `/user/v1/account/generateToken` + - Mandatory parameters marked as `required` must have valid values. + - Parameters cannot be empty or null. parameters: - name: X-auth-token in: header - description: To use the API, you require X-auth-token, available in the login API response. + description: >- + To use the API, you require X-auth-token, available in the login API + response. required: true schema: type: string @@ -133,33 +144,33 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/user/generateTokenRequest' + $ref: '#/components/schemas/user/generateTokenRequest' responses: '200': description: OK. Access token generated successfully. content: application/json: schema: - '$ref': '#/components/schemas/user/generateTokenResponse200' + $ref: '#/components/schemas/user/generateTokenResponse200' '400': description: Bad Request. Invalid input or missing required parameters. content: application/json: schema: - '$ref': '#/components/schemas/user/generateTokenResponse400' + $ref: '#/components/schemas/user/generateTokenResponse400' '401': description: Unauthorized. X-auth-token is either missing or invalid. content: application/json: schema: - '$ref': '#/components/schemas/user/generateTokenResponse401' + $ref: '#/components/schemas/user/generateTokenResponse401' '500': description: Internal Server Error. Something went wrong on the server. content: application/json: schema: - '$ref': '#/components/schemas/user/generateTokenResponse500' - '/user/v1/account/generateOtp': + $ref: '#/components/schemas/user/generateTokenResponse500' + /user/v1/account/generateOtp: post: summary: Generate OTP tags: @@ -172,7 +183,8 @@ paths: parameters: - name: X-auth-token in: header - description: To make use of the API, you require X-auth-token. This is Available + description: >- + To make use of the API, you require X-auth-token. This is Available in login API Response. required: true schema: @@ -181,27 +193,27 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/user/genrateOtpRequest' + $ref: '#/components/schemas/user/genrateOtpRequest' responses: '200': description: OK. OTP generated successfully. content: application/json: schema: - '$ref': '#/components/schemas/user/generateOtpResponse200' + $ref: '#/components/schemas/user/generateOtpResponse200' '400': description: Bad Request. Invalid input or missing required parameters. content: application/json: schema: - '$ref': '#/components/schemas/user/generateOtpResponse400' + $ref: '#/components/schemas/user/generateOtpResponse400' '500': description: Internal Server Error. Something went wrong on the server. content: application/json: schema: - '$ref': '#/components/schemas/user/generateOtpResponse500' - '/user/v1/account/resetPassword': + $ref: '#/components/schemas/user/generateOtpResponse500' + /user/v1/account/resetPassword: post: summary: Reset Password tags: @@ -214,7 +226,8 @@ paths: parameters: - name: X-auth-token in: header - description: To make use of the API, you require X-auth-token. This is Available + description: >- + To make use of the API, you require X-auth-token. This is Available in login API Response. required: true schema: @@ -223,27 +236,27 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/user/resetPasswordRequest' + $ref: '#/components/schemas/user/resetPasswordRequest' responses: '200': description: OK. Password reset successfully. content: application/json: schema: - '$ref': '#/components/schemas/user/resetPasswordResponse200' + $ref: '#/components/schemas/user/resetPasswordResponse200' '400': description: Bad Request. Invalid input or missing required parameters. content: application/json: schema: - '$ref': '#/components/schemas/user/resetPasswordResponse400' + $ref: '#/components/schemas/user/resetPasswordResponse400' '401': description: Unauthorized. X-auth-token is either missing or invalid. content: application/json: schema: - '$ref': '#/components/schemas/user/resetPasswordResponse401' - '/user/v1/account/logout': + $ref: '#/components/schemas/user/resetPasswordResponse401' + /user/v1/account/logout: post: summary: Logout tags: @@ -256,7 +269,8 @@ paths: parameters: - name: X-auth-token in: header - description: To make use of the API, you require X-auth-token. This is Available + description: >- + To make use of the API, you require X-auth-token. This is Available in login API Response. required: true schema: @@ -265,27 +279,27 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/user/logoutRequest' + $ref: '#/components/schemas/user/logoutRequest' responses: '200': description: OK. Logout successful. content: application/json: schema: - '$ref': '#/components/schemas/user/logoutResponse200' + $ref: '#/components/schemas/user/logoutResponse200' '400': description: Bad Request. Invalid input or missing required parameters. content: application/json: schema: - '$ref': '#/components/schemas/user/logoutResponse400' + $ref: '#/components/schemas/user/logoutResponse400' '401': description: Unauthorized. X-auth-token is either missing or invalid. content: application/json: schema: - '$ref': '#/components/schemas/user/logoutResponse401' - '/user/v1/account/registrationOtp': + $ref: '#/components/schemas/user/logoutResponse401' + /user/v1/account/registrationOtp: post: summary: Registration OTP tags: @@ -298,7 +312,8 @@ paths: parameters: - name: X-auth-token in: header - description: To make use of the API, you require X-auth-token. This is Available + description: >- + To make use of the API, you require X-auth-token. This is Available in login API Response. required: true schema: @@ -307,27 +322,27 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/user/registrationOtpRequest' + $ref: '#/components/schemas/user/registrationOtpRequest' responses: '200': description: OK. Registration OTP sent successfully. content: application/json: schema: - '$ref': '#/components/schemas/user/registrationOtpResponse200' + $ref: '#/components/schemas/user/registrationOtpResponse200' '400': description: Bad Request. Invalid input or missing required parameters. content: application/json: schema: - '$ref': '#/components/schemas/user/registrationOtp400Response' + $ref: '#/components/schemas/user/registrationOtp400Response' '406': description: Not Acceptable. User already exists. content: application/json: schema: - '$ref': '#/components/schemas/user/userCreateUser406Exist' - '/user/v1/account/list': + $ref: '#/components/schemas/user/userCreateUser406Exist' + /user/v1/account/list: get: summary: List user tags: @@ -340,7 +355,8 @@ paths: parameters: - name: X-auth-token in: header - description: To make use of the API, you require X-auth-token. This is Available + description: >- + To make use of the API, you require X-auth-token. This is Available in login API Response. required: true schema: @@ -379,8 +395,8 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/user/userList200Response' - '/user/v1/user/read/{userId}': + $ref: '#/components/schemas/user/userList200Response' + /user/v1/user/read/{userId}: get: summary: User Details tags: @@ -393,13 +409,14 @@ paths: parameters: - name: X-auth-token in: header - description: To make use of the API, you require X-auth-token. This is Available + description: >- + To make use of the API, you require X-auth-token. This is Available in login API Response. required: true schema: type: string - in: path - name: 'userId' + name: userId required: true description: Please append a valid User ID to the Request URL. schema: @@ -411,15 +428,15 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/profile/profileDetailsResponse200' + $ref: '#/components/schemas/profile/profileDetailsResponse200' '401': description: Unauthorized. X-auth-token is either missing or invalid. content: content: application/json: schema: - '$ref': '#/components/schemas/profile/Unauthorized401Response' - '/user/v1/user/update': + $ref: '#/components/schemas/profile/Unauthorized401Response' + /user/v1/user/update: patch: summary: Update User Profile tags: @@ -432,7 +449,8 @@ paths: parameters: - name: X-auth-token in: header - description: To make use of the API, you require X-auth-token. This is Available + description: >- + To make use of the API, you require X-auth-token. This is Available in login API Response. required: true schema: @@ -441,40 +459,45 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/profile/updateRequest' + $ref: '#/components/schemas/profile/updateRequest' responses: '200': description: OK. User profile updated successfully. content: application/json: schema: - '$ref': '#/components/schemas/profile/updateRequest200Response' + $ref: '#/components/schemas/profile/updateRequest200Response' '401': description: Unauthorized. X-auth-token is either missing or invalid. content: application/json: schema: - '$ref': '#/components/schemas/profile/Unauthorized401Response' - '/user/v1/user/share/{userId}': + $ref: '#/components/schemas/profile/Unauthorized401Response' + /user/v1/user/share/{userId}: get: summary: Share Mentor Profile tags: - Users APIs - description: | + description: > This API is associated with sharing a mentor's profile. - - Endpoint for sharing a mentor's profile: `/user/v1/user/share/{userId}` + + - Endpoint for sharing a mentor's profile: + `/user/v1/user/share/{userId}` + - Mandatory parameters marked as `required` must have valid values. + - Parameters cannot be empty or null. parameters: - name: X-auth-token in: header - description: To make use of the API, you require X-auth-token. This is Available + description: >- + To make use of the API, you require X-auth-token. This is Available in login API Response. required: true schema: type: string - in: path - name: 'userId' + name: userId required: true description: Please append a valid User ID to the Request URL. schema: @@ -486,41 +509,49 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/profile/shareMentorProfile200Response' + $ref: '#/components/schemas/profile/shareMentorProfile200Response' '400': description: Bad Request. Invalid parameters in the request. content: application/json: schema: - '$ref': '#/components/schemas/profile/shareMentorProfileBadRequest400Response' + $ref: >- + #/components/schemas/profile/shareMentorProfileBadRequest400Response '401': description: Unauthorized. X-auth-token is either missing or invalid. content: application/json: schema: - '$ref': '#/components/schemas/profile/Unauthorized401Response' - '/user/v1/cloud-services/file/getSignedUrl?fileName={file_name}': + $ref: '#/components/schemas/profile/Unauthorized401Response' + /user/v1/cloud-services/file/getSignedUrl?fileName={file_name}: get: summary: Cloud Services - Get Signed URL tags: - Cloud Services APIs - description: | + description: > This API is associated with cloud services for obtaining a signed URL. - - Endpoint for cloud services: `/user/v1/cloud-services/file/getSignedUrl` + + - Endpoint for cloud services: + `/user/v1/cloud-services/file/getSignedUrl` + - Mandatory parameters marked as `required` must have valid values. + - Parameters cannot be empty or null. parameters: - name: X-auth-token in: header - description: To make use of the API, you require X-auth-token. This is Available + description: >- + To make use of the API, you require X-auth-token. This is Available in login API Response. required: true schema: type: string - in: path - name: 'file_name' + name: file_name required: true - description: Please append a valid file name to the URL, indicating the file to be uploaded. + description: >- + Please append a valid file name to the URL, indicating the file to + be uploaded. schema: type: string example: image.jpg @@ -530,29 +561,37 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/cloudServices/cloudServicesResponse200' - '/user/v1/cloud-services/file/getDownloadableUrl?file_path={file_path}': + $ref: '#/components/schemas/cloudServices/cloudServicesResponse200' + /user/v1/cloud-services/file/getDownloadableUrl?file_path={file_path}: get: summary: Cloud Services - Get Downloadable URL tags: - Cloud Services APIs - description: | - This API is associated with cloud services for obtaining a downloadable URL. - - Endpoint for cloud services: `/user/v1/cloud-services/file/getDownloadableUrl` + description: > + This API is associated with cloud services for obtaining a downloadable + URL. + + - Endpoint for cloud services: + `/user/v1/cloud-services/file/getDownloadableUrl` + - Mandatory parameters marked as `required` must have valid values. + - Parameters cannot be empty or null. parameters: - name: X-auth-token in: header - description: To make use of the API, you require X-auth-token. This is Available + description: >- + To make use of the API, you require X-auth-token. This is Available in login API Response. required: true schema: type: string - in: path - name: 'file_path' + name: file_path required: true - description: Please append a valid file path to the URL, indicating the file to be downloaded. + description: >- + Please append a valid file path to the URL, indicating the file to + be downloaded. schema: type: string example: users/62832531a05cbd57b273aebb-1654149589875-image.jpg @@ -562,21 +601,28 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/cloudServices/cloudServicesDownloadResponse200' - '/user/v1/cloud-services/file/getSampleCSV': + $ref: >- + #/components/schemas/cloudServices/cloudServicesDownloadResponse200 + /user/v1/cloud-services/file/getSampleCSV: get: summary: Cloud Services - Get Sample CSV tags: - Cloud Services APIs - description: | - This API is associated with cloud services for obtaining a sample CSV file for bulk user create. - - Endpoint for cloud services: `/user/v1/cloud-services/file/getSampleCSV` + description: > + This API is associated with cloud services for obtaining a sample CSV + file for bulk user create. + + - Endpoint for cloud services: + `/user/v1/cloud-services/file/getSampleCSV` + - Mandatory parameters marked as `required` must have valid values. + - Parameters cannot be empty or null. parameters: - name: X-auth-token in: header - description: To make use of the API, you require X-auth-token. This is Available + description: >- + To make use of the API, you require X-auth-token. This is Available in login API Response. required: true schema: @@ -587,8 +633,8 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/cloudServices/getSampleCSVResponse200' - '/user/v1/form/create': + $ref: '#/components/schemas/cloudServices/getSampleCSVResponse200' + /user/v1/form/create: post: summary: Create Form tags: @@ -601,7 +647,8 @@ paths: parameters: - name: X-auth-token in: header - description: To make use of the API, you require X-auth-token. This is Available + description: >- + To make use of the API, you require X-auth-token. This is Available in login API Response. required: true schema: @@ -610,21 +657,21 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/form/createFormRequest' + $ref: '#/components/schemas/form/createFormRequest' responses: '200': description: OK. Form created successfully. content: application/json: schema: - '$ref': '#/components/schemas/form/createForm200Response' + $ref: '#/components/schemas/form/createForm200Response' '400': description: Bad Request. Invalid input data. content: application/json: schema: - '$ref': '#/components/schemas/form/createForm400Response' - '/user/v1/form/update/{formId}': + $ref: '#/components/schemas/form/createForm400Response' + /user/v1/form/update/{formId}: patch: summary: Update Form tags: @@ -637,13 +684,14 @@ paths: parameters: - name: X-auth-token in: header - description: To make use of the API, you require X-auth-token. This is Available + description: >- + To make use of the API, you require X-auth-token. This is Available in login API Response. required: true schema: type: string - in: path - name: 'formId' + name: formId required: true description: Please append a valid form ID to the request URL. schema: @@ -653,40 +701,45 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/form/updateFormRequest' + $ref: '#/components/schemas/form/updateFormRequest' responses: '200': description: OK. Form updated successfully. content: application/json: schema: - '$ref': '#/components/schemas/form/updateForm200Response' + $ref: '#/components/schemas/form/updateForm200Response' '400': description: Bad Request. Invalid input or missing required parameters. content: application/json: schema: - '$ref': '#/components/schemas/form/updateForm400Response' - '/user/v1/form/read/{formId}': + $ref: '#/components/schemas/form/updateForm400Response' + /user/v1/form/read/{formId}: post: summary: Get Form Details tags: - Form APIs - description: | + description: > This API is associated with retrieving form details. + - Endpoint for retrieving form details: `/user/v1/form/read/{formId}` - - It is mandatory to provide values for parameters marked with `required`. + + - It is mandatory to provide values for parameters marked with + `required`. + - Mandatory parameters cannot be empty or null. parameters: - name: X-auth-token in: header - description: To make use of the API, you require X-auth-token. This is Available + description: >- + To make use of the API, you require X-auth-token. This is Available in login API Response. required: true schema: type: string - in: path - name: 'formId' + name: formId required: true description: Please append a valid form Id to the Request URL. schema: @@ -696,21 +749,21 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/form/readFormRequest' + $ref: '#/components/schemas/form/readFormRequest' responses: '200': description: OK. Form details retrieved successfully. content: application/json: schema: - '$ref': '#/components/schemas/form/readForm200Response' + $ref: '#/components/schemas/form/readForm200Response' '400': description: Bad Request. Invalid or missing parameters. content: application/json: schema: - '$ref': '#/components/schemas/form/readForm400Response' - '/user/v1/admin/create': + $ref: '#/components/schemas/form/readForm400Response' + /user/v1/admin/create: post: summary: Create Admin User tags: @@ -724,21 +777,21 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/admin/createSystemUserRequest' + $ref: '#/components/schemas/admin/createSystemUserRequest' responses: '201': description: OK. Admin user created successfully. content: application/json: schema: - '$ref': '#/components/schemas/admin/createSystemUser201Response' + $ref: '#/components/schemas/admin/createSystemUser201Response' '406': description: Bad Request. System User already exists. content: application/json: schema: - '$ref': '#/components/schemas/admin/createSystemUser406Response' - '/user/v1/admin/login': + $ref: '#/components/schemas/admin/createSystemUser406Response' + /user/v1/admin/login: post: summary: Admin Login tags: @@ -752,21 +805,21 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/admin/loginSystemUserRequest' + $ref: '#/components/schemas/admin/loginSystemUserRequest' responses: '200': description: OK. Successful login. content: application/json: schema: - '$ref': '#/components/schemas/admin/loginSystemUser200Response' + $ref: '#/components/schemas/admin/loginSystemUser200Response' '400': description: Bad Request. System User doesn't exist. content: application/json: schema: - '$ref': '#/components/schemas/admin/loginSystemUser400Response' - '/user/v1/admin/deleteUser/{id}': + $ref: '#/components/schemas/admin/loginSystemUser400Response' + /user/v1/admin/deleteUser/{id}: delete: summary: Delete User tags: @@ -779,14 +832,18 @@ paths: parameters: - name: X-auth-token in: header - description: X-auth-token required for API access. Available in login API Response. + description: >- + X-auth-token required for API access. Available in login API + Response. required: true schema: type: string - in: path - name: 'id' + name: id required: true - description: User ID to be deleted. Please append a valid user ID to the request URL. + description: >- + User ID to be deleted. Please append a valid user ID to the request + URL. schema: type: string example: 1 @@ -796,14 +853,14 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/admin/deleteUser202Response' + $ref: '#/components/schemas/admin/deleteUser202Response' '400': description: Bad Request. User Already Deleted. content: application/json: schema: - '$ref': '#/components/schemas/admin/deleteUser400Response' - '/user/admin/addOrgAdmin': + $ref: '#/components/schemas/admin/deleteUser400Response' + /user/admin/addOrgAdmin: post: summary: Add Organization Admin description: | @@ -816,7 +873,9 @@ paths: parameters: - name: X-auth-token in: header - description: X-auth-token required for API access. Available in login API Response. + description: >- + X-auth-token required for API access. Available in login API + Response. required: true schema: type: string @@ -834,7 +893,9 @@ paths: organization_id: type: integer example: 55 - description: The ID of the organization to which the user will be assigned as Org-Admin. + description: >- + The ID of the organization to which the user will be + assigned as Org-Admin. responses: '200': description: OK. User assigned as the Org-Admin successfully. @@ -845,10 +906,10 @@ paths: properties: responseCode: type: string - example: 'OK' + example: OK message: type: string - example: 'User Assigned As The Org-Admin Successfully' + example: User Assigned As The Org-Admin Successfully result: type: object properties: @@ -868,24 +929,23 @@ paths: example: 2 title: type: string - example: 'mentor' + example: mentor user_type: type: integer example: 1 status: type: string - example: 'ACTIVE' + example: ACTIVE example: - id: 2 - title: 'mentor' + title: mentor user_type: 0 - status: 'ACTIVE' + status: ACTIVE - id: 3 - title: 'org_admin' + title: org_admin user_type: 0 - status: 'ACTIVE' - - '/user/v1/admin/deactivateOrg/{id}': + status: ACTIVE + /user/v1/admin/deactivateOrg/{id}: post: summary: Deactivate Organization tags: @@ -898,12 +958,14 @@ paths: parameters: - name: X-auth-token in: header - description: X-auth-token required for API access. Available in login API Response. + description: >- + X-auth-token required for API access. Available in login API + Response. required: true schema: type: string - in: path - name: 'id' + name: id required: true description: Please append a valid organization ID to the request URL. schema: @@ -915,15 +977,14 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/admin/deleteOrg202Response' + $ref: '#/components/schemas/admin/deleteOrg202Response' '400': description: Bad Request. Organization Entity Already Deleted. content: application/json: schema: - '$ref': '#/components/schemas/admin/deleteUser400Response' - - '/user/v1/admin/deactivateUser': + $ref: '#/components/schemas/admin/deleteUser400Response' + /user/v1/admin/deactivateUser: post: summary: Deactivate User tags: @@ -936,7 +997,9 @@ paths: parameters: - name: X-auth-token in: header - description: X-auth-token required for API access. Available in login API Response. + description: >- + X-auth-token required for API access. Available in login API + Response. required: true schema: type: string @@ -951,22 +1014,23 @@ paths: type: array items: type: integer - example: [1, 3] + example: + - 1 + - 3 responses: '202': description: Accepted. Users deactivated successfully. content: application/json: schema: - '$ref': '#/components/schemas/admin/deactivateUser202Response' + $ref: '#/components/schemas/admin/deactivateUser202Response' '400': description: Bad Request. User Entity Already Deleted. content: application/json: schema: - '$ref': '#/components/schemas/admin/deactivateUser400Response' - - '/user/v1/organization/create': + $ref: '#/components/schemas/admin/deactivateUser400Response' + /user/v1/organization/create: post: summary: Create Organization tags: @@ -980,7 +1044,9 @@ paths: parameters: - name: X-auth-token in: header - description: X-auth-token required for API access. Available in the login API Response. + description: >- + X-auth-token required for API access. Available in the login API + Response. required: true schema: type: string @@ -988,36 +1054,43 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/organization/createOrganizationRequest' + $ref: '#/components/schemas/organization/createOrganizationRequest' responses: '200': description: OK. Organization created successfully. content: application/json: schema: - '$ref': '#/components/schemas/organization/createOrganization200Response' + $ref: >- + #/components/schemas/organization/createOrganization200Response '400': description: Bad Request. Organization already exists content: application/json: schema: - '$ref': '#/components/schemas/organization/createOrganization400Response' - - '/user/v1/organization/update/{id}': + $ref: >- + #/components/schemas/organization/createOrganization400Response + /user/v1/organization/update/{id}: post: summary: Update Organization tags: - Organization - description: | + description: > This API is associated with updating an organization. + - Endpoint for updating organization: `/user/v1/organization/update` - - It is mandatory to provide values for parameters marked with `required`. + + - It is mandatory to provide values for parameters marked with + `required`. + - Mandatory parameters cannot be empty or null. operationId: updateOrganization parameters: - name: X-auth-token in: header - description: To make use of the API, you require X-auth-token. This is available in the login API response. + description: >- + To make use of the API, you require X-auth-token. This is available + in the login API response. required: true schema: type: string @@ -1032,35 +1105,42 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/organization/createOrganizationRequest' + $ref: '#/components/schemas/organization/createOrganizationRequest' responses: '200': description: OK. Organization updated successfully. content: application/json: schema: - '$ref': '#/components/schemas/organization/updateOrganization200Response' + $ref: >- + #/components/schemas/organization/updateOrganization200Response '400': description: Bad Request. Organization doesn't exist. content: application/json: schema: - '$ref': '#/components/schemas/organization/updateOrganization400Response' - - '/user/v1/organization/list': + $ref: >- + #/components/schemas/organization/updateOrganization400Response + /user/v1/organization/list: get: summary: List Organization tags: - Organization - description: | - This API is associated with listing organizations based on specified conditions. + description: > + This API is associated with listing organizations based on specified + conditions. + - Endpoint for listing organizations: `/user/v1/organization/list` + - Mandatory parameters marked as `required` must have valid values. + - Parameters cannot be empty or null. parameters: - name: X-auth-token in: header - description: To make use of the API, you require X-auth-token. This is available in the login API response. + description: >- + To make use of the API, you require X-auth-token. This is available + in the login API response. required: true schema: type: string @@ -1091,22 +1171,27 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/organization/organizationList200Response' - - '/user/v1/organization/requestOrgRole': + $ref: '#/components/schemas/organization/organizationList200Response' + /user/v1/organization/requestOrgRole: post: summary: Request Org Role - description: | - This API is associated with requesting a specific role within an organization. + description: > + This API is associated with requesting a specific role within an + organization. + - Endpoint: `/user/v1/organization/requestOrgRole` + - Mandatory parameters marked as `required` must have valid values. + - Parameters cannot be empty or null. tags: - Organization parameters: - name: X-auth-token in: header - description: User's access token, which already recognizes the user as the organization's mentee. + description: >- + User's access token, which already recognizes the user as the + organization's mentee. required: true schema: type: string @@ -1136,20 +1221,22 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/organization/requestOrgRole200Response' - - '/user/v1/userRole/list': + $ref: '#/components/schemas/organization/requestOrgRole200Response' + /user/v1/userRole/list: get: summary: Get User Roles tags: - - User Role APIs - description: | + - User Roles APIs + description: > Retrieve the list of user roles. + - Endpoint: `/user/v1/userRole/list` + - It is mandatory to provide values for parameters marked as `required`. - - The X-auth-token obtained from the login API is required in the header. + - The X-auth-token obtained from the login API is required in the + header. parameters: - name: X-auth-token in: header @@ -1157,22 +1244,107 @@ paths: required: true schema: type: string - + - in: query + name: page + description: Please add page number + schema: &ref_0 + type: integer + - in: query + name: limit + description: Number of records to limit + schema: *ref_0 + - in: query + name: search + description: Please search for information such as 'title' names + schema: &ref_1 + type: string + - in: query + name: user_type + description: filtering based on the user_type + schema: *ref_0 + - in: query + name: visibility + description: filtering based on the visibility + schema: *ref_1 + - in: query + name: status + description: filtering based on the status + schema: *ref_1 + - in: query + name: organization_id + description: filtering based on the organization_id + schema: *ref_0 responses: '200': description: OK content: application/json: schema: - '$ref': '#/components/schemas/userRole/userRoleListResponse200' + $schema: http://json-schema.org/draft-04/schema# + type: object + properties: + responseCode: + type: string + message: + type: string + result: + type: object + properties: + data: + type: array + items: + type: object + properties: + id: + type: number + title: + type: string + user_type: + type: number + visibility: + type: string + status: + type: string + organization_id: + type: number + count: + type: number + meta: + type: object + properties: + formsVersion: + type: array + items: + type: string + correlation: + type: string + examples: + example1: + value: + responseCode: OK + message: Roles fetched successfully + result: + data: + - id: 6 + title: system_manager + user_type: 1 + visibility: PUBLIC + status: ACTIVE + organization_id: 1 + count: 1 + meta: + formsVersion: [] + correlation: b36f45cf-039c-466d-9c50-4649fa4e0856 '401': description: Unauthorized content: application/json: schema: - '$ref': '#/components/schemas/profile/Unauthorized401Response' - - '/user/v1/entity-type/create': + $ref: '#/components/schemas/profile/Unauthorized401Response' + requestBody: + description: '' + content: {} + /user/v1/entity-type/create: post: summary: Create Entity Type tags: @@ -1186,7 +1358,9 @@ paths: parameters: - name: X-auth-token in: header - description: Access token of the user who is already a mentee of the organization. + description: >- + Access token of the user who is already a mentee of the + organization. required: true schema: type: string @@ -1194,33 +1368,31 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/entity-type/createEntityTypeRequest' + $ref: '#/components/schemas/entity-type/createEntityTypeRequest' responses: '201': description: OK Entity type created successfully content: application/json: schema: - '$ref': '#/components/schemas/entity-type/createEntityType201Response' + $ref: '#/components/schemas/entity-type/createEntityType201Response' '400': description: Bad Request. Entity type already exists content: application/json: schema: - '$ref': '#/components/schemas/entity-type/createEntityType400Response' - '/user/v1/entity-type/update/{id}': + $ref: '#/components/schemas/entity-type/createEntityType400Response' + /user/v1/entity-type/update/{id}: post: summary: Update Entity Type - tags: - - EntityType APIs + tags: [] description: | Use this API to update an entity type. - Endpoint: `/user/v1/entity-type/update/{id}` - Provide values for parameters marked as `required`. - The X-auth-token is mandatory and cannot be empty or null. - - parameters: + parameters: &ref_3 - name: X-auth-token in: header description: Access token obtained from the login API response. @@ -1234,27 +1406,25 @@ paths: schema: type: integer example: 1 - requestBody: content: application/json: schema: - '$ref': '#/components/schemas/entity-type/updateEntityTypeRequest' - + $ref: '#/components/schemas/entity-type/updateEntityTypeRequest' responses: '201': description: OK. Entity Type Updated Successfully content: application/json: schema: - '$ref': '#/components/schemas/entity-type/updateEntityType201Response' + $ref: '#/components/schemas/entity-type/updateEntityType201Response' '400': description: Bad Request. Entity type already exists content: application/json: schema: - '$ref': '#/components/schemas/entity-type/updateEntityType400Response' - '/user/v1/entity-type/read': + $ref: '#/components/schemas/entity-type/updateEntityType400Response' + /user/v1/entity-type/read: post: summary: Read Entity Type tags: @@ -1268,7 +1438,8 @@ paths: parameters: - name: X-auth-token in: header - description: To make use of the API, you require X-auth-token. This is Available + description: >- + To make use of the API, you require X-auth-token. This is Available in login API Response. required: true schema: @@ -1293,7 +1464,7 @@ paths: - ln read_user_entity: false responses: - 201: + '201': description: OK Entity Type Fetched Successfully content: application.json: @@ -1395,26 +1566,28 @@ paths: meta: correlation: 64cd08ba-cc87-4172-9633-2146dabe0b78 meeting_platform: BBB - 400: + '400': description: Bad Request. Entity type already exists content: application/json: schema: - '$ref': '#/components/schemas/userentity/createUserEntity400Response' - '/user/v1/entity-type/delete/{id}': + $ref: '#/components/schemas/userentity/createUserEntity400Response' + /user/v1/entity-type/delete/{id}: delete: summary: Delete Entity Type tags: - EntityType APIs description: > - This API is associated with deleting an entity type. - - Endpoint for deleting a user entity: `/user/v1/entity-type/delete` - - It is mandatory to provide values for parameters marked with `required`. - - Mandatory parameters cannot be empty or null. + This API is associated with deleting an entity type. - Endpoint for + deleting a user entity: `/user/v1/entity-type/delete` - It is mandatory + to provide values for parameters marked with `required`. - Mandatory + parameters cannot be empty or null. parameters: - name: X-auth-token in: header - description: To use the API, include X-auth-token. This is available in the login API response. + description: >- + To use the API, include X-auth-token. This is available in the login + API response. required: true schema: type: string @@ -1431,28 +1604,32 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/entity-type/deleteUserEntity202Response' + $ref: '#/components/schemas/entity-type/deleteUserEntity202Response' '400': description: Bad Request. Entity Not Found content: application/json: schema: - '$ref': '#/components/schemas/entity-type/deleteUserEntity400Response' - - '/user/v1/entity/create': + $ref: '#/components/schemas/entity-type/deleteUserEntity400Response' + /user/v1/entity/create: post: summary: Create Entity tags: - Entity APIs - description: | + description: > This API is associated with entity creation. + - Endpoint for creating an entity: `/user/v1/entity/create`. - - It is mandatory to provide values for parameters marked with `required`. + + - It is mandatory to provide values for parameters marked with + `required`. + - Mandatory parameters cannot be empty or null. parameters: - name: X-auth-token in: header - description: To make use of the API, you require X-auth-token. This is Available + description: >- + To make use of the API, you require X-auth-token. This is Available in login API Response. required: true schema: @@ -1461,9 +1638,9 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/userentity/createUserEntityRequest' + $ref: '#/components/schemas/userentity/createUserEntityRequest' responses: - 201: + '201': description: OK. Created content: application.json: @@ -1532,32 +1709,37 @@ paths: formsVersion: [] correlation: 6191e4d4-438b-4609-8de6-da3694b506b1 meeting_platform: BBB - 400: + '400': description: Bad Request. User Entity already exists content: application/json: schema: - '$ref': '#/components/schemas/userentity/createUserEntity400Response' - '/user/v1/entity/update/{id}': + $ref: '#/components/schemas/userentity/createUserEntity400Response' + /user/v1/entity/update/{id}: patch: summary: Update Entity tags: - Entity APIs - description: | + description: > This API is associated with updating an entity. + - Endpoint for updating an entity: `/user/v1/entity/update`. - - It is mandatory to provide values for parameters marked with `required`. + + - It is mandatory to provide values for parameters marked with + `required`. + - Mandatory parameters cannot be empty or null. parameters: - name: X-auth-token in: header - description: To make use of the API, you require X-auth-token. This is Available + description: >- + To make use of the API, you require X-auth-token. This is Available in login API Response. required: true schema: type: string - in: path - name: 'id' + name: id required: true description: Please append a valid user entity ID to the request URL. schema: @@ -1671,21 +1853,28 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/userentity/updateUserEntity400Response' - '/user/v1/entity/read?id={id}': + $ref: '#/components/schemas/userentity/updateUserEntity400Response' + /user/v1/entity/read?id={id}: get: summary: Read User Entity tags: - Entity APIs - description: | - This API is associated with retrieving a user entity based on ID or value. + description: > + This API is associated with retrieving a user entity based on ID or + value. + - Endpoint for retrieving a user entity: `/user/v1/entity/read`. - - It is mandatory to provide values for parameters marked with `required`. + + - It is mandatory to provide values for parameters marked with + `required`. + - Mandatory parameters cannot be empty or null. parameters: - name: X-auth-token in: header - description: To use the API, include X-auth-token. This is available in the login API response. + description: >- + To use the API, include X-auth-token. This is available in the login + API response. required: true schema: type: string @@ -1702,30 +1891,35 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/userentity/readUserEntity200Response' - - '/user/v1/entity/delete/{id}': + $ref: '#/components/schemas/userentity/readUserEntity200Response' + /user/v1/entity/delete/{id}: delete: summary: Delete Entity - tags: - - Entity APIs - description: | + tags: [] + description: > This API is associated with deleting an entity. + - Endpoint for deleting a user entity: `/user/v1/entity/delete`. - - It is mandatory to provide values for parameters marked with `required`. + + - It is mandatory to provide values for parameters marked with + `required`. + - Mandatory parameters cannot be empty or null. parameters: - - name: X-auth-token + - &ref_4 + name: X-auth-token in: header - description: To use the API, include X-auth-token. This is available in the login API response. + description: >- + To use the API, include X-auth-token. This is available in the login + API response. required: true schema: type: string - in: path - name: 'id' + name: id required: true description: Please append a valid user entity ID to the request URL. - schema: + schema: &ref_5 type: integer example: 31 responses: @@ -1734,21 +1928,24 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/userentity/deleteUserEntity202Response' + $ref: '#/components/schemas/userentity/deleteUserEntity202Response' '400': description: Bad Request. User Entity Already Deleted content: application/json: schema: - '$ref': '#/components/schemas/userentity/deleteUserEntity400Response' - - '/user/v1/org-admin/getRequests': + $ref: '#/components/schemas/userentity/deleteUserEntity400Response' + /user/v1/org-admin/getRequests: post: summary: Get Requests - description: | + description: > Get a list of organization requests based on specified filters + - Endpoint: `/user/v1/org-admin/getRequests`. - - It is mandatory to provide values for parameters marked with `required`. + + - It is mandatory to provide values for parameters marked with + `required`. + - Mandatory parameters cannot be empty or null. tags: - Org Admin APIs @@ -1835,13 +2032,17 @@ paths: name: Jake email: jake@gmail.com count: 12 - '/user/v1/org-admin/updateRequestStatus': + /user/v1/org-admin/updateRequestStatus: post: summary: Update Request Status - description: | + description: > Update the status of an organization request. + - Endpoint: `/user/v1/org-admin/updateRequestStatus`. - - It is mandatory to provide values for parameters marked with `required`. + + - It is mandatory to provide values for parameters marked with + `required`. + - Mandatory parameters cannot be empty or null. tags: - Org Admin APIs @@ -1871,7 +2072,10 @@ paths: description: Comments related to the request status update. status: type: string - enum: [APPROVED, REJECTED, UNDER_REVIEW] + enum: + - APPROVED + - REJECTED + - UNDER_REVIEW example: APPROVED description: The new status for the organization request. responses: @@ -1891,14 +2095,17 @@ paths: requester: id: 23 name: Jake - - '/user/v1/org-admin/getRequestDetails': + /user/v1/org-admin/getRequestDetails: get: summary: Get Request Details - description: | + description: > Get details of a specific organization request. + - Endpoint: `/user/v1/org-admin/getRequestDetails`. - - It is mandatory to provide values for parameters marked with `required`. + + - It is mandatory to provide values for parameters marked with + `required`. + - Mandatory parameters cannot be empty or null. tags: - Org Admin APIs @@ -1941,23 +2148,25 @@ paths: - Temp 1 - Temp 2 meta: {} - - '/user/v1/org-admin/bulkUserCreate': + /user/v1/org-admin/bulkUserCreate: post: summary: Bulk User Create - description: | + description: > Create multiple users in bulk using a CSV file. + - Endpoint: `/user/v1/org-admin/bulkUserCreate`. - - It is mandatory to provide values for parameters marked with `required`. + + - It is mandatory to provide values for parameters marked with + `required`. + - Mandatory parameters cannot be empty or null. - tags: - - Org Admin APIs + tags: [] parameters: - name: X-auth-token in: header description: Access token of the org admin. required: true - schema: + schema: &ref_2 type: string requestBody: required: true @@ -1986,14 +2195,17 @@ paths: type: text/csv updated_at: '2023-09-20T12:00:46.533Z' created_at: '2023-09-20T12:00:46.533Z' - - '/user/v1/org-admin/getBulkInvitesFilesList': + /user/v1/org-admin/getBulkInvitesFilesList: get: summary: Get Bulk Invite Files List - description: | + description: > Get a list of uploaded bulk invite CSV files. + - Endpoint: `/user/v1/org-admin/getBulkInvitesFilesList`. - - It is mandatory to provide values for parameters marked with `required`. + + - It is mandatory to provide values for parameters marked with + `required`. + - Mandatory parameters cannot be empty or null. tags: - Org Admin APIs @@ -2022,7 +2234,10 @@ paths: required: true schema: type: string - enum: [uploaded, processed, failed] + enum: + - uploaded + - processed + - failed example: uploaded responses: '200': @@ -2049,14 +2264,17 @@ paths: type: text/csv output_path: count: 2 - - '/user/v1/org-admin/deactivateUser/{id}': + /user/v1/org-admin/deactivateUser/{id}: post: summary: Deactivate User - description: | + description: > Deactivate a user + - Endpoint: `/user/v1/org-admin/deactivateUser`. - - It is mandatory to provide values for parameters marked with `required`. + + - It is mandatory to provide values for parameters marked with + `required`. + - Mandatory parameters cannot be empty or null. tags: - Org Admin APIs @@ -2080,21 +2298,24 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/admin/deactivateUser202Response' + $ref: '#/components/schemas/admin/deactivateUser202Response' '400': description: Bad Request. User Entity Already Deleted content: application/json: schema: - '$ref': '#/components/schemas/admin/deactivateUser400Response' - - '/user/v1/org-admin/inheritEntityType': + $ref: '#/components/schemas/admin/deactivateUser400Response' + /user/v1/org-admin/inheritEntityType: post: summary: Inherit Entity Type - description: | + description: > Inherit entity type. + - Endpoint: `/user/v1/org-admin/inheritEntityType`. - - It is mandatory to provide values for parameters marked with `required`. + + - It is mandatory to provide values for parameters marked with + `required`. + - Mandatory parameters cannot be empty or null. tags: - Org Admin APIs @@ -2109,35 +2330,41 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/org-admin/inheritEntityTypeRequest' + $ref: '#/components/schemas/org-admin/inheritEntityTypeRequest' responses: '202': description: Accepted content: application/json: schema: - '$ref': '#/components/schemas/org-admin/inheritEntityType202Response' + $ref: '#/components/schemas/org-admin/inheritEntityType202Response' '400': description: Bad Request. User Entity Already Deleted content: application/json: schema: - '$ref': '#/components/schemas/notification/createNotification400Response' - - '/user/v1/notification/template': + $ref: >- + #/components/schemas/notification/createNotification400Response + /user/v1/notification/template: post: summary: Create, Update, and Delete Notification tags: - Notification APIs - description: | + description: > This API is associated with notification create or update. + - Endpoint: `/user/v1/notification/template`. - - It is mandatory to provide values for parameters marked with `required`. + + - It is mandatory to provide values for parameters marked with + `required`. + - Mandatory parameters cannot be empty or null. parameters: - name: X-auth-token in: header - description: Access token required to use the API. Available in the login API response. + description: >- + Access token required to use the API. Available in the login API + response. required: true schema: type: string @@ -2145,21 +2372,263 @@ paths: content: application/json: schema: - '$ref': '#/components/schemas/notification/createNotificationRequest' + $ref: '#/components/schemas/notification/createNotificationRequest' responses: '202': description: Accepted content: application/json: schema: - '$ref': '#/components/schemas/notification/createNotification200Response' + $ref: >- + #/components/schemas/notification/createNotification200Response '400': description: Bad Request. content: application/json: schema: - '$ref': '#/components/schemas/notification/createNotification400Response' + $ref: >- + #/components/schemas/notification/createNotification400Response + /user/v1/userRole/Create: + post: + summary: Create User Role + description: > + Create roles for users. + + - Endpoint: `/user/v1/userRole/Create`. + + - It is mandatory to provide values for parameters marked with + `required`. + + - Mandatory parameters cannot be empty or null. + tags: + - User Roles APIs + parameters: + - name: X-auth-token + in: header + description: Access token of the user + required: true + schema: *ref_2 + requestBody: + required: true + content: + application/json: + schema: + $schema: http://json-schema.org/draft-04/schema# + type: object + properties: + title: + type: string + user_type: + type: number + status: + type: string + visibility: + type: string + required: + - user_type + - status + - title + - visibility + examples: + example1: + value: + title: system_admin + user_type: 1 + status: ACTIVE + visibility: PUBLIC + responses: + '201': + description: OK + content: + application/json: + schema: + $schema: http://json-schema.org/draft-04/schema# + type: object + properties: + responseCode: + type: string + message: + type: string + result: + type: object + properties: + title: + type: string + user_type: + type: number + status: + type: string + visibility: + type: string + organization_id: + type: number + meta: + type: object + properties: + formsVersion: + type: array + items: + type: string + correlation: + type: string + examples: + example1: + value: + responseCode: OK + message: Roles added successfully + result: + title: system_admin + user_type: 1 + status: ACTIVE + visibility: PUBLIC + organization_id: 1 + meta: + formsVersion: [] + correlation: 483b441a-c4c2-4c71-9aae-8dcf2d7ee5e8 + /user/v1/userRole/update/{id}: + post: + summary: Update User Roles + tags: + - User Roles APIs + description: | + Use this API to update an user Role. + + - Endpoint: `/user/v1/userRole/update/{id}` + - Provide values for parameters marked as `required`. + - The X-auth-token is mandatory and cannot be empty or null. + parameters: *ref_3 + requestBody: + content: + application/json: + schema: + $schema: http://json-schema.org/draft-04/schema# + type: object + properties: + title: + type: string + user_type: + type: number + status: + type: string + visibility: + type: string + required: + - title + - user_type + - status + - visibility + examples: + example1: + value: + title: system_ + user_type: 1 + status: ACTIVE + visibility: PUBLIC + responses: + '201': + description: OK. Roles updated successfully + content: + application/json: + schema: + $schema: http://json-schema.org/draft-04/schema# + type: object + properties: + responseCode: + type: string + message: + type: string + result: + type: object + properties: + title: + type: string + user_type: + type: number + status: + type: string + visibility: + type: string + organization_id: + type: number + meta: + type: object + properties: + formsVersion: + type: array + items: + type: string + correlation: + type: string + examples: + example1: + value: + responseCode: OK + message: Roles updated successfully + result: + title: system_ + user_type: 1 + status: ACTIVE + visibility: PUBLIC + organization_id: 3 + meta: + formsVersion: [] + correlation: 7672e4b5-2fa7-4022-a18b-cd574898e590 + /user/v1/userRole/delete/{id}: + delete: + summary: Delete User Role + tags: + - User Roles APIs + description: > + This API is associated with deleting an role. + + - Endpoint for deleting a user role: `/user/v1/userRole/delete/{id}`. + + - It is mandatory to provide values for parameters marked with + `required`. + - Mandatory parameters cannot be empty or null. + parameters: + - *ref_4 + - in: path + name: id + required: true + description: Please append a valid user role ID to the request URL. + schema: *ref_5 + example: 31 + responses: + '202': + description: OK. Accepted + content: + application/json: + schema: + $schema: http://json-schema.org/draft-04/schema# + type: object + properties: + responseCode: + type: string + message: + type: string + result: + type: object + properties: {} + meta: + type: object + properties: + formsVersion: + type: array + items: + type: string + correlation: + type: string + examples: + example1: + value: + responseCode: OK + message: Module deleted successfully + result: {} + meta: + formsVersion: [] + correlation: 645c901e-a7db-49a3-8d4c-dfc4f0b6dadb components: schemas: user: @@ -2196,7 +2665,9 @@ components: example: OK message: type: string - example: OTP has been sent to your registered email ID. Please enter the otp to complete the registration process. + example: >- + OTP has been sent to your registered email ID. Please enter the + otp to complete the registration process. result: type: array items: @@ -2208,13 +2679,13 @@ components: properties: name: type: string - example: 'Anu' + example: Anu email: type: string - example: 'anu@gmail.com' + example: anu@gmail.com password: type: string - example: 'password' + example: password userCreateUserRequest: description: User program and solution request type: object @@ -2265,10 +2736,10 @@ components: properties: access_token: type: string - example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' + example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... refresh_token: type: string - example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' + example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... user: type: object properties: @@ -2385,10 +2856,12 @@ components: properties: access_token: type: string - example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7ImlkIjo3LCJlbWFpbCI6ImhpbGJlcnQyOUB5YWhvby5jb20iLCJuYW1lIjoiQWRlbGlhIiwicm9sZXMiOlt7ImlkIjoyLCJ0aXRsZSI6Im1lbnRvciIsInVzZXJfdHlwZSI6MCwic3RhdHVzIjoiYWN0aXZlIn1dfSwiaWF0IjoxNjkwMjg2Nzg2LCJleHAiOjE2OTAzNzMxODZ9.0_Yi8yEu_G04VNJvVvafWMvO-Eb9TVsoCnp_pfrFVi8 + example: >- + eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7ImlkIjo3LCJlbWFpbCI6ImhpbGJlcnQyOUB5YWhvby5jb20iLCJuYW1lIjoiQWRlbGlhIiwicm9sZXMiOlt7ImlkIjoyLCJ0aXRsZSI6Im1lbnRvciIsInVzZXJfdHlwZSI6MCwic3RhdHVzIjoiYWN0aXZlIn1dfSwiaWF0IjoxNjkwMjg2Nzg2LCJleHAiOjE2OTAzNzMxODZ9.0_Yi8yEu_G04VNJvVvafWMvO-Eb9TVsoCnp_pfrFVi8 refresh_token: type: string - example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7ImlkIjo3LCJlbWFpbCI6ImhpbGJlcnQyOUB5YWhvby5jb20iLCJuYW1lIjoiQWRlbGlhIiwicm9sZXMiOlt7ImlkIjoyLCJ0aXRsZSI6Im1lbnRvciIsInVzZXJfdHlwZSI6MCwic3RhdHVzIjoiYWN0aXZlIn1dfSwiaWF0IjoxNjkwMjg2Nzg2LCJleHAiOjE3MDYwOTc5ODZ9.quHyyjz4DEc7rvKpxwzaiRyRKFkqrbTb8tNB5zX0eF0 + example: >- + eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7ImlkIjo3LCJlbWFpbCI6ImhpbGJlcnQyOUB5YWhvby5jb20iLCJuYW1lIjoiQWRlbGlhIiwicm9sZXMiOlt7ImlkIjoyLCJ0aXRsZSI6Im1lbnRvciIsInVzZXJfdHlwZSI6MCwic3RhdHVzIjoiYWN0aXZlIn1dfSwiaWF0IjoxNjkwMjg2Nzg2LCJleHAiOjE3MDYwOTc5ODZ9.quHyyjz4DEc7rvKpxwzaiRyRKFkqrbTb8tNB5zX0eF0 user: type: object properties: @@ -2436,7 +2909,7 @@ components: last_logged_in_at: type: string format: date-time - example: 2023-07-25T11:50:35.044Z + example: '2023-07-25T11:50:35.044Z' has_accepted_terms_and_conditions: type: boolean example: true @@ -2479,7 +2952,8 @@ components: type: array items: type: number - example: [2] + example: + - 2 image: type: string example: https://cloudstorage.com/container/abc.png @@ -2488,10 +2962,10 @@ components: example: null created_at: type: string - example: 2023-07-24T14:37:56.393Z + example: '2023-07-24T14:37:56.393Z' updated_at: type: string - example: 2023-07-25T11:50:35.044Z + example: '2023-07-25T11:50:35.044Z' user_roles: type: array items: @@ -2563,7 +3037,8 @@ components: refresh_token: type: string description: Refresh token. - example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7Il9pZCI6IjYyODMyNTMxYTA1Y2JkNTdiMjczYWViYiIsImVtYWlsIjoiYW5raXQuc0BwYWNld2lzZG9tLmNvbSIsIm5hbWUiOiJBbmtpdCIsImlzQU1lbnRvciI6dHJ1ZX0sImlhdCI6MTY1MzAxMDYxNCwiZXhwIjoxNjY4ODIxODE0fQ.hrIiKz3envHDPauVXSmA_BO_KKzWJAePsP3xD8l851s + example: >- + eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7Il9pZCI6IjYyODMyNTMxYTA1Y2JkNTdiMjczYWViYiIsImVtYWlsIjoiYW5raXQuc0BwYWNld2lzZG9tLmNvbSIsIm5hbWUiOiJBbmtpdCIsImlzQU1lbnRvciI6dHJ1ZX0sImlhdCI6MTY1MzAxMDYxNCwiZXhwIjoxNjY4ODIxODE0fQ.hrIiKz3envHDPauVXSmA_BO_KKzWJAePsP3xD8l851s generateTokenResponse200: description: OK. Access token has been generated successfully. type: object @@ -2579,7 +3054,8 @@ components: properties: access_token: type: string - example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7Il9pZCI6IjYyODMyNTMxYTA1Y2JkNTdiMjczYWViYiIsImVtYWlsIjoiYW5raXQuc0BwYWNld2lzZG9tLmNvbSIsIm5hbWUiOiJBbmtpdCIsImlzQU1lbnRvciI6dHJ1ZX0sImlhdCI6MTY1MzAxMDk4NCwiZXhwIjoxNjUzMDk3Mzg0fQ.BOZ_d6xNxuAJZ1ubdj94mCeO4jwXsC7t9JT4KXECVN + example: >- + eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7Il9pZCI6IjYyODMyNTMxYTA1Y2JkNTdiMjczYWViYiIsImVtYWlsIjoiYW5raXQuc0BwYWNld2lzZG9tLmNvbSIsIm5hbWUiOiJBbmtpdCIsImlzQU1lbnRvciI6dHJ1ZX0sImlhdCI6MTY1MzAxMDk4NCwiZXhwIjoxNjUzMDk3Mzg0fQ.BOZ_d6xNxuAJZ1ubdj94mCeO4jwXsC7t9JT4KXECVN generateTokenResponse400: description: Bad Request type: object @@ -2649,7 +3125,9 @@ components: example: OK message: type: string - example: OTP has been sent to your registered email ID. Please enter the number to update your password. + example: >- + OTP has been sent to your registered email ID. Please enter the + number to update your password. result: type: array items: @@ -2720,10 +3198,12 @@ components: properties: access_token: type: string - example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7ImlkIjo5LCJlbWFpbCI6ImJyYW5keW4udGVycnlAeWFob28uY29tIiwibmFtZSI6IkFzaGx5Iiwicm9sZXMiOlt7ImlkIjoyLCJ0aXRsZSI6Im1lbnRvciIsInVzZXJfdHlwZSI6MCwic3RhdHVzIjoiYWN0aXZlIn1dfSwiaWF0IjoxNjkwMjg1NzE4LCJleHAiOjE2OTAzNzIxMTh9.WmhUXIocykrBVti7vtznx_BMAaBtuqYUks4sc1J94Do + example: >- + eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7ImlkIjo5LCJlbWFpbCI6ImJyYW5keW4udGVycnlAeWFob28uY29tIiwibmFtZSI6IkFzaGx5Iiwicm9sZXMiOlt7ImlkIjoyLCJ0aXRsZSI6Im1lbnRvciIsInVzZXJfdHlwZSI6MCwic3RhdHVzIjoiYWN0aXZlIn1dfSwiaWF0IjoxNjkwMjg1NzE4LCJleHAiOjE2OTAzNzIxMTh9.WmhUXIocykrBVti7vtznx_BMAaBtuqYUks4sc1J94Do refresh_token: type: string - example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7ImlkIjo5LCJlbWFpbCI6ImJyYW5keW4udGVycnlAeWFob28uY29tIiwibmFtZSI6IkFzaGx5Iiwicm9sZXMiOlt7ImlkIjoyLCJ0aXRsZSI6Im1lbnRvciIsInVzZXJfdHlwZSI6MCwic3RhdHVzIjoiYWN0aXZlIn1dfSwiaWF0IjoxNjkwMjg1NzE4LCJleHAiOjE3MDYwOTY5MTh9.eqJ-KD0YA-Cdpz3XHWKgmi66uU6o229K9pyp6BwAAzI + example: >- + eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7ImlkIjo5LCJlbWFpbCI6ImJyYW5keW4udGVycnlAeWFob28uY29tIiwibmFtZSI6IkFzaGx5Iiwicm9sZXMiOlt7ImlkIjoyLCJ0aXRsZSI6Im1lbnRvciIsInVzZXJfdHlwZSI6MCwic3RhdHVzIjoiYWN0aXZlIn1dfSwiaWF0IjoxNjkwMjg1NzE4LCJleHAiOjE3MDYwOTY5MTh9.eqJ-KD0YA-Cdpz3XHWKgmi66uU6o229K9pyp6BwAAzI user: type: object properties: @@ -2787,10 +3267,10 @@ components: example: null created_at: type: string - example: 2023-07-25T11:48:38.270Z + example: '2023-07-25T11:48:38.270Z' updated_at: type: string - example: 2023-07-25T11:48:38.270Z + example: '2023-07-25T11:48:38.270Z' user_roles: type: array items: @@ -2847,8 +3327,8 @@ components: refresh_token: type: string description: Refresh token. - example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7Il9pZCI6IjYyODMyNTMxYTA1Y2JkNTdiMjczYWViYiIsImVtYWlsIjoiYW5raXQuc0BwYWNld2lzZG9tLmNvbSIsIm5hbWUiOiJBbmtpdCIsImlzQU1lbnRvciI6dHJ1ZX0sImlhdCI6MTY1MzAxMDYxNCwiZXhwIjoxNjY4ODIxODE0fQ.hrIiKz3envHDPauVXSmA_BO_KKzWJAePsP3xD8l851s - + example: >- + eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7Il9pZCI6IjYyODMyNTMxYTA1Y2JkNTdiMjczYWViYiIsImVtYWlsIjoiYW5raXQuc0BwYWNld2lzZG9tLmNvbSIsIm5hbWUiOiJBbmtpdCIsImlzQU1lbnRvciI6dHJ1ZX0sImlhdCI6MTY1MzAxMDYxNCwiZXhwIjoxNjY4ODIxODE0fQ.hrIiKz3envHDPauVXSmA_BO_KKzWJAePsP3xD8l851s logoutResponse200: description: OK, User logout successfully type: object @@ -2928,11 +3408,11 @@ components: example: jhon image: type: string - example: 'https://aws-bucket-storage-name.s3.ap-south-1.amazonaws.com/https://cloudstorage.com/container/abc.png' + example: >- + https://aws-bucket-storage-name.s3.ap-south-1.amazonaws.com/https://cloudstorage.com/container/abc.png count: type: number example: 1 - profile: profileDetailsResponse200: type: object @@ -3057,7 +3537,8 @@ components: items: type: string description: The user's location(s). - example: ['ap'] + example: + - ap about: type: string description: A brief description about the user. @@ -3175,7 +3656,6 @@ components: items: type: string example: [] - form: createFormRequest: type: object @@ -3473,7 +3953,6 @@ components: items: type: string example: [] - cloudServices: getSampleCSVResponse200: type: object @@ -3486,7 +3965,8 @@ components: example: Download Url Generated Successfully. result: type: string - example: https://mentoring-dev-storage.s3.ap-south-1.amazonaws.com/sample/bulk_user_creation.csv + example: >- + https://mentoring-dev-storage.s3.ap-south-1.amazonaws.com/sample/bulk_user_creation.csv cloudServicesResponse200: type: object properties: @@ -3501,7 +3981,8 @@ components: properties: signed_url: type: string - example: https://aws-bucket-storage-name.s3.ap-south-1.amazonaws.com/users/62832531a05cbd57b273aebb-1654149589875-laptop1.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=aws-access-key-id%2F20220602%2Fap-south-1%2Fs3%2Faws4_request&X-Amz-Date=20220602T055949Z&X-Amz-Expires=1800&X-Amz-Signature=0588b16fba45cb85efdc45749173c42ba26b47a9faa9bce5715c666b2657a4d4&X-Amz-SignedHeaders=host + example: >- + https://aws-bucket-storage-name.s3.ap-south-1.amazonaws.com/users/62832531a05cbd57b273aebb-1654149589875-laptop1.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=aws-access-key-id%2F20220602%2Fap-south-1%2Fs3%2Faws4_request&X-Amz-Date=20220602T055949Z&X-Amz-Expires=1800&X-Amz-Signature=0588b16fba45cb85efdc45749173c42ba26b47a9faa9bce5715c666b2657a4d4&X-Amz-SignedHeaders=host file_path: type: string example: users/62832531a05cbd57b273aebb-1654149589875-laptop1.jpg @@ -3519,8 +4000,8 @@ components: example: Download Url Generated Successfully. result: type: string - example: https://aws-bucket-storage-name.s3.ap-south-1.amazonaws.com/users/62832531a05cbd57b273aebb-1654149589875-image.jpg - + example: >- + https://aws-bucket-storage-name.s3.ap-south-1.amazonaws.com/users/62832531a05cbd57b273aebb-1654149589875-image.jpg admin: deactivateUser400Response: type: object @@ -3667,10 +4148,12 @@ components: properties: access_token: type: string - example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7Il9pZCI6IjYyOGQ4YjljNGRmOGFkM2ExM2QyODNlNyIsImVtYWlsIjoic3lzdGVtQGFkbWluLmNvbSIsInJvbGUiOiJhZG1pbiJ9LCJpYXQiOjE2NTM0NDQ2MDQsImV4cCI6MTY1MzUzMTAwNH0.l6I0MC16FegRLDym_VwgxFsa97ApUuOKohj2k49cV0I + example: >- + eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7Il9pZCI6IjYyOGQ4YjljNGRmOGFkM2ExM2QyODNlNyIsImVtYWlsIjoic3lzdGVtQGFkbWluLmNvbSIsInJvbGUiOiJhZG1pbiJ9LCJpYXQiOjE2NTM0NDQ2MDQsImV4cCI6MTY1MzUzMTAwNH0.l6I0MC16FegRLDym_VwgxFsa97ApUuOKohj2k49cV0I refresh_token: type: string - example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7Il9pZCI6IjYyOGQ4YjljNGRmOGFkM2ExM2QyODNlNyIsImVtYWlsIjoic3lzdGVtQGFkbWluLmNvbSIsInJvbGUiOiJhZG1pbiJ9LCJpYXQiOjE2NTM0NDQ2MDQsImV4cCI6MTY2OTI1NTgwNH0.TGDhpCVa0hTAbki8Dp5XGtQyqCMXu4Xu9L_72Yr4G1A + example: >- + eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7Il9pZCI6IjYyOGQ4YjljNGRmOGFkM2ExM2QyODNlNyIsImVtYWlsIjoic3lzdGVtQGFkbWluLmNvbSIsInJvbGUiOiJhZG1pbiJ9LCJpYXQiOjE2NTM0NDQ2MDQsImV4cCI6MTY2OTI1NTgwNH0.TGDhpCVa0hTAbki8Dp5XGtQyqCMXu4Xu9L_72Yr4G1A user: type: object properties: @@ -3709,7 +4192,7 @@ components: last_logged_in_at: type: string format: date-time - example: 2023-07-25T11:50:35.044Z + example: '2023-07-25T11:50:35.044Z' has_accepted_terms_and_conditions: type: boolean example: true @@ -3744,11 +4227,11 @@ components: created_at: type: string format: date-time - example: 2023-07-24T14:37:56.393Z + example: '2023-07-24T14:37:56.393Z' updated_at: type: string format: date-time - example: 2023-07-25T11:50:35.044Z + example: '2023-07-25T11:50:35.044Z' user_roles: type: array items: @@ -3814,7 +4297,6 @@ components: items: type: string example: [] - organization: requestOrgRole200Response: type: object @@ -3861,7 +4343,6 @@ components: type: string example: testing description: A brief description of the organization. - createOrganization200Response: type: object description: Organization created successfully @@ -3951,11 +4432,12 @@ components: example: sl description: type: string - example: Founded in 2017 ShikshaLokam is an Education Leadership Catalyst under the aegis of Advaith Foundation. + example: >- + Founded in 2017 ShikshaLokam is an Education Leadership + Catalyst under the aegis of Advaith Foundation. count: type: number example: 1 - entity-type: createEntityTypeRequest: type: object @@ -3984,7 +4466,6 @@ components: type: boolean description: Indicates whether the entity type has associated entities. example: STRING - updateEntityTypeRequest: type: object description: entity type update request @@ -4012,7 +4493,6 @@ components: type: boolean description: Indicates whether the entity type has associated entities. example: STRING - createEntityType201Response: type: object description: entity type request @@ -4049,10 +4529,10 @@ components: example: 15 updated_at: type: string - example: 2023-08-09T17:02:12.842Z + example: '2023-08-09T17:02:12.842Z' created_at: type: string - example: 2023-08-09T17:02:12.842Z + example: '2023-08-09T17:02:12.842Z' allow_filtering: type: string format: nullable @@ -4178,7 +4658,6 @@ components: items: type: string example: [] - userRole: userRoleListResponse200: type: object @@ -4210,15 +4689,14 @@ components: created_at: type: string format: date-time - example: 2023-08-07T11:27:23.560Z + example: '2023-08-07T11:27:23.560Z' updated_at: type: string format: date-time - example: 2023-08-07T11:27:23.560Z + example: '2023-08-07T11:27:23.560Z' deleted_at: type: date-time example: null - userentity: createUserEntityRequest: type: object @@ -4333,7 +4811,6 @@ components: deleted_at: type: string example: null - deleteUserEntity202Response: type: object description: Entity Deleted @@ -4439,11 +4916,11 @@ components: updated_at: type: string format: date-time - example: 2023-12-05T06:55:42.178Z + example: '2023-12-05T06:55:42.178Z' created_at: type: string format: date-time - example: 2023-12-05T06:55:42.178Z + example: '2023-12-05T06:55:42.178Z' updated_by: type: integer example: 9 @@ -4528,17 +5005,22 @@ components: created_at: type: string format: date-time - example: 2023-11-29T10:16:56.236Z + example: '2023-11-29T10:16:56.236Z' updated_at: type: string format: date-time - example: 2023-12-05T09:04:02.566Z + example: '2023-12-05T09:04:02.566Z' deleted_at: type: string example: null - - securitySchemes: + securitySchemes: null bearer: type: apiKey name: X-auth-token in: header +tags: + - name: User Roles APIs + description: '' + externalDocs: + description: '' + url: '' diff --git a/src/api-doc/index.html b/src/api-doc/index.html deleted file mode 100644 index fca524518..000000000 --- a/src/api-doc/index.html +++ /dev/null @@ -1,85725 +0,0 @@ - - - - - Elevate User - - - - - - - - - -
-
- -
-
- - - - - - - - -
-
-
-
-
-
-

- Elevate User - (1.0.0) -

-

- Download OpenAPI specification:Download -

-
-
-
- E-mail: - info@sunbird.org - - - Terms of Service -
-
-
-
-
-
    -
  • - The Users Service is a centralized Service to support other services. Apis - perform operations related to mentoring entities notification etc -
  • -
  • - The URL for Users API(s) is {context}/user/v1 - - Note: These resources can be used in other services -
  • -
-
-
-
-
-
-
-
-

- User APIs -

-
-
-
-
-
-
-

- Create user -

-
-
-

This Api is associated with Create User on mentoring

-
    -
  • - Then Endpoint for creating user /user/v1/account/create -
  • -
  • - It is mandatory to provide values for parameter marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
  • secretCode is required only for mentor
  • -
-
-
-
- Request Body schema: application/json -
-
-

Request body consist of metadata and accepts payload as JSON

-
- - - - - - - - - - - - - - - - - - - - - - - -
- name -
required
-
-
-
- string -
-
-
-

Name of the user

-
-
-
-
- email -
required
-
-
-
- string -
-
-
-

Email Id of user

-
-
-
-
- password -
required
-
-
-
- string - <password> - -
-
-
-

Password of user

-
-
-
-
- otp - -
-
- string -
-
-
-

Email OTP for verification

-
-
-
-
- has_accepted_terms_and_conditions - -
-
- boolean -
-
-
-

- Flag specifiying if user has accepted the terms & - conditions -

-
-
-
-
-
-

Responses

-
- -
-
- -
-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "name": - "John Deo", -
    -
  • -
  • -
    - "email": - "example@mail.com", -
    -
  • -
  • -
    - "password": - "Password", -
    -
  • -
  • -
    - "otp": - 6845975, -
    -
  • -
  • -
    - "has_accepted_terms_and_conditions": - true -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - - - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Sign-up successful Please - wait while logging in.", -
    -
  • -
  • -
    - "result": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Login -

-
-
-

This Api is associated with login user

-
    -
  • Then Endpoint for login user /user/v1/account/login
  • -
  • - It is mandatory to provide values for parameter marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
- Request Body schema: application/json -
-
- - - - - - - - - - - -
- email -
required
-
-
-
- string -
-
-
-

Email Id of user

-
-
-
-
- password -
required
-
-
-
- string -
-
-
-

Password of user

-
-
-
-
-
-

Responses

-
- -
-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "email": - "example@mail.com", -
    -
  • -
  • -
    - "password": - "Password" -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "User logged in - successfully.", -
    -
  • -
  • -
    - "result": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Generates access token. -

-
-
-

- This API is associated with re-login and generating access token from - refresh token -

-
    -
  • - Then Endpoint for generating access token - /user/v1/account/generateToken -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
- Request Body schema: application/json -
-
- - - - - - - -
- refresh_token - -
-
- string -
-
-
-

Refresh token.

-
-
-
-
-
-

Responses

-
- -
-
- -
-
- -
-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "refresh_token": - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7Il9pZCI6IjYyODMyNTMxYTA1Y2JkNTdiMjczYWViYiIsImVtYWlsIjoiYW5raXQuc0BwYWNld2lzZG9tLmNvbSIsIm5hbWUiOiJBbmtpdCIsImlzQU1lbnRvciI6dHJ1ZX0sImlhdCI6MTY1MzAxMDYxNCwiZXhwIjoxNjY4ODIxODE0fQ.hrIiKz3envHDPauVXSmA_BO_KKzWJAePsP3xD8l851s" -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - - - - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Access token generated - successfully", -
    -
  • -
  • -
    - "result": - { -
      -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Generate OTP -

-
-
-

This API is associated with generating OTP

-
    -
  • - Then Endpoint for generating OTP - user/v1/account/generateOtp -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
- Request Body schema: application/json -
-
- - - - - - - - - - - -
- email -
required
-
-
-
- string -
-
-
-

Email Id of user

-
-
-
-
- password -
required
-
-
-
- string -
-
-
-

Password of user

-
-
-
-
-
-

Responses

-
- -
-
- -
-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "email": - "example@mail.com", -
    -
  • -
  • -
    - "password": - "Password" -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - - - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "OTP has been sent to your - registered email ID. Please enter - the number to update your - password.", -
    -
  • -
  • -
    - "result": - [ ] -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Reset password -

-
-
-

This API is associated with reset password

-
    -
  • - Then Endpoint for reset password - user/v1/account/resetPassword -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
- Request Body schema: application/json -
-
- - - - - - - - - - - - - - - -
- email -
required
-
-
-
- string -
-
-
-

Email Id of user

-
-
-
-
- password -
required
-
-
-
- string -
-
-
-

New password of user

-
-
-
-
- otp -
required
-
-
-
- number -
-
-
-

OTP sent on mail

-
-
-
-
-
-

Responses

-
- -
-
- -
-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "email": - "example@mail.com", -
    -
  • -
  • -
    - "password": - "New password", -
    -
  • -
  • -
    - "otp": - 123456 -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - - - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Password reset - successfully.", -
    -
  • -
  • -
    - "result": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Logout -

-
-
-

This API is associated with logout

-
    -
  • Then Endpoint for logout /user/v1/account/logout
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
- Request Body schema: application/json -
-
- - - - - - - -
- refresh_token - -
-
- string -
-
-
-

Refresh token.

-
-
-
-
-
-

Responses

-
- -
-
- -
-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "refresh_token": - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7Il9pZCI6IjYyODMyNTMxYTA1Y2JkNTdiMjczYWViYiIsImVtYWlsIjoiYW5raXQuc0BwYWNld2lzZG9tLmNvbSIsIm5hbWUiOiJBbmtpdCIsImlzQU1lbnRvciI6dHJ1ZX0sImlhdCI6MTY1MzAxMDYxNCwiZXhwIjoxNjY4ODIxODE0fQ.hrIiKz3envHDPauVXSmA_BO_KKzWJAePsP3xD8l851s" -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - - - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "User logged out - successfully.", -
    -
  • -
  • -
    - "result": - [ ] -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Depreciated APIs -

-
-
-
-
-
-
-

- Terms & Condition -

-
-
-

- This API Requires only X-auth-token and it will set an email to verify true. -

-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
-

Responses

-
- -
-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "User successfully - updated.", -
    -
  • -
  • -
    - "result": - [ ] -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Users APIs -

-
-
-
-
-
-
-

- List user -

-
-
-

This API is associated with the listing of users based on condition

-
    -
  • Endpoint for list user /user/v1/account/list
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- query - Parameters -
- - - - - - - - - - - - - - - - - - - -
- userType -
required
-
-
-
- string -
-
- - Example: - - userType=mentor -
-
-
-

Type of user

-
-
-
-
- page - -
-
- number -
-
- - Example: - - page=1 -
-
-
-

Please add page number

-
-
-
-
- limit - -
-
- number -
-
- - Example: - - limit=2 -
-
-
-

number of record to limit

-
-
-
-
- search - -
-
- string -
-
- - Example: - - search=john -
-
-
-

Please search for name or title of user etc.

-
-
-
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { - - } -
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- User details -

-
-
-

This API is associated with the user's profile details

-
    -
  • - Then Endpoint for user profile details /user/v1/user/read/ -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- path - Parameters -
- - - - - - - -
- userId -
required
-
-
-
- integer -
-
- - Example: - - 1 -
-
-
-

Please append a valid User Id To the Request URL

-
-
-
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
-

Responses

-
- -
-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Profile fetched - successfully.", -
    -
  • -
  • -
    - "result": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Update user -

-
-
-

This API is associated with update user profile

-
    -
  • - Then Endpoint for update user profile /user/v1/user/update -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
- Request Body schema: application/json -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
- name - -
-
- string -
-
-
-
- location - -
-
- Array of strings -
-
-
-
- about - -
-
- string -
-
-
-
- has_accepted_terms_and_conditions - -
-
- boolean -
-
-
-
- gender - -
-
- string -
-
-
-
- image - -
-
- string -
-
-
-
-
-

Responses

-
- -
-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "name": - "Jhon", -
    -
  • -
  • -
    - "location": - [ -
      -
    • - -
    • -
    - ], -
    -
  • -
  • -
    - "about": - "This is test about of - mentee", -
    -
  • -
  • -
    - "has_accepted_terms_and_conditions": - true, -
    -
  • -
  • -
    - "gender": - "male", -
    -
  • -
  • - -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Profile updated - successfully.", -
    -
  • -
  • -
    - "result": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Share mentor profile -

-
-
-

This API is associated with Sharing mentor profile

-
    -
  • - The Endpoint for user profile details /user/v1/user/share/ -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- path - Parameters -
- - - - - - - -
- userId -
required
-
-
-
- string -
-
- - Example: - - 1 -
-
-
-

Please append a valid User Id To the Request URL

-
-
-
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
-

Responses

-
- -
-
- -
-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - - - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Profile share link generated - successfully.", -
    -
  • -
  • -
    - "result": - { -
      -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Cloud services APIs -

-
-
-
-
-
-
-

- Cloud services get signed url -

-
-
-

This API is associated with the cloud services for getting signed URL

-
    -
  • - Then Endpoint for cloud services - /user/v1/cloud-services/file/getSignedUrl -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- path - Parameters -
- - - - - - - -
- file_name -
required
-
-
-
- string -
-
- - Example: - - image.jpg -
-
-
-

- Please append a valid file name to url which has to - be uploaded -

-
-
-
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { - - } -
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Cloud services get download url -

-
-
-

- This API is associated with the cloud services for getting downloadable URL -

-
    -
  • - Then Endpoint for cloud services - /user/v1/cloud-services/file/getDownloadableUrl -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- path - Parameters -
- - - - - - - -
- file_path -
required
-
-
-
- string -
-
- - Example: - - users/62832531a05cbd57b273aebb-1654149589875-image.jpg -
-
-
-

- Please append a valid file path to url which has to - be downloaded -

-
-
-
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { - - } -
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Form APIs -

-
-
-
-
-
-
-

- Create form -

-
-
-

This API is associated with create form

-
    -
  • Then Endpoint for create form /user/v1/form/create
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
- Request Body schema: application/json -
-
- - - - - - - - - - - - - - - -
- type -
required
-
-
-
- string -
-
-
-
- sub_type -
required
-
-
-
- string -
-
-
-
- -
required
-
-
-
- object -
-
-
-
-
-

Responses

-
- -
-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "type": - "profile", -
    -
  • -
  • -
    - "sub_type": - "profileForm", -
    -
  • -
  • -
    - "data": - { -
      -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Form created - successfully", -
    -
  • -
  • -
    - "result": - [ ] -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Update form -

-
-
-

This API is associated with update form

-
    -
  • - Then Endpoint for update form /user/v1/form/update/{formId} -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- path - Parameters -
- - - - - - - -
- formId -
required
-
-
-
- string -
-
- - Example: - - 1 -
-
-
-

Please append a valid form Id to the Request URL.

-
-
-
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
- Request Body schema: application/json -
-
- - - - - - - - - - - - - - - -
- type -
required
-
-
-
- string -
-
-
-
- sub_type -
required
-
-
-
- string -
-
-
-
- -
required
-
-
-
- object -
-
-
-
-
-

Responses

-
- -
-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "type": - "profile", -
    -
  • -
  • -
    - "sub_type": - "profileForm", -
    -
  • -
  • -
    - "data": - { -
      -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Form updated - successfully", -
    -
  • -
  • -
    - "result": - [ ] -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Get form details -

-
-
-

This API is associated with update form

-
    -
  • - Then Endpoint for update form /user/v1/form/update/{formId} -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- path - Parameters -
- - - - - - - -
- formId -
required
-
-
-
- string -
-
- - Example: - - 62832531a05cbd57b273aebb -
-
-
-

Please append a valid form Id to the Request URL.

-
-
-
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
- Request Body schema: application/json -
-
- - - - - - - - - - - -
- type - -
-
- string -
-
-
-
- sub_type - -
-
- string -
-
-
-
-
-

Responses

-
- -
-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "type": - "profile", -
    -
  • -
  • -
    - "sub_type": - "profileForm" -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Form fetched - successfully", -
    -
  • -
  • -
    - "result": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Admin APIs -

-
-
-
-
-
-
-

- Create admin user -

-
-
-

API associated with creating new admin user

-
    -
  • - Then Endpoint for create system user /user/v1/admin/create -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
- Request Body schema: application/json -
-
- - - - - - - - - - - - - - - - - - - -
- name -
required
-
-
-
- string -
-
-
-
- email -
required
-
-
-
- string -
-
-
-
- password -
required
-
-
-
- string - <password> - -
-
-
-
- secret_code -
required
-
-
-
- string -
-
-
-
-
-

Responses

-
- -
-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "name": - "John Deo", -
    -
  • -
  • -
    - "email": - "systemuser@mail.com", -
    -
  • -
  • -
    - "password": - "password", -
    -
  • -
  • -
    - "secret_code": - "secret_code" -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Sign-up successful, Please - wait while logging in.", -
    -
  • -
  • -
    - "result": - [ -
      -
    • - -
    • -
    - ] -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Login admin user -

-
-
-

This API is associated with login system user

-
    -
  • - Then Endpoint for login system user - /user/v1/systemUsers/login -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
- Request Body schema: application/json -
-
- - - - - - - - - - - -
- email -
required
-
-
-
- string -
-
-
-
- password -
required
-
-
-
- string - <password> - -
-
-
-
-
-

Responses

-
- -
-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "email": - "systemuser@mail.com", -
    -
  • -
  • -
    - "password": - "password" -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "User logged in - successfully.", -
    -
  • -
  • -
    - "result": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Delete user -

-
-
-

This API is associated with delete user

-
    -
  • - Then Endpoint for delete user /user/v1/admin/deleteUser -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- path - Parameters -
- - - - - - - -
- id -
required
-
-
-
- string -
-
- - Example: - - 1 -
-
-
-

Please append a valid user id to the request url

-
-
-
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
-

Responses

-
- -
-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "User deleted - successfully", -
    -
  • -
  • -
    - "result": - [ -
      -
    • - -
    • -
    - ] -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Add Organization Admin -

-
-
-

This API is used to assign a user as the Org-Admin

-
    -
  • - Then Endpoint for delete user /user/v1/admin/addOrgAdmin -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
- Request Body schema: application/json -
-
- - - - - - - - - - - -
- user_id - -
-
- integer -
-
-
-
- org_id - -
-
- integer -
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "user_id": 12, -
    -
  • -
  • -
    - "org_id": 55 -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "User Assigned As The - Org-Admin Successfully", -
    -
  • -
  • -
    - "result": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Organization -

-
-
-
-
-
-
-

- Create Organization -

-
-
-

This API is associated with organization

-
    -
  • - The Endpoint for create organization - /user/v1/organization/create -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
- Request Body schema: application/json -
-
- - - - - - - - - - - - - - - -
- name -
required
-
-
-
- string -
-
-
-
- code -
required
-
-
-
- string -
-
-
-
- description -
required
-
-
-
- string -
-
-
-
-
-

Responses

-
- -
-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "name": - "MentorEd", -
    -
  • -
  • -
    - "code": - "mentor112", -
    -
  • -
  • -
    - "description": - "testing" -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Organization created - successfully", -
    -
  • -
  • -
    - "result": - [ ] -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Update Organization -

-
-
-

This API is associated with organization

-
    -
  • - The Endpoint for update organization - /user/v1/organization/update -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- path - Parameters -
- - - - - - - -
- id -
required
-
-
-
- integer -
-
- - Example: - - 1 -
-
-
-

Please append a valid user id to the request url

-
-
-
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
- Request Body schema: application/json -
-
- - - - - - - - - - - - - - - -
- name -
required
-
-
-
- string -
-
-
-
- code -
required
-
-
-
- string -
-
-
-
- description -
required
-
-
-
- string -
-
-
-
-
-

Responses

-
- -
-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "name": - "MentorEd", -
    -
  • -
  • -
    - "code": - "mentor112", -
    -
  • -
  • -
    - "description": - "testing" -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Organization updated - successfully", -
    -
  • -
  • -
    - "result": - [ ] -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- List Organization -

-
-
-

- This API is associated with the listing of organization based on condition -

-
    -
  • - Endpoint for list organization /user/v1/organization/list -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- path - Parameters -
- - - - - - - - - - - - - - - -
- page - -
-
- number -
-
- - Example: - - 1 -
-
-
-

Please add page number

-
-
-
-
- limit - -
-
- number -
-
- - Example: - - 2 -
-
-
-

number of record to limit

-
-
-
-
- search - -
-
- string -
-
- - Example: - - jhon -
-
-
-

Please search for name or title of user etc.

-
-
-
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Organization fetched - successfully.", -
    -
  • -
  • -
    - "result": - { -
      -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Request Org Role -

-
-
-

Request a specific role within an organization.

-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- User's access token which already recognizes the - user as the organization's mentee. -

-
-
-
-
-
-
- Request Body schema: application/json -
-
- - - - - - - - - - - -
- role - -
-
- string -
-
-
-
- - -
-
- object -
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "role": - "mentor", -
    -
  • -
  • -
    - "form_data": - { -
      -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Mentor role requested - successfully", -
    -
  • -
  • -
    - "result": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Get User Org Role Requests -

-
-
-

- Get a list of organization role requests for a specific role. This api is - used by the user to get the request for role change -

-
-
-
-
- query - Parameters -
- - - - - - - - - - - - - - - -
- page -
required
-
-
-
- integer -
-
-
-

Page number for pagination.

-
-
-
-
- limit -
required
-
-
-
- integer -
-
-
-

Number of items per page.

-
-
-
-
- role -
required
-
-
-
- string -
-
-
-

- The specific role for which requests are being - fetched. -

-
-
-
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- Access token of the user who is already a mentee of - the organization. -

-
-
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Org Role Requests Fetched - Successfully", -
    -
  • -
  • -
    - "result": - { -
      -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- UserRole APIs -

-
-
-
-
-
-
-

- UserRole List -

-
-
-

This API is associated with the user's role details

-
    -
  • - Then Endpoint for user profile details - /user/v1/userRole/list -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
-

Responses

-
- -
-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "User Role list fetched - successfully.", -
    -
  • -
  • -
    - "result": - [ -
      -
    • - -
    • -
    - ] -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- EntityType APIs -

-
-
-
-
-
-
-

- Create entity type -

-
-
-

You can use this API to create a user entity

-
    -
  • - The API Endpoint for creating a user entity is - /mentoring/v1/entity-type/create -
  • -
  • - It is mandatory to provide values for parameters which are marked as - required -
  • -
  • This is a mandatory parameter and cannot be empty or null.
  • -
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- Access token of the user who is already a mentee of - the organization. -

-
-
-
-
-
-
- Request Body schema: application/json -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
- value - -
-
- string -
-
-
-
- label - -
-
- string -
-
-
-
- type - -
-
- string -
-
-
-
- allow_filtering - -
-
- boolean -
-
-
-
- data_type - -
-
- string -
-
-
-
- has_entities - -
-
- boolean -
-
-
-
-
-

Responses

-
- -
-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "value": - "ln", -
    -
  • -
  • -
    - "label": - "Languages", -
    -
  • -
  • -
    - "type": - "SYSTEM", -
    -
  • -
  • -
    - "allow_filtering": - true, -
    -
  • -
  • -
    - "data_type": - "string", -
    -
  • -
  • -
    - "has_entities": - "STRING" -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - - -
-
-
-
- Content type -
application.json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Entity type created - successfully", -
    -
  • -
  • -
    - "result": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - }, -
    -
  • -
  • -
    - "meta": - { -
      -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Update entity type -

-
-
-

This API is associated with entity type updation

-
    -
  • - Then Endpoint for create entity /user/v1/entity-type/update -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- path - Parameters -
- - - - - - - -
- id -
required
-
-
-
- integer -
-
- - Example: - - 1 -
-
-
-

Please append a valid user id to the request url

-
-
-
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
- Request Body schema: application/json -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
- value - -
-
- string -
-
-
-
- label - -
-
- string -
-
-
-
- status - -
-
- string -
-
-
-
- type - -
-
- string -
-
-
-
- allow_filtering - -
-
- boolean -
-
-
-
- data_type - -
-
- string -
-
-
-
-
-

Responses

-
- -
-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "value": - "roles", -
    -
  • -
  • -
    - "label": - "Roles", -
    -
  • -
  • -
    - "status": - "ACTIVE", -
    -
  • -
  • -
    - "type": - "SYSTEM", -
    -
  • -
  • -
    - "allow_filtering": - true, -
    -
  • -
  • -
    - "data_type": - "string" -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "string", -
    -
  • -
  • -
    - "message": - "string", -
    -
  • -
  • -
    - "result": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - }, -
    -
  • -
  • -
    - "meta": - { -
      -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Read entity type -

-
-
-

This API is associated with entity type list

-
    -
  • - Then Endpoint for create entity /user/v1/entity-type/read -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
- Request Body schema: application/json -
-
- - - - - - - - - - - -
- value - -
-
- Array of strings -
-
-
-
- read_user_entity - -
-
- boolean -
-
-
-
-
-

Responses

-
- -
-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "value": - [ -
      -
    • - -
    • -
    - ], -
    -
  • -
  • -
    - "read_user_entity": - false -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - - -
-
-
-
- Content type -
application.json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Entity type fetched - successfully", -
    -
  • -
  • -
    - "result": - [ -
      -
    • - -
    • -
    - ], -
    -
  • -
  • -
    - "meta": - { -
      -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Delete entity type -

-
-
-

This API is associated with delete entity

-
    -
  • - Then Endpoint for delete user entity - /user/v1/entity-type/delete -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- path - Parameters -
- - - - - - - -
- id -
required
-
-
-
- integer -
-
- - Example: - - 31 -
-
-
-

- Please append a valid user entity id to the request - url -

-
-
-
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
-

Responses

-
- -
-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Entity deleted - successfully", -
    -
  • -
  • -
    - "result": - [ ] -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Entity APIs -

-
-
-
-
-
-
-

- Create entity -

-
-
-

This API is associated with entity creation

-
    -
  • Then Endpoint for create entity /user/v1/entity/create
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
- Request Body schema: application/json -
-
- - - - - - - - - - - - - - - - - - - -
- value -
required
-
-
-
- string -
-
-
-
- label -
required
-
-
-
- string -
-
-
-
- type -
required
-
-
-
- string -
-
-
-
- entity_type_id -
required
-
-
-
- integer -
-
-
-
-
-

Responses

-
- -
-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "value": - "AP", -
    -
  • -
  • -
    - "label": - "Andhra Pradesh", -
    -
  • -
  • -
    - "type": - "string", -
    -
  • -
  • -
    - "entity_type_id": 4 -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - - -
-
-
-
- Content type -
application.json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Entity created - successfully", -
    -
  • -
  • -
    - "result": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - }, -
    -
  • -
  • -
    - "meta": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Update entity -

-
-
-

This API is associated with update entity

-
    -
  • Then Endpoint for update entity /user/v1/entity/update
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- path - Parameters -
- - - - - - - -
- id -
required
-
-
-
- integer -
-
- - Example: - - 2 -
-
-
-

- Please append a valid user entity id to the request - url -

-
-
-
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
- Request Body schema: application.json -
-
- - - - - - - - - - - - - - - - - - - -
- value - -
-
- string -
-
-
-
- label - -
-
- string -
-
-
-
- status - -
-
- string -
-
-
-
- entity_type_id - -
-
- number -
-
-
-
-
-

Responses

-
- -
-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application.json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "value": - "en", -
    -
  • -
  • -
    - "label": - "English", -
    -
  • -
  • -
    - "status": - "ACTIVE", -
    -
  • -
  • -
    - "entity_type_id": 1 -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - - -
-
-
-
- Content type -
application.json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Entity updated - successfully", -
    -
  • -
  • -
    - "result": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - }, -
    -
  • -
  • -
    - "meta": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Read user entity -

-
-
-

This API is associated with getting user entity based on id or value

-
    -
  • - Then Endpoint for getting user entity /user/v1/entity/read -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- query - Parameters -
- - - - - - - -
- id -
required
-
-
-
- integer -
-
- - Example: - - id=1 -
-
-
-

Please append a valid id

-
-
-
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Entity fetched - successfully", -
    -
  • -
  • -
    - "result": - [ -
      -
    • - -
    • -
    - ] -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Delete entity -

-
-
-

This API is associated with delete entity

-
    -
  • - Then Endpoint for delete user entity /user/v1/entity/delete -
  • -
  • - It is mandatory to provide values for parameters marked with - required -
  • -
  • Mandatory parameter cannot be empty or null
  • -
-
-
-
-
- path - Parameters -
- - - - - - - -
- id -
required
-
-
-
- integer -
-
- - Example: - - 31 -
-
-
-

- Please append a valid user entity id to the request - url -

-
-
-
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

- To make use of the API, you require X-auth-token. - This is Available in login API Response. -

-
-
-
-
-
-
-

Responses

-
- -
-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Entity deleted - successfully", -
    -
  • -
  • -
    - "result": - [ ] -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Org Admin APIs -

-
-
-
-
-
-
-

- Get Requests -

-
-
-

Get a list of organization requests based on specified filters.

-
-
-
-
- query - Parameters -
- - - - - - - - - - - -
- page -
required
-
-
-
- integer -
-
-
-

Page number for pagination.

-
-
-
-
- limit -
required
-
-
-
- integer -
-
-
-

Number of items per page.

-
-
-
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

Access token of the org admin.

-
-
-
-
-
-
- Request Body schema: application/json -
-
- - - - - - - -
- - -
-
- object -
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "filters": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Organization requests fetched - successfully", -
    -
  • -
  • -
    - "result": - { -
      -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Update Request Status -

-
-
-

Update the status of an organization request.

-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

Access token of the org admin.

-
-
-
-
-
-
- Request Body schema: application/json -
-
- - - - - - - - - - - - - - - -
- request_id - -
-
- integer -
-
-
-
- comments - -
-
- Array of strings -
-
-
-
- status - -
-
- string -
-
- Enum: - "APPROVED" - "REJECTED" - "UNDER_REVIEW" -
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "request_id": 12, -
    -
  • -
  • -
    - "comments": - [ -
      -
    • - -
    • -
    - ], -
    -
  • -
  • -
    - "status": - "APPROVED" -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Successfully updated - organization request - status", -
    -
  • -
  • -
    - "result": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Get Request Details -

-
-
-

Get details of a specific organization request.

-
-
-
-
- query - Parameters -
- - - - - - - -
- request_id -
required
-
-
-
- integer -
-
- - Example: - - request_id=12 -
-
-
-

ID of the request to get details for.

-
-
-
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

Access token of the org admin.

-
-
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Organisation request details - fetched successfully", -
    -
  • -
  • -
    - "result": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Bulk User Create -

-
-
-

Create multiple users in bulk using a CSV file.

-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

Access token of the org admin.

-
-
-
-
-
-
- Request Body schema: application/json -
-
- - - - - - - -
- file_path - -
-
- string -
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "file_path": - "path/to/uploaded-csv-file" -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Bulk User Invites CSV - Uploaded Successfully", -
    -
  • -
  • -
    - "result": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Get Bulk Invite Files List -

-
-
-

Get a list of uploaded bulk invite CSV files.

-
-
-
-
- query - Parameters -
- - - - - - - - - - - - - - - -
- page -
required
-
-
-
- integer -
-
-
-

Page number for pagination.

-
-
-
-
- limit -
required
-
-
-
- integer -
-
-
-

Number of items per page.

-
-
-
-
- status -
required
-
-
-
- string -
-
- Enum: - "uploaded" - "processed" - "failed" -
-
- - Example: - - status=uploaded -
-
-
-

- Status of the bulk invite files - (uploaded/processed/failed). -

-
-
-
-
-
-
-
- header - Parameters -
- - - - - - - -
- X-auth-token -
required
-
-
-
- string -
-
-
-

Access token of the org admin.

-
-
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "responseCode": - "OK", -
    -
  • -
  • -
    - "message": - "Bulk Invites CSV List Fetched - Successfully", -
    -
  • -
  • -
    - "result": - { -
      -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - diff --git a/src/configs/index.js b/src/configs/index.js index dbf0e6e1c..dcca37b71 100644 --- a/src/configs/index.js +++ b/src/configs/index.js @@ -6,7 +6,6 @@ */ //Dependencies -require('./mongodb')() require('./kafka')() require('./cache')() require('./aes256cbc')() diff --git a/src/configs/kafka.js b/src/configs/kafka.js index 173d96467..6e4017d6c 100644 --- a/src/configs/kafka.js +++ b/src/configs/kafka.js @@ -36,7 +36,7 @@ module.exports = async () => { }) const subscribeToConsumer = async () => { - await consumer.subscribe({ topics: [process.env.RATING_KAFKA_TOPIC, process.env.CLEAR_INTERNAL_CACHE] }) + await consumer.subscribe({ topics: [process.env.CLEAR_INTERNAL_CACHE] }) await consumer.run({ eachMessage: async ({ topic, partition, message }) => { try { diff --git a/src/configs/mongodb.js b/src/configs/mongodb.js deleted file mode 100644 index 4e7dd4ea7..000000000 --- a/src/configs/mongodb.js +++ /dev/null @@ -1,56 +0,0 @@ -/** - * name : configs/mongodb - * author : Aman - * Date : 07-Oct-2021 - * Description : Mongodb connections configurations - */ - -//Dependencies -const mongoose = require('mongoose') -const mongoose_autopopulate = require('mongoose-autopopulate') -const mongoose_timestamp = require('mongoose-timestamp') - -const { elevateLog } = require('elevate-logger') -const logger = elevateLog.init() - -module.exports = function () { - // Added to remove depreciation warnings from logs. - - let parameters = '' - if (process.env.REPLICA_SET_NAME) { - parameters = '?replicaSet=' + process.env.REPLICA_SET_NAME - } - if (process.env.REPLICA_SET_NAME && process.env.REPLICA_SET_READ_PREFERENCE) { - parameters = parameters + '&readPreference=' + process.env.REPLICA_SET_READ_PREFERENCE - } - - let db - if (!parameters) { - db = mongoose.createConnection(process.env.MONGODB_URL, { - useNewUrlParser: true, - }) - } else { - db = mongoose.createConnection(process.env.MONGODB_URL + parameters, { - useNewUrlParser: true, - }) - } - - db.on('error', function () { - logger.error('Database connection error:', { - triggerNotification: true, - }) - }) - - db.once('open', function () { - logger.info('Connected to DB') - }) - - mongoose.plugin(mongoose_timestamp, { - createdAt: 'createdAt', - updatedAt: 'updatedAt', - }) - - mongoose.plugin(mongoose_autopopulate) - - global.db = db -} diff --git a/src/constants/common.js b/src/constants/common.js index 2b8ecf34c..82e7a1f45 100644 --- a/src/constants/common.js +++ b/src/constants/common.js @@ -31,6 +31,9 @@ const failureResponse = ({ message = 'Oops! Something Went Wrong.', statusCode = return error } +function getPaginationOffset(page, limit) { + return (page - 1) * limit +} module.exports = { pagination: { DEFAULT_PAGE_NO: 1, @@ -38,6 +41,7 @@ module.exports = { }, successResponse, failureResponse, + getPaginationOffset, guestUrls: [ '/user/v1/account/login', '/user/v1/account/create', @@ -46,7 +50,6 @@ module.exports = { '/user/v1/account/registrationOtp', '/user/v1/account/resetPassword', '/user/v1/admin/login', - '/user/v1/userRole/list', ], internalAccessUrls: [ '/user/v1/profile/details', @@ -58,6 +61,9 @@ module.exports = { '/user/v1/admin/triggerViewRebuildInternal', '/user/v1/admin/triggerPeriodicViewRefreshInternal', '/user/v1/account/search', + '/user/v1/organization/list', + '/user/v1/userRole/default', + '/user/v1/userRole/list', ], notificationEmailType: 'email', accessTokenExpiry: `${process.env.ACCESS_TOKEN_EXPIRY}d`, @@ -90,12 +96,12 @@ module.exports = { INACTIVE_STATUS: 'INACTIVE', MENTOR_ROLE: 'mentor', MENTEE_ROLE: 'mentee', + SESSION_MANAGER_ROLE: 'session_manager', redisUserPrefix: 'user_', redisOrgPrefix: 'org_', location: 'location', languages: 'languages', typeSystem: 'system', - ORG_ADMIN_ROLE: 'org_admin', UPLOADED_STATUS: 'UPLOADED', FAILED_STATUS: 'FAILED', PROCESSED_STATUS: 'PROCESSED', @@ -115,4 +121,6 @@ module.exports = { NO_OF_ATTEMPTS: 3, materializedViewsPrefix: 'm_', DELETED_STATUS: 'DELETED', + DEFAULT_ORG_VISIBILITY: 'PUBLIC', + ROLE_TYPE_NON_SYSTEM: 0, } diff --git a/src/constants/eventEndpoints.js b/src/constants/eventEndpoints.js index e47b5783b..366840630 100644 --- a/src/constants/eventEndpoints.js +++ b/src/constants/eventEndpoints.js @@ -28,5 +28,12 @@ module.exports = { route: '/mentoring/v1/org-admin/updateRelatedOrgs', }, ], + updateName: [ + { + method: 'POST', + baseUrl: `${process.env.MENTORING_SERVICE_URL}`, + route: '/mentoring/v1/sessions/bulkUpdateMentorNames', + }, + ], }, } diff --git a/src/controllers/v1/account.js b/src/controllers/v1/account.js index 227c6cc94..9ac9c015a 100644 --- a/src/controllers/v1/account.js +++ b/src/controllers/v1/account.js @@ -7,7 +7,6 @@ // Dependencies const accountService = require('@services/account') -const csv = require('csvtojson') module.exports = class Account { /** @@ -60,17 +59,18 @@ module.exports = class Account { * @name logout * @param {Object} req -request data. * @param {Object} req.decodedToken - it contains user token informations. - * @param {string} req.body.loggedInId - user id. * @param {string} req.body.refresh_token - refresh token. * @param {String} req.decodedToken.id - userId. * @returns {JSON} - accounts loggedout. */ async logout(req) { - const params = req.body - params.loggedInId = req.decodedToken.id try { - const loggedOutAccount = await accountService.logout(params) + const loggedOutAccount = await accountService.logout( + req.body, + req.decodedToken.id, + req.decodedToken.organization_id + ) return loggedOutAccount } catch (error) { return error @@ -87,9 +87,8 @@ module.exports = class Account { */ async generateToken(req) { - const params = req.body try { - const createdToken = await accountService.generateToken(params) + const createdToken = await accountService.generateToken(req.body) return createdToken } catch (error) { return error @@ -136,23 +135,6 @@ module.exports = class Account { } } - /** - * Bulk create mentors - * @method - * @name bulkCreateMentors - * @param {Object} req -request data. - * @returns {CSV} - created mentors. - */ - async bulkCreateMentors(req) { - try { - const mentors = await csv().fromString(req.files.mentors.data.toString()) - const createdMentors = await accountService.bulkCreateMentors(mentors, req.decodedToken) - return createdMentors - } catch (error) { - return error - } - } - /** * Accept term and condition * @method diff --git a/src/controllers/v1/entity-type.js b/src/controllers/v1/entity-type.js index 4478a0aab..bc8d9d197 100644 --- a/src/controllers/v1/entity-type.js +++ b/src/controllers/v1/entity-type.js @@ -72,7 +72,7 @@ module.exports = class Entity { async delete(req) { try { - return await entityTypeService.delete(req.params.id) + return await entityTypeService.delete(req.params.id, req.decodedToken.organization_id) } catch (error) { return error } diff --git a/src/controllers/v1/org-admin.js b/src/controllers/v1/org-admin.js index 3fef0adfa..484ea373a 100644 --- a/src/controllers/v1/org-admin.js +++ b/src/controllers/v1/org-admin.js @@ -55,7 +55,7 @@ module.exports = class OrgAdmin { }) } - const fileUploadList = await orgAdminService.getBulkInvitesFilesList(req) + const fileUploadList = await orgAdminService.getBulkInvitesFilesList(req) // TODO: Request Object shouldn't be allowed to leave controller layer. return fileUploadList } catch (error) { return error diff --git a/src/controllers/v1/userRole.js b/src/controllers/v1/userRole.js index 1447abe89..28c7cd4a2 100644 --- a/src/controllers/v1/userRole.js +++ b/src/controllers/v1/userRole.js @@ -10,16 +10,107 @@ const roleService = require('@services/userRole') module.exports = class userRole { /** - * list roles + * Create roles. * @method - * @name list - * @returns {JSON} - list of roles + * @name create + * @param {Object} req - Request data. + * @param {Object} req.body - Request body contains role creation details. + * @param {String} req.body.title - Title of the role. + * @param {Integer} req.body.userType - User type of the role. + * @param {String} req.body.status - Role status. + * @param {String} req.body.visibility - Visibility of the role. + * @param {Integer} req.body.organization_id - Organization ID for the role. + * @returns {JSON} - Response contains role creation details. */ + async create(req) { + try { + const createRole = await roleService.create(req.body, req.decodedToken.organization_id) + return createRole + } catch (error) { + return error + } + } + + /** + * Update roles. + * @method + * @name update + * @param {Object} req - Request data. + * @param {Object} req.body - Request body contains role update details. + * @param {String} req.body.title - Title of the role. + * @param {Integer} req.body.userType - User type of the role. + * @param {String} req.body.status - Role status. + * @param {String} req.body.visibility - Visibility of the role. + * @param {Integer} req.body.organization_id - Organization ID for the role. + * @returns {JSON} - Response contains role update details. + */ + + async update(req) { + try { + const updateRole = await roleService.update(req.params.id, req.body, req.decodedToken.organization_id) + return updateRole + } catch (error) { + return error + } + } + + /** + * Delete role. + * @method + * @name delete + * @param {Object} req - Request data. + * @returns {JSON} - Role deletion response. + */ + + async delete(req) { + try { + return await roleService.delete(req.params.id, req.decodedToken.organization_id) + } catch (error) { + return error + } + } + + /** + * Get all available roles. + * @method + * @name defaultlist + * @param {Array(String)} req.body.filters - Filters. + * @param {String} req.pageNo - Page number. + * @param {String} req.pageSize - Page size limit. + * @param {String} req.searchText - Search text. + * @param {Integer} req.decodedToken.organization_id - user organization_id. + * @returns {JSON} - Role list. + */ async list(req) { try { - const user = await roleService.list() - return user + const roleList = await roleService.list( + req.query, + req.pageNo, + req.pageSize, + req.searchText, + req.decodedToken.organization_id + ) + return roleList + } catch (error) { + return error + } + } + + /** + * Get all available roles. + * @method + * @name default + * @param {Array(String)} req.body.filters - Filters. + * @param {String} req.pageNo - Page number. + * @param {String} req.pageSize - Page size limit. + * @param {String} req.searchText - Search text. + * @returns {JSON} - Role list. + */ + async default(req) { + try { + const defaultRoleList = await roleService.defaultList(req.query, req.pageNo, req.pageSize, req.searchText) + return defaultRoleList } catch (error) { return error } diff --git a/src/database/migrations/20231211061329-user-roles-visibility-orgId.js b/src/database/migrations/20231211061329-user-roles-visibility-orgId.js new file mode 100644 index 000000000..5af82303f --- /dev/null +++ b/src/database/migrations/20231211061329-user-roles-visibility-orgId.js @@ -0,0 +1,22 @@ +'use strict' +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + const defaultOrgId = queryInterface.sequelize.options.defaultOrgId + await queryInterface.addColumn('user_roles', 'visibility', { + type: Sequelize.STRING, + allowNull: false, + defaultValue: 'PUBLIC', + }) + await queryInterface.addColumn('user_roles', 'organization_id', { + type: Sequelize.INTEGER, + allowNull: false, + defaultValue: defaultOrgId, + }) + }, + + async down(queryInterface, Sequelize) { + await queryInterface.removeColumn('user_roles', 'visibility') + await queryInterface.removeColumn('user_roles', 'organization_id') + }, +} diff --git a/src/database/migrations/20240109122038-fix-entity-type-data-type.js b/src/database/migrations/20240109122038-fix-entity-type-data-type.js new file mode 100644 index 000000000..f3f0921bd --- /dev/null +++ b/src/database/migrations/20240109122038-fix-entity-type-data-type.js @@ -0,0 +1,28 @@ +'use strict' + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + up: async (queryInterface, Sequelize) => { + try { + await queryInterface.sequelize.query(` + UPDATE "entity_types" + SET "data_type" = 'ARRAY[STRING]' + WHERE "value" = 'languages'; + `) + } catch (error) { + console.error('Error during migration:', error.message) + } + }, + + down: async (queryInterface, Sequelize) => { + try { + await queryInterface.sequelize.query(` + UPDATE "entity_types" + SET "data_type" = 'STRING' + WHERE "value" = 'languages'; + `) + } catch (error) { + console.error('Error during migration:', error.message) + } + }, +} diff --git a/src/database/migrations/20240109123510-add-preferred-languages-entity-type-entities.js b/src/database/migrations/20240109123510-add-preferred-languages-entity-type-entities.js new file mode 100644 index 000000000..2a5700409 --- /dev/null +++ b/src/database/migrations/20240109123510-add-preferred-languages-entity-type-entities.js @@ -0,0 +1,66 @@ +'use strict' + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + up: async (queryInterface, Sequelize) => { + const defaultOrgId = queryInterface.sequelize.options.defaultOrgId + await queryInterface.bulkInsert( + 'entity_types', + [ + { + value: 'preferred_language', + label: 'Preferred Language', + created_by: 0, + updated_by: 0, + allow_filtering: true, + data_type: 'STRING', + organization_id: defaultOrgId, + parent_id: null, + allow_custom_entities: false, + has_entities: true, + model_names: ['User'], + created_at: new Date(), + updated_at: new Date(), + }, + ], + {} + ) + const [preferredLanguageEntityType] = await queryInterface.sequelize.query( + "SELECT id FROM entity_types WHERE value = 'preferred_language'" + ) + const preferredLanguageEntityTypeId = preferredLanguageEntityType[0].id + await queryInterface.bulkInsert( + 'entities', + [ + { + entity_type_id: preferredLanguageEntityTypeId, + value: 'en', + label: 'English', + status: 'ACTIVE', + type: 'SYSTEM', + created_by: 0, + updated_by: 0, + created_at: new Date(), + updated_at: new Date(), + }, + { + entity_type_id: preferredLanguageEntityTypeId, + value: 'hi', + label: 'Hindi', + status: 'ACTIVE', + type: 'SYSTEM', + created_by: 0, + updated_by: 0, + created_at: new Date(), + updated_at: new Date(), + }, + ], + {} + ) + }, + + down: async (queryInterface, Sequelize) => { + await queryInterface.bulkDelete('entities', { value: ['en', 'hi'] }) + await queryInterface.bulkDelete('entity_types', { value: 'preferred_language' }) + }, +} diff --git a/src/database/migrations/20240110052934-encrypt-emails.js b/src/database/migrations/20240110052934-encrypt-emails.js new file mode 100644 index 000000000..0c41128fc --- /dev/null +++ b/src/database/migrations/20240110052934-encrypt-emails.js @@ -0,0 +1,126 @@ +// Migration for encrypting and decrypting emails + +'use strict' + +const emailEncryption = require('../../utils/emailEncryption') +const { Sequelize } = require('sequelize') + +module.exports = { + up: async (queryInterface, Sequelize) => { + await processTable(queryInterface, 'organization_user_invites', 'encrypt') + await processTable(queryInterface, 'users', 'encrypt') + await processTable(queryInterface, 'users_credentials', 'encrypt') + }, + + down: async (queryInterface, Sequelize) => { + await processTable(queryInterface, 'organization_user_invites', 'decrypt') + await processTable(queryInterface, 'users', 'decrypt') + await processTable(queryInterface, 'users_credentials', 'decrypt') + }, +} + +const processTable = async (queryInterface, tableName, operation) => { + const greenColor = '\x1b[32m' + const yellowColor = '\x1b[33m' + const resetColor = '\x1b[0m' + const redColor = '\x1b[31m' + + const records = await queryInterface.sequelize.query(`SELECT * FROM ${tableName};`, { + type: Sequelize.QueryTypes.SELECT, + }) + + const updates = records.map(async (record) => { + const isEmailValid = validateEmail(record.email) + + if (operation === 'encrypt') { + if (isEmailValid) { + const encryptedEmail = emailEncryption.encrypt(record.email) + if (tableName === 'users_credentials') { + const t = await queryInterface.sequelize.transaction() + try { + const existingRow = await queryInterface.sequelize.query( + `SELECT * FROM ${tableName} WHERE id = ${record.id};`, + { + type: Sequelize.QueryTypes.SELECT, + transaction: t, + } + ) + await queryInterface.sequelize.query(`DELETE FROM ${tableName} WHERE id = ${record.id};`, { + transaction: t, + }) + const modifiedRow = { ...existingRow[0], email: encryptedEmail } + await queryInterface.bulkInsert(tableName, [modifiedRow], { transaction: t }) + await t.commit() + } catch (error) { + console.log('ENCRYPTION ERROR: ', error) + await t.rollback() + } + } else + await queryInterface.sequelize.query( + `UPDATE ${tableName} SET email = '${encryptedEmail}' WHERE id = ${record.id};` + ) + } else { + console.warn( + `${redColor}Skipping encryption of invalid email in ${yellowColor}${tableName}${resetColor} table with id ${yellowColor}${record.id}${resetColor}: ${yellowColor}${record.email}${resetColor}` + ) + } + } else if (operation === 'decrypt') { + if (!isEmailValid) { + try { + const decryptedEmail = emailEncryption.decrypt(record.email) + const isDecryptedEmailValid = validateEmail(decryptedEmail) + if (isDecryptedEmailValid) { + if (tableName === 'users_credentials') { + const t = await queryInterface.sequelize.transaction() + try { + const existingRow = await queryInterface.sequelize.query( + `SELECT * FROM ${tableName} WHERE id = ${record.id};`, + { + type: Sequelize.QueryTypes.SELECT, + transaction: t, + } + ) + await queryInterface.sequelize.query( + `DELETE FROM ${tableName} WHERE id = ${record.id};`, + { + transaction: t, + } + ) + const modifiedRow = { ...existingRow[0], email: decryptedEmail } + await queryInterface.bulkInsert(tableName, [modifiedRow], { transaction: t }) + await t.commit() + } catch (error) { + console.log('DECRYPTION ERROR: ', error) + await t.rollback() + } + } else + await queryInterface.sequelize.query( + `UPDATE ${tableName} SET email = '${decryptedEmail}' WHERE id = ${record.id};` + ) + } else { + console.warn( + `${redColor}Skipping decryption of email in ${yellowColor}${tableName}${resetColor} table with id ${yellowColor}${record.id}${resetColor} because decrypted emailId wasn't a valid emailId.${resetColor}` + ) + } + } catch (error) { + console.log('Something went wrong: ', error) + console.log('EMAIL: ', record.email) + } + } else { + console.warn( + `${redColor}Skipping decryption of email in ${yellowColor}${tableName}${resetColor} table with id ${yellowColor}${record.id}${resetColor} because current emailId is in valid emailId format.${resetColor}` + ) + } + } + }) + + await Promise.all(updates) + console.log( + `${greenColor}Finished processing ${resetColor}${yellowColor}${tableName}${resetColor} ${greenColor}table.${resetColor}\n` + ) +} + +const validateEmail = (email) => { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ + return emailRegex.test(email) +} diff --git a/src/database/migrations/20240119142709-add-role-session-manager.js b/src/database/migrations/20240119142709-add-role-session-manager.js new file mode 100644 index 000000000..89a202ccd --- /dev/null +++ b/src/database/migrations/20240119142709-add-role-session-manager.js @@ -0,0 +1,27 @@ +'use strict' + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + const defaultOrgId = queryInterface.sequelize.options.defaultOrgId + + await queryInterface.bulkInsert( + 'user_roles', + [ + { + title: 'session_manager', + user_type: 1, + visibility: 'PUBLIC', + organization_id: defaultOrgId, + updated_at: new Date(), + created_at: new Date(), + }, + ], + {} + ) + }, + + async down(queryInterface, Sequelize) { + await queryInterface.bulkDelete('user_roles', { title: 'session_manager' }) + }, +} diff --git a/src/database/migrations/20240122091311-create-generic-invitation-template.js b/src/database/migrations/20240122091311-create-generic-invitation-template.js new file mode 100644 index 000000000..78ba2a30f --- /dev/null +++ b/src/database/migrations/20240122091311-create-generic-invitation-template.js @@ -0,0 +1,32 @@ +'use strict' +const moment = require('moment') + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + up: async (queryInterface, Sequelize) => { + const defaultOrgId = queryInterface.sequelize.options.defaultOrgId + if (!defaultOrgId) { + throw new Error('Default org ID is undefined. Please make sure it is set in sequelize options.') + } + let notificationTemplateData = [ + { + code: 'generic_invite', + subject: 'Welcome Aboard as a {roles}', + body: '

Dear {name},

We are delighted to inform you that you have been successfully onboarded as a {roles} for {orgName}. You can now explore {appName}.
We request you to register on our Mentoring Platform (if not already), to start your journey with us as a organization admin.

Click to register: {portalURL}', + status: 'ACTIVE', + type: 'email', + created_at: moment().format(), + updated_at: moment().format(), + email_header: 'email_header', + email_footer: 'email_footer', + organization_id: defaultOrgId, + }, + ] + + await queryInterface.bulkInsert('notification_templates', notificationTemplateData, {}) + }, + + down: async (queryInterface, Sequelize) => { + await queryInterface.bulkDelete('notification_templates', { code: 'generic_invite' }) + }, +} diff --git a/src/database/migrations/20240122092217-update-registration-template.js b/src/database/migrations/20240122092217-update-registration-template.js new file mode 100644 index 000000000..1a3d6ad31 --- /dev/null +++ b/src/database/migrations/20240122092217-update-registration-template.js @@ -0,0 +1,42 @@ +'use strict' + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + up: async (queryInterface, Sequelize) => { + const defaultOrgId = queryInterface.sequelize.options.defaultOrgId + if (!defaultOrgId) { + throw new Error('Default org ID is undefined. Please make sure it is set in sequelize options.') + } + const registrationTemplate = await queryInterface.sequelize.query( + `SELECT * FROM notification_templates WHERE code = :code AND organization_id = :organization_id LIMIT 1`, + { + replacements: { + code: process.env.REGISTRATION_EMAIL_TEMPLATE_CODE, + organization_id: defaultOrgId, + }, + type: Sequelize.QueryTypes.SELECT, + } + ) + + if (registrationTemplate) { + const update = await queryInterface.sequelize.query( + `UPDATE notification_templates SET body = :body WHERE code = :code AND organization_id = :organization_id`, + { + replacements: { + code: process.env.REGISTRATION_EMAIL_TEMPLATE_CODE, + organization_id: defaultOrgId, + body: '

Dear {name},

Welcome to {appName} community! . We are excited for you to start your journey as a {roles}.

Login to MentorED to start your journey
Click to register: {portalURL}', + }, + type: Sequelize.QueryTypes.UPDATE, + } + ) + console.log(update, 'update') + } else { + console.log('Registration template not found') + } + }, + + down: async (queryInterface, Sequelize) => { + //not required + }, +} diff --git a/src/database/migrations/20240123032543-make-session-manager-to-non-system-role.js b/src/database/migrations/20240123032543-make-session-manager-to-non-system-role.js new file mode 100644 index 000000000..ba5c201fe --- /dev/null +++ b/src/database/migrations/20240123032543-make-session-manager-to-non-system-role.js @@ -0,0 +1,42 @@ +'use strict' + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + up: async (queryInterface, Sequelize) => { + const defaultOrgId = queryInterface.sequelize.options.defaultOrgId + if (!defaultOrgId) { + throw new Error('Default org ID is undefined. Please make sure it is set in sequelize options.') + } + const userRole = await queryInterface.sequelize.query( + `SELECT * FROM user_roles WHERE title = :title AND organization_id = :organization_id LIMIT 1`, + { + replacements: { + title: 'session_manager', + organization_id: defaultOrgId, + }, + type: Sequelize.QueryTypes.SELECT, + } + ) + + if (userRole) { + const update = await queryInterface.sequelize.query( + `UPDATE user_roles SET user_type = :user_type WHERE title = :title AND organization_id = :organization_id`, + { + replacements: { + title: 'session_manager', + organization_id: defaultOrgId, + user_type: 0, + }, + type: Sequelize.QueryTypes.UPDATE, + } + ) + console.log(update, 'update') + } else { + console.log('role session manager not found') + } + }, + + down: async (queryInterface, Sequelize) => { + //not required + }, +} diff --git a/src/database/models/userRole.js b/src/database/models/userRole.js index 259ac73ab..2d58f23fc 100644 --- a/src/database/models/userRole.js +++ b/src/database/models/userRole.js @@ -1,5 +1,7 @@ 'use strict' +const common = require('@constants/common') module.exports = (sequelize, DataTypes) => { + const defaultOrgId = sequelize.options.defaultOrgId const UserRole = sequelize.define( 'UserRole', { @@ -15,12 +17,20 @@ module.exports = (sequelize, DataTypes) => { unique: true, }, user_type: { - type: DataTypes.INTEGER, //0 - non system user , 1 - system user + type: DataTypes.INTEGER, allowNull: false, }, status: { type: DataTypes.STRING, - defaultValue: 'ACTIVE', + defaultValue: common.ACTIVE_STATUS, + }, + organization_id: { + type: DataTypes.INTEGER, + allowNull: false, + }, + visibility: { + type: DataTypes.STRING, + defaultValue: 'PUBLIC', }, }, { sequelize, modelName: 'UserRole', tableName: 'user_roles', freezeTableName: true, paranoid: true } diff --git a/src/database/queries/entityType.js b/src/database/queries/entityType.js index b080e9054..3bca0f1b1 100644 --- a/src/database/queries/entityType.js +++ b/src/database/queries/entityType.js @@ -28,14 +28,7 @@ module.exports = class UserEntityData { try { const entityData = await EntityType.findAll({ where: { - [Op.or]: [ - { - created_by: 0, - }, - { - organization_id: orgId, - }, - ], + organization_id: orgId, ...filter, }, attributes, @@ -50,21 +43,24 @@ module.exports = class UserEntityData { try { const entityTypes = await EntityType.findAll({ where: filter, + raw: true, }) - const result = await Promise.all( - entityTypes.map(async (entityType) => { - const entities = await Entity.findAll({ - where: { entity_type_id: entityType.id }, - //attributes: { exclude: ['entity_type_id'] }, - }) + const entityTypeIds = entityTypes.map((entityType) => entityType.id) - return { - ...entityType.toJSON(), - entities: entities.map((entity) => entity.toJSON()), - } - }) - ) + const entities = await Entity.findAll({ + where: { entity_type_id: entityTypeIds, status: 'ACTIVE' }, + raw: true, + //attributes: { exclude: ['entity_type_id'] }, + }) + + const result = entityTypes.map((entityType) => { + const matchingEntities = entities.filter((entity) => entity.entity_type_id === entityType.id) + return { + ...entityType, + entities: matchingEntities, + } + }) return result } catch (error) { @@ -73,7 +69,7 @@ module.exports = class UserEntityData { } } - static async findUserEntityTypesAndEntitiesRaw(filter) { + /* static async findUserEntityTypesAndEntitiesRaw(filter) { try { const [result, metadata] = await Sequelize.query( `SELECT @@ -97,13 +93,14 @@ module.exports = class UserEntityData { console.error('Error fetching data:', error) throw error } - } + } */ - static async updateOneEntityType(id, update, options = {}) { + static async updateOneEntityType(id, organizationId, update, options = {}) { try { return await EntityType.update(update, { where: { id: id, + organization_id: organizationId, }, ...options, }) @@ -112,11 +109,12 @@ module.exports = class UserEntityData { } } - static async deleteOneEntityType(id) { + static async deleteOneEntityType(id, orgId) { try { return await EntityType.destroy({ where: { id: id, + organization_id: orgId, }, individualHooks: true, }) @@ -137,22 +135,25 @@ module.exports = class UserEntityData { try { const entityTypes = await EntityType.findAll({ where: filter, + raw: true, }) - const result = await Promise.all( - entityTypes.map(async (entityType) => { - const entities = await Entity.findAll({ - where: { entity_type_id: entityType.id, status: 'ACTIVE' }, - //attributes: { exclude: ['entity_type_id'] }, - }) + const entityTypeIds = entityTypes.map((entityType) => entityType.id) - return { - ...entityType.toJSON(), - entities: entities.map((entity) => entity.toJSON()), - } - }) - ) + // Fetch all matching entities using the IDs + const entities = await Entity.findAll({ + where: { entity_type_id: entityTypeIds, status: 'ACTIVE' }, + raw: true, + //attributes: { exclude: ['entity_type_id'] }, + }) + const result = entityTypes.map((entityType) => { + const matchingEntities = entities.filter((entity) => entity.entity_type_id === entityType.id) + return { + ...entityType, + entities: matchingEntities, + } + }) return result } catch (error) { return error diff --git a/src/database/queries/orgRoleRequest.js b/src/database/queries/orgRoleRequest.js index 2b98a30f3..0e799d1a6 100644 --- a/src/database/queries/orgRoleRequest.js +++ b/src/database/queries/orgRoleRequest.js @@ -69,12 +69,14 @@ exports.requestDetails = async (filter, options = {}) => { const requester = await User.findOne({ where: { id: reqDetails.requester_id }, attributes: ['id', 'name'], + raw: true, }) const handler = reqDetails.handled_by ? await User.findOne({ where: { id: reqDetails.handled_by }, attributes: ['id', 'name'], + raw: true, }) : null diff --git a/src/database/queries/orgUserInvite.js b/src/database/queries/orgUserInvite.js index 75f6b3214..eebed553a 100644 --- a/src/database/queries/orgUserInvite.js +++ b/src/database/queries/orgUserInvite.js @@ -1,6 +1,6 @@ 'use strict' const organizationUserInvite = require('../models/index').OrganizationUserInvite -const { UniqueConstraintError, ValidationError } = require('sequelize') +const { ValidationError } = require('sequelize') exports.create = async (data) => { try { @@ -8,9 +8,7 @@ exports.create = async (data) => { const result = createData.get({ plain: true }) return result } catch (error) { - if (error instanceof UniqueConstraintError) { - return 'USER_ALREADY_EXISTS' - } else if (error instanceof ValidationError) { + if (error instanceof ValidationError) { let message error.errors.forEach((err) => { message = `${err.path} cannot be null.` @@ -58,3 +56,15 @@ exports.deleteOne = async (id, options = {}) => { return error } } + +exports.findAll = async (filter, options = {}) => { + try { + return await organizationUserInvite.findAll({ + where: filter, + ...options, + raw: true, + }) + } catch (error) { + return error + } +} diff --git a/src/database/queries/userCredential.js b/src/database/queries/userCredential.js index 15b828dcc..64a724af7 100644 --- a/src/database/queries/userCredential.js +++ b/src/database/queries/userCredential.js @@ -1,13 +1,23 @@ 'use strict' const UserCredential = require('@database/models/index').UserCredential +const { UniqueConstraintError, ValidationError } = require('sequelize') exports.create = async (data) => { try { const res = await UserCredential.create(data) return res.get({ plain: true }) } catch (error) { - console.log(error) - throw error + if (error instanceof UniqueConstraintError) { + return 'User already exist' + } else if (error instanceof ValidationError) { + let message + error.errors.forEach((err) => { + message = `${err.path} cannot be null.` + }) + return message + } else { + return error.message + } } } @@ -20,7 +30,7 @@ exports.findOne = async (filter, options = {}) => { }) } catch (error) { console.log(error) - throw error + return error } } @@ -33,7 +43,7 @@ exports.updateUser = async (filter, update, options = {}) => { }) } catch (error) { console.log(error) - throw error + return error } } diff --git a/src/database/queries/userRole.js b/src/database/queries/userRole.js index b2fc03311..30001ce99 100644 --- a/src/database/queries/userRole.js +++ b/src/database/queries/userRole.js @@ -2,6 +2,7 @@ const UserRole = require('@database/models/index').UserRole const { Op } = require('sequelize') + exports.findOne = async (filter, options = {}) => { try { return await UserRole.findOne({ @@ -33,3 +34,53 @@ exports.findAll = async (filter, options = {}) => { return error } } + +exports.create = async (data, user_organization_id) => { + try { + return await UserRole.create(data, { returning: true }) + } catch (error) { + throw error + } +} + +exports.findRoleById = async (id) => { + try { + return await UserRole.findByPk(id) + } catch (error) { + throw error + } +} + +exports.findAllRoles = async (filter, attributes, options) => { + try { + return await UserRole.findAndCountAll({ + where: filter, + attributes, + ...options, + }) + } catch (error) { + throw error + } +} + +exports.updateRole = async (filter, updatedata) => { + try { + return await UserRole.update(updatedata, { + where: filter, + returning: true, + }) + } catch (error) { + throw error + } +} + +exports.deleteRole = async (filter) => { + try { + return await UserRole.destroy({ + where: filter, + individualHooks: true, + }) + } catch (error) { + throw error + } +} diff --git a/src/database/queries/users.js b/src/database/queries/users.js index e3a84b90e..fa1413f4c 100644 --- a/src/database/queries/users.js +++ b/src/database/queries/users.js @@ -3,6 +3,7 @@ const database = require('@database/models/index') const Organization = require('@database/models/index').Organization const { Op, QueryTypes } = require('sequelize') const Sequelize = require('@database/models/index').sequelize +const emailEncryption = require('@utils/emailEncryption') exports.getColumns = async () => { try { @@ -165,18 +166,23 @@ exports.findUserWithOrganization = async (filter, options = {}) => { return error } } -exports.listUsersFromView = async (roleId, organization_id, page, limit, search, userIds) => { +exports.listUsersFromView = async (roleId, organization_id, page, limit, search, userIds, emailIds) => { try { const offset = (page - 1) * limit const filterConditions = [] - if (search) { + if (emailIds) { + filterConditions.push(`users.email IN ('${emailIds.join("','")}')`) + } else if (search) { filterConditions.push(`users.name ILIKE :search`) } - if (roleId) { + if (!Array.isArray(roleId)) { filterConditions.push(`users.roles @> ARRAY[:roleId]::integer[]`) + } else { + // If roleId is an array, use the '&&' operator to check if there is an overlap between roleId and users.roles arrays + filterConditions.push(`ARRAY[:roleId] && users.roles`) } if (organization_id) { @@ -190,8 +196,10 @@ exports.listUsersFromView = async (roleId, organization_id, page, limit, search, const filterQuery = ` SELECT + COUNT(*) OVER () as total_count, users.id, users.name, + users.email, users.about, users.image, jsonb_build_object( @@ -209,7 +217,7 @@ exports.listUsersFromView = async (roleId, organization_id, page, limit, search, ${filterClause} ORDER BY users.name ASC - OFFSET + OFFSET :offset LIMIT :limit; @@ -224,12 +232,17 @@ exports.listUsersFromView = async (roleId, organization_id, page, limit, search, userIds: userIds, } - const users = await Sequelize.query(filterQuery, { + let users = await Sequelize.query(filterQuery, { type: QueryTypes.SELECT, replacements: replacements, }) - return { count: users.length, data: users } + users = users.filter((user) => { + user.email = emailEncryption.decrypt(user.email) + return user + }) + + return { count: users.length > 0 ? Number(users[0].total_count) : 0, data: users } } catch (error) { throw error } diff --git a/src/database/seeders/20230718130102-add_roles_to_roles_table.js b/src/database/seeders/20230718130102-add_roles_to_roles_table.js index e6cadc6ca..effc177d5 100644 --- a/src/database/seeders/20230718130102-add_roles_to_roles_table.js +++ b/src/database/seeders/20230718130102-add_roles_to_roles_table.js @@ -1,14 +1,13 @@ module.exports = { up: async (queryInterface, Sequelize) => { let rolesData = [] + const defaultOrgId = queryInterface.sequelize.options.defaultOrgId const roleArray = ['user', 'mentor', 'mentee', 'admin'] - //user_type denotes the role is system user or not 1: system user, 0: non system user roleArray.forEach(async function (role) { let user_type = 0 if (role == 'admin') { user_type = 1 } - let eachRow = { title: role, user_type: user_type, diff --git a/src/database/seeders/20230802144103-add-entity-and-entity-types.js b/src/database/seeders/20230802144103-add-entity-and-entity-types.js index b7ea934e1..b3610ad04 100644 --- a/src/database/seeders/20230802144103-add-entity-and-entity-types.js +++ b/src/database/seeders/20230802144103-add-entity-and-entity-types.js @@ -149,8 +149,10 @@ module.exports = { if (key === 'location') { entityTypeRow.allow_custom_entities = false + entityTypeRow.data_type = 'STRING' } else { entityTypeRow.allow_custom_entities = true + entityTypeRow.data_type = 'ARRAY[STRING]' } return entityTypeRow }) diff --git a/src/database/seeders/20230915100336-add_org_admin_role.js b/src/database/seeders/20230915100336-add_org_admin_role.js index daa8235de..64df2388d 100644 --- a/src/database/seeders/20230915100336-add_org_admin_role.js +++ b/src/database/seeders/20230915100336-add_org_admin_role.js @@ -4,6 +4,7 @@ module.exports = { async up(queryInterface, Sequelize) { let roleData = [] + const defaultOrgId = queryInterface.sequelize.options.defaultOrgId let eachRow = { title: 'org_admin', user_type: 1, diff --git a/src/database/seeders/20231221071000-alter_add_roles_table.js b/src/database/seeders/20231221071000-alter_add_roles_table.js new file mode 100644 index 000000000..e8172f989 --- /dev/null +++ b/src/database/seeders/20231221071000-alter_add_roles_table.js @@ -0,0 +1,36 @@ +'use strict' + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + const defaultOrgId = queryInterface.sequelize.options.defaultOrgId + const roleArray = ['user', 'mentor', 'mentee', 'admin', 'org_admin'] + + // Use Promise.all to wait for all async operations in the loop + await Promise.all( + roleArray.map(async function (role) { + let user_type = 0 + if (role === 'admin') { + user_type = 1 + } + + // Use the queryInterface.bulkUpdate for each role + await queryInterface.bulkUpdate( + 'user_roles', + { + visibility: 'PUBLIC', + organization_id: defaultOrgId, + }, + { + title: role, + user_type: user_type, + } + ) + }) + ) + }, + + async down(queryInterface, Sequelize) { + await queryInterface.bulkDelete('user_roles', null, {}) + }, +} diff --git a/src/db/forms/model.js b/src/db/forms/model.js deleted file mode 100644 index dfe495ecf..000000000 --- a/src/db/forms/model.js +++ /dev/null @@ -1,61 +0,0 @@ -/** - * name : db/forms/model - * author : Aman Gupta - * Date : 03-Nov-2021 - * Description : Forms schema - */ - -// Dependencies -const mongoose = require('mongoose') -const Schema = mongoose.Schema - -const mongooseLeanGetter = require('mongoose-lean-getters') - -const formSchema = new Schema({ - type: { - type: String, - required: true, - unique: true, - }, - subType: { - type: String, - required: true, - }, - action: { - type: String, - required: true, - }, - data: { - templateName: { - type: String, - required: true, - }, - fields: { - type: Object, - required: true, - }, - }, -}) -formSchema.plugin(mongooseLeanGetter) - -formSchema.pre('updateOne', function () { - const update = this.getUpdate() - - if (update.__v != null) { - delete update.__v - } - const keys = ['$set', '$setOnInsert'] - for (const key of keys) { - if (update[key] != null && update[key].__v != null) { - delete update[key].__v - if (Object.keys(update[key]).length === 0) { - delete update[key] - } - } - } - update.$inc = update.$inc || {} - update.$inc.__v = 1 -}) -const Forms = db.model('forms', formSchema) - -module.exports = Forms diff --git a/src/db/forms/queries.js b/src/db/forms/queries.js deleted file mode 100644 index 569bf438f..000000000 --- a/src/db/forms/queries.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * name : models/forms/query - * author : Aman karki - * Date : 07-Oct-2021 - * Description : Users database operations - */ - -const Forms = require('./model') - -module.exports = class FormsData { - static async createForm(data) { - try { - await new Forms(data).save() - return true - } catch (error) { - return error - } - } - - static async findOneForm(filter, projection = {}) { - try { - const formData = await Forms.findOne(filter, projection) - return formData - } catch (error) { - return error - } - } - - static async updateOneForm(filter, update, options = {}) { - try { - const res = await Forms.updateOne(filter, update, options) - if ((res.n === 1 && res.nModified === 1) || (res.matchedCount === 1 && res.modifiedCount === 1)) { - return 'FORM_UPDATED' - } else if ((res.n === 1 && res.nModified === 0) || (res.matchedCount === 1 && res.modifiedCount === 0)) { - return 'FORM_ALREADY_EXISTS' - } else { - return 'FORM_NOT_FOUND' - } - } catch (error) { - return error - } - } - - static async findAllTypeFormVersion() { - const projection = { - _id: 1, - type: 1, - __v: 1, - } - try { - const formData = await Forms.find({}, projection) - return formData - } catch (error) { - return error - } - } -} diff --git a/src/db/notification-template/model.js b/src/db/notification-template/model.js deleted file mode 100644 index 82bb951e8..000000000 --- a/src/db/notification-template/model.js +++ /dev/null @@ -1,61 +0,0 @@ -/** - * name : db/users/model - * author : Aman Gupta - * Date : 06-Dec-2021 - * Description : Template notification model - */ - -// Dependencies -const mongoose = require('mongoose') - -const Schema = mongoose.Schema - -const mongooseLeanGetter = require('mongoose-lean-getters') - -const notificationTemplateSchema = new Schema({ - type: { - type: String, - required: true, - }, - code: { - type: String, - required: true, - unique: true, - index: true, - }, - subject: { - type: String, - required: true, - }, - body: { - type: String, - required: true, - }, - status: { - type: String, - required: true, - }, - deleted: { - type: Boolean, - default: false, - }, - createdBy: { - type: mongoose.Types.ObjectId, - required: true, - index: true, - }, - updatedBy: { - type: mongoose.Types.ObjectId, - required: true, - }, - emailHeader: { - type: String, - }, - emailFooter: { - type: String, - }, -}) -notificationTemplateSchema.plugin(mongooseLeanGetter) -const Users = db.model('notificationtemplates', notificationTemplateSchema, 'notificationTemplates') - -module.exports = Users diff --git a/src/db/notification-template/query.js b/src/db/notification-template/query.js deleted file mode 100644 index 7f0f2e0b1..000000000 --- a/src/db/notification-template/query.js +++ /dev/null @@ -1,70 +0,0 @@ -/** - * name : models/notification-template/query - * author : Aman Gupta - * Date : 06-Dec-2021 - * Description : Notification template database operations - */ - -// Dependencies -const NotificationTemplate = require('./model') - -module.exports = class NotificationTemplateData { - static async findOneEmailTemplate(code) { - const filter = { - code, - type: 'email', - deleted: false, - status: 'active', - } - - try { - const templateData = await NotificationTemplate.findOne(filter).lean() - if (templateData && templateData.emailHeader) { - const header = await this.getEmailHeader(templateData.emailHeader) - if (header && header.body) { - templateData['body'] = header.body + templateData['body'] - } - } - - if (templateData && templateData.emailFooter) { - const footer = await this.getEmailFooter(templateData.emailFooter) - if (footer && footer.body) { - templateData['body'] = templateData['body'] + footer.body - } - } - return templateData - } catch (error) { - return error - } - } - static async getEmailHeader(header) { - try { - const filterEmailHeader = { - code: header, - type: 'emailHeader', - deleted: false, - status: 'active', - } - const headerData = await NotificationTemplate.findOne(filterEmailHeader).lean() - - return headerData - } catch (error) { - return error - } - } - static async getEmailFooter(footer) { - try { - const filterEmailFooter = { - code: footer, - type: 'emailFooter', - deleted: false, - status: 'active', - } - const footerData = await NotificationTemplate.findOne(filterEmailFooter).lean() - - return footerData - } catch (error) { - return error - } - } -} diff --git a/src/db/systemUsers/model.js b/src/db/systemUsers/model.js deleted file mode 100644 index 2dd6806da..000000000 --- a/src/db/systemUsers/model.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * name : db/users/model - * author : Aman Karki - * Date : 10-Nov-2021 - * Description : System User schema data - */ - -// Dependencies -const mongoose = require('mongoose') -const Schema = mongoose.Schema -const mongooseLeanGetter = require('mongoose-lean-getters') - -const userSchema = new Schema({ - email: { - address: { - type: String, - index: { - unique: true, - }, - required: true, - }, - verified: { - type: Boolean, - default: false, - }, - }, - password: { - type: String, - required: true, - }, - name: { - type: String, - required: true, - }, - role: String, -}) -userSchema.plugin(mongooseLeanGetter) -const SystemUsers = db.model('systemUsers', userSchema, 'systemUsers') - -module.exports = SystemUsers diff --git a/src/db/systemUsers/queries.js b/src/db/systemUsers/queries.js deleted file mode 100644 index ab9c7366b..000000000 --- a/src/db/systemUsers/queries.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * name : models/systemUsers/queries - * author : Aman karki - * Date : 07-Oct-2021 - * Description : System Users database operations - */ - -const SystemUsers = require('./model') - -module.exports = class SystemUsersData { - static async findUsersByEmail(email) { - try { - const userData = await SystemUsers.findOne({ 'email': email }).lean() - - return userData - } catch (error) { - return error - } - } - - static async create(data) { - try { - await new SystemUsers(data).save() - return true - } catch (error) { - return error - } - } -} diff --git a/src/db/userentities/model.js b/src/db/userentities/model.js deleted file mode 100644 index 7a0fe8861..000000000 --- a/src/db/userentities/model.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * name : db/userentities/model - * author : Aman Gupta - * Date : 04-Nov-2021 - * Description : User Entity schema - */ - -// Dependencies -const mongoose = require('mongoose') - -const Schema = mongoose.Schema -const mongooseLeanGetter = require('mongoose-lean-getters') - -const userEntitySchema = new Schema({ - value: { - type: String, - required: true, - }, - label: { - type: String, - required: true, - }, - status: { - type: String, - default: 'ACTIVE', - required: true, - }, - deleted: { - type: Boolean, - default: false, - required: true, - }, - type: { - type: String, - required: true, - index: true, - }, - createdBy: { - type: mongoose.Types.ObjectId, - required: true, - }, - updatedBy: { - type: mongoose.Types.ObjectId, - required: true, - }, -}) -userEntitySchema.plugin(mongooseLeanGetter) -const UserEntities = db.model('userEntities', userEntitySchema, 'userEntities') - -module.exports = UserEntities diff --git a/src/db/userentities/query.js b/src/db/userentities/query.js deleted file mode 100644 index adbbf26aa..000000000 --- a/src/db/userentities/query.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * name : models/entities/query - * author : Aman Gupta - * Date : 04-Nov-2021 - * Description : Users entities database operations - */ - -// Dependencies -const UserEntities = require('./model') - -module.exports = class UserEntityData { - static async createEntity(data) { - try { - const userEntityDataRes = await new UserEntities(data).save() - return userEntityDataRes - } catch (error) { - return error - } - } - - static async findOneEntity(filter, projection = {}) { - try { - const userEntityData = await UserEntities.findOne(filter, projection) - return userEntityData - } catch (error) { - return error - } - } - - static async findAllEntities(filter, projection = {}) { - try { - const userEntitiesData = await UserEntities.find(filter, projection) - return userEntitiesData - } catch (error) { - return error - } - } - - static async updateOneEntity(filter, update, options = {}) { - try { - const res = await UserEntities.updateOne(filter, update, options) - if ((res.n === 1 && res.nModified === 1) || (res.matchedCount === 1 && res.modifiedCount === 1)) { - resolve('ENTITY_UPDATED') - } else if ((res.n === 1 && res.nModified === 0) || (res.matchedCount === 1 && res.modifiedCount === 0)) { - return 'ENTITY_ALREADY_EXISTS' - } else { - return 'ENTITY_NOT_FOUND' - } - } catch (error) { - return error - } - } - - static async findOne(_id) { - try { - const filter = { - _id: ObjectId(_id), - } - return await UserEntities.findOne(filter) - } catch (error) { - return error - } - } -} diff --git a/src/db/users/model.js b/src/db/users/model.js deleted file mode 100644 index 570f1bd27..000000000 --- a/src/db/users/model.js +++ /dev/null @@ -1,92 +0,0 @@ -/** - * name : db/users/model - * author : Aman Karki - * Date : 07-Oct-2021 - * Description : User schema data - */ - -// Dependencies -const mongoose = require('mongoose') -const { aes256cbc } = require('elevate-encryption') -const Schema = mongoose.Schema -// Adding the package -const mongooseLeanGetter = require('mongoose-lean-getters') - -const userSchema = new Schema( - { - email: { - address: { - type: String, - set: aes256cbc.encrypt, - get: aes256cbc.decrypt, - index: { - unique: true, - }, - required: true, - }, - verified: { - type: Boolean, - default: false, - }, - }, - password: { - type: String, - required: true, - }, - name: { - type: String, - required: true, - }, - gender: String, - designation: [{ value: String, label: String }], - location: [{ value: String, label: String }], - about: String, - shareLink: String, - areasOfExpertise: [{ value: String, label: String }], - image: String, - experience: String, - lastLoggedInAt: Date, - isAMentor: { - type: Boolean, - default: false, - index: true, - }, - hasAcceptedTAndC: { - type: Boolean, - default: false, - }, - deleted: { - type: Boolean, - default: false, - required: true, - }, - educationQualification: { - type: String, - default: null, - }, - refreshTokens: [{ token: String, exp: Number }], - otpInfo: { - otp: Number, - exp: Number, - }, - languages: [{ value: String, label: String }], - rating: { - type: Object, - }, - preferredLanguage: { - type: String, - default: 'en', - }, - deletedAt: Date, - }, - { - versionKey: false, - toObject: { getters: true, setters: true }, - toJSON: { getters: true, setters: true }, - runSettersOnQuery: true, - } -) -userSchema.plugin(mongooseLeanGetter) -const Users = db.model('users', userSchema) - -module.exports = Users diff --git a/src/db/users/queries.js b/src/db/users/queries.js deleted file mode 100644 index 76b8fd603..000000000 --- a/src/db/users/queries.js +++ /dev/null @@ -1,166 +0,0 @@ -/** - * name : models/users/query - * author : Aman karki - * Date : 07-Oct-2021 - * Description : Users database operations - */ - -// Dependencies -const ObjectId = require('mongoose').Types.ObjectId -const Users = require('./model') - -module.exports = class UsersData { - static async findOne(filter, projection = {}) { - try { - const userData = await Users.findOne(filter, projection).lean({ - getters: true, - }) - return userData - } catch (error) { - return error - } - } - - static async findAllUsers(filter, projection = {}) { - try { - const usersData = await Users.find(filter, projection).lean({ - getters: true, - }) - return usersData - } catch (error) { - return error - } - } - - static async createUser(data) { - try { - await new Users(data).save() - return true - } catch (error) { - return error - } - } - - static async updateOneUser(filter, update, options = {}) { - try { - const res = await Users.updateOne(filter, update, options) - if ((res.n === 1 && res.nModified === 1) || (res.matchedCount === 1 && res.modifiedCount === 1)) { - return true - } else { - return false - } - } catch (error) { - return error - } - } - - static async searchMentors(page, limit, search, userId, match) { - try { - if (match == 'startsWith') { - search = '^' + search - } else if (match == 'endsWith') { - search = search + '$' - } - - let users = await Users.aggregate([ - { - $match: { - deleted: false, - isAMentor: true, - _id: { - $ne: ObjectId(userId), - }, - $or: [{ name: new RegExp(search, 'i') }], - }, - }, - { - $project: { - name: 1, - image: 1, - areasOfExpertise: 1, - about: 1, - designation: 1, - rating: 1, - }, - }, - { - $sort: { name: 1 }, - }, - { - $facet: { - totalCount: [{ $count: 'count' }], - data: [{ $skip: limit * (page - 1) }, { $limit: limit }], - }, - }, - { - $project: { - data: 1, - count: { - $arrayElemAt: ['$totalCount.count', 0], - }, - }, - }, - ]).collation({ locale: 'en', caseLevel: false }) - - return users - } catch (error) { - return error - } - } - static async listUsers(type, page, limit, search) { - try { - let isAMentorFlag = true - if (type === 'mentor') { - isAMentorFlag = true - } else if (type === 'mentee') { - isAMentorFlag = false - } - let users = await Users.aggregate([ - { - $match: { - deleted: false, - isAMentor: isAMentorFlag, - $or: [{ name: new RegExp(search, 'i') }], - }, - }, - { - $project: { - name: 1, - image: 1, - areasOfExpertise: 1, - }, - }, - { - $sort: { name: 1 }, - }, - { - $facet: { - totalCount: [{ $count: 'count' }], - data: [{ $skip: limit * (page - 1) }, { $limit: limit }], - }, - }, - { - $project: { - data: 1, - count: { - $arrayElemAt: ['$totalCount.count', 0], - }, - }, - }, - ]).collation({ locale: 'en', caseLevel: false }) - - return users - } catch (error) { - return error - } - } - - static async findOneAndReplace(filter, update) { - try { - const res = await Users.findOneAndReplace(filter, update) - return res - } catch (error) { - return error - } - } -} diff --git a/src/envVariables.js b/src/envVariables.js index 97f54fbfa..9e0bb7c2a 100644 --- a/src/envVariables.js +++ b/src/envVariables.js @@ -15,10 +15,6 @@ let enviromentVariables = { message: 'Required mongodb url', optional: false, }, - SALT_ROUNDS: { - message: 'Required salt rounds for encryption', - optional: false, - }, ACCESS_TOKEN_SECRET: { message: 'Required access token secret', optional: false, @@ -103,10 +99,6 @@ let enviromentVariables = { message: 'Required azure container name', optional: process.env.CLOUD_STORAGE === 'AZURE' ? false : true, }, - MENTOR_SECRET_CODE: { - message: 'Required mentor secret code', - optional: false, - }, ACCESS_TOKEN_EXPIRY: { message: 'Required access token expiry in days', optional: false, @@ -123,11 +115,6 @@ let enviromentVariables = { message: 'Internal Cache Expiry Time', optional: false, }, - - RATING_KAFKA_TOPIC: { - message: 'Kafka Rating Topic', - optional: false, - }, REDIS_HOST: { message: 'Redis Host Url', optional: false, @@ -189,14 +176,6 @@ let enviromentVariables = { message: 'Required Mentoring Service Url', optional: false, }, - MENTOR_INVITATION_EMAIL_TEMPLATE_CODE: { - message: 'Required mentor invitation email template code', - optional: false, - }, - MENTEE_INVITATION_EMAIL_TEMPLATE_CODE: { - message: 'Required mentee invitation email template code', - optional: false, - }, ADMIN_INVITEE_UPLOAD_EMAIL_TEMPLATE_CODE: { message: 'Required admin upload invitee email template code', optional: false, @@ -239,6 +218,23 @@ let enviromentVariables = { optional: false, default: 540000, }, + EMAIL_ID_ENCRYPTION_KEY: { + message: 'Required Email ID Encryption Key', + optional: false, + }, + EMAIL_ID_ENCRYPTION_IV: { + message: 'Required Email ID Encryption IV', + optional: false, + }, + EMAIL_ID_ENCRYPTION_ALGORITHM: { + message: 'Required Email ID Encryption Algorithm', + optional: false, + default: 'aes-256-cbc', + }, + GENERIC_INVITATION_EMAIL_TEMPLATE_CODE: { + message: 'Required generic invitation email template code', + optional: false, + }, } let success = true diff --git a/src/generics/kafka-communication.js b/src/generics/kafka-communication.js index 9644b0753..f1f975e54 100644 --- a/src/generics/kafka-communication.js +++ b/src/generics/kafka-communication.js @@ -10,6 +10,7 @@ const pushEmailToKafka = async (message) => { const payload = { topic: process.env.NOTIFICATION_KAFKA_TOPIC, messages: [{ value: JSON.stringify(message) }] } return await pushPayloadToKafka(payload) } catch (error) { + console.log(error) return error } } diff --git a/src/generics/utils.js b/src/generics/utils.js index 656e30a0d..7a57f1fc8 100644 --- a/src/generics/utils.js +++ b/src/generics/utils.js @@ -167,8 +167,70 @@ function validateInput(input, validationData, modelName) { const errors = [] for (const field of validationData) { const fieldValue = input[field.value] - //console.log('fieldValue', field.allow_custom_entities) - if (!fieldValue || field.allow_custom_entities === true) { + if (modelName && !field.model_names.includes(modelName) && input[field.value]) { + errors.push({ + param: field.value, + msg: `${field.value} is not allowed for the ${modelName} model.`, + }) + } + + function addError(field, value, dataType, message) { + errors.push({ + param: field.value, + msg: `${value} is invalid for data type ${dataType}. ${message}`, + }) + } + + if (fieldValue !== undefined) { + const dataType = field.data_type + + switch (dataType) { + case 'ARRAY[STRING]': + if (Array.isArray(fieldValue)) { + fieldValue.forEach((element) => { + if (typeof element !== 'string') { + addError(field, element, dataType, 'It should be a string') + } else if (field.allow_custom_entities && /[^A-Za-z0-9\s_]/.test(element)) { + addError( + field, + element, + dataType, + 'It should not contain special characters except underscore.' + ) + } + }) + } else { + addError(field, field.value, dataType, '') + } + break + + case 'STRING': + if (typeof fieldValue !== 'string') { + addError(field, fieldValue, dataType, 'It should be a string') + } else if (field.allow_custom_entities && /[^A-Za-z0-9\s_]/.test(fieldValue)) { + addError( + field, + fieldValue, + dataType, + 'It should not contain special characters except underscore.' + ) + } + break + + case 'NUMBER': + console.log('Type of', typeof fieldValue) + if (typeof fieldValue !== 'number') { + addError(field, fieldValue, dataType, '') + } + break + + default: + //isValid = false + break + } + } + + if (!fieldValue || field.allow_custom_entities === true || field.has_entities === false) { continue // Skip validation if the field is not present in the input or allow_custom_entities is true } @@ -187,13 +249,6 @@ function validateInput(input, validationData, modelName) { msg: `${fieldValue} is not a valid entity.`, }) } - - if (modelName && !field.model_names.includes(modelName)) { - errors.push({ - param: field.value, - msg: `${field.value} is not allowed for the ${modelName} model.`, - }) - } } if (errors.length === 0) { @@ -240,10 +295,13 @@ function restructureBody(requestBody, entityData, allowedKeys) { requestBody.custom_entity_text = {} if (!requestBody.meta) requestBody.meta = {} for (const currentFieldName in requestBody) { - const currentFieldValue = requestBody[currentFieldName] + const [currentFieldValue, isFieldValueAnArray] = Array.isArray(requestBody[currentFieldName]) + ? [[...requestBody[currentFieldName]], true] //If the requestBody[currentFieldName] is array, make a copy in currentFieldValue than a reference + : [requestBody[currentFieldName], false] const entityType = entityTypeMap.get(currentFieldName) if (entityType && entityType.get('allow_custom_entities')) { - if (Array.isArray(currentFieldValue)) { + if (isFieldValueAnArray) { + requestBody[currentFieldName] = [] const recognizedEntities = [] const customEntities = [] for (const value of currentFieldValue) { @@ -303,19 +361,11 @@ function processDbResponse(responseBody, entityType) { value: entity.value, label: entity.label, })) - if (matchingValues.length > 0) { + if (matchingValues.length > 0) output[key] = Array.isArray(output[key]) ? matchingValues : matchingValues.find((entity) => entity.value === output[key]) - } else if (Array.isArray(output[key])) { - output[key] = output[key].map((item) => { - if (item.value && item.label) return item - return { - value: item, - label: item, - } - }) - } + else if (Array.isArray(output[key])) output[key] = output[key].filter((item) => item.value && item.label) } if (output.meta && output.meta[key] && entityType.some((entity) => entity.value === output.meta[key].value)) { @@ -375,6 +425,14 @@ const generateWhereClause = (tableName) => { return whereClause } + +const getRoleTitlesFromId = (roleIds = [], roleList = []) => { + return roleIds.map((roleId) => { + const role = roleList.find((r) => r.id === roleId) + return role ? role.title : null + }) +} + module.exports = { generateToken, hashPassword, @@ -404,4 +462,5 @@ module.exports = { isValidEmail, isValidName, generateWhereClause, + getRoleTitlesFromId, } diff --git a/src/health-checks/health-check.js b/src/health-checks/health-check.js index 70691071d..74a870978 100644 --- a/src/health-checks/health-check.js +++ b/src/health-checks/health-check.js @@ -8,15 +8,9 @@ // Dependencies const { v1: uuidv1 } = require('uuid') -const mongodbHealthCheck = require('./mongodb') const kafkaHealthCheck = require('./kafka') const obj = { - MONGO_DB: { - NAME: 'Mongo.db', - FAILED_CODE: 'MONGODB_HEALTH_FAILED', - FAILED_MESSAGE: 'Mongo db is not connected', - }, KAFKA: { NAME: 'kafka', FAILED_CODE: 'KAFKA_HEALTH_FAILED', @@ -28,7 +22,6 @@ const obj = { let health_check = async function (req, res) { let checks = [] - let mongodbConnection = await mongodbHealthCheck.health_check() let kafkaServiceStatus = await kafkaHealthCheck.health_check() checks.push(checkResult('MONGO_DB', mongodbConnection)) diff --git a/src/health-checks/mongodb.js b/src/health-checks/mongodb.js deleted file mode 100644 index b12809415..000000000 --- a/src/health-checks/mongodb.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * name : mongodb.js. - * author : Aman Karki. - * created-date : 17--2021. - * Description : Mongodb health check. - */ - -// Dependencies -const mongoose = require('mongoose') - -function health_check() { - const db = mongoose.createConnection(process.env.MONGODB_URL) - db.on('error', function () { - return false - }) - - db.once('open', function () { - db.close(function () {}) - return true - }) -} - -module.exports = { - health_check: health_check, -} diff --git a/src/integration-test/userRole/responseSchema.js b/src/integration-test/userRole/responseSchema.js new file mode 100644 index 000000000..9022f3d42 --- /dev/null +++ b/src/integration-test/userRole/responseSchema.js @@ -0,0 +1,313 @@ +const createSchema = { + type: 'object', + properties: { + responseCode: { + type: 'string', + }, + message: { + type: 'string', + }, + result: { + type: 'object', + properties: { + title: { + type: 'string', + }, + user_type: { + type: 'integer', + }, + status: { + type: 'string', + }, + visibility: { + type: 'string', + }, + organization_id: { + type: 'integer', + }, + }, + required: ['title', 'user_type', 'status', 'visibility', 'organization_id'], + }, + meta: { + type: 'object', + properties: { + formsVersion: { + type: 'array', + items: {}, + }, + correlation: { + type: 'string', + }, + }, + required: ['formsVersion', 'correlation'], + }, + }, + required: ['responseCode', 'message', 'result', 'meta'], +} + +const updateSchema = { + type: 'object', + properties: { + responseCode: { + type: 'string', + }, + message: { + type: 'string', + }, + result: { + type: 'object', + properties: { + title: { + type: 'string', + }, + user_type: { + type: 'integer', + }, + status: { + type: 'string', + }, + visibility: { + type: 'string', + }, + organization_id: { + type: 'integer', + }, + }, + required: ['title', 'user_type', 'status', 'visibility', 'organization_id'], + }, + meta: { + type: 'object', + properties: { + formsVersion: { + type: 'array', + items: {}, + }, + correlation: { + type: 'string', + }, + }, + required: ['formsVersion', 'correlation'], + }, + }, + required: ['responseCode', 'message', 'result', 'meta'], +} + +const deleteSchema = { + type: 'object', + properties: { + responseCode: { + type: 'string', + }, + message: { + type: 'string', + }, + result: { + type: 'object', + }, + meta: { + type: 'object', + properties: { + formsVersion: { + type: 'array', + items: {}, + }, + correlation: { + type: 'string', + }, + }, + required: ['formsVersion', 'correlation'], + }, + }, + required: ['responseCode', 'message', 'result', 'meta'], +} + +const listSchema = { + type: 'object', + properties: { + responseCode: { + type: 'string', + }, + message: { + type: 'string', + }, + result: { + type: 'object', + properties: { + data: { + type: 'array', + items: [ + { + type: 'object', + properties: { + id: { + type: 'integer', + }, + title: { + type: 'string', + }, + user_type: { + type: 'integer', + }, + visibility: { + type: 'string', + }, + status: { + type: 'string', + }, + organization_id: { + type: 'integer', + }, + }, + required: ['id', 'title', 'user_type', 'visibility', 'status', 'organization_id'], + }, + { + type: 'object', + properties: { + id: { + type: 'integer', + }, + title: { + type: 'string', + }, + user_type: { + type: 'integer', + }, + visibility: { + type: 'string', + }, + status: { + type: 'string', + }, + organization_id: { + type: 'integer', + }, + }, + required: ['id', 'title', 'user_type', 'visibility', 'status', 'organization_id'], + }, + { + type: 'object', + properties: { + id: { + type: 'integer', + }, + title: { + type: 'string', + }, + user_type: { + type: 'integer', + }, + visibility: { + type: 'string', + }, + status: { + type: 'string', + }, + organization_id: { + type: 'integer', + }, + }, + required: ['id', 'title', 'user_type', 'visibility', 'status', 'organization_id'], + }, + { + type: 'object', + properties: { + id: { + type: 'integer', + }, + title: { + type: 'string', + }, + user_type: { + type: 'integer', + }, + visibility: { + type: 'string', + }, + status: { + type: 'string', + }, + organization_id: { + type: 'integer', + }, + }, + required: ['id', 'title', 'user_type', 'visibility', 'status', 'organization_id'], + }, + { + type: 'object', + properties: { + id: { + type: 'integer', + }, + title: { + type: 'string', + }, + user_type: { + type: 'integer', + }, + visibility: { + type: 'string', + }, + status: { + type: 'string', + }, + organization_id: { + type: 'integer', + }, + }, + required: ['id', 'title', 'user_type', 'visibility', 'status', 'organization_id'], + }, + { + type: 'object', + properties: { + id: { + type: 'integer', + }, + title: { + type: 'string', + }, + user_type: { + type: 'integer', + }, + visibility: { + type: 'string', + }, + status: { + type: 'string', + }, + organization_id: { + type: 'integer', + }, + }, + required: ['id', 'title', 'user_type', 'visibility', 'status', 'organization_id'], + }, + ], + }, + count: { + type: 'integer', + }, + }, + required: ['data', 'count'], + }, + meta: { + type: 'object', + properties: { + formsVersion: { + type: 'array', + items: {}, + }, + correlation: { + type: 'string', + }, + }, + required: ['formsVersion', 'correlation'], + }, + }, + required: ['responseCode', 'message', 'result', 'meta'], +} + +module.exports = { + createSchema, + updateSchema, + deleteSchema, + listSchema, +} diff --git a/src/integration-test/userRole/userRole.spec.js b/src/integration-test/userRole/userRole.spec.js new file mode 100644 index 000000000..ec9d589fe --- /dev/null +++ b/src/integration-test/userRole/userRole.spec.js @@ -0,0 +1,65 @@ +const { request, logIn, logError } = require('@commonTests') +let responseSchema = require('./responseSchema') + +describe('/user/v1/userRole', function () { + let userDetails + beforeAll(async () => { + userDetails = await logIn() + }) + + it('/create', async () => { + let res = await request.post('/user/v1/userRole/create').send({ + title: 'system_admin', + user_type: 1, + status: 'ACTIVE', + visibility: 'PUBLIC', + organization_id: 1, + }) + + logError(res) + expect(res.statusCode).toBe(201) + expect(res.body).toMatchSchema(responseSchema.createSchema) + }) + + it('/update', async () => { + let res = await request.post('/user/v1/userRole/update/7').send({ + title: 'system_adm', + user_type: 1, + status: 'ACTIVE', + visibility: 'PUBLIC', + organization_id: 1, + }) + + logError(res) + expect(res.statusCode).toBe(201) + expect(res.body).toMatchSchema(responseSchema.updateSchema) + }) + + it('/delete', async () => { + let res = await request.post('/user/v1/userRole/delete/7') + + logError(res) + expect(res.statusCode).toBe(202) + expect(res.body).toMatchSchema(responseSchema.deleteSchema) + }) + + it('/list', async () => { + let res = await request + .get('/user/v1/userRole/list') + .query({ page: 1, limit: 10, code: 'system', organization_id: 1 }) + + logError(res) + expect(res.statusCode).toBe(200) + expect(res.body).toMatchSchema(responseSchema.listSchema) + }) + + it('/default', async () => { + let res = await request + .get('/user/v1/userRole/default') + .query({ page: 1, limit: 10, code: 'system', organization_id: 1 }) + + logError(res) + expect(res.statusCode).toBe(200) + expect(res.body).toMatchSchema(responseSchema.listSchema) + }) +}) diff --git a/src/locales/en.json b/src/locales/en.json index aa2cb0592..35a25000b 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -104,5 +104,14 @@ "NAME_INVALID": "Name is invalid.", "ROLE_CHANGE_APPROVED": "Your request for becoming a mentor has been approved. Check email or login again to start your journey.", "MATERIALIZED_VIEW_GENERATED_SUCCESSFULLY": "Materialized views generated successfully", - "MATERIALIZED_VIEW_REFRESH_INITIATED_SUCCESSFULLY": "Materialized views refresh initiated successfully" + "MATERIALIZED_VIEW_REFRESH_INITIATED_SUCCESSFULLY": "Materialized views refresh initiated successfully", + "ROLE_CREATED_SUCCESSFULLY": "Roles added successfully", + "ROLE_UPDATED_SUCCESSFULLY": "Roles updated successfully", + "ROLE_DELETED_SUCCESSFULLY": "Module deleted successfully", + "ROLES_FETCHED_SUCCESSFULLY": "Roles fetched successfully", + "ROLE_NOT_UPDATED": "Roles not updated", + "ROLE_ALREADY_DELETED_OR_ROLE_NOT_PRESENT": "Roles already exists OR Roles not present", + "ROLE_NOT_DELETED": "Roles not deleted", + "ROLES_HAS_EMPTY_LIST": "Empty roles list", + "COLUMN_DOES_NOT_EXISTS": "Role column does not exists" } diff --git a/src/middlewares/authenticator.js b/src/middlewares/authenticator.js index f56d84685..7a4fd0928 100644 --- a/src/middlewares/authenticator.js +++ b/src/middlewares/authenticator.js @@ -127,6 +127,7 @@ module.exports = async function (req, res, next) { //update the token role as same as current user role decodedToken.data.roles = roles + decodedToken.data.organization_id = user.organization_id } req.decodedToken = decodedToken.data next() diff --git a/src/middlewares/pagination.js b/src/middlewares/pagination.js index 9aa449861..66590a997 100644 --- a/src/middlewares/pagination.js +++ b/src/middlewares/pagination.js @@ -7,7 +7,7 @@ const common = require('@constants/common') const httpStatus = require('@generics/http-status') function containsSpecialChars(str) { - const specialChars = /[`!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/ + const specialChars = /[`!#$%^&*()_+\-=\[\]{};':"\\|<>\/?~]/ return specialChars.test(str) } module.exports = (req, res, next) => { diff --git a/src/migrations/20220722162957-notificationTemplates.js b/src/migrations/20220722162957-notificationTemplates.js index c0de617d8..825e567c5 100644 --- a/src/migrations/20220722162957-notificationTemplates.js +++ b/src/migrations/20220722162957-notificationTemplates.js @@ -7,7 +7,7 @@ let emailTemplates = [ { code: 'emailotp', subject: 'MentorED - Reset Otp', - body: '

Dear {name},

Your OTP to reset your password is {otp}. Please enter the OTP to reset your password. For your security, please do not share this OTP with anyone.', + body: '

Dear {name},

Your OTP to reset your password is {otp}. Please enter the OTP to reset your password. For security, please do not share this OTP with anyone.', }, { code: 'registrationotp', diff --git a/src/package.json b/src/package.json index b7da86789..43288d152 100644 --- a/src/package.json +++ b/src/package.json @@ -60,13 +60,6 @@ "module-alias": "^2.2.3", "moment": "^2.29.1", "moment-timezone": "^0.5.34", - "mongodb": "^4.1.2", - "mongoose": "^6.1.6", - "mongoose-autopopulate": "^0.16.0", - "mongoose-delete": "^0.5.4", - "mongoose-lean-getters": "^0.3.5", - "mongoose-paginate-v2": "^1.4.2", - "mongoose-timestamp": "^0.6.0", "p-each-series": "^2.1.0", "path-to-regexp": "^6.2.1", "pg": "^8.11.0", diff --git a/src/sample.csv b/src/sample.csv index 98b1e86ee..f1c308291 100644 --- a/src/sample.csv +++ b/src/sample.csv @@ -1,3 +1,4 @@ name,email,roles Sarah,sarah@tunerlabs.com,mentee John,john@tunerlabs.com,mentor +Pradeep,pradeep@tunerlabs.com,"mentor,session_manager" diff --git a/src/scripts/encryptDecryptEmails.js b/src/scripts/encryptDecryptEmails.js new file mode 100644 index 000000000..3050fd6a8 --- /dev/null +++ b/src/scripts/encryptDecryptEmails.js @@ -0,0 +1,66 @@ +'use strict' +const { Sequelize } = require('sequelize') +require('dotenv').config({ path: '../.env' }) +const emailEncryption = require('../utils/emailEncryption') + +const nodeEnv = process.env.NODE_ENV || 'development' + +let databaseUrl + +switch (nodeEnv) { + case 'production': + databaseUrl = process.env.PROD_DATABASE_URL + break + case 'test': + databaseUrl = process.env.TEST_DATABASE_URL + break + default: + databaseUrl = process.env.DEV_DATABASE_URL +} + +if (!databaseUrl) { + console.error(`${nodeEnv} DATABASE_URL not found in environment variables.`) + process.exit(1) +} + +const sequelize = new Sequelize(databaseUrl, { + dialect: 'postgres', + logging: process.env.NODE_ENV === 'development' ? console.log : false, +}) + +const processTable = async (tableName, operation) => { + const records = await sequelize.query(`SELECT * FROM ${tableName};`, { + type: Sequelize.QueryTypes.SELECT, + }) + + for (const record of records) { + const columnValue = + operation === 'encrypt' ? emailEncryption.encrypt(record.email) : emailEncryption.decrypt(record.email) + await sequelize.query(`UPDATE ${tableName} SET email = '${columnValue}' WHERE id = ${record.id};`) + } + + console.log(`Finished processing ${tableName} table.`) +} + +const main = async () => { + const operation = process.argv[2] + + if (operation !== 'encrypt' && operation !== 'decrypt') { + console.error('Invalid operation. Please use "encrypt" or "decrypt".') + process.exit(1) + } + + try { + await processTable('organization_user_invites', operation) + await processTable('users', operation) + await processTable('users_credentials', operation) + } catch (err) { + console.error(err) + process.exit(1) + } + + console.log('Script completed successfully.') + process.exit(0) +} + +main() diff --git a/src/scripts/generateEncyrptionKeys.js b/src/scripts/generateEncyrptionKeys.js new file mode 100644 index 000000000..88f53d886 --- /dev/null +++ b/src/scripts/generateEncyrptionKeys.js @@ -0,0 +1,3 @@ +const crypto = require('crypto') +console.log('Email Id Encryption Key: ', crypto.randomBytes(32).toString('hex')) +console.log('Email Id Encryption IV:', crypto.randomBytes(16).toString('hex')) diff --git a/src/scripts/readme.md b/src/scripts/readme.md index ae1b1fe4d..060c6fc3f 100644 --- a/src/scripts/readme.md +++ b/src/scripts/readme.md @@ -8,3 +8,23 @@ ```bash node -r module-alias/register uploadSampleCSV.js ``` + +## Email Encryption/Decryption Script + +- Navigate to the script folder using the following command + ```bash + cd src/script + ``` +- Run the **encryptDecryptEmails.js** using the following commands for encryption and decryption respectively. + + Encryption: + + ``` + node ./encryptDecryptEmails.js encrypt + ``` + + Decryption: + + ``` + node ./encryptDecryptEmails.js decrypt + ``` diff --git a/src/scripts/updateEmailTemplates.js b/src/scripts/updateEmailTemplates.js deleted file mode 100644 index 64539c2c9..000000000 --- a/src/scripts/updateEmailTemplates.js +++ /dev/null @@ -1,28 +0,0 @@ -const mongoose = require('mongoose') -require('dotenv').config({ path: '../.env' }) -require('../configs/mongodb')() -const NotificationTemplate = require('../db/notification-template/model') - -const updateNotificationTemplate = async (code, newBody) => { - try { - const filter = { code } - const update = { body: newBody } - const options = { new: true } - const updatedDoc = await NotificationTemplate.findOneAndUpdate(filter, update, options) - - if (!updatedDoc) { - console.log('Document not found') - } else { - console.log('Updated document:', updatedDoc) - } - } catch (error) { - console.error('Error updating the document:', error) - } finally { - mongoose.disconnect() - } -} - -// Usage: -const newBodyContent = - "

MentorED

" -updateNotificationTemplate('email_header', newBodyContent) diff --git a/src/scripts/viewsScript.js b/src/scripts/viewsScript.js index f425f0b56..9a997ff9f 100644 --- a/src/scripts/viewsScript.js +++ b/src/scripts/viewsScript.js @@ -1,8 +1,9 @@ // Dependencies +require('module-alias/register') + const request = require('request') require('dotenv').config({ path: '../.env' }) const entityTypeQueries = require('../database/queries/entityType') - // Data const schedulerServiceUrl = process.env.SCHEDULER_SERVICE_HOST // Port address on which the scheduler service is running const mentoringBaseurl = `http://localhost:${process.env.APPLICATION_PORT}` diff --git a/src/services/account.js b/src/services/account.js index 56d1df106..98e5590c8 100644 --- a/src/services/account.js +++ b/src/services/account.js @@ -21,12 +21,12 @@ const kafkaCommunication = require('@generics/kafka-communication') const roleQueries = require('@database/queries/userRole') const orgDomainQueries = require('@database/queries/orgDomain') const userInviteQueries = require('@database/queries/orgUserInvite') -const FILESTREAM = require('@generics/file-stream') const entityTypeQueries = require('@database/queries/entityType') const utils = require('@generics/utils') const { Op } = require('sequelize') const { removeDefaultOrgEntityTypes } = require('@generics/utils') const UserCredentialQueries = require('@database/queries/userCredential') +const emailEncryption = require('@utils/emailEncryption') module.exports = class AccountHelper { /** * create account @@ -45,9 +45,10 @@ module.exports = class AccountHelper { const projection = ['password', 'refresh_tokens'] try { - const email = bodyData.email.toLowerCase() + const plaintextEmailId = bodyData.email.toLowerCase() + const encryptedEmailId = emailEncryption.encrypt(plaintextEmailId) let user = await UserCredentialQueries.findOne({ - email: email.toLowerCase(), + email: encryptedEmailId, password: { [Op.ne]: null, }, @@ -61,7 +62,7 @@ module.exports = class AccountHelper { } if (process.env.ENABLE_EMAIL_OTP_VERIFICATION === 'true') { - const redisData = await utilsHelper.redisGet(email) + const redisData = await utilsHelper.redisGet(encryptedEmailId) if (!redisData || redisData.otp != bodyData.otp) { return common.failureResponse({ message: 'OTP_INVALID', @@ -81,7 +82,7 @@ module.exports = class AccountHelper { const invitedUserId = await UserCredentialQueries.findOne( { - email: email, + email: encryptedEmailId, organization_user_invite_id: { [Op.ne]: null, }, @@ -89,12 +90,13 @@ module.exports = class AccountHelper { [Op.eq]: null, }, }, - { attributes: ['organization_user_invite_id'], raw: true } + { attributes: ['organization_user_invite_id', 'organization_id'], raw: true } ) if (invitedUserId) { invitedUserMatch = await userInviteQueries.findOne({ id: invitedUserId.organization_user_invite_id, + organization_id: invitedUserId.organization_id, }) //add org id here to optimize the query } @@ -102,7 +104,7 @@ module.exports = class AccountHelper { if (invitedUserMatch) { bodyData.organization_id = invitedUserMatch.organization_id roles = invitedUserMatch.roles - role = await roleQueries.findOne( + role = await roleQueries.findAll( { id: invitedUserMatch.roles }, { attributes: { @@ -111,7 +113,7 @@ module.exports = class AccountHelper { } ) - if (!role) { + if (!role.length > 0) { return common.failureResponse({ message: 'ROLE_NOT_FOUND', statusCode: httpStatusCode.not_acceptable, @@ -119,24 +121,25 @@ module.exports = class AccountHelper { }) } - if (role.title === common.ORG_ADMIN_ROLE) { - isOrgAdmin = true - - const defaultRole = await roleQueries.findOne( - { title: process.env.DEFAULT_ROLE }, - { - attributes: { - exclude: ['created_at', 'updated_at', 'deleted_at'], - }, - } - ) + role.forEach(async (eachRole) => { + if (eachRole.title === common.ORG_ADMIN_ROLE) { + const defaultRole = await roleQueries.findOne( + { title: process.env.DEFAULT_ROLE }, + { + attributes: { + exclude: ['created_at', 'updated_at', 'deleted_at'], + }, + } + ) - roles.push(defaultRole.id) - } + roles.push(defaultRole.id) + isOrgAdmin = true + } + }) bodyData.roles = roles } else { //find organization from email domain - let emailDomain = utilsHelper.extractDomainFromEmail(email) + let emailDomain = utilsHelper.extractDomainFromEmail(plaintextEmailId) let domainDetails = await orgDomainQueries.findOne({ domain: emailDomain, }) @@ -175,10 +178,11 @@ module.exports = class AccountHelper { } delete bodyData.role + bodyData.email = encryptedEmailId const insertedUser = await userQueries.create(bodyData) const userCredentialsBody = { - email: bodyData.email, + email: encryptedEmailId, password: bodyData.password, organization_id: insertedUser.organization_id, user_id: insertedUser.id, @@ -187,7 +191,7 @@ module.exports = class AccountHelper { if (invitedUserMatch) { userCredentials = await UserCredentialQueries.updateUser( { - email: bodyData.email, + email: encryptedEmailId, }, { user_id: insertedUser.id, password: bodyData.password }, { @@ -231,6 +235,23 @@ module.exports = class AccountHelper { user.user_roles = roleData + // format the roles for email template + let roleArray = [] + if (roleData.length > 0) { + const mentorRoleExists = roleData.some((role) => role.title === common.MENTOR_ROLE) + if (mentorRoleExists) { + const updatedRoleList = roleData.filter((role) => role.title !== common.MENTEE_ROLE) + roleArray = _.map(updatedRoleList, 'title') + } + } + + let roleToString = + roleArray.length > 0 + ? roleArray + .map((role) => role.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase())) + .join(' and ') + : '' + const accessToken = utilsHelper.generateToken( tokenDetail, process.env.ACCESS_TOKEN_SECRET, @@ -255,7 +276,7 @@ module.exports = class AccountHelper { } await userQueries.updateUser({ id: user.id, organization_id: userCredentials.organization_id }, update) - await utilsHelper.redisDel(email) + await utilsHelper.redisDel(encryptedEmailId) //make the user as org admin if (isOrgAdmin) { @@ -282,11 +303,13 @@ module.exports = class AccountHelper { const payload = { type: common.notificationEmailType, email: { - to: email, + to: plaintextEmailId, subject: templateData.subject, body: utilsHelper.composeEmailBody(templateData.body, { name: bodyData.name, appName: process.env.APP_NAME, + roles: roleToString || '', + portalURL: process.env.PORTAL_URL, }), }, } @@ -294,6 +317,7 @@ module.exports = class AccountHelper { await kafkaCommunication.pushEmailToKafka(payload) } + result.user.email = plaintextEmailId return common.successResponse({ statusCode: httpStatusCode.created, message: 'USER_CREATED_SUCCESSFULLY', @@ -317,8 +341,10 @@ module.exports = class AccountHelper { static async login(bodyData) { try { + const plaintextEmailId = bodyData.email.toLowerCase() + const encryptedEmailId = emailEncryption.encrypt(plaintextEmailId) const userCredentials = await UserCredentialQueries.findOne({ - email: bodyData.email.toLowerCase(), + email: encryptedEmailId, password: { [Op.ne]: null, }, @@ -425,20 +451,23 @@ module.exports = class AccountHelper { { attributes: ['id'] } ) let defaultOrgId = defaultOrg.id + const modelName = await userQueries.getModelName() let validationData = await entityTypeQueries.findUserEntityTypesAndEntities({ status: 'ACTIVE', organization_id: { [Op.in]: [user.organization_id, defaultOrgId], }, + model_names: { [Op.contains]: [modelName] }, }) + const prunedEntities = removeDefaultOrgEntityTypes(validationData, user.organization_id) user = utils.processDbResponse(user, prunedEntities) if (user && user.image) { user.image = await utils.getDownloadableUrl(user.image) } - + user.email = plaintextEmailId const result = { access_token: accessToken, refresh_token: refreshToken, user } return common.successResponse({ @@ -457,14 +486,15 @@ module.exports = class AccountHelper { * @method * @name logout * @param {Object} req -request data. - * @param {string} bodyData.loggedInId - user id. + * @param {Integer} user_id - user id. + * @param {Integer} organization_id - organization id. * @param {string} bodyData.refresh_token - refresh token. * @returns {JSON} - returns accounts loggedout information. */ - static async logout(bodyData) { + static async logout(bodyData, user_id, organization_id) { try { - const user = await userQueries.findByPk(bodyData.loggedInId) + const user = await userQueries.findOne({ id: user_id, organization_id }) if (!user) { return common.failureResponse({ message: 'USER_NOT_FOUND', @@ -483,6 +513,7 @@ module.exports = class AccountHelper { { id: user.id, organization_id: user.organization_id }, { refresh_tokens: refreshTokens } ) + /* If user doc not updated because of stored token does not matched with bodyData.refreshToken */ if (affectedRows == 0) { return common.failureResponse({ @@ -577,96 +608,82 @@ module.exports = class AccountHelper { static async generateOtp(bodyData) { try { - let otp - let isValidOtpExist = true + const plaintextEmailId = bodyData.email.toLowerCase() + const encryptedEmailId = emailEncryption.encrypt(plaintextEmailId) const userCredentials = await UserCredentialQueries.findOne({ - email: bodyData.email.toLowerCase(), + email: encryptedEmailId, password: { [Op.ne]: null, }, }) - if (!userCredentials) { + if (!userCredentials) return common.failureResponse({ message: 'USER_DOESNOT_EXISTS', statusCode: httpStatusCode.bad_request, responseCode: 'CLIENT_ERROR', }) - } + const user = await userQueries.findOne({ id: userCredentials.user_id, organization_id: userCredentials.organization_id, }) - if (!user) { + if (!user) return common.failureResponse({ message: 'USER_DOESNOT_EXISTS', statusCode: httpStatusCode.bad_request, responseCode: 'CLIENT_ERROR', }) - } - const userData = await utilsHelper.redisGet(bodyData.email.toLowerCase()) - - if (userData && userData.action === 'forgetpassword') { - otp = userData.otp // If valid then get previuosly generated otp - console.log(otp) - } else { - isValidOtpExist = false - } - - const isPasswordCorrect = bcryptJs.compareSync(bodyData.password, userCredentials.password) - if (isPasswordCorrect) { + const isPasswordSame = bcryptJs.compareSync(bodyData.password, userCredentials.password) + if (isPasswordSame) return common.failureResponse({ message: 'RESET_PREVIOUS_PASSWORD', statusCode: httpStatusCode.bad_request, responseCode: 'CLIENT_ERROR', }) - } - if (!isValidOtpExist) { - otp = Math.floor(Math.random() * 900000 + 100000) // 6 digit otp + const userData = await utilsHelper.redisGet(encryptedEmailId) + const [otp, isNew] = + userData && userData.action === 'forgetpassword' + ? [userData.otp, false] + : [Math.floor(Math.random() * 900000 + 100000), true] + if (isNew) { const redisData = { - verify: bodyData.email.toLowerCase(), + verify: encryptedEmailId, action: 'forgetpassword', otp, } - const res = await utilsHelper.redisSet( - bodyData.email.toLowerCase(), - redisData, - common.otpExpirationTime - ) - if (res !== 'OK') { + const res = await utilsHelper.redisSet(encryptedEmailId, redisData, common.otpExpirationTime) + if (res !== 'OK') return common.failureResponse({ message: 'UNABLE_TO_SEND_OTP', statusCode: httpStatusCode.internal_server_error, responseCode: 'SERVER_ERROR', }) - } } const templateData = await notificationTemplateQueries.findOneEmailTemplate( process.env.OTP_EMAIL_TEMPLATE_CODE, user.organization_id ) - if (templateData) { - // Push otp to kafka const payload = { type: common.notificationEmailType, email: { - to: bodyData.email, + to: plaintextEmailId, subject: templateData.subject, body: utilsHelper.composeEmailBody(templateData.body, { name: user.name, otp }), }, } - await kafkaCommunication.pushEmailToKafka(payload) } - + if (process.env.APPLICATION_ENV === 'development') console.log({ otp, isNew }) return common.successResponse({ statusCode: httpStatusCode.ok, message: 'OTP_SENT_SUCCESSFULLY', }) } catch (error) { + console.log(error) throw error } } @@ -681,79 +698,60 @@ module.exports = class AccountHelper { */ static async registrationOtp(bodyData) { - try { - let otp - let isValidOtpExist = true - const userCredentials = await UserCredentialQueries.findOne({ - email: bodyData.email.toLowerCase(), - password: { - [Op.ne]: null, - }, + const plaintextEmailId = bodyData.email.toLowerCase() + const encryptedEmailId = emailEncryption.encrypt(plaintextEmailId) + const userCredentials = await UserCredentialQueries.findOne({ + email: encryptedEmailId, + password: { + [Op.ne]: null, + }, + }) + if (userCredentials) + return common.failureResponse({ + message: 'USER_ALREADY_EXISTS', + statusCode: httpStatusCode.bad_request, + responseCode: 'CLIENT_ERROR', }) - if (userCredentials) { - return common.failureResponse({ - message: 'USER_ALREADY_EXISTS', - statusCode: httpStatusCode.bad_request, - responseCode: 'CLIENT_ERROR', - }) - } - - const userData = await utilsHelper.redisGet(bodyData.email.toLowerCase()) - - if (userData && userData.action === 'signup') { - otp = userData.otp // If valid then get previuosly generated otp - } else { - isValidOtpExist = false - } - if (!isValidOtpExist) { - otp = Math.floor(Math.random() * 900000 + 100000) // 6 digit otp - const redisData = { - verify: bodyData.email.toLowerCase(), - action: 'signup', - otp, - } - const res = await utilsHelper.redisSet( - bodyData.email.toLowerCase(), - redisData, - common.otpExpirationTime - ) - if (res !== 'OK') { - return common.failureResponse({ - message: 'UNABLE_TO_SEND_OTP', - statusCode: httpStatusCode.internal_server_error, - responseCode: 'SERVER_ERROR', - }) - } + const userData = await utilsHelper.redisGet(encryptedEmailId) + const [otp, isNew] = + userData && userData.action === 'signup' + ? [userData.otp, false] + : [Math.floor(Math.random() * 900000 + 100000), true] + if (isNew) { + const redisData = { + verify: encryptedEmailId, + action: 'signup', + otp, } - - const templateData = await notificationTemplateQueries.findOneEmailTemplate( - process.env.REGISTRATION_OTP_EMAIL_TEMPLATE_CODE - ) - - if (templateData) { - // Push otp to kafka - const payload = { - type: common.notificationEmailType, - email: { - to: bodyData.email, - subject: templateData.subject, - body: utilsHelper.composeEmailBody(templateData.body, { name: bodyData.name, otp }), - }, - } - - await kafkaCommunication.pushEmailToKafka(payload) + const res = await utilsHelper.redisSet(encryptedEmailId, redisData, common.otpExpirationTime) + if (res !== 'OK') { + return common.failureResponse({ + message: 'UNABLE_TO_SEND_OTP', + statusCode: httpStatusCode.internal_server_error, + responseCode: 'SERVER_ERROR', + }) } - if (process.env.APPLICATION_ENV === 'development') { - console.log(otp) + } + const templateData = await notificationTemplateQueries.findOneEmailTemplate( + process.env.REGISTRATION_OTP_EMAIL_TEMPLATE_CODE + ) + if (templateData) { + const payload = { + type: common.notificationEmailType, + email: { + to: plaintextEmailId, + subject: templateData.subject, + body: utilsHelper.composeEmailBody(templateData.body, { name: bodyData.name, otp }), + }, } - return common.successResponse({ - statusCode: httpStatusCode.ok, - message: 'REGISTRATION_OTP_SENT_SUCCESSFULLY', - }) - } catch (error) { - throw error + await kafkaCommunication.pushEmailToKafka(payload) } + if (process.env.APPLICATION_ENV === 'development') console.log(otp) + return common.successResponse({ + statusCode: httpStatusCode.ok, + message: 'REGISTRATION_OTP_SENT_SUCCESSFULLY', + }) } /** @@ -770,8 +768,10 @@ module.exports = class AccountHelper { static async resetPassword(bodyData) { const projection = ['location'] try { + const plaintextEmailId = bodyData.email.toLowerCase() + const encryptedEmailId = emailEncryption.encrypt(plaintextEmailId) const userCredentials = await UserCredentialQueries.findOne({ - email: bodyData.email.toLowerCase(), + email: encryptedEmailId, password: { [Op.ne]: null, }, @@ -806,10 +806,9 @@ module.exports = class AccountHelper { responseCode: 'CLIENT_ERROR', }) } - user.user_roles = roles - const redisData = await utilsHelper.redisGet(bodyData.email.toLowerCase()) + const redisData = await utilsHelper.redisGet(encryptedEmailId) if (!redisData || redisData.otp != bodyData.otp) { return common.failureResponse({ message: 'RESET_OTP_INVALID', @@ -817,18 +816,15 @@ module.exports = class AccountHelper { responseCode: 'CLIENT_ERROR', }) } - const isPasswordCorrect = bcryptJs.compareSync(bodyData.password, userCredentials.password) - if (isPasswordCorrect) { + const isPasswordSame = bcryptJs.compareSync(bodyData.password, userCredentials.password) + if (isPasswordSame) { return common.failureResponse({ message: 'RESET_PREVIOUS_PASSWORD', statusCode: httpStatusCode.bad_request, responseCode: 'CLIENT_ERROR', }) } - - const salt = bcryptJs.genSaltSync(10) - bodyData.password = bcryptJs.hashSync(bodyData.password, salt) - + bodyData.password = utilsHelper.hashPassword(bodyData.password) const tokenDetail = { data: { id: user.id, @@ -851,11 +847,9 @@ module.exports = class AccountHelper { let noOfTokensToKeep = common.refreshTokenLimit - 1 let refreshTokens = [] - if (userTokens && userTokens.length >= common.refreshTokenLimit) { + if (userTokens && userTokens.length >= common.refreshTokenLimit) refreshTokens = userTokens.splice(-noOfTokensToKeep) - } else { - refreshTokens = userTokens - } + else refreshTokens = userTokens refreshTokens.push(currentToken) const updateParams = { @@ -870,84 +864,25 @@ module.exports = class AccountHelper { ) await UserCredentialQueries.updateUser( { - email: userCredentials.email, + email: encryptedEmailId, }, { password: bodyData.password } ) - await utilsHelper.redisDel(bodyData.email.toLowerCase()) + await utilsHelper.redisDel(encryptedEmailId) - /* Mongoose schema is in strict mode, so can not delete otpInfo directly */ delete user.password delete user.otpInfo // Check if user and user.image exist, then fetch a downloadable URL for the image - if (user && user.image) { - user.image = await utils.getDownloadableUrl(user.image) - } - + if (user && user.image) user.image = await utils.getDownloadableUrl(user.image) const result = { access_token: accessToken, refresh_token: refreshToken, user } - return common.successResponse({ statusCode: httpStatusCode.ok, message: 'PASSWORD_RESET_SUCCESSFULLY', result, }) } catch (error) { - throw error - } - } - - //Is this api used ? - /** - * Bulk create mentors - * @method - * @name bulkCreateMentors - * @param {Array} mentors - mentor details. - * @param {Object} tokenInformation - token details. - * @returns {CSV} - created mentors. - */ - static async bulkCreateMentors(mentors, tokenInformation) { - try { - const systemUser = await systemUserData.findUsersByEmail(tokenInformation.email) - - if (!systemUser) { - return common.failureResponse({ - message: 'USER_DOESNOT_EXISTS', - statusCode: httpStatusCode.bad_request, - responseCode: 'CLIENT_ERROR', - }) - } - - if (systemUser.role.toLowerCase() !== 'admin') { - return common.failureResponse({ - message: 'NOT_AN_ADMIN', - statusCode: httpStatusCode.bad_request, - responseCode: 'CLIENT_ERROR', - }) - } - - const fileName = 'mentors-creation' - let fileStream = new FILESTREAM(fileName) - let input = fileStream.initStream() - - ;(async function () { - await fileStream.getProcessorPromise() - return { - isResponseAStream: true, - fileNameWithPath: fileStream.fileNameWithPath(), - } - })() - - for (const mentor of mentors) { - mentor.isAMentor = true - const data = await this.create(mentor) - mentor.email = mentor.email.address - mentor.status = data.message - input.push(mentor) - } - - input.push(null) - } catch (error) { + console.log(error) throw error } } @@ -1041,12 +976,11 @@ module.exports = class AccountHelper { params.pageSize, params.searchText ) - console.log('USERS:', users) let foundKeys = {} let result = [] /* Required to resolve all promises first before preparing response object else sometime - it will push unresolved promise object if you put this logic in below for loop */ + it will push unresolved promise object if you put this logic in below for loop */ await Promise.all( users.data.map(async (user) => { @@ -1145,6 +1079,8 @@ module.exports = class AccountHelper { */ static async changeRole(bodyData) { try { + const plaintextEmailId = bodyData.email.toLowerCase() + const encryptedEmailId = emailEncryption.encrypt(plaintextEmailId) let role = await roleQueries.findOne({ title: bodyData.role.toLowerCase() }) if (!role) { return common.failureResponse({ @@ -1154,7 +1090,7 @@ module.exports = class AccountHelper { }) } const userCredentials = await UserCredentialQueries.findOne({ - email: bodyData.email.toLowerCase(), + email: encryptedEmailId, password: { [Op.ne]: null, }, @@ -1184,6 +1120,7 @@ module.exports = class AccountHelper { message: 'USER_ROLE_UPDATED_SUCCESSFULLY', }) } catch (error) { + console.log(error) throw error } } @@ -1206,20 +1143,35 @@ module.exports = class AccountHelper { */ static async search(params) { try { - let role = await roleQueries.findOne( - { title: params.query.type.toLowerCase() }, + const types = params.query.type.toLowerCase().split(',') + const roles = await roleQueries.findAll( + { title: types }, { attributes: ['id'], } ) + const roleIds = roles.map((role) => role.id) + let emailIds = [] + let searchText = [] + + if (params.searchText) { + searchText = params.searchText.split(',') + } + searchText.forEach((element) => { + if (utils.isValidEmail(element)) { + emailIds.push(emailEncryption.encrypt(element.toLowerCase())) + } + }) + let users = await userQueries.listUsersFromView( - role && role.id ? role.id : '', + roleIds ? roleIds : [], params.query.organization_id ? params.query.organization_id : '', params.pageNo, params.pageSize, - params.searchText, - params.body.user_ids ? params.body.user_ids : false + emailIds.length == 0 ? params.searchText : false, + params.body.user_ids ? params.body.user_ids : false, + emailIds.length > 0 ? emailIds : false ) /* Required to resolve all promises first before preparing response object else sometime diff --git a/src/services/admin.js b/src/services/admin.js index 2a8d681ab..325cfd655 100644 --- a/src/services/admin.js +++ b/src/services/admin.js @@ -18,6 +18,7 @@ const { eventBroadcaster } = require('@helpers/eventBroadcaster') const { Op } = require('sequelize') const UserCredentialQueries = require('@database/queries/userCredential') const adminService = require('../generics/materializedViews') +const emailEncryption = require('@utils/emailEncryption') module.exports = class AdminHelper { /** @@ -41,7 +42,7 @@ module.exports = class AdminHelper { let updateParams = _generateUpdateParams(userId) const removeKeys = _.omit(user, _removeUserKeys()) const update = _.merge(removeKeys, updateParams) - await userQueries.updateUser({ email: user.email }, update) + await userQueries.updateUser({ id: user.id, organization_id: user.organization_id }, update) delete update.id await UserCredentialQueries.forceDeleteUserWithEmail(user.email) @@ -70,8 +71,9 @@ module.exports = class AdminHelper { */ static async create(bodyData) { try { - const email = bodyData.email.toLowerCase() - const user = await UserCredentialQueries.findOne({ email: email }) + const plaintextEmailId = bodyData.email.toLowerCase() + const encryptedEmailId = emailEncryption.encrypt(plaintextEmailId) + const user = await UserCredentialQueries.findOne({ email: encryptedEmailId }) if (user) { return common.failureResponse({ @@ -99,8 +101,8 @@ module.exports = class AdminHelper { ) bodyData.organization_id = organization.id } - bodyData.password = utils.hashPassword(bodyData.password) + bodyData.email = encryptedEmailId const createdUser = await userQueries.create(bodyData) const userCredentialsBody = { email: bodyData.email, @@ -108,12 +110,20 @@ module.exports = class AdminHelper { organization_id: createdUser.organization_id, user_id: createdUser.id, } - await UserCredentialQueries.create(userCredentialsBody) + const userData = await UserCredentialQueries.create(userCredentialsBody) + if (!userData?.id) { + return common.failureResponse({ + message: userData, + statusCode: httpStatusCode.not_acceptable, + responseCode: 'CLIENT_ERROR', + }) + } return common.successResponse({ statusCode: httpStatusCode.created, message: 'USER_CREATED_SUCCESSFULLY', }) } catch (error) { + console.log(error) throw error } } @@ -129,7 +139,9 @@ module.exports = class AdminHelper { */ static async login(bodyData) { try { - const userCredentials = await UserCredentialQueries.findOne({ email: bodyData.email.toLowerCase() }) + const plaintextEmailId = bodyData.email.toLowerCase() + const encryptedEmailId = emailEncryption.encrypt(plaintextEmailId) + const userCredentials = await UserCredentialQueries.findOne({ email: encryptedEmailId }) if (!userCredentials) { return common.failureResponse({ message: 'USER_DOESNOT_EXISTS', @@ -197,6 +209,7 @@ module.exports = class AdminHelper { result, }) } catch (error) { + console.log(error) throw error } } @@ -213,8 +226,10 @@ module.exports = class AdminHelper { try { let userCredentials if (emailId) { + const plaintextEmailId = emailId.toLowerCase() + const encryptedEmailId = emailEncryption.encrypt(plaintextEmailId) userCredentials = await UserCredentialQueries.findOne({ - email: emailId.toLowerCase(), + email: encryptedEmailId, }) } else { userCredentials = await UserCredentialQueries.findOne({ @@ -437,6 +452,7 @@ module.exports = class AdminHelper { */ static async deactivateUser(bodyData, loggedInUserId) { try { + let filterQuery = {} for (let item in bodyData) { filterQuery[item] = { [Op.in]: bodyData[item], @@ -446,8 +462,9 @@ module.exports = class AdminHelper { let userIds = [] if (bodyData.email) { + const encryptedEmailIds = bodyData.email.map((email) => emailEncryption.encrypt(email.toLowerCase())) const userCredentials = await UserCredentialQueries.findAll( - { email: { [Op.in]: bodyData.email } }, + { email: { [Op.in]: encryptedEmailIds } }, { attributes: ['user_id'], } @@ -484,9 +501,11 @@ module.exports = class AdminHelper { message: 'USER_DEACTIVATED', }) } catch (error) { + console.log(error) throw error } } + static async triggerViewRebuild(decodedToken) { try { const result = await adminService.triggerViewBuild() diff --git a/src/services/entityType.js b/src/services/entityType.js index cb287ab96..b268c1c66 100644 --- a/src/services/entityType.js +++ b/src/services/entityType.js @@ -52,12 +52,12 @@ module.exports = class EntityHelper { static async update(bodyData, id, loggedInUserId, orgId) { ;(bodyData.updated_by = loggedInUserId), (bodyData.organization_id = orgId) try { - const [updateCount, updatedEntityType] = await entityTypeQueries.updateOneEntityType(id, bodyData, { + const [updateCount, updatedEntityType] = await entityTypeQueries.updateOneEntityType(id, orgId, bodyData, { returning: true, raw: true, }) - if (updateCount === '0') { + if (updateCount === 0) { return common.failureResponse({ message: 'ENTITY_TYPE_NOT_FOUND', statusCode: httpStatusCode.bad_request, @@ -82,11 +82,17 @@ module.exports = class EntityHelper { } } - static async readAllSystemEntityTypes(organization_id) { + static async readAllSystemEntityTypes(orgId) { try { + const defaultOrg = await organizationQueries.findOne( + { code: process.env.DEFAULT_ORGANISATION_CODE }, + { attributes: ['id'] } + ) + const defaultOrgId = defaultOrg.id + const attributes = ['value', 'label', 'id'] - const entities = await entityTypeQueries.findAllEntityTypes(organization_id, attributes) + const entities = await entityTypeQueries.findAllEntityTypes([orgId, defaultOrgId], attributes) if (!entities.length) { return common.failureResponse({ @@ -140,10 +146,10 @@ module.exports = class EntityHelper { * @returns {JSON} - Entity deleted response. */ - static async delete(id) { + static async delete(id, orgId) { try { - const deleteCount = await entityTypeQueries.deleteOneEntityType(id) - if (deleteCount === '0') { + const deleteCount = await entityTypeQueries.deleteOneEntityType(id, orgId) + if (deleteCount === 0) { return common.failureResponse({ message: 'ENTITY_TYPE_NOT_FOUND', statusCode: httpStatusCode.bad_request, diff --git a/src/services/mentors.js b/src/services/mentors.js deleted file mode 100644 index 4c3960595..000000000 --- a/src/services/mentors.js +++ /dev/null @@ -1,84 +0,0 @@ -/** - * name : mentors.js - * author : Aman - * created-date : 12-Nov-2021 - * Description : User Profile Service Helper. - */ - -// Dependencies -const usersData = require('@db/users/queries') -const common = require('@constants/common') -const httpStatusCode = require('@generics/http-status') -const utilsHelper = require('@generics/utils') - -module.exports = class MentorsHelper { - /** - * List of mentors - * @method - * @name list - * @param {string} pageNo -page number. - * @param {string} pageSize -request data. - * @param {string} searchText - search text. - * @param {string} userId - logged in user id. - * @returns {Array} - Mentors list - */ - static async list(page, limit, search, userId, match) { - try { - const mentors = await usersData.searchMentors(page, limit, search, userId, match) - - if (mentors[0].data.length < 1) { - return common.successResponse({ - statusCode: httpStatusCode.ok, - message: 'MENTOR_LIST', - result: { - data: [], - count: 0, - }, - }) - } - - let foundKeys = {} - let result = [] - - /* Required to resolve all promises first before preparing response object else sometime - it will push unresolved promise object if you put this logic in below for loop */ - - await Promise.all( - mentors[0].data.map(async (mentor) => { - /* Assigned image url from the stored location */ - if (mentor.image) { - mentor.image = await utilsHelper.getDownloadableUrl(mentor.image) - } - return mentor - }) - ) - - for (let mentor of mentors[0].data) { - let firstChar = mentor.name.charAt(0) - firstChar = firstChar.toUpperCase() - - if (!foundKeys[firstChar]) { - result.push({ - key: firstChar, - values: [mentor], - }) - foundKeys[firstChar] = result.length - } else { - let index = foundKeys[firstChar] - 1 - result[index].values.push(mentor) - } - } - - return common.successResponse({ - statusCode: httpStatusCode.ok, - message: 'MENTOR_LIST', - result: { - data: result, - count: mentors[0].count, - }, - }) - } catch (error) { - throw error - } - } -} diff --git a/src/services/org-admin.js b/src/services/org-admin.js index bf34b3e93..25cd92c2c 100644 --- a/src/services/org-admin.js +++ b/src/services/org-admin.js @@ -23,6 +23,7 @@ const { eventBroadcaster } = require('@helpers/eventBroadcaster') const { Queue } = require('bullmq') const { Op } = require('sequelize') const UserCredentialQueries = require('@database/queries/userCredential') +const emailEncryption = require('@utils/emailEncryption') module.exports = class OrgAdminHelper { /** @@ -37,7 +38,11 @@ module.exports = class OrgAdminHelper { static async bulkUserCreate(filePath, tokenInformation) { try { const { id, organization_id } = tokenInformation - const { name, email } = await userQueries.findOne({ id }, { attributes: ['name', 'email'] }) + const { name, email } = await userQueries.findOne( + { id, organization_id }, + { attributes: ['name', 'email'] } + ) + const adminPlaintextEmailId = emailEncryption.decrypt(email) const organization = await organizationQueries.findOne({ id: organization_id }, { attributes: ['name'] }) @@ -50,6 +55,7 @@ module.exports = class OrgAdminHelper { } const result = await fileUploadQueries.create(creationData) + if (!result?.id) { return common.successResponse({ responseCode: 'CLIENT_ERROR', @@ -68,7 +74,7 @@ module.exports = class OrgAdminHelper { user: { id, name, - email, + email: adminPlaintextEmailId, organization_id, org_name: organization.name, }, @@ -89,6 +95,7 @@ module.exports = class OrgAdminHelper { result: result, }) } catch (error) { + console.log(error) throw error } } @@ -234,6 +241,7 @@ module.exports = class OrgAdminHelper { { id: requestId, organization_id: tokenInformation.organization_id }, bodyData ) + if (rowsAffected === 0) { return common.failureResponse({ message: 'ORG_ROLE_REQ_FAILED', @@ -242,7 +250,10 @@ module.exports = class OrgAdminHelper { }) } - const requestDetails = await orgRoleReqQueries.requestDetails({ id: requestId }) + const requestDetails = await orgRoleReqQueries.requestDetails({ + id: requestId, + organization_id: tokenInformation.organization_id, + }) const isApproved = bodyData.status === common.ACCEPTED_STATUS const isRejected = bodyData.status === common.REJECTED_STATUS @@ -250,8 +261,12 @@ module.exports = class OrgAdminHelper { const shouldSendEmail = isApproved || isRejected const message = isApproved ? 'ORG_ROLE_REQ_APPROVED' : 'ORG_ROLE_REQ_UPDATED' - const user = await userQueries.findByPk(requestDetails.requester_id) - console.log(shouldSendEmail, 'shouldSendEmail') + const user = await userQueries.findOne({ + id: requestDetails.requester_id, + organization_id: tokenInformation.organization_id, + }) + + console.log(isApproved, 'isApproved') if (isApproved) { await updateRoleForApprovedRequest(requestDetails, user) } @@ -267,6 +282,7 @@ module.exports = class OrgAdminHelper { result: requestDetails, }) } catch (error) { + console.log(error, 'error') throw error } } @@ -293,8 +309,9 @@ module.exports = class OrgAdminHelper { let userIds = [] if (bodyData.email) { + const encryptedEmailIds = bodyData.email.map((email) => emailEncryption.encrypt(email.toLowerCase())) const userCredentials = await UserCredentialQueries.findAll( - { email: { [Op.in]: bodyData.email } }, + { email: { [Op.in]: encryptedEmailIds } }, { attributes: ['user_id'], } @@ -330,6 +347,7 @@ module.exports = class OrgAdminHelper { message: 'USER_DEACTIVATED', }) } catch (error) { + console.log(error) throw error } } @@ -407,12 +425,6 @@ function updateRoleForApprovedRequest(requestDetails, user) { { attributes: ['title', 'id', 'user_type', 'status'] } ) - const systemRoleIds = userRoles - .filter((role) => role.user_type === common.ROLE_TYPE_SYSTEM) - .map((role) => role.id) - - let rolesToUpdate = [...systemRoleIds] - const newRole = await roleQueries.findOne( { id: requestDetails.role, status: common.ACTIVE_STATUS }, { attributes: ['title', 'id', 'user_type', 'status'] } @@ -426,11 +438,24 @@ function updateRoleForApprovedRequest(requestDetails, user) { }, }) - rolesToUpdate.push(requestDetails.role) + let rolesToUpdate = [requestDetails.role] + + let currentUserRoleIds = _.map(userRoles, 'id') + + //remove mentee role from roles array + const menteeRoleId = userRoles.find((role) => role.title === common.MENTEE_ROLE)?.id + if (menteeRoleId && currentUserRoleIds.includes(menteeRoleId)) { + _.pull(currentUserRoleIds, menteeRoleId) + } + rolesToUpdate.push(...currentUserRoleIds) + const roles = _.uniq(rolesToUpdate) await userQueries.updateUser( - { id: requestDetails.requester_id }, + { + id: requestDetails.requester_id, + organization_id: user.organization_id, + }, { roles, } @@ -444,6 +469,7 @@ function updateRoleForApprovedRequest(requestDetails, user) { success: true, }) } catch (error) { + console.log(error, 'error') return error } }) @@ -473,7 +499,7 @@ async function sendRoleRequestStatusEmail(userDetails, status) { const payload = { type: common.notificationEmailType, email: { - to: userDetails.email, + to: emailEncryption.decrypt(userDetails.email), subject: templateData.subject, body: utils.composeEmailBody(templateData.body, { name: userDetails.name, diff --git a/src/services/organization.js b/src/services/organization.js index a0d1a2d61..59e98d544 100644 --- a/src/services/organization.js +++ b/src/services/organization.js @@ -12,6 +12,7 @@ const { Op } = require('sequelize') const _ = require('lodash') const { eventBroadcaster } = require('@helpers/eventBroadcaster') const UserCredentialQueries = require('@database/queries/userCredential') +const emailEncryption = require('@utils/emailEncryption') module.exports = class OrganizationsHelper { /** @@ -54,6 +55,8 @@ module.exports = class OrganizationsHelper { // Send an invitation to the admin if an email is provided. if (bodyData.admin_email) { + const plaintextEmailId = bodyData.admin_email.toLowerCase() + const encryptedEmailId = emailEncryption.encrypt(plaintextEmailId) const role = await roleQueries.findOne( { title: common.ORG_ADMIN_ROLE }, { @@ -70,7 +73,7 @@ module.exports = class OrganizationsHelper { } const inviteeData = { - email: bodyData.admin_email, + email: encryptedEmailId, name: common.USER_ROLE, organization_id: createdOrganization.id, roles: [role.id], @@ -78,11 +81,19 @@ module.exports = class OrganizationsHelper { } const createdInvite = await userInviteQueries.create(inviteeData) - await UserCredentialQueries.create({ - email: bodyData.admin_email, + const userCred = await UserCredentialQueries.create({ + email: encryptedEmailId, organization_id: createdOrganization.id, organization_user_invite_id: createdInvite.id, }) + + if (!userCred?.id) { + return common.failureResponse({ + message: userCred, + statusCode: httpStatusCode.not_acceptable, + responseCode: 'CLIENT_ERROR', + }) + } //send email invitation const templateCode = process.env.ORG_ADMIN_INVITATION_EMAIL_TEMPLATE_CODE if (templateCode) { @@ -92,7 +103,7 @@ module.exports = class OrganizationsHelper { const payload = { type: common.notificationEmailType, email: { - to: bodyData.admin_email, + to: plaintextEmailId, subject: templateData.subject, body: utils.composeEmailBody(templateData.body, { name: inviteeData.name, @@ -118,6 +129,7 @@ module.exports = class OrganizationsHelper { result: createdOrganization, }) } catch (error) { + console.log(error) throw error } } @@ -295,6 +307,7 @@ module.exports = class OrganizationsHelper { { requester_id: tokenInformation.id, role: bodyData.role, + organization_id: tokenInformation.organization_id, }, { order: [['created_at', 'DESC']], diff --git a/src/services/user.js b/src/services/user.js index ea1534503..ee925391f 100644 --- a/src/services/user.js +++ b/src/services/user.js @@ -17,6 +17,8 @@ const organizationQueries = require('@database/queries/organization') const { removeDefaultOrgEntityTypes } = require('@generics/utils') const _ = require('lodash') const { Op } = require('sequelize') +const { eventBroadcaster } = require('@helpers/eventBroadcaster') +const emailEncryption = require('@utils/emailEncryption') module.exports = class UserHelper { /** @@ -63,6 +65,7 @@ module.exports = class UserHelper { organization_id: { [Op.in]: [orgId, defaultOrgId], }, + model_names: { [Op.contains]: [await userQueries.getModelName()] }, } let validationData = await entityTypeQueries.findUserEntityTypesAndEntities(filter) const prunedEntities = removeDefaultOrgEntityTypes(validationData) @@ -87,6 +90,19 @@ module.exports = class UserHelper { bodyData ) + const currentUser = updatedData[0] + + const currentName = currentUser.dataValues.name + const previousName = currentUser._previousDataValues?.name || null + + if (currentName !== previousName) { + eventBroadcaster('updateName', { + requestBody: { + mentor_name: currentName, + mentor_id: id, + }, + }) + } const redisUserKey = common.redisUserPrefix + id.toString() if (await utils.redisGet(redisUserKey)) { await utils.redisDel(redisUserKey) @@ -97,6 +113,7 @@ module.exports = class UserHelper { ) delete processDbResponse.refresh_tokens delete processDbResponse.password + processDbResponse.email = emailEncryption.decrypt(processDbResponse.email) return common.successResponse({ statusCode: httpStatusCode.accepted, message: 'PROFILE_UPDATED_SUCCESSFULLY', @@ -180,6 +197,7 @@ module.exports = class UserHelper { organization_id: { [Op.in]: [user.organization_id, defaultOrgId], }, + model_names: { [Op.contains]: [await userQueries.getModelName()] }, }) const prunedEntities = removeDefaultOrgEntityTypes(validationData, user.organization_id) const processDbResponse = utils.processDbResponse(user, prunedEntities) @@ -187,13 +205,14 @@ module.exports = class UserHelper { if (utils.validateRoleAccess(roles, common.MENTOR_ROLE)) { await utils.redisSet(redisUserKey, processDbResponse) } - + processDbResponse.email = emailEncryption.decrypt(processDbResponse.email) return common.successResponse({ statusCode: httpStatusCode.ok, message: 'PROFILE_FETCHED_SUCCESSFULLY', result: processDbResponse ? processDbResponse : {}, }) } else { + userDetails.email = emailEncryption.decrypt(userDetails.email) return common.successResponse({ statusCode: httpStatusCode.ok, message: 'PROFILE_FETCHED_SUCCESSFULLY', diff --git a/src/services/userInvite.js b/src/services/userInvite.js index 957c039c2..cbac38ffa 100644 --- a/src/services/userInvite.js +++ b/src/services/userInvite.js @@ -28,6 +28,7 @@ const inviteeFileDir = ProjectRootDir + common.tempFolderForBulkUpload const UserCredentialQueries = require('@database/queries/userCredential') const { Op } = require('sequelize') +const emailEncryption = require('@utils/emailEncryption') module.exports = class UserInviteHelper { static async uploadInvites(data) { @@ -36,15 +37,11 @@ module.exports = class UserInviteHelper { const filePath = data.fileDetails.input_path // download file to local directory const response = await this.downloadCSV(filePath) - if (!response.success) { - throw new Error('FAILED_TO_DOWNLOAD') - } + if (!response.success) throw new Error('FAILED_TO_DOWNLOAD') // extract data from csv const parsedFileData = await this.extractDataFromCSV(response.result.downloadPath) - if (!parsedFileData.success) { - throw new Error('FAILED_TO_READ_CSV') - } + if (!parsedFileData.success) throw new Error('FAILED_TO_READ_CSV') const invitees = parsedFileData.result.data // create outPut file and create invites @@ -53,8 +50,8 @@ module.exports = class UserInviteHelper { // upload output file to cloud const uploadRes = await this.uploadFileToCloud(outputFilename, inviteeFileDir, data.user.id) - const output_path = uploadRes.result.uploadDest + const update = { output_path, updated_by: data.user.id, @@ -62,13 +59,16 @@ module.exports = class UserInviteHelper { createResponse.result.isErrorOccured == true ? common.FAILED_STATUS : common.PROCESSED_STATUS, } - // //update output path in file uploads - const rowsAffected = await fileUploadQueries.update({ id: data.fileDetails.id }, update) + //update output path in file uploads + const rowsAffected = await fileUploadQueries.update( + { id: data.fileDetails.id, organization_id: data.user.organization_id }, + update + ) if (rowsAffected === 0) { throw new Error('FILE_UPLOAD_MODIFY_ERROR') } - // // send email to admin + // send email to admin const templateCode = process.env.ADMIN_INVITEE_UPLOAD_EMAIL_TEMPLATE_CODE if (templateCode) { const templateData = await notificationTemplateQueries.findOneEmailTemplate( @@ -78,7 +78,7 @@ module.exports = class UserInviteHelper { if (templateData) { const inviteeUploadURL = await utils.getDownloadableUrl(output_path) - await this.sendInviteeEmail(templateData, data.user, inviteeUploadURL) + await this.sendInviteeEmail(templateData, data.user, inviteeUploadURL) //Rename this to function to generic name since this function is used for both Invitee & Org-admin. } } @@ -91,6 +91,7 @@ module.exports = class UserInviteHelper { message: 'CSV_UPLOADED_SUCCESSFULLY', }) } catch (error) { + console.log(error, 'CSV PROCESSING ERROR') return reject({ success: false, message: error.message, @@ -138,6 +139,15 @@ module.exports = class UserInviteHelper { static async extractDataFromCSV(csvFilePath) { try { const csvToJsonData = await csv().fromFile(csvFilePath) + const header = Object.keys(csvToJsonData[0]) + + if (header.map((column) => column.toLowerCase()).includes('roles')) { + // Process the data, split roles, and handle unquoted roles + csvToJsonData.forEach((row) => { + const roles = row.roles.replace(/"/g, '').split(',') + row.roles = roles + }) + } return { success: true, result: { @@ -155,21 +165,37 @@ module.exports = class UserInviteHelper { static async createUserInvites(csvData, user, fileUploadId) { try { const outputFileName = utils.generateFileName(common.inviteeOutputFile, common.csvExtension) - const allRoles = _.uniq(_.map(csvData, 'roles').map((role) => role.toLowerCase())) - const roleList = await roleQueries.findAll({ title: allRoles }) + let menteeRoleId, mentorRoleId + + //get all the roles and map title and id + const roleList = await roleQueries.findAll({ + user_type: common.ROLE_TYPE_NON_SYSTEM, + status: common.ACTIVE_STATUS, + }) const roleTitlesToIds = {} roleList.forEach((role) => { roleTitlesToIds[role.title] = [role.id] + if (role.title === common.MENTEE_ROLE) { + menteeRoleId = role.id + } + if (role.title === common.MENTOR_ROLE) { + mentorRoleId = role.id + } }) //get all existing user - const emailArray = _.uniq(_.map(csvData, 'email')) + const emailArray = _.uniq(_.map(csvData, 'email')).map((email) => + emailEncryption.encrypt(email.toLowerCase()) + ) + const userCredentials = await UserCredentialQueries.findAll( { email: { [Op.in]: emailArray } }, { - attributes: ['user_id'], + attributes: ['user_id', 'email'], } ) + + //This is valid since UserCredentials Already Store The Encrypted Email ID const userIds = _.map(userCredentials, 'user_id') const existingUsers = await userQueries.findAll( { id: userIds }, @@ -177,7 +203,9 @@ module.exports = class UserInviteHelper { attributes: ['id', 'email', 'organization_id', 'roles'], } ) - const existingEmailsMap = new Map(existingUsers.map((eachUser) => [eachUser.email, eachUser])) + + //Get All The Users From Database based on UserIds From UserCredentials + const existingEmailsMap = new Map(existingUsers.map((eachUser) => [eachUser.email, eachUser])) //Figure Out Who Are The Existing Users //find default org id const defaultOrg = await organizationQueries.findOne({ code: process.env.DEFAULT_ORGANISATION_CODE }) @@ -187,62 +215,74 @@ module.exports = class UserInviteHelper { let isErrorOccured = false let isOrgUpdate = false - //fetch email template - const mentorTemplateCode = process.env.MENTOR_INVITATION_EMAIL_TEMPLATE_CODE || null - const menteeTemplateCode = process.env.MENTEE_INVITATION_EMAIL_TEMPLATE_CODE || null - - const [mentorTemplateData, menteeTemplateData] = await Promise.all([ - mentorTemplateCode - ? notificationTemplateQueries.findOneEmailTemplate(mentorTemplateCode, user.organization_id) - : null, - menteeTemplateCode - ? notificationTemplateQueries.findOneEmailTemplate(menteeTemplateCode, user.organization_id) - : null, - ]) - - const templates = { - [common.MENTOR_ROLE]: mentorTemplateData, - [common.MENTEE_ROLE]: menteeTemplateData, - } + //fetch generic email template + const emailTemplate = await notificationTemplateQueries.findOneEmailTemplate( + process.env.GENERIC_INVITATION_EMAIL_TEMPLATE_CODE, + user.organization_id + ) + + //find already invited users + const emailList = await userInviteQueries.findAll({ email: emailArray }) + const existingInvitees = {} + emailList.forEach((userInvitee) => { + existingInvitees[userInvitee.email] = [userInvitee.id] + }) // process csv data for (const invitee of csvData) { - //convert the fields to lower case - invitee.roles = invitee.roles.toLowerCase() invitee.email = invitee.email.toLowerCase() + const encryptedEmail = emailEncryption.encrypt(invitee.email.toLowerCase()) - //validate the fields + //find the invalid fields and generate error message + let invalidFields = [] if (!utils.isValidName(invitee.name)) { - invitee.statusOrUserId = 'NAME_INVALID' - input.push(invitee) - continue + invalidFields.push('name') } - if (!utils.isValidEmail(invitee.email)) { - invitee.statusOrUserId = 'EMAIL_INVALID' + invalidFields.push('email') + } + + const invalidRoles = invitee.roles.filter((role) => !roleTitlesToIds.hasOwnProperty(role.toLowerCase())) + if (invalidRoles.length > 0) { + invalidFields.push('roles') + } + + //merge all error message + if (invalidFields.length > 0) { + const errorMessage = `${ + invalidFields.length > 2 + ? invalidFields.slice(0, -1).join(', ') + ', and ' + invalidFields.slice(-1) + : invalidFields.join(' and ') + } ${invalidFields.length > 1 ? 'are' : 'is'} invalid.` + + invitee.statusOrUserId = errorMessage + invitee.roles = invitee.roles.length > 0 ? invitee.roles.join(',') : '' input.push(invitee) continue } - if (!roleTitlesToIds.hasOwnProperty(invitee.roles)) { - invitee.statusOrUserId = 'ROLE_INVALID' + const existingUser = existingEmailsMap.get(encryptedEmail) + //return error for already invited user + if (!existingUser && existingInvitees.hasOwnProperty(encryptedEmail)) { + invitee.statusOrUserId = 'User already exist' + invitee.roles = invitee.roles.length > 0 ? invitee.roles.join(',') : '' input.push(invitee) continue } - //update user details if the user exist and in default org - const existingUser = existingEmailsMap.get(invitee.email) - + // Update user details if the user exists and belongs to the default organization if (existingUser) { - invitee.statusOrUserId = 'USER_ALREADY_EXISTS' + invitee.statusOrUserId = 'User already exist' isErrorOccured = true const isOrganizationMatch = existingUser.organization_id === defaultOrgId || existingUser.organization_id === user.organization_id + if (isOrganizationMatch) { let userUpdateData = {} + //update user organization if (existingUser.organization_id != user.organization_id) { await userQueries.changeOrganization( existingUser.id, @@ -252,98 +292,163 @@ module.exports = class UserInviteHelper { organization_id: user.organization_id, } ) + isOrgUpdate = true userUpdateData.refresh_tokens = [] } - const areAllElementsInArray = _.every(roleTitlesToIds[invitee.roles], (element) => - _.includes(existingUser.roles, element) + + //find the new roles + const elementsNotInArray = _.difference( + _.map(invitee.roles, (role) => roleTitlesToIds[role.toLowerCase()]).flat(), + existingUser.roles ) - if (!areAllElementsInArray) { - userUpdateData.roles = roleTitlesToIds[invitee.roles] + + //update the user roles and handle downgrade of role + if (elementsNotInArray.length > 0) { + userUpdateData.roles = [] + if (existingUser.roles.includes(menteeRoleId)) { + if (existingUser.roles.length === 1) { + userUpdateData.roles.push(...elementsNotInArray, menteeRoleId) + } else { + userUpdateData.roles.push(...elementsNotInArray, ...existingUser.roles) + } + } else { + userUpdateData.roles.push(...elementsNotInArray, ...existingUser.roles) + } + + if (userUpdateData.roles.includes(mentorRoleId)) { + userUpdateData.roles = _.pull(userUpdateData.roles, menteeRoleId) + } + userUpdateData.refresh_tokens = [] } + //update user and user credentials table with new role organization if (isOrgUpdate || userUpdateData.roles) { - const userCredentials = await UserCredentialQueries.findOne({ - email: invitee.email, + const userCred = await UserCredentialQueries.findOne({ + email: encryptedEmail, }) - await userQueries.updateUser({ id: userCredentials.user_id }, userUpdateData) + await userQueries.updateUser({ id: userCred.user_id }, userUpdateData) await UserCredentialQueries.updateUser( { - email: invitee.email, + email: encryptedEmail, }, { organization_id: user.organization_id } ) - const userRoles = await roleQueries.findAll({ id: existingUser.roles }) - //call event to update in mentoring - if (!userUpdateData?.roles) { + const currentRoles = utils.getRoleTitlesFromId(existingUser.roles, roleList) + let newRoles = [] + newRoles = utils.getRoleTitlesFromId( + _.difference(userUpdateData.roles, existingUser.roles), + roleList + ) + //remove session_manager role because the mentee role is enough to change role in mentoring side + newRoles = newRoles.filter((role) => role !== common.SESSION_MANAGER_ROLE) + + //call event to update organization in mentoring + if (isOrgUpdate) { eventBroadcaster('updateOrganization', { requestBody: { user_id: existingUser.id, organization_id: user.organization_id, - roles: _.map(userRoles, 'title'), + roles: currentRoles, }, }) - } else { + } + + if (newRoles.length > 0) { + //call event to update role and organization in mentoring let requestBody = { user_id: existingUser.id, - new_roles: [invitee.roles], - current_roles: _.map(userRoles, 'title'), + new_roles: newRoles, + current_roles: currentRoles, } if (isOrgUpdate) requestBody.organization_id = user.organization_id eventBroadcaster('roleChange', { requestBody, }) } + + //remove user data from redis const redisUserKey = common.redisUserPrefix + existingUser.id.toString() await utils.redisDel(redisUserKey) + invitee.statusOrUserId = 'success' + } else { + invitee.statusOrUserId = 'No updates needed. User details are already up to date' } + } else { + //user doesn't have access to update user data + invitee.statusOrUserId = 'Unauthorized to update user details in a different organization.' } } else { + //new user invitee creation const inviteeData = { ...invitee, status: common.UPLOADED_STATUS, organization_id: user.organization_id, file_id: fileUploadId, - roles: roleTitlesToIds[invitee.roles] || [], + roles: (invitee.roles || []).map((roleTitle) => roleTitlesToIds[roleTitle.toLowerCase()] || []), + email: encryptedEmail, } + inviteeData.email = encryptedEmail const newInvitee = await userInviteQueries.create(inviteeData) - const newUserCred = await UserCredentialQueries.create({ - email: newInvitee.email, - organization_id: newInvitee.organization_id, - organization_user_invite_id: newInvitee.id, - }) - if (newUserCred.id) { - const { name, email, roles } = invitee - const userData = { - name, - email, - role: roles, - org_name: user.org_name, - } - const templateData = templates[roles] - //send email invitation for user - if (templateData && Object.keys(templateData).length > 0) { - await this.sendInviteeEmail(templateData, userData) + if (newInvitee?.id) { + console.log(newInvitee.roles, 'newInvitee.roles') + invitee.statusOrUserId = newInvitee.id + + //create user credentials entry + const newUserCred = await UserCredentialQueries.create({ + email: newInvitee.email, + organization_id: newInvitee.organization_id, + organization_user_invite_id: newInvitee.id, + }) + + if (newUserCred?.id) { + const { name, email } = invitee + const roles = utils.getRoleTitlesFromId(newInvitee.roles, roleList) + const roleToString = + roles.length > 0 + ? roles + .map((role) => + role.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()) + ) + .join(' and ') + : '' + + const userData = { + name, + email, + roles: roleToString, + org_name: user.org_name, + } + + //send email invitation for new user + if (emailTemplate) { + await this.sendInviteeEmail(emailTemplate, userData, null, { roles: roleToString }) + } + } else { + //delete invitation entry + isErrorOccured = true + await userInviteQueries.deleteOne(newInvitee.id) + invitee.statusOrUserId = newUserCred } } else { isErrorOccured = true - await userInviteQueries.deleteOne(newInvitee.id) + invitee.statusOrUserId = newInvitee } - - invitee.statusOrUserId = newInvitee.id || newInvitee } + //convert roles array to string + invitee.roles = invitee.roles.length > 0 ? invitee.roles.join(',') : '' input.push(invitee) } + //generate output csv const csvContent = utils.generateCSVContent(input) const outputFilePath = path.join(inviteeFileDir, outputFileName) - fs.writeFileSync(outputFilePath, csvContent) return { @@ -372,14 +477,26 @@ module.exports = class UserInviteHelper { const filePath = `${folderPath}/${fileName}` const fileData = fs.readFileSync(filePath, 'utf-8') - await request({ - url: fileUploadUrl, - method: 'put', - headers: { - 'x-ms-blob-type': common.azureBlobType, - 'Content-Type': 'multipart/form-data', - }, - body: fileData, + const result = await new Promise((resolve, reject) => { + try { + request( + { + url: fileUploadUrl, + method: 'put', + headers: { + 'x-ms-blob-type': common.azureBlobType, + 'Content-Type': 'multipart/form-data', + }, + body: fileData, + }, + (error, response, body) => { + if (error) reject(error) + else resolve(response.statusCode) + } + ) + } catch (error) { + reject(error) + } }) return { @@ -396,13 +513,16 @@ module.exports = class UserInviteHelper { } } - static async sendInviteeEmail(templateData, userData, inviteeUploadURL = null) { + static async sendInviteeEmail(templateData, userData, inviteeUploadURL = null, subjectComposeData = {}) { try { const payload = { type: common.notificationEmailType, email: { to: userData.email, - subject: templateData.subject, + subject: + subjectComposeData && Object.keys(subjectComposeData).length > 0 + ? utils.composeEmailBody(templateData.subject, subjectComposeData) + : templateData.subject, body: utils.composeEmailBody(templateData.body, { name: userData.name, role: userData.role || '', @@ -410,16 +530,17 @@ module.exports = class UserInviteHelper { appName: process.env.APP_NAME, inviteeUploadURL, portalURL: process.env.PORTAL_URL, + roles: userData.roles || '', }), }, } await kafkaCommunication.pushEmailToKafka(payload) - return { success: true, } } catch (error) { + console.log(error) throw error } } diff --git a/src/services/userRole.js b/src/services/userRole.js index 0fc7546e3..264702815 100644 --- a/src/services/userRole.js +++ b/src/services/userRole.js @@ -10,22 +10,216 @@ const httpStatusCode = require('@generics/http-status') const roleQueries = require('@database/queries/userRole') const common = require('@constants/common') +const { Op } = require('sequelize') +const organizationQueries = require('@database/queries/organization') module.exports = class userRoleHelper { /** - * list Roles + * Create roles. + * @method + * @name create + * @param {Object} req - Request data. + * @param {Object} req.body - Request body contains role creation details. + * @param {String} req.body.title - Title of the role. + * @param {Integer} req.body.userType - User type of the role. + * @param {String} req.body.status - Role status. + * @param {String} req.body.visibility - Visibility of the role. + * @param {Integer} req.body.organization_id - Organization ID for the role. + * @returns {JSON} - Response contains role creation details. + */ + static async create(bodyData, userOrganizationId) { + try { + bodyData.organization_id = userOrganizationId + const roles = await roleQueries.create(bodyData) + return common.successResponse({ + statusCode: httpStatusCode.created, + message: 'ROLE_CREATED_SUCCESSFULLY', + result: { + title: roles.title, + user_type: roles.user_type, + status: roles.status, + visibility: roles.visibility, + organization_id: roles.organization_id, + }, + }) + } catch (error) { + throw error + } + } + + /** + * Update roles. + * @method + * @name update + * @param {Object} req - Request data. + * @param {Object} req.body - Request body contains role update details. + * @param {String} req.body.title - Title of the role. + * @param {Integer} req.body.userType - User type of the role. + * @param {String} req.body.status - Role status. + * @param {String} req.body.visibility - Visibility of the role. + * @param {Integer} req.body.organization_id - Organization ID for the role. + * @returns {JSON} - Response contains role update details. + */ + + static async update(id, bodyData, userOrganizationId) { + try { + const filter = { id: id, organization_id: userOrganizationId } + const [updateCount, updateRole] = await roleQueries.updateRole(filter, bodyData) + if (updateCount == 0) { + return common.failureResponse({ + message: 'ROLE_NOT_UPDATED', + statusCode: httpStatusCode.bad_request, + responseCode: 'CLIENT_ERROR', + }) + } + return common.successResponse({ + statusCode: httpStatusCode.created, + message: 'ROLE_UPDATED_SUCCESSFULLY', + result: { + title: updateRole.title, + user_type: updateRole.user_type, + status: updateRole.status, + visibility: updateRole.visibility, + organization_id: updateRole.organization_id, + }, + }) + } catch (error) { + throw error + } + } + + /** + * Delete role. + * @method + * @name delete + * @param {Object} req - Request data. + * @returns {JSON} - Role deletion response. + */ + static async delete(id, userOrganizationId) { + try { + const filter = { id: id, organization_id: userOrganizationId } + const deleteRole = await roleQueries.deleteRole(filter) + + if (deleteRole === 0) { + return common.failureResponse({ + message: 'ROLE_NOT_DELETED', + statusCode: httpStatusCode.bad_request, + responseCode: 'CLIENT_ERROR', + }) + } + + return common.successResponse({ + statusCode: httpStatusCode.accepted, + message: 'ROLE_DELETED_SUCCESSFULLY', + result: {}, + }) + } catch (error) { + throw error + } + } + + /** + * Get all available roles. * @method * @name list - * @returns {JSON} - delete user response + * @param {Array(String)} req.body.filters - Filters. + * @param {String} req.pageNo - Page number. + * @param {String} req.pageSize - Page size limit. + * @param {String} req.searchText - Search text. + * @param {Integer} req.decodedToken.organization_id - user organization_id. + * @returns {JSON} - Role list. + */ + + static async list(filters, page, limit, search, userOrganizationId) { + try { + delete filters.search + const offset = common.getPaginationOffset(page, limit) + const options = { + offset, + limit, + } + let defaultOrg = await organizationQueries.findOne( + { code: process.env.DEFAULT_ORGANISATION_CODE }, + { attributes: ['id'] } + ) + let defaultOrgId = defaultOrg.id + const filter = { + [Op.or]: [{ organization_id: userOrganizationId }, { organization_id: defaultOrgId }], + title: { [Op.iLike]: `%${search}%` }, + ...filters, + } + const attributes = ['id', 'title', 'user_type', 'visibility', 'status', 'organization_id'] + const roles = await roleQueries.findAllRoles(filter, attributes, options) + + if (roles.rows == 0 || roles.count == 0) { + return common.failureResponse({ + message: 'ROLES_HAS_EMPTY_LIST', + statusCode: httpStatusCode.bad_request, + responseCode: 'CLIENT_ERROR', + }) + } + const results = { + data: roles.rows, + count: roles.count, + } + + return common.successResponse({ + statusCode: httpStatusCode.ok, + message: 'ROLES_FETCHED_SUCCESSFULLY', + result: results, + }) + } catch (error) { + throw error + } + } + + /** + * Get all available roles. + * @method + * @name defaultlist + * @param {Array(String)} req.body.filters - Filters. + * @param {String} req.pageNo - Page number. + * @param {String} req.pageSize - Page size limit. + * @param {String} req.searchText - Search text. + * @returns {JSON} - Role list. */ - static async list() { + static async defaultList(filters, page, limit, search) { try { - let roles = await roleQueries.findAll() + delete filters.search + const offset = common.getPaginationOffset(page, limit) + const options = { + offset, + limit, + } + let defaultOrg = await organizationQueries.findOne( + { code: process.env.DEFAULT_ORGANISATION_CODE }, + { attributes: ['id'] } + ) + let defaultOrgId = defaultOrg.id + const filter = { + organization_id: defaultOrgId, + title: { [Op.iLike]: `%${search}%` }, + ...filters, + } + const attributes = ['id', 'title', 'user_type', 'visibility', 'status', 'organization_id'] + const roles = await roleQueries.findAllRoles(filter, attributes, options) + + if (roles.rows == 0 || roles.count == 0) { + return common.failureResponse({ + message: 'ROLES_HAS_EMPTY_LIST', + statusCode: httpStatusCode.bad_request, + responseCode: 'CLIENT_ERROR', + }) + } + const results = { + data: roles.rows, + count: roles.count, + } return common.successResponse({ statusCode: httpStatusCode.ok, - message: 'USER_ROLE_LIST', - result: roles, + message: 'ROLES_FETCHED_SUCCESSFULLY', + result: results, }) } catch (error) { throw error diff --git a/src/setupFileAfterEnv.js b/src/setupFileAfterEnv.js index 7fd6f3042..6226cd686 100644 --- a/src/setupFileAfterEnv.js +++ b/src/setupFileAfterEnv.js @@ -5,6 +5,11 @@ const { matchers } = require('jest-json-schema') expect.extend(matchers) //Connect to database +/* +Uses MongoDB v4.1.4, which has an OSI Compliant License (GNU Affero General Public License, version 3) +MongoDB v4.1.4 repository: https://github.com/mongodb/mongo/tree/r4.1.4 +MongoDB v4.1.4 License: https://github.com/mongodb/mongo/blob/r4.1.4/LICENSE-Community.txt +*/ const db = mongoose.createConnection('mongodb://127.0.0.1:27017/elevate-mentoring', { useNewUrlParser: true, diff --git a/src/utils/emailEncryption.js b/src/utils/emailEncryption.js new file mode 100644 index 000000000..772ce56a2 --- /dev/null +++ b/src/utils/emailEncryption.js @@ -0,0 +1,29 @@ +'use strict' +const crypto = require('crypto') + +const secretKey = Buffer.from(process.env.EMAIL_ID_ENCRYPTION_KEY, 'hex') +const fixedIV = Buffer.from(process.env.EMAIL_ID_ENCRYPTION_IV, 'hex') +const algorithm = process.env.EMAIL_ID_ENCRYPTION_ALGORITHM + +const encrypt = (plainTextEmail) => { + try { + const cipher = crypto.createCipheriv(algorithm, secretKey, fixedIV) + return cipher.update(plainTextEmail, 'utf-8', 'hex') + cipher.final('hex') + } catch (err) { + console.log(err) + throw err + } +} + +const decrypt = (encryptedEmail) => { + try { + const decipher = crypto.createDecipheriv(algorithm, secretKey, fixedIV) + return decipher.update(encryptedEmail, 'hex', 'utf-8') + decipher.final('utf-8') + } catch (err) { + console.log(err) + throw err + } +} +const emailEncryption = { encrypt, decrypt } + +module.exports = emailEncryption diff --git a/src/validators/v1/entity-type.js b/src/validators/v1/entity-type.js index d265c2819..fca56b740 100644 --- a/src/validators/v1/entity-type.js +++ b/src/validators/v1/entity-type.js @@ -1,5 +1,5 @@ /** - * name : validators/v1/entity-types.js + * name : validators/v1/entity-type.js * author : Aman Gupta * Date : 04-Nov-2021 * Description : Validations of user entities controller @@ -11,7 +11,7 @@ module.exports = { .trim() .notEmpty() .withMessage('value field is empty') - .matches(/^[A-Za-z]+$/) + .matches(/^[A-Za-z_]+$/) .withMessage('value is invalid, must not contain spaces') req.checkBody('label') @@ -28,6 +28,15 @@ module.exports = { .matches(/^[A-Za-z]+$/) .withMessage('data_type is invalid, must not contain spaces') + req.checkBody('model_names') + .isArray() + .notEmpty() + .withMessage('model_names must be an array with at least one element') + + req.checkBody('model_names.*') + .isIn(['Session', 'MentorExtension', 'UserExtension']) + .withMessage('model_names must be in Session,MentorExtension,UserExtension') + req.checkBody('allow_filtering').optional().isEmpty().withMessage('allow_filtering is not allowed in create') }, @@ -49,24 +58,37 @@ module.exports = { .matches(/^[A-Z]+$/) .withMessage('status is invalid, must be in all caps') + req.checkBody('deleted').optional().isBoolean().withMessage('deleted is invalid') + req.checkBody('allow_filtering').optional().isEmpty().withMessage('allow_filtering is not allowed in create') + req.checkBody('data_type') - .optional() + .trim() + .notEmpty() + .withMessage('data_type field is empty') .matches(/^[A-Za-z]+$/) .withMessage('data_type is invalid, must not contain spaces') - req.checkBody('allow_filtering').optional().isEmpty().withMessage('allow_filtering is not allowed in create') + req.checkBody('model_names') + .isArray() + .notEmpty() + .withMessage('model_names must be an array with at least one element') + + req.checkBody('model_names.*') + .isIn(['Session', 'MentorExtension', 'UserExtension']) + .withMessage('model_names must be in Session,MentorExtension,UserExtension') }, read: (req) => { - console.log() - if (req.query.data_type) { - req.checkQuery('data_type') + if (req.query.type) { + req.checkQuery('type') .trim() .notEmpty() - .withMessage('data_type field is empty') + .withMessage('type field is empty') .matches(/^[A-Za-z]+$/) .withMessage('type is invalid, must not contain spaces') + req.checkQuery('deleted').optional().isBoolean().withMessage('deleted is invalid') + req.checkQuery('status') .optional() .trim() diff --git a/src/validators/v1/entity.js b/src/validators/v1/entity.js index 87c588638..ddaf66a65 100644 --- a/src/validators/v1/entity.js +++ b/src/validators/v1/entity.js @@ -21,12 +21,11 @@ module.exports = { .matches(/^[A-Za-z0-9 ]+$/) .withMessage('label is invalid') - req.checkBody('type') - .trim() + req.checkBody('entity_type_id') .notEmpty() - .withMessage('type field is empty') - .matches(/^[A-Za-z]+$/) - .withMessage('type is invalid, must not contain spaces') + .withMessage('entity_type_id field is empty') + .isNumeric() + .withMessage('entity_type_id is invalid, must be numeric') }, update: (req) => { @@ -55,7 +54,23 @@ module.exports = { .withMessage('type is invalid, must not contain spaces') }, - read: (req) => {}, + read: (req) => { + req.checkQuery('id') + .trim() + .optional() + .notEmpty() + .withMessage('id param is empty') + .isNumeric() + .withMessage('id param is invalid, must be an integer') + + req.checkQuery('value') + .trim() + .optional() + .notEmpty() + .withMessage('value field is empty') + .matches(/^[A-Za-z0-9 ]+$/) + .withMessage('value is invalid, must not contain spaces') + }, delete: (req) => { req.checkParams('id').notEmpty().withMessage('id param is empty') diff --git a/src/validators/v1/userRole.js b/src/validators/v1/userRole.js new file mode 100644 index 000000000..db654cb72 --- /dev/null +++ b/src/validators/v1/userRole.js @@ -0,0 +1,88 @@ +const validateList = (req, allowedVariables) => { + allowedVariables.forEach((variable) => { + req.checkQuery(variable) + .optional() + .matches(/^[A-Za-z0-9_]+$/) + .withMessage(`${variable} is invalid, must not contain spaces or special characters`) + }) +} + +module.exports = { + create: (req) => { + req.checkBody('title') + .trim() + .notEmpty() + .withMessage('title field is empty') + .matches(/^[a-z_]+$/) + .withMessage('title is invalid, must not contain spaces') + + req.checkBody('user_type') + .trim() + .notEmpty() + .withMessage('userType field is empty') + .matches(/^[0-9]+$/) + .withMessage('userType is invalid, must not contain spaces') + + req.checkBody('visibility') + .trim() + .notEmpty() + .withMessage('visibility field is empty') + .matches(/^[A-Z_]+$/) + .withMessage('visibility is invalid, must not contain spaces') + + req.checkBody('status') + .trim() + .matches(/^[A-Za-z]*$/) + .withMessage('status is invalid, must not contain spaces') + .optional({ checkFalsy: true }) + .notEmpty() + .withMessage('status field must be a non-empty string when provided') + }, + + update: (req) => { + req.checkParams('id').notEmpty().withMessage('id param is empty') + + req.checkBody('title') + .trim() + .notEmpty() + .withMessage('title field is empty') + .matches(/^[a-z_]+$/) + .withMessage('title is invalid, must not contain spaces') + + req.checkBody('user_type') + .trim() + .notEmpty() + .withMessage('userType field is empty') + .matches(/^[0-9]+$/) + .withMessage('userType is invalid, must not contain spaces') + + req.checkBody('visibility') + .trim() + .notEmpty() + .withMessage('visibility field is empty') + .matches(/^[A-Z_]+$/) + .withMessage('visibility is invalid, must not contain spaces') + + req.checkBody('status') + .trim() + .matches(/^[A-Za-z]*$/) + .withMessage('status is invalid, must not contain spaces') + .optional({ checkFalsy: true }) + .notEmpty() + .withMessage('status field must be a non-empty string when provided') + }, + + delete: (req) => { + req.checkParams('id').notEmpty().withMessage('id param is empty') + }, + + list: (req) => { + const allowedVariables = ['title', 'user_type', 'visibility', 'organization_id', 'status'] + validateList(req, allowedVariables) + }, + + default: (req) => { + const allowedVariables = ['title', 'user_type', 'visibility', 'organization_id', 'status'] + validateList(req, allowedVariables) + }, +}