Skip to content

Commit 6fedc82

Browse files
author
vikasrohit
authored
Merge pull request #495 from topcoder-platform/hotfix/post-release-2.1.1
[HOTFIX] [PROD] Post release 2.1.1
2 parents 053ce48 + b3a128a commit 6fedc82

File tree

5 files changed

+639
-21
lines changed

5 files changed

+639
-21
lines changed

src/routes/projects/get.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,18 @@ const ES_PROJECT_TYPE = config.get('elasticsearchConfig.docType');
2222
// var permissions = require('tc-core-library-js').middleware.permissions
2323
const permissions = tcMiddleware.permissions;
2424
const PROJECT_ATTRIBUTES = _.without(_.keys(models.Project.rawAttributes), 'utm', 'deletedAt');
25-
const PROJECT_MEMBER_ATTRIBUTES = _.without(_.keys(models.ProjectMember.rawAttributes), 'deletedAt');
25+
const PROJECT_MEMBER_ATTRIBUTES = _.concat(_.without(_.keys(models.ProjectMember.rawAttributes), 'deletedAt'),
26+
['firstName', 'lastName', 'handle', 'email']);
2627
const PROJECT_MEMBER_INVITE_ATTRIBUTES = _.without(_.keys(models.ProjectMemberInvite.rawAttributes), 'deletedAt');
2728
const PROJECT_ATTACHMENT_ATTRIBUTES = _.without(_.keys(models.ProjectAttachment.rawAttributes), 'deletedAt');
29+
const PROJECT_PHASE_ATTRIBUTES = _.without(
30+
_.keys(models.ProjectPhase.rawAttributes),
31+
'deletedAt',
32+
);
33+
const PROJECT_PHASE_PRODUCTS_ATTRIBUTES = _.without(
34+
_.keys(models.PhaseProduct.rawAttributes),
35+
'deletedAt',
36+
);
2837

2938
/**
3039
* Parse the ES search criteria and prepare search request body
@@ -52,13 +61,21 @@ const parseElasticSearchCriteria = (projectId, fields) => {
5261
sourceInclude = sourceInclude.concat(_.map(memberFields, single => `invites.${single}`));
5362
}
5463

64+
if (_.get(fields, 'project_phases', null)) {
65+
const phaseFields = _.get(fields, 'project_phases');
66+
sourceInclude = sourceInclude.concat(_.map(phaseFields, single => `phases.${single}`));
67+
}
68+
if (_.get(fields, 'project_phases_products', null)) {
69+
const phaseFields = _.get(fields, 'project_phases_products');
70+
sourceInclude = sourceInclude.concat(_.map(phaseFields, single => `phases.products.${single}`));
71+
}
5572
if (_.get(fields, 'attachments', null)) {
5673
const attachmentFields = _.get(fields, 'attachments');
5774
sourceInclude = sourceInclude.concat(_.map(attachmentFields, single => `attachments.${single}`));
5875
}
5976

6077
if (sourceInclude) {
61-
searchCriteria._sourceInclude = sourceInclude; // eslint-disable-line no-underscore-dangle
78+
searchCriteria._sourceIncludes = sourceInclude; // eslint-disable-line no-underscore-dangle
6279
}
6380

6481

@@ -87,9 +104,14 @@ const retrieveProjectFromES = (projectId, req) => {
87104
projects: PROJECT_ATTRIBUTES,
88105
project_members: PROJECT_MEMBER_ATTRIBUTES,
89106
project_member_invites: PROJECT_MEMBER_INVITE_ATTRIBUTES,
107+
project_phases: PROJECT_PHASE_ATTRIBUTES,
108+
project_phases_products: PROJECT_PHASE_PRODUCTS_ATTRIBUTES,
90109
attachments: PROJECT_ATTACHMENT_ATTRIBUTES,
91110
});
92111

112+
// if user is not admin, ignore email field for project_members
113+
fields = util.ignoreEmailField(req, fields);
114+
93115
const searchCriteria = parseElasticSearchCriteria(projectId, fields) || {};
94116
return new Promise((accept, reject) => {
95117
const es = util.getElasticSearchClient();
@@ -108,6 +130,7 @@ const retrieveProjectFromDB = (projectId, req) => {
108130
projects: PROJECT_ATTRIBUTES,
109131
project_members: PROJECT_MEMBER_ATTRIBUTES,
110132
});
133+
111134
return models.Project
112135
.findOne({
113136
where: { id: projectId },

src/routes/projects/get.spec.js

Lines changed: 270 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import chai from 'chai';
44
import sinon from 'sinon';
55
import request from 'supertest';
6-
6+
import _ from 'lodash';
77
import config from 'config';
88
import models from '../../models';
99
import util from '../../util';
@@ -23,7 +23,8 @@ const data = [
2323
billingAccountId: 1,
2424
name: 'test1',
2525
description: 'es_project',
26-
status: 'active',
26+
cancelReason: 'price/cost',
27+
status: 'draft',
2728
details: {
2829
utm: {
2930
code: 'code1',
@@ -42,6 +43,7 @@ const data = [
4243
firstName: 'es_member_1_firstName',
4344
lastName: 'Lastname',
4445
handle: 'test_tourist_handle',
46+
email: 'test@test.com',
4547
isPrimary: true,
4648
createdBy: 1,
4749
updatedBy: 1,
@@ -56,6 +58,30 @@ const data = [
5658
updatedBy: 1,
5759
},
5860
],
61+
invites: [
62+
{
63+
id: 1,
64+
userId: 40051335,
65+
email: 'test@topcoder.com',
66+
status: 'pending',
67+
},
68+
],
69+
phases: [
70+
71+
{
72+
id: 45,
73+
name: 'test phases',
74+
spentBudget: 0,
75+
products: [
76+
{
77+
78+
phaseId: 45,
79+
id: 3,
80+
name: 'tet product',
81+
},
82+
],
83+
},
84+
],
5985
attachments: [
6086
{
6187
id: 1,
@@ -80,6 +106,7 @@ describe('GET Project', () => {
80106
.then(() => testUtil.clearES())
81107
.then(() => {
82108
const p1 = models.Project.create({
109+
id: 5,
83110
type: 'generic',
84111
billingAccountId: 1,
85112
name: 'test1',
@@ -98,6 +125,10 @@ describe('GET Project', () => {
98125
projectId: project1.id,
99126
role: 'customer',
100127
isPrimary: true,
128+
firstName: 'Firstname',
129+
lastName: 'Lastname',
130+
handle: 'test_tourist_handle',
131+
email: 'test@test.com',
101132
createdBy: 1,
102133
updatedBy: 1,
103134
});
@@ -343,5 +374,242 @@ describe('GET Project', () => {
343374
});
344375
});
345376
});
377+
378+
describe('URL Query fields', () => {
379+
it('should not return "email" for project members when "fields" query param is not defined (to non-admin users)', (done) => {
380+
request(server)
381+
.get(`/v5/projects/${project1.id}?fields=members.handle`)
382+
.set({
383+
Authorization: `Bearer ${testUtil.jwts.member}`,
384+
})
385+
.expect('Content-Type', /json/)
386+
.expect(200)
387+
.end((err, res) => {
388+
if (err) {
389+
done(err);
390+
} else {
391+
const resJson = res.body;
392+
should.exist(resJson);
393+
resJson.members[0].should.have.property('handle');
394+
resJson.members[0].should.not.have.property('email');
395+
done();
396+
}
397+
});
398+
});
399+
400+
it('should not return "email" for project members even if it\'s defined in "fields" query param (to non-admin users)', (done) => {
401+
request(server)
402+
.get(`/v5/projects/${project1.id}?fields=members.email,members.handle`)
403+
.set({
404+
Authorization: `Bearer ${testUtil.jwts.member}`,
405+
})
406+
.expect('Content-Type', /json/)
407+
.expect(200)
408+
.end((err, res) => {
409+
if (err) {
410+
done(err);
411+
} else {
412+
const resJson = res.body;
413+
should.exist(resJson);
414+
resJson.members[0].should.have.property('handle');
415+
resJson.members[0].should.not.have.property('email');
416+
done();
417+
}
418+
});
419+
});
420+
421+
422+
it('should not return "cancelReason" if it is not listed in "fields" query param ', (done) => {
423+
request(server)
424+
.get(`/v5/projects/${project1.id}?fields=description`)
425+
.set({
426+
Authorization: `Bearer ${testUtil.jwts.member}`,
427+
})
428+
.expect('Content-Type', /json/)
429+
.expect(200)
430+
.end((err, res) => {
431+
if (err) {
432+
done(err);
433+
} else {
434+
const resJson = res.body;
435+
should.exist(resJson);
436+
resJson.should.have.property('description');
437+
resJson.description.should.be.eq('es_project');
438+
resJson.should.not.have.property('cancelReason');
439+
done();
440+
}
441+
});
442+
});
443+
444+
it('should not return "email" for project members when "fields" query param is not defined (to admin users)', (done) => {
445+
request(server)
446+
.get(`/v5/projects/${project1.id}?fields=description,members.id`)
447+
.set({
448+
Authorization: `Bearer ${testUtil.jwts.admin}`,
449+
})
450+
.expect('Content-Type', /json/)
451+
.expect(200)
452+
.end((err, res) => {
453+
if (err) {
454+
done(err);
455+
} else {
456+
const resJson = res.body;
457+
should.exist(resJson);
458+
resJson.members.should.have.lengthOf(2);
459+
resJson.members[0].should.not.have.property('email');
460+
done();
461+
}
462+
});
463+
});
464+
465+
it('should return "email" for project members if it\'s defined in "fields" query param (to admin users', (done) => {
466+
request(server)
467+
.get(`/v5/projects/${project1.id}?fields=description,members.id,members.email`)
468+
.set({
469+
Authorization: `Bearer ${testUtil.jwts.admin}`,
470+
})
471+
.expect('Content-Type', /json/)
472+
.expect(200)
473+
.end((err, res) => {
474+
if (err) {
475+
done(err);
476+
} else {
477+
const resJson = res.body;
478+
should.exist(resJson);
479+
resJson.members.should.have.lengthOf(2);
480+
resJson.members[0].should.have.property('email');
481+
resJson.members[0].email.should.be.eq('test@test.com');
482+
done();
483+
}
484+
});
485+
});
486+
487+
488+
it('should only return "id" field, when it\'s defined in "fields" query param', (done) => {
489+
request(server)
490+
.get(`/v5/projects/${project1.id}?fields=id`)
491+
.set({
492+
Authorization: `Bearer ${testUtil.jwts.admin}`,
493+
})
494+
.expect('Content-Type', /json/)
495+
.expect(200)
496+
.end((err, res) => {
497+
if (err) {
498+
done(err);
499+
} else {
500+
const resJson = res.body;
501+
should.exist(resJson);
502+
resJson.should.have.property('id');
503+
_.keys(resJson).length.should.be.eq(1);
504+
done();
505+
}
506+
});
507+
});
508+
509+
it('should only return "invites.userId" field, when it\'s defined in "fields" query param', (done) => {
510+
request(server)
511+
.get(`/v5/projects/${project1.id}?fields=invites.userId`)
512+
.set({
513+
Authorization: `Bearer ${testUtil.jwts.admin}`,
514+
})
515+
.expect('Content-Type', /json/)
516+
.expect(200)
517+
.end((err, res) => {
518+
if (err) {
519+
done(err);
520+
} else {
521+
const resJson = res.body;
522+
should.exist(resJson);
523+
resJson.invites[0].should.have.property('userId');
524+
_.keys(resJson.invites[0]).length.should.be.eq(1);
525+
done();
526+
}
527+
});
528+
});
529+
530+
it('should only return "members.role" field, when it\'s defined in "fields" query param', (done) => {
531+
request(server)
532+
.get(`/v5/projects/${project1.id}?fields=members.role`)
533+
.set({
534+
Authorization: `Bearer ${testUtil.jwts.admin}`,
535+
})
536+
.expect('Content-Type', /json/)
537+
.expect(200)
538+
.end((err, res) => {
539+
if (err) {
540+
done(err);
541+
} else {
542+
const resJson = res.body;
543+
should.exist(resJson);
544+
resJson.members[0].should.have.property('role');
545+
_.keys(resJson.members[0]).length.should.be.eq(1);
546+
done();
547+
}
548+
});
549+
});
550+
551+
it('should only return "attachments.title" field, when it\'s defined in "fields" query param', (done) => {
552+
request(server)
553+
.get(`/v5/projects/${project1.id}?fields=attachments.title`)
554+
.set({
555+
Authorization: `Bearer ${testUtil.jwts.admin}`,
556+
})
557+
.expect('Content-Type', /json/)
558+
.expect(200)
559+
.end((err, res) => {
560+
if (err) {
561+
done(err);
562+
} else {
563+
const resJson = res.body;
564+
should.exist(resJson);
565+
resJson.attachments[0].should.have.property('title');
566+
_.keys(resJson.attachments[0]).length.should.be.eq(1);
567+
done();
568+
}
569+
});
570+
});
571+
572+
it('should only return "phases.name" field, when it\'s defined in "fields" query param', (done) => {
573+
request(server)
574+
.get(`/v5/projects/${project1.id}?fields=phases.name`)
575+
.set({
576+
Authorization: `Bearer ${testUtil.jwts.admin}`,
577+
})
578+
.expect('Content-Type', /json/)
579+
.expect(200)
580+
.end((err, res) => {
581+
if (err) {
582+
done(err);
583+
} else {
584+
const resJson = res.body;
585+
should.exist(resJson);
586+
resJson.phases[0].should.have.property('name');
587+
_.keys(resJson.phases[0]).length.should.be.eq(1);
588+
done();
589+
}
590+
});
591+
});
592+
593+
it('should only return "phases.products.name" field, when it\'s defined in "fields" query param and "phases" is also defined', (done) => {
594+
request(server)
595+
.get(`/v5/projects/${project1.id}?fields=phases.products.name,phases.name`)
596+
.set({
597+
Authorization: `Bearer ${testUtil.jwts.admin}`,
598+
})
599+
.expect('Content-Type', /json/)
600+
.expect(200)
601+
.end((err, res) => {
602+
if (err) {
603+
done(err);
604+
} else {
605+
const resJson = res.body;
606+
should.exist(resJson);
607+
resJson.phases[0].products[0].should.have.property('name');
608+
_.keys(resJson.phases[0].products[0]).length.should.be.eq(1);
609+
done();
610+
}
611+
});
612+
});
613+
});
346614
});
347615
});

0 commit comments

Comments
 (0)