From 0eef869a543f325038852f7cade57ffa290aee2e Mon Sep 17 00:00:00 2001 From: Michael Baghel Date: Tue, 22 Jun 2021 18:48:04 +0400 Subject: [PATCH 1/8] Make skill matching case-insensitive in getRoleBySkills --- src/services/TeamService.js | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/services/TeamService.js b/src/services/TeamService.js index bc236cf1..3ec4acdb 100644 --- a/src/services/TeamService.js +++ b/src/services/TeamService.js @@ -13,7 +13,7 @@ const errors = require('../common/errors') const JobService = require('./JobService') const ResourceBookingService = require('./ResourceBookingService') const HttpStatus = require('http-status-codes') -const { Op } = require('sequelize') +const { Op, where, fn, col } = require('sequelize') const models = require('../models') const stopWords = require('../../data/stopWords.json') const { getAuditM2Muser } = require('../common/helper') @@ -799,17 +799,30 @@ roleSearchRequest.schema = Joi.object() * @returns {Role} the best matching Role */ async function getRoleBySkills (skills) { + // Case-insensitive search for roles matching any of the given skills const lowerCaseSkills = skills.map(skill => skill.toLowerCase()) - // find all roles which includes any of the given skills const queryCriteria = { - where: { listOfSkills: { [Op.overlap]: lowerCaseSkills } }, + where: where( + fn( + 'string_to_array', + fn( + 'lower', + fn( + 'array_to_string', + col('list_of_skills'), + ',' + ) + ), + ',' + ), + {[Op.overlap]: lowerCaseSkills }), raw: true } const roles = await Role.findAll(queryCriteria) if (roles.length > 0) { let result = _.each(roles, role => { - // calculate each found roles matching rate - role.skillsMatch = _.intersection(role.listOfSkills, lowerCaseSkills).length / skills.length + // calculate each found roles matching rate (must again be made case-insensitive) + role.skillsMatch = _.intersection(role.listOfSkills.map(skill => skill.toLowerCase()), lowerCaseSkills).length / skills.length // each role can have multiple rates, get the maximum of global rates role.maxGlobal = _.maxBy(role.rates, 'global').global }) From 4463065a681da22770c51c57a024f9753a30beb4 Mon Sep 17 00:00:00 2001 From: eisbilir Date: Tue, 22 Jun 2021 22:07:05 +0300 Subject: [PATCH 2/8] fix: role search --- src/services/TeamService.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/services/TeamService.js b/src/services/TeamService.js index bc236cf1..99a23888 100644 --- a/src/services/TeamService.js +++ b/src/services/TeamService.js @@ -760,7 +760,7 @@ async function roleSearchRequest (currentUser, data) { if (!_.isUndefined(data.roleId)) { role = await Role.findById(data.roleId) role = role.toJSON() - role.skillsMatch = 1; + role.skillsMatch = 1 // if skills is provided then use skills to find role } else if (!_.isUndefined(data.skills)) { // validate given skillIds and convert them into skill names @@ -799,17 +799,16 @@ roleSearchRequest.schema = Joi.object() * @returns {Role} the best matching Role */ async function getRoleBySkills (skills) { - const lowerCaseSkills = skills.map(skill => skill.toLowerCase()) // find all roles which includes any of the given skills const queryCriteria = { - where: { listOfSkills: { [Op.overlap]: lowerCaseSkills } }, + where: { listOfSkills: { [Op.overlap]: skills } }, raw: true } const roles = await Role.findAll(queryCriteria) if (roles.length > 0) { let result = _.each(roles, role => { // calculate each found roles matching rate - role.skillsMatch = _.intersection(role.listOfSkills, lowerCaseSkills).length / skills.length + role.skillsMatch = _.intersection(role.listOfSkills, skills).length / skills.length // each role can have multiple rates, get the maximum of global rates role.maxGlobal = _.maxBy(role.rates, 'global').global }) @@ -821,7 +820,7 @@ async function getRoleBySkills (skills) { } } // if no matching role found then return Custom role or empty object - return await Role.findOne({ where: { name: { [Op.iLike]: 'Custom' } } }) || {} + return await Role.findOne({ where: { name: { [Op.iLike]: 'Custom' } }, raw: true }) || {} } getRoleBySkills.schema = Joi.object() From 8043bcd98565ff6f09ca25577e09085e85aa05cd Mon Sep 17 00:00:00 2001 From: Michael Baghel Date: Wed, 23 Jun 2021 01:40:47 +0400 Subject: [PATCH 3/8] Add job title to RoleSearchRequest service and model. Update swagger and add migration. Add previousRoleSearchId to RoleSearchRequest service. --- docs/swagger.yaml | 7 +++++++ ...-06-22-role-search-request-add-job-title.js | 18 ++++++++++++++++++ src/models/RoleSearchRequest.js | 5 +++++ src/services/TeamService.js | 11 +++++++---- 4 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 migrations/2021-06-22-role-search-request-add-job-title.js diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 6ffdf86d..26b389f4 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -5249,6 +5249,9 @@ components: jobDescription: type: string description: "The description of the job." + jobTitle: + type: string + description: "An optional job title." - type: object required: - skills @@ -5281,6 +5284,10 @@ components: format: float description: "Rate at which searched skills match the given role" example: 0.75 + jobTitle: + type: string + description: "Optional job title." + example: "Lead Application Developer" SubmitTeamRequestBody: properties: teamName: diff --git a/migrations/2021-06-22-role-search-request-add-job-title.js b/migrations/2021-06-22-role-search-request-add-job-title.js new file mode 100644 index 00000000..c1df8fba --- /dev/null +++ b/migrations/2021-06-22-role-search-request-add-job-title.js @@ -0,0 +1,18 @@ +const config = require('config') + +/** + * Add jobTitle field to the RoleSearchRequest model. + */ + +module.exports = { + up: async (queryInterface, Sequelize) => { + await queryInterface.addColumn({ tableName: 'role_search_requests', schema: config.DB_SCHEMA_NAME }, 'job_title', + { + type: Sequelize.STRING(100), + allowNull: true + }) + }, + down: async (queryInterface, Sequelize) => { + await queryInterface.removeColumn({ tableName: 'role_search_requests', schema: config.DB_SCHEMA_NAME}, 'job_title') + } +} \ No newline at end of file diff --git a/src/models/RoleSearchRequest.js b/src/models/RoleSearchRequest.js index 384b74d0..c79ae84f 100644 --- a/src/models/RoleSearchRequest.js +++ b/src/models/RoleSearchRequest.js @@ -62,6 +62,11 @@ module.exports = (sequelize) => { type: Sequelize.UUID }) }, + jobTitle: { + field: 'job_title', + type: Sequelize.STRING(100), + allowNull: true + }, createdBy: { field: 'created_by', type: Sequelize.UUID, diff --git a/src/services/TeamService.js b/src/services/TeamService.js index 3ec4acdb..c5276332 100644 --- a/src/services/TeamService.js +++ b/src/services/TeamService.js @@ -776,11 +776,12 @@ async function roleSearchRequest (currentUser, data) { } data.roleId = role.id // create roleSearchRequest entity with found roleId - const { id: roleSearchRequestId } = await createRoleSearchRequest(currentUser, data) + const { id: roleSearchRequestId, jobTitle } = await createRoleSearchRequest(currentUser, data) + const entity = jobTitle ? { jobTitle, roleSearchRequestId } : { roleSearchRequestId }; // clean Role role = await _cleanRoleDTO(currentUser, role) // return Role - return _.assign(role, { roleSearchRequestId }) + return _.assign(role, entity) } roleSearchRequest.schema = Joi.object() @@ -789,8 +790,10 @@ roleSearchRequest.schema = Joi.object() data: Joi.object().keys({ roleId: Joi.string().uuid(), jobDescription: Joi.string().max(255), - skills: Joi.array().items(Joi.string().uuid().required()) - }).required().min(1) + skills: Joi.array().items(Joi.string().uuid().required()), + jobTitle: Joi.string().max(100), + previousRoleSearchRequestId: Joi.string().uuid() + }).required().or('roleId', 'jobDescription', 'skills') }).required() /** From d9ae1d33afd3187a14d2fdf9c4c6a20f3e379c5f Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Wed, 23 Jun 2021 14:01:43 +0300 Subject: [PATCH 4/8] fix: payment scheduler update payments in ES ref issue #213 --- src/services/PaymentSchedulerService.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/PaymentSchedulerService.js b/src/services/PaymentSchedulerService.js index b07694e4..f38eab6f 100644 --- a/src/services/PaymentSchedulerService.js +++ b/src/services/PaymentSchedulerService.js @@ -90,7 +90,7 @@ async function processPayment (workPeriodPayment) { const oldValue = workPeriodPayment.toJSON() const updated = await workPeriodPayment.update({ status: 'in-progress' }) // Update the modified status to es - await postEvent(config.TAAS_WORK_PERIOD_PAYMENT_UPDATE_TOPIC, updated.toJSON(), { oldValue }) + await postEvent(config.TAAS_WORK_PERIOD_PAYMENT_UPDATE_TOPIC, updated.toJSON(), { oldValue, key: `workPeriodPayment.billingAccountId:${updated.billingAccountId}` }) } // Check whether the number of processed records per minute exceeds the specified number, if it exceeds, wait for the next minute before processing await checkWait(PaymentSchedulerStatus.START_PROCESS) @@ -115,7 +115,7 @@ async function processPayment (workPeriodPayment) { // 5. update wp and save it should only update already existent Work Period Payment record with created "challengeId" and "status=completed". const updated = await workPeriodPayment.update({ challengeId: paymentScheduler.challengeId, status: 'completed' }) // Update the modified status to es - await postEvent(config.TAAS_WORK_PERIOD_PAYMENT_UPDATE_TOPIC, updated.toJSON(), { oldValue }) + await postEvent(config.TAAS_WORK_PERIOD_PAYMENT_UPDATE_TOPIC, updated.toJSON(), { oldValue, key: `workPeriodPayment.billingAccountId:${updated.billingAccountId}` }) await paymentScheduler.update({ step: PaymentSchedulerStatus.CLOSE_CHALLENGE, userId: paymentScheduler.userId, status: 'completed' }) @@ -128,7 +128,7 @@ async function processPayment (workPeriodPayment) { // If payment processing failed Work Periods Payment "status" should be changed to "failed" and populate "statusDetails" field with error details in JSON format. const updated = await workPeriodPayment.update({ statusDetails, status: 'failed' }) // Update the modified status to es - await postEvent(config.TAAS_WORK_PERIOD_PAYMENT_UPDATE_TOPIC, updated.toJSON(), { oldValue }) + await postEvent(config.TAAS_WORK_PERIOD_PAYMENT_UPDATE_TOPIC, updated.toJSON(), { oldValue, key: `workPeriodPayment.billingAccountId:${updated.billingAccountId}` }) if (paymentScheduler) { await paymentScheduler.update({ step: _.get(err, 'step'), userId: paymentScheduler.userId, status: 'failed' }) From 40bff9d3da332eb30da7ea370b0b1e2aa3e8c9d2 Mon Sep 17 00:00:00 2001 From: Michael Baghel Date: Wed, 23 Jun 2021 20:15:09 +0400 Subject: [PATCH 5/8] Allow anonymous user to access /getSkillsByJobDescription endpoint. --- src/controllers/TeamController.js | 2 +- src/routes/TeamRoutes.js | 2 -- src/services/TeamService.js | 3 +-- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/controllers/TeamController.js b/src/controllers/TeamController.js index d7992e14..998f9a81 100644 --- a/src/controllers/TeamController.js +++ b/src/controllers/TeamController.js @@ -114,7 +114,7 @@ async function getMe (req, res) { * @param res the response */ async function getSkillsByJobDescription (req, res) { - res.send(await service.getSkillsByJobDescription(req.authUser, req.body)) + res.send(await service.getSkillsByJobDescription(req.body)) } /** diff --git a/src/routes/TeamRoutes.js b/src/routes/TeamRoutes.js index 941be406..a2183d36 100644 --- a/src/routes/TeamRoutes.js +++ b/src/routes/TeamRoutes.js @@ -38,8 +38,6 @@ module.exports = { post: { controller: 'TeamController', method: 'getSkillsByJobDescription', - auth: 'jwt', - scopes: [constants.Scopes.READ_TAAS_TEAM] } }, '/taas-teams/:id': { diff --git a/src/services/TeamService.js b/src/services/TeamService.js index 717bf396..14723279 100644 --- a/src/services/TeamService.js +++ b/src/services/TeamService.js @@ -838,7 +838,7 @@ getRoleBySkills.schema = Joi.object() * @param {Object} data the search criteria * @returns {Object} the result */ -async function getSkillsByJobDescription (currentUser, data) { +async function getSkillsByJobDescription (data) { // load topcoder skills if needed. Using cached skills helps to avoid // unnecessary api calls which is extremely time comsuming. await _reloadCachedTopcoderSkills() @@ -881,7 +881,6 @@ async function getSkillsByJobDescription (currentUser, data) { getSkillsByJobDescription.schema = Joi.object() .keys({ - currentUser: Joi.object().required(), data: Joi.object().keys({ description: Joi.string().required() }).required() From c5f100f7b5445b7f91e3eafa4e421dbe47f7c191 Mon Sep 17 00:00:00 2001 From: Michael Baghel Date: Wed, 23 Jun 2021 22:07:14 +0400 Subject: [PATCH 6/8] Fix for role search request using job description. --- src/services/TeamService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/TeamService.js b/src/services/TeamService.js index 14723279..99590bc9 100644 --- a/src/services/TeamService.js +++ b/src/services/TeamService.js @@ -769,7 +769,7 @@ async function roleSearchRequest (currentUser, data) { role = await getRoleBySkills(skills) } else { // if only job description is provided, collect skill names from description - const tags = await getSkillsByJobDescription(currentUser, { description: data.jobDescription }) + const tags = await getSkillsByJobDescription({ description: data.jobDescription }) const skills = _.map(tags, 'tag') // find the best matching role role = await getRoleBySkills(skills) From dcdc554151d868ecfaff181a80ecce15aa946b84 Mon Sep 17 00:00:00 2001 From: eisbilir Date: Wed, 23 Jun 2021 23:18:40 +0300 Subject: [PATCH 7/8] fix: isExternalMember --- app-routes.js | 30 +++++ ...coder-bookings-api.postman_collection.json | 114 ++---------------- src/routes/TeamRoutes.js | 6 +- src/services/TeamService.js | 8 +- 4 files changed, 47 insertions(+), 111 deletions(-) diff --git a/app-routes.js b/app-routes.js index d3987ba2..1c407aee 100644 --- a/app-routes.js +++ b/app-routes.js @@ -59,6 +59,36 @@ module.exports = (app) => { next() } }) + } else { + // public API, but still try to authenticate token if provided, but allow missing/invalid token + actions.push((req, res, next) => { + const interceptRes = {} + interceptRes.status = () => interceptRes + interceptRes.json = () => interceptRes + interceptRes.send = () => next() + authenticator(_.pick(config, ['AUTH_SECRET', 'VALID_ISSUERS']))(req, interceptRes, next) + }) + + actions.push((req, res, next) => { + if (!req.authUser) { + next() + } else if (req.authUser.isMachine) { + if (!def.scopes || !req.authUser.scopes || !helper.checkIfExists(def.scopes, req.authUser.scopes)) { + req.authUser = undefined + } + next() + } else { + req.authUser.jwtToken = req.headers.authorization + // check if user has full manage permission + if (_.intersection(req.authUser.roles, constants.FullManagePermissionRoles).length) { + req.authUser.hasManagePermission = true + } + if (_.includes(req.authUser.roles, constants.UserRoles.ConnectManager)) { + req.authUser.isConnectManager = true + } + next() + } + }) } actions.push(method) diff --git a/docs/Topcoder-bookings-api.postman_collection.json b/docs/Topcoder-bookings-api.postman_collection.json index 845523ab..a3e40abe 100644 --- a/docs/Topcoder-bookings-api.postman_collection.json +++ b/docs/Topcoder-bookings-api.postman_collection.json @@ -1,6 +1,6 @@ { "info": { - "_postman_id": "6f274c86-24a5-412e-95e6-fafa34e2a936", + "_postman_id": "15f10b58-dda5-4aaf-96e5-061a5c901717", "name": "Topcoder-bookings-api", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, @@ -18153,16 +18153,14 @@ "response": [] }, { - "name": "send request with invalid token", + "name": "send request with public", "event": [ { "listen": "test", "script": { "exec": [ - "pm.test('Status code is 401', function () {\r", - " pm.response.to.have.status(401);\r", - " const response = pm.response.json()\r", - " pm.expect(response.message).to.eq(\"Invalid Token.\")\r", + "pm.test('Status code is 200', function () {\r", + " pm.response.to.have.status(200);\r", "});" ], "type": "text/javascript" @@ -18171,16 +18169,10 @@ ], "request": { "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer invalid_token", - "type": "text" - } - ], + "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"jobDescription\": \"Should have these skills: Machine Learning, Dropwizard, NGINX, appcelerator\"\r\n}", + "raw": "{\r\n \"jobDescription\": \"Should have these skills: Machine Learning, Dropwizard, NGINX, appcelerator, C#\"\r\n}", "options": { "raw": { "language": "json" @@ -18210,7 +18202,7 @@ "pm.test('Status code is 400', function () {\r", " pm.response.to.have.status(400);\r", " const response = pm.response.json()\r", - " pm.expect(response.message).to.eq(\"\\\"data\\\" must have at least 1 key\")\r", + " pm.expect(response.message).to.eq(\"\\\"data\\\" must contain at least one of [roleId, jobDescription, skills]\")\r", "});" ], "type": "text/javascript" @@ -19211,71 +19203,7 @@ ], "request": { "method": "POST", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token_administrator}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{ \"description\": \"Description A global leading healthcare company is seeking a strong Databricks Engineer to join their development team as they build their new Databricks workspace. Development efforts will contribute to the migration of data from Hadoop to Databricks to prepare data for visualization. Candidate must be well-versed in Databricks components and best practices, be an excellent problem solver and be comfortable working in a fast-moving, rapidly changing, and dynamic environment via Agile, SCRUM, and DevOps. PREFERRED QUALIFICATIONS: 2+ years of Azure Data Stack experience: Azure Data Services using ADF, ADLS, Databricks with PySpark, Azure DevOps & Azure Key Vault. Strong knowledge of various data warehousing methodologies and data modeling concepts. Hands-on experience using Azure, Azure data lake, Azure functions & Databricks Minimum 2-3+ years of Python experience (PySpark) Design & Develop Azure native solutions for Data Platform Minimum 3+ years of experience using Big Data ecosystem (Cloudera/Hortonworks) using Oozie, Hive, Impala, and Spark Expert in SQL and performance tuning\" }", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{URL}}/taas-teams/getSkillsByJobDescription", - "host": [ - "{{URL}}" - ], - "path": [ - "taas-teams", - "getSkillsByJobDescription" - ] - } - }, - "response": [] - }, - { - "name": "get skills by invalid token", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test('Status code is 401', function () {\r", - " pm.response.to.have.status(401);\r", - " const response = pm.response.json()\r", - " pm.expect(response.message).to.eq(\"Invalid Token.\")\r", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer invalid_token" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], + "header": [], "body": { "mode": "raw", "raw": "{ \"description\": \"Description A global leading healthcare company is seeking a strong Databricks Engineer to join their development team as they build their new Databricks workspace. Development efforts will contribute to the migration of data from Hadoop to Databricks to prepare data for visualization. Candidate must be well-versed in Databricks components and best practices, be an excellent problem solver and be comfortable working in a fast-moving, rapidly changing, and dynamic environment via Agile, SCRUM, and DevOps. PREFERRED QUALIFICATIONS: 2+ years of Azure Data Stack experience: Azure Data Services using ADF, ADLS, Databricks with PySpark, Azure DevOps & Azure Key Vault. Strong knowledge of various data warehousing methodologies and data modeling concepts. Hands-on experience using Azure, Azure data lake, Azure functions & Databricks Minimum 2-3+ years of Python experience (PySpark) Design & Develop Azure native solutions for Data Platform Minimum 3+ years of experience using Big Data ecosystem (Cloudera/Hortonworks) using Oozie, Hive, Impala, and Spark Expert in SQL and performance tuning\" }", @@ -19317,18 +19245,7 @@ ], "request": { "method": "POST", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token_administrator}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], + "header": [], "body": { "mode": "raw", "raw": "{ \"description\": \"\" }", @@ -19370,18 +19287,7 @@ ], "request": { "method": "POST", - "header": [ - { - "key": "Authorization", - "type": "text", - "value": "Bearer {{token_administrator}}" - }, - { - "key": "Content-Type", - "type": "text", - "value": "application/json" - } - ], + "header": [], "body": { "mode": "raw", "raw": "{}", diff --git a/src/routes/TeamRoutes.js b/src/routes/TeamRoutes.js index a2183d36..b2415e17 100644 --- a/src/routes/TeamRoutes.js +++ b/src/routes/TeamRoutes.js @@ -23,7 +23,7 @@ module.exports = { '/taas-teams/skills': { get: { controller: 'TeamController', - method: 'searchSkills', + method: 'searchSkills' } }, '/taas-teams/me': { @@ -37,7 +37,7 @@ module.exports = { '/taas-teams/getSkillsByJobDescription': { post: { controller: 'TeamController', - method: 'getSkillsByJobDescription', + method: 'getSkillsByJobDescription' } }, '/taas-teams/:id': { @@ -89,7 +89,7 @@ module.exports = { '/taas-teams/sendRoleSearchRequest': { post: { controller: 'TeamController', - method: 'roleSearchRequest', + method: 'roleSearchRequest' } }, '/taas-teams/submitTeamRequest': { diff --git a/src/services/TeamService.js b/src/services/TeamService.js index 14723279..94865aae 100644 --- a/src/services/TeamService.js +++ b/src/services/TeamService.js @@ -13,7 +13,7 @@ const errors = require('../common/errors') const JobService = require('./JobService') const ResourceBookingService = require('./ResourceBookingService') const HttpStatus = require('http-status-codes') -const { Op, where, fn, col } = require('sequelize') +const { Op } = require('sequelize') const models = require('../models') const stopWords = require('../../data/stopWords.json') const { getAuditM2Muser } = require('../common/helper') @@ -769,7 +769,7 @@ async function roleSearchRequest (currentUser, data) { role = await getRoleBySkills(skills) } else { // if only job description is provided, collect skill names from description - const tags = await getSkillsByJobDescription(currentUser, { description: data.jobDescription }) + const tags = await getSkillsByJobDescription({ description: data.jobDescription }) const skills = _.map(tags, 'tag') // find the best matching role role = await getRoleBySkills(skills) @@ -777,7 +777,7 @@ async function roleSearchRequest (currentUser, data) { data.roleId = role.id // create roleSearchRequest entity with found roleId const { id: roleSearchRequestId, jobTitle } = await createRoleSearchRequest(currentUser, data) - const entity = jobTitle ? { jobTitle, roleSearchRequestId } : { roleSearchRequestId }; + const entity = jobTitle ? { jobTitle, roleSearchRequestId } : { roleSearchRequestId } // clean Role role = await _cleanRoleDTO(currentUser, role) // return Role @@ -985,7 +985,7 @@ createRoleSearchRequest.schema = Joi.object() */ async function _cleanRoleDTO (currentUser, role) { // if current user is machine, it means user is not logged in - if (currentUser.isMachine || await isExternalMember(currentUser.userId)) { + if (_.isNil(currentUser) || currentUser.isMachine || await isExternalMember(currentUser.userId)) { role.isExternalMember = true if (role.rates) { role.rates = _.map(role.rates, rate => From d4ffcab910bf0c5903c64639e444e831932af4d6 Mon Sep 17 00:00:00 2001 From: eisbilir Date: Thu, 24 Jun 2021 00:09:35 +0300 Subject: [PATCH 8/8] fix: JD parser image links --- src/common/helper.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/helper.js b/src/common/helper.js index 773dde0f..5f9f13db 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -1960,8 +1960,8 @@ function removeTextFormatting (text) { // Remove footnotes text = _.replace(text, /\[\^.+?\](: .*?$)?/g, ' ') text = _.replace(text, /\s{0,2}\[.*?\]: .*?$/g, ' ') - // Remove images - text = _.replace(text, /!\[(.*?)\][[(].*?[\])]/g, ' $1 ') + // Remove images and keep description unless it is default description "image" + text = _.replace(text, /!(\[((?!image).*?)\]|\[.*?\])[[(].*?[\])]/g, ' $2 ') // Remove inline links text = _.replace(text, /\[(.*?)\][[(].*?[\])]/g, ' $1 ') // Remove blockquotes