Skip to content

Commit cb3f60b

Browse files
committed
winning submission from challenge 30090281 - Topcoder Project Service - Fix duplicated email invitations, fix issue #3002
1 parent 29ed9d4 commit cb3f60b

File tree

2 files changed

+128
-12
lines changed

2 files changed

+128
-12
lines changed

src/routes/projectMemberInvites/create.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ const buildCreateInvitePromises = (req, invite, invites, data, failed) => {
8383
});
8484

8585
// remove invites for users that are invited already
86-
_.remove(nonExistentUserEmails, email => _.some(invites, i => i.email === email));
86+
_.remove(nonExistentUserEmails, email =>
87+
_.some(invites, i => _.toLower(i.email) === _.toLower(email)));
8788
nonExistentUserEmails.forEach((email) => {
8889
const dataNew = _.clone(data);
8990

src/routes/projectMemberInvites/create.spec.js

Lines changed: 126 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -73,17 +73,40 @@ describe('Project Member Invite create', () => {
7373
createdBy: 1,
7474
updatedBy: 1,
7575
}).then(() => {
76-
models.ProjectMemberInvite.create({
77-
projectId: project1.id,
78-
userId: 40051335,
79-
email: null,
80-
role: PROJECT_MEMBER_ROLE.MANAGER,
81-
status: INVITE_STATUS.PENDING,
82-
createdBy: 1,
83-
updatedBy: 1,
84-
createdAt: '2016-06-30 00:33:07+00',
85-
updatedAt: '2016-06-30 00:33:07+00',
86-
}).then(() => {
76+
const promises = [
77+
models.ProjectMemberInvite.create({
78+
projectId: project1.id,
79+
userId: 40051335,
80+
email: null,
81+
role: PROJECT_MEMBER_ROLE.MANAGER,
82+
status: INVITE_STATUS.PENDING,
83+
createdBy: 1,
84+
updatedBy: 1,
85+
createdAt: '2016-06-30 00:33:07+00',
86+
updatedAt: '2016-06-30 00:33:07+00',
87+
}),
88+
models.ProjectMemberInvite.create({
89+
projectId: project1.id,
90+
email: 'duplicate_lowercase@gmail.com',
91+
role: PROJECT_MEMBER_ROLE.MANAGER,
92+
status: INVITE_STATUS.PENDING,
93+
createdBy: 1,
94+
updatedBy: 1,
95+
createdAt: '2016-06-30 00:33:07+00',
96+
updatedAt: '2016-06-30 00:33:07+00',
97+
}),
98+
models.ProjectMemberInvite.create({
99+
projectId: project1.id,
100+
email: 'DUPLICATE_UPPERCASE@gmail.com',
101+
role: PROJECT_MEMBER_ROLE.MANAGER,
102+
status: INVITE_STATUS.PENDING,
103+
createdBy: 1,
104+
updatedBy: 1,
105+
createdAt: '2016-06-30 00:33:07+00',
106+
updatedAt: '2016-06-30 00:33:07+00',
107+
}),
108+
];
109+
Promise.all(promises).then(() => {
87110
done();
88111
});
89112
});
@@ -640,6 +663,98 @@ describe('Project Member Invite create', () => {
640663
});
641664
});
642665

666+
it('should return 201 and empty response when trying add already invited member by lowercase email', (done) => {
667+
const mockHttpClient = _.merge(testUtil.mockHttpClient, {
668+
get: () => Promise.resolve({
669+
status: 200,
670+
data: {
671+
id: 'requesterId',
672+
version: 'v3',
673+
result: {
674+
success: true,
675+
status: 200,
676+
content: {
677+
success: [{
678+
roleName: USER_ROLE.COPILOT,
679+
}],
680+
},
681+
},
682+
},
683+
}),
684+
});
685+
sandbox.stub(util, 'getHttpClient', () => mockHttpClient);
686+
request(server)
687+
.post(`/v4/projects/${project1.id}/members/invite`)
688+
.set({
689+
Authorization: `Bearer ${testUtil.jwts.copilot}`,
690+
})
691+
.send({
692+
param: {
693+
emails: ['DUPLICATE_LOWERCASE@gmail.com'],
694+
role: 'customer',
695+
},
696+
})
697+
.expect('Content-Type', /json/)
698+
.expect(201)
699+
.end((err, res) => {
700+
if (err) {
701+
done(err);
702+
} else {
703+
const resJson = res.body.result.content.success;
704+
should.exist(resJson);
705+
resJson.length.should.equal(0);
706+
server.services.pubsub.publish.neverCalledWith('project.member.invite.created').should.be.true;
707+
done();
708+
}
709+
});
710+
});
711+
712+
it('should return 201 and empty response when trying add already invited member by uppercase email', (done) => {
713+
const mockHttpClient = _.merge(testUtil.mockHttpClient, {
714+
get: () => Promise.resolve({
715+
status: 200,
716+
data: {
717+
id: 'requesterId',
718+
version: 'v3',
719+
result: {
720+
success: true,
721+
status: 200,
722+
content: {
723+
success: [{
724+
roleName: USER_ROLE.COPILOT,
725+
}],
726+
},
727+
},
728+
},
729+
}),
730+
});
731+
sandbox.stub(util, 'getHttpClient', () => mockHttpClient);
732+
request(server)
733+
.post(`/v4/projects/${project1.id}/members/invite`)
734+
.set({
735+
Authorization: `Bearer ${testUtil.jwts.copilot}`,
736+
})
737+
.send({
738+
param: {
739+
emails: ['duplicate_uppercase@gmail.com'],
740+
role: 'customer',
741+
},
742+
})
743+
.expect('Content-Type', /json/)
744+
.expect(201)
745+
.end((err, res) => {
746+
if (err) {
747+
done(err);
748+
} else {
749+
const resJson = res.body.result.content.success;
750+
should.exist(resJson);
751+
resJson.length.should.equal(0);
752+
server.services.pubsub.publish.neverCalledWith('project.member.invite.created').should.be.true;
753+
done();
754+
}
755+
});
756+
});
757+
643758
describe('Bus api', () => {
644759
let createEventSpy;
645760

0 commit comments

Comments
 (0)