Skip to content

Commit 7f7818b

Browse files
authored
Merge pull request #219 from maxceem/connect-issue-2741-invite-existent-user
Fix 1 for Connect issue 2741 invite existent user
2 parents cf04b21 + a499d04 commit 7f7818b

File tree

2 files changed

+126
-110
lines changed

2 files changed

+126
-110
lines changed

src/routes/projectMemberInvites/create.js

Lines changed: 123 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,70 @@ const addMemberValidations = {
2828
},
2929
};
3030

31+
/**
32+
* Helper method to build promises for creating new invites in DB
33+
*
34+
* @param {Object} req express request object
35+
* @param {Object} invite invite to process
36+
* @param {Array} invites existent invites from DB
37+
* @param {Object} data template for new invites to be put in DB
38+
*
39+
* @returns {Promise<Promise[]>} list of promises
40+
*/
41+
const buildCreateInvitePromises = (req, invite, invites, data) => {
42+
const invitePromises = [];
43+
44+
if (invite.userIds) {
45+
// remove invites for users that are invited already
46+
_.remove(invite.userIds, u => _.some(invites, i => i.userId === u));
47+
invite.userIds.forEach((userId) => {
48+
const dataNew = _.clone(data);
49+
50+
dataNew.userId = userId;
51+
52+
invitePromises.push(models.ProjectMemberInvite.create(dataNew));
53+
});
54+
}
55+
56+
if (invite.emails) {
57+
// if for some emails there are already existent users, we will invite them by userId,
58+
// to avoid sending them registration email
59+
return util.lookupUserEmails(req, invite.emails)
60+
.then((existentUsers) => {
61+
// for existent users - invite by ids
62+
const existentUserIds = existentUsers.map(user => parseInt(user.id, 10));
63+
// the rest of email of non-existent users, so we will invite them by email
64+
const nonExistentUserEmails = invite.emails.filter(inviteEmail =>
65+
!_.find(existentUsers, { email: inviteEmail }),
66+
);
67+
68+
// remove invites for users that are invited already
69+
_.remove(existentUserIds, userId => _.some(invites, i => i.userId === userId));
70+
existentUserIds.forEach((userId) => {
71+
const dataNew = _.clone(data);
72+
73+
dataNew.userId = userId;
74+
75+
invitePromises.push(models.ProjectMemberInvite.create(dataNew));
76+
});
77+
78+
// remove invites for users that are invited already
79+
_.remove(nonExistentUserEmails, email => _.some(invites, i => i.email === email));
80+
nonExistentUserEmails.forEach((email) => {
81+
const dataNew = _.clone(data);
82+
83+
dataNew.email = email;
84+
85+
invitePromises.push(models.ProjectMemberInvite.create(dataNew));
86+
});
87+
88+
return Promise.resolve(invitePromises);
89+
});
90+
}
91+
92+
return Promise.resolve(invitePromises);
93+
};
94+
3195
module.exports = [
3296
// handles request validations
3397
validate(addMemberValidations),
@@ -103,120 +167,72 @@ module.exports = [
103167
createdBy: req.authUser.userId,
104168
updatedBy: req.authUser.userId,
105169
};
106-
const invitePromises = [];
107-
if (invite.userIds) {
108-
// remove invites for users that are invited already
109-
_.remove(invite.userIds, u => _.some(invites, i => i.userId === u));
110-
invite.userIds.forEach((userId) => {
111-
const dataNew = _.clone(data);
112-
_.assign(dataNew, {
113-
userId,
114-
});
115-
invitePromises.push(models.ProjectMemberInvite.create(dataNew));
116-
});
117-
}
118-
data.userId = null;
119-
120-
if (invite.emails) {
121-
// if for some emails there are already existent users, we will invite them by userId,
122-
// to avoid sending them registration email
123-
const existentUsers = util.lookupUserEmails(req, invite.emails).map((user) => {
124-
const userWithNumberId = {};
125-
_.assign(userWithNumberId, user, {
126-
id: parseInt(user.id, 10),
127-
});
128-
return userWithNumberId;
129-
});
130-
// for existent users - invite by ids
131-
const existentUserIds = _.map(existentUsers, 'id');
132-
// the rest of email of non-existent users, so we will invite them by email
133-
const nonExistentUserEmails = invite.emails.filter(inviteEmail =>
134-
!_.find(existentUsers, { email: inviteEmail }),
135-
);
136-
137-
// remove invites for users that are invited already
138-
_.remove(existentUserIds, userId => _.some(invites, i => i.userId === userId));
139-
existentUserIds.forEach((userId) => {
140-
const dataNew = _.clone(data);
141-
_.assign(dataNew, {
142-
userId,
143-
});
144-
invitePromises.push(models.ProjectMemberInvite.create(dataNew));
145-
});
146-
147-
// remove invites for users that are invited already
148-
_.remove(nonExistentUserEmails, email => _.some(invites, i => i.email === email));
149-
nonExistentUserEmails.forEach((email) => {
150-
const dataNew = _.clone(data);
151-
_.assign(dataNew, {
152-
email,
153-
});
154-
invitePromises.push(models.ProjectMemberInvite.create(dataNew));
155-
});
156-
}
157170

158-
if (invitePromises.length === 0) {
159-
return [];
160-
}
161-
162-
req.log.debug('Creating invites');
163-
const emailEventType = BUS_API_EVENT.PROJECT_MEMBER_EMAIL_INVITE_CREATED;
164-
return models.sequelize.Promise.all(invitePromises)
165-
.then((values) => {
166-
values.forEach((v) => {
167-
req.app.emit(EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_CREATED, {
168-
req,
169-
userId: v.userId,
170-
email: v.email,
171-
});
172-
req.app.services.pubsub.publish(
173-
EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_CREATED,
174-
v,
175-
{ correlationId: req.id },
176-
);
177-
// send email invite (async)
178-
if (v.email) {
179-
models.Project
180-
.find({
181-
where: { id: projectId },
182-
raw: true,
183-
})
184-
.then((_project) => {
185-
createEvent(emailEventType,
186-
{
187-
data: {
188-
connectURL: config.get('connectUrl'),
189-
accountsAppURL: config.get('accountsAppUrl'),
190-
subject: config.get('inviteEmailSubject'),
191-
projects: [
192-
{
193-
name: _project.name,
194-
projectId,
195-
sections: [
171+
return buildCreateInvitePromises(req, invite, invites, data)
172+
.then((invitePromises) => {
173+
if (invitePromises.length === 0) {
174+
return [];
175+
}
176+
177+
req.log.debug('Creating invites');
178+
const emailEventType = BUS_API_EVENT.PROJECT_MEMBER_EMAIL_INVITE_CREATED;
179+
return models.sequelize.Promise.all(invitePromises)
180+
.then((values) => {
181+
values.forEach((v) => {
182+
req.app.emit(EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_CREATED, {
183+
req,
184+
userId: v.userId,
185+
email: v.email,
186+
});
187+
req.app.services.pubsub.publish(
188+
EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_CREATED,
189+
v,
190+
{ correlationId: req.id },
191+
);
192+
// send email invite (async)
193+
if (v.email) {
194+
models.Project
195+
.find({
196+
where: { id: projectId },
197+
raw: true,
198+
})
199+
.then((_project) => {
200+
createEvent(emailEventType,
201+
{
202+
data: {
203+
connectURL: config.get('connectUrl'),
204+
accountsAppURL: config.get('accountsAppUrl'),
205+
subject: config.get('inviteEmailSubject'),
206+
projects: [
196207
{
197-
EMAIL_INVITES: true,
198-
title: config.get('inviteEmailSectionTitle'),
199-
projectName: _project.name,
208+
name: _project.name,
200209
projectId,
210+
sections: [
211+
{
212+
EMAIL_INVITES: true,
213+
title: config.get('inviteEmailSectionTitle'),
214+
projectName: _project.name,
215+
projectId,
216+
},
217+
],
201218
},
202219
],
203220
},
204-
],
205-
},
206-
recipients: [v.email],
207-
version: 'v3',
208-
from: {
209-
name: config.get('EMAIL_INVITE_FROM_NAME'),
210-
email: config.get('EMAIL_INVITE_FROM_EMAIL'),
211-
},
212-
categories: [`${process.env.NODE_ENV}:${emailEventType}`.toLowerCase()],
213-
}, req.log);
221+
recipients: [v.email],
222+
version: 'v3',
223+
from: {
224+
name: config.get('EMAIL_INVITE_FROM_NAME'),
225+
email: config.get('EMAIL_INVITE_FROM_EMAIL'),
226+
},
227+
categories: [`${process.env.NODE_ENV}:${emailEventType}`.toLowerCase()],
228+
}, req.log);
229+
});
230+
}
214231
});
215-
}
216-
});
217-
return values;
218-
});
219-
});
232+
return values;
233+
}); // models.sequelize.Promise.all
234+
}); // buildCreateInvitePromises
235+
}); // models.ProjectMemberInvite.getPendingInvitesForProject
220236
})
221237
.then(values => res.status(201).json(util.wrapResponse(req.id, values, null, 201)))
222238
.catch(err => next(err));

src/routes/projectMemberInvites/create.spec.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ describe('Project Member Invite create', () => {
8787
sinon.stub(server.services.pubsub, 'init', () => {});
8888
sinon.stub(server.services.pubsub, 'publish', () => {});
8989
// by default mock lookupUserEmails return nothing so all the cases are not broken
90-
sandbox.stub(util, 'lookupUserEmails', () => []);
90+
sandbox.stub(util, 'lookupUserEmails', () => Promise.resolve([]));
9191
});
9292
afterEach(() => {
9393
sandbox.restore();
@@ -296,10 +296,10 @@ describe('Project Member Invite create', () => {
296296
});
297297
sandbox.stub(util, 'getHttpClient', () => mockHttpClient);
298298
util.lookupUserEmails.restore();
299-
sandbox.stub(util, 'lookupUserEmails', () => [{
299+
sandbox.stub(util, 'lookupUserEmails', () => Promise.resolve([{
300300
id: '12345',
301301
email: 'hello@world.com',
302-
}]);
302+
}]));
303303
request(server)
304304
.post(`/v4/projects/${project2.id}/members/invite`)
305305
.set({

0 commit comments

Comments
 (0)