diff --git a/.gitignore b/.gitignore index ede208e0..90cf764a 100644 --- a/.gitignore +++ b/.gitignore @@ -69,3 +69,4 @@ config/credentials/* *.DS_Store package-lock.json +keycloak-public-keys/ \ No newline at end of file diff --git a/controllers/v1/programUsers.js b/controllers/v1/programUsers.js new file mode 100644 index 00000000..35d8ef63 --- /dev/null +++ b/controllers/v1/programUsers.js @@ -0,0 +1,22 @@ +/** + * name : programUsers.js + * author : Ankit Shahu + * created-date : 9-Jan-2023 + * Description : PII data related controller. +*/ + +/** + * programUsers + * @class + */ +module.exports = class ProgramUsers extends Abstract { + constructor() { + super("programUsers"); + } + + static get name() { + return "programUsers"; + } + +}; + diff --git a/databaseQueries/programUsers.js b/databaseQueries/programUsers.js new file mode 100644 index 00000000..479b8b8e --- /dev/null +++ b/databaseQueries/programUsers.js @@ -0,0 +1,52 @@ +/** + * name : programUsers.js + * author : Ankit Shahu + * created-date : 07-04-2023 + * Description : program users helper for DB interactions. + */ +module.exports = class programUsers { + + /** + * program users details. + * @method + * @name programUsersDocument + * @param {Array} [filterData = "all"] - program users filter query. + * @param {Array} [fieldsArray = "all"] - projected fields. + * @param {Array} [skipFields = "none"] - field not to include + * @returns {Array} program users details. + */ + + static programUsersDocument( + 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 programJoinedData = await database.models.programUsers + .find(queryObject, projection) + .lean(); + return resolve(programJoinedData); + } catch (error) { + return reject(error); + } + }); + } + +}; diff --git a/generics/constants/api-responses.js b/generics/constants/api-responses.js index 7404a87a..db81c809 100644 --- a/generics/constants/api-responses.js +++ b/generics/constants/api-responses.js @@ -134,5 +134,6 @@ module.exports = { "CERTIFICATE_GENERATION_FAILED" : "Certificate generation failed", "NOT_ELIGIBLE_FOR_CERTIFICATE" : "Project is not eligible for certificate", "ISSUER_KID_NOT_FOUND" : "Failed to fetch certificate issuer kid", - "PROJECT_SUBMITTED_FOR_REISSUE" : "Submitted for project certificate reIssue" + "PROJECT_SUBMITTED_FOR_REISSUE" : "Submitted for project certificate reIssue", + "PROGRAM_JOIN_FAILED" : "Failed to join program", }; diff --git a/generics/constants/endpoints.js b/generics/constants/endpoints.js index bec01de6..697d8521 100644 --- a/generics/constants/endpoints.js +++ b/generics/constants/endpoints.js @@ -52,5 +52,6 @@ module.exports = { PROJECT_CERTIFICATE_API_CALLBACK : "/v1/userProjects/certificateCallback", USER_READ_PRIVATE : "/private/user/v1/read", // !Caution: End point for reading user details without token. Do not use for public work flow GET_CERTIFICATE_KID : "/api/v1/PublicKey/search", + PROGRAM_JOIN: "/v1/programs/join", IS_TARGETED_BASED_ON_USER_PROFILE : "/v1/solutions/isTargetedBasedOnUserProfile", }; diff --git a/generics/services/core.js b/generics/services/core.js index 8a1ace5c..7090f3c4 100644 --- a/generics/services/core.js +++ b/generics/services/core.js @@ -725,6 +725,71 @@ const solutionDetailsBasedOnRoleAndLocation = function ( token,bodyData,solution }) } +/** + * program Join Api. + * @function + * @name joinProgram + * @param {String} userToken - User token. + * @param {Object} bodyData - Requested body data. + * @param {String} programId - program id. + * @returns {JSON} - Program Join Status. +*/ +const joinProgram = function (programId,bodyData,userToken) { + return new Promise(async (resolve, reject) => { + try { + + const url = + ML_CORE_URL + CONSTANTS.endpoints.PROGRAM_JOIN + "/" + programId; + const options = { + headers : { + "content-type": "application/json", + "internal-access-token": process.env.INTERNAL_ACCESS_TOKEN, + "x-authenticated-user-token" : userToken, + }, + + }; + + if ( bodyData.appVersion !== "" ) { + options.headers.appversion = bodyData.appVersion; + delete bodyData.appVersion + } + + if ( bodyData.appName !== "" ) { + options.headers.appname = bodyData.appName; + delete bodyData.appName + } + + + options.json = bodyData + request.post(url,options,kendraCallback); + + function kendraCallback(err, data) { + + let result = { + success : true + }; + + if (err) { + result.success = false; + } else { + + let response = data.body; + if( response.status === HTTP_STATUS_CODE['ok'].status ) { + result["data"] = response.result; + } else { + result.success = false; + } + } + + return resolve(result); + } + + } catch (error) { + return reject(error); + } + }) +} + const checkIfSolutionIsTargetedForUserProfile = function ( token,bodyData,solutionId ) { return new Promise(async (resolve, reject) => { @@ -770,12 +835,6 @@ const checkIfSolutionIsTargetedForUserProfile = function ( token,bodyData,soluti } }) } - - - - - - module.exports = { entityTypesDocuments : entityTypesDocuments, rolesDocuments : rolesDocuments, @@ -789,7 +848,8 @@ module.exports = { createSolution: createSolution, solutionBasedOnRoleAndLocation : solutionBasedOnRoleAndLocation, solutionDetailsBasedOnRoleAndLocation : solutionDetailsBasedOnRoleAndLocation, - getDownloadableUrl : getDownloadableUrl, + getDownloadableUrl : getDownloadableUrl, + joinProgram: joinProgram, checkIfSolutionIsTargetedForUserProfile:checkIfSolutionIsTargetedForUserProfile }; diff --git a/models/programUsers.js b/models/programUsers.js new file mode 100644 index 00000000..58b00a3e --- /dev/null +++ b/models/programUsers.js @@ -0,0 +1,34 @@ +module.exports = { + name: "programUsers", + schema: { + programId: { + type : "ObjectId", + required: true, + index: true + }, + userId: { + type: String, + index: true, + required: true, + }, + resourcesStarted: { + type: Boolean, + index: true, + default: false + }, + userProfile: { + type : Object, + required: true + }, + userRoleInformation: Object, + appInformation: Object + }, + compoundIndex: [ + { + "name" :{ userId: 1, programId: 1 }, + "indexType" : { unique: true } + } + ] +}; + + \ No newline at end of file diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index f35f82a3..71e341b2 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -21,12 +21,14 @@ const projectTemplateTaskQueries = require(DB_QUERY_BASE_PATH + "/projectTemplat const kafkaProducersHelper = require(GENERICS_FILES_PATH + "/kafka/producers"); const removeFieldsFromRequest = ["submissionDetails"]; const programsQueries = require(DB_QUERY_BASE_PATH + "/programs"); +const programUsers = require(DB_QUERY_BASE_PATH + "/programUsers"); const userProfileService = require(GENERICS_FILES_PATH + "/services/users"); const solutionsHelper = require(MODULES_BASE_PATH + "/solutions/helper"); const certificateTemplateQueries = require(DB_QUERY_BASE_PATH + "/certificateTemplates"); const certificateService = require(GENERICS_FILES_PATH + "/services/certificate"); const certificateValidationsHelper = require(MODULES_BASE_PATH + "/certificateValidations/helper"); const _ = require("lodash"); +const programUsersQueries = require(DB_QUERY_BASE_PATH + "/programUsers"); /** * UserProjectsHelper @@ -1115,7 +1117,36 @@ module.exports = class UserProjectsHelper { solutionDetails = solutionDetails.data[0]; } - + + // program join API call it will increment the noOfResourcesStarted counter and will make user join program + // before creating any project this api has to called + let programUsers = await programUsersQueries.programUsersDocuments( + { + userId : userId, + programId : solutionDetails.programId + }, + [ + "_id", + "resourcesStarted" + ] + ); + + if (!programUsers.length > 0 || ( programUsers.length > 0 && programUsers[0].resourcesStarted == false)) { + let programJoinBody = {}; + programJoinBody.userRoleInformation = bodyData; + programJoinBody.isResource = true; + let joinProgramData = await coreService.joinProgram ( + solutionDetails.programId, + programJoinBody, + userToken + ); + if ( !joinProgramData.success ) { + return resolve({ + status: HTTP_STATUS_CODE.bad_request.status, + message: CONSTANTS.apiResponses.PROGRAM_JOIN_FAILED + }); + } + } let projectCreation = await this.userAssignedProjectCreation( solutionDetails.projectTemplateId, @@ -1954,7 +1985,6 @@ module.exports = class UserProjectsHelper { let totalCount = 0; let data = []; - if( projects.success && projects.data && projects.data.data && Object.keys(projects.data.data).length > 0 ) { totalCount = projects.data.count;