diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..b33fc0fe --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,37 @@ +version: 2.17 +jobs: + build: + docker: + - image: circleci/node:10.0.0 + working_directory: ~/project/ + steps: + - checkout + - restore_cache: + key: dependency-cache-{{ checksum "package.json" }} + - run: + name: update-npm + command: 'sudo npm install -g npm@latest' + - run: + name: install-npm + command: npm install + - save_cache: + key: dependency-cache-{{checksum "package.json"}} + paths: + - ./node_modules + - run: + name: Executing unit test cases + command: npm install nyc && npm run coverage + + - run: + name: Install sonar scanner + command: 'sudo npm install -g sonarqube-scanner' + + - run: + name: Sonar scanner + command: | + sonar-scanner +workflows: + version: 2 + build_and_test: + jobs: + - build diff --git a/.env.sample b/.env.sample index 981fbc69..d736d733 100644 --- a/.env.sample +++ b/.env.sample @@ -35,9 +35,24 @@ ML_SURVEY_SERVICE_URL = "http://ml-survey-service:3000" # ML Project Service Service ML_PROJECT_SERVICE_URL = "http://ml-project-service:3000" // ML Project service url -# Elastic search configurations -ELASTICSEARCH_COMMUNICATIONS_ON_OFF = "ON/OFF" // Elastic search enable/disable flag E.g. ON/OFF -ELASTICSEARCH_HOST_URL = "http://127.0.0.1:9200" // Elastic search host url -ELASTICSEARCH_ENTITIES_INDEX = "entities" // Elastic search index name for storing entities - +#USER service USER_SERVICE_URL = "http://user-service:3000" // Base url of the sunbird enviornment + +CSV_REPORTS_PATH = "public/report" // Report path + +APP_PORTAL_BASE_URL = "https://dev.sunbirded.org" + +FORM_SERVICE_URL = "http://player:3000" // Base url for form search + +# Oracle Cloud Configuration +OCI_ACCESS_KEY_ID = '23b90..............d01d' // Oracle cloud storage access key Id +OCI_SECRET_ACCESS_KEY = '22levMw5Ci............SmNE=' // Oracle cloud storage secret access key +OCI_BUCKET_NAME = 'oracle cloud bucket name' // Oracle cloud bucket name +OCI_BUCKET_REGION = 'ap-hyderabad-1' // Oracle cloud bucket region +OCI_BUCKET_ENDPOINT = 'https://pmt5.compat.storage.ap-h1.oraclecloud.com' // Oracle cloud bucket endPoint + +# KAFKA Configurations +KAFKA_COMMUNICATIONS_ON_OFF = "ON/OFF" // Kafka enable or disable communication flag +KAFKA_URL = "100.0.0.1:9092" // IP address of kafka server with port without HTTP +KAFKA_GROUP_ID = "mlcore" // Kafka group id +PROGRAM_USERS_JOINED_TOPIC = "dev.programuser.info" // Kafka submission topic for pushing program joined user's data \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..bb5dc77f --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,36 @@ + +# Description +These recommendations are intended to promote code quality and team communication during software development. They cover a variety of topics, including ensuring that pull requests are submitted to the correct branch, documenting new methods, preserving consistency across many services, and avoiding typical blunders like accessing APIs or DB queries within loops. Sensitive data should not be uploaded, and changes to environment variables or database models should be executed consistently. Teams may work more effectively and develop higher-quality software by adhering to these standards. + + +## Type of change +Please choose appropriate options. + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] Enhancement (additive changes to improve performance) +- [ ] This change requires a documentation update + +## Checklist + +- [ ] It's critical to avoid making needless file modifications in contributions, such as adding new lines, console logs, or additional spaces, to guarantee cleaner and more efficient code. Furthermore, eliminating unnecessary imports from a file might enhance code readability and efficiency. +- [ ] Ensure that the pull request is assigned to the right base branch and that the development branch name contains the JIRA Task Id. Furthermore, each commit message should include the JIRA Task Id in the manner "ED-100: message". +- [ ] Only update packages if it is mentioned and authorized in the design document, and make sure that you have the required permissions. +- [ ] Avoid making API and database queries inside a loop as it can lead to performance issues and slow down the system. +- [ ] When calling another function inside a given function, add comments explaining the purpose and meaning of the passed arguments and expected return values. +- [ ] If adding a blank argument in a function, add a comment explaining the reason for the blank argument. +- [ ] Before submitting a pull request, do a self-review of your code to ensure there are no conflicts with the base branch and all comments have been addressed. +- [ ] Before merging a pull request, it's important to have other team members review it to catch any potential errors or issues +- [ ] To maintain code integrity, it's important to remove all related changes when removing code during a code review. +- [ ] If new constants, endpoints, or utility functions are introduced, it is important to check if they already exist in the service to avoid any duplication. +- [ ] Whenever a new environment variable is added to a service, it's important to ensure that the necessary changes are made to related files such as ".env.sample" and "envVariables.js" to maintain consistency and avoid errors. Additionally, the new environment variable should be added to the devops repository to ensure that it is properly documented and accessible to the team. +- [ ] When adding a new function to a service, it is important to document it with relevant information such as the name, parameters, and return value in a consistent format across all services. Additionally, if there are any changes to the API response, ensure that the documentation in the controllers is updated accordingly. +- [ ] Write a clear and concise commit message that describes the changes made. +- [ ] Maintain consistent function signature and code across all services when adding a function to multiple services. Implement changes to database models in all services that use the same model. +- [ ] Use only let and const. Do not use var. +- [ ] Make common functions for repetitive code blocks. +- [ ] Avoid uploading sensitive information such as secret tokens or passwords in pull requests to ensure data security. +- [ ] Maintain consistent indentation and spacing throughout the code. + + diff --git a/.gitignore b/.gitignore index ede208e0..16dd564f 100644 --- a/.gitignore +++ b/.gitignore @@ -60,6 +60,9 @@ typings/ # Ignore all credentials gcp,fcm config/credentials/* +# Ignore public directory +public/* + # next.js build output .next @@ -69,3 +72,4 @@ config/credentials/* *.DS_Store package-lock.json +keycloak-public-keys/ diff --git a/Jenkinsfile b/Jenkinsfile index 348ec1c9..07a39295 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -5,6 +5,7 @@ node('build-slave') { String ANSI_BOLD = "\u001B[1m" String ANSI_RED = "\u001B[31m" String ANSI_YELLOW = "\u001B[33m" + ansiColor('xterm') { timestamps { stage('Checkout') { @@ -19,8 +20,8 @@ node('build-slave') { commit_hash = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim() build_tag = sh(script: "echo " + params.github_release_tag.split('/')[-1] + "_" + commit_hash + "_" + env.BUILD_NUMBER, returnStdout: true).trim() echo "build_tag: " + build_tag - } - stage('Build') { + + stage('Build') { env.NODE_ENV = "build" print "Environment will be : ${env.NODE_ENV}" sh('git submodule update --init') @@ -32,6 +33,7 @@ node('build-slave') { stage('ArchiveArtifacts') { archiveArtifacts "metadata.json" currentBuild.description = "${build_tag}" + } } } } @@ -39,4 +41,4 @@ node('build-slave') { currentBuild.result = "FAILURE" throw err } -} +} \ No newline at end of file diff --git a/README.md b/README.md index b838a5b0..16672448 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ # ml-core-service -Centralised Service to support other Services +Centralised Service to support other Services. +It is used by ml-survey, ml-project + diff --git a/app.js b/app.js index ddab8a2c..09c40a81 100644 --- a/app.js +++ b/app.js @@ -45,18 +45,13 @@ app.use(express.static("public")); app.all("*", (req, res, next) => { - console.log("-------Request log starts here------------------"); - console.log( - "%s %s on %s from ", - req.method, - req.url, - new Date(), - req.headers["user-agent"] - ); - console.log("Request Headers: ", req.headers); - console.log("Request Body: ", req.body); - console.log("Request Files: ", req.files); - console.log("-------Request log ends here------------------"); + console.log({"Debugging ML Core Service": true}); + console.log("<------------Request log starts here------------------>"); + console.log("Request URL: ", req.url); + console.log("Request Headers: ", JSON.stringify(req.headers)); + console.log("Request Body: ", JSON.stringify(req.body)); + // console.log("Request Files: ", req.files); + console.log("<--------------Request log ends here------------------>"); next(); }); diff --git a/auto_build_deploy b/auto_build_deploy new file mode 100644 index 00000000..49743f56 --- /dev/null +++ b/auto_build_deploy @@ -0,0 +1,55 @@ +@Library('deploy-conf') _ +node('build-slave') { + try { + String ANSI_GREEN = "\u001B[32m" + String ANSI_NORMAL = "\u001B[0m" + String ANSI_BOLD = "\u001B[1m" + String ANSI_RED = "\u001B[31m" + String ANSI_YELLOW = "\u001B[33m" + + ansiColor('xterm') { + timestamps { + stage('Checkout') { + tag_name = env.JOB_NAME.split("/")[-1] + pre_checks() + if (!env.hub_org) { + println(ANSI_BOLD + ANSI_RED + "Uh Oh! Please set a Jenkins environment variable named hub_org with value as registery/sunbidrded" + ANSI_NORMAL) + error 'Please resolve the errors and rerun..' + } else + println(ANSI_BOLD + ANSI_GREEN + "Found environment variable named hub_org with value as: " + hub_org + ANSI_NORMAL) + } + cleanWs() + def scmVars = checkout scm + checkout scm: [$class: 'GitSCM', branches: [[name: "refs/tags/$tag_name"]], userRemoteConfigs: [[url: scmVars.GIT_URL]]] + build_tag = tag_name + "_" + env.BUILD_NUMBER + commit_hash = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim() + artifact_version = tag_name + "_" + commit_hash + echo "build_tag: " + build_tag + + // stage Build + env.NODE_ENV = "build" + print "Environment will be : ${env.NODE_ENV}" + sh('git submodule update --init') + sh('git submodule update --init --recursive --remote') + sh('chmod 777 build.sh') + sh("./build.sh ${build_tag} ${env.NODE_NAME} ${hub_org}") + + + // stage ArchiveArtifacts + archiveArtifacts "metadata.json" + currentBuild.description = "${build_tag}" + + } + } + currentBuild.result = "SUCCESS" + slack_notify(currentBuild.result, tag_name) + email_notify() + auto_build_deploy() + } + catch (err) { + currentBuild.result = "FAILURE" + slack_notify(currentBuild.result, tag_name) + email_notify() + throw err + } +} \ No newline at end of file diff --git a/config/db/elastic-search.js b/config/db/elastic-search.js index 166905fb..b5f97f3e 100644 --- a/config/db/elastic-search.js +++ b/config/db/elastic-search.js @@ -7,37 +7,37 @@ //dependencies -const { Client : esClient } = require('@elastic/elasticsearch'); - -/** - * Elastic search connection. - * @function - * @name connect - * @return {Object} elastic search client - */ - -var connect = function () { - - const elasticSearchClient = new esClient({ - node: process.env.ELASTICSEARCH_HOST_URL, - maxRetries: 5, - requestTimeout: 60000, - sniffOnStart: process.env.ELASTIC_SEARCH_SNIFF_ON_START - }); - - elasticSearchClient.ping({ - }, function (error) { - if (error) { - console.log(error); - } else { - console.log('Elasticsearch connection established.'); - } - }); - - return { - client: elasticSearchClient - }; - -}; - -module.exports = connect; +// const { Client : esClient } = require('@elastic/elasticsearch'); + +// /** +// * Elastic search connection. +// * @function +// * @name connect +// * @return {Object} elastic search client +// */ + +// var connect = function () { + +// const elasticSearchClient = new esClient({ +// node: process.env.ELASTICSEARCH_HOST_URL, +// maxRetries: 5, +// requestTimeout: 60000, +// sniffOnStart: process.env.ELASTIC_SEARCH_SNIFF_ON_START +// }); + +// elasticSearchClient.ping({ +// }, function (error) { +// if (error) { +// console.log(error); +// } else { +// console.log('Elasticsearch connection established.'); +// } +// }); + +// return { +// client: elasticSearchClient +// }; + +// }; + +// module.exports = connect; diff --git a/config/db/mongodb.js b/config/db/mongodb.js index 83095aa8..abace431 100644 --- a/config/db/mongodb.js +++ b/config/db/mongodb.js @@ -70,11 +70,16 @@ var databaseConfiguration = function () { return model; }; + var getCollection = function (modelName) { + return db.collection(modelName); + } + return { database: db, createModel: createModel, ObjectId: objectId, - models: db.models + models: db.models, + getCollection : getCollection }; }; diff --git a/config/globals.js b/config/globals.js index 458f3998..c47cb1ce 100644 --- a/config/globals.js +++ b/config/globals.js @@ -99,4 +99,7 @@ module.exports = function () { } }); + //define cache as global variable + global.cache = require(ROOT_PATH+"/generics/helpers/cache"); + }; diff --git a/config/index.js b/config/index.js index 8e7ac046..b77a7c37 100644 --- a/config/index.js +++ b/config/index.js @@ -23,8 +23,18 @@ let db_connect = function () { * @name elasticsearch_connect */ -let elasticsearch_connect = function () { - global.elasticsearch = require("./db/elastic-search")(); +// let elasticsearch_connect = function () { +// global.elasticsearch = require("./db/elastic-search")(); +// }; + +/** + * Kafka connection. + * @function + * @name kafka_connect +*/ + +const kafka_connect = function() { + global.kafkaClient = require("./kafka")(); }; const configuration = { @@ -35,6 +45,7 @@ const configuration = { }; db_connect(); -elasticsearch_connect(); +kafka_connect(); +// elasticsearch_connect(); module.exports = configuration; diff --git a/config/kafka.js b/config/kafka.js new file mode 100644 index 00000000..42a14fe5 --- /dev/null +++ b/config/kafka.js @@ -0,0 +1,48 @@ +/** + * name : kafka.js + * author : vishnu + * created-date : 10-Jan-2023 + * Description : Kafka Configurations related information. +*/ + + +//dependencies +const kafka = require('kafka-node'); + +/** + * Kafka configurations. + * @function + * @name connect +*/ + +const connect = function() { + + const Producer = kafka.Producer + KeyedMessage = kafka.KeyedMessage + + const client = new kafka.KafkaClient({ + kafkaHost : process.env.KAFKA_URL + }); + + client.on('error', function(error) { + console.log("kafka connection error!") + }); + + const producer = new Producer(client) + + producer.on('ready', function () { + console.log("Connected to Kafka"); + }); + + producer.on('error', function (err) { + console.log("kafka producer creation error!") + }) + + return { + kafkaProducer: producer, + kafkaClient: client + }; + +}; + +module.exports = connect; diff --git a/controllers/v1/admin.js b/controllers/v1/admin.js new file mode 100644 index 00000000..66fdaa4c --- /dev/null +++ b/controllers/v1/admin.js @@ -0,0 +1,252 @@ +/** + * name : admin.js + * author : Priyanka Pradeep + * created-date : 23-09-2022 + * Description : Admin Related information. + */ + + // Dependencies + const adminHelper = require(MODULES_BASE_PATH + "/admin/helper"); + + /** + * Admin + * @class + */ + module.exports = class Admin { + + static get name() { + return "admin"; + } + + /** + * @api {post} /kendra/api/v1/admin/dbFind/:collectionName + * List of data based on collection + * @apiVersion 1.0.0 + * @apiGroup Admin + * @apiSampleRequest /kendra/api/v1/admin/dbFind/projects + * @param {json} Request-Body: + * { + * "query" : { + "userId": "18155ae6-493d-4369-9668-165eb6dcaa2a", + "_id": "601921116ffa9c5e9d0b53e5" + }, + "projection" : ["title"], + "limit": 100, + "skip": 2 + } + * @apiParamExample {json} Response: + * { + "message": "Data Fetched Or Updated Successfully", + "status": 200, + "result": [ + { + "_id": "601921e86ffa9c5e9d0b53e7", + "title": "Please edit this project for submitting your Prerak Head Teacher of the Block-19-20 project" + }, + { + "_id": "60193ce26ffa9c5e9d0b53fe", + "title": "Please edit this project for submitting your Prerak Head Teacher of the Block-19-20 project" + } + ] + * } + * @apiUse successBody + * @apiUse errorBody + */ + + /** + * List of data based on collection + * @method + * @name dbFind + * @param {String} _id - MongoDB Collection Name + * @param {Object} req - Req Body + * @returns {JSON} list of data. + */ + + async dbFind(req) { + return new Promise(async (resolve, reject) => { + try { + + let result = await adminHelper.dbFind( + req.params._id, + req.body + ); + + return resolve(result); + + } catch (error) { + return reject({ + status: error.status || httpStatusCode.internal_server_error.status, + message: error.message || httpStatusCode.internal_server_error.message, + errorObject: error + }) + } + }) + } + + // /** + // * @api {post} /kendra/api/v1/admin/dbDelete/:collectionName + // * Delete the data from collection + // * @apiVersion 1.0.0 + // * @apiGroup Admin + // * @apiSampleRequest /kendra/api/v1/admin/dbDelete/projects + // * @param {json} Request-Body: + // * { + // * "query" : { + // * "_id": { $in : ["601921e86ffa9c5e9d0b53e7","60193ce26ffa9c5e9d0b53fe"]}, + // }, + // "mongoIdKeys" : ["_id"] + // } + // * @apiParamExample {json} Response: + // * { + // "message": "Data deleted successfully", + // "status": 200 + // * } + // * @apiUse successBody + // * @apiUse errorBody + // */ + + // /** + // * Delete the data from collection + // * @method + // * @name dbDelete + // * @param {String} _id - MongoDB Collection Name + // * @param {Object} req - Req Body + // * @returns {JSON} list of data. + // */ + + // async dbDelete(req) { + // return new Promise(async (resolve, reject) => { + // try { + + // let result = await adminHelper.dbDelete( + // req.params._id, + // req.body + // ); + + // return resolve(result); + + // } catch (error) { + // return reject({ + // status: error.status || httpStatusCode.internal_server_error.status, + // message: error.message || httpStatusCode.internal_server_error.message, + // errorObject: error + // }) + // } + // }) + // } + + /** + * @api {post} /kendra/api/v1/admin/dbUpdate/:collectionName + * Update the data in collection + * @apiVersion 1.0.0 + * @apiGroup Admin + * @apiSampleRequest /kendra/api/v1/admin/dbUpdate/projects + * @param {json} Request-Body: + * { + "findQuery": { + "_id": { "$in" : ["601921e86ffa9c5e9d0b53e7"] } + }, + "mongoIdKeys" : ["_id"], + "updateQuery" : { + "$set" : { "status": "started"} + } + } + * @apiParamExample {json} Response: + * { + "message": "Data Updated successfully", + "status": 200 + * } + * @apiUse successBody + * @apiUse errorBody + + + /** + * Update the data in collection + * @method + * @name dbUpdate + * @param {String} _id - MongoDB Collection Name + * @param {Object} req - Req Body + * @returns {JSON} list of data. + */ + + async dbUpdate(req) { + return new Promise(async (resolve, reject) => { + try { + + let result = await adminHelper.dbUpdate( + req.params._id, + req.body + ); + + return resolve(result); + + } catch (error) { + return reject({ + status: error.status || httpStatusCode.internal_server_error.status, + message: error.message || httpStatusCode.internal_server_error.message, + errorObject: error + }) + } + }) + } + + /** + * @api {post} /kendra/api/v1/admin/dbCreate/:collectionName + * Create the new record + * @apiVersion 1.0.0 + * @apiGroup Admin + * @apiSampleRequest /kendra/api/v1/admin/dbCreate/apps + * @param {json} Request-Body: + * { + "name": "samiksha", + "displayName": "Samiksha", + "description": "Get the app to discover more", + "logo": "https://storage.googleapis.com/download/storage/v1/b/sl-dev-storage/o/static%2Fapps%2Fsamiksha.png?alt=media", + "playstoreLink": "https://play.google.com/store/apps/details?id=org.shikshalokam.samiksha", + "appStoreLink": "https://apps.apple.com/in/app/shikshalokam-samiksha/id1442066610" + } + * @apiParamExample {json} Response: + * { + "message": "Data Created successfully", + "status": 200, + "result" : { + "_id": "601921e86ffa9c5e9d0b53e7" + } + } + * } + * @apiUse successBody + * @apiUse errorBody + */ + + /** + * Create the new record + * @method + * @name dbCreate + * @param {String} _id - MongoDB Collection Name + * @param {Object} req - Req Body + * @returns {JSON} list of data. + */ + + async dbCreate(req) { + return new Promise(async (resolve, reject) => { + try { + + let result = await adminHelper.dbCreate( + req.params._id, + req.body + ); + + return resolve(result); + + } catch (error) { + return reject({ + status: error.status || httpStatusCode.internal_server_error.status, + message: error.message || httpStatusCode.internal_server_error.message, + errorObject: error + }) + } + }) + } + +}; + \ No newline at end of file diff --git a/controllers/v1/app-releases.js b/controllers/v1/app-releases.js index 32781fee..2cf26b33 100644 --- a/controllers/v1/app-releases.js +++ b/controllers/v1/app-releases.js @@ -23,298 +23,6 @@ static get name() { return "appReleases"; } - - /** - * @api {post} /kendra-service/api/v1/app-releases/create - * Add latest app release - * @apiVersion 1.0.0 - * @apiGroup appVersion - * @apiSampleRequest /kendra-service/api/v1/app-releases/create - * @apiParamExample {json} Request: - * { - * "appName":"samiksha", - * "version" : "2.2.8", - * "releaseType" : "minor", - * "os" : "android", - * "text" : "A new version of app is available !!!", - * "title" : "New update available!!", - * "status" : "active", - * "appType" : "assessment", - * "releaseNotes":"new feature like image capture is available" - * } - * @apiParamExample {json} Response: - * { - * "message": "App version created", - * "status": 200, - * "result": { - * "appName": "samiksha", - * "version": "2.2.8", - * "releaseType": "minor", - * "os": "android", - * "text": "A new version of app is available !!!", - * "title": "New update available!!", - * "status": "active", - * "appType": "assessment", - * "createdBy" : "", - * "updatedBy" : "", - * "releaseNotes": "new feature like image capture is available", - * "message": "App version updated" - * } - * } - * @apiUse successBody - * @apiUse errorBody - */ - - /** - * Create app version data. - * @method - * @name create - * @param {Request} req request body. - * @returns {JSON} created app version data. - */ - - async create(req) { - return new Promise(async (resolve, reject) => { - try { - - let createAppRelease = await versionHelper.create( - req.body, - req.userDetails.userId - ); - - return resolve({ - message : constants.apiResponses.APP_VERSION_CREATED, - result : createAppRelease - }); - - } catch (error) { - reject({ - status: - error.status || - httpStatusCode["internal_server_error"].status, - - message: - error.message || - httpStatusCode["internal_server_error"].message - }) - } - }) - } - - /** - * @api {post} /kendra-service/api/v1/app-releases/update - * Update app release data - * @apiVersion 1.0.0 - * @apiGroup appVersion - * @apiSampleRequest /kendra-service/api/v1/app-releases/update/5e7cb395fb8ce4182f5ffcac - * @apiParamExample {json} Request: - * { - * "appName":"samiksha", - * "version" : "2.2.8", - * "releaseType" : "minor", - * "os" : "android", - * "text" : "A new version of app is available !!!", - * "title" : "New update available!!", - * "status" : "active", - * "appType" : "assessment", - * "releaseNotes":"new feature like image capture is available" - * } - * @apiParamExample {json} Response: - * { - * "message": "App version updated", - * "status": 200, - * "result": { - * "appName": "samiksha", - * "version": "2.2.9", - * "releaseType": "minor", - * "os": "android", - * "text": "A new version of app is available !!!", - * "title": "New update available!!", - * "status": "active", - * "appType": "assessment", - * "releaseNotes": "new feature like image capture is available", - * "updatedBy" : "", - * "createdBy" : "" - * } - * } - * @apiUse successBody - * @apiUse errorBody - */ - - /** - * Update app version data. - * @method - * @name update - * @param {Request} req request body. - * @returns {JSON} updated app version data. - */ - - async update(req) { - return new Promise(async (resolve, reject) => { - try { - - req.body.updatedBy = req.userDetails.userId; - req.body.updatedAt = new Date(); - - let updateVersionData = await versionHelper.update( - req.params._id, - req.body - ); - - return resolve(updateVersionData); - - } catch (error) { - reject({ - status: - error.status || - httpStatusCode["internal_server_error"].status, - - message: - error.message || - httpStatusCode["internal_server_error"].message - }) - } - }) - } - - /** - * @api {get} /kendra-service/api/v1/app-releases/list?appName=:appName&os=:os&releaseType=:releaseType&status=:status - * List of app releases - * @apiVersion 1.0.0 - * @apiGroup appVersion - * @apiSampleRequest /kendra-service/api/v1/app-releases/list?appName=samiksha&os=android&releaseType=minor&status=active - * @apiParamExample {json} Response: - * { - * "message": "Lists of app version data", - * "status": 200, - * "result": [ - * { - * "_id": "5e664e2437cef27c691183d7", - * "appName": "samiksha", - * "os": "android", - * "version": "2.2.6", - * "__v": 0, - * "appType": "assessment", - * "createdAt": "2020-03-09T14:09:40.940Z", - * "createdBy" : "", - * "releaseNotes": "new feature like image capture is available", - * "releaseType": "minor", - * "status": "inactive", - * "text": "A new version of this app is available.", - * "title": "New update available !", - * "updatedAt": "2020-03-26T11:04:51.976Z", - * "updatedBy" : "" - * } - * ] - * } - * @apiUse successBody - * @apiUse errorBody - */ - - /** - * Lists of app version data. - * @method - * @name list - * @param {Request} req request data. - * @returns {JSON} App version data lists. - */ - - async list(req) { - return new Promise(async (resolve, reject) => { - try { - - let listOfVersionData = await versionHelper.versionDataList( - req.query - ); - - return resolve(listOfVersionData); - - } catch (error) { - reject({ - status: - error.status || - httpStatusCode["internal_server_error"].status, - - message: - error.message || - httpStatusCode["internal_server_error"].message - }) - } - }) - } - - /** - * @api {post} /kendra-service/api/v1/app-releases/upload - * Upload latest app release - * @apiVersion 1.0.0 - * @apiGroup appVersion - * @apiSampleRequest /kendra-service/api/v1/app-releases/upload - * @apiParam {File} versionUpdate Mandatory versionUpdate file of type CSV. - * @apiUse successBody - * @apiUse errorBody - */ - - /** - * Upload latest app version. - * @method - * @name upload - * @param {Request} req request body. - * @returns {JSON} Response consists of message and status code. - */ - - async upload(req) { - return new Promise(async (resolve, reject) => { - - try { - - if (!req.files || !req.files.versionUpdate) { - return resolve( - { - status : httpStatusCode["bad_request"].status, - message : constants.apiResponses.VERSION_UPDATE_FILE_TYPE - } - ) - } - - let versionData = - await csv().fromString(req.files.versionUpdate.data.toString()); - - const fileName = `version-update`; - let fileStream = new csvFileStream(fileName); - let input = fileStream.initStream(); - - (async function () { - await fileStream.getProcessorPromise(); - return resolve({ - isResponseAStream: true, - fileNameWithPath: fileStream.fileNameWithPath() - }); - })(); - - let createdByUser = req.userDetails.userId; - - for( let version = 0; version < versionData.length; version ++) { - - let result = await versionHelper.upload( - versionData[version], - createdByUser - ); - input.push(result); - } - input.push(null); - } catch (error) { - reject({ - status: - error.status || - httpStatusCode["internal_server_error"].status, - - message: - error.message || - httpStatusCode["internal_server_error"].message - }) - } - }) - } }; \ No newline at end of file diff --git a/controllers/v1/apps.js b/controllers/v1/apps.js index 1c49f982..ff7e4f23 100644 --- a/controllers/v1/apps.js +++ b/controllers/v1/apps.js @@ -78,145 +78,6 @@ } }) } - - - /** - * @api {post} /kendra/api/v1/apps/create - * Create an app - * @apiVersion 1.0.0 - * @apiGroup Apps - * @apiSampleRequest /kendra/api/v1/apps/details/create - * @apiParamExample {fromData} Request: - * { - "name": "samiksha", - "displayName": "Samiksha", - "description": "Get the app to discover more", - "playstoreLink": "https://play.google.com/store/apps/details?id=org.shikshalokam.samiksha", - "appStoreLink": "https://apps.apple.com/in/app/shikshalokam-samiksha/id1442066610", - "status": "active" - * } - * @apiParamExample {json} Response: - * { - "message": "App created successfully.", - "status": 200 - * } - * @apiUse successBody - * @apiUse errorBody - */ - - /** - * Create app - * @method - * @name create - * @param {files} req.files.logo - app logo - * @param {String} req.body.name - name of the app - * @param {String} req.body.displayName - displayName of the app - * @param {String} req.body.description - description of the app - * @param {String} req.body.playstoreLink - playstoreLink of the app - * @param {String} req.body.appStoreLink - appStoreLink of the app - * @param {String} req.body.status - status - * @returns {String} - message . - */ - - async create(req) { - return new Promise(async (resolve, reject) => { - try { - - let createDetails = await appsHelper.create( - req.files.logo, - req.body - ); - - return resolve({ - message : createDetails.message, - result : createDetails.data - }); - - } catch (error) { - reject({ - status: - error.status || - httpStatusCode["internal_server_error"].status, - - message: - error.message || - httpStatusCode["internal_server_error"].message - }) - } - }) - } - - /** - * @api {post} /kendra/api/v1/apps/update/{{name}} - * Update app details - * @apiVersion 1.0.0 - * @apiGroup Apps - * @apiSampleRequest /kendra/api/v1/apps/details/update/samiksha - * @apiParamExample {fromData} Request: - * { - "displayName": "Samiksha", - "description": "Get the app to discover more", - "playstoreLink": "https://play.google.com/store/apps/details?id=org.shikshalokam.samiksha", - "appStoreLink": "https://apps.apple.com/in/app/shikshalokam-samiksha/id1442066610", - "status": "active" - * } - * @apiParamExample {json} Response: - * { - "message": "App details updated successfully.", - "status": 200 - * } - * @apiUse successBody - * @apiUse errorBody - */ - - /** - * Update app details - * @method - * @name update - * @param {String} req.params._id - name of the app - * @param {files} req.files.logo - app logo - * @param {String} req.body.displayName - displayName of the app - * @param {String} req.body.description - description of the app - * @param {String} req.body.playstoreLink - playstoreLink of the app - * @param {String} req.body.appStoreLink - appStoreLink of the app - * @param {String} req.body.status - status - * @returns {String} - message . - */ - - async update(req) { - return new Promise(async (resolve, reject) => { - try { - - let file = ""; - - if(req.files && req.files.logo) { - file = req.files.logo; - } - - let updateDetails = await appsHelper.update( - req.params._id, - file, - req.body - ); - - return resolve({ - message: updateDetails.message, - result: updateDetails.data - }); - - } catch (error) { - reject({ - status: - error.status || - httpStatusCode["internal_server_error"].status, - - message: - error.message || - httpStatusCode["internal_server_error"].message - }) - } - }) - } }; \ No newline at end of file diff --git a/controllers/v1/certificateBaseTemplates.js b/controllers/v1/certificateBaseTemplates.js new file mode 100644 index 00000000..170bce10 --- /dev/null +++ b/controllers/v1/certificateBaseTemplates.js @@ -0,0 +1,83 @@ +/** + * name : certificateBaseTemplates.js + * author : Vishnu + * created-date : 11-Jan-2023 + * Description : Certificate base template related information. +*/ + +// Dependencies +const certificateBaseTemplateHelper = require(MODULES_BASE_PATH + "/certificateBaseTemplates/helper"); + +module.exports = class CertificateBaseTemplates extends Abstract { + // Adding model schema + constructor() { + super(schemas["certificateBaseTemplates"]); + } + + /** + * @api {post/patch} /kendra/api/v1/certificateBaseTemplates/createOrUpdate + * @apiVersion 1.0.0 + * @apiName Create certificate template + * @apiGroup certificateBaseTemplates + * @apiParamExample {json} Request-Body: + * { + "code": "SingleLogoSingleSign" + } + + * @apiHeader {String} internal-access-token - internal access token + * @apiHeader {String} X-authenticated-user-token - Authenticity token + * @apiSampleRequest /kendra/api/v1/certificateBaseTemplates/createOrUpdate + * @apiUse successBody + * @apiUse errorBody + * @apiParamExample {json} Response: + * { + "status": 200, + "message": "Base template added successfully", + "result": { + id": "6011136a2d25b926974d9ec9" + } + } + */ + + /** + * Create/update certificate base template. + * @method + * @name createOrUpdate + * @param {Object} req - requested data. + * @returns {JSON} Created certificate base template data. + */ + + async createOrUpdate(req) { + return new Promise(async (resolve, reject) => { + try { + if ( req.method === "POST" ) { + if ( req.files && req.files.file ) { + let certificateBaseTemplateData = await certificateBaseTemplateHelper.create( req.body, req.files, req.userDetails.userId ); + certificateBaseTemplateData["result"] = certificateBaseTemplateData.data; + return resolve(certificateBaseTemplateData); + } else { + throw ({ + status: httpStatusCode["bad_request"].status, + message: httpStatusCode["bad_request"].message + }); + } + } else if ( req.method === "PATCH" ) { + let certificateBaseTemplateData = await certificateBaseTemplateHelper.update( + req.params._id, + req.body, + req.files, + req.userDetails.userId + ); + certificateBaseTemplateData["result"] = certificateBaseTemplateData.data; + return resolve(certificateBaseTemplateData); + } + } catch (error) { + return reject({ + status: error.status || httpStatusCode.internal_server_error.status, + message: error.message || httpStatusCode.internal_server_error.message, + errorObject: error + }); + } + }); + } +} \ No newline at end of file diff --git a/controllers/v1/certificateTemplates.js b/controllers/v1/certificateTemplates.js new file mode 100644 index 00000000..e0a74d27 --- /dev/null +++ b/controllers/v1/certificateTemplates.js @@ -0,0 +1,245 @@ +/** + * name : certificateTemplates.js + * author : Vishnu + * created-date : 29-Sep-2022 + * Description : Certificate template related information. +*/ + +// Dependencies +const certificateTemplateHelper = require(MODULES_BASE_PATH + "/certificateTemplates/helper"); + +module.exports = class CertificateTemplates extends Abstract { + // Adding model schema + constructor() { + super(schemas["certificateTemplates"]); + } + + /** + * @api {post/patch} /kendra/api/v1/certificateTemplates/createOrUpdate + * @apiVersion 1.0.0 + * @apiName Create certificate template + * @apiGroup CertificateTemplates + * @apiParamExample {json} Request-Body: + * { "templateUrl" :"certificateTemplates/6343bd978f9d8980b7841e85/ba9aa220-ff1b-4717-b6ea-ace55f04fc16_2022-9-10-1665383945769.svg", + "criteria" : { + "validationText" : "Complete validation message", + "expression" : "C1&&C2&&C3", + "conditions" : { + "C1" : { + "validationText" : "Project Should be submitted within program Enddate", + "expression" : "C1&&C2", + "conditions" : { + "C1" : { + "scope" : "project", + "key" : "status", + "operator" : "==", + "value" : "submitted" + }, + "C2" : { + "scope" : "project", + "key" : "completedDate", + "operator" : "<", + "value" : "15-08-2022" + } + } + }, + "C2" : { + "validationText" : "Evidence project level validation", + "expression" : "C1", + "conditions" : { + "C1" : { + "scope" : "project", + "key" : "attachments", + "function" : "count", + "filter" : { + "key" : "type", + "value" : "all" + }, + "operator" : ">", + "value" : 1 + } + } + }, + "C3" : { + "validationText" : "Evidence task level validation", + "expression" : "C1&&C2&&C3", + "conditions" : { + "C1" : { + "scope" : "task", + "key" : "attachments", + "function" : "count", + "filter" : { + "key" : "type", + "value" : "all" + }, + "operator" : ">", + "value" : 1 + } + } + } + } + }, + "issuer": { + "name":"Kerala" + }, + "status" : "active", + "solutionId" : "5ff9dc1b9259097d48017bbe", + "programId" : "605083ba09b7bd61555580fb" + + } + + * @apiHeader {String} internal-access-token - internal access token + * @apiHeader {String} X-authenticated-user-token - Authenticity token + * @apiSampleRequest /kendra/api/v1/certificateTemplates/create + * @apiUse successBody + * @apiUse errorBody + * @apiParamExample {json} Response: + * { + "status": 200, + "message": "Template added successfully", + "result": { + id": "6011136a2d25b926974d9ec9" + } + } + */ + + /** + * Create/update certificate template. + * @method + * @name createOrUpdate + * @param {Object} req - requested data. + * @returns {JSON} Created certificate template data. + */ + + async createOrUpdate(req) { + return new Promise(async (resolve, reject) => { + try { + if ( req.method === "POST" ) { + let certificateTemplateData = await certificateTemplateHelper.create( req.body ); + certificateTemplateData["result"] = certificateTemplateData.data; + return resolve(certificateTemplateData); + } else if ( req.method === "PATCH" ) { + let certificateTemplateData = await certificateTemplateHelper.update( + req.params._id, + req.body, + ); + certificateTemplateData["result"] = certificateTemplateData.data; + return resolve(certificateTemplateData); + } + } catch (error) { + return reject({ + status: error.status || httpStatusCode.internal_server_error.status, + message: error.message || httpStatusCode.internal_server_error.message, + errorObject: error + }); + } + }); + } + + /** + * @api {post} /kendra/api/v1/certificateTemplates/uploadCertificateTemplate + * @apiVersion 1.0.0 + * @apiName upload certificate template + * @apiGroup uploadCertificateTemplate + * @apiHeader {String} internal-access-token - internal access token + * @apiHeader {String} X-authenticated-user-token - Authenticity token + * @apiSampleRequest /kendra/api/v1/certificateTemplates/uploadCertificateTemplate + * @apiUse successBody + * @apiUse errorBody + * @apiParamExample {json} Response: + * { + "message": "File uploaded successfully", + "status": 200, + "result": { + "success": true, + "data": { + "templateUrl": "certificateTemplates/6343bd978f9d8980b7841e85/ba9aa220-ff1b-4717-b6ea-ace55f04fc16_2022-9-10-1665383945769.svg" + } + } + } + */ + + /** + * uploadTemplate. + * @method + * @name uploadTemplate + * @param {Object} req - requested data. + * @returns {JSON} file uploaded details. + */ + + async uploadTemplate(req) { + return new Promise(async (resolve, reject) => { + try { + + if ( req.files && req.files.file ) { + let uploadedTemplateDetails = + await certificateTemplateHelper.uploadToCloud( + req.files, + req.params._id, + req.userDetails ? req.userDetails.userId : "", + req.query.updateTemplate ? req.query.updateTemplate : true + ); + return resolve({ + message: constants.apiResponses.FILE_UPLOADED, + result: uploadedTemplateDetails + }) + } else { + return reject({ + status: httpStatusCode["bad_request"].status, + message: httpStatusCode["bad_request"].message + + }); + } + } catch (error) { + return reject({ + status: error.status || httpStatusCode.internal_server_error.status, + message: error.message || httpStatusCode.internal_server_error.message, + errorObject: error + }); + } + }); + } + /** + * @api {post} /kendra/api/v1/certificateTemplates/createSvg + * @apiVersion 1.0.0 + * @apiName createSvg certificate template svg + * @apiGroup createSvg + * @apiHeader {String} internal-access-token - internal access token + * @apiHeader {String} X-authenticated-user-token - Authenticity token + * @apiSampleRequest /kendra/api/v1/certificateTemplates/createSvg?baseTemplateId= + * @apiUse successBody + * @apiUse errorBody + * @apiParamExample {json} Response: + * { + "message": "Template edited successfully", + "status": 200, + "result": { + "url": "https://sunbirdstagingpublic.blob.core.windows.net/samiksha/certificateTemplates/BASE_TEMPLATE/_22-10-2022-1669120782574.svg?sv=2020-10-02&st=2022-11-22T12%3A39%3A42Z&se=2023-11-22T12%3A49%3A42Z&sr=b&sp=rw&sig=gLnKb1T32swAQQ%2Bgtaaa967d6c0GIL%2FGRcGCwjvpI30%3D" + } +} + */ + + /** + * generate cettificate templateSvg. + * @method + * @name createSvg + * @param {Object} req - requested data. + * @returns {JSON} -svg uploaded details. + */ + + async createSvg(req) { + return new Promise(async (resolve, reject) => { + try { + + let svgData = await certificateTemplateHelper.createSvg(req.files, req.body, req.query.baseTemplateId); + return resolve(svgData); + } catch (error) { + return reject({ + status: error.status || httpStatusCode.internal_server_error.status, + message: error.message || httpStatusCode.internal_server_error.message, + errorObject: error + }); + } + }); + } +} \ No newline at end of file diff --git a/controllers/v1/dictionary/keywords.js b/controllers/v1/dictionary/keywords.js deleted file mode 100644 index e4946f89..00000000 --- a/controllers/v1/dictionary/keywords.js +++ /dev/null @@ -1,217 +0,0 @@ -/** - * name : keywords.js - * author : Akash Shah - * created-date : 02-Jan-2020 - * Description : Dictionary Keywords - */ - -// dependencies - -const csv = require('csvtojson'); -const csvFileStream = require(ROOT_PATH + "/generics/file-stream"); -const dictionaryHelper = require(MODULES_BASE_PATH + "/dictionary/helper"); - -/** - * Keywords - * @class -*/ - -module.exports = class Keywords { - - /** - * @apiDefine errorBody - * @apiError {String} status 4XX,5XX - * @apiError {String} message Error - */ - - /** - * @apiDefine successBody - * @apiSuccess {String} status 200 - * @apiSuccess {String} result Data - */ - - - /** - * @api {post} /kendra/api/v1/dictionary/keywords/upload - * Upload Keywords to Dictionary - * @apiVersion 1.0.0 - * @apiGroup Dictionary - * @apiHeader {String} X-authenticated-user-token Authenticity token - * @apiHeader {String} internal-access-token Internal access token - * @apiParam {File} keywords Mandatory keywords file of type csv. - * @apiSampleRequest /kendra/api/v1/dictionary/keywords/upload - * @apiUse successBody - * @apiUse errorBody - */ - - /** - * Push keywords data to the dictionary index. - * @method - * @name upload - * @param {Request} req request body. - * @returns {csv} Response consists of exactly - * the same csv that we upload with extra column status. - */ - - - async upload(req) { - - return new Promise(async (resolve, reject) => { - - try { - - if (!req.files || !req.files.keywords) { - throw { message: constants.apiResponses.DICTIONARY_KEYWORDS_MISSING_FILE_ERROR }; - } - - const checkIfKeywordsCanBeUploaded = await dictionaryHelper - .keywordsIndexTypeMapExists(); - - if(!checkIfKeywordsCanBeUploaded.data) { - throw { message: constants.apiResponses.DICTIONARY_KEYWORDS_MAPPING_MISSING_ERROR } - } - - let keywordsData = - await csv().fromString(req.files.keywords.data.toString()); - - const fileName = `keywords`; - let fileStream = new csvFileStream(fileName); - let input = fileStream.initStream(); - - (async function () { - await fileStream.getProcessorPromise(); - return resolve({ - isResponseAStream: true, - fileNameWithPath: fileStream.fileNameWithPath() - }); - })(); - - for (let pointerToKeywordsData = 0; - pointerToKeywordsData < keywordsData.length; - pointerToKeywordsData++) { - const row = keywordsData[pointerToKeywordsData]; - row.status = constants.common.FAILED; - if(row.word && row.word != "") { - let addOrRemoveOperation - if(row.action == "remove") { - addOrRemoveOperation = await dictionaryHelper.removeWordFromDictionary(row.word); - } else { - addOrRemoveOperation = await dictionaryHelper.addWordToDictionary(row.word); - } - if(!addOrRemoveOperation.data) { - row.status = constants.common.FAILED; - } else { - row.status = constants.common.SUCCESS; - } - } - input.push(row); - } - - input.push(null); - - } catch (error) { - - return reject({ - status: - error.status || httpStatusCode["internal_server_error"].status, - - message: - error.message || httpStatusCode["internal_server_error"].message, - - errorObject: error - }); - - } - }) - - } - - - /** - * @api {post} /kendra/api/v1/dictionary/keywords/update - * Update content Keywords in Dictionary - * @apiVersion 1.0.0 - * @apiGroup Dictionary - * @apiHeader {String} X-authenticated-user-token Authenticity token - * @apiHeader {String} internal-access-token Internal access token - * @apiParamExample {json} Response: - "keywords": [ - "Keyword1", - "Keyword2", - "Keyword3" - ] - * @apiSampleRequest /kendra/api/v1/dictionary/keywords/update - * @apiUse successBody - * @apiUse errorBody - */ - - /** - * Update Content Keywords in Dictionary. - * @method - * @name update - * @param {Request} req request body. - * @returns {json} Response consists of success or failure to update keywords. - */ - - - async update(req) { - - return new Promise(async (resolve, reject) => { - - try { - - const checkIfKeywordsCanBeUploaded = await dictionaryHelper - .keywordsIndexTypeMapExists(); - - if(!checkIfKeywordsCanBeUploaded.data) { - throw { message: constants.apiResponses.DICTIONARY_KEYWORDS_MAPPING_MISSING_ERROR } - } - - let keywordsData = req.body.keywords; - - let keywordsUpdateResult = new Array; - - for (let pointerToKeywordsData = 0; - pointerToKeywordsData < keywordsData.length; - pointerToKeywordsData++) { - const keyword = keywordsData[pointerToKeywordsData]; - let status = constants.common.FAILED; - if(keyword != "") { - let addOrUpdateKeyword = await dictionaryHelper.addWordToDictionary(keyword); - - if(!addOrUpdateKeyword.data) { - status = constants.common.FAILED; - } else { - status = constants.common.SUCCESS; - } - } - keywordsUpdateResult.push({ - keyword : keyword, - status : status - }); - } - - return resolve({ - result: keywordsUpdateResult, - message: constants.apiResponses.DICTIONARY_KEYWORDS_UPDATE_SUCCESS - }); - - } catch (error) { - - return reject({ - status: - error.status || httpStatusCode["internal_server_error"].status, - - message: - error.message || httpStatusCode["internal_server_error"].message, - - errorObject: error - }); - - } - }) - - } - -}; - diff --git a/controllers/v1/entities.js b/controllers/v1/entities.js index 73068539..35ce6360 100644 --- a/controllers/v1/entities.js +++ b/controllers/v1/entities.js @@ -152,9 +152,8 @@ module.exports = class Entities extends Abstract { */ subEntityList(req) { - return new Promise(async (resolve, reject) => { - + if( !(req.params._id || req.body.entities) ) { return resolve({ status : httpStatusCode.bad_request.status, @@ -162,99 +161,35 @@ module.exports = class Entities extends Abstract { }) } - try { - - let entityDocuments = await entitiesHelper.subEntityList( - req.body.entities ? req.body.entities : "", - req.params._id ? req.params._id : "", - req.query.type ? req.query.type : "", - req.searchText, - req.pageSize, - req.pageNo - ); - - if(entityDocuments.result && entityDocuments.result.data && Array.isArray(entityDocuments.result.data) && entityDocuments.result.data.length > 0) { - for (let pointerToEntitiesArray = 0; pointerToEntitiesArray < entityDocuments.result.data.length; pointerToEntitiesArray++) { - if(entityDocuments.result.data[pointerToEntitiesArray].entityType == "school") { - entityDocuments.result.data[pointerToEntitiesArray].label += " - " + entityDocuments.result.data[pointerToEntitiesArray].externalId; - } - } - } - - return resolve(entityDocuments); - - } catch (error) { - - return reject({ - status: error.status || httpStatusCode.internal_server_error.status, - message: error.message || httpStatusCode.internal_server_error.message, - errorObject: error - }) - - } - - - }) - } - - /** - * @api {get} /kendra/api/v1/entities/details/:entityId - * Get entities details information - * @apiVersion 1.0.0 - * @apiGroup Entities - * @apiHeader {String} X-authenticated-user-token Authenticity token - * @apiSampleRequest /kendra/api/v1/entities/details/5db173598a8e070bedca6ba1 - * @apiUse successBody - * @apiUse errorBody - * @apiParamExample {json} Response: - * { - * "message": "Entity information fetched successfully", - * "status": 200, - * "result": { - "_id": "5db173598a8e070bedca6ba1", - "entityTypeId": "5d7a290e6371783ceb11064c", - "entityType": "state", - "metaInformation": { - "externalId": "DL", - "name": "Delhi", - "region": "NORTH", - "capital": "NEW DELHI" - }, - "updatedBy": "2be2fd94-f25e-4402-8e36-20907b45c650", - "createdBy": "2be2fd94-f25e-4402-8e36-20907b45c650", - "updatedAt": "2019-10-24T10:16:44.833Z", - "createdAt": "2019-10-24T09:48:09.005Z" - } - } - - /** - * Entity details. - * @method - * @name details - * @param {Object} req - requested entity information. - * @param {String} req.params._id - entity id - * @returns {JSON} - Entity details information. - */ - - details(req) { - - return new Promise(async (resolve, reject) => { - try { - let result = await entitiesHelper.details( - req.params._id + let entityDocuments = await entitiesHelper.subEntityList( + req.body.entities ? req.body.entities : "", + req.params._id ? req.params._id : "", + req.query.type ? req.query.type : "", + req.searchText, + req.pageSize, + req.pageNo ); - return resolve(result); - + if(entityDocuments.result && entityDocuments.result.data && Array.isArray(entityDocuments.result.data) && entityDocuments.result.data.length > 0) { + for (let pointerToEntitiesArray = 0; pointerToEntitiesArray < entityDocuments.result.data.length; pointerToEntitiesArray++) { + if(entityDocuments.result.data[pointerToEntitiesArray].entityType == "school") { + entityDocuments.result.data[pointerToEntitiesArray].label += " - " + entityDocuments.result.data[pointerToEntitiesArray].externalId; + } + } + } + + return resolve(entityDocuments); + } catch (error) { - + return reject({ status: error.status || httpStatusCode.internal_server_error.status, message: error.message || httpStatusCode.internal_server_error.message, errorObject: error }) + } }) } @@ -317,131 +252,6 @@ module.exports = class Entities extends Abstract { }) } - /** - * @api {get} /kendra/api/v1/entities/subEntitiesRoles/:entityId - * Get roles based on entity type. - * @apiVersion 1.0.0 - * @apiGroup Entities - * @apiHeader {String} X-authenticated-user-token Authenticity token - * @apiSampleRequest /kendra/api/v1/entities/subEntitiesRoles/5da829874c67d63cca1bd9d0 - * @apiUse successBody - * @apiUse errorBody - * @apiParamExample {json} Response: - * { - "message": "Successfully fetched user roles", - "status": 200, - "result": [ - { - "_id": "5d6e521066a9a45df3aa891e", - "code": "HM", - "title": "Headmaster" - }, - { - "_id": "5d6e521066a9a45df3aa891f", - "code": "CRP", - "title": "Cluster Resource Person" - }, - { - "_id": "5d6e521066a9a45df3aa8920", - "code": "BEO", - "title": "Block Education Officer" - }, - { - "_id": "5d6e521066a9a45df3aa8921", - "code": "DEO", - "title": "District Education Officer" - } - ] - } - */ - - /** - * Roles based on entity type - * @method - * @name subEntitiesRoles - * @param {String} req.params._id - entityId. - * @returns {JSON} - Array of user roles. - */ - - subEntitiesRoles(req) { - return new Promise(async (resolve, reject) => { - - try { - - const subEntityRoles = await entitiesHelper.subEntitiesRoles(req.params._id); - - resolve(subEntityRoles); - - } catch (error) { - - return reject({ - status: - error.status || - httpStatusCode["internal_server_error"].status, - - message: - error.message || - httpStatusCode["internal_server_error"].message - }); - } - }); - } - - /** - * @api {get} /kendra/api/v1/entities/subEntityTypeList/:entityId - * Get entities child hierarchy path. - * @apiVersion 1.0.0 - * @apiGroup Entities - * @apiHeader {String} X-authenticated-user-token Authenticity token - * @apiSampleRequest /kendra/api/v1/entities/subEntityTypeList/5da829874c67d63cca1bd9d0 - * @apiUse successBody - * @apiUse errorBody - * @apiParamExample {json} Response: - * { - "message": "Entities child hierarchy path fetched successfully", - "status": 200, - "result": [ - "district", - "block", - "cluster", - "school" - ] - } - */ - - /** - * Entities child hierarchy path - * @method - * @name subEntitiesRoles - * @param {String} req.params._id - entityId. - * @returns {JSON} - Entities child hierarchy path - */ - - subEntityTypeList(req) { - return new Promise(async (resolve, reject) => { - - try { - - const subEntityTypeListData = - await entitiesHelper.subEntityTypeList(req.params._id); - - resolve(subEntityTypeListData); - - } catch (error) { - - return reject({ - status: - error.status || - httpStatusCode["internal_server_error"].status, - - message: - error.message || - httpStatusCode["internal_server_error"].message - }); - } - }); - } - /** * @api {get} /kendra/api/v1/entities/getUsersByEntityAndRole/:entityId?role="" * @apiVersion 1.0.0 @@ -509,7 +319,7 @@ module.exports = class Entities extends Abstract { * @apiVersion 1.0.0 * @apiGroup Entities * @apiHeader {String} X-authenticated-user-token Authenticity token - * @apiSampleRequest /kendra/api/v1/entities/subEntityListBasedOnRoleAndLocation/236f5cff-c9af-4366-b0b6-253a1789766a?role=DEO + * @apiSampleRequest /kendra/api/v1/entities/subEntityListBasedOnRoleAndLocation/236f5cff-c9af-4366-b0b6-253a1789766a?role=DEO,HM * @apiUse successBody * @apiUse errorBody * @apiParamExample {json} Response: @@ -538,11 +348,24 @@ module.exports = class Entities extends Abstract { try { - const subEntityTypeListData = - await entitiesHelper.subEntityListBasedOnRoleAndLocation( - req.params._id, - req.query.role - ); + + let currentMaximumCountOfRequiredEntities = 0; + let subEntityTypeListData = new Array; + // Calculate required entity type for each of the role and send the output of the role which has maximum length. + for (let roleCount = 0; roleCount < req.query.role.split(",").length; roleCount++) { + const eachRole = req.query.role.split(",")[roleCount]; + const entityTypeMappingData = + await entitiesHelper.subEntityListBasedOnRoleAndLocation( + req.params._id, + eachRole + ); + + if(entityTypeMappingData.result && entityTypeMappingData.result.length > currentMaximumCountOfRequiredEntities) { + currentMaximumCountOfRequiredEntities = entityTypeMappingData.result.length; + subEntityTypeListData = entityTypeMappingData; + subEntityTypeListData.result = entityTypeMappingData.result; + } + } resolve(subEntityTypeListData); @@ -560,5 +383,66 @@ module.exports = class Entities extends Abstract { } }); } + + /** + * @api {get} /kendra/api/v1/entities/details/:entityId + * Get entities details information + * @apiVersion 1.0.0 + * @apiGroup Entities + * @apiHeader {String} X-authenticated-user-token Authenticity token + * @apiSampleRequest /kendra/api/v1/entities/details/5db173598a8e070bedca6ba1 + * @apiUse successBody + * @apiUse errorBody + * @apiParamExample {json} Response: + * { + * "message": "Entity information fetched successfully", + * "status": 200, + * "result": { + "_id": "5db173598a8e070bedca6ba1", + "entityTypeId": "5d7a290e6371783ceb11064c", + "entityType": "state", + "metaInformation": { + "externalId": "DL", + "name": "Delhi", + "region": "NORTH", + "capital": "NEW DELHI" + }, + "updatedBy": "2be2fd94-f25e-4402-8e36-20907b45c650", + "createdBy": "2be2fd94-f25e-4402-8e36-20907b45c650", + "updatedAt": "2019-10-24T10:16:44.833Z", + "createdAt": "2019-10-24T09:48:09.005Z" + } + } + /** + * Entity details. + * @method + * @name details + * @param {Object} req - requested entity information. + * @param {String} req.params._id - entity id + * @returns {JSON} - Entity details information. + */ + + details(req) { + + return new Promise(async (resolve, reject) => { + + try { + + let result = await entitiesHelper.details( + req.params._id ? req.params._id :"",req.body ? req.body : {} + ); + + return resolve(result); + + } catch (error) { + + return reject({ + status: error.status || httpStatusCode.internal_server_error.status, + message: error.message || httpStatusCode.internal_server_error.message, + errorObject: error + }) + } + }) + } }; diff --git a/controllers/v1/programUsers.js b/controllers/v1/programUsers.js new file mode 100644 index 00000000..6ed2242d --- /dev/null +++ b/controllers/v1/programUsers.js @@ -0,0 +1,22 @@ +/** + * name : programUsers.js + * author : Vishnu + * created-date : 9-Jan-2023 + * Description : PII data related controller. +*/ + +/** + * programUsers + * @class + */ +module.exports = class ProgramUsers extends Abstract { + constructor() { + super(schemas["programUsers"]); + } + + static get name() { + return "programUsers"; + } + +}; + diff --git a/controllers/v1/programs.js b/controllers/v1/programs.js index 0fa8e7b2..ddd72a86 100644 --- a/controllers/v1/programs.js +++ b/controllers/v1/programs.js @@ -100,10 +100,6 @@ module.exports = class Programs extends Abstract { ], "keywords" : [], "concepts" : [], - "createdFor" : [ - "0126427034137395203", - "0124487522476933120" - ], "userId":"a082787f-8f8f-42f2-a706-35457ca6f1fd", "imageCompression" : { "quality" : 10 @@ -116,6 +112,8 @@ module.exports = class Programs extends Abstract { "entities" : ["5d6609ef81a57a6173a79e78"], "roles" : ["HM"] } + "startDate" : "2023-04-06T09:35:00.000Z", + "endDate" : ""2024-04-06T09:35:00.000Z" // optional } * @apiParamExample {json} Response: { @@ -184,10 +182,6 @@ module.exports = class Programs extends Abstract { ], "keywords" : [], "concepts" : [], - "createdFor" : [ - "0126427034137395203", - "0124487522476933120" - ], "userId":"a082787f-8f8f-42f2-a706-35457ca6f1fd", "imageCompression" : { "quality" : 10 @@ -200,6 +194,8 @@ module.exports = class Programs extends Abstract { "entities" : ["5d6609ef81a57a6173a79e78"], "roles" : ["HM"] } + "startDate" : "2023-04-06T09:35:00.000Z", + "endDate" : ""2024-04-06T09:35:00.000Z" // optional } * @apiParamExample {json} Response: { @@ -245,71 +241,6 @@ module.exports = class Programs extends Abstract { } }) } - - /** - * @api {post} /assessment/api/v1/programs/forUserRoleAndLocation?page=:page&limit=:limit Auto targeted programs - * @apiVersion 1.0.0 - * @apiName Auto targeted programs - * @apiGroup Programs - * @apiParamExample {json} Request-Body: - * { - "role" : "HM", - "state" : "236f5cff-c9af-4366-b0b6-253a1789766a", - "district" : "1dcbc362-ec4c-4559-9081-e0c2864c2931", - "school" : "c5726207-4f9f-4f45-91f1-3e9e8e84d824" - } - * @apiHeader {String} X-authenticated-user-token Authenticity token - * @apiSampleRequest /assessment/api/v1/programs/forUserRoleAndLocation?page=1&limit=5 - * @apiUse successBody - * @apiUse errorBody - * @apiParamExample {json} Response: - * { - "message": "Targeted programs fetched successfully", - "status": 200, - "result": { - "data": [ - { - "_id": "5ff438b04698083dbfab7284", - "externalId": "TEST_SCOPE_PROGRAM", - "name": "TEST scope in program", - "solutions": 16 - } - ], - "count": 1 - }} - */ - - /** - * Lists of programs based on role and location. - * @method - * @name forUserRoleAndLocation - * @param {Object} req - requested data. - * @returns {Array} List of programs. - */ - - async forUserRoleAndLocation(req) { - return new Promise(async (resolve, reject) => { - try { - - let targetedPrograms = await programsHelper.forUserRoleAndLocation( - req.body, - req.pageSize, - req.pageNo, - req.searchText - ); - - targetedPrograms.result = targetedPrograms.data; - return resolve(targetedPrograms); - - } catch (error) { - return reject({ - status: error.status || httpStatusCode.internal_server_error.status, - message: error.message || httpStatusCode.internal_server_error.message, - errorObject: error - }); - } - }); - } /** * @api {post} /assessment/api/v1/programs/addRolesInScope/:programId Add roles in programs @@ -515,4 +446,110 @@ module.exports = class Programs extends Abstract { }); } -} \ No newline at end of file + /** + * @api {get} /assessment/api/v1/programs/details/:programId Program Details + * @apiVersion 1.0.0 + * @apiName Program Details + * @apiGroup Programs + * @apiHeader {String} X-authenticated-user-token Authenticity token + * @apiSampleRequest /assessment/api/v1/programs/details/5ffbf8909259097d48017bbf + * @apiUse successBody + * @apiUse errorBody + * @apiParamExample {json} Response: + * { + "message": "Program details fetched successfully", + "status": 200, + "result": { + + } + } + */ + + /** + * Details of the program + * @method + * @name details + * @param {Object} req - requested data. + * @param {String} req.params._id - program id. + * @returns {Array} Program scope roles. + */ + + async details(req) { + return new Promise(async (resolve, reject) => { + try { + + let programData = await programsHelper.details( + req.params._id + ); + + programData["result"] = programData.data; + + return resolve(programData); + + } catch (error) { + return reject({ + status: error.status || httpStatusCode.internal_server_error.status, + message: error.message || httpStatusCode.internal_server_error.message, + errorObject: error + }); + } + }); + } + + /** + * @api {get} /assessment/api/v1/programs/join/:programId + * @apiVersion 1.0.0 + * @apiName Program Join + * @apiGroup Programs + * @apiHeader {String} X-authenticated-user-token Authenticity token + * @apiHeader {String} X-App-Ver Appversion + * @apiSampleRequest /assessment/api/v1/programs/join/5ffbf8909259097d48017bbf + * @apiUse successBody + * @apiUse errorBody + * @apiParamExample {json} Response: + * + */ + + /** + * join program + * @method + * @name join + * @param {Object} req - requested data. + * @param {String} req.params._id - program id. + * @returns {Object} Program join status. + */ + + async join(req) { + return new Promise(async (resolve, reject) => { + try { + + let programJoin = await programsHelper.join( + req.params._id, + req.body, + req.userDetails.userId, + req.userDetails.userToken, + req.headers["x-app-id"] ? + req.headers["x-app-id"] : + req.headers.appname ? req.headers.appname : "", + req.headers["x-app-ver"] ? + req.headers["x-app-ver"] : + req.headers.appversion ? req.headers.appversion : "", + req.headers["internal-access-token"] ? + true : + req.headers.internalAccessToken ? true : false + ); + programJoin["result"] = programJoin.data; + return resolve(programJoin); + + } catch (error) { + return reject({ + status: error.status || httpStatusCode.internal_server_error.status, + message: error.message || httpStatusCode.internal_server_error.message, + errorObject: error + }); + } + }); + } + +} + diff --git a/controllers/v1/solutions.js b/controllers/v1/solutions.js index ff134626..c72ef519 100644 --- a/controllers/v1/solutions.js +++ b/controllers/v1/solutions.js @@ -4,6 +4,9 @@ * created-date : 03-Sep-2020 * Description : Solution related information. */ + + + // Dependencies const solutionsHelper = require(MODULES_BASE_PATH + "/solutions/helper"); module.exports = class Solutions extends Abstract { @@ -23,26 +26,22 @@ module.exports = class Solutions extends Abstract { * "language" : [], * "keywords" : [], * "concepts" : [], - * "createdFor" : ["01305447637218918413"], "themes" : [], "flattenedThemes" : [], - "entities" : [ - "5beaa888af0065f0e0a10515" - ], + "entities" : ["bc75cc99-9205-463e-a722-5326857838f8","8ac1efe9-0415-4313-89ef-884e1c8eee34"] "registry" : [], "isRubricDriven" : false, "enableQuestionReadOut" : false, "allowMultipleAssessemts" : false, "isDeleted" : false, - "rootOrganisations" : [ - "01305447637218918413" - ], "programExternalId" : "AMAN_TEST_123-1607937244986", "entityType" : "school", "type" : "improvementProject", "subType" : "improvementProject", "isReusable" : false, - "externalId" : "01c04166-a65e-4e92-a87b-a9e4194e771d-1607936956167" + "externalId" : "01c04166-a65e-4e92-a87b-a9e4194e771d-1607936956167", + "minNoOfSubmissionsRequired" : 2, + "allowMultipleAssessemts" : true } * @apiHeader {String} internal-access-token internal access token * @apiHeader {String} X-authenticated-user-token Authenticity token @@ -210,7 +209,7 @@ module.exports = class Solutions extends Abstract { * @apiGroup Solutions * @apiParamExample {json} Request-Body: * { - "role" : "HM", + "role" : "HM,DEO", "state" : "236f5cff-c9af-4366-b0b6-253a1789766a", "district" : "1dcbc362-ec4c-4559-9081-e0c2864c2931", "school" : "c5726207-4f9f-4f45-91f1-3e9e8e84d824", @@ -279,7 +278,7 @@ module.exports = class Solutions extends Abstract { * @apiGroup Solutions * @apiParamExample {json} Request-Body: * { - "role" : "HM", + "role" : "HM,DEO", "state" : "236f5cff-c9af-4366-b0b6-253a1789766a", "district" : "1dcbc362-ec4c-4559-9081-e0c2864c2931", "school" : "c5726207-4f9f-4f45-91f1-3e9e8e84d824" @@ -542,12 +541,12 @@ module.exports = class Solutions extends Abstract { } /** - * @api {get} /kendra/api/v1/solutions/details/:solutionId Solution details + * @api {get} /kendra/api/v1/solutions/getDetails/:solutionId Solution details * @apiVersion 1.0.0 * @apiName Details of the solution. * @apiGroup Solutions * @apiHeader {String} X-authenticated-user-token Authenticity token - * @apiSampleRequest /kendra/api/v1/solutions/details/5ffbf8909259097d48017bbf + * @apiSampleRequest /kendra/api/v1/solutions/getDetails/5ffbf8909259097d48017bbf * @apiUse successBody * @apiUse errorBody * @apiParamExample {json} Response: @@ -571,7 +570,6 @@ module.exports = class Solutions extends Abstract { " Courses " ], "concepts": [], - "createdFor": [], "themes": [ { "type": "theme", @@ -600,7 +598,6 @@ module.exports = class Solutions extends Abstract { "isAPrivateProgram": false, "allowMultipleAssessemts": false, "isDeleted": false, - "rootOrganisations": [], "deleted": false, "externalId": "99199aec-66b8-11eb-b81d-a08cfd79f8b7-OBSERVATION-TEMPLATE", "name": "Enrollment challenges in DIKSHA Courses", @@ -647,17 +644,17 @@ module.exports = class Solutions extends Abstract { /** * Details of the solution. * @method - * @name details + * @name getDetails * @param {Object} req - requested data. * @param {String} req.params._id - solution id. * @returns {Object} Solution details */ - async details(req) { + async getDetails(req) { return new Promise(async (resolve, reject) => { try { - let solutionData = await solutionsHelper.details( + let solutionData = await solutionsHelper.getDetails( req.params._id ); @@ -675,67 +672,6 @@ module.exports = class Solutions extends Abstract { }); } - /** - * @api {post} /kendra/api/v1/solutions/targetedEntity/:solutionId Targeted entity in solution. - * @apiVersion 1.0.0 - * @apiName Targeted entity in solution. - * @apiGroup Solutions - * @apiParamExample {json} Request-Body: - * { - "state" : "bc75cc99-9205-463e-a722-5326857838f8", - "district" : "b54a5c6d-98be-4313-af1c-33040b1703aa", - "school" : "2a128c91-a5a2-4e25-aa21-3d9196ad8203" - } - * @apiHeader {String} X-authenticated-user-token Authenticity token - * @apiSampleRequest /kendra/api/v1/solutions/targetedEntity/600ac0d1c7de076e6f9943b9 - * @apiUse successBody - * @apiUse errorBody - * @apiParamExample {json} Response: - * { - "message": "Targeted entity in solution fetched successfully", - "status": 200, - "result": { - "_id": "5fd098e2e049735a86b748ad", - "entityType": "district", - "metaInformation": { - "name": "VIZIANAGARAM" - } - }} - */ - - /** - * Targeted entity in solution - * @method - * @name targetedEntity - * @param {Object} req - requested data. - * @param {String} req.params._id - solution id. - * @returns {Array} Details entity. - */ - - async targetedEntity(req) { - return new Promise(async (resolve, reject) => { - try { - - let detailEntity = await solutionsHelper.targetedEntity( - req.params._id, - req.body - ); - - detailEntity["result"] = detailEntity.data; - - return resolve(detailEntity); - - } catch (error) { - return reject({ - status: error.status || httpStatusCode.internal_server_error.status, - message: error.message || httpStatusCode.internal_server_error.message, - errorObject: error - }); - } - }); - } - - /** * @api {post} /kendra/api/v1/solutions/targetedSolutions?type=:solutionType&page=:page&limit=:limit&search=:search&filter=:filter * List of assigned solutions and targetted ones. @@ -744,7 +680,7 @@ module.exports = class Solutions extends Abstract { * @apiSampleRequest /kendra/api/v1/solutions/targetedSolutions?type=observation&page=1&limit=10&search=a&filter=assignedToMe * @apiParamExample {json} Request: * { - * "role" : "HM", + * "role" : "HM,DEO", "state" : "236f5cff-c9af-4366-b0b6-253a1789766a", "district" : "1dcbc362-ec4c-4559-9081-e0c2864c2931", "school" : "c5726207-4f9f-4f45-91f1-3e9e8e84d824" @@ -813,55 +749,171 @@ module.exports = class Solutions extends Abstract { }) } - /** - * @api {get} /kendra/api/v1/solutions/listByProgramId/:programId + /** + * @api {get} /kendra/api/v1/solutions/fetchLink/:solutionId * @apiVersion 1.0.0 - * @apiName List solutions by program id + * @apiName Get link by solution id * @apiGroup Solutions - * @apiSampleRequest /kendra/api/v1/solutions/listByProgramId/5fa28620b6bd9b757dc4e932 + * @apiSampleRequest /kendra/api/v1/solutions/fetchLink/5fa28620b6bd9b757dc4e932 * @apiHeader {String} X-authenticated-user-token Authenticity token * @apiUse successBody * @apiUse errorBody * @apiParamExample {json} Response: * { - "message": "Solutions lists fetched successfully", + "message": "Solution Link generated successfully", "status": 200, - "result": [ - { - "_id": "5fa28620b6bd9b757dc4e943", - "isRubricDriven": false, - "externalId": "eb670a66-1e5e-11eb-a3bf-000d3af02677-OBSERVATION-TEMPLATE-1604486688116", - "name": "स्कूल सुरक्षा चेकलिस्ट", - "description": "स्कूल सुरक्षा चेकलिस्ट", - "type": "observation", - "subType": "school" - } - ] -} + "result": "https://dev.sunbirded.org/manage-learn/create-observation/38cd93bdb87489c3890fe0ab00e7d406" + } + */ + + /** + * Get link by solution id. + * @method + * @name fetchLink + * @param {Object} req - requested data. + * @param {String} req.params._id - solution Id + * @returns {Array} + */ + + async fetchLink(req) { + return new Promise(async (resolve, reject) => { + try { + + let solutionData = await solutionsHelper.fetchLink( + req.params._id, + req.userDetails.userId + ); + + return resolve(solutionData); + + } + catch (error) { + return reject({ + status: error.status || httpStatusCode.internal_server_error.status, + message: error.message || httpStatusCode.internal_server_error.message, + errorObject: error + }) + } + }) + } + + /** + * @api {post} /kendra/api/v1/solutions/verifyLink/:link + * @apiVersion 1.0.0 + * @apiName verify Link + * @apiGroup Solutions + * @apiSampleRequest /kendra/api/v1/solutions/verifyLink/6f8d395f674dcb3146ade10f972da9d0 + * @apiHeader {String} X-authenticated-user-token Authenticity token + * @apiUse successBody + * @apiUse errorBody + * @apiParamExample {json} Request: + * { + * "role" : "HM,DEO", + "state" : "236f5cff-c9af-4366-b0b6-253a1789766a", + "district" : "1dcbc362-ec4c-4559-9081-e0c2864c2931", + "school" : "c5726207-4f9f-4f45-91f1-3e9e8e84d824" + } + * @apiParamExample {json} Response: + * { + "message": "Solution Link verified successfully", + "status": 200, + "result": { + isATargetedSolution : true/false, + type: improvementProject, + solutionId : “5f6853f293734140ccce90cf”, + projectId : “”, + obervationId: “”, + surveyId: “” + } + } */ /** - * List solutions by program id. + * verify Link * @method - * @name listByProgramId + * @name verifyLink * @param {Object} req - requested data. - * @param {String} req.params._id - program id . + * @param {String} req.params._id - solution link * @returns {Array} */ - async listByProgramId(req) { + async verifyLink(req) { + return new Promise(async (resolve, reject) => { + try { + + let solutionData = await solutionsHelper.verifyLink( + req.params._id, + req.body, + req.userDetails.userId, + req.userDetails.userToken, + req.query.hasOwnProperty("createProject") ? gen.utils.convertStringToBoolean(req.query.createProject) : true + ); + + return resolve(solutionData); + + } + catch (error) { + return reject({ + status: error.status || httpStatusCode.internal_server_error.status, + message: error.message || httpStatusCode.internal_server_error.message, + errorObject: error + }) + } + }) + } + + + + /** + * @api {post} /kendra/api/v1/solutions/verifySolution/:Id + * @apiVersion 1.0.0 + * @apiName verify Solutions targeted + * @apiGroup Solutions + * @apiSampleRequest /kendra/api/v1/solutions/verifySolution/5f6853f293734140ccce90cf + * @apiHeader {String} X-authenticated-user-token Authenticity token + * @apiUse successBody + * @apiUse errorBody + * @apiParamExample {json} Request: + * { + * "role" : "HM,DEO", + "state" : "236f5cff-c9af-4366-b0b6-253a1789766a", + "district" : "1dcbc362-ec4c-4559-9081-e0c2864c2931", + "school" : "c5726207-4f9f-4f45-91f1-3e9e8e84d824" + } + * @apiParamExample {json} Response: + * { + "message": "Solution Link verified successfully", + "status": 200, + "result": { + isATargetedSolution : true/false, + _id : “5f6853f293734140ccce90cf”, + } + } + */ + + /** + * verify Solution + * @method + * @name verifySolution + * @param {Object} req - requested data. + * @param {String} req.params._id - solution id + * @returns {Array} + */ + + async isTargetedBasedOnUserProfile(req) { return new Promise(async (resolve, reject) => { try { - - let solutionData = await solutionsHelper.listByProgramId( - req.params._id + + let solutionData = await solutionsHelper.isTargetedBasedOnUserProfile( + req.params._id, + req.body, ); - solutionData["result"] = solutionData.data; return resolve(solutionData); + } catch (error) { - reject({ + return reject({ status: error.status || httpStatusCode.internal_server_error.status, message: error.message || httpStatusCode.internal_server_error.message, errorObject: error @@ -869,5 +921,200 @@ module.exports = class Solutions extends Abstract { } }) } + + /** + * @api {post} /kendra/api/v1/solutions/details/:solutionId + * @apiVersion 1.0.0 + * @apiName Get Project Template or Solution Questions + * @apiGroup Solutions + * @apiSampleRequest /kendra/api/v1/solutions/details/5ff9d50f9259097d48017bbb + * @apiHeader {String} X-authenticated-user-token Authenticity token + * @apiUse successBody + * @apiUse errorBody + * @apiParamExample {json} Request: + * { + * "role" : "HM,DEO", + "state" : "236f5cff-c9af-4366-b0b6-253a1789766a", + "district" : "1dcbc362-ec4c-4559-9081-e0c2864c2931", + "school" : "c5726207-4f9f-4f45-91f1-3e9e8e84d824" + } + * @apiParamExample {json} Response: + * { + "message": "Successfully fetched details", + "status": 200, + "result": { + "_id": "5f97d2f6bf3a3b1c0116c80a", + "status": "published", + "isDeleted": false, + "categories": [ + { + "_id": "5f102331665bee6a740714e8", + "name": "Teachers", + "externalId": "teachers" + }, + { + "name": "newCategory", + "externalId": "", + "_id": "" + } + ], + "tasks": [ + { + "_id": "289d9558-b98f-41cf-81d3-92486f114a49", + "name": "Task 1", + "description": "Task 1 description", + "status": "notStarted", + "isACustomTask": false, + "startDate": "2020-09-29T09:08:41.667Z", + "endDate": "2020-09-29T09:08:41.667Z", + "lastModifiedAt": "2020-09-29T09:08:41.667Z", + "type": "single", + "isDeleted": false, + "attachments": [ + { + "name": "download(2).jpeg", + "type": "image/jpeg", + "sourcePath": "projectId/userId/imageName" + } + ], + "remarks": "Tasks completed", + "assignee": "Aman", + "children": [ + { + "_id": "289d9558-b98f-41cf-81d3-92486f114a50", + "name": "Task 2", + "description": "Task 2 description", + "status": "notStarted", + "children": [], + "isACustomTask": false, + "startDate": "2020-09-29T09:08:41.667Z", + "endDate": "2020-09-29T09:08:41.667Z", + "lastModifiedAt": "2020-09-29T09:08:41.667Z", + "type": "single", + "isDeleted": false, + "externalId": "task 2", + "isDeleteable": false, + "createdAt": "2020-10-28T05:58:24.907Z", + "updatedAt": "2020-10-28T05:58:24.907Z", + "isImportedFromLibrary": false + } + ], + "externalId": "task 1", + "isDeleteable": false, + "createdAt": "2020-10-28T05:58:24.907Z", + "updatedAt": "2020-10-28T05:58:24.907Z", + "isImportedFromLibrary": false + } + ], + "resources": [], + "deleted": false, + "__v": 0, + "description": "Project 1 description" + } +} + */ + + /** + * get solution details + * @method + * @name details + * @param {Object} req - requested data. + * @param {String} req.params._id - solution Id + * @returns {Array} + */ + + async details(req) { + return new Promise(async (resolve, reject) => { + try { + + let solutionData = await solutionsHelper.details( + req.params._id, + req.body, + req.userDetails.userId, + req.userDetails.userToken + ); + + return resolve(solutionData); + + } + catch (error) { + return reject({ + status: error.status || httpStatusCode.internal_server_error.status, + message: error.message || httpStatusCode.internal_server_error.message, + errorObject: error + }) + } + }) + } + + /** + * @api {get} /kendra/api/v1/solutions/read/:solutionId Read Solution Report Informations + * @apiVersion 1.0.0 + * @apiName Read Solution Report Informations + * @apiGroup Solutions + * @apiSampleRequest /kendra/api/v1/solutions/read/5ff9d50f9259097d48017bbb + * @apiHeader {String} X-authenticated-user-token Authenticity token + * @apiUse successBody + * @apiUse errorBody + * @apiParamExample {json} Response: + * { + "message": "Solution details fetched successfully", + "status": 200, + "result": { + "districts": [ + { + "name" : "ANANTAPUR", + "locationId" : "2f76dcf5-e43b-4f71-a3f2-c8f19e1fce03", + },{ + "name" : "EAST GODAVARI", + "locationId" : "aecac7ab-15e4-45c9-ac7b-d716444cd652", + } + ], + "organisations": [ + { + "orgName": "TAMILNADU", + "organisationId": "01269878797503692810" + }, + { + "orgName": "JHS NARHARPUR", + "organisationId": "0869878797503692810" + } + ] + } + } + */ + + /** + * Read Solution Report Informations. + * @method + * @name read + * @param {Object} req - requested data. + * @param {String} req.params._id - solution id. + * @returns {JSON} + */ + + // End Point is Deprecated This API is moved to Reports Services. + + + // async read(req) { + // return new Promise(async (resolve, reject) => { + // try { + + // let solutionData = await solutionsHelper.read( + // req.params._id, + // req.userDetails.userId + // ); + + // return resolve(solutionData); + // } + // catch (error) { + // reject({ + // status: error.status || httpStatusCode.internal_server_error.status, + // message: error.message || httpStatusCode.internal_server_error.message, + // errorObject: error + // }) + // } + // }) + // } } \ No newline at end of file diff --git a/controllers/v1/static-links.js b/controllers/v1/static-links.js index 8a9dd332..23ec4289 100644 --- a/controllers/v1/static-links.js +++ b/controllers/v1/static-links.js @@ -23,210 +23,4 @@ module.exports = class StaticLinks extends Abstract { return "static-links"; } - /** - * @api {get} /kendra/api/v1/static-links/list Static Link list - * @apiVersion 1.0.0 - * @apiName Static Link list - * @apiGroup Static Links - * @apiHeader {String} X-authenticated-user-token Authenticity token - * @apiSampleRequest /kendra/api/v1/static-links/list - * @apiParamExample {json} Response: - * { - * "message": "Static Links fetched successfully.", - "status": 200, - "result": [ - privacyPolicy": { - "_id": "5d259439a9bc1209d0184390", - "value": "privacyPolicy", - "link": "https://shikshalokam.org/wp-content/uploads/2019/01/data_privacy_policy.html", - "title": "Privacy Policy" - }, - "termsOfUse": { - "_id": "5d259439a9bc1209d0184391", - "value": "termsOfUse", - "link": "https://shikshalokam.org/wp-content/uploads/2019/05/Final-ShikshaLokam-Terms-of-Use-MCM-08052019-Clean-copy-1.html", - "title": "Terms of Use" - } - - ] - } - * @apiUse successBody - * @apiUse errorBody - */ - - /** - * List static links. - * @method - * @name list - * @returns {Array} List of all static links. - */ - - list(req) { - return new Promise(async (resolve, reject) => { - - try { - - let result = await staticLinksHelper.list( - req.headers.apptype, - req.headers.appname - ) - - return resolve(result); - - } catch (error) { - - return reject({ - status: error.status || httpStatusCode.internal_server_error.status, - message: error.message || httpStatusCode.internal_server_error.message, - errorObject: error - }); - - } - - - }) - } - - /** - * @api {post} /kendra/api/v1/static-links/bulkCreate Upload Static Links Information CSV - * @apiVersion 1.0.0 - * @apiName Upload Static Links Information CSV - * @apiGroup Static Links - * @apiParam {File} staticLinks Mandatory static links file of type CSV. - * @apiSampleRequest /kendra/api/v1/static-links/bulkCreate - * @apiUse successBody - * @apiUse errorBody - */ - - /** - * Create bulk static links via csv. - * @method - * @name bulkCreate - * @param {Object} req - request data. - * @param {CSV} req.files.staticLinks - static links data. - * @returns {CSV} - */ - - bulkCreate(req) { - return new Promise(async (resolve, reject) => { - - try { - - let staticLinksCSVData = await csv().fromString(req.files.staticLinks.data.toString()); - - if (!staticLinksCSVData || staticLinksCSVData.length < 1) { - throw constants.apiResponses.FILE_DATA_MISSING; - } - - let newStaticLinkData = await staticLinksHelper.bulkCreate(staticLinksCSVData, req.userDetails); - - if (newStaticLinkData.length > 0) { - const fileName = `StaticLink-Upload`; - let fileStream = new FileStream(fileName); - let input = fileStream.initStream(); - - (async function () { - await fileStream.getProcessorPromise(); - return resolve({ - isResponseAStream: true, - fileNameWithPath: fileStream.fileNameWithPath() - }); - }()); - - await Promise.all(newStaticLinkData.map(async staticLink => { - input.push(staticLink); - })) - - input.push(null); - - } else { - throw constants.apiResponses.SOMETHING_WENT_WRONG; - } - - } catch (error) { - - return reject({ - status: error.status || httpStatusCode.internal_server_error.status, - message: error.message || httpStatusCode.internal_server_error.message, - errorObject: error - }) - - } - - - }) - } - - /** - * @api {post} /kendra/api/v1/static-links/bulkUpdate Upload Static Links Information CSV - * @apiVersion 1.0.0 - * @apiName Upload Static Links Information CSV - * @apiGroup Static Links - * @apiParam {File} staticLinks Mandatory static links file of type CSV. - * @apiSampleRequest /kendra/api/v1/static-links/bulkUpdate - * @apiUse successBody - * @apiUse errorBody - */ - - /** - * Upsate bulk static links via csv. - * @method - * @name bulkUpdate - * @param {Object} req - request data. - * @param {CSV} req.files.staticLinks - static links data. - * @returns {CSV} - */ - - bulkUpdate(req) { - return new Promise(async (resolve, reject) => { - - try { - - - let staticLinksCSVData = await csv().fromString(req.files.staticLinks.data.toString()); - - if (!staticLinksCSVData || staticLinksCSVData.length < 1) { - throw messageConstants.apiResponses.FILE_DATA_MISSING; - } - - let newStaticLinkData = await staticLinksHelper.bulkUpdate(staticLinksCSVData, req.userDetails); - - if (newStaticLinkData.length > 0) { - - const fileName = `StaticLink-Upload`; - let fileStream = new FileStream(fileName); - let input = fileStream.initStream(); - - (async function () { - await fileStream.getProcessorPromise(); - return resolve({ - isResponseAStream: true, - fileNameWithPath: fileStream.fileNameWithPath() - }); - }()); - - await Promise.all(newStaticLinkData.map(async staticLink => { - input.push(staticLink); - })) - - input.push(null); - - } else { - throw constants.apiResponses.SOMETHING_WENT_WRONG; - } - - } catch (error) { - - return reject({ - status: error.status || httpStatusCode.internal_server_error.status, - message: error.message || httpStatusCode.internal_server_error.message, - errorObject: error - }) - - } - - - }) - } - } \ No newline at end of file diff --git a/controllers/v1/user-extension.js b/controllers/v1/user-extension.js index dac17579..e5295db0 100644 --- a/controllers/v1/user-extension.js +++ b/controllers/v1/user-extension.js @@ -7,7 +7,6 @@ // Dependencies const userExtensionHelper = require(ROOT_PATH+"/module/user-extension/helper"); - /** * UserExtension * @class @@ -22,82 +21,6 @@ module.exports = class UserExtension extends Abstract { return "user-extension"; } - /** - * @api {post} /kendra/api/v1/user-extension/getProfile/{{userId}} Get user profile - * @apiVersion 1.0.0 - * @apiName Get user profile - * @apiGroup User Extension - * @apiHeader {String} X-authenticated-user-token Authenticity token - * @apiSampleRequest /kendra/api/v1/user-extension/getProfile/e97b5582-471c-4649-8401-3cc4249359bb - * @apiUse successBody - * @apiUse errorBody - * @apiParamExample {json} Response: - * { - * "_id": "5d5e4758f89df53a1d26b454", - "externalId": "a1", - "roles": [ - { - "_id": "5d5e47051f5a363a0a187029", - "code": "HM", - "title": "Headmaster", - "immediateSubEntityType": "school", - "entities": [ - { - "_id": "5bfe53ea1d0c350d61b78d0f", - "externalId": "1208138", - "name": "Shri Shiv Middle School, Shiv Kutti, Teliwara, Delhi", - "childrenCount": 0, - "entityType": "school", - "entityTypeId": "5ce23d633c330302e720e65f", - "subEntityGroups": [ - "parent" - ] - } - ] - } - ] - * } - */ - - /** - * Get profile of user. - * @method - * @name getProfile - * @param {Object} req - request data. - * @param {String} req.params._id - user id. - * @returns {JSON} User profile data. - */ - - getProfile(req) { - return new Promise(async (resolve, reject) => { - - try { - - let result = await userExtensionHelper.profileWithEntityDetails({ - userId: (req.params._id && req.params._id != "") ? req.params._id : req.userDetails.userId, - status: constants.common.ACTIVE, - isDeleted: false - },req.headers.appname); - - return resolve({ - message: constants.apiResponses.USER_EXTENSION_FETCHED, - result: result - }); - - } catch (error) { - - return reject({ - status: error.status || httpStatusCode.internal_server_error.status, - message: error.message || httpStatusCode.internal_server_error.message, - errorObject: error - }) - - } - - - }) - } - /** * @api {post} /kendra/api/v1/user-extension/update/{{userId}} Update user profile * @apiVersion 1.0.0 @@ -211,276 +134,6 @@ module.exports = class UserExtension extends Abstract { }) } - - /** - * @api {post} /kendra/api/v1/user-extension/updateProfileRoles/{{userId}} Update profile roles. - * @apiVersion 1.0.0 - * @apiName Update profile roles. - * @apiGroup User Extension - * @apiHeader {String} X-authenticated-user-token Authenticity token - * @apiSampleRequest /kendra/api/v1/user-extension/updateProfileRoles/8f6d6fd2-c069-41f1-b94d-ad2befcc964b - * @apiParamExample {json} Request-body: - * { - "stateId" : "5da829874c67d63cca1bd9d0", - "roles" : [{ - "_id" : "5d7a2d266371783ceb11064e", - "entities" : [ - "5da70ff64c67d63cca1b94e0" - ] - } - ] - } - * @apiUse successBody - * @apiUse errorBody - * @apiParamExample {json} Response: - * { - "message": "User extension updated successfully", - "status": 200, - "result": { - "_id": "5f587db299333547476e8f80", - "externalId": "a1", - "state": { - "_id": "5da829874c67d63cca1bd9d0", - "name": "Punjab" - }, - "roles": [ - { - "_id": "5d7a2d266371783ceb11064e", - "code": "SPD", - "title": "State Project Director", - "immediateSubEntityType": "school", - "entities": [ - { - "_id": "5da70ff64c67d63cca1b94e0", - "externalId": "3200100201", - "name": "PUNJAB GPS ASPAL KHURD", - "childrenCount": 0, - "entityType": "school", - "entityTypeId": "5d15a959e9185967a6d5e8a6", - "subEntityGroups": [], - "relatedEntities": [ - { - "_id": "5da829874c67d63cca1bd9d0", - "entityTypeId": "5d7a290e6371783ceb11064c", - "entityType": "state", - "metaInformation": { - "externalId": "PBS", - "name": "Punjab" - } - }, - { - "_id": "5da829a94c67d63cca1bd9d2", - "entityTypeId": "5d15a959e9185967a6d5e8ac", - "entityType": "district", - "metaInformation": { - "externalId": "BARNALA -3", - "name": "BARNALA" - } - }, - { - "_id": "5da829f64c67d63cca1bd9f1", - "entityTypeId": "5d15a959e9185967a6d5e8ab", - "entityType": "block", - "metaInformation": { - "externalId": "PJ-BARNALA", - "name": "BARNALA" - } - }, - { - "_id": "5da82a1c4c67d63cca1bdae8", - "entityTypeId": "5d15c4ec03cbf959ccabdd2b", - "entityType": "cluster", - "metaInformation": { - "externalId": "PJ-GPS KALEKE", - "name": "GPS KALEKE", - "city": "" - } - } - ] - } - ] - } - ] - } -} - */ - - /** - * Update profile roles. - * @method - * @name updateProfileRoles - * @param {Object} req - request data. - * @param {String} req.params._id - user id. - * @returns {JSON} Update profile roles. - */ - - updateProfileRoles(req) { - return new Promise(async (resolve, reject) => { - - try { - - const userExtensionData = - await userExtensionHelper.updateProfileRoles( - req.body, - req.params._id ? req.params._id : req.userDetails.userId, - req.userDetails.userName - ); - - return resolve(userExtensionData); - - } catch (error) { - - return reject({ - status: error.status || httpStatusCode.internal_server_error.status, - message: error.message || httpStatusCode.internal_server_error.message, - errorObject: error - }) - - } - - - }) - } - - - /** - * @api {get} /kendra/api/v1/user-extension/getEntities?entityType=school&page=1&limi=10&search=govt Get user entities. - * @apiVersion 1.0.0 - * @apiName Get user entities. - * @apiGroup User Extension - * @apiHeader {String} X-authenticated-user-token Authenticity token - * @apiSampleRequest /kendra/api/v1/user-extension/getEntities/?entityType=school&page=1&limi=10&search=govt - * @apiUse successBody - * @apiUse errorBody - * @apiParamExample {json} Response: - { - "message": "User entities fetched successfully.", - "status": 200, - "result": { - "data": [ - { - "_id": "5beaa888af0065f0e0a10515", - "name": "Apple School" - }, - { - "_id": "5bfe53ea1d0c350d61b78d0a", - "name": "Sachdeva Convent School, Street No.-5 Sangam Vihar (Wazirabad - Jagatpur Road), Delhi" - }, - { - "_id": "5bfe53ea1d0c350d61b78d0c", - "name": "Divyansh Public School, Kh.No.28/20 & 29/16/02 Sangam Vihar, Burari, Delhi" - }, - { - "_id": "5de6322c187dca40bbbd0aef", - "name": "gsspachelkalan" - } - ], - "count":4 -} -**/ - - /** - * Get user entities - * @method - * @name getEntities - * @param {Object} req - request data. - * @param {String} req.params._id - user id. - * @param {string} req.query.entityType - entity type - * @returns {JSON} User profile data. - */ - - getEntities(req) { - return new Promise(async (resolve, reject) => { - - try { - - let result = await userExtensionHelper.getEntities( - (req.params._id && req.params._id != "") ? req.params._id : req.userDetails.userId , - req.query.entityType ? req.query.entityType : "", - req.pageSize, - req.pageNo, - req.searchText - ); - - return resolve({ - message: constants.apiResponses.USER_ENTITIES_FOUND, - result: result - }); - - } catch (error) { - - return reject({ - status: error.status || httpStatusCode.internal_server_error.status, - message: error.message || httpStatusCode.internal_server_error.message, - errorObject: error - }) - - } - - - }) - } - - /** - * @api {get} /kendra/api/v1/user-extension/getEntityTypes Get user entity types. - * @apiVersion 1.0.0 - * @apiName Get user entity types - * @apiGroup User Extension - * @apiHeader {String} X-authenticated-user-token Authenticity token - * @apiSampleRequest /kendra/api/v1/user-extension/getEntityTypes - * @apiUse successBody - * @apiUse errorBody - * @apiParamExample {json} Response: - { - "message": "User entity types fetched successfully.", - "status": 200, - "result": [ - "school", - "smc", - "district" - ] -} -**/ - - - /** - * Get user entity types. - * @method - * @name getEntityTypes - * @param {Object} req - request data. - * @param {String} req.params._id - user id. - * @returns {JSON} User profile data. - */ - - getEntityTypes(req) { - return new Promise(async (resolve, reject) => { - - try { - - let result = await userExtensionHelper.getEntityTypes({ - userId: (req.params._id && req.params._id != "") ? req.params._id : req.userDetails.userId, - status: constants.common.ACTIVE, - isDeleted: false, - }); - - return resolve({ - message: constants.apiResponses.USER_ENTITY_TYPE_FOUND, - result: result - }); - - } catch (error) { - - return reject({ - status: error.status || httpStatusCode.internal_server_error.status, - message: error.message || httpStatusCode.internal_server_error.message, - errorObject: error - }) - - } - - - }) - } /** * @api {get} /kendra/api/v1/user-extension/programsByPlatformRoles?role=:role1,role2 List of programs for platform user @@ -610,4 +263,69 @@ module.exports = class UserExtension extends Abstract { }) } + + + /** + * @api {get} /kendra/api/v1/user-extension/read + * @apiVersion 1.0.0 + * @apiName user Extension List + * @apiGroup User Extension + * @apiHeader {String} X-authenticated-user-token Authenticity token + * @apiSampleRequest /kendra/api/v1/user-extension/read + * @apiUse successBody + * @apiUse errorBody + * @apiParamExample {json} Response: + * { + "status":200, + "message":"User extension fetched successfully", + "success":true, + "result":{ + "_id":"60a777528b5424712b5faec3", + "platformRoles":[ + { + "programs":[ + "5f34ec17585244939f89f90c", + "5f34e44681871d939950bca6" + ] + }, + { + "programs":[ + "5f34ec17585244939f89f90c", + "5f75b90454670074deacf087" + ] + }, + { + "programs":[ + "5f34ec17585244939f89f90c", + "5f75b90454670074deacf087" + ] + } + ] + } +} +**/ + read(req){ + return new Promise(async (resolve, reject) => { + let userInformation = await userExtensionHelper.userExtensionDocument( { + userId: req.userDetails.userId, + status: constants.common.ACTIVE, + isDeleted: false + }, { _id: 1, "platformRoles.programs" :1,"platformRoles.code":1}); + + if ( !userInformation ) { + return resolve({ + status: httpStatusCode.bad_request.status, + message: constants.apiResponses.NOT_AUTHORIZED_TO_ACCESS + }) + }else { + return resolve({ + status: httpStatusCode.ok.status, + message: constants.apiResponses.USER_EXTENSION_FTECHED, + success: true, + result: userInformation + }) + } + }); + } + }; diff --git a/controllers/v1/user-profile.js b/controllers/v1/user-profile.js index 8bf29d1a..acabd60a 100644 --- a/controllers/v1/user-profile.js +++ b/controllers/v1/user-profile.js @@ -23,199 +23,5 @@ module.exports = class UserProfile extends Abstract { return "user-profile"; } - /** - * @api {get} /kendra/api/v1/user-profile/getForm - * getForm return user profile form - * @apiVersion 1.0.0 - * @apiGroup user profile - * @apiHeader {String} X-authenticated-user-token Authenticity token - * @apiSampleRequest /kendra/api/v1/user-profile/getForm - * @apiUse successBody - * @apiUse errorBody - * @apiParamExample {json} Response: - * { - "message": "User Profile form fetched successfully ", - "status": 200, - "result": { - "form": [ - { - "label": "firstName", - "field": "firstName", - "value": "", - "visible": true, - "editable": true, - "validation": { - "required": true, - "regex": "/^[A-Za-z]+$/" - }, - "input": "text" - }, - { - "label": "lastName", - "field": "lastName", - "value": "", - "visible": true, - "editable": true, - "validation": { - "required": true, - "regex": "/^[A-Za-z]+$/" - }, - "input": "text" - }, - { - "label": "email", - "field": "email", - "value": "", - "visible": true, - "editable": true, - "validation": { - "required": true, - "regex": "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$" - }, - "input": "text" - }, - { - "label": "phoneNumber", - "field": "phoneNumber", - "value": "", - "visible": true, - "editable": true, - "validation": { - "required": true, - "regex": "^((+)?(d{2}[-]))?(d{10}){1}?$" - }, - "input": "text" - }, - { - "label": "state", - "field": "state", - "value": "", - "visible": true, - "editable": true, - "validation": { - "required": true, - "regex": "" - }, - "input": "select", - "options": [ - { - "label": "", - "value": "" - } - ] - } - ], - "statesWithSubEntities": { - "5da829874c67d63cca1bd9d0": [ - "district", - "block", - "cluster", - "school" - ] - } - } - } - */ - - getForm(req) { - return new Promise(async (resolve, reject) => { - - try { - - let userProfileForm = await userProfileHelper.getForm( - req.userDetails, - req.headers['appname'], - req.headers['os'] - ); - - resolve(userProfileForm); - - } catch (error) { - - return reject({ - status: - error.status || - httpStatusCode["internal_server_error"].status, - - message: - error.message || - httpStatusCode["internal_server_error"].message - }); - } - }); - } - - /** - * @api {post} /kendra/api/v1/user-profile/save - * save's the user profile data - * @apiVersion 1.0.0 - * @apiGroup user profile - * @apiHeader {String} X-authenticated-user-token Authenticity token - * @apiSampleRequest /kendra/api/v1/user-profile/save - * @apiUse successBody - * @apiUse errorBody - * @apiParamExample {json} Request: - * { - * "data":{ - * "firstName": "", - * "lastName": "", - * "email": "", - * "phoneNumber": "", - * "district":[{ - * "label":"name", - * "value":"id" - * }] - * } - * @apiParamExample {json} Response: - * { - "message": "User profile Saved successfully", - "status": 200, - "result": { - "data" : { - "firstName" : "A", - "lastName" : "B", - "email" : "a@b.com", - "phoneNumber" : "9591553529", - "district" : [ - { - "label": "name", - "value": "id" - } - ] - } - } - } -} - */ - - save(req) { - return new Promise(async (resolve, reject) => { - - try { - - let userProfileSave = - await userProfileHelper.save( - req.body.data, - req.userDetails.userId, - req.userDetails.userName ? req.userDetails.userName : "" - ); - - resolve( userProfileSave); - - } catch (error) { - - return reject({ - status: - error.status || - httpStatusCode["internal_server_error"].status, - - message: - error.message || - httpStatusCode["internal_server_error"].message - }); - } - }); - } - }; diff --git a/controllers/v1/users.js b/controllers/v1/users.js index 0295b129..bb1d907c 100644 --- a/controllers/v1/users.js +++ b/controllers/v1/users.js @@ -40,114 +40,6 @@ module.exports = class Users extends Abstract { return "users"; } - /** - * @api {post} /kendra/api/v1/users/create - * create user - * @apiVersion 1.0.0 - * @apiGroup Users - * @apiHeader {String} X-authenticated-user-token Authenticity token - * @apiSampleRequest /kendra/api/v1/users/create - * @apiUse successBody - * @apiUse errorBody - * @apiParamExample {json} Request: - * { - "userName": "a", - "email": "a@a.com", - "role": "SYS_ADMIN", - "createdBy": "SYSTEM", - } - - */ - - /** - * Check whether the email id provided is sys admin or not. - * @method - * @name create - * @param {Request} req request body. - * @returns {JSON} Returns success as true or false. - */ - - create(req) { - - return new Promise(async (resolve, reject) => { - - try { - - let systemAdminDocument = - await usersHelper.create(req.body); - - return resolve(systemAdminDocument); - - } catch (error) { - - return reject({ - status: - error.status || - httpStatusCode["internal_server_error"].status, - - message: - error.message || - httpStatusCode["internal_server_error"].message - }) - - } - - - }) - } - - /** - * @api {post} /kendra/api/v1/users/isSystemAdmin - * check if user is system admin or not. - * @apiVersion 1.0.0 - * @apiGroup Users - * @apiHeader {String} X-authenticated-user-token Authenticity token - * @apiSampleRequest /kendra/api/v1/users/isSystemAdmin - * @apiUse successBody - * @apiUse errorBody - * @apiParamExample {json} Request: - * { - * "email":"a@gmail.com" - } - */ - - /** - * Check whether the email id provided is sys admin or not. - * @method - * @name isSystemAdmin - * @param {Request} req request body. - * @returns {JSON} Returns success as true or false. - */ - - isSystemAdmin(req) { - - return new Promise(async (resolve, reject) => { - - try { - - let systemAdminDocument = - await usersHelper.isSystemAdmin(req.body.email); - - return resolve(systemAdminDocument); - - } catch (error) { - - return reject({ - status: - error.status || - httpStatusCode["internal_server_error"].status, - - message: - error.message || - httpStatusCode["internal_server_error"].message - }) - - } - - - }) - } - /** * @api {get} /kendra/api/v1/users/privatePrograms/:userId List of user private programs * @apiVersion 2.0.0 @@ -233,10 +125,8 @@ module.exports = class Users extends Abstract { "keywords 2" ], "concepts": [], - "createdFor": [], "components": [], "isAPrivateProgram": true, - "rootOrganisations": [], "_id": "5f44b08cdbe917732246149f", "deleted": false, "externalId": "Test project program-1598337164794", @@ -255,7 +145,6 @@ module.exports = class Users extends Abstract { "language": [], "keywords": [], "concepts": [], - "createdFor": [], "themes": [], "flattenedThemes": [], "entities": [], @@ -266,7 +155,6 @@ module.exports = class Users extends Abstract { "isAPrivateProgram": false, "allowMultipleAssessemts": false, "isDeleted": false, - "rootOrganisations": [], "_id": "5f44b08cdbe91773224614a0", "deleted": false, "name": "Test project solution", @@ -301,9 +189,10 @@ module.exports = class Users extends Abstract { await usersHelper.createProgramAndSolution( (req.params._id && req.params._id != "") ? req.params._id : - req.userDetails.id, + req.userDetails.userId, req.body, - req.userDetails.userToken + req.userDetails.userToken, + req.query.createADuplicateSolution ? req.query.createADuplicateSolution : "" ); return resolve(createdProgramAndSolution); @@ -318,149 +207,6 @@ module.exports = class Users extends Abstract { }); } - /** - * @api {get} /kendra/api/v1/users/entitiesMappingForm/:stateId?roleId=:roleId - * Entities mapping form. - * @apiVersion 1.0.0 - * @apiGroup Users - * @apiHeader {String} X-authenticated-user-token Authenticity token - * @apiSampleRequest /kendra/api/v1/users/entitiesMappingForm/5da829874c67d63cca1bd9d0?roleId=5d6e521066a9a45df3aa891e - * @apiUse successBody - * @apiUse errorBody - * @apiParamExample {json} Response: - * { - "message": "Entities mapping form fetched successfully", - "status": 200, - "result": [ - { - "field": "district", - "label": "Select District", - "value": "", - "visible": true, - "editable": true, - "input": "text", - "validation": { - "required": false - } - }, - { - "field": "block", - "label": "Select Block", - "value": "", - "visible": true, - "editable": true, - "input": "text", - "validation": { - "required": true - } - }]} - */ - - /** - * Lists of entities mapping form - * @method - * @name entitiesMappingForm - * @param {Request} req request body. - * @returns {JSON} List of entiites mapping form. - */ - - entitiesMappingForm(req) { - - return new Promise(async (resolve, reject) => { - - try { - - const entitiesMappingData = - await usersHelper.entitiesMappingForm( - req.params._id, - req.query.roleId - ); - - resolve(entitiesMappingData); - - } catch (error) { - - return reject({ - status: - error.status || - httpStatusCode["internal_server_error"].status, - - message: - error.message || - httpStatusCode["internal_server_error"].message - }) - - } - - - }) - } - - /** - * @api {get} /kendra/api/v1/users/getUserOrganisationsAndRootOrganisations - * Get organisation and root organisation - * @apiVersion 1.0.0 - * @apiGroup Users - * @apiHeader {String} X-authenticated-user-token Authenticity token - * @apiSampleRequest /kendra/api/v1/users/getUserOrganisationsAndRootOrganisations - * @apiUse successBody - * @apiUse errorBody - * @apiParamExample {json} Response: - * { - "message": "User organisations fetched successfully", - "status": 200, - "result": { - "createdFor": [ - "01305447637218918413" - ], - "rootOrganisations": [ - "01305447637218918413" - ] - } - } - */ - - /** - * Organisations and root organisations. - * @method - * @name getUserOrganisationsAndRootOrganisations - * @param {Request} req request body. - * @returns {JSON} Organisations and root organisations of user. - */ - - getUserOrganisationsAndRootOrganisations(req) { - - return new Promise(async (resolve, reject) => { - - try { - - const userOrganisations = - await usersHelper.getUserOrganisationsAndRootOrganisations( - (req.params._id && req.params._id != "") ? - req.params._id : - req.userDetails.id, - req.userDetails.userToken - ); - - resolve(userOrganisations); - - } catch (error) { - - return reject({ - status: - error.status || - httpStatusCode["internal_server_error"].status, - - message: - error.message || - httpStatusCode["internal_server_error"].message - }) - - } - - - }) - } /** * @api {post} /kendra/api/v1/users/solutions/:programId?page=:page&limit=:limit&search=:searchText @@ -472,7 +218,7 @@ module.exports = class Users extends Abstract { * @apiSampleRequest /kendra/api/v1/users/solutions/5ff438b04698083dbfab7284?page=1&limit=10 * @apiParamExample {json} Request-Body: * { - "role" : "HM", + "role" : "HM,DEO", "state" : "236f5cff-c9af-4366-b0b6-253a1789766a", "district" : "1dcbc362-ec4c-4559-9081-e0c2864c2931", "school" : "c5726207-4f9f-4f45-91f1-3e9e8e84d824" @@ -509,7 +255,8 @@ module.exports = class Users extends Abstract { "count": 3, "programName": "TEST_SCOPE_PROGRAM", "programId": "5ff438b04698083dbfab7284", - "description": "View and participate in educational programs active in your location and designed for your role." + "description": "View and participate in educational programs active in your location and designed for your role.", + "programEndDate": "2023-04-06T09:35:00.000Z" }} **/ @@ -524,14 +271,15 @@ module.exports = class Users extends Abstract { solutions(req) { return new Promise(async (resolve, reject) => { try { - + let targetedSolutions = await usersHelper.solutions( req.params._id, req.body, req.pageSize, req.pageNo, req.searchText, - req.userDetails.userToken + req.userDetails.userToken, + req.userDetails.userId ); targetedSolutions["result"] = targetedSolutions.data; @@ -564,7 +312,7 @@ module.exports = class Users extends Abstract { * @apiUse errorBody * @apiParamExample {json} Request: * { - "role" : "HM", + "role" : "HM,DEO", "state" : "236f5cff-c9af-4366-b0b6-253a1789766a", "district" : "1dcbc362-ec4c-4559-9081-e0c2864c2931", "school" : "c5726207-4f9f-4f45-91f1-3e9e8e84d824" @@ -619,7 +367,8 @@ module.exports = class Users extends Abstract { req.body, req.pageNo, req.pageSize, - req.searchText + req.searchText, + req.userDetails.userId ); programs.result = programs.data; @@ -677,15 +426,28 @@ module.exports = class Users extends Abstract { return new Promise(async (resolve, reject) => { try { + + let currentMaximumCountOfRequiredEntities = 0; + let requiredEntities = new Array; + let roleArray = req.query.role.split(","); + + // Calculate required entities for each of the role and send the output of the role which has maximum length. + for (let roleCount = 0; roleCount < roleArray.length; roleCount++) { + const eachRole = roleArray[roleCount]; + const entitiesMappingData = + await usersHelper.entityTypesByLocationAndRole( + req.params._id, + eachRole + ); + if(entitiesMappingData.data && entitiesMappingData.data.length > currentMaximumCountOfRequiredEntities) { + currentMaximumCountOfRequiredEntities = entitiesMappingData.data.length; + requiredEntities = entitiesMappingData; + requiredEntities.result = entitiesMappingData.data; + } + } - const entitiesMappingData = - await usersHelper.entityTypesByLocationAndRole( - req.params._id, - req.query.role - ); - - entitiesMappingData["result"] = entitiesMappingData.data; - resolve(entitiesMappingData); + // entitiesMappingData["result"] = requiredEntities; + resolve(requiredEntities); } catch (error) { @@ -715,7 +477,7 @@ module.exports = class Users extends Abstract { "state" : "bc75cc99-9205-463e-a722-5326857838f8", "district" : "b54a5c6d-98be-4313-af1c-33040b1703aa", "school" : "2a128c91-a5a2-4e25-aa21-3d9196ad8203", - "role" : "DEO" + "role" : "DEO,HM" } * @apiHeader {String} X-authenticated-user-token Authenticity token * @apiSampleRequest /kendra/api/v1/users/targetedEntity/601d41607d4c835cf8b724ad @@ -744,15 +506,73 @@ module.exports = class Users extends Abstract { async targetedEntity(req) { return new Promise(async (resolve, reject) => { try { + + let roleArray = req.body.role.split(","); + let targetedEntities = {}; + + if ( roleArray.length === 1 ) { + + const detailEntity = + await usersHelper.targetedEntity( + req.params._id, + req.body + ); + + detailEntity["result"] = detailEntity.data; - let detailEntity = await usersHelper.targetedEntity( - req.params._id, - req.body - ); - - detailEntity["result"] = detailEntity.data; - - return resolve(detailEntity); + return resolve(detailEntity); + + } else { + + let roleWiseTargetedEntities = new Array(); + + for ( let roleCount = 0; roleCount < roleArray.length; roleCount++ ) { + + const eachRole = roleArray[roleCount]; + let bodyData = _.omit(req.body, ['role']); + bodyData.role = eachRole; + + const detailEntity = + await usersHelper.targetedEntity( + req.params._id, + bodyData + ); + + if ( detailEntity.data && Object.keys(detailEntity.data).length > 0 ) { + roleWiseTargetedEntities.push(detailEntity.data); + } + } + //no targeted entity + if ( roleWiseTargetedEntities.length == 0 ) { + throw { + status: httpStatusCode["bad_request"].status, + message: constants.apiResponses.ENTITIES_NOT_ALLOWED_IN_ROLE + }; + } + //one targeted entity + else if ( roleWiseTargetedEntities && roleWiseTargetedEntities.length == 1 ) { + + targetedEntities.result = roleWiseTargetedEntities[0]; + + } + // multiple targeted entity + else if ( roleWiseTargetedEntities && roleWiseTargetedEntities.length > 1 ) { + // request body contain role and entity information + let targetedEntity = await usersHelper.getHighestTargetedEntity( + roleWiseTargetedEntities, req.body + ); + + if ( !targetedEntity.data ) { + throw { + status: httpStatusCode["bad_request"].status, + message: constants.apiResponses.ENTITIES_NOT_ALLOWED_IN_ROLE + }; + } + targetedEntities.result = targetedEntity.data; + } + } + + return resolve(targetedEntities); } catch (error) { return reject({ diff --git a/envVariables.js b/envVariables.js index 251f13d5..10e78659 100644 --- a/envVariables.js +++ b/envVariables.js @@ -29,6 +29,7 @@ let enviromentVariables = { "optional" : true, "requiredIf" : { "key": "CLOUD_STORAGE", + "operator": "EQUALS", "value" : "GC" } }, @@ -37,6 +38,7 @@ let enviromentVariables = { "optional" : true, "requiredIf" : { "key": "CLOUD_STORAGE", + "operator": "EQUALS", "value" : "GC" } }, @@ -45,6 +47,7 @@ let enviromentVariables = { "optional" : true, "requiredIf" : { "key": "CLOUD_STORAGE", + "operator": "EQUALS", "value" : "AZURE" } }, @@ -53,6 +56,7 @@ let enviromentVariables = { "optional" : true, "requiredIf" : { "key": "CLOUD_STORAGE", + "operator": "EQUALS", "value" : "AZURE" } }, @@ -61,6 +65,7 @@ let enviromentVariables = { "optional" : true, "requiredIf" : { "key": "CLOUD_STORAGE", + "operator": "EQUALS", "value" : "AZURE" } }, @@ -69,6 +74,7 @@ let enviromentVariables = { "optional" : true, "requiredIf" : { "key": "CLOUD_STORAGE", + "operator": "EQUALS", "value" : "AWS" } }, @@ -77,6 +83,7 @@ let enviromentVariables = { "optional" : true, "requiredIf" : { "key": "CLOUD_STORAGE", + "operator": "EQUALS", "value" : "AWS" } }, @@ -85,6 +92,7 @@ let enviromentVariables = { "optional" : true, "requiredIf" : { "key": "CLOUD_STORAGE", + "operator": "EQUALS", "value" : "AWS" } }, @@ -93,6 +101,7 @@ let enviromentVariables = { "optional" : true, "requiredIf" : { "key": "CLOUD_STORAGE", + "operator": "EQUALS", "value" : "AWS" } }, @@ -101,6 +110,7 @@ let enviromentVariables = { "optional" : true, "requiredIf" : { "key": "CLOUD_STORAGE", + "operator": "EQUALS", "value" : "AWS" } }, @@ -116,20 +126,89 @@ let enviromentVariables = { "message" : "Required project service API endpoint", "optional" : false }, - "ELASTICSEARCH_COMMUNICATIONS_ON_OFF" : { - "message" : "Enable/Disable elastic search communications", + "USER_SERVICE_URL" : { + "message" : "Sunbird environment base url", "optional" : false }, - "ELASTICSEARCH_HOST_URL" : { - "message" : "Elastic search host url", + "CSV_REPORTS_PATH" : { + "message" : "CSV Report Path", + "optional" : true, + "default": "public/reports" + }, + "APP_PORTAL_BASE_URL" : { + "message" : "App Portal base url", + "optional" : true, + "default": "https://dev.sunbirded.org" + }, + "FORM_SERVICE_URL" : { + "message" : "Form service base url", + "optional" : true, + "default": "http://player:3000" + }, + "OCI_ACCESS_KEY_ID" : { + "message" : "Required oracle access key id", + "optional" : true, + "requiredIf" : { + "key": "CLOUD_STORAGE", + "operator": "EQUALS", + "value" : "OCI" + } + }, + "OCI_SECRET_ACCESS_KEY" : { + "message" : "Required Oracle secret access key", + "optional" : true, + "requiredIf" : { + "key": "CLOUD_STORAGE", + "operator": "EQUALS", + "value" : "OCI" + } + }, + "OCI_BUCKET_NAME" : { + "message" : "Required Oracle bucket name", + "optional" : true, + "requiredIf" : { + "key": "CLOUD_STORAGE", + "operator": "EQUALS", + "value" : "OCI" + } + }, + "OCI_BUCKET_REGION" : { + "message" : "Required Oracle bucket region", + "optional" : true, + "requiredIf" : { + "key": "CLOUD_STORAGE", + "operator": "EQUALS", + "value" : "OCI" + } + }, + "OCI_BUCKET_ENDPOINT" : { + "message" : "Required Oracle bucket endpoint", + "optional" : true, + "requiredIf" : { + "key": "CLOUD_STORAGE", + "operator": "EQUALS", + "value" : "OCI" + } + }, + "KAFKA_COMMUNICATIONS_ON_OFF" : { + "message" : "Enable/Disable kafka communications", "optional" : false }, - "ELASTICSEARCH_ENTITIES_INDEX" : { - "message" : "Elastic search entities index", + "KAFKA_URL" : { + "message" : "Required Kafka Url", + "optional" : true, + "requiredIf" : { + "key": "KAFKA_COMMUNICATIONS_ON_OFF", + "operator": "EQUALS", + "value" : "ON" + } + }, + "KAFKA_GROUP_ID" : { + "message" : "Required kafka group id", "optional" : false }, - "USER_SERVICE_URL" : { - "message" : "Sunbird environment base url", + "PROGRAM_USERS_JOINED_TOPIC" : { + "message" : "OFF/TOPIC_NAME", "optional" : false } } @@ -139,52 +218,67 @@ let success = true; module.exports = function() { Object.keys(enviromentVariables).forEach(eachEnvironmentVariable=>{ - let tableObj = { [eachEnvironmentVariable] : "PASSED" }; + let tableObj = { + [eachEnvironmentVariable] : "PASSED" + }; + + let keyCheckPass = true; + let validRequiredIfOperators = ["EQUALS","NOT_EQUALS"] - if( - enviromentVariables[eachEnvironmentVariable].requiredIf - && process.env[enviromentVariables[eachEnvironmentVariable].requiredIf.key] - && process.env[enviromentVariables[eachEnvironmentVariable].requiredIf.key] === enviromentVariables[eachEnvironmentVariable].requiredIf.value - ) { - enviromentVariables[eachEnvironmentVariable].optional = false; + if(enviromentVariables[eachEnvironmentVariable].optional === true + && enviromentVariables[eachEnvironmentVariable].requiredIf + && enviromentVariables[eachEnvironmentVariable].requiredIf.key + && enviromentVariables[eachEnvironmentVariable].requiredIf.key != "" + && enviromentVariables[eachEnvironmentVariable].requiredIf.operator + && validRequiredIfOperators.includes(enviromentVariables[eachEnvironmentVariable].requiredIf.operator) + && enviromentVariables[eachEnvironmentVariable].requiredIf.value + && enviromentVariables[eachEnvironmentVariable].requiredIf.value != "") { + switch (enviromentVariables[eachEnvironmentVariable].requiredIf.operator) { + case "EQUALS": + if(process.env[enviromentVariables[eachEnvironmentVariable].requiredIf.key] === enviromentVariables[eachEnvironmentVariable].requiredIf.value) { + enviromentVariables[eachEnvironmentVariable].optional = false; + } + break; + case "NOT_EQUALS": + if(process.env[enviromentVariables[eachEnvironmentVariable].requiredIf.key] != enviromentVariables[eachEnvironmentVariable].requiredIf.value) { + enviromentVariables[eachEnvironmentVariable].optional = false; + } + break; + default: + break; + } } - - if( - !process.env[eachEnvironmentVariable] && - !(enviromentVariables[eachEnvironmentVariable].optional) - ) { - success = false; - - if( - enviromentVariables[eachEnvironmentVariable].default && - enviromentVariables[eachEnvironmentVariable].default != "" - ) { - process.env[eachEnvironmentVariable] = - enviromentVariables[eachEnvironmentVariable].default; + if(enviromentVariables[eachEnvironmentVariable].optional === false) { + if(!(process.env[eachEnvironmentVariable]) + || process.env[eachEnvironmentVariable] == "") { + success = false; + keyCheckPass = false; + } else if (enviromentVariables[eachEnvironmentVariable].possibleValues + && Array.isArray(enviromentVariables[eachEnvironmentVariable].possibleValues) + && enviromentVariables[eachEnvironmentVariable].possibleValues.length > 0) { + if(!enviromentVariables[eachEnvironmentVariable].possibleValues.includes(process.env[eachEnvironmentVariable])) { + success = false; + keyCheckPass = false; + enviromentVariables[eachEnvironmentVariable].message += ` Valid values - ${enviromentVariables[eachEnvironmentVariable].possibleValues.join(", ")}` + } } + } + + if((!(process.env[eachEnvironmentVariable]) + || process.env[eachEnvironmentVariable] == "") + && enviromentVariables[eachEnvironmentVariable].default + && enviromentVariables[eachEnvironmentVariable].default != "") { + process.env[eachEnvironmentVariable] = enviromentVariables[eachEnvironmentVariable].default; + } - if( - enviromentVariables[eachEnvironmentVariable] && - enviromentVariables[eachEnvironmentVariable].message !== "" - ) { + if(!keyCheckPass) { + if(enviromentVariables[eachEnvironmentVariable].message !== "") { tableObj[eachEnvironmentVariable] = enviromentVariables[eachEnvironmentVariable].message; } else { - tableObj[eachEnvironmentVariable] = "required"; - } - - } else { - - tableObj[eachEnvironmentVariable] = "Passed"; - - if( - enviromentVariables[eachEnvironmentVariable].possibleValues && - !enviromentVariables[eachEnvironmentVariable].possibleValues.includes(process.env[eachEnvironmentVariable]) - ) { - tableObj[eachEnvironmentVariable] = ` Valid values - ${enviromentVariables[eachEnvironmentVariable].possibleValues.join(", ")}`; + tableObj[eachEnvironmentVariable] = `FAILED - ${eachEnvironmentVariable} is required`; } - } tableData.push(tableObj); diff --git a/generics/constants/api-responses.js b/generics/constants/api-responses.js index ba4eb2bd..36dccbfb 100644 --- a/generics/constants/api-responses.js +++ b/generics/constants/api-responses.js @@ -130,7 +130,6 @@ module.exports = { "ERROR_CREATING_PROGRAM" : "Error creating program", "PROGRAM_NOT_FOUND" : "Program not found" , "PROGRAM_EXIST" : "Program exist", - "PROGRAM_DESCRIPTION" : "Program description", "USER_TARGETED_PROGRAMS_FETCHED" : "Users programs fetched successfully", "USER_TARGETED_SOLUTIONS_FETCHED" : "Users solutions fetched successfully", "ROLE_REQUIRED_IN_SCOPE" : "Required role in scope", @@ -148,7 +147,7 @@ module.exports = { "TARGETED_SOLUTIONS_FETCHED": "Successfully targeted solutions fetched", "NO_ENTITY_FOUND_IN_LOCATION": "Entity not found in location", "TARGETED_PROGRAMS_FETCHED": "Targeted programs fetched successfully", - "PROGRAM_DESCRIPTION": "View and participate in educational programs active in your location and designed for your role", + "PROGRAM_DESCRIPTION": "View and participate in educational programs active in your location and designed for the role you selected", "ENTITIES_NOT_EXIST_IN_LOCATION" : "Entities does not belongs in the location", "ROLES_ADDED_IN_PROGRAM" : "Successfully added roles in program scope", "ENTITIES_ADDED_IN_PROGRAM" : "Successfully added entities in program scope", @@ -167,4 +166,36 @@ module.exports = { "USER_PLATFORM_ROLES": "List of user platform roles" , "USER_PLATFORM_ROLE_NOT_FOUND": "Not found user platform role", "PLATFORM_USER_PROGRAMS": "List of programs for platform user", + "ENTITY_TYPE_REQUIRED": "required entity type", + "COURSE_LINK_REQUIRED" : "Course link is required", + "LINK_GENERATED": "Link generated successfully", + "LINK_REQUIRED_CHECK": "Link is required", + "REQUIRED_USER_AUTH_TOKEN": "User auth token is required", + "INVALID_LINK": "The Link is not valid", + "LINK_IS_EXPIRED": "Link is expired", + "LINK_VERIFIED": "Link is verified successfully", + "SOLUTION_DETAILS_VERIFIED": "Solution verified successfully", + "SOLUTION_NOT_FOUND_OR_NOT_A_TARGETED": "Solution is not targeted to the role", + "PROJECT_TEMPLATE_ID_NOT_FOUND": "Project templates not exists in solution", + "SOLUTION_TARGETED_ENTITY" : "Targeted Entity Types Fetched", + "ENTITY_ID_OR_LOCATION_ID_NOT_FOUND" : "Entity id or location id not found", + "NOT_AUTHORIZED_TO_ACCESS" :"You are not authorised to access this resource", + "NO_LOCATION_ID_FOUND_IN_DATA" : "Location Id not found in data", + "DATA_FETCHED_SUCCESSFULLY" : "Data fetched successfully", + "DATA_DELETED_SUCCESSFULLY" : "Data deleted successfully", + "DATA_UPDATED_SUCCESSFULLY" : "Data updated successfully", + "DATA_CREATED_SUCCESSFULLY" : "Data created successfully", + "FAILED_TO_CREATE_RECORD" : "Failed to create record in DB", + "CERTIFICATE_TEMPLATE_ADDED" : "Template added successfully", + "CERTIFICATE_TEMPLATE_UPDATED" : "Template updated successfully", + "CERTIFICATE_TEMPLATE_NOT_UPDATED" : "Template updation failed", + "BASE_CERTIFICATE_TEMPLATE_NOT_FOUND" : "Base certificate template not found", + "CERTIFICATE_BASE_TEMPLATE_UPDATED" : "Base template updated successfully", + "CERTIFICATE_BASE_TEMPLATE_NOT_UPDATED" : "Base template updation failed", + "PROGRAM_JOIN_FAILED" : "Failed to join program", + "JOINED_PROGRAM" : "You have joined this program successfully", + "PROGRAM_NOT_CREATED" : "Program not created", + "PROGRAM_USERS_NOT_CREATED" : "Program users not created", + "PROGRAM_USERS_NOT_UPDATED" : "Program users not updated", + "USER_EXTENSION_FTECHED":"User extension fetched successfully" }; diff --git a/generics/constants/common.js b/generics/constants/common.js index 89200113..9160c321 100644 --- a/generics/constants/common.js +++ b/generics/constants/common.js @@ -19,7 +19,7 @@ module.exports = { "STATE_ENTITY_TYPE":"state", "ALL_APP_VERSION" : "allAppVersion", "ACTIVE" : "active", - "IN_ACTIVE" : "inactive", + "INACTIVE" : "inactive", "BODH_DIAL_CODE_LIVE_STATUS" : "Live", "GOA_STATE" : "Goa", "UNNATI_APP_NAME" : "unnati", @@ -56,5 +56,24 @@ module.exports = { "CREATED_BY_ME" : "createdByMe", "DEFAULT_PAGE_SIZE" : 100, "DEFAULT_PAGE_NO" : 1, - "DHITI" : "dhiti" + "DHITI" : "dhiti", + "DEFAULT_SURVEY_REMOVED_DAY" : 15, + "COURSE" : "course", + "PREFIX_FOR_SOLUTION_LINK" : "manage-learn", + "CREATE_OBSERVATION": "/create-observation/", + "CREATE_SURVEY" : "/create-survey/", + "CREATE_PROJECT" : "/create-project/", + "LINK" : "link", + "DISCOVERED_BY_ME" : "discoveredByMe", + "DEFAULT_SUBMISSION_REQUIRED" : 1, + "SCHOOL" : "school", + "SUBENTITY" :"subEntityTypesOf_", + "CACHE_TTL" : 43200, + "PROFILE_CONFIG_FORM_KEY" : "profileConfig_v2", + "SERVER_TIME_OUT" : 5000, + "CERTIFICATE" : "certificate", + "ORACLE_CLOUD_SERVICE" : "OCI", + "REVOKED" : "REVOKED", + "PROGRAM" : "Program" + }; diff --git a/generics/constants/endpoints.js b/generics/constants/endpoints.js index 465da2d5..e64fb4a4 100644 --- a/generics/constants/endpoints.js +++ b/generics/constants/endpoints.js @@ -6,24 +6,34 @@ */ module.exports = { - CREATE_USER_PROFILE : "/userProfile/create", - UPDATE_USER_PROFILE : "/userProfile/update", - VERIFY_USER_PROFILE : "/userProfile/verify", - PLATFORM_USER_PROFILE : "/platformUserRoles/getProfile", - USER_PROFILE_DETAILS : "/userProfile/details", + CREATE_USER_PROFILE : "/v1/userProfile/create", + UPDATE_USER_PROFILE : "/v1/userProfile/update", + VERIFY_USER_PROFILE : "/v1/userProfile/verify", + PLATFORM_USER_PROFILE : "/v1/platformUserRoles/getProfile", + USER_PROFILE_DETAILS : "/v1/userProfile/details", SUNBIRD_GENERATE_DIALCODE : "/dialcode/v1/generate", SUNBIRD_PUBLISH_DIALCODE : "/dialcode/v1/publish", SUNBIRD_DIALCODE_STATUS : "/dialcode/v1/read", SUNBIRD_CONTENT_LINK : "/dialcode/v1/content/link", - SUNBIRD_PUBLISH_CONTENT : "/content/v1/publish", + SUNBIRD_PUBLISH_CONTENT : "/v1/content/v1/publish", USER_READ : "/api/user/v1/read", SUNBIRD_INDEX_SYNC : "/data/v1/index/sync", SUNBIRD_CREATE_CONTENT : "/content/v1/create", SUNBIRD_UPLOAD_CONTENT : "/content/v1/upload", SUNBIRD_ORGANISATION_LISTS : "/organisations/list", SUNBIRD_USER_SEARCH :"/users/search", - GET_USER_ASSIGNED_OBSERVATION : "/observations/userAssigned", - GET_USER_ASSIGNED_SURVEY : "/surveys/userAssigned", - GET_USER_ASSIGNED_PROJECT : "/userProjects/userAssigned", - IMPORTED_PROJECT : "/userProjects/importedProjects" + GET_USER_ASSIGNED_OBSERVATION : "/v1/observations/userAssigned", + GET_USER_ASSIGNED_SURVEY : "/v1/surveys/userAssigned", + GET_USER_ASSIGNED_PROJECT : "/v1/userProjects/userAssigned", + IMPORTED_PROJECT : "/v1/userProjects/importedProjects", + GET_PROJECT_DETAILS : "/v1/userProjects/details", + GET_TEMPLATE_DETAILS : "/v1/project/templates/details", + LIST_PROJECT : "/v1/userProjects/list", + GET_QUESTIONS : "/v1/solutions/questions", + GET_OBSERVATION : "/v1/observations/details", + GET_LOCATION_DATA : "/v1/location/search", + GET_FORM_DATA : "/plugin/v1/form/read", + GET_SCHOOL_DATA : "/v1/org/search", + USER_READ_V5 : "/v5/user/read", + USER_CONSENT_API: "/user/v1/consent/update" } \ No newline at end of file diff --git a/generics/file-stream.js b/generics/file-stream.js index 0f08dff9..80fab286 100644 --- a/generics/file-stream.js +++ b/generics/file-stream.js @@ -9,7 +9,7 @@ const json2Csv = require('json2csv').Transform; const stream = require("stream"); const fs = require("fs"); const moment = require("moment-timezone"); -const DEFAULT_REPORTS_PATH = gen.utils.checkIfEnvDataExistsOrNot("DEFAULT_REPORTS_PATH"); +const DEFAULT_REPORTS_PATH = process.env.CSV_REPORTS_PATH; /** * FileStream @@ -21,10 +21,8 @@ let FileStream = class FileStream { constructor(fileName) { const currentDate = new Date(); const fileExtensionWithTime = moment(currentDate).tz("Asia/Kolkata").format("YYYY_MM_DD_HH_mm") + ".csv"; - if(!process.env.CSV_REPORTS_PATH){ - process.env.CSV_REPORTS_PATH = DEFAULT_REPORTS_PATH; - } - const filePath = `${process.env.CSV_REPORTS_PATH}/${moment(currentDate).tz("Asia/Kolkata").format("YYYY_MM_DD")}/`; + + const filePath = `${DEFAULT_REPORTS_PATH}/${moment(currentDate).tz("Asia/Kolkata").format("YYYY_MM_DD")}/`; this.ensureDirectoryPath(filePath); this.input = new stream.Readable({ objectMode: true }); this.fileName = filePath + fileName + "_" + fileExtensionWithTime; diff --git a/generics/helpers/cache.js b/generics/helpers/cache.js new file mode 100644 index 00000000..7c6e10f4 --- /dev/null +++ b/generics/helpers/cache.js @@ -0,0 +1,63 @@ +/** + * name : cache.js + * author : Priyanka Pradeep + * created-date : 21-Feb-2020 + * Description : Cache set , get and remove functionality. + */ + + const nodeCache = require( "node-cache" ); + const cache = new nodeCache(); + + /** + * Get cache data. + * @method + * @name get - Get specific cache data + * @params key - name of the cache key. + * @returns {Array} - return specific cache data. +*/ + +function getValue(key){ + let data = []; + + if (cache.has(key)) { + data = cache.get(key); + return data; + } else { + return false; + } +} + + + /** + * Set new cache data + * @method + * @name set + * @params key - name of the cache key. + * @params value - cache data to set. + * @returns {Boolean} - true +*/ + +function setValue(key, value, timeout){ + let data = cache.set( key, value, timeout ); + return true; +} + +/** + * delete cache data + * @method + * @name remove + * @params key - cache key need to be removed. + * @returns {Boolean} - true +*/ + +function removeKey(key){ + + let data = cache.del(key); + return true; +} + +module.exports = { + getValue : getValue, + setValue : setValue, + removeKey : removeKey +} \ No newline at end of file diff --git a/generics/helpers/http-request.js b/generics/helpers/http-request.js index 25fd3836..28d95e97 100644 --- a/generics/helpers/http-request.js +++ b/generics/helpers/http-request.js @@ -20,7 +20,6 @@ var convert = require('xml-js'); * @class */ var Request = class Request { - constructor() {} /** * diff --git a/generics/helpers/utils.js b/generics/helpers/utils.js index 5bfe2df1..bfcefbd1 100644 --- a/generics/helpers/utils.js +++ b/generics/helpers/utils.js @@ -7,6 +7,7 @@ // Dependencies const {validate : uuidValidate,v4 : uuidV4} = require('uuid'); +const md5 = require("md5"); /** * convert string to camelCaseToTitleCase. @@ -199,12 +200,22 @@ function valueParser(dataToBeParsed) { * check whether string is valid uuid. * @function * @name checkValidUUID - * @param {String} str + * @param {String} uuids * @returns {Boolean} returns a Boolean value true/false */ -function checkValidUUID(str) { - var validateUUID = uuidValidate(str); +function checkValidUUID(uuids) { + + var validateUUID = true; + if(Array.isArray(uuids)){ + for (var i = 0; uuids.length > i; i++) { + if(!uuidValidate(uuids[i])){ + validateUUID = false + } + } + }else { + validateUUID = uuidValidate(uuids); + } return validateUUID; } @@ -213,6 +224,66 @@ function convertStringToBoolean(stringData) { return stringToBoolean; } +/** + * md5 hash + * @function + * @name md5Hash + * @returns {String} returns hashed value. +*/ + +function md5Hash(value) { + return md5(value); +} + +/** + * filter out location id and code + * @function + * @name filterLocationIdandCode + * @returns {Object} - Object contain locationid and location code array. +*/ + +function filterLocationIdandCode(dataArray) { + let locationIds = []; + let locationCodes = []; + dataArray.forEach(element=>{ + if (this.checkValidUUID(element)) { + locationIds.push(element); + } else { + locationCodes.push(element); + } + }); + return ({ + ids : locationIds, + codes : locationCodes + }); +} + +function arrayIdsTobjectIds(ids) { + return ids.map(id => ObjectId(id)); +} + +/** + * check whether string contains only number + * @function + * @name checkIfStringIsNumber + * @returns {Boolean} returns a Boolean value true/false +*/ + +function checkIfStringIsNumber(str) { + return /^[0-9]+$/.test(str); +} + +/** + * array of object to array objectId + * @function + * @name arrayOfObjectToArrayOfObjectId + * @returns {Boolean} returns a Boolean value true/false +*/ + +function arrayOfObjectToArrayOfObjectId(ids) { + return ids.map(obj => obj._id); +} + module.exports = { camelCaseToTitleCase : camelCaseToTitleCase, lowerCase : lowerCase, @@ -226,5 +297,10 @@ module.exports = { epochTime : epochTime, valueParser : valueParser, checkValidUUID : checkValidUUID, - convertStringToBoolean : convertStringToBoolean + convertStringToBoolean : convertStringToBoolean, + md5Hash : md5Hash, + filterLocationIdandCode : filterLocationIdandCode, + arrayIdsTobjectIds : arrayIdsTobjectIds, + checkIfStringIsNumber : checkIfStringIsNumber, + arrayOfObjectToArrayOfObjectId : arrayOfObjectToArrayOfObjectId }; diff --git a/generics/http-status-codes/index.js b/generics/http-status-codes/index.js index 82aa9978..1c68a696 100644 --- a/generics/http-status-codes/index.js +++ b/generics/http-status-codes/index.js @@ -187,5 +187,8 @@ module.exports = { 'insufficient_storage': { status: 507, message: "Insufficient Storage" + }, + 'http_responsecode_ok':{ + message:"OK" } }; diff --git a/generics/kafka/producers.js b/generics/kafka/producers.js new file mode 100644 index 00000000..9f660e12 --- /dev/null +++ b/generics/kafka/producers.js @@ -0,0 +1,85 @@ +/** + * name : producer.js + * author : Vishnu + * created-date : 10-Jan-2023 + * Description : Kafka Producer related information. +*/ + +// Dependencies +const kafkaCommunicationsOnOff = process.env.KAFKA_COMMUNICATIONS_ON_OFF; +const programUsersSubmissionTopic = (process.env.PROGRAM_USERS_JOINED_TOPIC != "OFF") ? process.env.PROGRAM_USERS_JOINED_TOPIC : `${process.env.APPLICATION_ENV}.programuser.info`; + +/** + * Push program users to kafka. + * @function + * @name pushProgramUsersToKafka + * @param {Object} message - Message data. +*/ + +const pushProgramUsersToKafka = function (message) { + return new Promise(async (resolve, reject) => { + try { + + let kafkaPushStatus = await pushMessageToKafka([{ + topic: programUsersSubmissionTopic, + messages: JSON.stringify(message) + }]); + + return resolve(kafkaPushStatus); + + } catch (error) { + return reject(error); + } + }) +} + + + +/** + * Push message to kafka. + * @function + * @name pushMessageToKafka + * @param {Object} payload - Payload data. +*/ + +const pushMessageToKafka = function(payload) { + return new Promise((resolve, reject) => { + + if (kafkaCommunicationsOnOff != "ON") { + throw reject("Kafka configuration is not done"); + } + + console.log("-------Kafka producer log starts here------------------"); + console.log("Topic Name: ", payload[0].topic); + console.log("Message: ", JSON.stringify(payload)); + console.log("-------Kafka producer log ends here------------------"); + + + kafkaClient.kafkaProducer.send(payload, (err, data) => { + if (err) { + return reject("Kafka push to topic "+ payload[0].topic +" failed."); + } else { + return resolve(data); + } + }) + + }).then(result => { + + return { + status : "success", + message: "Kafka push to topic "+ payload[0].topic +" successful with number - "+result[payload[0].topic][0] + }; + + }).catch((err) => { + return { + status : "failed", + message: err + } + }) +} + + +module.exports = { + pushProgramUsersToKafka : pushProgramUsersToKafka +}; + diff --git a/generics/middleware/authenticator.js b/generics/middleware/authenticator.js index 67d217a1..a3e8f87b 100644 --- a/generics/middleware/authenticator.js +++ b/generics/middleware/authenticator.js @@ -69,7 +69,8 @@ module.exports = async function (req, res, next) { "/user-roles/list", "/forms/details", "/programs/list", - "/entities/getUsersByEntityAndRole/" + "/entities/getUsersByEntityAndRole/", + "/entities/details" ]; let performInternalAccessTokenCheck = false; @@ -101,7 +102,12 @@ module.exports = async function (req, res, next) { "/solutions/addRolesInScope", "/solutions/addEntitiesInScope", "solutions/removeRolesInScope", - "/solutions/removeEntitiesInScope" + "/solutions/removeEntitiesInScope", + "/programs/details", + "/admin/dbUpdate", + "/admin/dbFind", + "/admin/dbDelete", + "/admin/dbCreate" ]; for(let path = 0; path < mandatoryInternalAccessApiPaths.length ; path++ ) { diff --git a/generics/services/azure.js b/generics/services/azure.js index 553408e8..00cfcd7c 100644 --- a/generics/services/azure.js +++ b/generics/services/azure.js @@ -10,25 +10,25 @@ let blobServiceClient; let containerClient; (async () => { + if( process.env.CLOUD_STORAGE === "AZURE" ) { + const sharedKeyCredential = new StorageSharedKeyCredential(AZURE_ACCOUNT_NAME, AZURE_ACCOUNT_KEY); - const sharedKeyCredential = new StorageSharedKeyCredential(AZURE_ACCOUNT_NAME, AZURE_ACCOUNT_KEY); + // Create the BlobServiceClient object which will be used to create a container client + blobServiceClient = new BlobServiceClient( + `https://${AZURE_ACCOUNT_NAME}.blob.core.windows.net`, + sharedKeyCredential + ); - // Create the BlobServiceClient object which will be used to create a container client - blobServiceClient = new BlobServiceClient( - `https://${AZURE_ACCOUNT_NAME}.blob.core.windows.net`, - sharedKeyCredential - ); + // Get a reference to a container + containerClient = await blobServiceClient.getContainerClient(AZURE_STORAGE_CONTAINER); - // Get a reference to a container - containerClient = await blobServiceClient.getContainerClient(AZURE_STORAGE_CONTAINER); + const checkIfContainerExists = await containerClient.exists(); - const checkIfContainerExists = await containerClient.exists(); - - if (!checkIfContainerExists) { - // Create the container - const createContainerResponse = await containerClient.create(); + if (!checkIfContainerExists) { + // Create the container + const createContainerResponse = await containerClient.create(); + } } - })(); /** diff --git a/generics/services/form.js b/generics/services/form.js new file mode 100644 index 00000000..0f423c9b --- /dev/null +++ b/generics/services/form.js @@ -0,0 +1,111 @@ +/** + * name : form.js + * author : Vishnudas + * Date : 02-Mar-2022 + * Description : calling sunbird form api and setting cache. + */ + +//dependencies +const request = require('request'); + +/** + * @function + * @name configForStateLocation + * @param {String} stateLocationCode - state location code. eg: 28 + * @param {object} entityKey - key to store data in cache. eg: subEntityTypesOf_bc75cc99-9205-463e-a722-5326857838f8 + * @returns {Promise} returns a promise. +*/ + + +const configForStateLocation = function ( stateLocationCode, entityKey ) { + return new Promise(async (resolve, reject) => { + try { + //Get Sub Entity Types present in a particular state + let subEntitiesDetails = await formRead( stateLocationCode ); + if( !subEntitiesDetails.success ) { + return resolve({ + message : constants.apiResponses.ENTITY_NOT_FOUND, + result : [] + }) + } + + let subEntityData = subEntitiesDetails.data.form.data.fields[1].children.teacher; + //Entity type is stored in a key called code + let subEntities = subEntityData.map( subEntity => { + return subEntity.code; + }) + //set cache data for given state + let setCache = cache.setValue(entityKey, subEntities, constants.common.CACHE_TTL); + return resolve(subEntities); + + } catch (error) { + return reject(error); + } + }) +} + +/** + * + * @function + * @name formRead + * @param {String} stateLocationCode - State location code. + * @returns {Promise} returns a promise. +*/ +async function formRead ( stateLocationCode ) { + return new Promise(async (resolve, reject) => { + try { + + let bodyData = { + request : { + type: constants.common.PROFILE_CONFIG_FORM_KEY, + subType: stateLocationCode, + action: constants.common.GET_METHOD + } + } + + const url = + process.env.FORM_SERVICE_URL + constants.endpoints.GET_FORM_DATA; + + const options = { + headers : { + "content-type": "application/json", + }, + json : bodyData + }; + + request.post(url, options, requestCallBack); + let result = { + success : true + }; + function requestCallBack(err, data) { + + if (err) { + result.success = false; + } else { + + let response = data.body; + + if( response.responseCode === constants.common.OK) { + result["data"] = response.result; + result.success = true; + } else { + result.success = false; + } + } + return resolve(result); + } + setTimeout(function () { + return resolve (result = { + success : false + }); + }, constants.common.SERVER_TIME_OUT); + + } catch (error) { + return reject(error); + } + }) +} + +module.exports = { + configForStateLocation : configForStateLocation +} \ No newline at end of file diff --git a/generics/services/improvement-project.js b/generics/services/improvement-project.js index c34e5199..77251bdf 100644 --- a/generics/services/improvement-project.js +++ b/generics/services/improvement-project.js @@ -7,6 +7,7 @@ //dependencies const request = require('request'); +const patternForDetectLang = /^[A-Za-z0-9]*$/; /** * List of user assigned projects. @@ -28,7 +29,12 @@ var assignedProjects = function ( token,search = "",filter = "" ) { if( filter !== "" ) { url = url + "&filter=" + filter; } - + + //finding the type of language + if (!patternForDetectLang.test(search)) { + url = encodeURI(url); + } + return new Promise(async (resolve, reject) => { try { @@ -43,7 +49,6 @@ var assignedProjects = function ( token,search = "",filter = "" ) { } else { let response = JSON.parse(data.body); - if( response.status === httpStatusCode['ok'].status ) { result["data"] = response.result; } else { @@ -130,7 +135,198 @@ var importedProjects = function ( token,programId = "" ) { } +/** + * Get project detail by solutionId. + * @function + * @name getProjectDetail + * @param {String} token - logged in user token. + * @param {String} link - link + * @param {Object} bodyData - bodyData + * @returns {Promise} returns a promise. +*/ + +var getProjectDetail = function ( solutionId, token, bodyData = {} ) { + + let getProjectDetailUrl = + process.env.ML_PROJECT_SERVICE_URL + + constants.endpoints.GET_PROJECT_DETAILS + "?solutionId=" + solutionId; + + return new Promise(async (resolve, reject) => { + try { + + function improvementProjectCallback(err, data) { + + let result = { + success : true, + message: "", + status:"" + }; + + if (err) { + result.success = false; + } else { + + let response = data.body; + if( response.status === httpStatusCode['ok'].status ) { + result["data"] = response.result; + } else { + result.success = false; + } + + result.message = response.message; + result.status = response.status; + } + + return resolve(result); + } + + const options = { + headers : { + "content-type": "application/json", + "x-authenticated-user-token" : token + } + }; + + if( bodyData !== "" ) { + options.json = bodyData + } + + request.get(getProjectDetailUrl,options,improvementProjectCallback) + + } catch (error) { + return reject(error); + } + }) + +} + +/** + * Get template detail. + * @function + * @name getTemplateDetail + * @param {String} token - logged in user token. + * @param {String} templateId - templateId + * @returns {Promise} returns a promise. +*/ + +var getTemplateDetail = function ( templateId, token , isAPrivateProgram ) { + + let url = + process.env.ML_PROJECT_SERVICE_URL + + constants.endpoints.GET_TEMPLATE_DETAILS + "/" + templateId+"?isAPrivateProgram="+isAPrivateProgram; + + return new Promise(async (resolve, reject) => { + try { + + function improvementProjectCallback(err, data) { + + let result = { + success : true, + message: "", + status:"" + }; + + if (err) { + result.success = false; + } else { + + let response = JSON.parse(data.body); + if( response.status == httpStatusCode['ok'].status ) { + result["result"] = response.result; + } else { + result.success = false; + } + + result.message = response.message; + result.status = response.status; + } + + return resolve(result); + } + + const options = { + headers : { + "content-type": "application/json", + "x-authenticated-user-token" : token + } + }; + + request.get(url,options,improvementProjectCallback) + + } catch (error) { + return reject(error); + } + }) + +} + +/** + * List of project. + * @function + * @name projectDocuments + * @param {Object} filterData - Filter data. + * @param {Array} projection - Projected data. + * @param {Array} skipFields - Field to skip. + * @returns {JSON} - List of projects. +*/ + +const projectDocuments = function ( + userToken, + filterData = "all", + projection = "all", + skipFields = "none", +) { + return new Promise(async (resolve, reject) => { + try { + + const url = process.env.ML_PROJECT_SERVICE_URL + constants.endpoints.LIST_PROJECT; + + function improvementProjectCallback(err, data) { + + let result = { + success : true + }; + + if (err) { + result.success = false; + } else { + + let response = data.body; + if( response.status === httpStatusCode['ok'].status) { + result["data"] = response.result; + } else { + result.success = false; + } + } + + return resolve(result); + } + + const options = { + headers : { + "content-type": "application/json", + // "internal-access-token": process.env.INTERNAL_ACCESS_TOKEN, + "x-authenticated-user-token" : userToken + }, + json : { + query : filterData, + projection : projection, + skipFields : skipFields + } + }; + + request.post(url,options,improvementProjectCallback); + + } catch (error) { + return reject(error); + } + }) +} + module.exports = { assignedProjects : assignedProjects, - importedProjects : importedProjects + importedProjects : importedProjects, + getProjectDetail : getProjectDetail, + getTemplateDetail : getTemplateDetail, + projectDocuments : projectDocuments } \ No newline at end of file diff --git a/generics/services/oracle-cloud.js b/generics/services/oracle-cloud.js new file mode 100644 index 00000000..681b9566 --- /dev/null +++ b/generics/services/oracle-cloud.js @@ -0,0 +1,158 @@ +/** + * name : oracle-cloud.js + * author : vishnu + * created-date : 10-Nov-2022 + * Description : All oracle-cloud related functionality + */ + +// Dependencies +const AWS = require('aws-sdk'); +const OCI_ACCESS_KEY_ID = +(process.env.OCI_ACCESS_KEY_ID && process.env.OCI_ACCESS_KEY_ID != "") ? +process.env.OCI_ACCESS_KEY_ID : ""; + +const OCI_SECRET_ACCESS_KEY = +(process.env.OCI_SECRET_ACCESS_KEY && process.env.OCI_SECRET_ACCESS_KEY != "") ? +process.env.OCI_SECRET_ACCESS_KEY : ""; + +AWS.config.update({ + accessKeyId : OCI_ACCESS_KEY_ID, + secretAccessKey : OCI_SECRET_ACCESS_KEY, + region : process.env.OCI_BUCKET_REGION, + endpoint : process.env.OCI_BUCKET_ENDPOINT, + s3ForcePathStyle : true, + signatureVersion : 'v4' +}); + +const s3 = new AWS.S3(); + +/** + * Upload file in oracle cloud. + * @function + * @name uploadFile + * @param file - file to upload. + * @param filePath - file path + * @returns {Object} - upload file information +*/ + +let uploadFile = function( file,filePath,bucketName ) { + + return new Promise( async(resolve,reject)=>{ + + let bucket = bucketName ? bucketName : process.env.DEFAULT_BUCKET_NAME; + const uploadParams = { + Bucket: bucket, + Key: filePath, + Body: file + }; + + s3.upload(uploadParams,function(err,data){ + if( err ) { + return reject({ + message : "Could not upload file in oracle" + }); + } else { + let result = { + name : data.key, + bucket : data.Bucket, + location : data.Location + }; + + return resolve(result); + } + }) + + }) +} + +/** + * Get downloadable url. + * @function + * @name getDownloadableUrl + * @param filePath - file path + * @returns {String} - Get downloadable url link +*/ + +let getDownloadableUrl = function( filePath,bucketName ) { + + return new Promise( async(resolve,reject)=>{ + + try { + let bucket = bucketName ? bucketName : process.env.DEFAULT_BUCKET_NAME; + let downloadableUrl = + `${process.env.OCI_BUCKET_ENDPOINT}/${bucket}/${filePath}`; + return resolve(downloadableUrl); + } catch(error) { + return reject(error); + } + + }) +} + + /** + * Get oracle s3 cloud signed url. + * @method + * @name getS3SignedUrl + * @param {String} fileName - fileName. + * @param {String} bucketName - name of the bucket. + * @returns {Object} - signed url and s3 file name. + */ + +let signedUrl = ( fileName ,bucketName ) =>{ + return new Promise(async (resolve, reject) => { + try { + + if( fileName == "" ) { + throw new Error(httpStatusCode.bad_request.status); + } + + let noOfMinutes = constants.common.NO_OF_MINUTES; + let expiry = constants.common.NO_OF_EXPIRY_TIME * noOfMinutes; + + try { + + const url = await s3.getSignedUrl('putObject', { + Bucket : bucketName, + Key : fileName, + Expires : expiry + }); + + let result = { + success : true, + url : url + }; + + if(url && url != "") { + + result["name"] = fileName; + + } else { + + result["success"] = false; + + } + + return resolve(result); + + } catch (error) { + return resolve({ + success : false, + message : error.message, + response : error + }); + } + + + } catch (error) { + return reject(error); + } + }) +} + + +module.exports = { + s3: s3, + uploadFile : uploadFile, + getDownloadableUrl : getDownloadableUrl, + signedUrl : signedUrl +}; \ No newline at end of file diff --git a/generics/services/samiksha.js b/generics/services/samiksha.js deleted file mode 100644 index a0035482..00000000 --- a/generics/services/samiksha.js +++ /dev/null @@ -1,137 +0,0 @@ -/** - * name : samiksha.js - * author : Aman Jung Karki - * Date : 11-Nov-2019 - * Description : All samiksha related api call. - */ - -//dependencies - -const request = require('request'); - -/** - * List of assigned user observations. - * @function - * @name assignedObservations - * @param {String} token - logged in user token. - * @param {String} [ search = "" ] - search data. - * @param {String} [ filter = "" ] - * @returns {Promise} returns a promise. -*/ - -var assignedObservations = function ( token,search = "",filter = "" ) { - - let userAssignedUrl = - process.env.ML_SURVEY_SERVICE_URL + - constants.endpoints.GET_USER_ASSIGNED_OBSERVATION + "?search=" + search; - - if( filter !== "" ) { - userAssignedUrl = userAssignedUrl + "&filter=" + filter; - } - - return new Promise(async (resolve, reject) => { - try { - - function assessmentCallback(err, data) { - - let result = { - success : true - }; - - if (err) { - result.success = false; - } else { - - let response = JSON.parse(data.body); - - if( response.status === httpStatusCode['ok'].status ) { - result["data"] = response.result; - } else { - result.success = false; - } - } - - return resolve(result); - } - - const options = { - headers : { - "content-type": "application/json", - "x-authenticated-user-token" : token - } - }; - - request.get(userAssignedUrl,options,assessmentCallback) - - } catch (error) { - return reject(error); - } - }) - -} - -/** - * List of user assigned surveys. - * @function - * @name assignedSurveys - * @param {String} token - logged in user token. - * @param {String} [search = ""] - search data. - * @param {String} [filter = ""] - filter key. - * @returns {Promise} returns a promise. -*/ - -var assignedSurveys = function ( token,search = "",filter = "" ) { - - let userAssignedUrl = - process.env.ML_SURVEY_SERVICE_URL + - constants.endpoints.GET_USER_ASSIGNED_SURVEY + "?search=" + search; - - if( filter !== "" ) { - userAssignedUrl = userAssignedUrl + "&filter=" + filter; - } - - return new Promise(async (resolve, reject) => { - try { - - function assessmentCallback(err, data) { - - let result = { - success : true - }; - - if (err) { - result.success = false; - } else { - - let response = JSON.parse(data.body); - - if( response.status === httpStatusCode['ok'].status ) { - result["data"] = response.result; - } else { - result.success = false; - } - } - - return resolve(result); - } - - const options = { - headers : { - "content-type": "application/json", - "x-authenticated-user-token" : token - } - }; - - request.get(userAssignedUrl,options,assessmentCallback) - - } catch (error) { - return reject(error); - } - }) - -} - -module.exports = { - assignedObservations : assignedObservations, - assignedSurveys : assignedSurveys -}; \ No newline at end of file diff --git a/generics/services/survey.js b/generics/services/survey.js new file mode 100644 index 00000000..0663361b --- /dev/null +++ b/generics/services/survey.js @@ -0,0 +1,270 @@ +/** + * name : survey.js + * author : Aman Jung Karki + * Date : 11-Nov-2019 + * Description : All survey related api call. + */ + +//dependencies + +const request = require('request'); + +/** + * List of assigned user observations. + * @function + * @name assignedObservations + * @param {String} token - logged in user token. + * @param {String} [ search = "" ] - search data. + * @param {String} [ filter = "" ] + * @returns {Promise} returns a promise. +*/ + +var assignedObservations = function ( token,search = "",filter = "" ) { + + let userAssignedUrl = + process.env.ML_SURVEY_SERVICE_URL + + constants.endpoints.GET_USER_ASSIGNED_OBSERVATION + "?search=" + search; + + if( filter !== "" ) { + userAssignedUrl = userAssignedUrl + "&filter=" + filter; + } + + return new Promise(async (resolve, reject) => { + try { + + function assessmentCallback(err, data) { + + let result = { + success : true + }; + + if (err) { + result.success = false; + } else { + + let response = JSON.parse(data.body); + + if( response.status === httpStatusCode['ok'].status ) { + result["data"] = response.result; + } else { + result.success = false; + } + } + + return resolve(result); + } + + const options = { + headers : { + "content-type": "application/json", + "x-authenticated-user-token" : token + } + }; + + request.get(userAssignedUrl,options,assessmentCallback) + + } catch (error) { + return reject(error); + } + }) + +} + +/** + * List of user assigned surveys. + * @function + * @name assignedSurveys + * @param {String} token - logged in user token. + * @param {String} [search = ""] - search data. + * @param {String} [filter = ""] - filter key. + * @param {Array} - solutionIds - survey solutionIds + * @returns {Promise} returns a promise. +*/ + +var assignedSurveys = function ( token,search = "",filter = "", surveyReportPage = "", solutionIds = []) { + + let userAssignedUrl = + process.env.ML_SURVEY_SERVICE_URL + + constants.endpoints.GET_USER_ASSIGNED_SURVEY + "?search=" + search; + + if( filter !== "" ) { + userAssignedUrl = userAssignedUrl + "&filter=" + filter; + } + + if( surveyReportPage !== "" ) { + userAssignedUrl = userAssignedUrl + "&surveyReportPage=" + surveyReportPage; + } + let requestBody = {} + if (solutionIds.length > 0) { + requestBody.solutionIds = solutionIds; + } + + + return new Promise(async (resolve, reject) => { + try { + + function assessmentCallback(err, data) { + + let result = { + success : true + }; + + if (err) { + result.success = false; + } else { + + let response = JSON.parse(data.body); + if( response.status === httpStatusCode['ok'].status ) { + result["data"] = response.result; + } else { + result.success = false; + } + } + + return resolve(result); + } + + const options = { + headers : { + "content-type": "application/json", + "x-authenticated-user-token" : token + }, + body: JSON.stringify(requestBody) + + }; + + request.get(userAssignedUrl,options,assessmentCallback) + + } catch (error) { + return reject(error); + } + }) + +} + +/** + * Get questions from solution. + * @function + * @name getQuestions + * @param {String} token - logged in user token. + * @param {String} solutionId - solution Id + * @returns {Promise} returns a promise. +*/ + +var getQuestions = function ( solutionId, token ) { + + let getQuestionsUrl = + process.env.ML_SURVEY_SERVICE_URL + + constants.endpoints.GET_QUESTIONS + "/" + solutionId; + + return new Promise(async (resolve, reject) => { + try { + + function assessmentCallback(err, data) { + + let result = { + success : true, + message: "", + status:"" + }; + + if (err) { + result.success = false; + } else { + + let response = JSON.parse(data.body); + if( response.status === httpStatusCode['ok'].status ) { + result["result"] = response.result; + } else { + result.success = false; + } + + result.message = response.message; + result.status = response.status; + } + + return resolve(result); + } + + const options = { + headers : { + "content-type": "application/json", + "x-authenticated-user-token" : token + } + }; + + request.get(getQuestionsUrl,options,assessmentCallback) + + } catch (error) { + return reject(error); + } + }) + +} + +/** + * Get observation. + * @function + * @name getObservationDetail + * @param {String} token - logged in user token. + * @param {String} solutionId - solution Id + * @returns {Promise} returns a promise. +*/ + +var getObservationDetail = function ( solutionId, token ) { + + let url = + process.env.ML_SURVEY_SERVICE_URL + + constants.endpoints.GET_OBSERVATION + "?solutionId=" + solutionId; + + return new Promise(async (resolve, reject) => { + try { + + function assessmentCallback(err, data) { + + let result = { + success : true, + message: "", + status:"" + }; + + if (err) { + result.success = false; + } else { + + let response = JSON.parse(data.body); + if( response.status === httpStatusCode['ok'].status ) { + result["result"] = response.result; + } else { + result.success = false; + } + + result.message = response.message; + result.status = response.status; + } + + return resolve(result); + } + + const options = { + headers : { + "content-type": "application/json", + "x-authenticated-user-token" : token + } + }; + + request.get(url,options,assessmentCallback) + + } catch (error) { + return reject(error); + } + }) + +} + +module.exports = { + assignedObservations : assignedObservations, + assignedSurveys : assignedSurveys, + getQuestions : getQuestions, + getObservationDetail : getObservationDetail +}; \ No newline at end of file diff --git a/generics/services/users.js b/generics/services/users.js index 9ba0ef1d..ed6c8c92 100644 --- a/generics/services/users.js +++ b/generics/services/users.js @@ -12,11 +12,11 @@ const userServiceUrl = process.env.USER_SERVICE_URL; const profile = function ( token,userId = "" ) { return new Promise(async (resolve, reject) => { try { - - let url = userServiceUrl + constants.endpoints.USER_READ; + + let url = userServiceUrl + constants.endpoints.USER_READ_V5; if( userId !== "" ) { - url = url + "/" + userId; + url = url + "/" + userId + "?" + "fields=organisations,roles,locations,declarations,externalIds" } const options = { @@ -26,7 +26,7 @@ const profile = function ( token,userId = "" ) { } }; - request.post(url,options,kendraCallback); + request.get(url,options,kendraCallback); function kendraCallback(err, data) { @@ -36,13 +36,15 @@ const profile = function ( token,userId = "" ) { if (err) { result.success = false; + console.log("error occured user read api call :",err) } else { let response = JSON.parse(data.body); - if( response.status === httpStatusCode['ok'].status ) { + console.log("response from userRead api call :",response) + if( response.responseCode === httpStatusCode['http_responsecode_ok'].message ) { result["data"] = response.result.response; } else { - result["message"] = response.message; + result["message"] = response.params.status; result.success = false; } @@ -57,6 +59,357 @@ const profile = function ( token,userId = "" ) { }) } +/** + * + * @function + * @name locationSearch + * @param {object} filterData - contain filter object. + * @param {String} pageSize - requested page size. + * @param {String} pageNo - requested page number. + * @param {String} searchKey - search string. + * @param {Boolean} formatResult - format result + * @param {Boolean} returnObject - return object or array. + * @param {Boolean} resultForSearchEntities - format result for searchEntities api call. + * @returns {Promise} returns a promise. +*/ + +const locationSearch = function ( filterData, pageSize = "", pageNo = "", searchKey = "" , formatResult = false, returnObject = false, resultForSearchEntities = false ) { + return new Promise(async (resolve, reject) => { + try { + let bodyData = {}; + bodyData["request"] = {}; + bodyData["request"]["filters"] = filterData; + + if ( pageSize !== "" ) { + bodyData["request"]["limit"] = pageSize; + } + + if ( pageNo !== "" ) { + let offsetValue = pageSize * ( pageNo - 1 ); + bodyData["request"]["offset"] = offsetValue; + } + + if ( searchKey !== "" ) { + bodyData["request"]["query"] = searchKey + } + + + const url = + userServiceUrl + constants.endpoints.GET_LOCATION_DATA; + const options = { + headers : { + "content-type": "application/json", + }, + json : bodyData + }; + request.post(url,options,requestCallback); + + let result = { + success : true + }; + + function requestCallback(err, data) { + if (err) { + result.success = false; + } else { + let response = data.body; + + if( response.responseCode === constants.common.OK && + response.result && + response.result.response && + response.result.response.length > 0 + ) { + // format result if true + if ( formatResult ) { + let entityDocument = []; + response.result.response.map(entityData => { + let data = {}; + data._id = entityData.id; + data.entityType = entityData.type; + data.metaInformation = {}; + data.metaInformation.name = entityData.name; + data.metaInformation.externalId = entityData.code; + data.registryDetails = {}; + data.registryDetails.locationId = entityData.id; + data.registryDetails.code = entityData.code; + entityDocument.push(data); + }); + if ( returnObject ) { + result["data"] = entityDocument[0]; + result["count"] = response.result.count; + } else { + result["data"] = entityDocument; + result["count"] = response.result.count; + } + } else if ( resultForSearchEntities ) { + let entityDocument = []; + response.result.response.map(entityData => { + let data = {}; + data._id = entityData.id; + data.name = entityData.name; + data.externalId = entityData.code; + entityDocument.push(data); + }); + result["data"] = entityDocument; + result["count"] = response.result.count; + }else { + result["data"] = response.result.response; + result["count"] = response.result.count; + } + } else { + result.success = false; + } + } + return resolve(result); + } + + setTimeout(function () { + return resolve (result = { + success : false + }); + }, constants.common.SERVER_TIME_OUT); + + + } catch (error) { + return reject(error); + } + }) + } + + + + /** + * + * @function + * @name orgSchoolSearch + * @param {object} filterData - contain filter object. + * @param {String} pageSize - requested page size. + * @param {String} pageNo - requested page number. + * @param {String} searchKey - search string. + * @param {String} searchKey - search key for fuzzy search. + * @param {String} fields - required field filter. + * @returns {Promise} returns a promise. + */ + const orgSchoolSearch = function ( filterData, pageSize = "", pageNo = "", searchKey = "", fields = [] ) { + return new Promise(async (resolve, reject) => { + try { + + let bodyData = {}; + bodyData["request"] = {}; + bodyData["request"]["filters"] = filterData; + + if ( pageSize !== "" ) { + bodyData["request"]["limit"] = pageSize; + } + + if ( pageNo !== "" ) { + let offsetValue = pageSize * ( pageNo - 1 ); + bodyData["request"]["offset"] = offsetValue; + } + + if ( searchKey !== "" ) { + if ( gen.utils.checkIfStringIsNumber(searchKey) ) { + bodyData["request"]["fuzzy"] = { + "externalId" : searchKey + } + } else { + bodyData["request"]["fuzzy"] = { + "orgName" : searchKey + } + } + + } + + //for getting specified key data only. + if ( fields.length > 0 ) { + bodyData["request"]["fields"] = fields; + } + + const url = + userServiceUrl + constants.endpoints.GET_SCHOOL_DATA; + const options = { + headers : { + "content-type": "application/json" + }, + json : bodyData + }; + + request.post(url,options,requestCallback); + let result = { + success : true + }; + + function requestCallback(err, data) { + + if (err) { + result.success = false; + } else { + let response = data.body; + if( response.responseCode === constants.common.OK && + response.result && + response.result.response && + response.result.response.content && + response.result.response.content.length > 0 + ){ + result["data"] = response.result.response.content; + result["count"] = response.result.response.count; + } else { + result.success = false; + } + } + return resolve(result); + } + setTimeout(function () { + return resolve (result = { + success : false + }); + }, constants.common.SERVER_TIME_OUT); + + } catch (error) { + return reject(error); + } + }) + } + +/** + * get subEntities of matching type by recursion. + * @method + * @name getSubEntitiesBasedOnEntityType + * @param parentIds {Array} - Array of entity Ids- for which we are finding sub entities of given entityType + * @param entityType {string} - EntityType. + * @returns {Array} - Sub entities matching the type . +*/ + +async function getSubEntitiesBasedOnEntityType( parentIds, entityType, result ) { + + if( !parentIds.length > 0 ){ + return result; + } + let bodyData={ + "parentId" : parentIds + }; + + let entityDetails = await locationSearch(bodyData); + if( !entityDetails.success ) { + return (result); + } + + let entityData = entityDetails.data; + let parentEntities = []; + entityData.map(entity => { + if( entity.type == entityType ) { + result.push(entity.id) + } else { + parentEntities.push(entity.id) + } + }); + + if( parentEntities.length > 0 ){ + await getSubEntitiesBasedOnEntityType(parentEntities,entityType,result) + } + + let uniqueEntities = _.uniq(result); + return uniqueEntities; +} + +/** + * get Parent Entities of an entity. + * @method + * @name getParentEntities + * @param {String} entityId - entity id + * @returns {Array} - parent entities. +*/ + +async function getParentEntities( entityId, iteration = 0, parentEntities ) { + + if ( iteration == 0 ) { + parentEntities = []; + } + + let filterQuery = { + "id" : entityId + }; + + let entityDetails = await locationSearch(filterQuery); + if ( !entityDetails.success ) { + return parentEntities; + } else { + + let entityData = entityDetails.data[0]; + if ( iteration > 0 ) parentEntities.push(entityData); + if ( entityData.parentId ) { + iteration = iteration + 1; + entityId = entityData.parentId; + await getParentEntities(entityId, iteration, parentEntities); + } + } + + return parentEntities; + +} + +/** + * update user consent for sharing the PII. + * @method + * @name setUserConsent + * @param {String} token - user token + * @returns {Object} consentData - consent data. +*/ + +const setUserConsent = function ( token, consentData ) { + return new Promise(async (resolve, reject) => { + try { + + let url = userServiceUrl + constants.endpoints.USER_CONSENT_API; + + const options = { + headers : { + "content-type": "application/json", + "x-authenticated-user-token" : token + }, + body: JSON.stringify(consentData) + }; + + request.post(url,options,requestCallback); + + function requestCallback(err, data) { + + let result = { + success : true + }; + + if (err) { + result.success = false; + } else { + + let response = JSON.parse(data.body); + if( response.responseCode === httpStatusCode['http_responsecode_ok'].message ) { + result["data"] = response; + } else { + result["message"] = response; + result.success = false; + } + + } + return resolve(result); + } + setTimeout(function () { + return resolve (result = { + success : false + }); + }, constants.common.SERVER_TIME_OUT); + + } catch (error) { + return reject(error); + } + }) +} + module.exports = { - profile : profile + profile : profile, + locationSearch : locationSearch, + orgSchoolSearch :orgSchoolSearch, + getSubEntitiesBasedOnEntityType : getSubEntitiesBasedOnEntityType, + getParentEntities : getParentEntities, + setUserConsent : setUserConsent } diff --git a/healthCheck/elastic-search.js b/healthCheck/elastic-search.js deleted file mode 100644 index 288216cd..00000000 --- a/healthCheck/elastic-search.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * name : elastic-search.js. - * author : Aman Karki. - * created-date : 02-Feb-2021. - * Description : Elastic Search health check. -*/ - -// Dependencies - -const { Client : esClient } = require('@elastic/elasticsearch'); - -function health_check() { - return new Promise( async (resolve,reject) => { - - const elasticSearchClient = new esClient({ - node: process.env.ELASTICSEARCH_HOST_URL - }); - - elasticSearchClient.ping({ - }, function (error) { - if (error) { - return resolve(false); - } else { - return resolve(true); - } - }); - }) -} - -module.exports = { - health_check : health_check -} \ No newline at end of file diff --git a/healthCheck/health-check.js b/healthCheck/health-check.js index 6ebf8693..0c3af31e 100644 --- a/healthCheck/health-check.js +++ b/healthCheck/health-check.js @@ -10,7 +10,6 @@ const { v1 : uuidv1 } = require('uuid'); const assessmentHealthCheck = require("./assessments"); const mongodbHealthCheck = require("./mongodb"); -const elasticSearchHealthCheck = require("./elastic-search"); const obj = { MONGO_DB: { @@ -23,11 +22,6 @@ const obj = { FAILED_CODE: 'ASSESSMENT_SERVICE_HEALTH_FAILED', FAILED_MESSAGE: 'ASSessment service is not healthy' }, - ELASTIC_SEARCH : { - NAME: 'ElasticSearch.db', - FAILED_CODE: 'ELASTIC_SEARCH_HEALTH_FAILED', - FAILED_MESSAGE: 'Elastic search is not connected' - }, NAME: 'KendraServiceHealthCheck', API_VERSION: '1.0' } @@ -37,11 +31,9 @@ let health_check = async function(req,res) { let checks = []; let mongodbConnection = await mongodbHealthCheck.health_check(); let assessmentServiceStatus = await assessmentHealthCheck.health_check(); - let elasticSearchConnection = await elasticSearchHealthCheck.health_check(); checks.push(checkResult("MONGO_DB",mongodbConnection)); checks.push(checkResult("ASSESSMENT_SERVICE",assessmentServiceStatus)); - checks.push(checkResult("ELASTIC_SEARCH",elasticSearchConnection)); let checkServices = checks.filter( check => check.healthy === false); diff --git a/healthCheck/mongodb.js b/healthCheck/mongodb.js index 76fc2576..9b8e196c 100644 --- a/healthCheck/mongodb.js +++ b/healthCheck/mongodb.js @@ -17,6 +17,7 @@ function health_check() { return resolve(false) }); db.once("open", function() { + db.close(function(){}); return resolve(true); }); }) diff --git a/migrations/entity-generalisation-5.0/README.md b/migrations/entity-generalisation-5.0/README.md new file mode 100644 index 00000000..74a88505 --- /dev/null +++ b/migrations/entity-generalisation-5.0/README.md @@ -0,0 +1,7 @@ +## Migrations for entity generalization +Steps to run the migration files +- Navigate to migrations/entity-generalisation-5.0/ folder +- The first script which convert mongoId to locationId. To run the script use the below command, + > node convertEntityIdToLocationId.js +- Run the final script which add entity hierarchy to the existing observationSubmissions & project collections. + > node addEntityHierarchyInProjectsAndSubmissions.js \ No newline at end of file diff --git a/migrations/entity-generalisation-5.0/addEntityHierarchyInProjectsAndSubmissions.js b/migrations/entity-generalisation-5.0/addEntityHierarchyInProjectsAndSubmissions.js new file mode 100644 index 00000000..cd6dd756 --- /dev/null +++ b/migrations/entity-generalisation-5.0/addEntityHierarchyInProjectsAndSubmissions.js @@ -0,0 +1,172 @@ +let _ = require("lodash"); +const request = require('request'); +const path = require("path"); +let rootPath = path.join(__dirname, '../../') +let endPoints = require(rootPath+'/generics/constants/endpoints') +require('dotenv').config({ path: rootPath+'/.env' }) +let MongoClient = require("mongodb").MongoClient; +let endPoint = endPoints.GET_LOCATION_DATA; +let userServiceUrl = process.env.USER_SERVICE_URL; +let mongoUrl = process.env.MONGODB_URL; +let dbName = mongoUrl.split("/").pop(); +let url = mongoUrl.split(dbName)[0]; + +(async () => { + + let connection = await MongoClient.connect(url, { useNewUrlParser: true }); + let db = connection.db(dbName); + try { + + let collections = { + "observationSubmissions" : { + "query" : { + }, + "projection": { + entityId:1,_id:0 + } + }, + "projects" : { + "query" : { + entityId:{$exists:true} + }, + "projection": { + entityId:1,_id:0 + } + } + } + + let entityIds = []; + //loop project & obeservationSubmission collections and get entityId + for ( let eachModel in collections ) { + //fetch each current collection and get entityId + let collectionDocs = await db.collection(eachModel).find(collections[eachModel].query).project(collections[eachModel].projection).toArray(); + collectionDocs.forEach( eachDoc => { + entityIds.push(eachDoc.entityId.toString()); + }) + + //eliminate duplication of ids + entityIds = _.uniq(entityIds); + } + + let chunkOfEntityIds = _.chunk(entityIds, 5); + //loop entity + for ( let chunkPointer = 0; chunkPointer < chunkOfEntityIds.length; chunkPointer++ ) { + //chunk of entity ids + let entityIds = chunkOfEntityIds[chunkPointer]; + + for ( let entityIdpointer = 0 ; entityIdpointer < entityIds.length; entityIdpointer++ ) { + let parentData = await getParentEntities(entityIds[entityIdpointer]); + + for ( let currentModel in collections ) { + + let findQuery = { + "entityId": entityIds[entityIdpointer] + } + + let updateObject = { + "$set" : {} + }; + + //update query for project and observationSubmission + updateObject["$set"]["entityInformation.hierarchy"] = parentData; + //update the projects & observationSubmission collection + if ( updateObject && Object.keys(updateObject["$set"]).length > 0 ) { + let updateCollection = await db.collection(currentModel).updateMany(findQuery,updateObject); + } + } + + } + + } + console.log("observationsSubmissions & projects collections updated") + + + // call location search api + function locationSearch ( filterData ) { + return new Promise(async (resolve, reject) => { + try { + + let bodyData={}; + bodyData["request"] = {}; + bodyData["request"]["filters"] = filterData; + const url = + userServiceUrl + endPoint; + const options = { + headers : { + "content-type": "application/json" + }, + json : bodyData + }; + + request.post(url,options,requestCallback); + let result = { + success : true + }; + + function requestCallback(err, data) { + if (err) { + result.success = false; + } else { + let response = data.body; + + if( response.responseCode === "OK" && + response.result && + response.result.response && + response.result.response.length > 0 + ) { + result["data"] = response.result.response; + result["count"] = response.result.count; + } else { + result.success = false; + } + } + return resolve(result); + } + + setTimeout(function () { + return resolve (result = { + success : false + }); + }, 5000); + + } catch (error) { + return reject(error); + } + }) + } + + //get parent data of a given entity + async function getParentEntities( entityId, iteration = 0, parentEntities ) { + + if ( iteration == 0 ) { + parentEntities = []; + } + + let filterQuery = { + "id" : entityId + }; + + let entityDetails = await locationSearch(filterQuery); + if ( !entityDetails.success ) { + return parentEntities; + } else { + + let entityData = entityDetails.data[0]; + if ( iteration > 0 ) parentEntities.push(entityData); + if ( entityData.parentId ) { + iteration = iteration + 1; + entityId = entityData.parentId; + await getParentEntities(entityId, iteration, parentEntities); + } + } + + return parentEntities; + + } + + connection.close(); + } + catch (error) { + console.log(error) + } +})().catch(err => console.error(err)); diff --git a/migrations/entity-generalisation-5.0/convertEntityIdToLocationId.js b/migrations/entity-generalisation-5.0/convertEntityIdToLocationId.js new file mode 100644 index 00000000..3773d265 --- /dev/null +++ b/migrations/entity-generalisation-5.0/convertEntityIdToLocationId.js @@ -0,0 +1,296 @@ +/** + * name : entityToLocationId.js + * author : Priyanka Pradeep + * created-date : 02-Sep-2022 + * Description : Migration script for convert entity to locationId. + */ + +const path = require("path"); +let rootPath = path.join(__dirname, '../../') +require('dotenv').config({ path: rootPath+'/.env' }) +let _ = require("lodash"); +let mongoUrl = process.env.MONGODB_URL; +let dbName = mongoUrl.split("/").pop(); +let url = mongoUrl.split(dbName)[0]; +var MongoClient = require('mongodb').MongoClient; +var ObjectId = require('mongodb').ObjectID; + +(async () => { + + let connection = await MongoClient.connect(url, { useNewUrlParser: true }); + let db = connection.db(dbName); + console.log(dbName,"dbName") + try { + + let collections = { + "observationSubmissions" : { + "query" : { + }, + "projection": { + entityId:1,_id:0 + } + }, + "projects" : { + "query" : { + entityId:{$exists:true} + }, + "projection": { + entityId:1,_id:0 + } + } + } + + let entityIds = []; + //loop project & obeservationSubmission collections and get entityId + for ( let eachModel in collections ) { + //fetch each current collection and get entityId + let collectionDocs = await db.collection(eachModel).find(collections[eachModel].query).project(collections[eachModel].projection).toArray(); + collectionDocs.forEach( eachDoc => { + entityIds.push(eachDoc.entityId.toString()); + }) + + //eliminate duplication of ids + entityIds = _.uniq(entityIds); + } + + let chunkOfEntityIds = _.chunk(entityIds, 5); + //loop entity + for ( let chunkPointer = 0; chunkPointer < chunkOfEntityIds.length; chunkPointer++ ) { + + let entityIds = chunkOfEntityIds[chunkPointer]; + //convert to obejctId + let entityIdToObjectId = await convertToObjectId(entityIds); + //fetch entity documents + let entityDocuments = await db.collection('entities').find({_id:{ $in : entityIdToObjectId}}).project({ + "registryDetails.locationId":1, + "registryDetails.code":1, + }).toArray(); + + //loop the entity documents + for ( let entityPointer = 0; entityPointer < entityDocuments.length; entityPointer++ ) { + + let currentEntity = entityDocuments[entityPointer]; + //loop all the collections + for ( let currentModel in collections ) { + + let findQuery = { + "entityId": currentEntity._id + } + + let updateObject = { + "$set" : {} + }; + + //update query for project + if ( currentModel == "projects") { + updateObject["$set"]["entityId"] = currentEntity.registryDetails.locationId; + updateObject["$set"]["entityInformation._id"] = currentEntity.registryDetails.locationId; + updateObject["$set"]["entityInformation.externalId"] = currentEntity.registryDetails.code; + + } else if ( currentModel == "observationSubmissions" ) { + updateObject["$set"]["entityId"] = currentEntity.registryDetails.locationId; + updateObject["$set"]["entityExternalId"] = currentEntity.registryDetails.code; + updateObject["$set"]["entityInformation.externalId"] = currentEntity.registryDetails.code; + } + + //update the projects & observationSubmission collection + if ( updateObject && Object.keys(updateObject["$set"]).length > 0 ) { + let updateCollection = await db.collection(currentModel).updateMany(findQuery,updateObject); + } + } + } + } + + console.log("observations & projects collections updated") + + //update programs, solutions & observation collections + let otherCollections = { + "programs" : { + "query" : { + 'scope.entities':{$exists:true, $ne : []} + }, + "projection": { + 'scope.entities':1,_id:1 + } + }, + "observations" : { + "query" : { + entities:{$exists:true, $ne: []} + }, + "projection": { + entities:1,_id:1 + } + }, + "solutions.1" : { + "query" : { + 'scope.entities':{$exists:true, $ne : []} + }, + "projection": { + 'scope.entities':1,_id:1 + }, + "field": "scope.entities" + }, + "solutions.2" : { + "query" : { + 'entities':{$exists:true, $ne : []} + }, + "projection": { + entities:1,_id:1 + }, + "field": "entities" + } + + } + + //loop observation, solutions and programs collections + for ( let eachCollection in otherCollections ) { + + let getName = eachCollection.split("."); + let collectionName = getName[0]; + let otherCollectionDocs = await db.collection(collectionName).find(otherCollections[eachCollection].query).project(otherCollections[eachCollection].projection).toArray(); + let chunkOfOtherCollection = _.chunk(otherCollectionDocs, 10); + + for (let pointerToOtherCollection = 0; pointerToOtherCollection < chunkOfOtherCollection.length; pointerToOtherCollection++ ) { + let eachChunkCollections = chunkOfOtherCollection[pointerToOtherCollection]; + //loop each collection + for ( let pointerToChunk = 0; pointerToChunk < eachChunkCollections.length; pointerToChunk++ ) { + + let eachDocument = eachChunkCollections[pointerToChunk]; + let query = { + _id: eachDocument._id + } + + let updateQuery = { + "$set" : {} + } + + let eachEntityIds = []; + if ( eachCollection == "observations") { + eachEntityIds = eachDocument.entities; + } else if ( eachCollection == "programs" ) { + eachEntityIds = eachDocument.scope.entities; + } else if ( eachCollection == "solutions.1" && otherCollections[eachCollection].field == "scope.entities") { + eachEntityIds = eachDocument.scope.entities; + } else if ( eachCollection == "solutions.2" && otherCollections[eachCollection].field == "entities") { + eachEntityIds = eachDocument.entities; + } + + let entityIdToObjectId = await convertToObjectId(eachEntityIds); + //fetch entity + let entityDocs = await db.collection('entities').find({_id:{ $in : entityIdToObjectId}, registryDetails:{$exists:true}}).project({ + "registryDetails.locationId":1, + "registryDetails.code":1, + }).toArray(); + + let newEntityIds = []; + if ( entityDocs && entityDocs.length > 0 ) { + entityDocs.forEach( entity => { + newEntityIds.push(entity.registryDetails.locationId); + }) + } + + if ( eachCollection == "observations" ) { + updateQuery["$set"] = { + "entities" : newEntityIds + } + } else if ( eachCollection == "programs") { + updateQuery["$set"] = { + "scope.entities" : newEntityIds + } + } else if ( eachCollection == "solutions.1" && otherCollections[eachCollection].field == "scope.entities") { + updateQuery["$set"] = { + "scope.entities" : newEntityIds + } + } else if ( eachCollection == "solutions.2" && otherCollections[eachCollection].field == "entities") { + updateQuery["$set"] = { + "entities" : newEntityIds + } + } + + //update collection + if ( updateQuery && Object.keys(updateQuery["$set"]).length > 0 ) { + let updateCollections = await db.collection(collectionName).updateMany(query,updateQuery); + } + } + } + + console.log(eachCollection,"updated") + } + + //update observationInformation in project task + let projectDocument = await db.collection('projects').find({ + "tasks.type" : "observation", + "tasks.observationInformation":{$exists:true} + }).project({_id:1}).toArray(); + + let chunkOfProjectDocument = _.chunk(projectDocument, 10); + let projectIds; + + for (let pointerToProject = 0; pointerToProject < chunkOfProjectDocument.length; pointerToProject++) { + + projectIds = await chunkOfProjectDocument[pointerToProject].map( + projectDoc => { + return projectDoc._id; + } + ); + + let projectDocuments = await db.collection('projects').find({ + _id: { $in : projectIds } + }).project({ + "_id": 1, + "tasks._id" : 1, + "tasks.observationInformation" : 1 + }).toArray(); + + await Promise.all( + projectDocuments.map(async eachProject => { + + let tasks = eachProject.tasks; + for ( let taskCounter = 0; taskCounter < tasks.length; taskCounter++) { + let currentTask = tasks[taskCounter]; + if ( currentTask.observationInformation ) { + + let observationInformation = currentTask.observationInformation; + + let taskUpdateObject = { + "$set" : {} + }; + + if ( observationInformation.entityId ) { + + let entityDocuments = await db.collection('entities').find({_id: ObjectId(observationInformation.entityId) }).project({ + "registryDetails.locationId":1 + }).toArray(); + + if ( entityDocuments.length > 0 ) { + observationInformation.entityId = entityDocuments[0].registryDetails.locationId; + } + } + + taskUpdateObject["$set"]["tasks.$.observationInformation"] = observationInformation; + + if ( taskUpdateObject["$set"] && Object.keys(taskUpdateObject["$set"]).length > 0 ) { + + let updateTaskData = await db.collection('projects').findOneAndUpdate({ + "_id": eachProject._id, + "tasks._id": currentTask._id + }, taskUpdateObject); + } + } + } + })) + + } + console.log("project task updated") + console.log("completed") + + function convertToObjectId(ids){ + return ids.map(id => ObjectId(id)); + } + + connection.close(); + } + catch (error) { + console.log(error) + } +})().catch(err => console.error(err)); diff --git a/migrations/programs-startDate-EndDate-6.0/README.md b/migrations/programs-startDate-EndDate-6.0/README.md new file mode 100644 index 00000000..48f65eb8 --- /dev/null +++ b/migrations/programs-startDate-EndDate-6.0/README.md @@ -0,0 +1,5 @@ +## Migrations for ED-523 program startDate and endDate +Steps to run the migration files +- Navigate to migrations/programs-startDate-EndDate-6.0/ folder +- Run the script which will add startDate and endDate to programs. + > node setProgramsStartDateAndEndDate.js \ No newline at end of file diff --git a/migrations/programs-startDate-EndDate-6.0/setProgramsStartDateAndEndDate.js b/migrations/programs-startDate-EndDate-6.0/setProgramsStartDateAndEndDate.js new file mode 100644 index 00000000..51afbbbe --- /dev/null +++ b/migrations/programs-startDate-EndDate-6.0/setProgramsStartDateAndEndDate.js @@ -0,0 +1,67 @@ +let _ = require("lodash"); +const path = require("path"); +let MongoClient = require("mongodb").MongoClient; +let mongoUrl = process.env.MONGODB_URL; +let dbName = mongoUrl.split("/").pop(); +let url = mongoUrl.split(dbName)[0]; +var fs = require('fs'); + +(async () => { + + let connection = await MongoClient.connect(url, { useNewUrlParser: true }); + let db = connection.db(dbName); + let updatedProgramIds = []; + try { + console.log("-----------------------Started----------------------"); + // get all active programs with createdAt value + let collectionDocs = await db.collection("programs").find({ + status: "active", + createdAt: {"$exists": true}, + }).project({_id:1,createdAt:1}).toArray(); + + //reduce array to small chunks + let chunkOfprogramsDetails = _.chunk(collectionDocs, 50); + + //loop each chunk + for ( let chunkPointer = 0; chunkPointer < chunkOfprogramsDetails.length; chunkPointer++ ) { + + let currentChunk = chunkOfprogramsDetails[chunkPointer]; + //loop each program details + for ( let currentChunkIndex = 0 ; currentChunkIndex < currentChunk.length; currentChunkIndex++ ) { + + let monthToAdd = 12; // ---------------------set to one year ----------------------------- + let id = currentChunk[currentChunkIndex]._id; + const startDate = currentChunk[currentChunkIndex].createdAt + const endDate = new Date(currentChunk[currentChunkIndex].createdAt); + endDate.setFullYear( endDate.getFullYear(), endDate.getMonth() + monthToAdd ); + + //update programs + await db.collection("programs").findOneAndUpdate( + { '_id': id }, + { $set: { startDate: startDate, endDate : endDate} } + ); + console.log("program Updated : ",id) + updatedProgramIds.push(id) + } + } + //write updated program ids to file + fs.writeFile( + 'updatedProgramIds.json', + + JSON.stringify(updatedProgramIds), + + function (err) { + if (err) { + console.error('Crap happens'); + } + } + ); + console.log("-----------------------Finished----------------------"); + console.log(" finished programs updation of startDate and endDate"); + console.log("-----------------------------------------------------"); + connection.close(); + } + catch (error) { + console.log(error) + } +})().catch(err => console.error(err)); diff --git a/models/certificateBaseTemplates.js b/models/certificateBaseTemplates.js new file mode 100644 index 00000000..2901f51f --- /dev/null +++ b/models/certificateBaseTemplates.js @@ -0,0 +1,16 @@ +module.exports = { + name: "certificateBaseTemplates", + schema: { + code: { + type : String, + required : true + }, + name: { + type : String, + required : true + }, + url: { + type : String, + } + } +}; \ No newline at end of file diff --git a/models/certificateTemplates.js b/models/certificateTemplates.js new file mode 100644 index 00000000..929b8542 --- /dev/null +++ b/models/certificateTemplates.js @@ -0,0 +1,30 @@ +module.exports = { + name: "certificateTemplates", + schema: { + templateUrl: String, + issuer: { + type : Object, + required : true + }, + status: { + type : String, + required : true, + default : "ACTIVE" + }, + solutionId: { + type : "ObjectId", + index : true, + unique : true + }, + programId: { + type : "ObjectId", + index : true, + required : true + }, + criteria: { + type : Object, + required : true + }, + baseTemplateId : "ObjectId" + } +}; \ No newline at end of file diff --git a/models/programUsers.js b/models/programUsers.js new file mode 100644 index 00000000..dbcbcd91 --- /dev/null +++ b/models/programUsers.js @@ -0,0 +1,38 @@ +module.exports = { + name: "programUsers", + schema: { + programId: { + type : "ObjectId", + required: true, + index: true + }, + userId: { + type: String, + required: true, + index: true + }, + resourcesStarted: { + type: Boolean, + index: true, + default: false + }, + userProfile: { + type : Object, + required: true + }, + userRoleInformation: Object, + appInformation: Object, + consentShared: { + type: Boolean, + default: false + } + }, + compoundIndex: [ + { + "name" :{ userId: 1, programId: 1 }, + "indexType" : { unique: true } + } + ] +}; + + \ No newline at end of file diff --git a/models/programs.js b/models/programs.js index 32a08728..cabd3b92 100644 --- a/models/programs.js +++ b/models/programs.js @@ -2,31 +2,45 @@ module.exports = { name: "programs", schema: { externalId: String, - name: String, - description: String, + name: { + type : String, + index : true + }, + description: { + type : String, + index : true + }, owner: String, createdBy: String, updatedBy: String, - status: String, + status: { + type : String, + index : true + }, + startDate:{ + type: Date, + index: true, + require:true + }, + endDate: { + type : Date, + index : true, + require: true + }, resourceType: [String], language: [String], keywords: [String], concepts: ["json"], - createdFor: [String], imageCompression: {}, components: ["json"], components: ["json"], isAPrivateProgram : { default : false, - type : Boolean - }, - rootOrganisations : { - type : [String], - default : [] + type : Boolean, + index : true }, scope : { entityType : String, - entityTypeId : "ObjectId", entities : { type : Array, index : true @@ -41,8 +55,16 @@ module.exports = { }, isDeleted: { default : false, - type : Boolean - } + type : Boolean, + index : true + }, + requestForPIIConsent: Boolean, + metaInformation: Object, + rootOrganisations : { + type : Array, + require : true + }, + createdFor : Array } }; \ No newline at end of file diff --git a/models/solutions.js b/models/solutions.js index 62d4768c..fe162fa5 100644 --- a/models/solutions.js +++ b/models/solutions.js @@ -3,21 +3,25 @@ module.exports = { schema: { externalId: String, isReusable: Boolean, - name: String, - description: String, + name: { + type : String, + index : true + }, + description: { + type : String, + index : true + }, author: String, parentSolutionId: "ObjectId", resourceType: Array, language: Array, keywords: Array, concepts: Array, - createdFor: Array, scoringSystem: String, levelToScoreMapping: Object, themes: Array, flattenedThemes : Array, questionSequenceByEcm: Object, - entityTypeId: "ObjectId", entityType: String, type: String, subType: String, @@ -28,7 +32,10 @@ module.exports = { programDescription: String, entityProfileFieldsPerEntityTypes: Object, startDate: Date, - endDate: Date, + endDate: { + type : Date, + index : true + }, status: String, evidenceMethods: Object, sections: Object, @@ -59,17 +66,13 @@ module.exports = { }, isDeleted: { default : false, - type : Boolean - }, - rootOrganisations : { - type : [String], - default : [] + type : Boolean, + index : true }, project : Object, referenceFrom : String, scope : { entityType : String, - entityTypeId : "ObjectId", entities : { type : Array, index : true @@ -86,6 +89,19 @@ module.exports = { default : "Domains", type : String }, - criteriaLevelReport : Boolean + criteriaLevelReport : Boolean, + license:Object, + link: { + type : String, + index : true + }, + minNoOfSubmissionsRequired: { + type: Number, + default: 1 + }, + reportInformation : Object, + certificateTemplateId : "ObjectId", + rootOrganisations : Array, + createdFor : Array } }; \ No newline at end of file diff --git a/models/user-roles.js b/models/user-roles.js index bf9dce95..455f3ca7 100644 --- a/models/user-roles.js +++ b/models/user-roles.js @@ -10,7 +10,9 @@ module.exports = { schema: { code: { type: String, - required: true + required: true, + index: true, + unique: true }, title: { type: String, diff --git a/module/admin/helper.js b/module/admin/helper.js new file mode 100644 index 00000000..3cb1271d --- /dev/null +++ b/module/admin/helper.js @@ -0,0 +1,280 @@ +/** + * name : admin/helper.js + * author : Priyanka Pradeep + * created-date : 23-09-2022 + * Description : All admin related helper functions. + */ + +//Dependencies + +/** + * adminHelper + * @class +*/ + +module.exports = class adminHelper { + + /** + * List of data based on collection. + * @method + * @name list + * @param {Object} filterQueryObject - filter query data. + * @param {Object} [projection = {}] - projected data. + * @returns {Promise} returns a promise. + */ + + static list( + collection, + query = "all", + fields = "all", + skipFields = "none", + limitingValue = 100, + skippingValue = 0, + sortedData = "" + ) { + return new Promise(async (resolve, reject) => { + try { + + let queryObject = {}; + + if (query != "all") { + queryObject = query; + } + + let projectionObject = {}; + + if (fields != "all") { + + fields.forEach(element => { + projectionObject[element] = 1; + }); + } + + if (skipFields != "none") { + skipFields.forEach(element => { + projectionObject[element] = 0; + }); + } + + let mongoDBDocuments; + + if( sortedData !== "" ) { + + mongoDBDocuments = await database.getCollection(collection) + .find(queryObject) + .project(projectionObject) + .sort(sortedData) + .limit(limitingValue) + .toArray(); + + } else { + + mongoDBDocuments = await database.getCollection(collection) + .find(queryObject) + .project(projectionObject) + .skip(skippingValue) + .limit(limitingValue) + .toArray(); + } + // finding document count from db. We can't get it from result array length because a limiting value is passed + let docCount = await database.getCollection(collection) + .find(queryObject).count(); + + return resolve({ + success: true, + message: constants.apiResponses.DATA_FETCHED_SUCCESSFULLY, + data: mongoDBDocuments, + count: docCount + }); + + } catch (error) { + return resolve({ + success: false, + message: error.message, + data: false + }); + } + }) + + + } + + + /** + * List of data based on collection + * @method + * @name dbFind + * @param {Object} reqBody - request body + * @returns {Object} - collection details. + */ + + static dbFind( collection, reqBody ) { + return new Promise(async (resolve, reject) => { + try { + + if ( reqBody.mongoIdKeys ) { + reqBody.query = await this.convertStringToObjectIdInQuery(reqBody.query, reqBody.mongoIdKeys); + } + + let mongoDBDocuments = await this.list( + collection, + reqBody.query, + reqBody.projection ? reqBody.projection : [], + "none", + reqBody.limit ? reqBody.limit : 100, + reqBody.skip ? reqBody.skip : 0 + + ); + + return resolve({ + message : constants.apiResponses.DATA_FETCHED_SUCCESSFULLY, + success : true, + result: mongoDBDocuments.data ? mongoDBDocuments.data : [], + count: mongoDBDocuments.count ? mongoDBDocuments.count : 0 + }); + + } catch (error) { + return resolve({ + success: false, + message: error.message, + data: false + }); + } + }) + } + + // /** + // * Delete the data from collection + // * @method + // * @name dbDelete + // * @param {Object} reqBody - request body + // * @returns {Object} - collection details. + // */ + + // static dbDelete( collection, reqBody ) { + // return new Promise(async (resolve, reject) => { + // try { + + // if ( reqBody.mongoIdKeys ) { + // reqBody.query = await this.convertStringToObjectIdInQuery(reqBody.query, reqBody.mongoIdKeys); + // } + + // let deleteData = await database.getCollection(collection).deleteMany(reqBody.query); + + // return resolve({ + // message : constants.apiResponses.DATA_DELETED_SUCCESSFULLY, + // success : true + // }); + + // } catch (error) { + // return resolve({ + // success: false, + // message: error.message, + // data: false + // }); + // } + // }) + // } + + /** + * Update the data in any model + * @method + * @name dbUpdate + * @param {Object} reqBody - request body + * @returns {Object} - collection details. + */ + + static dbUpdate( collection, reqBody ) { + return new Promise(async (resolve, reject) => { + try { + + if ( reqBody.mongoIdKeys ) { + reqBody.query = await this.convertStringToObjectIdInQuery(reqBody.findQuery, reqBody.mongoIdKeys); + } + + let updateData = await database.getCollection(collection).updateMany(reqBody.findQuery, reqBody.updateQuery); + + return resolve({ + message : constants.apiResponses.DATA_UPDATED_SUCCESSFULLY, + success : true + }); + + } catch (error) { + return resolve({ + success: false, + message: error.message, + data: false + }); + } + }) + } + + /** + * Create a new mongodb record + * @method + * @name dbCreate + * @param {Object} reqBody - request body + * @returns {Object} - collection details. + */ + + static dbCreate( collection, reqBody ) { + return new Promise(async (resolve, reject) => { + try { + + let insertData = await database.getCollection(collection).create(reqBody); + + if( !insertData._id ) { + throw { + message : constants.apiResponses.FAILED_TO_CREATE_RECORD + } + } + + return resolve({ + message : constants.apiResponses.DATA_CREATED_SUCCESSFULLY, + success : true, + result : { + _id : insertData._id + } + }); + + } catch (error) { + return resolve({ + success: false, + message: error.message, + data: false + }); + } + }) + } + + /** + * Convert String to ObjectIds inside Query. + * @method + * @name convertStringToObjectIdInQuery + * @returns {Array} Query. + */ + + static convertStringToObjectIdInQuery(query, mongoIdKeys) { + + for ( let pointerToArray = 0; pointerToArray < mongoIdKeys.length; pointerToArray++ ) { + let eachKey = mongoIdKeys[pointerToArray]; + let currentQuery = query[eachKey]; + + if ( typeof(currentQuery) === "string") { + query[eachKey] = gen.utils.convertStringToObjectId(currentQuery); + } else if ( typeof(currentQuery) === "object") { + + let nestedKey = Object.keys(query[eachKey]); + if ( nestedKey ) { + let convertedIds = []; + nestedKey = nestedKey[0]; + query[eachKey][nestedKey] = gen.utils.arrayIdsTobjectIds(currentQuery[nestedKey]); + } + } + } + + return query; + } + +} + diff --git a/module/admin/validator/v1.js b/module/admin/validator/v1.js new file mode 100644 index 00000000..c19311b0 --- /dev/null +++ b/module/admin/validator/v1.js @@ -0,0 +1,34 @@ +/** + * name : v1.js + * author : Priyanka Pradeep + * created-date : 25-Sep-2022 + * Description : Admin validation. + */ + +module.exports = (req) => { + + let adminValidator = { + + dbFind : function () { + req.checkParams("_id").exists().withMessage("required mongodb collection name"); + req.checkBody("query").exists().withMessage("required mongoDB find query"); + }, + dbDelete : function () { + req.checkParams("_id").exists().withMessage("required mongodb collection name"); + req.checkBody("query").exists().withMessage("required mongoDB find query"); + }, + dbUpdate : function () { + req.checkParams("_id").exists().withMessage("required mongodb collection name"); + req.checkBody("findQuery").exists().withMessage("required mongoDB find query"); + req.checkBody("updateQuery").exists().withMessage("required mongoDB update query"); + }, + dbCreate : function () { + req.checkParams("_id").exists().withMessage("required mongodb collection name"); + }, + } + + if (adminValidator[req.params.method]) { + adminValidator[req.params.method](); + } + +}; \ No newline at end of file diff --git a/module/app-releases/helper.js b/module/app-releases/helper.js index 548b899e..6d14546a 100644 --- a/module/app-releases/helper.js +++ b/module/app-releases/helper.js @@ -14,281 +14,6 @@ let sessionHelpers = require(ROOT_PATH+"/generics/helpers/sessions"); module.exports = class VersionHelper { - /** - * List of version data. - * @method - * @name list - * @param {Object} filterQueryObject - filter query data. - * @param {Object} [projection = {}] - projected data. - * @returns {Promise} returns a promise. - */ - - static list( filterQueryObject = {} , fields = "all" ) { - return new Promise(async (resolve, reject) => { - try { - - let projection = {}; - - if (fields != "all") { - fields.forEach(element => { - projection[element] = 1; - }); - } - - let versionData = - await database.models.appReleases.find( - filterQueryObject, - projection - ).lean(); - - return resolve(versionData); - - } catch (error) { - return reject(error); - } - }) - - - } - - /** - * Upload app release data. - * @method - * @name upload - * @param {Object} data - app update data. - * @param {String} data.appName - app name of notification. - * @param {String} data.title - title of notification. - * @param {String} data.text - text of notification. - * @param {String} data.version - version of the app. - * @param {String} data.status - status of the update notification. - * @param {String} data.os - device os. - * @param {String} data.releaseType - release type versions - * @returns {Promise} returns a promise. - */ - - static upload( data,userId ) { - return new Promise(async (resolve, reject) => { - try { - - let uploadVersion = - await this.create( - data, - userId - ); - - return resolve(uploadVersion); - - } catch (error) { - return reject(error); - } - }) - } - - /** - * Create app release data. - * @method - * @name create - * @returns {Object} create version data. - */ - - static create( data,userId ) { - return new Promise( async (resolve,reject)=>{ - try { - - // let sessionPath = - // `${constants.common.ALL_APP_VERSION}-${gen.utils.lowerCase(data.appName)}-${gen.utils.lowerCase(data.os)}`; - - data.appName = gen.utils.lowerCase(data.appName); - data.os = gen.utils.lowerCase(data.os); - data.createdBy = userId; - data.updatedBy = userId; - - let versionRelease = await database.models.appReleases.findOneAndUpdate({ - appName : data.appName, - os : data.os, - version : data.version - },{ $set : data },{ upsert : true, new: true }).lean(); - - if( versionRelease._id ) { - - if( versionRelease.status === constants.common.ACTIVE ) { - - await database.models.appReleases.updateMany( - { - _id : { $ne : versionRelease._id }, - appName : data.appName, - os : data.os - }, - { - status : constants.common.IN_ACTIVE - } - ); - - // let versionData = _versionPayload(data); - // sessionHelpers.set(sessionPath,versionData); - } - data.message = constants.apiResponses.APP_VERSION_UPDATED; - } else { - data.message = constants.apiResponses.APP_VERSION_NOT_UPDATED; - } - - return resolve(data); - } catch(e) { - return reject(e); - } - }) - } - - /** - * Update app release data. - * @method - * @name update - * @param updateData - app version update data - * @returns {Object} updated version data. - */ - - static update( id,updateData ) { - return new Promise( async (resolve,reject)=>{ - try { - - let currentAppVersion = await this.list( - { - _id : id - },["_id","appName","os"] - ); - - if( !currentAppVersion[0] ) { - return resolve({ - status : httpStatusCode.bad_request.status, - message : constants.apiResponses.APP_VERSION_NOT_FOUND - }) - } - - // <- Dirty fix.Currently not required. - - // let sessionPath = - // `${constants.common.ALL_APP_VERSION}-${gen.utils.lowerCase(currentAppVersion[0].appName)}-${gen.utils.lowerCase(currentAppVersion[0].os)}`; - - if( updateData.status === constants.common.ACTIVE ) { - - await database.models.appReleases.findOneAndUpdate({ - _id : { $ne : currentAppVersion[0]._id }, - appName : gen.utils.lowerCase(currentAppVersion[0].appName), - os : currentAppVersion[0].os - },{ - $set : { - status : constants.common.IN_ACTIVE - } - }) - } - - let updateAppReleaseData = - await database.models.appReleases.findOneAndUpdate({ - _id : currentAppVersion[0]._id - },{ - $set : updateData - },{ new : true }).lean(); - - if( !updateAppReleaseData || !updateAppReleaseData._id ) { - return resolve({ - status : httpStatusCode.bad_request.status, - message : constants.apiResponses.APP_VERSION_NOT_UPDATED - }) - } - - // <- Dirty fix.Currently not required. - - // let versionData = _versionPayload(updateAppReleaseData); - // sessionHelpers.set(sessionPath,versionData); - - return resolve({ - message : constants.apiResponses.APP_VERSION_UPDATED, - result : updateAppReleaseData - }); - } catch(e) { - return reject(e); - } - }) - } - - /** - * List of all version data. - * @method - * @name versionDataList - * @param requestedData - requestedData - * @returns {Array} List of all app release version. - */ - - static versionDataList( filters = {} ) { - return new Promise( async (resolve,reject)=>{ - try { - - let queryObject = {}; - - if( filters.appName ) { - queryObject["appName"] = filters.appName; - } - - if( filters.os ) { - queryObject["os"] = filters.os; - } - - if( filters.releaseType ) { - queryObject["releaseType"] = filters.releaseType; - } - - if( filters.status ) { - queryObject["status"] = filters.status; - } - - let versionDataList = await this.list( - queryObject - ); - - if( versionDataList.length < 1 ) { - return resolve({ - status : httpStatusCode.bad_request.status, - message : constants.apiResponses.APP_VERSION_NOT_FOUND - }) - } - - return resolve({ - message : constants.apiResponses.APP_VERSION_LISTS, - result : versionDataList - }); - } catch(e) { - return reject(e); - } - }) -} } -/** - * Version payload data. - * @method - * @name _versionPayload - * @returns {Object} return version data. -*/ - -function _versionPayload(data) { - return { - is_read : false, - internal : true, - action : "versionUpdate", - appName : data.appName, - created_at : new Date(), - text : data.text, - title : data.title, - type : "Information", - payload : { - appVersion : data.version, - updateType : data.releaseType, - type : "appUpdate", - releaseNotes : data.releaseNotes, - os : data.os - }, - appType : data.appType - }; - -} diff --git a/module/app-releases/validator/v1.js b/module/app-releases/validator/v1.js index 828fd7ea..79b0c7fb 100644 --- a/module/app-releases/validator/v1.js +++ b/module/app-releases/validator/v1.js @@ -9,34 +9,6 @@ module.exports = (req) => { let appReleaseValidator = { - create : function () { - req.checkBody('appName').exists().withMessage("Name of the app is required"); - req.checkBody('version').exists().withMessage("Version for the app is required"); - req.checkBody('releaseType').exists().withMessage("App release type is required").isIn( - ["major","minor"] - ).withMessage("Release type should be either major or minor"); - req.checkBody('os').exists().withMessage("App os is required").isIn( - [ - "android", - "ios" - ] - ).withMessage("os should be android or ios"); - - req.checkBody('text').exists().withMessage("text is required"); - req.checkBody('title').exists().withMessage("App release title is required"); - req.checkBody('status').exists().withMessage("Status of app release is required"); - req.checkBody('releaseNotes').exists().withMessage("Release notes is required"); - req.checkBody('appType').exists().withMessage("App type is required").isIn( - [ - process.env.ASSESSMENT_APPLICATION_APP_TYPE, - process.env.IMPROVEMENT_PROJECT_APPLICATION_APP_TYPE - ] - ).withMessage(`App Type should be one of the following : ${process.env.ASSESSMENT_APPLICATION_APP_TYPE} or ${process.env.IMPROVEMENT_PROJECT_APPLICATION_APP_TYPE}`); - }, - update : function () { - req.checkParams('_id').exists().withMessage("required version id"); - } - } if ( appReleaseValidator[req.params.method] ) { diff --git a/module/apps/helper.js b/module/apps/helper.js index 20b2d41d..9751b37c 100644 --- a/module/apps/helper.js +++ b/module/apps/helper.js @@ -164,178 +164,5 @@ module.exports = class AppsHelper { }) } - - /** - * Create app details - * @method - * @name create - * @param {file} logo - logo of the app - * @param {Object} appDetails - details of the app - * @returns {String} - message. - */ - - static create(logo= {}, appDetails = {}) { - return new Promise(async (resolve, reject) => { - try { - - if (!Object.keys(logo).length) { - throw new Error(constants.apiResponses.LOGO_REQUIRED) - } - - if (!appDetails.name) { - throw new Error(constants.apiResponses.NAME_FIELD_REQUIRED) - } - - if (!appDetails.description) { - throw new Error(constants.apiResponses.DESCRIPTION_REQUIRED) - } - - if (!appDetails.displayName) { - throw new Error(constants.apiResponses.DiSPLAY_NAME_REQUIRED) - } - - if (!appDetails.playstoreLink) { - throw new Error(constants.apiResponses.PLAYSTORE_LINK_REQUIRED) - } - - let bucketName = ""; - if (process.env.CLOUD_STORAGE == constants.common.GOOGLE_CLOUD_SERVICE) { - bucketName = process.env.GCP_BUCKET_NAME - } - else if (process.env.CLOUD_STORAGE == constants.common.AWS_SERVICE) { - bucketName = process.env.AWS_BUCKET_NAME - } - else if (process.env.CLOUD_STORAGE == constants.common.AZURE_SERVICE) { - bucketName = process.env.AZURE_STORAGE_CONTAINER - } - - let filePath = constants.common.APPS_UPLOAD_FILE_PATH + appDetails.name + path.extname(logo.name); - - let uploadFile = await filesHelper.upload - ( - logo, - filePath, - bucketName, - process.env.CLOUD_STORAGE - ) - - if (uploadFile["name"] && uploadFile.name !== "") { - appDetails.logo = filePath; - } - - await database.models.apps.create( - { - name: appDetails.name, - displayName: appDetails.displayName, - description: appDetails.description, - playstoreLink: appDetails.playstoreLink, - appStoreLink: appDetails.appStoreLink ? appDetails.appStoreLink : "", - logo: appDetails.logo ? appDetails.logo : "", - createdAt: new Date(), - updatedAt: new Date(), - createdBy: appDetails.createdBy ? appDetails.createdBy : "SYSTEM", - updatedBy: appDetails.updatedBy ? appDetails.updatedBy : "SYSTEM", - status: appDetails.status ? appDetails.status : constants.common.ACTIVE, - isDeleted: appDetails.isDeleted - } - ); - - return resolve({ - success: true, - message: constants.apiResponses.APP_DETAILS_CREATED, - data: true - }); - - } catch (error) { - return resolve({ - success: false, - message: error.message, - data: false - }); - } - }) - } - - - /** - * Update app details - * @method - * @name update - * @param {String} name - app name - * @param {file} logo - logo of the app - * @param {Object} appDetails - details of the app to update - * @param {String} userId - userId - * @returns {String} - message. - */ - - static update(name= "", logo = {}, appDetails = {}) { - return new Promise(async (resolve, reject) => { - try { - - if(name == "") { - throw new Error(constants.apiResponses.APP_NAME_REQUIRED) - } - - if (!Object.keys(appDetails).length) { - throw new Error(constants.apiResponses.APP_DETAILS_REQUIRED) - } - - if (logo !== "") { - - let bucketName = ""; - if (process.env.CLOUD_STORAGE == constants.common.GOOGLE_CLOUD_SERVICE) { - bucketName = process.env.GCP_BUCKET_NAME - } - else if (process.env.CLOUD_STORAGE == constants.common.AWS_SERVICE) { - bucketName = process.env.AWS_BUCKET_NAME - } - else if (process.env.CLOUD_STORAGE == constants.common.AZURE_SERVICE) { - bucketName = process.env.AZURE_STORAGE_CONTAINER - } - - let filePath = constants.common.APPS_UPLOAD_FILE_PATH + name + path.extname(logo.name); - - let uploadFile = await filesHelper.upload - ( - logo, - filePath, - bucketName, - process.env.CLOUD_STORAGE - ) - - if (uploadFile["name"] && uploadFile.name !== "") { - appDetails.logo = filePath; - } - } - - let appDetailsToSet = {}; - - Object.keys(appDetails).forEach( key => { - appDetailsToSet[key] = appDetails[key] - }) - - await database.models.apps.updateOne( - { name: name }, - { - $set : appDetailsToSet - } - ); - - return resolve({ - success: true, - message: constants.apiResponses.APP_DETAILS_UPDATED, - data: true - }); - - } catch (error) { - return resolve({ - success: false, - message: error.message, - data: false - }); - } - }) - } - } diff --git a/module/apps/validator/v1.js b/module/apps/validator/v1.js index 7b5f1c13..2efd3008 100644 --- a/module/apps/validator/v1.js +++ b/module/apps/validator/v1.js @@ -4,19 +4,7 @@ module.exports = (req) => { getDetails: function () { req.checkParams('_id').exists().withMessage("required app name").notEmpty().withMessage("required app name"); - }, - - create: function () { - req.checkBody('name').exists().withMessage("required name field").notEmpty().withMessage("required name field"); - req.checkBody('displayName').exists().withMessage("required displayName field").notEmpty().withMessage("required name field"); - req.checkBody('description').exists().withMessage("required description field").notEmpty().withMessage("required name field"); - req.checkBody('playstoreLink').exists().withMessage("required playStoreLink field").notEmpty().withMessage("required name field"); - }, - - update: function () { - req.checkParams('_id').exists().withMessage("required app name").notEmpty().withMessage("required app name"); } - } if (appsValidator[req.params.method]) { diff --git a/module/certificateBaseTemplates/helper.js b/module/certificateBaseTemplates/helper.js new file mode 100644 index 00000000..38a8ff51 --- /dev/null +++ b/module/certificateBaseTemplates/helper.js @@ -0,0 +1,101 @@ +/** + * name : helper.js + * author : Vishnu + * created-date : 11-Jan-2023 + * Description : Certificate Template related helper functionality. +*/ +// dependencies +const certificateTemplateHelper = require(MODULES_BASE_PATH + "/certificateTemplates/helper"); + +/** + * CertificateBaseTemplatesHelper + * @class +*/ +module.exports = class CertificateBaseTemplatesHelper { + + /** + * Create certificate base template. + * @method create + * @name create + * @param {Object} data - certificate base template creation data. + * @returns {JSON} created certificate base template details. + */ + + static create( data, file, userId ) { + return new Promise(async (resolve, reject) => { + try { + let uploadFile = await certificateTemplateHelper.uploadToCloud( file, "", userId, false ); + if( !uploadFile.success ) { + throw ({ + message: constants.apiResponses.COULD_NOT_UPLOAD_CONTENT + }) + } + data.url = uploadFile.data.templateUrl; + let certificateTemplateCreated = + await database.models.certificateBaseTemplates.create( + data + ); + return resolve({ + message : constants.apiResponses.CERTIFICATE_BASE_TEMPLATE_ADDED, + data : { + id : certificateTemplateCreated._id + } + }); + + } catch (error) { + return reject(error); + } + }); + } + + /** + * Update certificate base template. + * @method update + * @name update + * @param {Object} data - certificate template updation data. + * @param {String} baseTemplateId - certificate template Id. + * @param {String} file - file. + * @param {String} userId - userId. + * @returns {JSON} Updated certificate template details. + */ + + static update( baseTemplateId, data, file = {}, userId ) { + return new Promise(async (resolve, reject) => { + try { + if ( Object.keys(file).length > 0 ) { + let uploadFile = await certificateTemplateHelper.uploadToCloud( file, "", userId, false ); + if( !uploadFile.success ) { + throw ({ + message: constants.apiResponses.COULD_NOT_UPLOAD_CONTENT + }) + } + data.url = uploadFile.data.templateUrl; + } + + let updateObject = { + "$set" : data + }; + let certificateBaseTemplateUpdated = + await database.models.certificateBaseTemplates.findOneAndUpdate( + { _id : baseTemplateId }, + updateObject + ); + if ( certificateBaseTemplateUpdated == null ) { + throw{ + message: constants.apiResponses.CERTIFICATE_BASE_TEMPLATE_NOT_UPDATED + } + } + return resolve({ + message : constants.apiResponses.CERTIFICATE_BASE_TEMPLATE_UPDATED, + data : { + id : certificateBaseTemplateUpdated._id + } + }); + + } catch (error) { + return reject(error); + } + }); + } + +} diff --git a/module/certificateBaseTemplates/validator/v1.js b/module/certificateBaseTemplates/validator/v1.js new file mode 100644 index 00000000..428cde70 --- /dev/null +++ b/module/certificateBaseTemplates/validator/v1.js @@ -0,0 +1,27 @@ +/** + * name : v1.js + * author : Vishnu + * created-date : 11-Jan-2023 + * Description : Certificate Base Template Validation. +*/ + +module.exports = (req) => { + + let certificateBaseTemplatesValidator = { + + createOrUpdate : function () { + if ( req.method === "POST" ) { + req.checkBody("code").exists().withMessage("Base template code required"); + req.checkBody("name").exists().withMessage("Base template name required"); + } else if ( req.method === "PATCH" ) { + req.checkParams("_id").exists().withMessage("Base template Id required"); + req.checkParams("_id").isMongoId().withMessage("Base template Id is not valid"); + } + } + } + + if (certificateBaseTemplatesValidator[req.params.method]) { + certificateBaseTemplatesValidator[req.params.method](); + } + +}; \ No newline at end of file diff --git a/module/certificateTemplates/helper.js b/module/certificateTemplates/helper.js new file mode 100644 index 00000000..0f4bf252 --- /dev/null +++ b/module/certificateTemplates/helper.js @@ -0,0 +1,316 @@ +/** + * name : helper.js + * author : Vishnu + * created-date : 29-sep-2022 + * Description : Certificate Template related helper functionality. +*/ +// dependencies +const request = require("request"); +let fs = require('fs'); +const cheerio = require('cheerio'); +const filesHelpers = require(ROOT_PATH+"/module/cloud-services/files/helper"); + +/** + * CertificateTemplatesHelper + * @class +*/ +module.exports = class CertificateTemplatesHelper { + + /** + * Create certificate template. + * @method create + * @name create + * @param {Object} data - certificate template creation data. + * @returns {JSON} created certificate template details. + */ + + static create(data) { + return new Promise(async (resolve, reject) => { + try { + let certificateTemplateCreated = + await database.models.certificateTemplates.create( + data + ); + return resolve({ + message : constants.apiResponses.CERTIFICATE_TEMPLATE_ADDED, + data : { + id : certificateTemplateCreated._id + } + }); + + } catch (error) { + return reject(error); + } + }); + } + + /** + * Update certificate template. + * @method update + * @name update + * @param {String} templateId - certificate template Id. + * @param {Object} data - certificate template updation data. + * @returns {JSON} Updated certificate template details. + */ + + static update(templateId, data) { + return new Promise(async (resolve, reject) => { + try { + + // in case support team pass below values as empty string (not valid) we cant check it with validator. So adding it here + // If templateUrl value passed as empty string. + if ( !data.templateUrl ) { + delete data.templateUrl; + } + // If solutionId value passed as empty string. + if ( !data.solutionId ) { + delete data.solutionId; + } + // If programId value passed as empty string. + if ( !data.programId ) { + delete data.programId; + } + let updateObject = { + "$set" : data + }; + let certificateTemplateUpdated = + await database.models.certificateTemplates.findOneAndUpdate( + {_id: templateId}, + updateObject + ); + if ( certificateTemplateUpdated == null ) { + throw{ + message: constants.apiResponses.CERTIFICATE_TEMPLATE_NOT_UPDATED + } + } + return resolve({ + message : constants.apiResponses.CERTIFICATE_TEMPLATE_UPDATED, + data : { + id : certificateTemplateUpdated._id + } + }); + + } catch (error) { + return reject(error); + } + }); + } + + /** + * upload certificate template. + * @method + * @name uploadToCloud + * @param {Object} fileData - file to upload. + * @param {String} templateId - templateId. + * @param {String} userId - user Id. + * @returns {JSON} Uploaded certificate template details. + */ + + static uploadToCloud(fileData, templateId, userId = "", updateTemplate) { + return new Promise(async (resolve, reject) => { + try { + let fileName; + const now = new Date(); + const date = now.getDate() + "-"+ now.getMonth() + "-" + now.getFullYear() + "-" + now.getTime(); + if ( updateTemplate == false ) { + fileName = userId + "_" + date + ".svg"; + } else { + fileName = templateId + '/' + userId + "_" + date + ".svg"; + } + + const requestData = { + "templates": { + "files": [fileName] + } + }; + + let referenceType; + if ( updateTemplate == false ) { + referenceType = "baseTemplates" + } else { + referenceType = constants.common.CERTIFICATE + } + + let signedUrl = + await filesHelpers.preSignedUrls( + requestData, // data to upload + referenceType, // referenceType + ); + + // upload file using signed Url + if (signedUrl.data && + Object.keys(signedUrl.data).length > 0 && + signedUrl.data.templates && + signedUrl.data.templates.files.length > 0 && + signedUrl.data.templates.files[0].url && + signedUrl.data.templates.files[0].url !== "" + ) { + let fileUploadUrl = signedUrl.data.templates.files[0].url; + let file = fileData.file.data; + + try { + await request({ + url: fileUploadUrl, + method: 'put', + headers: { + "x-ms-blob-type" : "BlockBlob", + "Content-Type": "multipart/form-data" + }, + body: file + }) + let updateCertificateTemplate = {}; + if (updateTemplate === true) { + // Update certificate template url in certificateTemplates collection + updateCertificateTemplate = await this.update( + templateId, + { + // certificateTemplates/6343bd978f9d8980b7841e85/ba9aa220-ff1b-4717-b6ea-ace55f04fc16_2022-9-10-1665383945769.svg + templateUrl : signedUrl.data.templates.files[0].payload.sourcePath + } + ); + return resolve({ + success: true, + data: { + templateId: updateCertificateTemplate.data.id, + templateUrl: signedUrl.data.templates.files[0].payload.sourcePath + } + }) + } + return resolve({ + success: true, + data: { + templateUrl: signedUrl.data.templates.files[0].payload.sourcePath + } + }) + + } catch (error) { + return reject(error); + } + } + else { + return resolve({ + success: false + }) + } + } catch (error) { + return reject(error); + } + }); + } + + /** + * create svg template by editing base template. + * @method + * @name createSvg + * @param {Object} files - file to replace. + * @param {Object} textData - texts to edit. + * @param {String} baseTemplateId - Base template Id. + * @returns {JSON} Uploaded certificate template details. + */ + static createSvg( files, textData, baseTemplateId ) { + return new Promise(async (resolve, reject) => { + try { + let baseTemplateData = await database.models.certificateBaseTemplates.find({ + _id: baseTemplateId + },["url"]).lean(); + + if ( !baseTemplateData.length > 0 || !baseTemplateData[0].url || baseTemplateData[0].url == "" ) { + throw { + message: constants.apiResponses.BASE_CERTIFICATE_TEMPLATE_NOT_FOUND + } + } + let templateUrl = baseTemplateData[0].url; + // getDownloadable url of svg file that we are using as template + let baseTemplateDownloadableUrl = await filesHelpers.getDownloadableUrl([templateUrl]); + let baseTemplate = await getBaseTemplate( baseTemplateDownloadableUrl.result[0].url) + if ( !baseTemplate.success ) { + throw { + message: constants.apiResponses.BASE_CERTIFICATE_TEMPLATE_NOT_FOUND + } + } + // load Base template svg elements + const $ = cheerio.load(baseTemplate.result); + let htmltags = ["","","","","",""]; + let imageNames = ["stateLogo1","stateLogo2","signatureImg1","signatureImg2"]; + let textKeys = ["stateTitle","signatureTitleName1","signatureTitleDesignation1","signatureTitleName2","signatureTitleDesignation2"]; + + // edit image elements + for ( let imageNamesIndex = 0; imageNamesIndex < imageNames.length; imageNamesIndex++ ) { + + if ( files[imageNames[imageNamesIndex]] && files[imageNames[imageNamesIndex]]['data'] && files[imageNames[imageNamesIndex]]['data'] != "" ) { + let data = files[imageNames[imageNamesIndex]]['data']; + let imageData = 'data:image/png;base64,' + data.toString('base64'); + const element = $('#' + imageNames[imageNamesIndex]); + element.attr('href',imageData); + } + + } + + // edit text elements + for ( let textKeysIndex = 0; textKeysIndex < textKeys.length; textKeysIndex++ ) { + if ( textData[textKeys[textKeysIndex]] ) { + let updateText = textData[textKeys[textKeysIndex]]; + const element = $('#' + textKeys[textKeysIndex]); + element.text(updateText); + } + } + let updatedSvg = $.html(); + + // remove html tags- svg file does not require these tags + for ( let index = 0; index < htmltags.length; index++ ) { + updatedSvg = updatedSvg.replace( htmltags[index], "" ); + } + + // file data to upload to cloud + let fileData = { + file : { + data: updatedSvg + } + }; + // upload new svg created from base template to cloud + const uploadTemplate = await this.uploadToCloud(fileData, "BASE_TEMPLATE", "", false ) + if ( !uploadTemplate.success ) { + throw { + message : constants.apiResponses.COULD_NOT_UPLOAD_CONTENT + } + } + + // getDownloadable url of uploaded template + let downloadableUrl = await filesHelpers.getDownloadableUrl([uploadTemplate.data.templateUrl]); + return resolve({ + message: "Template edited successfully", + result: { + url: downloadableUrl.result[0].url + } + }); + } catch (error) { + return reject(error); + } + }) + } + +} + +// Function to fetch data information from cloud using downloadable Url +const getBaseTemplate = function ( templateUrl ) { + return new Promise(async (resolve, reject) => { + try { + request.get(templateUrl, function (error, response, body) { + if (!error && response.statusCode == 200) { + + return resolve({ + success: true, + result: body + }); + } else { + throw { + success: false + } + } + }); + + } catch (error) { + return reject(error); + } + }) +} diff --git a/module/certificateTemplates/validator/v1.js b/module/certificateTemplates/validator/v1.js new file mode 100644 index 00000000..0d7c1681 --- /dev/null +++ b/module/certificateTemplates/validator/v1.js @@ -0,0 +1,37 @@ +/** + * name : v1.js + * author : Vishnu + * created-date : 29-Sep-2022 + * Description : Certificate Template Validation. +*/ + +module.exports = (req) => { + + let certificateTemplatesValidator = { + + createOrUpdate : function () { + if ( req.method === "POST" ) { + req.checkBody('solutionId', 'solutionId is required').exists({checkFalsy: true}).isLength({ min: 1 }); + req.checkBody('programId', 'programId is required').exists({checkFalsy: true}).isLength({ min: 1 }); + req.checkBody("issuer").exists().withMessage("issuer is required"); + req.checkBody("criteria").exists().withMessage("criteria is required"); + req.checkBody("status").exists().withMessage("status is required"); + } else if ( req.method === "PATCH" ) { + req.checkParams("_id").exists().withMessage("required certificate templateId"); + } + + }, + uploadCertificateTemplate : function () { + req.checkParams("_id").exists().withMessage("required certificate templateId"); + }, + createSvg : function () { + req.checkQuery("baseTemplateId").not().isEmpty().withMessage("Please provide base template Id. This cannot be empty"); + req.checkQuery("baseTemplateId").isMongoId().withMessage("Base template Id is not valid"); + } + } + + if (certificateTemplatesValidator[req.params.method]) { + certificateTemplatesValidator[req.params.method](); + } + +}; \ No newline at end of file diff --git a/module/cloud-services/files/helper.js b/module/cloud-services/files/helper.js index d0444ff2..6f568c01 100644 --- a/module/cloud-services/files/helper.js +++ b/module/cloud-services/files/helper.js @@ -24,10 +24,11 @@ module.exports = class FilesHelper { * @param {Array} payloadData - payload for files data. * @param {String} referenceType - reference type * @param {String} userId - Logged in user id. + * @param {String} templateId - certificateTemplateId. * @returns {Array} - consists of all signed urls files. */ - static preSignedUrls(payloadData, referenceType,userId) { + static preSignedUrls(payloadData, referenceType, userId = "") { return new Promise(async (resolve, reject) => { try { @@ -40,6 +41,8 @@ module.exports = class FilesHelper { bucketName = process.env.AWS_BUCKET_NAME; } else if (cloudStorage === "GC" ) { bucketName = process.env.GCP_BUCKET_NAME; + } else if (cloudStorage === constants.common.ORACLE_CLOUD_SERVICE ) { + bucketName = process.env.OCI_BUCKET_NAME; } else { bucketName = process.env.AZURE_STORAGE_CONTAINER; } @@ -83,7 +86,12 @@ module.exports = class FilesHelper { if (referenceType == constants.common.DHITI) { folderPath = "reports/" - + } else if (referenceType == constants.common.CERTIFICATE) { + // Folder path specifically for project certificates + folderPath = "certificateTemplates/"; + } else if (referenceType == "baseTemplates") { + // Folder path specifically for project certificates + folderPath = "certificateBaseTemplates/"; } else { folderPath = "survey/" + payloadIds[0] + "/" + userId + "/" + gen.utils.generateUniqueId() + "/"; } @@ -136,7 +144,10 @@ module.exports = class FilesHelper { bucketName = process.env.AWS_BUCKET_NAME; } else if (cloudStorage === "GC") { bucketName = process.env.GCP_BUCKET_NAME; - } else { + } else if (cloudStorage === constants.common.ORACLE_CLOUD_SERVICE) { + bucketName = process.env.OCI_BUCKET_NAME; + } + else { bucketName = process.env.AZURE_STORAGE_CONTAINER; } diff --git a/module/dictionary/helper.js b/module/dictionary/helper.js deleted file mode 100644 index 7fe6c316..00000000 --- a/module/dictionary/helper.js +++ /dev/null @@ -1,201 +0,0 @@ -/** - * name : dictionary/helper.js - * author : Akash Shah - * created-date : 03-Jan-2020 - * Description : All Dictionary related helper functions. - */ - -// Dependencies - -const elasticSearchHelper = require(GENERIC_HELPERS_PATH + "/elastic-search"); - -// Constants -const dictionaryIndex = gen.utils.checkIfEnvDataExistsOrNot("ELASTICSEARCH_DICTIONARY_INDEX"); -const dictionaryIndexType = gen.utils.checkIfEnvDataExistsOrNot("ELASTICSEARCH_DICTIONARY_INDEX_TYPE"); - -/** - * DictionaryHelper - * @class -*/ - -module.exports = class DictionaryHelper { - - /** - * Find the closest word. - * @method - * @name spellcheck - * @param {String} word word to check for spelling. - * @returns {Promise} returns a promise. - */ - - static spellcheck(word = "") { - return new Promise(async (resolve, reject) => { - try { - - if(word == "") { - throw new Error("Missing word."); - } - - let queryObject = { - query : { - match : { - words : { - query : word, - fuzziness : 2 - } - } - } - } - - const dictionaryIndexMapping = await elasticSearchHelper.searchDocumentFromIndex(dictionaryIndex, dictionaryIndexType, queryObject); - - return resolve({ - success : true, - message : "Matching keyword found.", - data : dictionaryIndexMapping[0].words - }); - - } catch (error) { - return resolve({ - success : true, - message : error.message, - data : false - }); - } - }) - } - - /** - * Check if mapping for dictionary index exists in Elastic search. - * @method - * @name keywordsIndexTypeMapExists - * @returns {Promise} returns a promise. - */ - - static keywordsIndexTypeMapExists() { - return new Promise(async (resolve, reject) => { - try { - - if(dictionaryIndex == "") { - throw new Error("Missing dictionary index name"); - } - - if(dictionaryIndexType == "") { - throw new Error("Missing dictionary index type name"); - } - - const dictionaryIndexMapping = await elasticSearchHelper.getIndexTypeMapping(dictionaryIndex, dictionaryIndexType); - - if(dictionaryIndexMapping.statusCode != httpStatusCode["ok"].status) { - throw new Error("Keywords index type map does not exist."); - } - - return resolve({ - success : true, - message : "Keywords index type map exists", - data : true - }); - - } catch (error) { - return resolve({ - success : true, - message : error.message, - data : false - }); - } - }) - } - - - /** - * Remove word from dictionary index. - * @method - * @name removeWordFromDictionary - * @param {String} word word to remove from dictionary index. - * @returns {Promise} returns a promise. - */ - - static removeWordFromDictionary(word = "") { - return new Promise(async (resolve, reject) => { - try { - - if(word == "") { - throw new Error("Invalid word value."); - } - - word = word.toLowerCase(); - - const dictionaryWordRemoval = await elasticSearchHelper - .deleteDocumentFromIndex( - dictionaryIndex, - dictionaryIndexType, - encodeURI(word) - ); - - if(dictionaryWordRemoval.statusCode != httpStatusCode["ok"].status) { - throw new Error("Failed to remove word from dictionary.") - } - - return resolve({ - success : true, - message : "Success", - data : true - }); - - } catch (error) { - return resolve({ - success : true, - message : error.message, - data : false - }); - } - }) - } - - - /** - * Add word to dictionary index. - * @method - * @name addWordToDictionary - * @param {String} word word to add to dictionary index. - * @returns {Promise} returns a promise. - */ - - static addWordToDictionary(word = "") { - return new Promise(async (resolve, reject) => { - try { - - if(word == "") { - throw new Error("Invalid word value."); - } - - word = word.toLowerCase(); - - const addWordToDictionary = await elasticSearchHelper.createOrUpdateDocumentInIndex( - dictionaryIndex, - dictionaryIndexType, - encodeURI(word), - {words : word } - ); - - if(addWordToDictionary.statusCode != httpStatusCode["ok"].status && addWordToDictionary.statusCode != httpStatusCode["created"].status) { - throw new Error("Failed to add word to dictionary.") - } - - return resolve({ - success : true, - message : "Success", - data : true - }); - - } catch (error) { - return resolve({ - success : true, - message : error.message, - data : false - }); - } - }) - } - -}; \ No newline at end of file diff --git a/module/dictionary/keywords/validator/v1.js b/module/dictionary/keywords/validator/v1.js deleted file mode 100644 index 7478c52e..00000000 --- a/module/dictionary/keywords/validator/v1.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = (req) => { - - let keywordsValidator = { - - update: function () { - req.checkBody('keywords').isLength({ min: 1 }).withMessage("required keywords"); - } - } - - if (keywordsValidator[req.params.method]) { - keywordsValidator[req.params.method](); - } - -}; \ No newline at end of file diff --git a/module/entities/helper.js b/module/entities/helper.js index 18efb73f..2be4fd78 100644 --- a/module/entities/helper.js +++ b/module/entities/helper.js @@ -1,7 +1,10 @@ + + const entityTypesHelper = require(MODULES_BASE_PATH + "/entityTypes/helper"); const userRolesHelper = require(MODULES_BASE_PATH + "/user-roles/helper"); -const elasticSearch = require(GENERIC_HELPERS_PATH + "/elastic-search"); - +// const elasticSearch = require(GENERIC_HELPERS_PATH + "/elastic-search"); +const userService = require(ROOT_PATH + "/generics/services/users"); +const formService = require(ROOT_PATH + '/generics/services/form'); module.exports = class EntitiesHelper { @@ -18,65 +21,65 @@ module.exports = class EntitiesHelper { * @returns {Array} - returns an array of entities data. */ - static entityDocuments( - findQuery = "all", - fields = "all", - skipFields = "none", - limitingValue = "", - skippingValue = "", - sortedData = "" - ) { - return new Promise(async (resolve, reject) => { - try { - - let queryObject = {}; - - if (findQuery != "all") { - queryObject = findQuery; - if( queryObject._id && typeof queryObject._id != "object" && !gen.utils.isValidMongoId(queryObject._id.toString()) ) { - queryObject["registryDetails.locationId"] = queryObject._id; - delete queryObject._id + static entityDocuments( + findQuery = "all", + fields = "all", + skipFields = "none", + limitingValue = "", + skippingValue = "", + sortedData = "" + ) { + return new Promise(async (resolve, reject) => { + try { + + let queryObject = {}; + + if (findQuery != "all") { + queryObject = findQuery; + if( queryObject._id && typeof queryObject._id != "object" && !gen.utils.isValidMongoId(queryObject._id.toString()) ) { + queryObject["registryDetails.locationId"] = queryObject._id; + delete queryObject._id + } } - } - - let projectionObject = {}; - - if (fields != "all") { - fields.forEach(element => { - projectionObject[element] = 1; - }); - } - - if (skipFields != "none") { - skipFields.forEach(element => { - projectionObject[element] = 0; - }); - } - - let entitiesDocuments; - - if( sortedData !== "" ) { + let projectionObject = {}; - entitiesDocuments = await database.models.entities - .find(queryObject, projectionObject) - .sort(sortedData) - .limit(limitingValue) - .skip(skippingValue) - .lean(); - } else { + if (fields != "all") { + + fields.forEach(element => { + projectionObject[element] = 1; + }); + } + + if (skipFields != "none") { + skipFields.forEach(element => { + projectionObject[element] = 0; + }); + } - entitiesDocuments = await database.models.entities - .find(queryObject, projectionObject) - .limit(limitingValue) - .skip(skippingValue) - .lean(); + let entitiesDocuments; + + if( sortedData !== "" ) { + + entitiesDocuments = await database.models.entities + .find(queryObject, projectionObject) + .sort(sortedData) + .limit(limitingValue) + .skip(skippingValue) + .lean(); + } else { + + entitiesDocuments = await database.models.entities + .find(queryObject, projectionObject) + .limit(limitingValue) + .skip(skippingValue) + .lean(); + } + return resolve(entitiesDocuments); + } catch (error) { + return reject(error); } - return resolve(entitiesDocuments); - } catch (error) { - return reject(error); - } - }); + }); } /** @@ -101,7 +104,7 @@ module.exports = class EntitiesHelper { queryObject["$match"]["_id"] = {}; queryObject["$match"]["_id"]["$in"] = entityIds; } - + if( searchText !== "") { queryObject["$match"]["$or"] = [ { "metaInformation.name": new RegExp(searchText, 'i') }, @@ -141,7 +144,7 @@ module.exports = class EntitiesHelper { } } ]); - + return resolve(entityDocuments); } catch (error) { @@ -237,63 +240,34 @@ module.exports = class EntitiesHelper { * @returns {Array} - List of all immediateEntities based on entityId. */ - static immediateEntities(entityId, searchText = "",pageSize="",pageNo="") { + static immediateEntities(entityId, searchText = "", pageSize = "", pageNo = "") { return new Promise(async (resolve, reject) => { try { - - let projection = [ - constants.schema.ENTITYTYPE, - constants.schema.GROUPS - ]; - - let entitiesDocument = await this.entityDocuments({ - _id: entityId - }, projection); - - let immediateEntities = []; - - if (entitiesDocument[0] && - entitiesDocument[0].groups && - Object.keys(entitiesDocument[0].groups).length > 0 - ) { - - let getImmediateEntityTypes = - await entityTypesHelper.entityTypesDocument({ - name : entitiesDocument[0].entityType - },["immediateChildrenEntityType"] - ); - - let immediateEntitiesIds; - - Object.keys(entitiesDocument[0].groups).forEach(entityGroup => { - if ( - getImmediateEntityTypes[0].immediateChildrenEntityType && - getImmediateEntityTypes[0].immediateChildrenEntityType.length > 0 && - getImmediateEntityTypes[0].immediateChildrenEntityType.includes(entityGroup) - ) { - immediateEntitiesIds = - entitiesDocument[0].groups[entityGroup]; - } - }) - - if ( - Array.isArray(immediateEntitiesIds) && - immediateEntitiesIds.length > 0 - ) { - - let searchImmediateData = await this.search( - searchText, - pageSize, - pageNo, - immediateEntitiesIds - ); - - immediateEntities = searchImmediateData[0]; - } + //List of all immediateEntities based on entityId. + let bodyData = { + "parentId" : entityId + }; + // When filter passed as parentId all immediate child or sub entities data is returned. + let entitiesData = await userService.locationSearch( + bodyData, + pageSize, + pageNo, + searchText + ); + // No data found or API call failure + if( !entitiesData.success ) { + return resolve({ + data : [], + count : 0 + }); } - - return resolve(immediateEntities); + let immediateLocation = entitiesData.data; + + return resolve({ + data : immediateLocation, + count : entitiesData.count + }); } catch(error) { return reject(error); @@ -302,7 +276,7 @@ module.exports = class EntitiesHelper { } /** - * Get immediate entities for requested Array. + * Get sub entities for requested Array. * @method * @name subList * @param {params} entities - array of entitity ids @@ -314,54 +288,62 @@ module.exports = class EntitiesHelper { * @returns {Array} - List of all sub list entities. */ - static subEntityList( entities,entityId,type,search,limit,pageNo ) { + static subEntityList( entities, entityId, type, search, limit, pageNo ) { return new Promise(async (resolve, reject) => { try { - - let result = []; + + let result = {}; + let subEntitiesResult = {}; + let listOfSubEntities = []; let obj = { entityId : entityId, type : type, search : search, - limit : limit, + limit :limit, pageNo : pageNo } - + //entity id passed as query parameter if ( entityId !== "" ) { - result = await this.subEntities( + subEntitiesResult = await this.subEntities( obj ); } else { - - await Promise.all(entities.map(async (entity)=> { - - obj["entityId"] = entity; - let entitiesDocument = await this.subEntities( + // entityId is an Array of entities passed in request body + obj["entityId"] = entities; + subEntitiesResult = await this.subEntities( obj ); - - if( Array.isArray(entitiesDocument.data) && - entitiesDocument.data.length > 0 - ) { - result = entitiesDocument; - } - })); + } - - if( result.data && result.data.length > 0 ) { - result.data = result.data.map(data=>{ - let cloneData = {...data}; - cloneData["label"] = cloneData.name; - cloneData["value"] = cloneData._id; - return cloneData; - }) + + //formating sub entities data. + if ( subEntitiesResult && subEntitiesResult.data && subEntitiesResult.data.length > 0 ) { + let formatedEntities = []; + listOfSubEntities = subEntitiesResult.data + listOfSubEntities.map( entityData => { + let data = { + _id: entityData.id, + entityType: entityData.type, + name: entityData.name, + externalId: entityData.code, + label: entityData.name, + value: entityData.id + }; + formatedEntities.push(data) + } ); + listOfSubEntities = []; + listOfSubEntities = formatedEntities; } - + + result.data = listOfSubEntities; + result.count = subEntitiesResult.count; + resolve({ message: constants.apiResponses.ENTITIES_FETCHED, result: result - }); + }); + } catch(error) { return reject(error); } @@ -378,13 +360,13 @@ module.exports = class EntitiesHelper { static subEntities( entitiesData ) { return new Promise(async (resolve, reject) => { - + try { - + let entitiesDocument; - + if( entitiesData.type !== "" ) { - + // If requested for a specific entity tye use entityTraversal entitiesDocument = await this.entityTraversal( entitiesData.entityId, entitiesData.type, @@ -393,7 +375,7 @@ module.exports = class EntitiesHelper { entitiesData.pageNo ); } else { - + // Get immediate entities entitiesDocument = await this.immediateEntities( entitiesData.entityId, entitiesData.search, @@ -403,6 +385,7 @@ module.exports = class EntitiesHelper { } return resolve(entitiesDocument); + } catch(error) { return reject(error); } @@ -410,56 +393,97 @@ module.exports = class EntitiesHelper { } /** - * Get immediate entities. + * Get entities by traversal. * @method - * @name listByEntityType + * @name entityTraversal * @param {Object} entityId - * @returns {Array} - List of all immediateEntities based on entityId. + * @returns {Array} - List of all Entities based on entityId using traversal. */ static entityTraversal( entityId, entityTraversalType = "", searchText = "", - pageSize, - pageNo + pageSize = "", + pageNo = "", ) { return new Promise(async (resolve, reject) => { try { + //list entities of type {entityTraversalType} which comes under the specified entity{entityId} + if( entityTraversalType == constants.common.SCHOOL) { + let filterData = { + "orgLocation.id" : entityId + } + let fields = ["externalId"] + // Get school location code using org search. + let subentitiesCode = await userService.orgSchoolSearch( filterData, pageSize, pageNo, searchText, fields ); + + if( !subentitiesCode.success ) { + return resolve({ + data : [], + count : subentitiesCode.count + }) + } + + let schoolDetails = subentitiesCode.data; + //get code from all data + let schoolCodes = []; + //some default field is also coming. So filtering externalId from result + schoolDetails.map(schoolData=> { + schoolCodes.push(schoolData.externalId); + }); + + + let bodyData = { + "code" : schoolCodes + }; + + // Get school data using location code fetched using org api call + let entitiesData = await userService.locationSearch( bodyData ); - let entityTraversal = `groups.${entityTraversalType}`; - - let entitiesDocument = - await this.entityDocuments( - { - _id: entityId, - "groups" : { $exists : true }, - [entityTraversal] : { $exists: true } - }, - [ entityTraversal ] - ); + if( !entitiesData.success ) { + return resolve({ + data : [], + count : 0 + }) + } + + return resolve({ + data : entitiesData.data, + count : subentitiesCode.count + }) - if( !entitiesDocument[0] ) { - return resolve([]); - } - let result = []; - - if( entitiesDocument[0].groups[entityTraversalType].length > 0 ) { + } else { + /* if {entityId} is of a state and {entityTraversalType} is block , getSubEntitiesBasedOnEntityType will return all entities of type block in that state*/ + let subEntitiesArray = []; - let entityTraversalData = await this.search( - searchText, + let subEntities = await userService.getSubEntitiesBasedOnEntityType( entityId, entityTraversalType, subEntitiesArray ) + + if( !subEntities.length > 0 ) { + return resolve({ + data : subEntities, + count : 0 + }) + } + // call locationSearch with search and pagination + let filter = { + "id" : subEntities + } + let subentitiesData = await userService.locationSearch( + filter, pageSize, pageNo, - entitiesDocument[0].groups[entityTraversalType] + searchText ); - - result = entityTraversalData[0]; - + + return resolve({ + data : subentitiesData.data, + count : subentitiesData.count + }) + } - return resolve(result); - } catch(error) { return reject(error); } @@ -510,44 +534,6 @@ module.exports = class EntitiesHelper { }) } - /** - * Entity details information. - * @method - * @name details - * @param {String} entityId - _id of entity. - * @return {Object} - consists of entity details information. - */ - - static details( entityId ) { - return new Promise(async (resolve, reject) => { - try { - - let entityDocument = await this.entityDocuments( - { - _id : entityId - }, - "all", - ["groups"] - ); - - if ( !entityDocument[0] ) { - return resolve({ - status : httpStatusCode.bad_request.status, - message : constants.apiResponses.ENTITY_NOT_FOUND - }) - } - - resolve({ - message : constants.apiResponses.ENTITY_INFORMATION_FETCHED, - result : entityDocument[0] - }); - - } catch (error) { - return reject(error); - } - }) - } - /** * List of Entities * @method @@ -578,113 +564,6 @@ module.exports = class EntitiesHelper { }); } - /** - * List roles by entity type. - * @method - * @name subEntitiesRoles - * @param entityId - entity id. - * @returns {Object} List of roles by entity id. - */ - - static subEntitiesRoles( entityId ) { - return new Promise(async (resolve, reject) => { - try { - - const entityDocuments = await this.entityDocuments({ - _id : entityId - },["childHierarchyPath","allowedRoles"]); - - if( !entityDocuments.length > 0 ) { - return resolve({ - message : constants.apiResponses.STATE_NOT_FOUND, - result : [] - }) - } - - let queryObject = {}; - - if( entityDocuments[0].allowedRoles && entityDocuments[0].allowedRoles.length > 0 ) { - queryObject["code"] = {}; - queryObject["code"]["$in"] = entityDocuments[0].allowedRoles; - } - - let lengthOfQuery = Object.keys(queryObject).length; - - if( !lengthOfQuery > 0 ) { - - if ( - !entityDocuments[0].childHierarchyPath || - !entityDocuments[0].childHierarchyPath.length > 0 - ) { - return resolve({ - message : constants.apiResponses.SUB_ENTITY_NOT_FOUND, - result : [] - }); - } - - queryObject[ "entityTypes.entityType"] = {}; - queryObject[ "entityTypes.entityType"]["$in"] = - entityDocuments[0].childHierarchyPath; - } - - const rolesData = await userRolesHelper.roleDocuments( - queryObject,["code","title"] - ); - - if( !rolesData.length > 0 ) { - return resolve({ - message : constants.apiResponses.USER_ROLES_NOT_FOUND, - result : [] - }) - } - - return resolve({ - message : constants.apiResponses.USER_ROLES_FETCHED, - result : rolesData - }); - - } catch (error) { - return reject(error); - } - }) - -} - - /** - * Sub entity type list. - * @method - * @name subEntityTypeList - * @param entityId - entity id. - * @returns {Array} List of sub entity type. - */ - - static subEntityTypeList( entityId ) { - return new Promise(async (resolve, reject) => { - try { - - const entityDocuments = await this.entityDocuments({ - _id : entityId - },["childHierarchyPath"]); - - if( !entityDocuments.length > 0 ) { - return resolve({ - message : constants.apiResponses.ENTITY_NOT_FOUND, - result : [] - }) - } - - return resolve({ - message : constants.apiResponses.ENTITIES_CHILD_HIERACHY_PATH, - result : entityDocuments[0].childHierarchyPath - }); - - } catch (error) { - return reject(error); - } - }) - - } - /** * Get users by entityId and role * @method @@ -695,128 +574,128 @@ module.exports = class EntitiesHelper { * @returns {Array} - List of userIds and entityIds */ - static getUsersByEntityAndRole( entityId= "", role= "" ) { - return new Promise(async (resolve, reject) => { - try { +// static getUsersByEntityAndRole( entityId= "", role= "" ) { +// return new Promise(async (resolve, reject) => { +// try { - if (entityId == "") { - throw new Error(constants.apiResponses.ENTITY_ID_REQUIRED); - } +// if (entityId == "") { +// throw new Error(constants.apiResponses.ENTITY_ID_REQUIRED); +// } - if (role == "") { - throw new Error(constants.apiResponses.ROLE_REQUIRED) - } +// if (role == "") { +// throw new Error(constants.apiResponses.ROLE_REQUIRED) +// } - let userRole = await userRolesHelper.roleDocuments({ - code : role - },[ - "entityTypes" - ]); +// let userRole = await userRolesHelper.roleDocuments({ +// code : role +// },[ +// "entityTypes" +// ]); - if(!userRole.length) { - throw new Error(constants.apiResponses.INVALID_ROLE) - } +// if(!userRole.length) { +// throw new Error(constants.apiResponses.INVALID_ROLE) +// } - let entityType = userRole[0].entityTypes[0].entityType; +// let entityType = userRole[0].entityTypes[0].entityType; - let entityTypeOfInputEntityId = await this.entityDocuments - ( - { _id: entityId }, - ["entityType"] - ) +// let entityTypeOfInputEntityId = await this.entityDocuments +// ( +// { _id: entityId }, +// ["entityType"] +// ) - if (entityTypeOfInputEntityId.length == 0) { - throw new Error(constants.apiResponses.ENTITY_NOT_FOUND); - } +// if (entityTypeOfInputEntityId.length == 0) { +// throw new Error(constants.apiResponses.ENTITY_NOT_FOUND); +// } - let entityDocument = []; - let entityIds = []; - - if (entityType == entityTypeOfInputEntityId[0].entityType) { - entityIds.push(entityId) - } - else { - entityDocument = await this.entityDocuments - ( - { - _id: entityId - }, - [ - `groups.${entityType}` - ] - ) +// let entityDocument = []; +// let entityIds = []; + +// if (entityType == entityTypeOfInputEntityId[0].entityType) { +// entityIds.push(entityId) +// } +// else { +// entityDocument = await this.entityDocuments +// ( +// { +// _id: entityId +// }, +// [ +// `groups.${entityType}` +// ] +// ) - if (entityDocument.length > 0) { - entityIds = entityDocument[0].groups[entityType] - } - } +// if (entityDocument.length > 0) { +// entityIds = entityDocument[0].groups[entityType] +// } +// } - if (!entityIds.length) { - throw new Error(constants.apiResponses.USERS_NOT_FOUND); - } +// if (!entityIds.length) { +// throw new Error(constants.apiResponses.USERS_NOT_FOUND); +// } - let chunkOfEntities = _.chunk(entityIds, 1000); +// let chunkOfEntities = _.chunk(entityIds, 1000); - let entitiesFromEs = []; +// let entitiesFromEs = []; - for(let entities = 0; entities < chunkOfEntities.length; entities++) { +// for(let entities = 0; entities < chunkOfEntities.length; entities++) { - let queryObject = { - "query": { - "ids" : { - "values" : chunkOfEntities[entities] - } - }, - "_source": [`data.roles.${role}`,"data.externalId"] - } - - let entityDocuments = await elasticSearch.searchDocumentFromIndex - ( - process.env.ELASTICSEARCH_ENTITIES_INDEX, - "_doc", - queryObject, - "all", - 1000 - ) +// let queryObject = { +// "query": { +// "ids" : { +// "values" : chunkOfEntities[entities] +// } +// }, +// "_source": [`data.roles.${role}`,"data.externalId"] +// } + +// let entityDocuments = await elasticSearch.searchDocumentFromIndex +// ( +// process.env.ELASTICSEARCH_ENTITIES_INDEX, +// "_doc", +// queryObject, +// "all", +// 1000 +// ) - if (entityDocuments && entityDocuments.length > 0) { - entitiesFromEs = [...entitiesFromEs, ...entityDocuments] - } - } - - if (!entitiesFromEs.length) { - throw new Error(constants.apiResponses.USERS_NOT_FOUND) - } +// if (entityDocuments && entityDocuments.length > 0) { +// entitiesFromEs = [...entitiesFromEs, ...entityDocuments] +// } +// } + +// if (!entitiesFromEs.length) { +// throw new Error(constants.apiResponses.USERS_NOT_FOUND) +// } - let result = []; +// let result = []; - for (let entity = 0; entity < entitiesFromEs.length; entity++) { - if (entitiesFromEs[entity].data.roles && Object.keys(entitiesFromEs[entity].data.roles).length > 0) { - for (let user = 0; user < entitiesFromEs[entity].data.roles[role].length; user++) { - result.push({ - entityId: entitiesFromEs[entity].id, - entityExternalId: entitiesFromEs[entity].data.externalId ? entitiesFromEs[entity].data.externalId : "", - userId: entitiesFromEs[entity].data.roles[role][user] - }) - } - } - } +// for (let entity = 0; entity < entitiesFromEs.length; entity++) { +// if (entitiesFromEs[entity].data.roles && Object.keys(entitiesFromEs[entity].data.roles).length > 0) { +// for (let user = 0; user < entitiesFromEs[entity].data.roles[role].length; user++) { +// result.push({ +// entityId: entitiesFromEs[entity].id, +// entityExternalId: entitiesFromEs[entity].data.externalId ? entitiesFromEs[entity].data.externalId : "", +// userId: entitiesFromEs[entity].data.roles[role][user] +// }) +// } +// } +// } - resolve({ - success: true, - message : constants.apiResponses.USERS_AND_ENTITIES_FETCHED, - data : result - }); - - } catch (error) { - return resolve({ - success: false, - message: error.message, - data: false - }); - } - }) - } +// resolve({ +// success: true, +// message : constants.apiResponses.USERS_AND_ENTITIES_FETCHED, +// data : result +// }); + +// } catch (error) { +// return resolve({ +// success: false, +// message: error.message, +// data: false +// }); +// } +// }) +// } /** * Sub entity type list. @@ -827,10 +706,12 @@ module.exports = class EntitiesHelper { * @returns {Array} List of sub entity type. */ - static subEntityListBasedOnRoleAndLocation( stateLocationId,role ) { + static subEntityListBasedOnRoleAndLocation( stateLocationId, role ) { return new Promise(async (resolve, reject) => { try { - + //key will be subEntityTypesOf_{stateLocationId} + let entityKey = constants.common.SUBENTITY + stateLocationId; + let rolesDocument = await userRolesHelper.roleDocuments({ code : role },["entityTypes.entityType"]); @@ -841,58 +722,63 @@ module.exports = class EntitiesHelper { message: constants.apiResponses.USER_ROLES_NOT_FOUND } } - - let filterQuery = { - "registryDetails.code" : stateLocationId - }; - - if( gen.utils.checkValidUUID(stateLocationId) ) { - filterQuery = { - "registryDetails.locationId" : stateLocationId + //check if data already available in cache + let subEntities = []; + let cacheData = await cache.getValue(entityKey); + + if( !cacheData ) { + + let bodyData={ + "id" : stateLocationId }; - } + // Calling location search to fetch state code + let entitiesData = await userService.locationSearch( bodyData ); + + if( !entitiesData.success ) { + return resolve({ + message : constants.apiResponses.ENTITY_NOT_FOUND, + result : [] + }) + } + // form search using state location code + let stateLocationCode = entitiesData.data[0].code; + subEntities = await formService.configForStateLocation( stateLocationCode, entityKey ); + if( !subEntities.length > 0 ) { + return resolve({ + message : constants.apiResponses.ENTITY_NOT_FOUND, + result : [] + }) + } + } else { + subEntities = cacheData; + } + + let result = subEntities; + let targetedEntityType = ""; - const entityDocuments = await this.entityDocuments(filterQuery,["childHierarchyPath"]); - - if( !entityDocuments.length > 0 ) { - return resolve({ - message : constants.apiResponses.ENTITY_NOT_FOUND, - result : [] - }) - } - - let result = []; - - if( rolesDocument[0].entityTypes[0].entityType === constants.common.STATE_ENTITY_TYPE ) { - result = entityDocuments[0].childHierarchyPath; - result.unshift(constants.common.STATE_ENTITY_TYPE); - } else { - - let targetedEntityType = ""; - - rolesDocument[0].entityTypes.forEach(singleEntityType => { - if( entityDocuments[0].childHierarchyPath.includes(singleEntityType.entityType) ) { - targetedEntityType = singleEntityType.entityType; - } - }); - - let findTargetedEntityIndex = - entityDocuments[0].childHierarchyPath.findIndex(element => element === targetedEntityType); - - if( findTargetedEntityIndex < 0 ) { - throw { - message : constants.apiResponses.SUB_ENTITY_NOT_FOUND, - result : [] - } + rolesDocument[0].entityTypes.forEach(singleEntityType => { + if( subEntities.includes(singleEntityType.entityType) ) { + targetedEntityType = singleEntityType.entityType; + } + }); + + let findTargetedEntityIndex = + subEntities.findIndex(element => element === targetedEntityType); + if( findTargetedEntityIndex < 0 ) { + throw { + message : constants.apiResponses.SUB_ENTITY_NOT_FOUND, + result : [] } - - result = entityDocuments[0].childHierarchyPath.slice(findTargetedEntityIndex); - } + } + + result = subEntities.slice(findTargetedEntityIndex); + - return resolve({ - message : constants.apiResponses.ENTITIES_CHILD_HIERACHY_PATH, - result : result - }); + return resolve({ + success: true, + message : constants.apiResponses.ENTITIES_CHILD_HIERACHY_PATH, + result : result + }); } catch (error) { return reject(error); @@ -901,4 +787,82 @@ module.exports = class EntitiesHelper { } + /** + * Entity details information. + * @method + * @name details + * @param {String} entityId - _id of entity. + * @param {Object} requestData - query details. + * @param {String} requestData.locationIds - array of location Ids + * @param {String} requestData.entityIds - array of entity Ids + * @return {Array} - consists of entity details information. + */ + + static details(entityId, requestData = {}) { + return new Promise(async (resolve, reject) => { + try { + + let entityIds = []; + let query = {}; + query["$or"] = []; + + if (entityId) { + entityIds.push(entityId) + } + if (requestData && requestData.entityIds) { + entityIds.push(...requestData.entityIds); + } + if(entityIds.length == 0 && !requestData.locationIds && !requestData.codes){ + throw { + message : constants.apiResponses.ENTITY_ID_OR_LOCATION_ID_NOT_FOUND, + } + } + + if (entityIds.length > 0) { + query["$or"].push({ + _id: { + $in: entityIds + } + }) + } + + if (requestData && requestData.locationIds) { + query["$or"].push({ + "registryDetails.locationId": { + $in: requestData.locationIds + } + }) + } + if (requestData && requestData.codes) { + query["$or"].push({ + "registryDetails.code": { + $in: requestData.codes + } + }) + } + + let entityDocument = await this.entityDocuments( + query, + "all", + ["groups"] + ); + + if (entityDocument && entityDocument.length ==0 ) { + return resolve({ + status : httpStatusCode.bad_request.status, + message : constants.apiResponses.ENTITY_NOT_FOUND + }) + } + + resolve({ + message : constants.apiResponses.ENTITY_INFORMATION_FETCHED, + result : entityDocument + }); + + } catch (error) { + return reject(error); + } + }) + } + } diff --git a/module/entities/validator/v1.js b/module/entities/validator/v1.js index 890ad290..2b6e228a 100644 --- a/module/entities/validator/v1.js +++ b/module/entities/validator/v1.js @@ -6,12 +6,6 @@ module.exports = (req) => { immediateEntities : function () { req.checkParams('_id').exists().withMessage("required Entity id"); }, - details : function () { - req.checkParams('_id').exists().withMessage("required Entity id"); - }, - subEntitiesRoles : function() { - req.checkParams('_id').exists().withMessage("required Entity id"); - }, listByIds : function () { req.checkBody('entities').exists().withMessage("required Entity ids") .isArray().withMessage("entities should be array") @@ -20,11 +14,6 @@ module.exports = (req) => { entitiesValidation(entities) ).withMessage("invalid entity ids"); }, - subEntityTypeList : function () { - req.checkParams('_id') - .exists() - .withMessage("required Entity id"); - }, getUsersByEntityAndRole: function () { req.checkParams('_id').exists().withMessage("required entity id") .isMongoId().withMessage("Invalid entity id"); @@ -33,7 +22,27 @@ module.exports = (req) => { subEntityListBasedOnRoleAndLocation : function () { req.checkParams('_id').exists().withMessage("required state location id"); req.checkQuery('role').exists().withMessage("required role code"); - } + }, + details : function () { + req.checkParams('_id').optional() + .isMongoId().withMessage("Invalid entity id"); + + req.checkBody('entityIds').optional() + .isArray().withMessage("entityIds should be array") + .custom(entities => + entitiesValidation(entities) + ).withMessage("invalid entity ids"); + + req.checkBody('locationIds').optional() + .isArray().withMessage("locationIds should be array") + .custom(location => + gen.utils.checkValidUUID(location) + ).withMessage("invalid location ids"); + + req.checkBody('codes').optional() + .isArray().withMessage("codes should be array") + + }, } if (entityValidator[req.params.method]) { diff --git a/module/files/helper.js b/module/files/helper.js index 96d1d5db..42a1b3c7 100644 --- a/module/files/helper.js +++ b/module/files/helper.js @@ -14,6 +14,7 @@ const awsServices = require(ROOT_PATH + '/generics/services/aws'); const googleCloudServices = require(ROOT_PATH + '/generics/services/google-cloud'); const azureService = require(ROOT_PATH + '/generics/services/azure'); +const oracleSerices = require(ROOT_PATH + '/generics/services/oracle-cloud'); /** * FilesHelper @@ -81,6 +82,12 @@ module.exports = class FilesHelper { filePathForBucket, bucketName ) + } else if (cloudStorage === constants.common.ORACLE_CLOUD_SERVICE) { + result = await oracleSerices.uploadFile( + file, + filePathForBucket, + bucketName + ) } if (deleteFile) { _removeFiles(filePath); @@ -202,6 +209,8 @@ module.exports = class FilesHelper { signedUrlResponse = await awsServices.signedUrl(file, bucket) } else if (cloudStorage === constants.common.AZURE_SERVICE) { signedUrlResponse = await azureService.signedUrl(file, bucket) + } else if (cloudStorage === constants.common.ORACLE_CLOUD_SERVICE) { + signedUrlResponse = await oracleSerices.signedUrl(file, bucket) } if (signedUrlResponse.success) { @@ -396,8 +405,9 @@ function _getLinkFromCloudService(filePath, bucketName, cloudStorage) { ) } else if (cloudStorage === constants.common.AZURE_SERVICE) { result = await azureService.getDownloadableUrl(filePath, bucketName) + } else if (cloudStorage === constants.common.ORACLE_CLOUD_SERVICE) { + result = await oracleSerices.getDownloadableUrl(filePath, bucketName) } - return resolve(result) } catch (error) { return reject(error) diff --git a/module/programUsers/helper.js b/module/programUsers/helper.js new file mode 100644 index 00000000..3429474b --- /dev/null +++ b/module/programUsers/helper.js @@ -0,0 +1,157 @@ +/** + * name : helper.js + * author : Vishnu + * created-date : 12-Jan-2023 + * Description : Programs users related helper functionality. + */ + +// Dependencies + +/** + * ProgramUsersHelper + * @class +*/ +module.exports = class ProgramUsersHelper { + /** + * Create program users + * @method + * @name create + * @param {Object} data + * @returns {JSON} - create programUsers. + */ + + static create(data) { + return new Promise(async (resolve, reject) => { + try { + + let programUsers = await database.models.programUsers.create( + data + ); + + if( !programUsers._id ) { + throw { + message : constants.apiResponses.PROGRAM_USERS_NOT_CREATED + }; + } + return resolve(programUsers); + + } catch (error) { + return reject(error); + } + }) + } + + /** + * Update program users + * @method + * @name update + * @param {Object} query + * @param {Object} update + * @param {Object} options + * @returns {JSON} - create programUsers. + */ + + static update(query, update, options = {}) { + return new Promise(async (resolve, reject) => { + try { + + let programUsers = await database.models.programUsers.findOneAndUpdate( + query, + update, + options + ); + if( !programUsers._id ) { + throw { + message : constants.apiResponses.PROGRAM_USERS_NOT_UPDATED + }; + } + return resolve(programUsers); + + } catch (error) { + return reject(error); + } + }) + } + + /** + * find program users details + * @method + * @name programUsersDocuments + * @param {Array} [filterData = "all"] - template filter query. + * @param {Array} [fieldsArray = "all"] - projected fields. + * @param {Array} [skipFields = "none"] - field not to include + * @returns {Array} Lists of program user. + */ + static programUsersDocuments( + filterData = "all", + fieldsArray = "all", + skipFields = "none" + ) { + return new Promise(async (resolve, reject) => { + try { + + let queryObject = (filterData != "all") ? filterData : {}; + let projection = {} + + if (fieldsArray != "all") { + fieldsArray.forEach(field => { + projection[field] = 1; + }); + } + + if( skipFields !== "none" ) { + skipFields.forEach(field=>{ + projection[field] = 0; + }); + } + + let programUsers = + await database.models.programUsers.find( + queryObject, + projection + ).lean(); + + return resolve(programUsers); + + } catch (error) { + return reject(error); + } + }); + } + + /** + * check if user joined a program or not and consentShared + * @method + * @name checkForUserJoinedProgramAndConsentShared + * @param {String} programId + * @param {String} userId + * @returns {Object} result. + */ + + static checkForUserJoinedProgramAndConsentShared( + programId, + userId + ) { + return new Promise(async (resolve, reject) => { + try { + let result = {}; + const query = { + userId: userId, + programId: programId + } + + //Check data present in programUsers collection. + let programUsers = await this.programUsersDocuments( + query, + ["_id","consentShared"] + ); + result.joinProgram = programUsers.length > 0 ? true : false; + result.consentShared = programUsers.length > 0 ? programUsers[0].consentShared : false; + return resolve(result); + + } catch (error) { + return reject(error); + } + }); + } +} \ No newline at end of file diff --git a/module/programs/helper.js b/module/programs/helper.js index 22875482..dba9aa73 100644 --- a/module/programs/helper.js +++ b/module/programs/helper.js @@ -10,6 +10,10 @@ const entityTypesHelper = require(MODULES_BASE_PATH + "/entityTypes/helper"); const entitiesHelper = require(MODULES_BASE_PATH + "/entities/helper"); const userRolesHelper = require(MODULES_BASE_PATH + "/user-roles/helper"); +const userService = require(ROOT_PATH + "/generics/services/users"); +const kafkaProducersHelper = require(ROOT_PATH + "/generics/kafka/producers"); +const programUsersHelper = require(MODULES_BASE_PATH + "/programUsers/helper"); + /** * ProgramsHelper @@ -23,14 +27,18 @@ module.exports = class ProgramsHelper { * @name programDocuments * @param {Array} [filterQuery = "all"] - solution ids. * @param {Array} [fieldsArray = "all"] - projected fields. - * @param {Array} [skipFields = "none"] - field not to include + * @param {Array} [skipFields = "none"] - field not to include. + * @param {Number} pageNo - page no. + * @param {Number} pageSize - page size. * @returns {Array} List of programs. */ static programDocuments( filterQuery = "all", fieldsArray = "all", - skipFields = "none" + skipFields = "none", + pageNo = "", + pageSize = "" ) { return new Promise(async (resolve, reject) => { try { @@ -38,7 +46,7 @@ module.exports = class ProgramsHelper { let queryObject = (filterQuery != "all") ? filterQuery : {}; let projection = {} - + let pagination = {}; if (fieldsArray != "all") { fieldsArray.forEach(field => { projection[field] = 1; @@ -50,10 +58,17 @@ module.exports = class ProgramsHelper { projection[field] = 0; }) } - + if( pageNo !== "" && pageSize !== "" ) { + pagination = { + skip: pageSize * (pageNo - 1), + limit: pageSize + } + } + let programData = await database.models.programs.find( queryObject, - projection + projection, + pagination ).lean(); return resolve(programData); @@ -64,59 +79,42 @@ module.exports = class ProgramsHelper { }); } - /** - * Create program - * @method - * @name create - * @param {Array} data - * @returns {JSON} - create program. - */ + /** + * Create program + * @method + * @name create + * @param {Array} data + * @returns {JSON} - create program. + */ static create(data) { return new Promise(async (resolve, reject) => { try { - let programData = { - "externalId" : data.externalId, - "name" : data.name, - "description" : data.description , - "owner" : data.userId, - "createdBy" : data.userId, - "updatedBy" : data.userId, "isDeleted" : false, "status" : "active", - "resourceType" : [ - "Program" - ], - "language" : [ - "English" - ], - "keywords" : [ - "keywords 1", - "keywords 2" - ], - "concepts" : [], - "createdFor" : data.createdFor, - "rootOrganisations" : data.rootOrganisations, - "imageCompression" : { - "quality" : 10 - }, "components" : [], - "isAPrivateProgram" : data.isAPrivateProgram ? data.isAPrivateProgram : false + "isAPrivateProgram" : data.isAPrivateProgram ? data.isAPrivateProgram : false, + "owner" : data.userId, + "createdBy" : data.userId, + "updatedBy" : data.userId, } - + _.assign(programData, { + ...data + }); + programData = _.omit(programData,['scope','userId']) let program = await database.models.programs.create( programData ); - + if( !program._id ) { throw { message : constants.apiResponses.PROGRAM_NOT_CREATED }; } - + if( data.scope ) { let programScopeUpdated = await this.setScope( @@ -208,47 +206,66 @@ module.exports = class ProgramsHelper { let scope = {}; if( scopeData.entityType ) { + // Get entity details of type {scopeData.entityType} + let bodyData = { + "type" : scopeData.entityType + } + let entityTypeData = await userService.locationSearch( bodyData ); - let entityTypeData = await entityTypesHelper.entityTypesDocument( - { - name : scopeData.entityType - }, - ["name","_id"] - ); - - if( !entityTypeData.length > 0 ) { + if( !entityTypeData.success ) { return resolve({ status : httpStatusCode.bad_request.status, message : constants.apiResponses.ENTITY_TYPES_NOT_FOUND }); } - scope["entityType"] = entityTypeData[0].name; - scope["entityTypeId"] = entityTypeData[0]._id; + scope["entityType"] = entityTypeData.data[0].type; + } if( scopeData.entities && scopeData.entities.length > 0 ) { - - let entities = - await entitiesHelper.entityDocuments( - { - _id : { $in : scopeData.entities }, - entityTypeId : scope.entityTypeId - },["_id"] - ); - if( !entities.length > 0 ) { - return resolve({ - status : httpStatusCode.bad_request.status, - message : constants.apiResponses.ENTITIES_NOT_FOUND - }); + //call learners api for search + let entityIds = []; + let bodyData={}; + let locationData = gen.utils.filterLocationIdandCode(scopeData.entities) + + //locationIds contain id of location data. + if ( locationData.ids.length > 0 ) { + bodyData = { + "id" : locationData.ids, + "type" : scopeData.entityType + } + let entityData = await userService.locationSearch( bodyData ); + if ( entityData.success ) { + entityData.data.forEach( entity => { + entityIds.push(entity.id) + }); + } } - - scope["entities"] = entities.map(entity => { - return entity._id; - }); - - } + + if ( locationData.codes.length > 0 ) { + let filterData = { + "code" : locationData.codes, + "type" : scopeData.entityType + } + let entityDetails = await userService.locationSearch( filterData ); + + if ( entityDetails.success ) { + let entitiesData = entityDetails.data; + entitiesData.forEach( entity => { + entityIds.push(entity.id) + }); + } + } + + if( !entityIds.length > 0 ) { + throw { + message : constants.apiResponses.ENTITIES_NOT_FOUND + }; + } + scope["entities"] = entityIds; + } if( scopeData.roles ) { @@ -320,7 +337,11 @@ module.exports = class ProgramsHelper { data.updatedBy = userId; data.updatedAt = new Date(); - + //convert components to objectedIds + if (data.components && data.components.length > 0) { + data.components = data.components.map(component => gen.utils.convertStringToObjectId(component)); + } + let program = await database.models.programs.findOneAndUpdate({ _id : programId },{ $set : _.omit(data,["scope"]) }, { new: true }); @@ -370,17 +391,18 @@ module.exports = class ProgramsHelper { * @method * @name list * @param {Number} pageNo - page no. - * @param {Nmber} pageSize - page size. + * @param {Number} pageSize - page size. * @param {String} searchText - text to search. + * @param {Object} filter - filter. + * @param {Array} projection - projection. * @returns {Object} - Programs list. */ - static list(pageNo,pageSize,searchText,filter = {},projection) { + static list(pageNo="",pageSize="",searchText,filter = {},projection) { return new Promise( async (resolve, reject) => { try { - let programDocument = []; let matchQuery = { status : constants.common.ACTIVE }; @@ -390,6 +412,7 @@ module.exports = class ProgramsHelper { } if ( searchText !== "" ) { + matchQuery["$or"] = []; matchQuery["$or"].push( { @@ -401,6 +424,10 @@ module.exports = class ProgramsHelper { }); } + let sortQuery = { + $sort: {"createdAt": -1} + } + let projection1 = {}; if( projection && projection.length > 0 ) { @@ -424,11 +451,17 @@ module.exports = class ProgramsHelper { facetQuery["$facet"]["totalCount"] = [ { "$count": "count" } ]; - - facetQuery["$facet"]["data"] = [ - { $skip: pageSize * (pageNo - 1) }, - { $limit: pageSize } - ]; + + if ( pageSize === "" && pageNo === "" ) { + facetQuery["$facet"]["data"] = [ + { $skip: 0} + ]; + } else { + facetQuery["$facet"]["data"] = [ + { $skip: pageSize * (pageNo - 1) }, + { $limit: pageSize } + ]; + } let projection2 = {}; projection2["$project"] = { @@ -438,11 +471,10 @@ module.exports = class ProgramsHelper { } }; - programDocument.push({ $match : matchQuery }, { $project : projection1 }, facetQuery, projection2); + programDocument.push({ $match : matchQuery }, sortQuery,{ $project : projection1 }, facetQuery, projection2); let programDocuments = await database.models.programs.aggregate(programDocument); - return resolve({ success : true, message : constants.apiResponses.PROGRAM_LIST, @@ -460,7 +492,7 @@ module.exports = class ProgramsHelper { }) } - /** + /** * List of programs based on role and location. * @method * @name forUserRoleAndLocation @@ -471,7 +503,7 @@ module.exports = class ProgramsHelper { * @returns {JSON} - List of programs based on role and location. */ - static forUserRoleAndLocation( bodyData, pageSize, pageNo,searchText = "" ) { + static forUserRoleAndLocation( bodyData, pageSize, pageNo, searchText = "", projection ) { return new Promise(async (resolve, reject) => { @@ -480,56 +512,29 @@ module.exports = class ProgramsHelper { let queryData = await this.queryBasedOnRoleAndLocation( bodyData ); - + if( !queryData.success ) { return resolve(queryData); } - + queryData.data.startDate ={"$lte": new Date()} + queryData.data.endDate ={"$gte": new Date()} + let targetedPrograms = await this.list( pageNo, pageSize, searchText, queryData.data, - ["name", "externalId","components"] + projection ); - - if ( targetedPrograms.success && targetedPrograms.data && targetedPrograms.data.data.length > 0) { - - for ( - let targetedProgram = 0; - targetedProgram < targetedPrograms.data.data.length; - targetedProgram ++ - ) { - - let currentTargetedProgram = targetedPrograms.data.data[targetedProgram]; - - if( currentTargetedProgram.components.length > 0 ) { - - let solutions = await database.models.solutions.find({ - _id : { $in : currentTargetedProgram.components }, - isDeleted : false, - status : constants.common.ACTIVE - },{ - _id : 1 - }); - - if( solutions.length > 0 ) { - currentTargetedProgram["solutions"] = solutions.length; - delete currentTargetedProgram.components; - } - - } - } - } - + return resolve({ success: true, message: constants.apiResponses.TARGETED_PROGRAMS_FETCHED, - data: targetedPrograms.data + data: targetedPrograms.data.data, + count: targetedPrograms.data.count }); } catch (error) { - return resolve({ success : false, message : error.message, @@ -552,35 +557,21 @@ module.exports = class ProgramsHelper { static queryBasedOnRoleAndLocation( data ) { return new Promise(async (resolve, reject) => { try { - + let locationIds = Object.values(_.omit(data,["role","filter"])).map(locationId => { return locationId; }); - - let filterData = { - $or : [{ - "registryDetails.code" : { $in : locationIds } - },{ - "registryDetails.locationId" : { $in : locationIds } - }] - }; - - let entities = await entitiesHelper.entityDocuments(filterData,["_id"]); - - if( !entities.length > 0 ) { + if( !locationIds.length > 0 ) { throw { - message : constants.apiResponses.NO_ENTITY_FOUND_IN_LOCATION + message : constants.apiResponses.NO_LOCATION_ID_FOUND_IN_DATA } } - let entityIds = entities.map(entity => { - return entity._id; - }); - + let filterQuery = { - "scope.roles.code" : { $in : [constants.common.ALL_ROLES,data.role] }, - "scope.entities" : { $in : entityIds }, + "scope.roles.code" : { $in : [constants.common.ALL_ROLES,...data.role.split(",")] }, + "scope.entities" : { $in : locationIds }, "isDeleted" : false, status : constants.common.ACTIVE } @@ -711,47 +702,65 @@ module.exports = class ProgramsHelper { * @returns {JSON} - Added entities data. */ - static addEntitiesInScope( programId,entities ) { + static addEntitiesInScope( programId, entities ) { return new Promise(async (resolve, reject) => { try { - let programData = await this.programDocuments({ _id : programId, scope : { $exists : true }, isAPrivateProgram : false - },["_id","scope.entityTypeId"]); - + },["_id","scope.entityType"]); + if( !programData.length > 0 ) { throw { message : constants.apiResponses.PROGRAM_NOT_FOUND }; } + + let entityIds = []; + let bodyData={}; + let locationData = gen.utils.filterLocationIdandCode(entities) + + if ( locationData.ids.length > 0 ) { + bodyData = { + "id" : locationData.ids, + "type": programData[0].scope.entityType + } + let entityData = await userService.locationSearch( bodyData ); + if ( entityData.success ) { + entityData.data.forEach( entity => { + entityIds.push(entity.id) + }); + } + } - let entitiesData = - await entitiesHelper.entityDocuments({ - _id : { $in : entities }, - entityTypeId : programData[0].scope.entityTypeId - },["_id"]); + if ( locationData.codes.length > 0 ) { + let filterData = { + "code" : locationData.codes, + "type": programData[0].scope.entityType + } + let entityDetails = await userService.locationSearch( filterData ); - if( !entitiesData.length > 0 ) { + if ( entityDetails.success ) { + entityDetails.data.forEach( entity => { + entityIds.push(entity.externalId) + }); + } + } + + if( !entityIds.length > 0 ) { throw { message : constants.apiResponses.ENTITIES_NOT_FOUND }; } - let entityIds = []; - - entitiesData.forEach(entity => { - entityIds.push(entity._id); - }); - let updateProgram = await database.models.programs.findOneAndUpdate({ _id : programId },{ $addToSet : { "scope.entities" : { $each : entityIds } } },{ new : true }).lean(); - + if( !updateProgram || !updateProgram._id ) { throw { message : constants.apiResponses.PROGRAM_NOT_UPDATED @@ -764,6 +773,7 @@ module.exports = class ProgramsHelper { }); } catch(error) { + return resolve({ success : false, status : error.status ? @@ -853,50 +863,39 @@ module.exports = class ProgramsHelper { static removeEntitiesInScope( programId,entities ) { return new Promise(async (resolve, reject) => { try { - let programData = await this.programDocuments({ _id : programId, scope : { $exists : true }, isAPrivateProgram : false - },["_id","scope.entityTypeId"]); - + },["_id","scope.entities"]); + if( !programData.length > 0 ) { throw { message : constants.apiResponses.PROGRAM_NOT_FOUND }; } - - let entitiesData = - await entitiesHelper.entityDocuments({ - _id : { $in : entities }, - entityTypeId : programData[0].scope.entityTypeId - },["_id"]); - + let entitiesData = []; + entitiesData = programData[0].scope.entities; + if( !entitiesData.length > 0 ) { throw { message : constants.apiResponses.ENTITIES_NOT_FOUND }; } - - let entityIds = []; - entitiesData.forEach(entity => { - entityIds.push(entity._id); - }); - let updateProgram = await database.models.programs.findOneAndUpdate({ _id : programId },{ - $pull : { "scope.entities" : { $in : entityIds } } + $pull : { "scope.entities" : { $in : entities} } },{ new : true }).lean(); - + if( !updateProgram || !updateProgram._id ) { throw { message : constants.apiResponses.PROGRAM_NOT_UPDATED } } - + return resolve({ message : constants.apiResponses.ENTITIES_REMOVED_IN_PROGRAM, success : true @@ -911,6 +910,229 @@ module.exports = class ProgramsHelper { }) } }) + } + + /** + * Program details. + * @method + * @name details + * @param {String} programId - Program Id. + * @returns {Object} - Details of the program. + */ + + static details(programId) { + return new Promise(async (resolve, reject) => { + try { + + let programData = await this.programDocuments({ + _id: programId + }); + + if ( !programData.length > 0 ) { + return resolve({ + status: httpStatusCode.bad_request.status, + message: constants.apiResponses.PROGRAM_NOT_FOUND + }); + } + + return resolve({ + message: constants.apiResponses.PROGRAMS_FETCHED, + success: true, + data: programData[0] + }); + + } catch (error) { + return resolve({ + success: false, + status: error.status + ? error.status + : httpStatusCode['internal_server_error'].status, + message: error.message + }); + } + }); + } + + /** + * Program join. + * @method + * @name join + * @param {String} programId - Program Id. + * @param {Object} data - body data (can include isResourse flag && userRoleInformation). + * @param {String} userId - Logged in user id. + * @param {String} userToken - User token. + * @param {String} [appName = ""] - App Name. + * @param {String} [appVersion = ""] - App Version. + * @param {Boolean} callConsetAPIOnBehalfOfUser - required to call consent api or not + * @returns {Object} - Details of the program join. + */ + + static join( programId, data, userId, userToken, appName = "", appVersion = "", callConsetAPIOnBehalfOfUser = false ) { + return new Promise(async (resolve, reject) => { + try { + let pushProgramUsersDetailsToKafka = false; + //Using programId fetch program details. Also checking the program status in the query. + let programData = await this.programDocuments({ + _id: programId, + status: constants.common.ACTIVE, + isDeleted: false + },[ + "name", + "externalId", + "requestForPIIConsent", + "rootOrganisations" + ] + ); + + if ( !programData.length > 0 ) { + throw ({ + status: httpStatusCode.bad_request.status, + message: constants.apiResponses.PROGRAM_NOT_FOUND + }); + } + let programUsersData = {}; + let update = {}; + + // check if user already joined for program or not + const programUsersDetails = await programUsersHelper.programUsersDocuments( + { + userId: userId, + programId: programId + }, + ["_id","consentShared"] + ); + // if user not joined for program. we have add more key values to programUsersData + if ( !programUsersDetails.length > 0 ) { + // Fetch user profile information by calling sunbird's user read api. + // !Important check specific fields of userProfile. + let userProfile = await userService.profile(userToken, userId); + if (!userProfile.success || + !userProfile.data || + !userProfile.data.profileUserTypes || + !userProfile.data.profileUserTypes.length > 0 || + !userProfile.data.userLocations || + !userProfile.data.userLocations.length > 0 + ) { + throw ({ + status: httpStatusCode.bad_request.status, + message: constants.apiResponses.PROGRAM_JOIN_FAILED + }); + } + programUsersData = { + programId: programId, + userRoleInformation: data.userRoleInformation, + userId: userId, + userProfile: userProfile.data, + resourcesStarted : false + } + if( appName != "" ) { + programUsersData['appInformation.appName'] = appName; + } + if( appVersion != "" ) { + programUsersData['appInformation.appVersion'] = appVersion; + } + + //For internal calls add consent using sunbird api + if( callConsetAPIOnBehalfOfUser && programData[0].hasOwnProperty('requestForPIIConsent') && programData[0].requestForPIIConsent === true ){ + if( !programData[0].rootOrganisations || !programData[0].rootOrganisations.length > 0 ) { + throw { + message: constants.apiResponses.PROGRAM_JOIN_FAILED, + status: httpStatusCode.bad_request.status + } + } + let userConsentRequestBody = { + "request": { + "consent": { + "status": constants.common.REVOKED, + "userId": userProfile.data.id, + "consumerId": programData[0].rootOrganisations[0], + "objectId": programId, + "objectType": constants.common.PROGRAM + } + } + } + let consentResponse = await userService.setUserConsent(userToken, userConsentRequestBody) + + if(!consentResponse.success){ + throw { + message: constants.apiResponses.PROGRAM_JOIN_FAILED, + status: httpStatusCode.bad_request.status + } + } + + } + + } + + // if requestForPIIConsent Is false and user not joined program till now then set pushProgramUsersDetailsToKafka = true; + // if requestForPIIConsent == true and data.consentShared value is true which means user interacted with the consent popup set pushProgramUsersDetailsToKafka = true; + // if programUsersDetails[0].consentShared === true which means the data is already pushed to Kafka once + if((programData[0].hasOwnProperty('requestForPIIConsent') && programData[0].requestForPIIConsent === false && !programUsersDetails.length > 0) || + ((programData[0].hasOwnProperty('requestForPIIConsent') && programData[0].requestForPIIConsent === true) && (data.hasOwnProperty('consentShared') && data.consentShared == true && + (programUsersDetails.length > 0 && programUsersDetails[0].consentShared === false || !programUsersDetails.length > 0)))) { + + pushProgramUsersDetailsToKafka = true; + + } + + //create or update query + const query = { + programId: programId, + userId: userId + }; + //if a resource is started + if ( data.isResource ) { + programUsersData.resourcesStarted = true; + } + //if user interacted with the consent-popup + if ( data.hasOwnProperty('consentShared') ) { + programUsersData.consentShared = data.consentShared; + } + update['$set'] = programUsersData; + + // add record to programUsers collection + let joinProgram = await programUsersHelper.update(query, update, { new:true, upsert:true }); + + if (!joinProgram._id) { + throw { + message: constants.apiResponses.PROGRAM_JOIN_FAILED, + status: httpStatusCode.bad_request.status + } + } + + let joinProgramDetails = joinProgram.toObject(); + + if ( pushProgramUsersDetailsToKafka ) { + joinProgramDetails.programName = programData[0].name; + joinProgramDetails.programExternalId = programData[0].externalId; + joinProgramDetails.requestForPIIConsent = programData[0].requestForPIIConsent; + // push programUsers details to kafka + await kafkaProducersHelper.pushProgramUsersToKafka(joinProgramDetails); + + } + + return resolve({ + message: constants.apiResponses.JOINED_PROGRAM, + success: true, + data: { + _id : joinProgram._id + } + }); + + } catch (error) { + return resolve({ + success: false, + status: error.status + ? error.status + : httpStatusCode['internal_server_error'].status, + message: error.message + }); + } + }); } }; + + + +const solutionsHelper = require(MODULES_BASE_PATH + "/solutions/helper"); \ No newline at end of file diff --git a/module/programs/validator/v1.js b/module/programs/validator/v1.js index ef7adce0..4ffa98f3 100644 --- a/module/programs/validator/v1.js +++ b/module/programs/validator/v1.js @@ -10,10 +10,9 @@ module.exports = (req) => { let programsValidator = { create : function () { - req.checkBody('createdFor').exists().withMessage("required organisation created for id"); - req.checkBody('rootOrganisations').exists().withMessage("required root organisations id"); req.checkBody('externalId').exists().withMessage("required program externalId"); req.checkBody('name').exists().withMessage("required program name"); + req.checkBody('requestForPIIConsent').exists().withMessage("required requestForPIIConsent value of program"); }, update : function () { req.checkParams("_id").exists().withMessage("required program id"); @@ -33,7 +32,12 @@ module.exports = (req) => { removeEntitiesInScope : function () { req.checkParams("_id").exists().withMessage("required program id"); req.checkBody("entities").exists().withMessage("required entities to be added"); - } + }, + join : function () { + req.checkParams("_id").exists().withMessage("required program id"); + req.checkParams("_id").isMongoId().withMessage("Invalid program ID"); + req.checkBody("userRoleInformation").exists().withMessage("required userRoleInformation to be added"); + }, } if (programsValidator[req.params.method]) { diff --git a/module/solutions/helper.js b/module/solutions/helper.js index 39cbb6b2..798b10a6 100644 --- a/module/solutions/helper.js +++ b/module/solutions/helper.js @@ -4,32 +4,32 @@ * created-date : 03-sep-2020 * Description : Solution related helper functionality. */ - // Dependencies - const programsHelper = require(MODULES_BASE_PATH + "/programs/helper"); const entityTypesHelper = require(MODULES_BASE_PATH + "/entityTypes/helper"); const entitiesHelper = require(MODULES_BASE_PATH + "/entities/helper"); const userRolesHelper = require(MODULES_BASE_PATH + "/user-roles/helper"); -const assessmentService = require(ROOT_PATH + '/generics/services/samiksha'); +const surveyService = require(ROOT_PATH + '/generics/services/survey'); const improvementProjectService = require(ROOT_PATH + '/generics/services/improvement-project'); +const appsPortalBaseUrl = process.env.APP_PORTAL_BASE_URL + "/" ; +const userService = require(ROOT_PATH + "/generics/services/users"); +const programUsersHelper = require(MODULES_BASE_PATH + "/programUsers/helper"); /** * SolutionsHelper * @class */ module.exports = class SolutionsHelper { - - /** + /** * Solution Data * @method * @name solutionDocuments * @param {Array} [filterQuery = "all"] - solution ids. * @param {Array} [fieldsArray = "all"] - projected fields. * @param {Array} [skipFields = "none"] - field not to include - * @returns {Array} List of solutions. + * @returns {Array} List of solutions. */ - + static solutionDocuments( filterQuery = "all", fieldsArray = "all", @@ -93,7 +93,7 @@ module.exports = class SolutionsHelper { }); } - /** + /** * Create solution. * @method * @name createSolution @@ -101,7 +101,7 @@ module.exports = class SolutionsHelper { * @returns {JSON} solution creation data. */ - static createSolution(solutionData) { + static createSolution(solutionData) { return new Promise(async (resolve, reject) => { try { @@ -119,37 +119,61 @@ module.exports = class SolutionsHelper { solutionData.programName = programData[0].name; solutionData.programDescription = programData[0].description; - let entityTypeData = - await entityTypesHelper.entityTypesDocument({ - name : solutionData.entityType - },["_id"]); + + if( solutionData.type == constants.common.COURSE && !solutionData.link ) { - if( !entityTypeData.length > 0 ) { - throw { - message : constants.apiResponses.ENTITY_TYPES_NOT_FOUND - } + return resolve({ + status : httpStatusCode.bad_request.status, + message : constants.apiResponses.COURSE_LINK_REQUIRED + }); + } - - solutionData.entityTypeId = entityTypeData[0]._id; - + if( solutionData.entities && solutionData.entities.length > 0 ) { + let entityIds = []; + let locationData = gen.utils.filterLocationIdandCode(solutionData.entities) + + if ( locationData.ids.length > 0 ) { + let bodyData = { + "id" : locationData.ids + } + let entityData = await userService.locationSearch( bodyData ); + if ( entityData.success ) { + entityData.data.forEach( entity => { + entityIds.push(entity.id) + }); + } + } + + if ( locationData.codes.length > 0 ) { + let filterData = { + "externalId" : locationData.codes + } + let schoolDetails = await userService.orgSchoolSearch( filterData ); - let entitiesData = - await entitiesHelper.entityDocuments({ - _id : { $in : solutionData.entities } - },["_id"]); - - if( !entitiesData.length > 0 ) { - throw { - message : constants.apiResponses.ENTITIES_NOT_FOUND + if ( schoolDetails.success ) { + let schoolData = schoolDetails.data; + schoolData.forEach( entity => { + entityIds.push(entity.externalId) + }); } } - entitiesData = entitiesData.map( entity => { - return entity._id; - }) + if( !entityIds.length > 0 ) { + throw { + message : constants.apiResponses.ENTITIES_NOT_FOUND + }; + } - solutionData.entities = entitiesData; + solutionData.entities = entityIds; + } + + if( solutionData.minNoOfSubmissionsRequired && + solutionData.minNoOfSubmissionsRequired > constants.common.DEFAULT_SUBMISSION_REQUIRED + ) { + if (!solutionData.allowMultipleAssessemts){ + solutionData.minNoOfSubmissionsRequired = constants.common.DEFAULT_SUBMISSION_REQUIRED; + } } solutionData.status = constants.common.ACTIVE; @@ -172,16 +196,14 @@ module.exports = class SolutionsHelper { }, { $addToSet: { components : solutionCreation._id } }); - + if( !solutionData.excludeScope && programData[0].scope ) { - let solutionScope = await this.setScope( solutionData.programId, solutionCreation._id, solutionData.scope ? solutionData.scope : {} ); - } return resolve({ @@ -197,7 +219,7 @@ module.exports = class SolutionsHelper { }); } - /** + /** * Set scope in solution * @method * @name setScope @@ -210,12 +232,11 @@ module.exports = class SolutionsHelper { * @returns {JSON} - scope in solution. */ - static setScope( programId,solutionId,scopeData ) { + static setScope( programId,solutionId,scopeData ) { return new Promise(async (resolve, reject) => { - try { - + let programData = await programsHelper.programDocuments({ _id : programId },["_id","scope"]); @@ -225,7 +246,7 @@ module.exports = class SolutionsHelper { message : constants.apiResponses.PROGRAM_NOT_FOUND }); } - + let solutionData = await this.solutionDocuments({ _id : solutionId },["_id"]); if( !solutionData.length > 0 ) { @@ -234,79 +255,83 @@ module.exports = class SolutionsHelper { message : constants.apiResponses.SOLUTION_NOT_FOUND }); } - if( programData[0].scope ) { let currentSolutionScope = JSON.parse(JSON.stringify(programData[0].scope)); - + if( Object.keys(scopeData).length > 0 ) { - if( scopeData.entityType ) { - - let entityType = await entityTypesHelper.entityTypesDocument( - { - name : scopeData.entityType - }, - ["name","_id"] - ); - - currentSolutionScope.entityType = entityType[0].name; - currentSolutionScope.entityTypeId = entityType[0]._id; - + let bodyData = { "type" : scopeData.entityType } + let entityTypeData = await userService.locationSearch( bodyData); + if ( entityTypeData.success ) { + currentSolutionScope.entityType = entityTypeData.data[0].type + } } if( scopeData.entities && scopeData.entities.length > 0 ) { - - let entities = - await entitiesHelper.entityDocuments( - { - _id : { $in : scopeData.entities }, - entityTypeId : currentSolutionScope.entityTypeId - },["_id"] - ); - - if( !entities.length > 0 ) { + //call learners api for search + let entityIds = []; + let bodyData={}; + let locationData = gen.utils.filterLocationIdandCode(scopeData.entities) + + if ( locationData.ids.length > 0 ) { + bodyData = { + "id" : locationData.ids, + "type" : currentSolutionScope.entityType + } + let entityData = await userService.locationSearch( bodyData ); + if ( entityData.success ) { + entityData.data.forEach( entity => { + entityIds.push(entity.id) + }); + } + } + + if ( locationData.codes.length > 0 ) { + let filterData = { + "code" : locationData.codes, + "type" : currentSolutionScope.entityType + } + let entityDetails = await userService.locationSearch( filterData ); + + if ( entityDetails.success ) { + entityDetails.data.forEach( entity => { + entityIds.push(entity.id) + }); + } + } + + if( !entityIds.length > 0 ) { return resolve({ status : httpStatusCode.bad_request.status, message : constants.apiResponses.ENTITIES_NOT_FOUND }); } - - let entityIds = []; - - if( currentSolutionScope.entityType !== programData[0].scope.entityType ) { - for( let entity = 0; entity < entities.length; entity ++ ) { - let entityQuery = { - _id : { $in : currentSolutionScope.entities }, - [`groups.${currentSolutionScope.entityType}`] : entities[entity]._id - } - - let entityInParent = - await entitiesHelper.entityDocuments(entityQuery); - - if( entityInParent.length > 0 ) { - entityIds.push(ObjectId(entities[entity]._id)); - } - } - } else { - entityIds = entities.map(entity => { - return ObjectId(entity._id); - }) - } - - if( !entityIds.length > 0 ) { + let entitiesData = []; + + // if( currentSolutionScope.entityType !== programData[0].scope.entityType ) { + // let result = []; + // let childEntities = await userService.getSubEntitiesBasedOnEntityType(currentSolutionScope.entities, currentSolutionScope.entityType, result); + // if( childEntities.length > 0 ) { + // entitiesData = entityIds.filter(element => childEntities.includes(element)); + // } + // } else { + entitiesData = entityIds + // } - return resolve({ - status : httpStatusCode.bad_request.status, - message : constants.apiResponses.SCOPE_ENTITY_INVALID - }); + if( !entitiesData.length > 0 ) { + + return resolve({ + status : httpStatusCode.bad_request.status, + message : constants.apiResponses.SCOPE_ENTITY_INVALID + }); - } + } - currentSolutionScope.entities = entityIds; + currentSolutionScope.entities = entitiesData; } - + if( scopeData.roles ) { if( Array.isArray(scopeData.roles) && scopeData.roles.length > 0 ) { @@ -332,7 +357,7 @@ module.exports = class SolutionsHelper { } } } - + let updateSolution = await database.models.solutions.findOneAndUpdate( { @@ -354,7 +379,7 @@ module.exports = class SolutionsHelper { success : true, message : constants.apiResponses.SOLUTION_UPDATED }); - + } catch (error) { return resolve({ success : false @@ -362,7 +387,7 @@ module.exports = class SolutionsHelper { } }) - } + } /** * Update solution. @@ -373,7 +398,7 @@ module.exports = class SolutionsHelper { * @returns {JSON} solution creation data. */ - static update(solutionId, solutionData, userId) { + static update(solutionId, solutionData, userId) { return new Promise(async (resolve, reject) => { try { @@ -395,6 +420,14 @@ module.exports = class SolutionsHelper { "$set" : {} }; + if( solutionData.minNoOfSubmissionsRequired && + solutionData.minNoOfSubmissionsRequired > constants.common.DEFAULT_SUBMISSION_REQUIRED + ) { + if (!solutionData.allowMultipleAssessemts){ + solutionData.minNoOfSubmissionsRequired = constants.common.DEFAULT_SUBMISSION_REQUIRED; + } + } + let solutionUpdateData = solutionData; Object.keys(_.omit(solutionUpdateData,["scope"])).forEach(updationData=>{ @@ -458,15 +491,20 @@ module.exports = class SolutionsHelper { * @returns {JSON} List of solutions. */ - static list(type,subType,filter = {},pageNo,pageSize,searchText,projection) { + static list(type,subType,filter = {},pageNo,pageSize,searchText,projection) { return new Promise(async (resolve, reject) => { try { let matchQuery = { - "isDeleted" : false, - status : constants.common.ACTIVE + "isDeleted" : false }; + if( type == constants.common.SURVEY ) { + matchQuery["status"] = { $in: [ constants.common.ACTIVE, constants.common.INACTIVE ] } + } else { + matchQuery.status = constants.common.ACTIVE ; + } + if( type !== "" ) { matchQuery["type"] = type; } @@ -535,7 +573,7 @@ module.exports = class SolutionsHelper { $arrayElemAt: ["$totalCount.count", 0] } }; - + let solutionDocuments = await database.models.solutions.aggregate([ { $match : matchQuery }, @@ -546,7 +584,7 @@ module.exports = class SolutionsHelper { facetQuery, projection2 ]); - + return resolve({ success : true, message : constants.apiResponses.SOLUTIONS_LIST, @@ -577,104 +615,109 @@ module.exports = class SolutionsHelper { * @returns {JSON} - List of solutions based on role and location. */ - static forUserRoleAndLocation( - bodyData, - type, - subType = "", - programId, - pageSize, - pageNo, - searchText = "" - ) { - return new Promise(async (resolve, reject) => { - try { - - let queryData = await this.queryBasedOnRoleAndLocation( - bodyData, - type, - subType, - programId - ); - - if( !queryData.success ) { - return resolve(queryData); - } - - let matchQuery = queryData.data; - - if( type === "" && subType === "" ) { - - let targetedTypes = _targetedSolutionTypes(); - - matchQuery["$or"] = []; - - targetedTypes.forEach( type => { + static forUserRoleAndLocation( + bodyData, + type, + subType = "", + programId, + pageSize, + pageNo, + searchText = "" + ) { + return new Promise(async (resolve, reject) => { + try { + + let queryData = await this.queryBasedOnRoleAndLocation( + bodyData, + type, + subType, + programId + ); - let singleType = { - type : type - }; - - if( type === constants.common.IMPROVEMENT_PROJECT ) { - singleType["projectTemplateId"] = { $exists : true }; + if( !queryData.success ) { + return resolve(queryData); + } + + let matchQuery = queryData.data; + + if( type === "" && subType === "" ) { + + let targetedTypes = _targetedSolutionTypes(); + + matchQuery["$or"] = []; + + targetedTypes.forEach( type => { + + let singleType = { + type : type + }; + + if( type === constants.common.IMPROVEMENT_PROJECT ) { + singleType["projectTemplateId"] = { $exists : true }; + } + + matchQuery["$or"].push(singleType); + }) + } else { + + if( type !== "" ) { + matchQuery["type"] = type; + } + + if( subType !== "" ) { + matchQuery["subType"] = subType; } - - matchQuery["$or"].push(singleType); - }) - } else { - - if( type !== "" ) { - matchQuery["type"] = type; } - - if( subType !== "" ) { - matchQuery["subType"] = subType; + + if ( programId !== "" ) { + matchQuery["programId"] = ObjectId(programId); } + + let targetedSolutions = await this.list( + type, + subType, + matchQuery, + pageNo, + pageSize, + searchText, + [ + "name", + "description", + "programName", + "programId", + "externalId", + "projectTemplateId", + "type", + "language", + "creator", + "endDate", + "link", + "referenceFrom", + "entityType", + "certificateTemplateId" + ] + ); + + return resolve({ + success: true, + message: constants.apiResponses.TARGETED_SOLUTIONS_FETCHED, + data: targetedSolutions.data + }); + + } catch (error) { + + return resolve({ + success : false, + message : error.message, + data : {} + }); + } + + }) + } - if ( programId !== "" ) { - matchQuery["programId"] = ObjectId(programId); - } - - let targetedSolutions = await this.list( - type, - subType, - matchQuery, - pageNo, - pageSize, - searchText, - [ - "name", - "description", - "programName", - "programId", - "externalId", - "projectTemplateId", - "type", - "language", - "creator" - ] - ); - - return resolve({ - success: true, - message: constants.apiResponses.TARGETED_SOLUTIONS_FETCHED, - data: targetedSolutions.data - }); - - } catch (error) { - - return resolve({ - success : false, - message : error.message, - data : {} - }); - - } - - }) - } - - /** + /** * Auto targeted query field. * @method * @name queryBasedOnRoleAndLocation @@ -682,7 +725,7 @@ module.exports = class SolutionsHelper { * @returns {JSON} - Auto targeted solutions query. */ - static queryBasedOnRoleAndLocation( data ) { + static queryBasedOnRoleAndLocation( data, type = "" ) { return new Promise(async (resolve, reject) => { try { @@ -693,42 +736,37 @@ module.exports = class SolutionsHelper { registryIds.push(data[requestedDataKey]); entityTypes.push(requestedDataKey); }) - - let filterData = { - $or : [{ - "registryDetails.code" : { $in : registryIds } - },{ - "registryDetails.locationId" : { $in : registryIds } - }] - }; - - let entities = await entitiesHelper.entityDocuments(filterData,["_id"]); - - if( !entities.length > 0 ) { + if( !registryIds.length > 0 ) { throw { - message : constants.apiResponses.NO_ENTITY_FOUND_IN_LOCATION + message : constants.apiResponses.NO_LOCATION_ID_FOUND_IN_DATA } } - - let entityIds = entities.map(entity => { - return entity._id; - }); - + let filterQuery = { - "scope.roles.code" : { $in : [constants.common.ALL_ROLES,data.role] }, - "scope.entities" : { $in : entityIds }, + "scope.roles.code" : { $in : [constants.common.ALL_ROLES,...data.role.split(",")] }, + "scope.entities" : { $in : registryIds }, "scope.entityType" : { $in : entityTypes }, - isReusable : false, - "isDeleted" : false, - status : constants.common.ACTIVE + "isReusable" : false, + "isDeleted" : false }; + + if( type === constants.common.SURVEY ) { + + filterQuery["status"] = { $in: [ constants.common.ACTIVE, constants.common.INACTIVE ] } + let validDate = new Date(); + validDate.setDate(validDate.getDate() - constants.common.DEFAULT_SURVEY_REMOVED_DAY ); + filterQuery["endDate"] = { $gte : validDate } + + } else { + filterQuery.status = constants.common.ACTIVE; + } if( data.filter && Object.keys(data.filter).length > 0 ) { let solutionsSkipped = []; if( data.filter.skipSolutions ) { - + data.filter.skipSolutions.forEach( solution => { solutionsSkipped.push(ObjectId(solution.toString())); }); @@ -760,7 +798,7 @@ module.exports = class SolutionsHelper { }) } - /** + /** * Details of solution based on role and location. * @method * @name detailsBasedOnRoleAndLocation @@ -769,7 +807,7 @@ module.exports = class SolutionsHelper { * @returns {JSON} - Details of solution based on role and location. */ - static detailsBasedOnRoleAndLocation( solutionId,bodyData ) { + static detailsBasedOnRoleAndLocation( solutionId,bodyData ) { return new Promise(async (resolve, reject) => { @@ -780,9 +818,7 @@ module.exports = class SolutionsHelper { if( !queryData.success ) { return resolve(queryData); } - queryData.data["_id"] = solutionId; - let targetedSolutionDetails = await this.solutionDocuments( queryData.data, @@ -799,10 +835,12 @@ module.exports = class SolutionsHelper { "entityType", "entityTypeId", "language", - "creator" + "creator", + "link", + "certificateTemplateId" ] ); - + if( !targetedSolutionDetails.length > 0 ) { throw { status : httpStatusCode["bad_request"].status, @@ -829,7 +867,7 @@ module.exports = class SolutionsHelper { }) } - /** + /** * Add roles in solution scope. * @method * @name addRolesInScope @@ -838,7 +876,7 @@ module.exports = class SolutionsHelper { * @returns {JSON} - Added roles data. */ - static addRolesInScope( solutionId,roles ) { + static addRolesInScope( solutionId,roles ) { return new Promise(async (resolve, reject) => { try { @@ -915,7 +953,7 @@ module.exports = class SolutionsHelper { }) } - /** + /** * Add entities in solution. * @method * @name addEntitiesInScope @@ -927,7 +965,6 @@ module.exports = class SolutionsHelper { static addEntitiesInScope( solutionId,entities ) { return new Promise(async (resolve, reject) => { try { - let solutionData = await this.solutionDocuments({ _id : solutionId, @@ -942,7 +979,6 @@ module.exports = class SolutionsHelper { message : constants.apiResponses.SOLUTION_NOT_FOUND }); } - let programData = await programsHelper.programDocuments({ _id : solutionData[0].programId },["scope.entities","scope.entityType"]); @@ -953,40 +989,62 @@ module.exports = class SolutionsHelper { message : constants.apiResponses.PROGRAM_NOT_FOUND }); } - + if( solutionData[0].scope.entityType !== programData[0].scope.entityType ) { - let checkEntityInParent = - await entitiesHelper.entityDocuments({ - _id : programData[0].scope.entities, - [`groups.${solutionData[0].entityType}`] : entities - },["_id"]); - + let matchData = []; + let checkEntityInParent = []; + let childEntities = await userService.getSubEntitiesBasedOnEntityType(programData[0].scope.entities, solutionData[0].scope.entityType, matchData); + + if( !childEntities.length > 0 ) { + throw { + message : constants.apiResponses.ENTITY_NOT_EXISTS_IN_PARENT + } + } + checkEntityInParent = entities.filter(element => childEntities.includes(element)); if( !checkEntityInParent.length > 0 ) { throw { message : constants.apiResponses.ENTITY_NOT_EXISTS_IN_PARENT } } } + let entityIds = []; + let bodyData={}; + let locationData = gen.utils.filterLocationIdandCode(entities) + + if ( locationData.ids.length > 0 ) { + bodyData = { + "id" : locationData.ids, + "type" : solutionData[0].scope.entityType + } + let entityData = await userService.locationSearch( bodyData ); + if ( entityData.success ) { + entityData.data.forEach( entity => { + entityIds.push(entity.id) + }); + } + } - let entitiesData = - await entitiesHelper.entityDocuments({ - _id : { $in : entities }, - entityType : solutionData[0].scope.entityType - },["_id"]); + if ( locationData.codes.length > 0 ) { + let filterData = { + "code" : locationData.codes, + "type" : solutionData[0].scope.entityType + } + let entityDetails = await userService.locationSearch( filterData ); - if( !entitiesData.length > 0 ) { + if ( entityDetails.success ) { + entityDetails.data.forEach( entity => { + entityIds.push(entity.id) + }); + } + } + + if( !entityIds.length > 0 ) { throw { message : constants.apiResponses.ENTITIES_NOT_FOUND }; } - let entityIds = []; - - entitiesData.forEach(entity => { - entityIds.push(entity._id); - }); - let updateSolution = await database.models.solutions.findOneAndUpdate({ _id : solutionId },{ @@ -1003,7 +1061,6 @@ module.exports = class SolutionsHelper { message : constants.apiResponses.ENTITIES_ADDED_IN_SOLUTION, success : true }); - } catch(error) { return resolve({ success : false, @@ -1015,7 +1072,7 @@ module.exports = class SolutionsHelper { }) } - /** + /** * remove roles from solution scope. * @method * @name removeRolesInScope @@ -1024,66 +1081,75 @@ module.exports = class SolutionsHelper { * @returns {JSON} - Removed solution roles. */ - static removeRolesInScope( solutionId,roles ) { + static removeRolesInScope(solutionId, roles) { return new Promise(async (resolve, reject) => { try { + let solutionData = await this.solutionDocuments( + { + _id: solutionId, + scope: { $exists: true }, + isReusable: false, + isDeleted: false, + }, + ["_id"] + ); - let solutionData = - await this.solutionDocuments({ - _id : solutionId, - scope : { $exists : true }, - isReusable : false, - isDeleted : false - },["_id"]); - - if( !solutionData.length > 0 ) { + if (!solutionData.length > 0) { return resolve({ - status : httpStatusCode.bad_request.status, - message : constants.apiResponses.SOLUTION_NOT_FOUND + status: httpStatusCode.bad_request.status, + message: constants.apiResponses.SOLUTION_NOT_FOUND, }); } - let userRoles = await userRolesHelper.roleDocuments({ - code : { $in : roles } - },["_id","code"] + let userRoles = await userRolesHelper.roleDocuments( + { + code: { $in: roles }, + }, + ["_id", "code"] ); - - if( !userRoles.length > 0 ) { + + if (!userRoles.length > 0) { return resolve({ - status : httpStatusCode.bad_request.status, - message : constants.apiResponses.INVALID_ROLE_CODE + status: httpStatusCode.bad_request.status, + message: constants.apiResponses.INVALID_ROLE_CODE, }); } - let updateSolution = await database.models.solutions.findOneAndUpdate({ - _id : solutionId - },{ - $pull : { "scope.roles" : { $in : userRoles } } - },{ new : true }).lean(); + let updateSolution = await database.models.solutions + .findOneAndUpdate( + { + _id: solutionId, + }, + { + $pull: { "scope.roles": { $in: userRoles } }, + }, + { new: true } + ) + .lean(); - if( !updateSolution || !updateSolution._id ) { + if (!updateSolution || !updateSolution._id) { throw { - message : constants.apiResponses.SOLUTION_NOT_UPDATED - } + message: constants.apiResponses.SOLUTION_NOT_UPDATED, + }; } return resolve({ - message : constants.apiResponses.ROLES_REMOVED_IN_SOLUTION, - success : true + message: constants.apiResponses.ROLES_REMOVED_IN_SOLUTION, + success: true, }); - - } catch(error) { + } catch (error) { return resolve({ - success : false, - status : error.status ? - error.status : httpStatusCode['internal_server_error'].status, - message : error.message - }) + success: false, + status: error.status + ? error.status + : httpStatusCode['internal_server_error'].status, + message: error.message, + }); } - }) - } + }); + } - /** + /** * remove entities in solution scope. * @method * @name removeEntitiesInScope @@ -1092,111 +1158,108 @@ module.exports = class SolutionsHelper { * @returns {JSON} - Removed entities from solution scope. */ - static removeEntitiesInScope( solutionId,entities ) { + static removeEntitiesInScope(solutionId, entities) { return new Promise(async (resolve, reject) => { try { + let solutionData = await this.solutionDocuments( + { + _id: solutionId, + scope: { $exists: true }, + isReusable: false, + isDeleted: false, + }, + ["_id", "scope.entities"] + ); - let solutionData = - await this.solutionDocuments({ - _id : solutionId, - scope : { $exists : true }, - isReusable : false, - isDeleted : false - },["_id","scope.entityTypeId"]); - - if( !solutionData.length > 0 ) { + if (!solutionData.length > 0) { return resolve({ - status : httpStatusCode.bad_request.status, - message : constants.apiResponses.SOLUTION_NOT_FOUND + status: httpStatusCode.bad_request.status, + message: constants.apiResponses.SOLUTION_NOT_FOUND, }); } - - let entitiesData = - await entitiesHelper.entityDocuments({ - _id : { $in : entities }, - entityTypeId : solutionData[0].scope.entityTypeId - },["_id"]); - + let entitiesData = []; + entitiesData = solutionData[0].scope.entities; if( !entitiesData.length > 0 ) { - throw { - message : constants.apiResponses.ENTITIES_NOT_FOUND - }; + throw { + message : constants.apiResponses.ENTITIES_NOT_FOUND + }; } - let entityIds = []; - - entitiesData.forEach(entity => { - entityIds.push(entity._id); - }); - - let updateSolution = await database.models.solutions.findOneAndUpdate({ - _id : solutionId - },{ - $pull : { "scope.entities" : { $in : entityIds } } - },{ new : true }).lean(); + let updateSolution = await database.models.solutions + .findOneAndUpdate( + { + _id: solutionId, + }, + { + $pull: { "scope.entities": { $in: entities } }, + }, + { new: true } + ) + .lean(); - if( !updateSolution || !updateSolution._id ) { + if (!updateSolution || !updateSolution._id) { throw { - message : constants.apiResponses.SOLUTION_NOT_UPDATED - } + message: constants.apiResponses.SOLUTION_NOT_UPDATED, + }; } return resolve({ - message : constants.apiResponses.ENTITIES_REMOVED_IN_SOLUTION, - success : true + message: constants.apiResponses.ENTITIES_REMOVED_IN_SOLUTION, + success: true, }); - - } catch(error) { + } catch (error) { return resolve({ - success : false, - status : error.status ? - error.status : httpStatusCode['internal_server_error'].status, - message : error.message - }) + success: false, + status: error.status + ? error.status + : httpStatusCode['internal_server_error'].status, + message: error.message, + }); } - }) - } + }); + } - /** + /** * Solution details. * @method * @name details - * @param {String} solutionId - Program Id. + * @param {String} solutionId - Solution Id. * @returns {Object} - Details of the solution. */ - static details( solutionId ) { + static getDetails(solutionId) { return new Promise(async (resolve, reject) => { try { + let solutionData = await this.solutionDocuments({ + _id: solutionId, + isDeleted: false, + }); - let solutionData = - await this.solutionDocuments({ _id : solutionId,isDeleted : false }); - - if( !solutionData.length > 0 ) { + if ( !solutionData.length > 0 ) { return resolve({ - status : httpStatusCode.bad_request.status, - message : constants.apiResponses.SOLUTION_NOT_FOUND + status: httpStatusCode.bad_request.status, + message: constants.apiResponses.SOLUTION_NOT_FOUND, }); } return resolve({ - message : constants.apiResponses.SOLUTION_DETAILS_FETCHED, - success : true, - data : solutionData[0] + message: constants.apiResponses.SOLUTION_DETAILS_FETCHED, + success: true, + data: solutionData[0], }); - - } catch(error) { + } catch (error) { return resolve({ - success : false, - status : error.status ? - error.status : httpStatusCode['internal_server_error'].status, - message : error.message - }) + success: false, + status: error.status + ? error.status + : httpStatusCode['internal_server_error'].status, + message: error.message, + }); } - }) + }); } - - /** + + /** * List of solutions and targeted ones. * @method * @name targetedSolutions @@ -1204,11 +1267,20 @@ module.exports = class SolutionsHelper { * @returns {Object} - Details of the solution. */ - static targetedSolutions(requestedData,solutionType,userToken,pageSize,pageNo,search,filter, surveyReportPage = "") { + static targetedSolutions( + requestedData, + solutionType, + userToken, + pageSize, + pageNo, + search, + filter, + surveyReportPage = "" + ) { return new Promise(async (resolve, reject) => { - try { + try { - let assignedSolutions = await this.assignedUserSolutions( + let assignedSolutions = await this.assignedUserSolutions( solutionType, userToken, search, @@ -1216,75 +1288,84 @@ module.exports = class SolutionsHelper { surveyReportPage ); - let totalCount = 0; - let mergedData = []; - let solutionIds = []; + let totalCount = 0; + let mergedData = []; + let solutionIds = []; + if( assignedSolutions.success && assignedSolutions.data ) { - if( assignedSolutions.success && assignedSolutions.data ) { + // Remove observation solutions which for project tasks. + + _.remove(assignedSolutions.data.data, function(solution) { + return solution.referenceFrom == constants.common.PROJECT && solution.type == constants.common.OBSERVATION; + }); - totalCount = assignedSolutions.data.count; - mergedData = assignedSolutions.data.data; + totalCount = assignedSolutions.data.data && assignedSolutions.data.data.length > 0 ? assignedSolutions.data.data.length : totalCount; + mergedData = assignedSolutions.data.data; - if( mergedData.length > 0 ) { + if ( mergedData.length > 0 ) { + let programIds = []; - let programIds = []; + mergedData.forEach( mergeSolutionData => { + if( mergeSolutionData.solutionId ) { + solutionIds.push(mergeSolutionData.solutionId); + } - mergedData.forEach( mergeSolutionData => { - if( mergeSolutionData.solutionId ) { - solutionIds.push(mergeSolutionData.solutionId); - } + if( mergeSolutionData.programId ) { + programIds.push(mergeSolutionData.programId); + } + }); - if( mergeSolutionData.programId ) { - programIds.push(mergeSolutionData.programId); - } - }); + let programsData = await programsHelper.programDocuments({ + _id : { $in : programIds } + },["name"]); - let programsData = await programsHelper.programDocuments({ - _id : { $in : programIds } - },["name"]); - - if( programsData.length > 0 ) { - - let programs = - programsData.reduce( - (ac, program) => - ({ ...ac, [program._id.toString()]: program }), {} - ); - - mergedData = mergedData.map( data => { - if( data.programId && programs[data.programId.toString()]) { - data.programName = programs[data.programId.toString()].name; - } - return data; - }) - } + if( programsData.length > 0 ) { + + let programs = + programsData.reduce( + (ac, program) => + ({ ...ac, [program._id.toString()]: program }), {} + ); + + mergedData = mergedData.map((data) => { + if( data.programId && programs[data.programId.toString()]) { + data.programName = programs[data.programId.toString()].name; + } + return data; + }); } } + } - requestedData["filter"] = {}; - if( solutionIds.length > 0 ) { - requestedData["filter"]["skipSolutions"] = solutionIds; - } + requestedData['filter'] = {}; + if( solutionIds.length > 0 ) { + requestedData["filter"]["skipSolutions"] = solutionIds; + } - if( filter && filter !== "" ) { - if( filter === constants.common.CREATED_BY_ME ) { - requestedData["filter"]["isAPrivateProgram"] = { - $ne : false - }; - } else if( filter === constants.common.ASSIGN_TO_ME ) { - requestedData["filter"]["isAPrivateProgram"] = false; - } + if( filter && filter !== "" ) { + if( filter === constants.common.CREATED_BY_ME ) { + requestedData["filter"]["isAPrivateProgram"] = { + $ne : false + }; + } else if( filter === constants.common.ASSIGN_TO_ME ) { + requestedData["filter"]["isAPrivateProgram"] = false; } + } - let targetedSolutions = { - success : false - }; + let targetedSolutions = { + success: false + }; - surveyReportPage = gen.utils.convertStringToBoolean(surveyReportPage); + let getTargetedSolution = true; - if ( !surveyReportPage ) { - - targetedSolutions = + if ( filter === constants.common.DISCOVERED_BY_ME ) { + getTargetedSolution = false; + } else if ( gen.utils.convertStringToBoolean(surveyReportPage) === true ) { + getTargetedSolution = false; + } + + if( getTargetedSolution ) { + targetedSolutions = await this.forUserRoleAndLocation( requestedData, solutionType, @@ -1294,58 +1375,58 @@ module.exports = class SolutionsHelper { constants.common.DEFAULT_PAGE_NO, search ); - } - - if( targetedSolutions.success ) { + } - if( targetedSolutions.data.data && targetedSolutions.data.data.length > 0 ) { - totalCount += targetedSolutions.data.count; + if( targetedSolutions.success ) { - if( mergedData.length !== pageSize ) { + if( targetedSolutions.success && targetedSolutions.data.data && targetedSolutions.data.data.length > 0 ) { - targetedSolutions.data.data.forEach(targetedSolution => { - targetedSolution.solutionId = targetedSolution._id; - targetedSolution._id = ""; - targetedSolution["creator"] = targetedSolution.creator ? targetedSolution.creator : ""; - - if ( solutionType === constants.common.SURVEY ) { - targetedSolution.isCreator = false; - } + totalCount += targetedSolutions.data.count; + targetedSolutions.data.data.forEach(targetedSolution => { + targetedSolution.solutionId = targetedSolution._id; + targetedSolution._id = ""; - mergedData.push(targetedSolution); - delete targetedSolution.type; - delete targetedSolution.externalId; - }); - } - } - - } + if( solutionType !== constants.common.COURSE ) { + targetedSolution["creator"] = targetedSolution.creator ? targetedSolution.creator : ""; + } + + if ( solutionType === constants.common.SURVEY ) { + targetedSolution.isCreator = false; + } + + mergedData.push(targetedSolution); + delete targetedSolution.type; + delete targetedSolution.externalId; + + }); + } + } - if( mergedData.length > 0 ) { - let startIndex = pageSize * (pageNo - 1); - let endIndex = startIndex + pageSize; - mergedData = mergedData.slice(startIndex,endIndex) - } + if( mergedData.length > 0 ) { + let startIndex = pageSize * (pageNo - 1); + let endIndex = startIndex + pageSize; + mergedData = mergedData.slice(startIndex,endIndex) + } return resolve({ - success : true, - message : constants.apiResponses.TARGETED_OBSERVATION_FETCHED, - data : { - data : mergedData, - count : totalCount - } + success: true, + message: constants.apiResponses.TARGETED_OBSERVATION_FETCHED, + data: { + data: mergedData, + count: totalCount + } }); - } catch (error) { return reject({ status: error.status || httpStatusCode.internal_server_error.status, - message: error.message || httpStatusCode.internal_server_error.message, - errorObject: error + message: + error.message || httpStatusCode.internal_server_error.message, + errorObject: error, }); } - }) + }); } - /** + /** * Solution details. * @method * @name assignedUserSolutions @@ -1361,7 +1442,7 @@ module.exports = class SolutionsHelper { if ( solutionType === constants.common.OBSERVATION ) { userAssignedSolutions = - await assessmentService.assignedObservations( + await surveyService.assignedObservations( userToken, search, filter @@ -1370,7 +1451,7 @@ module.exports = class SolutionsHelper { } else if ( solutionType === constants.common.SURVEY) { userAssignedSolutions = - await assessmentService.assignedSurveys( + await surveyService.assignedSurveys( userToken, search, filter, @@ -1386,7 +1467,7 @@ module.exports = class SolutionsHelper { } return resolve(userAssignedSolutions); - } catch(error) { + } catch(error) { return resolve({ success : false, status : error.status ? @@ -1398,77 +1479,643 @@ module.exports = class SolutionsHelper { } /** - * Targeted entity in solution + * Get link by solution id * @method - * @name targetedEntity + * @name fetchLink * @param {String} solutionId - solution Id. - * @param {Object} requestedData - requested data + * @param {String} appName - app Name. + * @param {String} userId - user Id. * @returns {Object} - Details of the solution. */ - static targetedEntity( solutionId,requestedData ) { + static fetchLink(solutionId, userId) { return new Promise(async (resolve, reject) => { try { - let solutionData = - await this.solutionDocuments({ - _id : solutionId, - isDeleted : false - },["entityType","type"]); + let solutionData = await this.solutionDocuments( + { + _id: solutionId, + isReusable: false, + isAPrivateProgram: false + }, + ["link", "type", 'author'] + ); - if( !solutionData.length > 0 ) { + if ( !Array.isArray(solutionData) || solutionData.length < 1 ) { return resolve({ - status : httpStatusCode.bad_request.status, - message : constants.apiResponses.SOLUTION_NOT_FOUND + message: constants.apiResponses.SOLUTION_NOT_FOUND, + result: {} }); } - if( !requestedData[ solutionData[0].entityType ] ) { + let prefix = constants.common.PREFIX_FOR_SOLUTION_LINK; + + let solutionLink, link; + + if ( !solutionData[0].link ) { + + let updateLink = await gen.utils.md5Hash( + solutionData[0]._id + "###" + solutionData[0].author + ); + + let updateSolution = await this.update( + solutionId, + { link: updateLink }, + userId + ); + + solutionLink = updateLink; + } else { + solutionLink = solutionData[0].link; + } + + link = _generateLink( + appsPortalBaseUrl, + prefix, + solutionLink, + solutionData[0].type + ); + + return resolve({ + success: true, + message: constants.apiResponses.LINK_GENERATED, + result: link + }); + + } catch (error) { + return resolve({ + success: false, + status: error.status + ? error.status + : httpStatusCode['internal_server_error'].status, + message: error.message + }); + } + }); + } + + /** + * Verify solution link + * @method + * @name verifyLink + * @param {String} solutionId - solution Id. + * @param {String} userId - user Id. + * @param {String} userToken - user token. + * @param {Boolean} createProject - create project. + * @param {Object} bodyData - Req Body. + * @returns {Object} - Details of the solution. + */ + + static verifyLink(link = "", bodyData = {}, userId = "", userToken = "", createProject = true ) { + return new Promise(async (resolve, reject) => { + try { + + let verifySolution = await this.verifySolutionDetails( + link, + userId, + userToken + ); + + let checkForTargetedSolution = await this.checkForTargetedSolution( + link, + bodyData, + userId, + userToken + ); + if( !checkForTargetedSolution || Object.keys(checkForTargetedSolution.result).length <= 0 ) { + return resolve(checkForTargetedSolution); + } + + let solutionData = checkForTargetedSolution.result; + + if( solutionData.type == constants.common.OBSERVATION ) { + // Targeted solution + if(checkForTargetedSolution.result.isATargetedSolution) { + + let observationDetailFromLink = await surveyService.getObservationDetail( + solutionData.solutionId, + userToken + ); + + checkForTargetedSolution.result["observationId"] = (observationDetailFromLink.result && observationDetailFromLink.result._id != "") + ? observationDetailFromLink.result._id + : ""; + + } + + } else if ( solutionData.type === constants.common.IMPROVEMENT_PROJECT ) { + // Targeted solution + if( checkForTargetedSolution.result.isATargetedSolution && createProject ) { + //targeted user with project creation + + let projectDetailFromLink = await improvementProjectService.getProjectDetail( + solutionData.solutionId, + userToken, + bodyData + ); + + if ( !projectDetailFromLink || !projectDetailFromLink.data ) { + return resolve(projectDetailFromLink); + } + + checkForTargetedSolution.result['projectId'] = projectDetailFromLink.data._id + ? projectDetailFromLink.data._id + : ""; + + } else if( checkForTargetedSolution.result.isATargetedSolution && !createProject ) { + //targeted user with no project creation + let findQuery = { + userId: userId, + projectTemplateId : solutionData.projectTemplateId, + referenceFrom: { + $ne : constants.common.LINK + }, + isDeleted : false + }; + + let checkTargetedProjectExist = + await improvementProjectService.projectDocuments( + userToken, + findQuery, + ["_id"] + ); + + if ( + checkTargetedProjectExist.success && + checkTargetedProjectExist.data && + checkTargetedProjectExist.data.length > 0 && + checkTargetedProjectExist.data[0]._id != "" + ) { + checkForTargetedSolution.result['projectId'] = + checkTargetedProjectExist.data[0]._id; + } + + } else { + //non targeted project exist + let checkIfUserProjectExistsQuery = { + createdBy: userId, + referenceFrom: constants.common.LINK, + link: link, + }; + + let checkForProjectExist = + await improvementProjectService.projectDocuments( + userToken, + checkIfUserProjectExistsQuery, + ["_id"] + ); + + if ( + checkForProjectExist.success && + checkForProjectExist.data && + checkForProjectExist.data.length > 0 && + checkForProjectExist.data[0]._id != "" + ) { + checkForTargetedSolution.result['projectId'] = + checkForProjectExist.data[0]._id; + } + + } + + } + + return resolve(checkForTargetedSolution); + + } catch (error) { + return resolve({ + success: false, + status: error.status + ? error.status + : httpStatusCode['internal_server_error'].status, + message: error.message + }); + } + }); + } + + + /** + * Verify solution id + * @method + * @name verifySolution + * @param {String} solutionId - solution Id. + * @param {String} userId - user Id. + * @param {String} userToken - user token. + * @param {Boolean} createProject - create project. + * @param {Object} bodyData - Req Body. + * @returns {Object} - Details of the solution. + * Takes SolutionId and userRoleInformation as parameters. + * @return {Object} - { + "message": "Solution is not targeted to the role", + "status": 200, + "result": { + "isATargetedSolution": false/true, + "_id": "63987b5d26a3620009a1142d" + } + } + */ + + static isTargetedBasedOnUserProfile(solutionId = "", bodyData = {}) { + return new Promise(async (resolve, reject) => { + try { + + let response = { + isATargetedSolution: false, + _id: solutionId, + }; + + let queryData = await this.queryBasedOnRoleAndLocation(bodyData); + if ( !queryData.success ) { + return resolve(queryData); + } + + queryData.data["_id"] = solutionId; + let matchQuery = queryData.data; + let solutionData = await this.solutionDocuments(matchQuery, [ + "_id", + "type", + "programId", + "name", + ]); + + if ( !Array.isArray(solutionData) || solutionData.length < 1 ) { + + return resolve({ - status : httpStatusCode.bad_request.status, - message : constants.apiResponses.SOLUTION_ENTITY_TYPE_NOT_FOUND + success: true, + message: + constants.apiResponses.SOLUTION_NOT_FOUND_OR_NOT_A_TARGETED, + result: response }); } - let filterQuery = { - "registryDetails.code" : requestedData[solutionData[0].entityType] + response.isATargetedSolution = true; + return resolve({ + success: true, + message: constants.apiResponses.SOLUTION_DETAILS_VERIFIED, + result: response, + }); + } catch (error) { + return resolve({ + success: false, + status: error.status + ? error.status + : httpStatusCode['internal_server_error'].status, + message: error.message + }); + } + }); + } + + /** + * Verify Solution details. + * @method + * @name verifySolutionDetails + * @param {String} solutionId - Program Id. + * @returns {Object} - Details of the solution. + */ + + static verifySolutionDetails(link = "", userId = "", userToken = "") { + return new Promise(async (resolve, reject) => { + try { + let response = { + verified: false, }; - if( gen.utils.checkValidUUID( requestedData[solutionData[0].entityType] ) ) { - filterQuery = { - "registryDetails.locationId" : requestedData[solutionData[0].entityType] - }; - } + if ( userToken == "" ) { + throw new Error(constants.apiResponses.REQUIRED_USER_AUTH_TOKEN); + } - let entities = await entitiesHelper.entityDocuments(filterQuery,["metaInformation.name","entityType"]) + if ( userId == "" ) { + throw new Error(constants.apiResponses.USER_ID_REQUIRED_CHECK); + } - if( !entities.length > 0 ) { - throw { - message : constants.apiResponses.ENTITY_NOT_FOUND - } + let solutionData = await this.solutionDocuments( + { + link: link, + isReusable: false, + status: { + $ne: constants.common.INACTIVE, + }, + }, + ["type", "status", "endDate"] + ); + + if ( !Array.isArray(solutionData) || solutionData.length < 1 ) { + return resolve({ + message: constants.apiResponses.INVALID_LINK, + result: [], + }); + } + + if ( solutionData[0].status !== constants.common.ACTIVE ) { + return resolve({ + message: constants.apiResponses.LINK_IS_EXPIRED, + result: [], + }); } - if( entities[0].metaInformation && entities[0].metaInformation.name ) { - entities[0]["entityName"] = entities[0].metaInformation.name; - delete entities[0].metaInformation; + if ( + solutionData[0].endDate && + new Date() > new Date(solutionData[0].endDate) + ) { + + if ( solutionData[0].status === constants.common.ACTIVE ) { + let updateSolution = await this.update( + solutionData[0]._id, + { + status: constants.common.INACTIVE, + }, + userId + ); + } + + return resolve({ + message: constants.apiResponses.LINK_IS_EXPIRED, + result: [], + }); } + response.verified = true; return resolve({ - message : constants.apiResponses.SOLUTION_TARGETED_ENTITY, - success : true, - data : entities[0] + message: constants.apiResponses.LINK_VERIFIED, + result: response, }); + } + catch (error) { + return resolve({ + success: false, + status: error.status + ? error.status + : httpStatusCode['internal_server_error'].status, + message: error.message, + }); + } + }); + } + + /** + * Check the user is targeted. + * @method + * @name checkForTargetedSolution + * @param {String} link - Solution link. + * @returns {Object} - Details of the solution. + */ + + static checkForTargetedSolution( + link = "", + bodyData = {}, + userId = "", + userToken = "" + ) { + return new Promise(async (resolve, reject) => { + try { + + let response = { + isATargetedSolution: false, + link: link, + }; + + let solutionDetails = await this.solutionDocuments({ link: link }, [ + "type", + "_id", + "programId", + "name", + "projectTemplateId" + ]); + + let queryData = await this.queryBasedOnRoleAndLocation(bodyData); + if ( !queryData.success ) { + return resolve(queryData); + } + + queryData.data["link"] = link; + let matchQuery = queryData.data; + + let solutionData = await this.solutionDocuments(matchQuery, [ + "_id", + "link", + "type", + "programId", + "name", + "projectTemplateId" + ]); + + if ( !Array.isArray(solutionData) || solutionData.length < 1 ) { + + response.solutionId = solutionDetails[0]._id; + response.type = solutionDetails[0].type; + response.name = solutionDetails[0].name; + response.programId = solutionDetails[0].programId; + + return resolve({ + success: true, + message: + constants.apiResponses.SOLUTION_NOT_FOUND_OR_NOT_A_TARGETED, + result: response + }); + } + + response.isATargetedSolution = true; + Object.assign(response, solutionData[0]); + response.solutionId = solutionData[0]._id; + response.projectTemplateId = solutionDetails[0].projectTemplateId ? solutionDetails[0].projectTemplateId : ""; + delete response._id; - } catch(error) { return resolve({ - success : false, - status : error.status ? - error.status : httpStatusCode['internal_server_error'].status, - message : error.message - }) + success: true, + message: constants.apiResponses.SOLUTION_DETAILS_VERIFIED, + result: response, + }); + + } catch (error) { + return resolve({ + success: false, + status: error.status + ? error.status + : httpStatusCode['internal_server_error'].status, + message: error.message, + }); } - }) - } + }); + } + + /** + * Fetch template observation based on solution Id. + * @method + * @name details + * @param {String} solutionId - Solution Id. + * @returns {Object} - Details of the solution. + */ + + static details(solutionId, bodyData = {}, userId = "", userToken = "") { + return new Promise(async (resolve, reject) => { + try { + + let solutionData = await this.solutionDocuments({ _id: solutionId }, [ + "type", + "projectTemplateId", + "programId" + ]); + + + if ( !Array.isArray(solutionData) || solutionData.length < 1 ) { + return resolve({ + message: constants.apiResponses.SOLUTION_NOT_FOUND, + result: [], + }); + } + + solutionData = solutionData[0]; + let templateOrQuestionDetails; + //this will get wether user is targeted to the solution or not based on user Role Information + const isSolutionTargeted = await this.isTargetedBasedOnUserProfile(solutionId, bodyData) + + if ( solutionData.type === constants.common.IMPROVEMENT_PROJECT ) { + if ( !solutionData.projectTemplateId ) { + throw { + message: constants.apiResponses.PROJECT_TEMPLATE_ID_NOT_FOUND, + }; + } + + templateOrQuestionDetails = + await improvementProjectService.getTemplateDetail( + solutionData.projectTemplateId, + userToken, + isSolutionTargeted.result.isATargetedSolution ? false : true + ); + + } else if ( solutionData.type === constants.common.OBSERVATION ) { + + templateOrQuestionDetails = + await surveyService.getQuestions( + solutionData._id, + userToken + ); + } else { + + templateOrQuestionDetails = { + status : httpStatusCode.ok.status, + message : constants.apiResponses.SOLUTION_TYPE_INVALID, + result : {} + } + + } + + if ( solutionData.programId ) { + // add ["rootOrganisations","requestForPIIConsent","programJoined"] values to response. Based on these values front end calls PII consent + let programData = await programsHelper.programDocuments({ + _id : solutionData.programId + },["rootOrganisations","requestForPIIConsent"]); + + templateOrQuestionDetails.result.rootOrganisations = (programData[0].rootOrganisations) ? programData[0].rootOrganisations[0] : ""; + if( programData[0].hasOwnProperty('requestForPIIConsent') ) { + templateOrQuestionDetails.result.requestForPIIConsent = programData[0].requestForPIIConsent; + } + } + + //Check data present in programUsers collection. + //checkForUserJoinedProgramAndConsentShared will returns an object which contain joinProgram and consentShared status + let programJoinStatus = await programUsersHelper.checkForUserJoinedProgramAndConsentShared(solutionData.programId,userId); + templateOrQuestionDetails.result.programJoined = programJoinStatus.joinProgram; + templateOrQuestionDetails.result.consentShared = programJoinStatus.consentShared; + + return resolve(templateOrQuestionDetails); + + } catch (error) { + return resolve({ + success: false, + status: error.status + ? error.status + : httpStatusCode['internal_server_error'].status, + message: error.message, + }); + } + }); + } + + /** + * Solution Report Information. + * @method + * @name read + * @param {String} solutionId - Solution Id. + * @param {String} userId - User Id. + * @returns {Object} - Report Information of the solution. + */ + + // static read(solutionId, userId) { + // return new Promise(async (resolve, reject) => { + // try { + + // let userInformation = await userExtensionsHelperV2.userExtensionDocument({ + // userId: userId, + // "platformRoles.code" : { $in : ["PROGRAM_MANAGER","PROGRAM_DESIGNER"]}, + // status: constants.common.ACTIVE, + // isDeleted: false + // }, { _id: 1, "platformRoles.programs" :1}); + + // if ( !userInformation ) { + // return resolve({ + // status: httpStatusCode.bad_request.status, + // message: constants.apiResponses.NOT_AUTHORIZED_TO_ACCESS + // }) + // } + + // let userPrograms = userInformation.platformRoles ? userInformation.platformRoles : []; + // let programs = []; + + // if ( !userPrograms.length > 0 ) { + // return resolve({ + // status: httpStatusCode.bad_request.status, + // message: constants.apiResponses.NOT_AUTHORIZED_TO_ACCESS + // }) + // } + + // userPrograms.map(eachProgram => { + // if ( eachProgram["programs"] && eachProgram["programs"].length > 0 ) { + // programs.push(...eachProgram["programs"]); + // } + // }); + + // if ( !programs.length > 0 ) { + // return resolve({ + // status: httpStatusCode.bad_request.status, + // message: constants.apiResponses.NOT_AUTHORIZED_TO_ACCESS + // }) + // } + + // let solutionData = await this.solutionDocuments({ + // _id: solutionId, + // isDeleted: false, + // programId : { $in : programs } + // },["reportInformation"]); + + // if ( !Array.isArray(solutionData) || solutionData.length < 1 ) { + // return resolve({ + // message: constants.apiResponses.SOLUTION_NOT_FOUND, + // result: {}, + // }); + // } + + // solutionData = solutionData[0]; + + // return resolve({ + // message: constants.apiResponses.SOLUTION_DETAILS_FETCHED, + // success: true, + // result: solutionData.reportInformation ? solutionData.reportInformation : {}, + // }); + + // } catch (error) { + // return resolve({ + // success: false, + // status: error.status + // ? error.status + // : httpStatusCode['internal_server_error'].status, + // message: error.message, + // }); + // } + // }); + // } }; @@ -1483,6 +2130,33 @@ function _targetedSolutionTypes() { return [ constants.common.OBSERVATION, constants.common.SURVEY, - constants.common.IMPROVEMENT_PROJECT + constants.common.IMPROVEMENT_PROJECT, + constants.common.COURSE ] } + +/** + * Generate sharing Link. + * @method + * @name _targetedSolutionTypes + * @returns {Array} - Targeted solution types + */ + +function _generateLink(appsPortalBaseUrl, prefix, solutionLink, solutionType) { + + let link; + + switch (solutionType) { + case constants.common.OBSERVATION: + link = appsPortalBaseUrl + prefix + constants.common.CREATE_OBSERVATION + solutionLink; + break; + case constants.common.IMPROVEMENT_PROJECT: + link = appsPortalBaseUrl + prefix + constants.common.CREATE_PROJECT + solutionLink; + break; + default: + link = appsPortalBaseUrl + prefix + constants.common.CREATE_SURVEY + solutionLink; + } + + return link; + +} diff --git a/module/solutions/validator/v1.js b/module/solutions/validator/v1.js index 6b3e61d6..b4024224 100644 --- a/module/solutions/validator/v1.js +++ b/module/solutions/validator/v1.js @@ -10,10 +10,7 @@ module.exports = (req) => { let solutionsValidator = { create : function () { - req.checkBody('createdFor').exists().withMessage("required organisation created for id"); - req.checkBody('rootOrganisations').exists().withMessage("required root organisations id"); req.checkBody('programExternalId').exists().withMessage("required program externalId"); - req.checkBody('entityType').exists().withMessage("required entity type"); req.checkBody('externalId').exists().withMessage("required solution externalId"); req.checkBody('name').exists().withMessage("required solution name"); req.checkBody('type').exists().withMessage("required solution type"); @@ -47,8 +44,11 @@ module.exports = (req) => { getSolutions : function () { req.checkQuery("type").exists().withMessage("required solution type") }, - targetedEntity : function () { + fetchLink : function () { req.checkParams("_id").exists().withMessage("required solution id"); + }, + verifyLink : function () { + req.checkParams("_id").exists().withMessage("required solution link"); } } diff --git a/module/static-links/helper.js b/module/static-links/helper.js index d6215a26..6da58a4d 100644 --- a/module/static-links/helper.js +++ b/module/static-links/helper.js @@ -57,206 +57,5 @@ module.exports = class StaticLinksHelper { } - /** - * List static links. - * @method - * @name list - * @param {String} [appType = "" ] - app type. - * @param {String} [appname = "" ] - name of app. - * @returns {Array} List of static links data. - */ - - static list(appType = "", appname = "") { - return new Promise(async (resolve, reject) => { - try { - - let appSpecificQuery = { - status: constants.common.ACTIVE, - isDeleted: false - }; - - if (appType && appType !== "") { - appSpecificQuery["appType"] = appType; - } - - let commonSpecificQuery = { ...appSpecificQuery }; - - if (appname && appname !== "") { - appSpecificQuery["appName"] = appname; - } - - let projection = ["value", "link", "title", "metaInformation"]; - let appSpecificStaticLinks = []; - - - if (appSpecificQuery.appType && appSpecificQuery.appName) { - appSpecificStaticLinks = - await this.staticLinksDocuments(appSpecificQuery, projection); - } - - if (appSpecificStaticLinks.length > 0) { - - let appSpecificValue = []; - - appSpecificStaticLinks.forEach(staticLink => { - appSpecificValue.push(staticLink.value); - }); - - commonSpecificQuery["value"] = { $nin: appSpecificValue }; - } - - commonSpecificQuery.isCommon = true; - - let commonStaticLinks = - await this.staticLinksDocuments(commonSpecificQuery, projection); - - let result = {}; - - appSpecificStaticLinks = _.keyBy(appSpecificStaticLinks, 'value'); - commonStaticLinks = _.keyBy(commonStaticLinks, "value"); - result = { ...appSpecificStaticLinks, ...commonStaticLinks }; - - return resolve({ - message: constants.apiResponses.STATIC_LINKS_FETCHED, - result: result - }); - - } catch (error) { - return reject(error); - } - }) - - - } - - /** - * Bulk create static links. - * @method - * @name bulkCreate - * @param {Object} staticLinksCSVData - array of static links - * @param {Object} userDetails - logged in user data. - * @param {String} userDetails.id - logged in user id. - * @returns {Array} List of static links with _SYSTEM_ID. - */ - - static bulkCreate(staticLinksCSVData, userDetails) { - - return new Promise(async (resolve, reject) => { - try { - - const staticLinksUploadedData = await Promise.all( - staticLinksCSVData.map(async staticLink => { - - - try { - - let newLink = await database.models.staticLinks.create( - _.merge({ - "status": "active", - "updatedBy": userDetails.id, - "createdBy": userDetails.id - }, gen.utils.valueParser(staticLink)) - ); - - if (newLink._id) { - staticLink["_SYSTEM_ID"] = newLink._id; - staticLink.status = "Success"; - } else { - staticLink["_SYSTEM_ID"] = ""; - staticLink.status = "Failed"; - } - - } catch (error) { - staticLink["_SYSTEM_ID"] = ""; - staticLink.status = error.message; - } - - - return staticLink; - }) - ) - - - return resolve(staticLinksUploadedData); - - } catch (error) { - return reject(error) - } - }) - - } - - /** - * Bulk update static links. - * @method - * @name bulkUpdate - * @param {Object} staticLinksCSVData - array of static links - * @param {Object} userDetails - logged in user data. - * @param {String} userDetails.id - logged in user id. - * @returns {Array} List of static links with _SYSTEM_ID. - */ - - static bulkUpdate(staticLinksCSVData, userDetails) { - - return new Promise(async (resolve, reject) => { - try { - - const staticLinksUploadedData = await Promise.all( - staticLinksCSVData.map(async staticLink => { - - try { - - let updatedData = - _.merge({ - "updatedBy": userDetails.id - }, gen.utils.valueParser(staticLink)); - - let unSetData = {}; - - if (staticLink.appName && staticLink.appName !== "") { - updatedData["isCommon"] = false; - } else { - updatedData["isCommon"] = true; - unSetData["appName"] = 1; - } - - let updateLink = await database.models.staticLinks.findOneAndUpdate( - { - _id: staticLink._SYSTEM_ID - }, - { - "$unset": unSetData, - "$set": _.omit(updatedData, ["_SYSTEM_ID"]) - } - ); - - if (updateLink._id) { - staticLink["_SYSTEM_ID"] = updateLink._id; - staticLink.status = "Success"; - } else { - staticLink["_SYSTEM_ID"] = ""; - staticLink.status = "Failed"; - } - - } catch (error) { - staticLink["_SYSTEM_ID"] = ""; - staticLink.status = error.message; - } - - - return staticLink; - }) - ) - - - return resolve(staticLinksUploadedData); - - } catch (error) { - return reject(error); - } - }) - - } - } \ No newline at end of file diff --git a/module/static-links/validator/v1.js b/module/static-links/validator/v1.js index d2af8004..27caa984 100644 --- a/module/static-links/validator/v1.js +++ b/module/static-links/validator/v1.js @@ -8,11 +8,6 @@ module.exports = (req) => { let staticLinksValidator = { - list: function () { - req.checkHeaders('apptype').exists().withMessage("required app type") - } - - } if (staticLinksValidator[req.params.method]) { diff --git a/module/user-extension/helper.js b/module/user-extension/helper.js index 4c9f80b0..89c1d646 100644 --- a/module/user-extension/helper.js +++ b/module/user-extension/helper.js @@ -14,7 +14,7 @@ const entityTypesHelper = require(MODULES_BASE_PATH + "/entityTypes/helper"); const entitiesHelper = require(MODULES_BASE_PATH + "/entities/helper"); const userRolesHelper = require(MODULES_BASE_PATH + "/user-roles/helper"); -const elasticSearch = require(GENERIC_HELPERS_PATH + "/elastic-search"); +// const elasticSearch = require(GENERIC_HELPERS_PATH + "/elastic-search"); const programsHelper = require(MODULES_BASE_PATH + "/programs/helper"); const solutionsHelper = require(MODULES_BASE_PATH + "/solutions/helper"); @@ -187,219 +187,6 @@ module.exports = class UserExtensionHelper { }) } - /** - * Get profile with entity details - * @method - * @name profileWithEntityDetails - * @param {Object} filterQueryObject - filtered data. - * @returns {Object} - */ - - static profileWithEntityDetails(filterQueryObject, appName) { - return new Promise(async (resolve, reject) => { - try { - - const entityTypesArray = await entityTypesHelper.entityTypesDocument( - "all", - [ - "name", - "immediateChildrenEntityType" - ] - ); - - let enityTypeToImmediateChildrenEntityMap = {}; - - if (entityTypesArray.length > 0) { - entityTypesArray.forEach(entityType => { - enityTypeToImmediateChildrenEntityMap[entityType.name] = (entityType.immediateChildrenEntityType && entityType.immediateChildrenEntityType.length > 0) ? entityType.immediateChildrenEntityType : []; - }) - } - - let queryObject = [ - { - $match: filterQueryObject - }, - { - $lookup: { - "from": "entities", - "localField": "roles.entities", - "foreignField": "_id", - "as": "entityDocuments" - } - }, - { - $lookup: { - "from": "userRoles", - "localField": "roles.roleId", - "foreignField": "_id", - "as": "roleDocuments" - } - }, - { - $project: { - "externalId": 1, - "roles": 1, - "roleDocuments._id": 1, - "roleDocuments.code": 1, - "roleDocuments.title": 1, - "entityDocuments._id": 1, - "entityDocuments.metaInformation.externalId": 1, - "entityDocuments.metaInformation.name": 1, - "entityDocuments.groups": 1, - "entityDocuments.entityType": 1, - "entityDocuments.entityTypeId": 1, - "ratings": 1 - } - } - ]; - - let userExtensionData = - await database.models.userExtension.aggregate(queryObject); - let relatedEntities = []; - - if (userExtensionData[0]) { - - let roleMap = {}; - - if (userExtensionData[0].entityDocuments && userExtensionData[0].entityDocuments.length > 0) { - - let projection = [ - "metaInformation.externalId", - "metaInformation.name", - "metaInformation.addressLine1", - "metaInformation.addressLine2", - "metaInformation.administration", - "metaInformation.city", - "metaInformation.country", - "entityTypeId", - "entityType" - ]; - - relatedEntities = - await entitiesHelper.relatedEntities( - userExtensionData[0].entityDocuments[0]._id, - userExtensionData[0].entityDocuments[0].entityTypeId, - userExtensionData[0].entityDocuments[0].entityType, - projection - ); - - } - - // <- Dirty fix. Profile update for only Goa state. - // In future can be removed if required for all state. - - let goaStateExists = false; - let goaState = constants.common.GOA_STATE.toUpperCase(); - - if (relatedEntities.length > 0) { - - let checkGoaStateExistsOrNot = relatedEntities.some( - entity => entity.metaInformation.name.toUpperCase() === goaState - ); - - if ( - checkGoaStateExistsOrNot && - appName === constants.common.UNNATI_APP_NAME - ) { - goaStateExists = true; - } - - } - - if (userExtensionData[0].roleDocuments && userExtensionData[0].roleDocuments.length > 0) { - - userExtensionData[0].roleDocuments.forEach(role => { - roleMap[role._id.toString()] = role; - }) - let entityMap = {}; - - if (userExtensionData[0].entityDocuments && userExtensionData[0].entityDocuments.length > 0) { - - userExtensionData[0].entityDocuments.forEach(entity => { - entity.metaInformation.childrenCount = 0; - entity.metaInformation.entityType = entity.entityType; - entity.metaInformation.entityTypeId = entity.entityTypeId; - entity.metaInformation.subEntityGroups = new Array; - - Array.isArray(enityTypeToImmediateChildrenEntityMap[entity.entityType]) && enityTypeToImmediateChildrenEntityMap[entity.entityType].forEach(immediateChildrenEntityType => { - if (entity.groups && entity.groups[immediateChildrenEntityType]) { - entity.metaInformation.immediateSubEntityType = immediateChildrenEntityType; - entity.metaInformation.childrenCount = entity.groups[immediateChildrenEntityType].length; - } - }) - - entity.groups && Array.isArray(Object.keys(entity.groups)) && Object.keys(entity.groups).forEach(subEntityType => { - entity.metaInformation.subEntityGroups.push(subEntityType); - }) - - // <- Dirty fix. Profile update for only Goa state. - // In future can be removed if required for all state. - - if ( - appName === constants.common.UNNATI_APP_NAME && - entity.metaInformation.name.toUpperCase() === goaState - ) { - goaStateExists = true; - } - - entityMap[entity._id.toString()] = entity; - }) - } - - for (let userExtensionRoleCounter = 0; userExtensionRoleCounter < userExtensionData[0].roles.length; userExtensionRoleCounter++) { - for (let userExtenionRoleEntityCounter = 0; userExtenionRoleEntityCounter < userExtensionData[0].roles[userExtensionRoleCounter].entities.length; userExtenionRoleEntityCounter++) { - userExtensionData[0].roles[userExtensionRoleCounter].entities[userExtenionRoleEntityCounter] = { - _id: entityMap[userExtensionData[0].roles[userExtensionRoleCounter].entities[userExtenionRoleEntityCounter].toString()]._id, - ...entityMap[userExtensionData[0].roles[userExtensionRoleCounter].entities[userExtenionRoleEntityCounter].toString()].metaInformation - }; - } - roleMap[userExtensionData[0].roles[userExtensionRoleCounter].roleId.toString()].immediateSubEntityType = (userExtensionData[0].roles[userExtensionRoleCounter].entities[0] && userExtensionData[0].roles[userExtensionRoleCounter].entities[0].entityType) ? userExtensionData[0].roles[userExtensionRoleCounter].entities[0].entityType : ""; - roleMap[userExtensionData[0].roles[userExtensionRoleCounter].roleId.toString()].entities = userExtensionData[0].roles[userExtensionRoleCounter].entities; - } - } - - let showPopupForm = false; - let userProfile = await database.models.userProfile.findOne( - { - userId: filterQueryObject.userId, - status: { - $in: [ - constants.common.USER_PROFILE_VERIFIED_STATUS, - constants.common.USER_PROFILE_PENDING_STATUS - ] - } - }, { _id: 1, status: 1 }).lean(); - - if (userProfile == null) { - showPopupForm = true - } - - return resolve( - _.merge(_.omit( - userExtensionData[0], - ["roles", "entityDocuments", "roleDocuments"] - ), - { roles: _.isEmpty(roleMap) ? [] : Object.values(roleMap) }, - { relatedEntities: relatedEntities }, - { - allowProfileUpdateForm: goaStateExists - }, { - showPopupForm: goaStateExists && showPopupForm ? true : false - } - ) - ); - } else { - return resolve({ - status: httpStatusCode['bad_request'].status, - message: constants.apiResponses.USER_EXTENSION_NOT_FOUND - }); - } - } catch (error) { - return reject(error); - } - }) - } - /** * Update user profile data. * @method @@ -431,159 +218,6 @@ module.exports = class UserExtensionHelper { }); } - /** - * Update profile roles - * @method - * @name updateProfileRoles - * @param {Object} requestedData - requested data. - * @param {String} userId - Logged in user id. - * @param {String} userName - Logged in user name. - * @returns {Object} - */ - - static updateProfileRoles(requestedData, userId, userName) { - - return new Promise(async (resolve, reject) => { - try { - - const userExtensionData = await this.userExtensionDocument({ - userId: userId - }, { - "roles": 1, - "createdBy": 1, - "externalId": 1 - }); - - if ( - userExtensionData && - userExtensionData.roles && userExtensionData.roles.length > 0 - ) { - return resolve({ - message: constants.apiResponses.USER_DATA_EXISTS, - result: [] - }) - } - - const entityData = await entitiesHelper.entityDocuments({ - _id: requestedData.stateId - }, ["_id", "metaInformation.name"]); - - if (!entityData.length > 0) { - return resolve({ - message: constants.apiResponses.STATE_NOT_FOUND, - result: [] - }) - } - - requestedData["state"] = { - _id: ObjectId(requestedData.stateId), - name: entityData[0].metaInformation.name - } - - for ( - let pointerToRole = 0; - pointerToRole < requestedData.roles.length; - pointerToRole++ - ) { - - const rolesData = - await userRolesHelper.roleDocuments( - { - _id: requestedData.roles[pointerToRole]._id - }, [ - "code", - "entityTypes" - ] - ); - - if (!rolesData.length > 0) { - return resolve({ - message: constants.apiResponses.USER_ROLES_NOT_FOUND, - result: [] - }); - } - - let entityTypes = rolesData[0].entityTypes.map(roleData => { - return roleData.entityType; - }) - - let entities = []; - - if ( !requestedData.roles[pointerToRole].entities || (requestedData.roles[pointerToRole].entities && requestedData.roles[pointerToRole].entities.length == 0 )) { - entities.push(requestedData.stateId); - } - - if (requestedData.roles[pointerToRole].entities && requestedData.roles[pointerToRole].entities.length > 0) { - const entitiesData = await entitiesHelper.entityDocuments({ - _id: { $in: requestedData.roles[pointerToRole].entities }, - entityType: { $in: entityTypes } - }, ["_id"]); - - if (entitiesData.length > 0) { - - entities = entitiesData.map(entity => { - return entity._id - }); - } - } - - requestedData.roles[pointerToRole].roleId = rolesData[0]._id; - requestedData.roles[pointerToRole].code = rolesData[0].code; - requestedData.roles[pointerToRole].entities = entities; - - delete requestedData.roles[pointerToRole]._id; - } - - if (!userExtensionData) { - - requestedData.userId = userId; - requestedData.createdBy = - userExtensionData && userExtensionData.createdBy ? - userExtensionData.createdBy : userId; - - requestedData.status = constants.common.ACTIVE; - requestedData.externalId = - userExtensionData && userExtensionData.externalId ? - userExtensionData.externalId : userName; - - requestedData.isDeleted = false; - } - - requestedData.updatedBy = userId; - - await database.models.userExtension.findOneAndUpdate({ - userId: userId - }, { - $set: requestedData - }, { - upsert: true, - new: true - }); - - const responseData = await this.profileWithEntityDetailsV2({ - userId: userId, - status: constants.common.ACTIVE, - isDeleted: false - }); - - //update user role in elasticsearch - if (requestedData.roles.length > 0) { - await this.updateUserRolesInEntitiesElasticSearch(userId, requestedData.roles); - } - - await this.pushUserToElasticSearch(userId); - - resolve({ - message: constants.apiResponses.USER_EXTENSION_UPDATED, - result: responseData.result - }); - - } catch (error) { - return reject(error); - } - }) - } - /** * Get profile with entity details * @method @@ -764,151 +398,6 @@ module.exports = class UserExtensionHelper { }) } - /** - * Get profile with entity details - * @method - * @name getEntities - * @param {Object} userId - Logged in user id. - * @param {String} entityType - entity type - * @param {String} pageSize - Size of page. - * @param {String} pageNo - Recent page no. - * @param {String} search - search text. - * @returns {Object} - */ - - static getEntities(userId, entityType, pageSize, pageNo, searchText) { - return new Promise(async (resolve, reject) => { - try { - - let filterQueryObject = { - userId : userId, - status: constants.common.ACTIVE, - isDeleted: false - } - - if (searchText !== "") { - filterQueryObject["$or"] = [ - { "entityDocuments.metaInformation.name": new RegExp(searchText, 'i') }, - ]; - } - if (entityType) { - filterQueryObject['entityDocuments.entityType'] = entityType; - } - let queryObject = [ - { - $lookup: { - "from": "entities", - "localField": "roles.entities", - "foreignField": "_id", - "as": "entityDocuments" - } - }, - { - $unwind: "$entityDocuments" - }, - { - $match: filterQueryObject - }, - { - $facet: { - "totalCount": [ - { "$count": "count" } - ], - "data": [ - { $skip: pageSize * (pageNo - 1) }, - { $limit: pageSize } - ], - } - }, { - $project: { - "data": 1, - "count": { - $arrayElemAt: ["$totalCount.count", 0] - } - } - } - ]; - - let userExtensionData = - await database.models.userExtension.aggregate(queryObject); - - - let entityDocuments = []; - if (userExtensionData[0]) { - userExtensionData[0].data.forEach(entity => { - entityDocuments.push({ - _id: entity.entityDocuments._id, - name: entity.entityDocuments.metaInformation.name, - }); - }); - return resolve({ data: entityDocuments, count: userExtensionData[0].count }) - - } else { - return resolve({ - status: httpStatusCode['bad_request'].status, - message: constants.apiResponses.USER_ENTITIES_NOT_FOUND - }); - } - } catch (error) { - return reject(error); - } - }) - } - - /** - * Get user entity types - * @method - * @name getEntityTypes - * @param {Object} filterQueryObject - filtered data. - * @returns {Object} - */ - - static getEntityTypes(filterQueryObject) { - return new Promise(async (resolve, reject) => { - try { - - let entityTypes = []; - let queryObject = [ - { - $lookup: { - "from": "entities", - "localField": "roles.entities", - "foreignField": "_id", - "as": "entityDocuments" - } - }, - { - $match: filterQueryObject - } - ]; - - let userExtensionData = - await database.models.userExtension.aggregate(queryObject); - - if (userExtensionData[0]) { - - if (userExtensionData[0].entityDocuments && userExtensionData[0].entityDocuments.length > 0) { - - userExtensionData[0].entityDocuments.forEach(entity => { - if (entity.entityType && !entityTypes.includes(entity.entityType)) { - entityTypes.push(entity.entityType); - } - }); - } - return resolve(entityTypes) - - } else { - return resolve({ - status: httpStatusCode['bad_request'].status, - message: constants.apiResponses.USER_ENTITY_TYPE_NOT_FOUND - }); - } - } catch (error) { - return reject(error); - } - }) - } - /** * Update user roles in entities elastic search * @method @@ -916,66 +405,66 @@ module.exports = class UserExtensionHelper { * @name userId - user id * @name userRoles - array of userRoles. */ -static updateUserRolesInEntitiesElasticSearch(userId = "", userRoles = []) { - return new Promise(async (resolve, reject) => { - try { +// static updateUserRolesInEntitiesElasticSearch(userId = "", userRoles = []) { +// return new Promise(async (resolve, reject) => { +// try { - await Promise.all(userRoles.map( async role => { - await Promise.all(role.entities.map(async entity => { - - let entityDocument = await elasticSearch.getData - ({ - id : entity, - index : process.env.ELASTICSEARCH_ENTITIES_INDEX, - type : "_doc" - }) +// await Promise.all(userRoles.map( async role => { +// await Promise.all(role.entities.map(async entity => { + +// let entityDocument = await elasticSearch.getData +// ({ +// id : entity, +// index : process.env.ELASTICSEARCH_ENTITIES_INDEX, +// type : "_doc" +// }) - if (entityDocument.statusCode == httpStatusCode.ok.status) { +// if (entityDocument.statusCode == httpStatusCode.ok.status) { - entityDocument = entityDocument.body["_source"].data; +// entityDocument = entityDocument.body["_source"].data; - if (!entityDocument.roles) { - entityDocument.roles = {}; - } +// if (!entityDocument.roles) { +// entityDocument.roles = {}; +// } - if (entityDocument.roles[role.code]) { - if (!entityDocument.roles[role.code].includes(userId)) { - entityDocument.roles[role.code].push(userId); - - await elasticSearch.createOrUpdateDocumentInIndex - ( - process.env.ELASTICSEARCH_ENTITIES_INDEX, - "_doc", - entity, - {data: entityDocument } - ) - } - } - else { - entityDocument.roles[role.code] = [userId]; - - await elasticSearch.createOrUpdateDocumentInIndex - ( - process.env.ELASTICSEARCH_ENTITIES_INDEX, - "_doc", - entity, - {data: entityDocument } - ) - } - } - })) - })) - - return resolve({ - success: true - }); - - } - catch (error) { - return reject(error); - } -}) -} +// if (entityDocument.roles[role.code]) { +// if (!entityDocument.roles[role.code].includes(userId)) { +// entityDocument.roles[role.code].push(userId); + +// await elasticSearch.createOrUpdateDocumentInIndex +// ( +// process.env.ELASTICSEARCH_ENTITIES_INDEX, +// "_doc", +// entity, +// {data: entityDocument } +// ) +// } +// } +// else { +// entityDocument.roles[role.code] = [userId]; + +// await elasticSearch.createOrUpdateDocumentInIndex +// ( +// process.env.ELASTICSEARCH_ENTITIES_INDEX, +// "_doc", +// entity, +// {data: entityDocument } +// ) +// } +// } +// })) +// })) + +// return resolve({ +// success: true +// }); + +// } +// catch (error) { +// return reject(error); +// } +// }) +// } /** @@ -986,46 +475,46 @@ static updateUserRolesInEntitiesElasticSearch(userId = "", userRoles = []) { * @returns {Object} */ - static pushUserToElasticSearch(userId) { - return new Promise(async (resolve, reject) => { - try { - - let userInformation = await this.userExtensionDocument - ( - { userId: userId }, - [ "_id", - "status", - "isDeleted", - "deleted", - "roles", - "userId", - "externalId", - "updatedBy", - "createdBy", - "updatedAt", - "createdAt"] - ) +// static pushUserToElasticSearch(userId) { +// return new Promise(async (resolve, reject) => { +// try { + +// let userInformation = await this.userExtensionDocument +// ( +// { userId: userId }, +// [ "_id", +// "status", +// "isDeleted", +// "deleted", +// "roles", +// "userId", +// "externalId", +// "updatedBy", +// "createdBy", +// "updatedAt", +// "createdAt"] +// ) - await elasticSearch.createOrUpdateDocumentInIndex( - process.env.ELASTICSEARCH_USER_EXTENSION_INDEX, - "_doc", - userId, - { - data : userInformation - } - ); - - return resolve({ - success : true - }); +// await elasticSearch.createOrUpdateDocumentInIndex( +// process.env.ELASTICSEARCH_USER_EXTENSION_INDEX, +// "_doc", +// userId, +// { +// data : userInformation +// } +// ); + +// return resolve({ +// success : true +// }); - } - catch(error) { - return reject(error); - } - }) +// } +// catch(error) { +// return reject(error); +// } +// }) - } +// } /** * List of programs for platform user @@ -1055,7 +544,7 @@ static updateUserRolesInEntitiesElasticSearch(userId = "", userRoles = []) { } const userInformation = await this.userExtensionDocument(findQuery,projection); - + if (!userInformation) { return resolve({ status: httpStatusCode.bad_request.status, @@ -1082,17 +571,22 @@ static updateUserRolesInEntitiesElasticSearch(userId = "", userRoles = []) { } for( let program = 0; program < currentRole.programs.length;program++) { - programMapToRole[currentRole.programs[program].toString()] = currentRole.code; + if(programMapToRole[currentRole.programs[program].toString()] && programMapToRole[currentRole.programs[program].toString()].length > 0){ + programMapToRole[currentRole.programs[program].toString()].push(currentRole.code); + }else { + programMapToRole[currentRole.programs[program].toString()] = [currentRole.code]; + } + programIds.push(currentRole.programs[program]); } } } - + const programData = await programsHelper.programDocuments({ _id: {$in: programIds}, status: constants.common.ACTIVE - },["externalId","name","description"]); + },["externalId","name","description","requestForPIIConsent"]); if (programData.length > 0) { programsDocuments = programData.map(program => { @@ -1165,14 +659,17 @@ static updateUserRolesInEntitiesElasticSearch(userId = "", userRoles = []) { const solutionDocuments = await solutionsHelper.solutionDocuments({ _id: {$in: programDocuments[0].components}, programId: programId, - isReusable: false + isReusable: false, + isDeleted: false },[ "externalId", "description", "name", "type", "subType", - "isRubricDriven" + "isRubricDriven", + "scoringSystem", + "criteriaLevelReport" ]); return resolve({ diff --git a/module/user-extension/validator/v1.js b/module/user-extension/validator/v1.js index d9b6be9f..7eedcbdb 100644 --- a/module/user-extension/validator/v1.js +++ b/module/user-extension/validator/v1.js @@ -8,10 +8,6 @@ module.exports = (req) => { let userExtensionValidator = { - updateProfileRoles : function () { - req.checkBody('stateId').exists().withMessage("State id is required"); - req.checkBody('roles').exists().withMessage("Roles data is required"); - }, solutions: function () { req.checkParams('_id').exists().withMessage('Required program id'); } diff --git a/module/user-profile/helper.js b/module/user-profile/helper.js index 9c18712f..f32c75b6 100644 --- a/module/user-profile/helper.js +++ b/module/user-profile/helper.js @@ -39,201 +39,6 @@ module.exports = class UserProfileHelper { } - /** - * Get user profile form data. - * @method - * @name getForm - * @returns {json} Response consists of user form data. - */ - - static getForm(loggedInUser, appName = "", device = "") { - return new Promise(async (resolve, reject) => { - try { - - let formData = await formsHelper.formsDocument({ - name: constants.common.USER_PROFILE_FORM_NAME - }); - - if( !formData[0] ) { - return reject({ - message : - constants.apiResponses.COULD_NOT_GET_FORM - }); - } - - let name = appName + "." + device; - let userProfileScreenVisitedTrack = { - [name] : true - }; - - let userExtensionData = - await database.models.userExtension.findOne( - { - userId: loggedInUser.userId - },{ - userProfileScreenVisitedTrack: 1 - }).lean(); - - if ( userExtensionData ) { - let updateData = {}; - - if ( userExtensionData.userProfileScreenVisitedTrack ) { - updateData = userExtensionData.userProfileScreenVisitedTrack; - updateData[name] = true; - } else { - updateData = userProfileScreenVisitedTrack; - } - - database.models.userExtension.findOneAndUpdate( - { - userId: loggedInUser.userId - }, { - "$set": { userProfileScreenVisitedTrack: updateData } - } - ); - } - - let states = - await entitiesHelper.entityDocuments( - { - entityType: constants.common.STATE_ENTITY_TYPE - },[ - "entityTypeId", - "metaInformation.name", - "groups", - "childHierarchyPath" - ] - ); - - let statesInformation = []; - let childHierarchyForState = {}; - - await Promise.all(states.map(async function (state) { - - if ( state.groups && Object.keys(state.groups).length > 0) { - - let stateWithSubEntity = - await _checkStateWithSubEntities( - state.groups, - state.entityTypeId - ); - - if ( stateWithSubEntity ) { - childHierarchyForState[state._id] = - state.childHierarchyPath; - } - } - - statesInformation.push({ - label: state.metaInformation.name, - value: state._id - }); - })); - - let userData = await this.list( { - userId : loggedInUser.userId, - deleted : false - }, { - metaInformation: 1, - _id: 1, - status : 1 - }); - - if ( userData.length < 1 ) { - let userProfileCreated = await this.create( - loggedInUser - ); - - userData = userProfileCreated; - } - - let canSubmit = true; - - if( Array.isArray(userData) && userData.length > 0 ) { - - let verifiedUserProfile = userData.find( - user=>user.status === - constants.common.USER_PROFILE_VERIFIED_STATUS - ); - - let pendingUserProfile = userData.find( - user=>user.status === - constants.common.USER_PROFILE_PENDING_STATUS - ); - - if( verifiedUserProfile ) { - userData = verifiedUserProfile; - - if( pendingUserProfile ) { - canSubmit = false; - } - - } else if(pendingUserProfile) { - userData = pendingUserProfile; - canSubmit = false; - } else { - userData = userData[0]; - } - } - - if( ! _.isEmpty(userData.metaInformation.state)) { - - let entitiesSequence = - childHierarchyForState[userData.metaInformation.state.value.toString()]; - - entitiesSequence.forEach(entitySequence=>{ - - let entityData = userData.metaInformation[entitySequence]; - - if( entityData ) { - - let form = { ...formData[0].value[0] }; - form.label = - entitySequence.charAt(0).toUpperCase() + entitySequence.slice(1); - - form.editable = canSubmit ? canSubmit : false; - form.field = entitySequence; - form.input = "multiselect"; - form.value = entityData; - form.validation = {}; - formData[0].value.push(form); - } - - }) - } - - formData[0].value.forEach(form=>{ - - if( form.field == "state" ) { - - form["options"] = states.map(state=>{ - return { - label : state.metaInformation.name, - value : state._id, - } - }); - } - - form.editable = canSubmit ? canSubmit : false; - form.value = - userData.metaInformation[form.field] ? - userData.metaInformation[form.field] : ""; - }) - - return resolve({ - result : { - form: formData[0].value, - statesWithSubEntities: childHierarchyForState, - canSubmit : canSubmit - }, - message : constants.apiResponses.FORM_FETCH - }); - } catch (error) { - return reject(error); - } - }); - } - /** * Create userProfile * @method @@ -371,118 +176,6 @@ module.exports = class UserProfileHelper { } - /* - * save user profile data. - * @method - * @name save - * @return {json} Response consists of saved user profile data. - */ - - static save( metaInformationData, userId,externalId ) { - return new Promise(async (resolve, reject) => { - try { - - let userProfileData = await this.list({ - userId: userId, - status : - constants.common.USER_PROFILE_PENDING_VERIFICATION_STATUS - },{ - status: 1, - _id: 1 - }); - - if( userProfileData && userProfileData[0] ) { - return resolve({ - message : - constants.apiResponses.PROFILE_UNDER_PENDING_VERIFICATION - }); - } - - - let updateUserProfileData = { - updatedBy : userId, - status : - constants.common.USER_PROFILE_PENDING_VERIFICATION_STATUS, - submittedAt : new Date() - } - - Object.keys(metaInformationData).forEach(metaDataKey=>{ - - if( Array.isArray(metaInformationData[metaDataKey]) && - metaInformationData[metaDataKey].length > 0 - ) { - metaInformationData[metaDataKey].forEach(entity=>{ - entity.value = - gen.utils.convertStringToObjectId(entity.value); - }) - } else { - - if( - typeof(metaInformationData[metaDataKey]) === "string" - ) { - - metaInformationData[metaDataKey] = - gen.utils.convertStringToObjectId( - metaInformationData[metaDataKey] - ); - - } else { - - metaInformationData[metaDataKey].value = - gen.utils.convertStringToObjectId( - metaInformationData[metaDataKey].value - ); - } - } - }); - - updateUserProfileData["metaInformation"] = metaInformationData; - - userProfileData = await this.list({ - userId: userId, - status : constants.common.USER_PROFILE_NOT_VERIFIED_STATUS - },{ - status: 1, - _id: 1 - }); - - let saveUserProfileInformation; - - if( userProfileData && userProfileData[0] ) { - - saveUserProfileInformation = - await database.models.userProfile.findOneAndUpdate( - { - _id: userProfileData[0]._id - },{ - $set : updateUserProfileData - },{ new : true } - ).lean(); - - } else { - - updateUserProfileData["userId"] = userId; - updateUserProfileData["createdBy"] = userId; - updateUserProfileData["externalId"] = externalId; - - saveUserProfileInformation = - await database.models.userProfile.create( - updateUserProfileData - ); - } - - return resolve({ - result : saveUserProfileInformation.metaInformation, - message : constants.apiResponses.USER_PROFILE_SAVED - }); - - } catch (error) { - return reject(error); - } - }); - } - - /** * User profile whose information is not verified or not sent for verification. * @method diff --git a/module/user-profile/validator/v1.js b/module/user-profile/validator/v1.js index 6152b2c2..c3c8fab2 100644 --- a/module/user-profile/validator/v1.js +++ b/module/user-profile/validator/v1.js @@ -2,14 +2,6 @@ module.exports = (req) => { let userProfileValidator = { - getForm : function() { - req.checkHeaders('appname').exists().withMessage("required app name in headers"), - req.checkHeaders('os').exists().withMessage('required os in headers') - }, - save: function () { - req.checkBody('data').exists().withMessage("Required meta information data"); - } - } if (userProfileValidator[req.params.method]) { diff --git a/module/user-roles/helper.js b/module/user-roles/helper.js index 45f893af..a0bfafd2 100644 --- a/module/user-roles/helper.js +++ b/module/user-roles/helper.js @@ -29,7 +29,6 @@ module.exports = class UserRolesHelper { ) { return new Promise(async (resolve, reject) => { try { - let queryObject = (filterQuery != "all") ? filterQuery : {}; let projection = {} @@ -50,7 +49,7 @@ module.exports = class UserRolesHelper { queryObject, projection ).lean(); - + return resolve(userRolesData); } catch (error) { diff --git a/module/users/helper.js b/module/users/helper.js index e863e732..35ee9c8b 100644 --- a/module/users/helper.js +++ b/module/users/helper.js @@ -5,6 +5,7 @@ * Description : All User related information including sys_admin. */ + // Dependencies const programsHelper = require(MODULES_BASE_PATH + "/programs/helper"); const solutionsHelper = require(MODULES_BASE_PATH + "/solutions/helper"); @@ -12,6 +13,10 @@ const userRolesHelper = require(MODULES_BASE_PATH + "/user-roles/helper"); const entitiesHelper = require(MODULES_BASE_PATH + "/entities/helper"); const improvementProjectService = require(ROOT_PATH + "/generics/services/improvement-project"); const userService = require(ROOT_PATH + "/generics/services/users"); +const formService = require(ROOT_PATH + '/generics/services/form'); +const programUsersHelper = require(MODULES_BASE_PATH + "/programUsers/helper"); +const surveyService = require(ROOT_PATH + '/generics/services/survey'); + /** @@ -21,87 +26,6 @@ const userService = require(ROOT_PATH + "/generics/services/users"); module.exports = class UsersHelper { - /** - * create user. - * @method - * @name create - * @param {Object} userData user data. - * @param {String} userData.email email id of the user. - * @param {String} userData.userName name of the user. - * @param {String} userData.role role of the user. - * @returns {Promise} returns a promise. - */ - - static create(userData) { - return new Promise(async (resolve, reject) => { - try { - - let checkUserExistence = - await database.models.users.findOne({ - email: userData.email - }).lean(); - - if (checkUserExistence) { - throw { - message: "User already exists" - } - } - - let createUser = - await database.models.users.create(userData) - - let response = { - success: true, - message: "User created successfully" - } - - if (!createUser) { - response["success"] = false; - response["message"] = "User could not be created"; - } - - return resolve(response); - - } catch (error) { - return reject(error); - } - }) - } - - /** - * check if the provided email is sys admin or not. - * @method - * @name isSystemAdmin - * @param {String} userEmail user email address. - * @returns {Promise} returns a promise. - */ - - static isSystemAdmin(userEmail) { - return new Promise(async (resolve, reject) => { - try { - - let userDocument = - await database.models.users.findOne({ - email: userEmail, - role: "SYS_ADMIN" - }).lean(); - - let response = { - success: true - } - - if (!userDocument) { - response["success"] = false; - } - - return resolve(response); - - } catch (error) { - return reject(error); - } - }) - } - /** * List of all private programs created by user * @method @@ -110,262 +34,334 @@ module.exports = class UsersHelper { * @returns {Array} - List of all private programs created by user. */ - static privatePrograms(userId) { - return new Promise(async (resolve, reject) => { - try { - - let userPrivatePrograms = - await programsHelper.userPrivatePrograms( - userId - ); - - return resolve({ - message: constants.apiResponses.PRIVATE_PROGRAMS_LIST, - result: userPrivatePrograms - }) - - } catch (error) { - return reject(error); - } - }) - } - - /** - * Create user program and solution - * @method - * @name createProgramAndSolution - * @param {string} userId - logged in user Id. - * @param {object} programData - data needed for creation of program. - * @param {object} solutionData - data needed for creation of solution. - * @returns {Array} - Created user program and solution. - */ - - static createProgramAndSolution(userId, data, userToken) { - return new Promise(async (resolve, reject) => { - try { - - let userPrivateProgram = {}; - let dateFormat = gen.utils.epochTime(); - - const organisationAndRootOrganisations = - await this.getUserOrganisationsAndRootOrganisations(userId, userToken); - - if (data.programId && data.programId !== "") { - - userPrivateProgram = await programsHelper.programDocuments( - { - _id: data.programId, - createdBy: userId - } - ); - - if (!userPrivateProgram.length > 0) { - return resolve({ - status: httpStatusCode['bad_request'].status, - message: constants.apiResponses.PROGRAM_NOT_FOUND, - result: {} - }) - } - - userPrivateProgram = userPrivateProgram[0]; - - } else { - - let programData = { - name: data.programName, - isAPrivateProgram: true, - status: constants.common.ACTIVE_STATUS, - externalId: - data.programExternalId ? - data.programExternalId : - data.programName + "-" + dateFormat, - description: - data.programDescription ? - data.programDescription : - data.programName, - userId: userId - } - - programData.createdFor = organisationAndRootOrganisations.result.createdFor; - programData.rootOrganisations = organisationAndRootOrganisations.result.rootOrganisations; + static privatePrograms(userId) { + return new Promise(async (resolve, reject) => { + try { + + let userPrivatePrograms = + await programsHelper.userPrivatePrograms( + userId + ); + + return resolve({ + message: constants.apiResponses.PRIVATE_PROGRAMS_LIST, + result: userPrivatePrograms + }) + + } catch (error) { + return reject(error); + } + }) + } - userPrivateProgram = - await programsHelper.create( - programData - ); - } + /** + * Create user program and solution + * @method + * @name createProgramAndSolution + * @param {string} userId - logged in user Id. + * @param {object} programData - data needed for creation of program. + * @param {object} solutionData - data needed for creation of solution. + * @returns {Array} - Created user program and solution. + */ - let solutionDataToBeUpdated = { - programId: userPrivateProgram._id, - programExternalId: userPrivateProgram.externalId, - programName: userPrivateProgram.name, - programDescription: userPrivateProgram.description, - isAPrivateProgram: userPrivateProgram.isAPrivateProgram - }; + static createProgramAndSolution( + userId, + data, + userToken, + createADuplicateSolution = "" + ) { + return new Promise(async (resolve, reject) => { + try { + let userPrivateProgram = {}; + let dateFormat = gen.utils.epochTime(); + let parentSolutionInformation = {}; + + createADuplicateSolution = gen.utils.convertStringToBoolean( + createADuplicateSolution + ); + //program part + if ( data.programId && data.programId !== "" ) { + + let filterQuery = { + _id: data.programId + } - if ( Array.isArray(data.entities) && data.entities && data.entities.length > 0) { + if ( createADuplicateSolution === false ) { + filterQuery.createdBy = userId; + } - let entityData = await entitiesHelper.entityDocuments( - { - _id: { $in: data.entities } - }, ["entityType", "entityTypeId"] - ); + let checkforProgramExist = await programsHelper.programDocuments( + filterQuery, + "all", + ["__v"] + ); + + if ( !checkforProgramExist.length > 0 ) { + return resolve({ + status: httpStatusCode["bad_request"].status, + message: constants.apiResponses.PROGRAM_NOT_FOUND, + result: {} + }); + } - if (!entityData.length > 0) { - return resolve({ - status: httpStatusCode['bad_request'].status, - message: constants.apiResponses.ENTITY_NOT_FOUND, - result: {} - }) - } + if ( createADuplicateSolution === true ) { + + let duplicateProgram = checkforProgramExist[0]; + duplicateProgram = await _createProgramData( + duplicateProgram.name, + duplicateProgram.externalId ? duplicateProgram.externalId + "-" + dateFormat : duplicateProgram.name + "-" + dateFormat, + true, + constants.common.ACTIVE, + duplicateProgram.description, + userId, + duplicateProgram.startDate, + duplicateProgram.endDate, + userId + ); + + userPrivateProgram = await programsHelper.create( + _.omit(duplicateProgram, ["_id", "components", "scope"]) + ); + + } else { + userPrivateProgram = checkforProgramExist[0]; + } + } else { + /* If the programId is not passed from the front end, we will enter this else block. + In this block, we need to provide the necessary basic details to create a new program, Including startDate and endDate.*/ + // Current date + let startDate = new Date(); + // Add one year to the current date + let endDate = new Date(); + endDate.setFullYear(endDate.getFullYear() + 1); + let programData = await _createProgramData( + data.programName, + data.programExternalId + ? data.programExternalId + : data.programName + "-" + dateFormat, + true, + constants.common.ACTIVE, + data.programDescription + ? data.programDescription + : data.programName, + userId, + startDate, + endDate + ); + + userPrivateProgram = await programsHelper.create(programData); + } - if( data.type && data.type !== constants.common.IMPROVEMENT_PROJECT ) { - solutionDataToBeUpdated["entities"] = entityData.map(entity => entity._id); - } + let solutionDataToBeUpdated = { + programId: userPrivateProgram._id, + programExternalId: userPrivateProgram.externalId, + programName: userPrivateProgram.name, + programDescription: userPrivateProgram.description, + isAPrivateProgram: userPrivateProgram.isAPrivateProgram + }; + + //entities + if ( + Array.isArray(data.entities) && + data.entities && + data.entities.length > 0 + ) { + + let entitiesData = []; + let bodyData = {}; + + let locationData = gen.utils.filterLocationIdandCode(data.entities) + + if ( locationData.ids.length > 0 ) { - solutionDataToBeUpdated["entityType"] = entityData[0].entityType; - solutionDataToBeUpdated["entityTypeId"] = entityData[0].entityTypeId; - } + bodyData = { + "id" : locationData.ids + } + let entityData = await userService.locationSearch( bodyData ); + + if ( !entityData.success ) { + return resolve({ + status: httpStatusCode["bad_request"].status, + message: constants.apiResponses.ENTITY_NOT_FOUND, + result: {} + }); + } - let solution = "" + entityData.data.forEach( entity => { + entitiesData.push(entity.id) + }); - if (data.solutionId && data.solutionId !== "") { + solutionDataToBeUpdated["entityType"] = entityData.data[0].type; + + } - let solutionData = - await solutionsHelper.solutionDocuments({ - _id: data.solutionId, - isReusable: false - }, ["_id"]); + if ( locationData.codes.length > 0 ) { + let filterData = { + "code" : locationData.codes + } + let entityDetails = await userService.locationSearch( filterData ); + let entityDocuments = entityDetails.data; + if ( !entityDetails.success || !entityDocuments.length > 0 ) { + return resolve({ + status: httpStatusCode["bad_request"].status, + message: constants.apiResponses.ENTITY_NOT_FOUND, + result: {} + }); + } - if (!solutionData.length > 0) { + entityDocuments.forEach( entity => { + entitiesData.push(entity.id) + }); - return resolve({ - status: httpStatusCode['bad_request'].status, - message: constants.apiResponses.SOLUTION_NOT_FOUND, - result: {} - }) - } + solutionDataToBeUpdated["entityType"] = constants.common.SCHOOL; + } - solution = - await database.models.solutions.findOneAndUpdate({ - _id: solutionData[0]._id - }, { - $set: solutionDataToBeUpdated - }, { - new: true - }); + if ( data.type && data.type !== constants.common.IMPROVEMENT_PROJECT ) { + solutionDataToBeUpdated["entities"] = entitiesData; + } + } + //solution part + let solution = ""; + if ( data.solutionId && data.solutionId !== "" ) { + let solutionData = await solutionsHelper.solutionDocuments( + { + _id: data.solutionId, + }, + ["name", "link", "type", "subType", "externalId", "description", "certificateTemplateId"] + ); + + if ( !solutionData.length > 0 ) { + return resolve({ + status: httpStatusCode["bad_request"].status, + message: constants.apiResponses.SOLUTION_NOT_FOUND, + result: {} + }); + } - } else { + if ( createADuplicateSolution === true ) { - solutionDataToBeUpdated["type"] = - data.type ? data.type : constants.common.ASSESSMENT; - solutionDataToBeUpdated["subType"] = - data.subType ? data.subType : constants.common.INSTITUTIONAL; - - solutionDataToBeUpdated["isReusable"] = false; - - if (data.solutionName) { - solutionDataToBeUpdated["name"] = data.solutionName; - solutionDataToBeUpdated["externalId"] = - data.solutionExternalId ? - data.solutionExternalId : data.solutionName + "-" + dateFormat; - solutionDataToBeUpdated["description"] = - data.solutionDescription ? data.solutionDescription : data.solutionName; - } else { - solutionDataToBeUpdated["name"] = userPrivateProgram.programName, - solutionDataToBeUpdated["externalId"] = userId + "-" + dateFormat; - solutionDataToBeUpdated["description"] = userPrivateProgram.programDescription; - } + let duplicateSolution = solutionData[0]; + let solutionCreationData = await _createSolutionData( + duplicateSolution.name, + duplicateSolution.externalId ? duplicateSolution.externalId + "-" + dateFormat : duplicateSolution.name + "-" + dateFormat, + true, + constants.common.ACTIVE, + duplicateSolution.description, + userId, + false, + duplicateSolution._id + ); - solutionDataToBeUpdated.createdFor = organisationAndRootOrganisations.result.createdFor; - solutionDataToBeUpdated.rootOrganisations = organisationAndRootOrganisations.result.rootOrganisations; - solutionDataToBeUpdated.updatedBy = userId; + _.merge(duplicateSolution, solutionCreationData); + _.merge(duplicateSolution, solutionDataToBeUpdated); - solution = await solutionsHelper.create(solutionDataToBeUpdated); - } + solution = await solutionsHelper.create( + _.omit(duplicateSolution, ["_id", "link"]) + ); - if (solution._id) { + parentSolutionInformation.solutionId = duplicateSolution._id; + parentSolutionInformation.link = duplicateSolution.link; - await database.models.programs.findOneAndUpdate( - { - _id: userPrivateProgram._id - }, { - $addToSet: { components: ObjectId(solution._id) } - }); - } + } else { + if ( solutionData[0].isReusable === false ) { return resolve({ - message: constants.apiResponses.USER_PROGRAM_AND_SOLUTION_CREATED, - result: { - program: userPrivateProgram, - solution: solution - } + status: httpStatusCode["bad_request"].status, + message: constants.apiResponses.SOLUTION_NOT_FOUND, + result: {} }); - - } catch (error) { - console.log(error); - return reject(error); - } - }) - } - - /** - * Get user organisations and root organisations. - * @method - * @name getUserOrganisationsAndRootOrganisations - * @param {string} userId - logged in user Id. - * @param {object} userToken - Logged in user token. - * @returns {Array} - Get user organisations and root organisations. - */ - - static getUserOrganisationsAndRootOrganisations(userId, userToken) { - return new Promise(async (resolve, reject) => { - try { - - const userProfileData = - await userService.profile(userToken,userId); - - if (!userProfileData.status) { - return resolve({message: userProfileData.message}); + } + + solution = await database.models.solutions.findOneAndUpdate({ + _id: solutionData[0]._id + }, + { + $set: solutionDataToBeUpdated + }, + { + new: true } + ); + } + } else { + + let externalId, description; + if ( data.solutionName ) { + + externalId = data.solutionExternalId + ? data.solutionExternalId + : data.solutionName + "-" + dateFormat; + description = data.solutionDescription + ? data.solutionDescription + : data.solutionName; + + } else { + + externalId = userId + "-" + dateFormat; + description = userPrivateProgram.programDescription; + } - const createdFor = - userProfileData.organisations.map( - organisation => { - return organisation.organisationId - } - ); + let createSolutionData = await _createSolutionData( + data.solutionName + ? data.solutionName + : userPrivateProgram.programName, + externalId, + userPrivateProgram.isAPrivateProgram, + constants.common.ACTIVE, + description, + "", + false, + "", + data.type ? data.type : constants.common.ASSESSMENT, + data.subType ? data.subType : constants.common.INSTITUTIONAL, + userId + ); + + _.merge(solutionDataToBeUpdated, createSolutionData); + + solution = await solutionsHelper.create(solutionDataToBeUpdated); + } - const rootOrganisations = [userProfileData.rootOrgId]; + if ( solution && solution._id ) { + await database.models.programs.findOneAndUpdate( + { + _id: userPrivateProgram._id + }, + { + $addToSet: { components: ObjectId(solution._id) } + } + ); + } - return resolve({ - message: constants.apiResponses.USER_ORGANISATIONS_FETCHED, - result: { - createdFor: createdFor, - rootOrganisations: rootOrganisations - } - }); + return resolve({ + message: constants.apiResponses.USER_PROGRAM_AND_SOLUTION_CREATED, + result: { + program: userPrivateProgram, + solution: solution, + parentSolutionInformation: parentSolutionInformation + } + }); - } catch (error) { - return reject(error); - } - }) - } + } catch (error) { + return reject(error); + } + }); + } - /** + /** * Entities mapping form data. * @method * @name entitiesMappingForm - * @param {String} stateId - state id. + * @param {String} stateCode - state code. * @param {String} roleId - role id. * @returns {Object} returns a list of entitiesMappingForm. */ - static entitiesMappingForm(stateId, roleId) { + static entitiesMappingForm(stateCode, roleId, entityKey) { return new Promise(async (resolve, reject) => { try { - + const rolesData = await userRolesHelper.roleDocuments({ _id: roleId }, ["entityTypes.entityType"]); @@ -376,31 +372,32 @@ module.exports = class UsersHelper { result: [] }) } - - const entitiesData = await entitiesHelper.entityDocuments( - { - _id: stateId, - }, ["childHierarchyPath"] - ); - - if (!entitiesData.length > 0) { - return resolve({ - message: constants.apiResponses.ENTITY_NOT_FOUND, - result: [] - }) + + let subEntities = []; + let cacheData= await cache.getValue(entityKey); + + if( !cacheData ) { + subEntities = await formService.configForStateLocation( stateCode,entityKey ); + if( !subEntities.length > 0 ) { + return resolve({ + message : constants.apiResponses.ENTITY_NOT_FOUND, + result : [] + }) + } + } else { + subEntities = cacheData; } - let roleEntityType = ""; rolesData[0].entityTypes.forEach(roleData => { - if (entitiesData[0].childHierarchyPath.includes(roleData.entityType)) { + if (subEntities.includes(roleData.entityType)) { roleEntityType = roleData.entityType; } }) - + let entityTypeIndex = - entitiesData[0].childHierarchyPath.findIndex(path => path === roleEntityType); - + subEntities.findIndex(path => path === roleEntityType); + let form = { "field": "", "label": "", @@ -416,12 +413,12 @@ module.exports = class UsersHelper { let forms = []; for ( - let pointerToChildHierarchy = 0; + let pointerToChildHierarchy = 1; pointerToChildHierarchy < entityTypeIndex + 1; pointerToChildHierarchy++ ) { let cloneForm = JSON.parse(JSON.stringify(form)); - let entityType = entitiesData[0].childHierarchyPath[pointerToChildHierarchy]; + let entityType = subEntities[pointerToChildHierarchy]; cloneForm["field"] = entityType; cloneForm["label"] = `Select ${gen.utils.camelCaseToTitleCase(entityType)}`; @@ -436,254 +433,373 @@ module.exports = class UsersHelper { message: constants.apiResponses.ENTITIES_MAPPING_FORM_FETCHED, result: forms }); - } catch (error) { return reject(error); } }) } - /** - * User targeted solutions. - * @method - * @name solutions - * @param {String} programId - program id. - * @param {Object} requestedData requested data. - * @param {String} pageSize page size. - * @param {String} pageNo page no. - * @param {String} search search text. - * @returns {Object} targeted user solutions. - */ - - static solutions( programId,requestedData,pageSize,pageNo,search,token ) { - return new Promise(async (resolve, reject) => { - try { - - let programData = await programsHelper.programDocuments({ - _id : programId - },["name"]); + /** + * User targeted solutions. + * @method + * @name solutions + * @param {String} programId - program id. + * @param {Object} requestedData requested data. + * @param {String} pageSize page size. + * @param {String} pageNo page no. + * @param {String} search search text. + * @param {String} token user token. + * @param {String} userId user userId. + * @returns {Object} targeted user solutions. + */ - if( !programData.length > 0 ) { - return resolve({ - status : httpStatusCode["bad_request"].status, - message : constants.apiResponses.PROGRAM_NOT_FOUND - }) - } + static solutions(programId, requestedData, pageSize, pageNo, search, token, userId) { + return new Promise(async (resolve, reject) => { + + try { + let programData = await programsHelper.programDocuments( + { + _id: programId + }, + ["name","requestForPIIConsent","rootOrganisations","endDate"] + ); + + if (!programData.length > 0) { + return resolve({ + status: httpStatusCode["bad_request"].status, + message: constants.apiResponses.PROGRAM_NOT_FOUND + }); + } - let autoTargetedSolutions = - await solutionsHelper.forUserRoleAndLocation( - requestedData, - "", - "", - programId, - constants.common.DEFAULT_PAGE_SIZE, - constants.common.DEFAULT_PAGE_NO, - search - ); - - let totalCount = 0; - let mergedData = []; - - if( autoTargetedSolutions.data.data && autoTargetedSolutions.data.data.length > 0 ) { - - totalCount = autoTargetedSolutions.data.count; - - mergedData = autoTargetedSolutions.data.data; - - mergedData = - mergedData.map( targetedData => { - delete targetedData.programId; - delete targetedData.programName; - return targetedData; - }); - } - - let importedProjects = - await improvementProjectService.importedProjects( - token, - programId - ); - - if( importedProjects.success ) { - - if( importedProjects.data && importedProjects.data.length > 0 ) { - - totalCount += importedProjects.data.length; - - - importedProjects.data.forEach(importedProject => { - let data = importedProject.solutionInformation; - data["projectTemplateId"] = importedProject.projectTemplateId; - data["type"] = constants.common.IMPROVEMENT_PROJECT; - mergedData.push(data); - }); + let autoTargetedSolutions = + await solutionsHelper.forUserRoleAndLocation( + requestedData, + "", + "", + programId, + constants.common.DEFAULT_PAGE_SIZE, + constants.common.DEFAULT_PAGE_NO, + search + ); + + let totalCount = 0; + let mergedData = []; + let projectSolutionIdIndexMap = {} - } - } + if ( + autoTargetedSolutions.data.data && + autoTargetedSolutions.data.data.length > 0 + ) { - if( mergedData.length > 0 ) { - let startIndex = pageSize * (pageNo - 1); - let endIndex = startIndex + pageSize; - mergedData = mergedData.slice(startIndex,endIndex) - } + // Remove observation solutions which for project tasks. + + _.remove(autoTargetedSolutions.data.data, function(solution) { +   return solution.referenceFrom == constants.common.PROJECT && solution.type == constants.common.OBSERVATION; + }); - let result = { - programName : programData[0].name, - programId : programId, - description : constants.common.TARGETED_SOLUTION_TEXT, - data : mergedData, - count : totalCount - } + totalCount = autoTargetedSolutions.data.data.length; + mergedData = autoTargetedSolutions.data.data; - return resolve({ - message : constants.apiResponses.PROGRAM_SOLUTIONS_FETCHED, - success : true, - data : result - }) - } catch (error) { - return resolve({ - success : false, - data : { - description : constants.common.TARGETED_SOLUTION_TEXT, - data : [], - count : 0 - } - }) + mergedData = mergedData.map((targetedData, index) => { + if(targetedData.type == constants.common.IMPROVEMENT_PROJECT) { + projectSolutionIdIndexMap[targetedData._id.toString()] = index; } - }) - } + delete targetedData.programId; + delete targetedData.programName; + return targetedData; + }); - /** - * User targeted programs. - * @method - * @name programs - * @param {Object} bodyData - request body data. - * @param {String} pageNo - Page number. - * @param {String} pageSize - Page size. - * @param {String} searchText - Search text. - * @returns {Array} - Get user targeted programs. - */ - - static programs(bodyData, pageNo, pageSize,searchText) { - return new Promise(async (resolve, reject) => { - try { + } - let targetedProgrms = await programsHelper.forUserRoleAndLocation( - bodyData, - pageSize, - pageNo, - searchText - ); + // Get projects already started by a user in a given program + + let importedProjects = await improvementProjectService.importedProjects( + token, + programId + ); + + // Add projectId to the solution object if the user has already started a project for the improvement project solution. + + if (importedProjects.success) { + + if (importedProjects.data && importedProjects.data.length > 0) { + + importedProjects.data.forEach((importedProject) => { + + + if( projectSolutionIdIndexMap[importedProject.solutionInformation._id] !== undefined ) { + mergedData[projectSolutionIdIndexMap[importedProject.solutionInformation._id]].projectId = importedProject._id; + } else { + let data = importedProject.solutionInformation; + data['projectTemplateId'] = importedProject.projectTemplateId; + data['projectId'] = importedProject._id; + data["type"] = constants.common.IMPROVEMENT_PROJECT; + mergedData.push(data); + totalCount = totalCount + 1; + } + + }); + } + } - if (!targetedProgrms.success) { - throw { - message : constants.apiResponses.PROGRAM_NOT_FOUND - } + if (mergedData.length > 0) { + let startIndex = pageSize * (pageNo - 1); + let endIndex = startIndex + pageSize; + mergedData = mergedData.slice(startIndex, endIndex); + } + + // get all solutionIds of type survey + let surveySolutionIds = []; + mergedData.forEach( element => { + if( element.type === constants.common.SURVEY ) { + surveySolutionIds.push(element._id) + } + }); + + + if ( surveySolutionIds.length > 0 ) { + let userSurveySubmission = + await surveyService.assignedSurveys( + token, + "", + "", + false, + surveySolutionIds + ); + + if ( userSurveySubmission.success && + userSurveySubmission.data && + userSurveySubmission.data.data && + userSurveySubmission.data.data.length > 0 + ) { + for ( let surveySubmissionPointer = 0; surveySubmissionPointer < userSurveySubmission.data.data.length; surveySubmissionPointer++ ) { + for ( let mergedDataPointer = 0; mergedDataPointer < mergedData.length; mergedDataPointer++ ) { + if ( mergedData[mergedDataPointer].type == constants.common.SURVEY && userSurveySubmission.data.data[surveySubmissionPointer].solutionId == mergedData[mergedDataPointer]._id ) { + mergedData[mergedDataPointer].submissionId = userSurveySubmission.data.data[surveySubmissionPointer].submissionId; + break; + } + } - - targetedProgrms.data["description"] = constants.apiResponses.PROGRAM_DESCRIPTION; - - return resolve({ - success: true, - message: constants.apiResponses.USER_TARGETED_PROGRAMS_FETCHED, - data : targetedProgrms.data - }); - - } catch (error) { - return resolve({ - success: false, - message: error.message, - data : { - description : constants.common.TARGETED_SOLUTION_TEXT, - data : [], - count : 0 - } - }); + } } - }) - } - - /** - * List of entity types by location and role. - * @method - * @name entityTypesByLocationAndRole - * @param {String} stateLocationId - state location id. - * @param {String} role - role. - * @returns {Object} returns a list of entity type by location and role. - */ - - static entityTypesByLocationAndRole(stateLocationId, role) { - return new Promise(async (resolve, reject) => { - try { + } + + let result = { + programName: programData[0].name, + programId: programId, + programEndDate : programData[0].endDate, + description: constants.common.TARGETED_SOLUTION_TEXT, + rootOrganisations : ( programData[0].rootOrganisations && programData[0].rootOrganisations.length > 0 ) ? programData[0].rootOrganisations[0] : "", + data: mergedData, + count: totalCount, + programEndDate: programData[0].endDate + }; + if( programData[0].hasOwnProperty('requestForPIIConsent') ) { + result.requestForPIIConsent = programData[0].requestForPIIConsent; + } + //Check data present in programUsers collection. + //checkForUserJoinedProgramAndConsentShared will returns an object which contain joinProgram and consentShared status. + let programJoinStatus = await programUsersHelper.checkForUserJoinedProgramAndConsentShared(programId,userId); + result.programJoined = programJoinStatus.joinProgram; + result.consentShared = programJoinStatus.consentShared; - let filterQuery = { - "registryDetails.code" : stateLocationId - }; - - if( gen.utils.checkValidUUID( stateLocationId ) ) { - filterQuery = { - "registryDetails.locationId" : stateLocationId - }; - } + return resolve({ + message: constants.apiResponses.PROGRAM_SOLUTIONS_FETCHED, + success: true, + data: result + }); + } catch (error) { + return resolve({ + success: false, + data: { + description: constants.common.TARGETED_SOLUTION_TEXT, + data: [], + count: 0 + }, + }); + } + }); + } - const entitiesData = await entitiesHelper.entityDocuments(filterQuery, ["_id"]); + /** + * User targeted programs. + * @method + * @name programs + * @param {Object} bodyData - request body data. + * @param {String} pageNo - Page number. + * @param {String} pageSize - Page size. + * @param {String} searchText - Search text. + * @param {String} userId - User Id. + * @returns {Array} - Get user targeted programs. + */ - if (!entitiesData.length > 0) { - throw { - message: constants.apiResponses.ENTITIES_NOT_EXIST_IN_LOCATION - } - } + static programs(bodyData, pageNo, pageSize, searchText, userId) { + return new Promise(async (resolve, reject) => { + try { + + let programDetails = {}; + let targetedProgramIds = []; + let nonTargetedProgramIds = [] + let programCount= 0; + + // getting all program details matching the user profile. not passing pageSize and pageNo to get all data. + let targetedPrograms = await programsHelper.forUserRoleAndLocation( + bodyData, + "", // not passing page size + "", // not passing page number + searchText, + ["_id"] + ); + + // targetedPrograms.data contain all programIds targeted to current user profile. + if ( targetedPrograms.success && targetedPrograms.data && targetedPrograms.data.length > 0) { + targetedProgramIds = gen.utils.arrayOfObjectToArrayOfObjectId(targetedPrograms.data); + programCount = targetedPrograms.count; + } - const rolesDocument = await userRolesHelper.roleDocuments({ - code : role.toUpperCase() - },["_id","entityTypes.entityType"]); + // In case user changed profile after joined a program, we need to find the such program details. (programs not targeted to user profile anymore) + let nontargetedJoinedPrograms = await this.getUserJoinedProgramDetailsWithPreviousProfiles( + targetedProgramIds, + searchText, + userId + ); + + if ( nontargetedJoinedPrograms.success && nontargetedJoinedPrograms.data ) { + nonTargetedProgramIds = nontargetedJoinedPrograms.data; + programCount = programCount + nontargetedJoinedPrograms.count; // update program count + } - if (!rolesDocument.length > 0) { - throw { - message: constants.apiResponses.USER_ROLES_NOT_FOUND - } - } + //find total number of programs related to user + let userRelatedPrograms = targetedProgramIds.concat(nonTargetedProgramIds); - let entityTypes = []; - let stateEntityExists = false; + if (!userRelatedPrograms.length > 0) { + throw { + message: constants.apiResponses.PROGRAM_NOT_FOUND, + }; + } - rolesDocument[0].entityTypes.forEach( roleDocument => { - if( roleDocument.entityType === constants.common.STATE_ENTITY_TYPE ) { - stateEntityExists = true; - } - }); + let userRelatedProgramsData = await programsHelper.programDocuments( + {_id : {$in:userRelatedPrograms}}, + ["name", "externalId","metaInformation"], + "none", //not passing skip fields + pageNo, + pageSize + ); + + if (!userRelatedProgramsData.length > 0) { + throw { + message: constants.apiResponses.PROGRAM_NOT_FOUND, + }; + } + programDetails.data = userRelatedProgramsData; + programDetails.count = programCount; + programDetails.description = constants.apiResponses.PROGRAM_DESCRIPTION; + + return resolve({ + success: true, + message: constants.apiResponses.PROGRAMS_FETCHED, + data: programDetails + }); + } catch (error) { + console.log("Error :",error) + return resolve({ + success: false, + message: error.message, + data: { + description: constants.common.TARGETED_SOLUTION_TEXT, + data: [], + count: 0, + }, + }); + } + }); + } - if( stateEntityExists ) { - entityTypes = [constants.common.STATE_ENTITY_TYPE] - } else { - - let entitiesMappingForm = - await this.entitiesMappingForm( - entitiesData[0]._id, - rolesDocument[0]._id - ); - - entitiesMappingForm.result.forEach( entitiesMappingData => { - entityTypes.push(entitiesMappingData.field) - }); - } + /** + * List of entity types by location and role. + * @method + * @name entityTypesByLocationAndRole + * @param {String} stateLocationId - state location id. + * @param {String} role - role. + * @returns {Object} returns a list of entity type by location and role. + */ + static entityTypesByLocationAndRole(stateLocationId, role) { + return new Promise(async (resolve, reject) => { + try { + let entityKey = constants.common.SUBENTITY + stateLocationId; + const rolesDocument = await userRolesHelper.roleDocuments( + { + code: role.toUpperCase(), + }, + ["_id", "entityTypes.entityType"] + ); + + if (!rolesDocument.length > 0) { + throw { + message: constants.apiResponses.USER_ROLES_NOT_FOUND + }; + } + + let bodyData={}; + if (gen.utils.checkValidUUID(stateLocationId)) { + bodyData = { + "id" : stateLocationId + }; + } else { + bodyData = { + "code" : stateLocationId + }; + } + + let entityData = await userService.locationSearch( bodyData ); + + if ( !entityData.success ) { + throw { + message: constants.apiResponses.ENTITIES_NOT_EXIST_IN_LOCATION, + }; + } + + + let entityTypes = []; + let stateEntityExists = false; - return resolve({ - success : true, - message : constants.apiResponses.ENTITY_TYPES_FETCHED, - data : entityTypes - }); + rolesDocument[0].entityTypes.forEach((roleDocument) => { + + if (roleDocument.entityType === constants.common.STATE_ENTITY_TYPE) { + stateEntityExists = true; + } + }); + + if (stateEntityExists) { + entityTypes = [constants.common.STATE_ENTITY_TYPE]; + } else { + let entitiesMappingForm = await this.entitiesMappingForm( + entityData.data[0].code, + rolesDocument[0]._id, + entityKey + ); + + entitiesMappingForm.result.forEach((entitiesMappingData) => { + entityTypes.push(entitiesMappingData.field); + }); + } - } catch (error) { - return resolve({ - success : false, - message : error.message - }); - } - }) - } + return resolve({ + success: true, + message: constants.apiResponses.ENTITY_TYPES_FETCHED, + data: entityTypes + }); + } catch (error) { + return resolve({ + success: false, + message: error.message + }); + } + }); + } + - /** + /** * User Targeted entity. * @method * @name targetedEntity @@ -692,114 +808,312 @@ module.exports = class UsersHelper { * @returns {Object} - Details of the solution. */ - static targetedEntity( solutionId,requestedData ) { + static targetedEntity(solutionId, requestedData) { return new Promise(async (resolve, reject) => { try { - - let solutionData = - await solutionsHelper.solutionDocuments({ - _id : solutionId, - isDeleted : false - },["entityType","type"]); - - if( !solutionData.length > 0 ) { + let solutionData = await solutionsHelper.solutionDocuments( + { + _id: solutionId, + isDeleted: false + }, + ["entityType", "type"] + ); + + if (!solutionData.length > 0) { return resolve({ - status : httpStatusCode.bad_request.status, - message : constants.apiResponses.SOLUTION_NOT_FOUND + status: httpStatusCode.bad_request.status, + message: constants.apiResponses.SOLUTION_NOT_FOUND }); } - - let rolesDocument = await userRolesHelper.roleDocuments({ - code : requestedData.role - },["entityTypes.entityType"]); - - if( !rolesDocument.length > 0 ) { - throw { - status : httpStatusCode["bad_request"].status, - message: constants.apiResponses.USER_ROLES_NOT_FOUND - } + let rolesDocument = await userRolesHelper.roleDocuments( + { + code: requestedData.role + }, + ["entityTypes.entityType"] + ); + + if (!rolesDocument.length > 0) { + throw { + status: httpStatusCode["bad_request"].status, + message: constants.apiResponses.USER_ROLES_NOT_FOUND + }; } - - let requestedEntityTypes = Object.keys(_.omit(requestedData,["role"])); + + let requestedEntityTypes = Object.keys(_.omit(requestedData, ['role'])); let targetedEntityType = ""; - - rolesDocument[0].entityTypes.forEach(singleEntityType => { - if( requestedEntityTypes.includes(singleEntityType.entityType) ) { - targetedEntityType = singleEntityType.entityType; - } + + rolesDocument[0].entityTypes.forEach((singleEntityType) => { + if (requestedEntityTypes.includes(singleEntityType.entityType)) { + targetedEntityType = singleEntityType.entityType; + } }); - - if( !requestedData[targetedEntityType] ) { - throw { - status : httpStatusCode["bad_request"].status, - message: constants.apiResponses.ENTITIES_NOT_ALLOWED_IN_ROLE + + if (!requestedData[targetedEntityType]) { + throw { + status: httpStatusCode["bad_request"].status, + message: constants.apiResponses.ENTITIES_NOT_ALLOWED_IN_ROLE + }; + } + let filterData ={}; + if (solutionData[0].entityType === targetedEntityType) { + // if solution entity type and user tageted entity type are same + if (gen.utils.checkValidUUID(requestedData[targetedEntityType])) { + filterData = { + "parentId" : requestedData[targetedEntityType] } + let entitiesData = await userService.locationSearch( filterData ); + if( entitiesData.success ){ + targetedEntityType = constants.common.STATE_ENTITY_TYPE; + } + } else if ( targetedEntityType === constants.common.SCHOOL ) { + targetedEntityType = constants.common.STATE_ENTITY_TYPE; + } + } + + if (gen.utils.checkValidUUID(requestedData[targetedEntityType])) { + filterData = { + "id" : requestedData[targetedEntityType] + }; + } else { + filterData = { + "code" : requestedData[targetedEntityType] + }; + } + let entitiesDocument = await userService.locationSearch( filterData ); + if ( !entitiesDocument.success ) { + throw { + message: constants.apiResponses.ENTITY_NOT_FOUND + }; } + let entityData = entitiesDocument.data; + let entityDataFormated = { + "_id" : entityData[0].id, + "entityType" : entityData[0].type, + "entityName" : entityData[0].name + } + return resolve({ + message: constants.apiResponses.SOLUTION_TARGETED_ENTITY, + success: true, + data: entityDataFormated + }); + } catch (error) { + return resolve({ + success: false, + status: error.status + ? error.status + : httpStatusCode['internal_server_error'].status, + message: error.message + }); + } + }); + } - if( solutionData[0].entityType === targetedEntityType ) { + /** + * Highest Targeted entity. + * @method + * @name getHighestTargetedEntity + * @param {Object} requestedData - requested data + * @returns {Object} - Entity. + */ - let filterQuery = { - "registryDetails.code" : requestedData[targetedEntityType] - }; - - if( gen.utils.checkValidUUID( requestedData[targetedEntityType] ) ) { - filterQuery = { - "registryDetails.locationId" : requestedData[targetedEntityType] - }; - } - - let entities = await entitiesHelper.entityDocuments(filterQuery,["groups"]); + static getHighestTargetedEntity( roleWiseTargetedEntities, requestedData ) { + return new Promise(async (resolve, reject) => { + try { + let entityKey = constants.common.SUBENTITY + requestedData.state; + let subEntityTypes = []; + let cacheData = await cache.getValue(entityKey); + + if( !cacheData ) { + let filterData = { + "id" : requestedData.state + }; - if( !entities.length > 0 ) { - throw { - message : constants.apiResponses.ENTITY_NOT_FOUND - } - } + let entitiesData = await userService.locationSearch( filterData ); - if( entities[0] && entities[0].groups && Object.keys(entities[0].groups).length > 0 ) { - targetedEntityType = constants.common.STATE_ENTITY_TYPE; + if( !entitiesData.success ) { + return resolve({ + message : constants.apiResponses.ENTITY_NOT_FOUND, + result : [] + }) + } + let stateLocationCode = entitiesData.data[0].code; + subEntityTypes = await formService.configForStateLocation( stateLocationCode,entityKey ); + if( !subEntityTypes.length > 0 ) { + return resolve({ + message : constants.apiResponses.ENTITY_NOT_FOUND, + result : [] + }) + } + } else { + subEntityTypes = cacheData; + } + + let targetedIndex = subEntityTypes.length; + let roleWiseTarget; + for( let roleWiseEntityIndex = 0; roleWiseEntityIndex < roleWiseTargetedEntities.length; roleWiseEntityIndex++ ) { + for( let subEntitiesIndex = 0; subEntitiesIndex < subEntityTypes.length; subEntitiesIndex++ ) { + if( roleWiseTargetedEntities[roleWiseEntityIndex].entityType == subEntityTypes[subEntitiesIndex] ) { + if( subEntitiesIndex < targetedIndex) { + targetedIndex = subEntitiesIndex; + roleWiseTarget = roleWiseEntityIndex; + } } + } } + let targetedEntity = roleWiseTargetedEntities[roleWiseTarget]; + return resolve({ + message: constants.apiResponses.SOLUTION_TARGETED_ENTITY, + success: true, + data: targetedEntity + }); + } catch (error) { + return resolve({ + success: false, + status: error.status + ? error.status + : httpStatusCode['internal_server_error'].status, + message: error.message + }); + } + }); + } - let filterData = { - "registryDetails.code" : requestedData[targetedEntityType] - }; - - if( gen.utils.checkValidUUID( requestedData[targetedEntityType] ) ) { - filterData = { - "registryDetails.locationId" : requestedData[targetedEntityType] - }; - } - let entities = await entitiesHelper.entityDocuments(filterData,["metaInformation.name","entityType"]) + /** + * Find non-targeted joined program. + * @method + * @name getUserJoinedProgramDetailsWithPreviousProfiles + * @param {Array} targetedProgramIds - programIds + * @param {String} searchText - search text + * @param {String} userId - userId + * @returns {Object} - non-targeted joined program details. + */ + static getUserJoinedProgramDetailsWithPreviousProfiles( targetedProgramIds, searchText = "", userId ) { + return new Promise(async (resolve, reject) => { + try { + let programUsersIds = []; + let nonTargettedProgramDetails = []; + + // find all programs joined by the user + // programUsersData will contain list of programs joined by user from all the profiles. This can be considered as the super set of user programs + let programUsersData = await programUsersHelper.programUsersDocuments( + { + userId: userId + }, + ["programId"] + ); + + if ( programUsersData.length > 0 ) { + programUsersIds = programUsersData.map(function (obj) { + return obj.programId; + }); + } - if( !entities.length > 0 ) { - throw { - message : constants.apiResponses.ENTITY_NOT_FOUND + // if we find the difference between programUsersData and targettedProgramIds we will get program details joined by user other than the current profile + let previousProfilesJoinedProgramIds = _.differenceWith(programUsersIds, targetedProgramIds,_.isEqual); + + if ( previousProfilesJoinedProgramIds.length > 0 ) { + let findQuery = { + "_id": { "$in" : previousProfilesJoinedProgramIds }, + "startDate": {"$lte": new Date()}, + "endDate": {"$gte": new Date()} } - } - if( entities[0].metaInformation && entities[0].metaInformation.name ) { - entities[0]["entityName"] = entities[0].metaInformation.name; - delete entities[0].metaInformation; + //call program details to check if the program is active or not + let programDetails = await programsHelper.list( + "", // not passing page number + "", // not passing page size + searchText, + findQuery, + ["_id"] + ); + + // get _ids to array + if ( programDetails.success > 0 && programDetails.data && programDetails.data.data && programDetails.data.data.length > 0 ) { + nonTargettedProgramDetails = gen.utils.arrayOfObjectToArrayOfObjectId(programDetails.data.data); + } } - + return resolve({ - message : constants.apiResponses.SOLUTION_TARGETED_ENTITY, - success : true, - data : entities[0] + success: true, + data: nonTargettedProgramDetails, + count: nonTargettedProgramDetails.length }); - - } catch(error) { + } catch (error) { + console.log("error :",error) return resolve({ - success : false, - status : error.status ? - error.status : httpStatusCode['internal_server_error'].status, - message : error.message - }) + success: false, + status: error.status + ? error.status + : httpStatusCode['internal_server_error'].status, + message: error.message + }); } - }) - } + }); + } + + +}; + +/** + * Generate program creation data. + * @method + * @name _createProgramData + * @returns {Object} - program creation data + */ + +function _createProgramData(name, externalId, isAPrivateProgram, status, description, userId, startDate, endDate, createdBy = "") { + + let programData = {}; + programData.name = name; + programData.externalId = externalId; + programData.isAPrivateProgram = isAPrivateProgram; + programData.status = status; + programData.description = description; + programData.userId = userId; + programData.createdBy = createdBy; + programData.startDate = startDate; + programData.endDate = endDate; + return programData; + +} + +/** + * Generate solution creation data. + * @method + * @name _createSolutionData + * @returns {Object} - solution creation data + */ + +function _createSolutionData(name = "", externalId = "", isAPrivateProgram = "", status, description = "", userId, isReusable = "", parentSolutionId = "", type = "", subType = "", updatedBy="") { + + let solutionData = {}; + solutionData.name = name; + solutionData.externalId = externalId; + solutionData.isAPrivateProgram = isAPrivateProgram; + solutionData.status = status; + solutionData.description = description; + solutionData.userId = userId; + if( parentSolutionId ) { + solutionData.parentSolutionId = parentSolutionId; + } + if( type){ + solutionData.type = type; + } + if( subType){ + solutionData.subType = subType; + } + if( updatedBy){ + solutionData.updatedBy = updatedBy; + } + if( isReusable){ + solutionData.isReusable = isReusable; + } + + return solutionData; + +} + + -}; \ No newline at end of file diff --git a/module/users/validator/v1.js b/module/users/validator/v1.js index a0494d8d..ec218d91 100644 --- a/module/users/validator/v1.js +++ b/module/users/validator/v1.js @@ -1,21 +1,7 @@ module.exports = (req) => { let userValidator = { - create : function () { - req.checkBody('email').exists().withMessage("required email id"); - req.checkBody('userName').exists() - .withMessage("required name of the user"); - - req.checkBody('role').exists() - .withMessage("required role of the user"); - }, - isSystemAdmin: function () { - req.checkBody('email').exists().withMessage("required email id"); - }, - entitiesMappingForm : function () { - req.checkParams('_id').exists().withMessage("required Entity id"); - req.checkQuery('roleId').exists().withMessage("required role id"); - }, + solutions : function () { req.checkParams('_id').exists().withMessage("required Program id"); }, diff --git a/package.json b/package.json index 10904f94..32642b16 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "scripts": { "test": "mocha --timeout 10000", "start": "node app.js", - "dev": "nodemon app.js" + "dev": "nodemon app.js", + "coverage": " nyc --reporter=lcov mocha --timeout 10000 test --exit" }, "keywords": [ "shikshalokam", @@ -19,7 +20,6 @@ "license": "ISC", "dependencies": { "@azure/storage-blob": "^12.1.1", - "@elastic/elasticsearch": "^6.7.1", "@google-cloud/storage": "^4.3.1", "adm-zip": "^0.4.14", "aws-sdk": "^2.624.0", @@ -29,6 +29,7 @@ "cache-manager": "^2.9.0", "chai": "^4.2.0", "chai-http": "^4.3.0", + "cheerio": "^1.0.0-rc.12", "cli-table": "^0.3.1", "commander": "^2.20.0", "cors": "^2.8.4", @@ -52,23 +53,25 @@ "jwt-decode": "^2.2.0", "kafka-node": "^5.0.0", "keycloak-auth-utils": "^3.3.0", + "log": "^1.4.0", "mocha": "^6.2.2", "moment-timezone": "^0.5.23", "mongodb": "^3.2.3", "mongoose": "^5.0.17", "mongoose-autopopulate": "^0.6.1", - "mongoose-delete": "^0.4.0", + "mongoose-delete": "^0.5.3", "mongoose-timestamp": "^0.6.0", "mongoose-ttl": "0.0.3", + "node-cache": "^5.1.2", "node-schedule": "^1.3.2", "nodemailer": "^6.4.1", + "nyc": "^15.1.0", "p-each-series": "^2.1.0", "path": "^0.12.7", "request": "^2.88.0", "require-all": "^2.2.0", "uuid": "^8.3.2", - "xml-js": "^1.6.11", - "log": "^1.4.0" + "xml-js": "^1.6.11" }, "devDependencies": { "grunt-apidoc": "^0.11.0", diff --git a/public/pushNotifications/2019_12_13/translate-language-to-csv_2019_12_13_15_18.csv b/public/pushNotifications/2019_12_13/translate-language-to-csv_2019_12_13_15_18.csv deleted file mode 100644 index dc08d33a..00000000 --- a/public/pushNotifications/2019_12_13/translate-language-to-csv_2019_12_13_15_18.csv +++ /dev/null @@ -1,259 +0,0 @@ -"key","en" -"ionLabel_date","Date" -"ionLabel_generalQuestions","General Questions" -"ionLabel_selectObservationType","Select a observation Type" -"ionLabel_selectSolution","Select a Solution" -"ionLabel_selectSchool","Select Schools" -"actionSheet_confirmLeave","Are you sure to leave." -"actionSheet_saveCurrentDataConfirmation","Old details will be lost ? Are you sure to overwrite the details" -"actionSheet_edit","Edit" -"actionSheet_delete","Delete" -"actionSheet_confirm","Confirm" -"actionSheet_completeobservation","Are you sure to mark this observation as complete?" -"actionSheet_deleteEntity","Are you sure to remove the entity form observation?" -"actionSheet_deleteSubmission","Are you sure to remove the submission ? " -"actionSheet_surveyAction","Survey Actions" -"actionSheet_restrictAction","Further you won't be able to do any kind of action." -"actionSheet_view","View" -"actionSheet_pleaseEnterPasscode","Please enter the passcode." -"actionSheet_submit","Submit" -"actionSheet_passcode","Passcode" -"actionSheet_login","Login" -"actionSheet_ecmNotApplicableMessage","Do you want mark ECM as not applicable / not allowed?" -"actionSheet_dataLooseConfirm","All your datas will be lost. Do you want to continue?" -"actionSheet_previousUserName","Please enter previous username." -"actionSheet_sessionExpired","Session has expired. Please login again to continue" -"actionSheet_slowInternet","You are connected to a slower data network. Image upload may take longer time. Do you want to continue?" -"actionSheet_name","Name" -"actionSheet_feedback","Feedback" -"actionSheet_send","Send" -"actionSheet_start","Start" -"actionSheet_warning","WARNING!" -"actionSheet_schoolSurveyEarse","All schools survey data will be erased. This action is irreversable.Do you want to continue?" -"actionSheet_appTermination","App termination" -"actionSheet_appTerminationMessage","Do you want to close the app?" -"actionSheet_closeApp","Close App" -"actionSheet_ecmNotApplicable","ECM Not Applicable" -"actionSheet_ecmNotAllowed","ECM Not Allowed" -"actionSheet_confirmDelete","Confirm Delete" -"actionSheet_addImage","Add Images" -"actionSheet_camera","Camera" -"actionSheet_upload","Upload" -"actionSheet_cancel","Cancel" -"actionSheet_confirmDeleteImage","Do you want to delete this image?" -"actionSheet_confirmDeleteInstance","Do you want to delete this instance?" -"actionSheet_entityDelete","Are you sure you want to delete the entity?" -"actionSheet_yes","Yes" -"actionSheet_ok","Ok" -"actionSheet_no","No" -"actionSheet_uploadImage","Upload Image" -"actionSheet_uploadFile","Upload File" -"actionSheet_publish","Publish" -"actionSheet_chooseAction","Choose a action" -"actionSheet_deleteObservation","Are you sure you want to delete the observation?" -"actionSheet_networkSlowAlert","You are connected to a slower data network. Image upload may take longer time. Do you want to continue?" -"actionSheet_reportWithScore","Report with score" -"actionSheet_reportWithoutScore","Report without score" -"buttons_observeAgain","Observe Again" -"buttons_view","View" -"buttons_and","and" -"buttons_addNew","Add New" -"buttons_viewReport","View Report" -"buttons_draft","Draft" -"buttons_publish","Publish" -"buttons_goToEntities","Go TO Entitites" -"buttons_active","Active" -"buttons_completed","Completed" -"buttons_profile","Profile" -"buttons_ratings","Rate" -"buttons_start","Start" -"buttons_survey","survey" -"buttons_delete","Delete" -"buttons_done","Done" -"buttons_previous","Previous" -"buttons_today","Today" -"buttons_about","About" -"buttons_save","Save" -"buttons_addSchools","Add Schools" -"buttons_next","Next" -"buttons_back","Back" -"buttons_upload","Upload" -"buttons_skip","Skip" -"buttons_submit","Submit" -"buttons_cancel","Cancel" -"buttons_addEntity","Add Entity" -"buttons_send","Send" -"buttons_yes","Yes" -"buttons_no","No" -"buttons_login","Login" -"buttons_flag","Flag" -"buttons_parentInfo","Parent Info" -"buttons_add","Add" -"buttons_parent","Parent" -"buttons_registry","Registry" -"buttons_capture","Capture" -"buttons_refresh","Refresh" -"buttons_feedback","Feedback" -"buttons_update","Update" -"buttons_Leader","School Leader" -"buttons_Teacher","Teacher" -"buttons_saveDraft","Draft" -"buttons_markAsComplete","Mark as completed" -"buttons_preview","Preview" -"buttons_viewReports","View Reports" -"buttons_viewObservationReport","View Observation report" -"buttons_viewEntityReport","View Entity Report" -"buttons_edit","Edit" -"buttons_observation","Observation" -"buttons_loadMore","Load More" -"buttons_selectAll","Select All" -"buttons_undo","Undo" -"tabs_home","Home" -"tabs_view","All" -"tabs_mySchool","My Schools" -"tabs_faqs","FAQs" -"tabs_about","About" -"tabs_questions","Questions" -"tabs_rate","Rate" -"tabs_individual","Individual" -"tabs_institutional","Institutions" -"tabs_observations","Observations" -"tabs_myObservation","My Observation" -"tabs_draftObservation","Draft Observation" -"tabs_addObservation","Add Observation" -"tabs_userRolePage","Roles" -"tabs_dashboard","Dashboard" -"tabs_inProgress","In progress" -"tabs_completed","Completed" -"tabs_setting","Settings" -"headings_zone","Zones" -"headings_state","State" -"headings_lastModified","Last Modefied at" -"headings_district","District" -"headings_entities","Entities" -"headings_userRolePage","Roles" -"headings_dashboard","Dashboard" -"headings_cluster","Cluster" -"headings_myActiveAssessment","My active assessments" -"headings_submissionListPage","Submission List" -"headings_addObservation","Add Observation" -"headings_solutionDetails","Solution Details" -"headings_home","Home" -"headings_observations","Observations" -"headings_myInstitutions","My Institutions" -"headings_mySchool","My Schools" -"headings_faqs","FAQs" -"headings_programList","Program List" -"headings_about","About" -"headings_school","School" -"headings_profile","Profile" -"headings_survey","Survey" -"headings_addSchools","Add Schools" -"headings_surveySections","Survey - Sections" -"headings_evidenceMethods","Evidence Methods" -"headings_sections","Sections" -"headings_questionnaire","Questionnaire" -"headings_feedback","Feedback" -"headings_logout","Logout" -"headings_startDate","Start Date" -"headings_endDate","End Date" -"headings_description","Description" -"headings_criteria","Criteria" -"headings_rate","Rate" -"headings_uplaod","Upload" -"headings_images","Images" -"headings_ratedCriterias","Rated Criterias" -"headings_flagCriteria","Flag Criteria" -"headings_questionMap","Question Map" -"headings_remarks","Remarks" -"headings_editSchoolProfile","Edit School Profile" -"headings_schoolProfile","School Profile" -"headings_surveyEcm","Survey - ECM" -"headings_parentContact","Parent Contacts" -"headings_parentRegistry","Parent Registry" -"headings_schoolLeaderRegistry","School Leader Registry" -"headings_teacherRegistry","Teacher Registry" -"headings_generalQuestions","General Questions" -"headings_individualAssessments","Individual Assessments" -"headings_observationDetails","Observation Details" -"headings_dashboardAssessmentListing","Assessments" -"headings_selectAssessment","Select an Assessment type" -"headings_observationList","Observations List" -"headings_hint","Hint" -"headings_submissionPreview","Submission Preview" -"headings_observationReports","Report" -"headings_observationUpdate","Observation Update" -"headings_notifications","Notifications" -"headings_settings","Settings" -"labels_welcome","Welcome" -"labels_assessments","Assessments" -"labels_goTo","Goto" -"labels_name","Name" -"labels_feedback","Feedback" -"labels_choose","Choose" -"labels_language","Language" -"labels_remarks","Remarks" -"labels_image","Image" -"labels_completed","Completed" -"labels_inprogress","In Progress" -"labels_submitted","Submitted" -"labels_notApplicable","NA" -"labels_readytoPublish","Ready to Publish" -"labels_files","Files" -"labels_languages","Languages" -"labels_totalScore","Total score" -"labels_scoreAchieved","Score achieved" -"labels_totalObservations","Total Observations" -"languages_english","English" -"languages_hindi","हिंदी" -"languages_telugu","తెలుగు" -"languages_malayalam","മലയാളം" -"languages_tamil","தமிழ்" -"languages_kannada","ಕನ್ನಡ" -"languages_gujarati","ગુજરાતી" -"message_noReportsFound","No reports found" -"message_noSubmission","No Submissions" -"message_startSearchSchool","search school" -"message_noSchoolFound","No Schools Found" -"message_searchEntities","Search for entities" -"message_startSearchSolution","Search for solutions" -"message_dontCloseApp","Please don't close the app or go back." -"message_of","of" -"message_noProgram","No Program" -"message_program","Program" -"message_images","Images" -"message_startSearchEntity","Start typing entity name" -"message_uploading","Uploading" -"message_completeObservation","Are you sure you want to mark this observation as complete?" -"message_restrictAction","Further you won't be able to do any kind of action." -"message_noQuestionAvailable"," No questions available" -"message_noDraft","No Drafts" -"message_noEntity","No Entity" -"message_no","No" -"message_noSolutionFound","No Solutions" -"message_noProgramsFound","No Programs found." -"message_deleteObservation","Are you sure you want to delete the observation?" -"message_confirmDeleteObservation","You will not see this observation anymore. Do you wish to delete?" -"message_viewAll","View all" -"message_noUnreadNotifications","You have no new Notifications!" -"message_noObservationfound","No observation found" -"toastMessage_selectSolution","Select a Solution" -"toastMessage_selectObservationType","Select a observation Type" -"toastMessage_networkDisconnected","NetWork Disconnected" -"toastMessage_networkConnected","Network Connected" -"toastMessage_ok","OK" -"toastMessage_allValueAreMandatory","Fill Mandatory Field" -"toastMessage_enableInternet","Please enable your internet connection to continue." -"toastMessage_connectToInternet","Please connect to internet " -"toastMessage_networkConnectionForAction","You need network connection for this action." -"toastMessage_noPermissionToPage","You dont have permission to view this page." -"toastMessage_someThingWentWrong","Something went Wrong." -"toastMessage_someThingWentWrongTryLater","Something went Wrong , Please Try after sometime." -"toastMessage_enableToGetGoogleUrls","Unable to get google urls" -"toastMessage_submissionCompleted","Submission completed successfully" -"toastMessage_errorGettingLoaction","Error in getting location" -"toastMessage_fillAllFields","Please fill all the fields" -"toastMessage_loginAgain","Please login again." -"toastMessage_userNameMisMatch","Username didnot match. Please login again." -"toastMessage_loactionForAction","Location should be turned on for this action." -"toastMessage_questionResponseNotRequiredForCompleteAssessment","This questions response is not required to complete the assessment." \ No newline at end of file diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 00000000..2a62b0be --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,8 @@ +sonar.projectName=ml-core-service +sonar.language=js +sonar.projectKey=project-sunbird_ml-core-service +sonar.host.url=https://sonarcloud.io +sonar.exclusions=test/** +sonar.javascript.lcov.reportPaths=coverage/lcov.info +sonar.organization=project-sunbird +sonar.projectVersion=1.0 \ No newline at end of file