Skip to content

Commit 061e893

Browse files
committed
Merge branch 'feature/integration-test-fix' into feature/m2m-support
# Conflicts: # docs/Topcoder-bookings-api.postman_collection.json # src/services/TeamService.js
2 parents 857c1b7 + 44a26d2 commit 061e893

13 files changed

+169
-47
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,7 @@ dist
116116
.pnp.*
117117

118118
# api.env
119-
api.env
119+
api.env
120+
121+
# macOS files
122+
.DS_Store

app.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ const logger = require('./src/common/logger')
1414
// setup express app
1515
const app = express()
1616

17-
app.use(cors())
17+
app.use(cors({
18+
// Allow browsers access pagination data in headers
19+
exposedHeaders: ['X-Page', 'X-Per-Page', 'X-Total', 'X-Total-Pages', 'X-Prev-Page', 'X-Next-Page']
20+
}))
1821
app.use(express.json())
1922
app.use(express.urlencoded({ extended: true }))
2023
app.set('port', config.PORT)

docs/Topcoder-bookings-api.postman_collection.json

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4089,12 +4089,37 @@
40894089
}
40904090
],
40914091
"url": {
4092-
"raw": "{{URL}}/taas-teams",
4092+
"raw": "{{URL}}/taas-teams?perPage=10&page=1&name=*taas*&sortBy=lastActivityAt&sortOrder=desc",
40934093
"host": [
40944094
"{{URL}}"
40954095
],
40964096
"path": [
40974097
"taas-teams"
4098+
],
4099+
"query": [
4100+
{
4101+
"key": "perPage",
4102+
"value": "10"
4103+
},
4104+
{
4105+
"key": "page",
4106+
"value": "1"
4107+
},
4108+
{
4109+
"key": "name",
4110+
"value": "*taas*",
4111+
"description": "case-insensitive; support wildcard match"
4112+
},
4113+
{
4114+
"key": "sortBy",
4115+
"value": "lastActivityAt",
4116+
"description": "allows: createdAt, updatedAt, lastActivityAt, id, status, name, type, best match"
4117+
},
4118+
{
4119+
"key": "sortOrder",
4120+
"value": "desc",
4121+
"description": "allows: asc, desc"
4122+
}
40984123
]
40994124
}
41004125
},
@@ -4283,4 +4308,4 @@
42834308
}
42844309
],
42854310
"protocolProfileBehavior": {}
4286-
}
4311+
}

docs/swagger.yaml

Lines changed: 79 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -172,12 +172,10 @@ paths:
172172
content:
173173
application/json:
174174
schema:
175-
type: object
176-
properties:
177-
result:
178-
type: array
179-
items:
180-
$ref: '#/components/schemas/Job'
175+
type: array
176+
items:
177+
$ref: '#/components/schemas/Job'
178+
181179
headers:
182180
X-Next-Page:
183181
schema:
@@ -573,12 +571,10 @@ paths:
573571
content:
574572
application/json:
575573
schema:
576-
type: object
577-
properties:
578-
result:
579-
type: array
580-
items:
581-
$ref: '#/components/schemas/JobCandidate'
574+
type: array
575+
items:
576+
$ref: '#/components/schemas/JobCandidate'
577+
582578
headers:
583579
X-Next-Page:
584580
schema:
@@ -982,12 +978,10 @@ paths:
982978
content:
983979
application/json:
984980
schema:
985-
type: object
986-
properties:
987-
result:
988-
type: array
989-
items:
990-
$ref: '#/components/schemas/ResourceBooking'
981+
type: array
982+
items:
983+
$ref: '#/components/schemas/ResourceBooking'
984+
991985
headers:
992986
X-Next-Page:
993987
schema:
@@ -1278,6 +1272,44 @@ paths:
12781272
- Teams
12791273
description: |
12801274
Search my teams. Teams is project in topcoder with type=='talent-as-a-service'
1275+
parameters:
1276+
- in: query
1277+
name: page
1278+
required: false
1279+
schema:
1280+
type: integer
1281+
default: 1
1282+
description: The page number.
1283+
- in: query
1284+
name: perPage
1285+
required: false
1286+
schema:
1287+
type: integer
1288+
default: 20
1289+
description: The number of items to list per page.
1290+
- in: query
1291+
name: sortBy
1292+
required: false
1293+
schema:
1294+
type: string
1295+
default: createdAt
1296+
enum: ['createdAt', 'updatedAt', 'lastActivityAt', 'id', 'status', 'name', 'type', 'best match']
1297+
description: The sort by column.
1298+
- in: query
1299+
name: sortOrder
1300+
required: false
1301+
schema:
1302+
type: string
1303+
default: desc
1304+
enum: ['desc','asc']
1305+
description: The sort order. Not allowed when sortBy is `best match`.
1306+
- in: query
1307+
name: name
1308+
required: false
1309+
schema:
1310+
type: string
1311+
description: filter by name, case-insensitive; support wildcard match.
1312+
example: '*taas*'
12811313
security:
12821314
- bearerAuth: []
12831315
responses:
@@ -1289,6 +1321,35 @@ paths:
12891321
type: array
12901322
items:
12911323
$ref: '#/components/schemas/Team'
1324+
headers:
1325+
X-Next-Page:
1326+
schema:
1327+
type: integer
1328+
description: The index of the next page
1329+
X-Page:
1330+
schema:
1331+
type: integer
1332+
description: The index of the current page (starting at 1)
1333+
X-Per-Page:
1334+
schema:
1335+
type: integer
1336+
description: The number of items to list per page
1337+
X-Prev-Page:
1338+
schema:
1339+
type: integer
1340+
description: The index of the previous page
1341+
X-Total:
1342+
schema:
1343+
type: integer
1344+
description: The total number of items
1345+
X-Total-Pages:
1346+
schema:
1347+
type: integer
1348+
description: The total number of pages
1349+
Link:
1350+
schema:
1351+
type: string
1352+
description: Pagination link header.
12921353
'400':
12931354
description: Bad request
12941355
content:

src/bootstrap.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ const Joi = require('joi')
33
const path = require('path')
44
const logger = require('./common/logger')
55

6+
Joi.page = () => Joi.number().integer().min(1).default(1)
7+
Joi.perPage = () => Joi.number().integer().min(1).default(20)
68
Joi.rateType = () => Joi.string().valid('hourly', 'daily', 'weekly', 'monthly')
79
Joi.jobStatus = () => Joi.string().valid('sourcing', 'in-review', 'assigned', 'closed', 'cancelled')
810
Joi.workload = () => Joi.string().valid('full-time', 'fractional')

src/common/helper.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -156,14 +156,6 @@ function setResHeaders (req, res, result) {
156156
}
157157
res.set('Link', link)
158158
}
159-
160-
// Allow browsers access pagination data in headers
161-
let accessControlExposeHeaders = res.get('Access-Control-Expose-Headers') || ''
162-
accessControlExposeHeaders += accessControlExposeHeaders ? ', ' : ''
163-
// append new values, to not override values set by someone else
164-
accessControlExposeHeaders += 'X-Page, X-Per-Page, X-Total, X-Total-Pages, X-Prev-Page, X-Next-Page'
165-
166-
res.set('Access-Control-Expose-Headers', accessControlExposeHeaders)
167159
}
168160

169161
/**
@@ -337,19 +329,27 @@ function isDocumentMissingException (err) {
337329
/**
338330
* Function to get projects
339331
* @param {String} token the user request token
332+
* @param {Object} criteria the search criteria
340333
* @returns the request result
341334
*/
342-
async function getProjects (token) {
335+
async function getProjects (token, criteria = {}) {
343336
const url = `${config.TC_API}/projects?type=talent-as-a-service`
344337
const res = await request
345338
.get(url)
339+
.query(criteria)
346340
.set('Authorization', token)
347341
.set('Content-Type', 'application/json')
348342
.set('Accept', 'application/json')
349343
localLogger.debug({ context: 'getProjects', message: `response body: ${JSON.stringify(res.body)}` })
350-
return _.map(res.body, item => {
344+
const result = _.map(res.body, item => {
351345
return _.pick(item, ['id', 'name'])
352346
})
347+
return {
348+
total: Number(_.get(res.headers, 'x-total')),
349+
page: Number(_.get(res.headers, 'x-page')),
350+
perPage: Number(_.get(res.headers, 'x-per-page')),
351+
result
352+
}
353353
}
354354

355355
/**

src/controllers/JobCandidateController.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ async function deleteJobCandidate (req, res) {
5959
async function searchJobCandidates (req, res) {
6060
const result = await service.searchJobCandidates(req.query)
6161
helper.setResHeaders(req, res, result)
62-
res.send({ result: result.result })
62+
res.send(result.result)
6363
}
6464

6565
module.exports = {

src/controllers/JobController.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ async function deleteJob (req, res) {
5959
async function searchJobs (req, res) {
6060
const result = await service.searchJobs(req.query)
6161
helper.setResHeaders(req, res, result)
62-
res.send({ result: result.result })
62+
res.send(result.result)
6363
}
6464

6565
module.exports = {

src/controllers/ResourceBookingController.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ async function deleteResourceBooking (req, res) {
5959
async function searchResourceBookings (req, res) {
6060
const result = await service.searchResourceBookings(req.query)
6161
helper.setResHeaders(req, res, result)
62-
res.send({ result: result.result })
62+
res.send(result.result)
6363
}
6464

6565
module.exports = {

src/controllers/TeamController.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22
* Controller for TaaS teams endpoints
33
*/
44
const service = require('../services/TeamService')
5+
const helper = require('../common/helper')
56

67
/**
78
* Search teams
89
* @param req the request
910
* @param res the response
1011
*/
1112
async function searchTeams (req, res) {
12-
res.send(await service.searchTeams(req.authUser))
13+
const result = await service.searchTeams(req.authUser, req.query)
14+
helper.setResHeaders(req, res, result)
15+
res.send(result.result)
1316
}
1417

1518
/**

src/services/TeamService.js

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,46 @@ async function _getJobsByProjectIds (projectIds) {
3737
/**
3838
* List teams
3939
* @param {Object} currentUser the user who perform this operation
40+
* @param {Object} criteria the search criteria
4041
* @returns {Object} the search result, contain total/page/perPage and result array
4142
*/
42-
async function searchTeams (currentUser) {
43+
async function searchTeams (currentUser, criteria) {
4344
if (currentUser.isMachine) {
4445
const m2mToken = await helper.getM2Mtoken()
4546
currentUser.jwtToken = `Bearer ${m2mToken}`
4647
}
47-
// Get projects from /v5/projects
48-
const projects = await helper.getProjects(currentUser.jwtToken)
49-
50-
return await getTeamDetail(currentUser, projects)
48+
const sort = `${criteria.sortBy} ${criteria.sortOrder}`
49+
// Get projects from /v5/projects with searching criteria
50+
const { total, page, perPage, result: projects } = await helper.getProjects(
51+
currentUser.jwtToken,
52+
{
53+
page: criteria.page,
54+
perPage: criteria.perPage,
55+
name: criteria.name,
56+
sort
57+
}
58+
)
59+
return {
60+
total,
61+
page,
62+
perPage,
63+
result: await getTeamDetail(currentUser, projects)
64+
}
5165
}
5266

5367
searchTeams.schema = Joi.object().keys({
54-
currentUser: Joi.object().required()
68+
currentUser: Joi.object().required(),
69+
criteria: Joi.object().keys({
70+
page: Joi.page(),
71+
perPage: Joi.perPage(),
72+
sortBy: Joi.string().valid('createdAt', 'updatedAt', 'lastActivityAt', 'id', 'status', 'name', 'type', 'best match').default('lastActivityAt'),
73+
sortOrder: Joi.when('sortBy', {
74+
is: 'best match',
75+
then: Joi.forbidden().label('sortOrder(with sortBy being `best match`)'),
76+
otherwise: Joi.string().valid('asc', 'desc').default('desc')
77+
}),
78+
name: Joi.string()
79+
}).required()
5580
}).required()
5681

5782
/**
@@ -79,7 +104,7 @@ async function getTeamDetail (currentUser, projects, isSearch = true) {
79104
for (const project of projects) {
80105
const rbs = _.filter(resourceBookings, { projectId: project.id })
81106
const res = _.clone(project)
82-
res.weeklyCount = 0
107+
res.weeklyCost = 0
83108
res.resources = []
84109

85110
if (rbs && rbs.length > 0) {
@@ -110,7 +135,7 @@ async function getTeamDetail (currentUser, projects, isSearch = true) {
110135
// normally startDate is smaller than endDate for a resourceBooking so not check if startDate < endDate
111136
if ((!item.startDate || startDate < lastDay) &&
112137
(!item.endDate || endDate > firstDay)) {
113-
res.weeklyCount += item.customerRate
138+
res.weeklyCost += item.customerRate
114139
}
115140
}
116141

test/unit/TeamService.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ describe('Team service test', () => {
109109
expect(entity[0]).to.deep.eql({
110110
id: 9050,
111111
name: 'sample',
112-
weeklyCount: 0,
112+
weeklyCost: 0,
113113
resources: []
114114
})
115115
expect(stubGetProjects.calledOnce).to.be.true

test/unit/common/testData.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,7 @@ const jobsRequestBody = [
590590
const taasTeamItem0ResponseBody = {
591591
id: 9050,
592592
name: 'sample',
593-
weeklyCount: 29.7,
593+
weeklyCost: 29.7,
594594
resources: [
595595
{
596596
id: '1b88e433-828b-4e0d-9fb5-ef75b9dcca6e',
@@ -620,7 +620,7 @@ const taasTeamItem0ResponseBody = {
620620
const taasTeam9050ResponseBody = {
621621
id: 9050,
622622
name: 'sample',
623-
weeklyCount: 29.7,
623+
weeklyCost: 29.7,
624624
resources: [
625625
{
626626
id: '1b88e433-828b-4e0d-9fb5-ef75b9dcca6e',

0 commit comments

Comments
 (0)