3 var env = require('gitter-web-env');
5 var userService = require('gitter-web-users');
6 var persistence = require('gitter-web-persistence');
7 var userDefaultFlagsService = require('./user-default-flags-service');
8 var Troupe = persistence.Troupe;
9 var assert = require('assert');
10 var mongoUtils = require('gitter-web-persistence-utils/lib/mongo-utils');
11 var Promise = require('bluebird');
12 var ObjectID = require('mongodb').ObjectID;
13 var mongooseUtils = require('gitter-web-persistence-utils/lib/mongoose-utils');
14 var StatusError = require('statuserror');
15 var roomMembershipService = require('./room-membership-service');
16 var policyFactory = require('gitter-web-permissions/lib/policy-factory');
17 var debug = require('debug')('gitter:app:one-to-one-room-service');
19 function getOneToOneRoomQuery(userId1, userId2) {
20 // Need to use $elemMatch due to a regression in Mongo 2.6, see https://jira.mongodb.org/browse/SERVER-13843
24 { oneToOneUsers: { $elemMatch: { userId: userId1 } } },
25 { oneToOneUsers: { $elemMatch: { userId: userId2 } } }
30 function findOneToOneRoom(fromUserId, toUserId) {
31 assert(fromUserId, 'Need to provide fromUserId');
32 assert(toUserId, 'Need to provide toUserId');
34 fromUserId = mongoUtils.asObjectID(fromUserId);
35 toUserId = mongoUtils.asObjectID(toUserId);
37 if (mongoUtils.objectIDsEqual(fromUserId, toUserId)) throw new StatusError(417); // You cannot be in a troupe with yourself.
39 var query = getOneToOneRoomQuery(fromUserId, toUserId);
41 /* Find the existing one-to-one.... */
42 return persistence.Troupe.findOne(query).exec();
45 function findOneToOneRoomsForUserId(userId) {
46 assert(userId, 'userId required');
48 return persistence.Troupe.find({
52 userId: mongoUtils.asObjectID(userId)
63 * Returns [troupe, existing]
65 function upsertNewOneToOneRoom(userId1, userId2) {
66 var query = getOneToOneRoomQuery(userId1, userId2);
68 // Second attempt is an upsert
72 githubType: 'ONETOONE',
73 groupId: null, // One-to-ones are never in a group
87 public: false // One-to-ones are always private
91 debug('Attempting upsert for new one-to-one room');
93 // Upsert returns [model, existing] already
94 return mongooseUtils.upsert(Troupe, query, {
95 $setOnInsert: insertFields
99 function addOneToOneMemberToRoom(troupeId, userId) {
100 // Deal with https://github.com/troupe/gitter-webapp/issues/1227
101 return userDefaultFlagsService.getDefaultFlagsOneToOneForUserId(userId).then(function(flags) {
102 return roomMembershipService.addRoomMember(troupeId, userId, flags, null);
107 * Ensure that the current user is in the one-to-one room
109 function ensureUsersInRoom(troupeId, fromUserId, toUserId) {
110 return roomMembershipService
111 .findMembershipForUsersInRoom(troupeId, [fromUserId, toUserId])
112 .then(function(userIds) {
113 // Both members are in the room
114 if (userIds.length === 2) return;
116 var fromUserInRoom = userIds.some(function(userId) {
117 return mongoUtils.objectIDsEqual(userId, fromUserId);
120 var toUserInRoom = userIds.some(function(userId) {
121 return mongoUtils.objectIDsEqual(userId, toUserId);
124 debug('Re-adding users to room: fromUser=%s, toUser=%s', fromUserInRoom, toUserInRoom);
127 !fromUserInRoom && addOneToOneMemberToRoom(troupeId, fromUserId),
128 !toUserInRoom && addOneToOneMemberToRoom(troupeId, toUserId)
134 * Ensure that both users are in the one-to-one room
136 function addOneToOneUsersToNewRoom(troupeId, fromUserId, toUserId) {
137 return userDefaultFlagsService
138 .getDefaultOneToOneFlagsForUserIds([fromUserId, toUserId])
139 .then(function(userFlags) {
140 var fromUserFlags = userFlags[fromUserId];
141 var toUserFlags = userFlags[toUserId];
143 if (!fromUserFlags) throw new StatusError(404);
144 if (!toUserFlags) throw new StatusError(404);
147 roomMembershipService.addRoomMember(troupeId, fromUserId, fromUserFlags, null),
148 roomMembershipService.addRoomMember(troupeId, toUserId, toUserFlags, null)
154 * Find a one-to-one troupe, otherwise create it
156 * @return {[ troupe, other-user ]}
158 function findOrCreateOneToOneRoom(fromUser, toUserId) {
159 assert(fromUser, 'Need to provide fromUser');
160 assert(fromUser._id, 'fromUser invalid');
161 assert(toUserId, 'Need to provide toUserId');
163 var fromUserId = fromUser._id;
164 toUserId = mongoUtils.asObjectID(toUserId);
172 .then(function(toUser) {
173 if (!toUser) throw new StatusError(404, 'User does not exist');
174 this.toUser = toUser;
175 return findOneToOneRoom(fromUserId, toUserId);
177 .then(function(existingRoom) {
179 return [existingRoom, true];
182 var toUser = this.toUser;
184 // TODO: in future we need to add request one-to-one here...
186 .createPolicyForOneToOne(fromUser, toUser)
187 .then(function(policy) {
188 return policy.canJoin();
190 .then(function(canJoin) {
192 var err = new StatusError(404);
193 err.githubType = 'ONETOONE';
194 err.uri = toUser.username;
198 return upsertNewOneToOneRoom(fromUserId, toUserId);
201 .spread(function(troupe, isAlreadyExisting) {
202 debug('findOrCreate isAlreadyExisting=%s', isAlreadyExisting);
204 var troupeId = troupe._id;
205 this.troupe = troupe;
207 if (isAlreadyExisting) {
208 return ensureUsersInRoom(troupeId, fromUserId, toUserId);
210 stats.event('new_troupe', {
216 return addOneToOneUsersToNewRoom(troupeId, fromUserId, toUserId);
220 return [this.troupe, this.toUser];
226 findOrCreateOneToOneRoom: Promise.method(findOrCreateOneToOneRoom),
227 findOneToOneRoom: Promise.method(findOneToOneRoom),
228 findOneToOneRoomsForUserId: findOneToOneRoomsForUserId