diff --git a/data/demo-data.json b/data/demo-data.json index 19f5f8ef..fe12aac9 100644 --- a/data/demo-data.json +++ b/data/demo-data.json @@ -7258,10 +7258,30 @@ } ], "Role": [ + { + "id": "f5e01b7c-466f-45c8-989c-16ff831d7e59", + "name": "Custom", + "description": null, + "listOfSkills": null, + "rates": [{ + "global": 1200, + "offShore": 1200, + "inCountry": 1200 + }], + "numberOfMembers": null, + "numberOfMembersAvailable": null, + "imageUrl": null, + "timeToCandidate": null, + "timeToInterview": null, + "createdBy": "57646ff9-1cd3-4d3c-88ba-eb09a395366c", + "updatedBy": null, + "createdAt": "2021-05-27T21:43:08.201Z", + "updatedAt": "2021-05-27T21:43:08.201Z" + }, { "id": "c145247d-5757-463d-9317-ff9e7026d403", "name": "Angular Developer", - "description": "Angular is an open-source, client-side framework based on TypeScript and designed for building web applications.", + "description": "* Writes tested and documented JavaScript, HTML and CSS\n* Makes design and technical decisions for AngularJS projects\n* Develops application code and unit test in the AngularJS, Rest Web Services and Java technologies", "listOfSkills": [ "database", "winforms", @@ -7293,7 +7313,7 @@ { "id": "d7ff0289-d3ea-44d8-b39a-53bba5b5b309", "name": "Dev Ops Engineer", - "description": "A DevOps engineer introduces processes, tools, and methodologies to balance needs throughout the software development life cycle, from coding and deployment, to maintenance and updates.", + "description": "* Introduces processes, tools, and methodologies\n* Balances needs throughout the software development life cycle\n* Configures server images, optimizes task performance in correspondence with engineers", "listOfSkills": [ "dropwizard", "nginx", @@ -7337,7 +7357,7 @@ { "id": "e7b7e818-40d4-4102-b486-09bdd21400b8", "name": "Salesforce Developer", - "description": "A Salesforce developer is a programmer who builds Salesforce applications across various PaaS (Platform as a Service) platforms.", + "description": "* Meets with project managers to determine CRM needs\n* Develops customized solutions within the Salesforce platform\n* Designs, codes, and implements Salesforce applications\n* Creates timelines and development goals\n* Tests the stability and functionality of the application\n* Troubleshoots and fixes bugs\n* Writes documents and provides technical training for Salesforce Staff\n* Maintains the security and integrity of the application software", "listOfSkills": [ "docker", ".net", diff --git a/docs/Topcoder-bookings-api.postman_collection.json b/docs/Topcoder-bookings-api.postman_collection.json index b37011a5..676507e0 100644 --- a/docs/Topcoder-bookings-api.postman_collection.json +++ b/docs/Topcoder-bookings-api.postman_collection.json @@ -19627,7 +19627,7 @@ "response": [] }, { - "name": "create role Niche", + "name": "create role Custom", "event": [ { "listen": "test", @@ -19652,7 +19652,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"name\": \"Niche\",\n \"rates\": [\n {\n \"global\": 10,\n \"inCountry\": 10,\n \"offShore\": 10\n }\n ]\n}", + "raw": "{\n \"name\": \"Custom\",\n \"rates\": [\n {\n \"global\": 1200,\n \"inCountry\": 1200,\n \"offShore\": 1200\n }\n ]\n}", "options": { "raw": { "language": "json" @@ -20424,7 +20424,7 @@ "pm.test('Status code is 200', function () {\r", " pm.response.to.have.status(200);\r", " const response = pm.response.json()\r", - " pm.expect(response.name).to.eq(\"Niche\")\r", + " pm.expect(response.name).to.eq(\"Custom\")\r", "});" ], "type": "text/javascript" diff --git a/docs/swagger.yaml b/docs/swagger.yaml index e6cb5988..99a23268 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -5505,6 +5505,11 @@ components: isExternalMember: type: boolean description: "Is the user external member" + skillsMatch: + type: number + format: float + description: "Rate at which searched skills match the given role" + example: 0.75 SubmitTeamRequestBody: properties: teamName: diff --git a/src/controllers/RoleController.js b/src/controllers/RoleController.js index 747cbe4d..084a095a 100644 --- a/src/controllers/RoleController.js +++ b/src/controllers/RoleController.js @@ -10,7 +10,7 @@ const service = require('../services/RoleService') * @param res the response */ async function getRole (req, res) { - res.send(await service.getRole(req.authUser, req.params.id, req.query.fromDb)) + res.send(await service.getRole(req.params.id, req.query.fromDb)) } /** @@ -47,7 +47,7 @@ async function deleteRole (req, res) { * @param res the response */ async function searchRoles (req, res) { - res.send(await service.searchRoles(req.authUser, req.query)) + res.send(await service.searchRoles(req.query)) } module.exports = { diff --git a/src/routes/RoleRoutes.js b/src/routes/RoleRoutes.js index 7230b593..ce8441a9 100644 --- a/src/routes/RoleRoutes.js +++ b/src/routes/RoleRoutes.js @@ -14,16 +14,12 @@ module.exports = { get: { controller: 'RoleController', method: 'searchRoles', - auth: 'jwt', - scopes: [constants.Scopes.READ_ROLE, constants.Scopes.ALL_ROLE] } }, '/taas-roles/:id': { get: { controller: 'RoleController', method: 'getRole', - auth: 'jwt', - scopes: [constants.Scopes.READ_ROLE, constants.Scopes.ALL_ROLE] }, patch: { controller: 'RoleController', diff --git a/src/routes/TeamRoutes.js b/src/routes/TeamRoutes.js index b82f4f02..941be406 100644 --- a/src/routes/TeamRoutes.js +++ b/src/routes/TeamRoutes.js @@ -24,8 +24,6 @@ module.exports = { get: { controller: 'TeamController', method: 'searchSkills', - auth: 'jwt', - scopes: [constants.Scopes.READ_TAAS_TEAM] } }, '/taas-teams/me': { @@ -94,8 +92,6 @@ module.exports = { post: { controller: 'TeamController', method: 'roleSearchRequest', - auth: 'jwt', - scopes: [constants.Scopes.CREATE_ROLE_SEARCH_REQUEST] } }, '/taas-teams/submitTeamRequest': { diff --git a/src/services/RoleService.js b/src/services/RoleService.js index bbd2c141..765d0f5c 100644 --- a/src/services/RoleService.js +++ b/src/services/RoleService.js @@ -74,7 +74,7 @@ async function _checkIfSameNamedRoleExists (roleName) { * @param {Boolean} fromDb flag if query db for data or not * @returns {Object} the role */ -async function getRole (currentUser, id, fromDb = false) { +async function getRole (id, fromDb = false) { if (!fromDb) { try { const role = await esClient.get({ @@ -95,7 +95,6 @@ async function getRole (currentUser, id, fromDb = false) { } getRole.schema = Joi.object().keys({ - currentUser: Joi.object().required(), id: Joi.string().uuid().required(), fromDb: Joi.boolean() }).required() @@ -230,7 +229,7 @@ deleteRole.schema = Joi.object().keys({ * @param {Object} criteria the search criteria * @returns {Object} the search result */ -async function searchRoles (currentUser, criteria) { +async function searchRoles (criteria) { // clean skill names and convert into an array criteria.skillsList = _.filter(_.map(_.split(criteria.skillsList, ','), skill => _.trim(skill)), skill => !_.isEmpty(skill)) try { @@ -291,7 +290,6 @@ async function searchRoles (currentUser, criteria) { } searchRoles.schema = Joi.object().keys({ - currentUser: Joi.object().required(), criteria: Joi.object().keys({ skillsList: Joi.string(), keyword: Joi.string() diff --git a/src/services/TeamService.js b/src/services/TeamService.js index 566262a3..9e32ebde 100644 --- a/src/services/TeamService.js +++ b/src/services/TeamService.js @@ -16,6 +16,7 @@ const HttpStatus = require('http-status-codes') const { Op } = require('sequelize') const models = require('../models') const stopWords = require('../../data/stopWords.json') +const { getAuditM2Muser } = require('../common/helper') const Role = models.Role const RoleSearchRequest = models.RoleSearchRequest const topcoderSkills = {} @@ -750,11 +751,16 @@ getMe.schema = Joi.object() * @returns {Object} the created project */ async function roleSearchRequest (currentUser, data) { + // if currentUser is undefined then set to machine + if (_.isUndefined(currentUser)) { + currentUser = getAuditM2Muser() + } let role // if roleId is provided then find role with given id. if (!_.isUndefined(data.roleId)) { role = await Role.findById(data.roleId) role = role.toJSON() + 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 @@ -779,7 +785,7 @@ async function roleSearchRequest (currentUser, data) { roleSearchRequest.schema = Joi.object() .keys({ - currentUser: Joi.object().required(), + currentUser: Joi.object(), data: Joi.object().keys({ roleId: Joi.string().uuid(), jobDescription: Joi.string().max(255), @@ -793,28 +799,29 @@ 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]: skills } }, + where: { listOfSkills: { [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.matchingRate = _.intersection(role.listOfSkills, skills).length / skills.length + role.skillsMatch = _.intersection(role.listOfSkills, lowerCaseSkills).length / skills.length // each role can have multiple rates, get the maximum of global rates role.maxGlobal = _.maxBy(role.rates, 'global').global }) - // sort roles by matchingRate, global rate and name - result = _.orderBy(result, ['matchingRate', 'maxGlobal', 'name'], ['desc', 'desc', 'asc']) - if (result[0].matchingRate >= config.ROLE_MATCHING_RATE) { + // sort roles by skillsMatch, global rate and name + result = _.orderBy(result, ['skillsMatch', 'maxGlobal', 'name'], ['desc', 'desc', 'asc']) + if (result[0].skillsMatch >= config.ROLE_MATCHING_RATE) { // return the 1st role - return _.omit(result[0], ['matchingRate', 'maxGlobal']) + return _.omit(result[0], ['maxGlobal']) } } - // if no matching role found then return Niche role or empty object - return await Role.findOne({ where: { name: { [Op.iLike]: 'Niche' } } }) || {} + // if no matching role found then return Custom role or empty object + return await Role.findOne({ where: { name: { [Op.iLike]: 'Custom' } } }) || {} } getRoleBySkills.schema = Joi.object()