@tiledesk/tiledesk-server 2.9.26 → 2.9.28

Sign up to get free protection for your applications and to get access to all the features.
@@ -17,8 +17,10 @@ var UIDGenerator = require("../utils/UIDGenerator");
17
17
  const { TdCache } = require('../utils/TdCache');
18
18
  const { QuoteManager } = require('./QuoteManager');
19
19
  var configGlobal = require('../config/global');
20
+ const projectService = require('./projectService');
20
21
  const axios = require("axios").default;
21
22
 
23
+
22
24
  const apiUrl = process.env.API_URL || configGlobal.apiUrl;
23
25
 
24
26
  let tdCache = new TdCache({
@@ -272,7 +274,7 @@ class RequestService {
272
274
  var beforeParticipants = requestBeforeRoute.participants;
273
275
  winston.debug("beforeParticipants: ", beforeParticipants);
274
276
 
275
- return that.routeInternal(request, departmentid, id_project, nobot).then(function (routedRequest) {
277
+ return that.routeInternal(request, departmentid, id_project, nobot).then( async function (routedRequest) {
276
278
 
277
279
  winston.debug("after routeInternal", routedRequest);
278
280
  // winston.info("requestBeforeRoute.participants " +requestBeforeRoute.request_id , requestBeforeRoute.participants);
@@ -286,13 +288,22 @@ class RequestService {
286
288
  winston.debug("beforeDepartmentId:" + beforeDepartmentId);
287
289
  }
288
290
 
291
+
289
292
  let afterDepartmentId;
290
293
  if (routedRequest.department) {
291
294
  afterDepartmentId = routedRequest.department.toString();
292
295
  winston.debug("afterDepartmentId:" + afterDepartmentId);
293
296
  }
294
297
 
295
-
298
+ winston.debug("requestBefore status: ", requestBeforeRoute.status)
299
+ winston.debug("routedRequest status: ", routedRequest.status)
300
+ /**
301
+ * Case 1
302
+ * After internal routing:
303
+ * - same STATUS
304
+ * - same DEPARTMENT
305
+ * - same PARTICIPANTS
306
+ */
296
307
  if (requestBeforeRoute.status === routedRequest.status &&
297
308
  beforeDepartmentId === afterDepartmentId &&
298
309
  requestUtil.arraysEqual(beforeParticipants, routedRequest.participants)) {
@@ -317,6 +328,62 @@ class RequestService {
317
328
  });
318
329
  }
319
330
 
331
+ let project = await projectService.getCachedProject(id_project).catch((err) => {
332
+ winston.warn("Error getting cached project. Skip conversation quota check.")
333
+ winston.warn("Getting cached project error: ", err)
334
+ })
335
+
336
+
337
+ let isTestConversation = false;
338
+ let isVoiceConversation = false;
339
+ let isStandardConversation = false;
340
+
341
+ let payload = {
342
+ project: project,
343
+ request: request
344
+ }
345
+
346
+ if (request.attributes && request.attributes.sourcePage && (request.attributes.sourcePage.indexOf("td_draft=true") > -1)) {
347
+ winston.verbose("is a test conversation --> skip quote availability check")
348
+ isTestConversation = true;
349
+ }
350
+ else if (request.channel && (request.channel.name === 'voice-vxml')) {
351
+ winston.verbose("is a voice conversation --> skip quote availability check")
352
+ isVoiceConversation = true;
353
+ }
354
+ else {
355
+ isStandardConversation = true;
356
+ let available = await qm.checkQuote(project, request, 'requests');
357
+ if (available === false) {
358
+ winston.info("Requests limits reached for project " + project._id)
359
+ return reject("Requests limits reached for project " + project._id);
360
+ }
361
+ }
362
+
363
+ /**
364
+ * Case 2 - Leaving TEMP status
365
+ * After internal routing:
366
+ * - STATUS changed from 50 to 100 or 200
367
+ */
368
+ if (requestBeforeRoute.status === RequestConstants.TEMP && (routedRequest.status === RequestConstants.ASSIGNED || routedRequest.status === RequestConstants.UNASSIGNED)) {
369
+ // console.log("Case 2 - Leaving TEMP status")
370
+ if (isStandardConversation) {
371
+ requestEvent.emit('request.create.quote', payload);
372
+ }
373
+ }
374
+
375
+ /**
376
+ * Case 3 - Conversation opened through proactive message
377
+ * After internal routing:
378
+ * - STATUS changed from undefined to 100
379
+ */
380
+ if ((!requestBeforeRoute.status || requestBeforeRoute.status === undefined) && routedRequest.status === RequestConstants.ASSIGNED) {
381
+ // console.log("Case 3 - 'Proactive' request")
382
+ if (isStandardConversation) {
383
+ requestEvent.emit('request.create.quote', payload);
384
+ }
385
+ }
386
+
320
387
  //cacheinvalidation
321
388
  return routedRequest.save(function (err, savedRequest) {
322
389
  // https://stackoverflow.com/questions/54792749/mongoose-versionerror-no-matching-document-found-for-id-when-document-is-being
@@ -469,6 +536,232 @@ class RequestService {
469
536
 
470
537
  async create(request) {
471
538
 
539
+ if (!request.createdAt) {
540
+ request.createdAt = new Date();
541
+ }
542
+
543
+ var request_id = request.request_id;
544
+ var project_user_id = request.project_user_id;
545
+ var lead_id = request.lead_id;
546
+ var id_project = request.id_project;
547
+ var first_text = request.first_text;
548
+ var departmentid = request.departmentid;
549
+ var sourcePage = request.sourcePage;
550
+ var language = request.language;
551
+ var userAgent = request.userAgent;
552
+ var status = request.status;
553
+ var createdBy = request.createdBy;
554
+ var attributes = request.attributes;
555
+ var subject = request.subject;
556
+ var preflight = request.preflight;
557
+ var channel = request.channel;
558
+ var location = request.location;
559
+ var participants = request.participants || [];
560
+ var tags = request.tags;
561
+ var notes = request.notes;
562
+ var priority = request.priority;
563
+ var auto_close = request.auto_close;
564
+ var followers = request.followers;
565
+ let createdAt = request.createdAt;
566
+
567
+ if (!departmentid) {
568
+ departmentid = 'default';
569
+ }
570
+
571
+ if (!createdBy) {
572
+ if (project_user_id) {
573
+ createdBy = project_user_id;
574
+ } else {
575
+ createdBy = "system";
576
+ }
577
+ }
578
+
579
+ // Utils
580
+ let payload;
581
+ let isTestConversation = false;
582
+ let isVoiceConversation = false;
583
+ let isStandardConversation = false;
584
+ var that = this;
585
+
586
+ return new Promise( async (resolve, reject) => {
587
+ var context = {
588
+ request: {
589
+ request_id: request_id, project_user_id: project_user_id, lead_id: lead_id, id_project: id_project,
590
+ first_text: first_text, departmentid: departmentid, sourcePage: sourcePage, language: language, userAgent: userAgent, status: status,
591
+ createdBy: createdBy, attributes: attributes, subject: subject, preflight: preflight, channel: channel, location: location,
592
+ participants: participants, tags: tags, notes: notes,
593
+ priority: priority, auto_close: auto_close, followers: followers
594
+ }
595
+ };
596
+ winston.debug("context", context);
597
+
598
+ var participantsAgents = [];
599
+ var participantsBots = [];
600
+ var hasBot = false;
601
+ var dep_id = undefined;
602
+ var assigned_at = undefined;
603
+ var agents = [];
604
+ var snapshot = {};
605
+
606
+ try {
607
+ // (method) DepartmentService.getOperators(departmentid: any, projectid: any, nobot: any, disableWebHookCall: any, context: any): Promise<any>
608
+ var result = await departmentService.getOperators(departmentid, id_project, false, undefined, context);
609
+ winston.debug("getOperators", result);
610
+
611
+ } catch (err) {
612
+ return reject(err);
613
+ }
614
+
615
+ agents = result.agents;
616
+
617
+ if (status == 50) {
618
+ // skip assignment
619
+ if (participants.length == 0) {
620
+ dep_id = result.department._id;
621
+ }
622
+ } else {
623
+
624
+ let project = await projectService.getCachedProject(id_project).catch((err) => {
625
+ winston.warn("Error getting cached project. Skip conversation quota check.")
626
+ winston.warn("Getting cached project error: ", err)
627
+ })
628
+
629
+ payload = {
630
+ project: project,
631
+ request: request
632
+ }
633
+
634
+ if (attributes && attributes.sourcePage && (attributes.sourcePage.indexOf("td_draft=true") > -1)) {
635
+ winston.verbose("is a test conversation --> skip quote availability check")
636
+ isTestConversation = true;
637
+ }
638
+ else if (channel && (channel.name === 'voice-vxml')) {
639
+ winston.verbose("is a voice conversation --> skip quote availability check")
640
+ isVoiceConversation = true;
641
+ }
642
+ else {
643
+ isStandardConversation = true;
644
+ let available = await qm.checkQuote(project, request, 'requests');
645
+ if (available === false) {
646
+ winston.info("Requests limits reached for project " + project._id)
647
+ return reject("Requests limits reached for project " + project._id);
648
+ }
649
+ }
650
+
651
+
652
+ if (participants.length == 0) {
653
+ if (result.operators && result.operators.length > 0) {
654
+ participants.push(result.operators[0].id_user.toString());
655
+ }
656
+ // for preflight it is important to save agents in req for trigger. try to optimize it
657
+ dep_id = result.department._id;
658
+ }
659
+
660
+ if (participants.length > 0) {
661
+ status = RequestConstants.ASSIGNED;
662
+ // botprefix
663
+ if (participants[0].startsWith("bot_")) {
664
+
665
+ hasBot = true;
666
+ winston.debug("hasBot:" + hasBot);
667
+
668
+ // botprefix
669
+ var assigned_operator_idStringBot = participants[0].replace("bot_", "");
670
+ winston.debug("assigned_operator_idStringBot:" + assigned_operator_idStringBot);
671
+
672
+ participantsBots.push(assigned_operator_idStringBot);
673
+
674
+ } else {
675
+
676
+ participantsAgents.push(participants[0]);
677
+
678
+ }
679
+
680
+ assigned_at = Date.now();
681
+
682
+ } else {
683
+ status = RequestConstants.UNASSIGNED;
684
+ }
685
+ }
686
+
687
+ if (dep_id) {
688
+ snapshot.department = result.department;
689
+ }
690
+
691
+ snapshot.agents = agents;
692
+ snapshot.availableAgentsCount = that.getAvailableAgentsCount(agents);
693
+
694
+ if (request.requester) {
695
+ snapshot.requester = request.requester;
696
+ }
697
+ if (request.lead) {
698
+ snapshot.lead = request.lead;
699
+ }
700
+
701
+ var newRequest = new Request({
702
+ request_id: request_id,
703
+ requester: project_user_id,
704
+ lead: lead_id,
705
+ first_text: first_text,
706
+ subject: subject,
707
+ status: status,
708
+ participants: participants,
709
+ participantsAgents: participantsAgents,
710
+ participantsBots: participantsBots,
711
+ hasBot: hasBot,
712
+ department: dep_id,
713
+ // agents: agents,
714
+ //others
715
+ sourcePage: sourcePage,
716
+ language: language,
717
+ userAgent: userAgent,
718
+ assigned_at: assigned_at,
719
+ attributes: attributes,
720
+ //standard
721
+ id_project: id_project,
722
+ createdBy: createdBy,
723
+ updatedBy: createdBy,
724
+ preflight: preflight,
725
+ channel: channel,
726
+ location: location,
727
+ snapshot: snapshot,
728
+ tags: tags,
729
+ notes: notes,
730
+ priority: priority,
731
+ auto_close: auto_close,
732
+ followers: followers,
733
+ createdAt: createdAt
734
+ });
735
+
736
+ if (isTestConversation) {
737
+ newRequest.draft = true;
738
+ }
739
+
740
+ winston.debug('newRequest.', newRequest);
741
+
742
+ //cacheinvalidation
743
+ return newRequest.save( async function (err, savedRequest) {
744
+
745
+ if (err) {
746
+ winston.error('RequestService error for method createWithIdAndRequester for newRequest' + JSON.stringify(newRequest), err);
747
+ return reject(err);
748
+ }
749
+ winston.debug("Request created", savedRequest.toObject());
750
+
751
+ requestEvent.emit('request.create.simple', savedRequest);
752
+
753
+ if (isStandardConversation) {
754
+ requestEvent.emit('request.create.quote', payload);;
755
+ }
756
+
757
+ return resolve(savedRequest);
758
+
759
+ });
760
+ })
761
+ }
762
+
763
+ async _create(request) {
764
+
472
765
  var startDate = new Date();
473
766
 
474
767
  if (!request.createdAt) {
@@ -512,6 +805,7 @@ class RequestService {
512
805
  var followers = request.followers;
513
806
  let createdAt = request.createdAt;
514
807
 
808
+
515
809
  if (!departmentid) {
516
810
  departmentid = 'default';
517
811
  }
@@ -560,11 +854,12 @@ class RequestService {
560
854
  isVoiceConversation = true;
561
855
  }
562
856
  else {
563
- let available = await qm.checkQuote(p, request, 'requests');
564
- if (available === false) {
565
- winston.info("Requests limits reached for project " + p._id)
566
- return false;
567
- }
857
+ // console.log("! check quota moved")
858
+ // let available = await qm.checkQuote(p, request, 'requests');
859
+ // if (available === false) {
860
+ // winston.info("Requests limits reached for project " + p._id)
861
+ // return false;
862
+ // }
568
863
  }
569
864
 
570
865
 
@@ -626,6 +921,18 @@ class RequestService {
626
921
 
627
922
  status = RequestConstants.ASSIGNED;
628
923
 
924
+ /**
925
+ * QUOTAS - START!!!
926
+ */
927
+ if (!isTestConversation && !isVoiceConversation) {
928
+ requestEvent.emit('request.create.quote', payload);
929
+ }
930
+ /**
931
+ * QUOTAS - END!!!
932
+ */
933
+
934
+
935
+
629
936
  // botprefix
630
937
  if (participants[0].startsWith("bot_")) {
631
938
 
@@ -736,7 +1043,7 @@ class RequestService {
736
1043
  requestEvent.emit('request.create.simple', savedRequest);
737
1044
 
738
1045
  if (!isTestConversation && !isVoiceConversation) {
739
- requestEvent.emit('request.create.quote', payload);;
1046
+ // requestEvent.emit('request.create.quote', payload);;
740
1047
  }
741
1048
 
742
1049
  return resolve(savedRequest);
@@ -752,7 +1059,7 @@ class RequestService {
752
1059
  }
753
1060
 
754
1061
 
755
- async _create(request) {
1062
+ async __create(request) {
756
1063
 
757
1064
  var startDate = new Date();
758
1065
 
@@ -2505,6 +2812,34 @@ class RequestService {
2505
2812
 
2506
2813
  }
2507
2814
 
2815
+ async getConversationsCount(id_project, status, preflight, hasBot, startDate, endDate) {
2816
+ return new Promise( async (resolve, reject) => {
2817
+ let query = { id_project: id_project, status: status, preflight: preflight};
2818
+ if (hasBot != null) {
2819
+ query.hasBot = hasBot;
2820
+ }
2821
+ if (status === 201) {
2822
+ query.status = {
2823
+ $in: [100,200]
2824
+ }
2825
+ }
2826
+ if (preflight === null) {
2827
+ delete query.preflight;
2828
+ }
2829
+ if (startDate && endDate) {
2830
+ query.createdAt = { $gte: startDate.toDate(), $lte: endDate.toDate() }
2831
+ }
2832
+ winston.debug("getConversationsCount query: ", query)
2833
+ let count = await Request.countDocuments(query).catch((err) => {
2834
+ winston.error("Error getting requests count: ", err);
2835
+ reject(err);
2836
+ })
2837
+ winston.verbose("Requests found for query " + JSON.stringify(query) + ": " + count);
2838
+ resolve(count)
2839
+ })
2840
+ }
2841
+
2842
+
2508
2843
  }
2509
2844
 
2510
2845
 
@@ -167,7 +167,7 @@
167
167
  style=" background-color: #ff8574 !important; border: none; color: white; padding: 6px 18px; text-align: center; text-decoration: none; display: inline-block; font-size: 14px; font-weight: 600; letter-spacing: 1px; margin: 4px 2px; cursor: pointer; border-radius: 8px;">
168
168
  Enjoy Tiledesk
169
169
  </a> -->
170
- <a href="https://panel.tiledesk.com/v3/cds/#/project/{{project_id}}/chatbot/{{chatbot_id}}/intent/0?jwt={{token}}"
170
+ <a href="{{redirect_url}}"
171
171
  style=" background-color: #ff8574 !important; border: none; color: white; padding: 6px 18px; text-align: center; text-decoration: none; display: inline-block; font-size: 14px; font-weight: 600; letter-spacing: 1px; margin: 4px 2px; cursor: pointer; border-radius: 8px;">
172
172
  Enjoy Tiledesk
173
173
  </a>
@@ -184,7 +184,7 @@
184
184
  {{baseScope.baseUrl}}/#/project/{{project_id}}/home
185
185
  </a> -->
186
186
  <a
187
- href="https://panel.tiledesk.com/v3/cds/#/project/{{project_id}}/chatbot/{{chatbot_id}}/intent/0?jwt={{token}}">
187
+ href="{{redirect_url}}">
188
188
  {{baseScope.baseUrl}}/#/project/{{project_id}}/home
189
189
  </a>
190
190
  </div>
@@ -68,6 +68,7 @@ const mockProjectBasicPlan = {
68
68
  "name": "mock-project",
69
69
  "activeOperatingHours": false,
70
70
  "createdBy": "64e36f5cbf72263f7c05ba36",
71
+ "isActiveSubscription": true,
71
72
  "profile": {
72
73
  "name": "Basic",
73
74
  "trialDays": 14,
@@ -110,6 +111,33 @@ const mockProjectPremiumPlan = {
110
111
  "createdAt": new Date('2023-10-16T08:45:54.058Z')
111
112
  }
112
113
 
114
+ const mockProjectPremiumPlan2 = {
115
+ "_id": "64e36f5dbf72263f7c059999",
116
+ "status": 100,
117
+ "ipFilterEnabled": false,
118
+ "ipFilter": [],
119
+ "ipFilterDenyEnabled": false,
120
+ "ipFilterDeny": [],
121
+ "name": "mock-project",
122
+ "activeOperatingHours": false,
123
+ "createdBy": "64e36f5cbf72263f7c05ba36",
124
+ "isActiveSubscription": true,
125
+ "profile": {
126
+ "name": "Premium",
127
+ "trialDays": 14,
128
+ "agents": 0,
129
+ "type": "payment",
130
+ "subStart": new Date('2024-01-31T10:00:00.058Z')
131
+ },
132
+ "versions": 20115,
133
+ "channels": [
134
+ {
135
+ "name": "chat21"
136
+ }
137
+ ],
138
+ "createdAt": new Date('2024-01-20T10:00:00.058Z')
139
+ }
140
+
113
141
  const mockProjectCustomPlan = {
114
142
  "_id": "64e36f5dbf72263f7c059999",
115
143
  "status": 100,
@@ -167,4 +195,4 @@ const mockOldProjecPlusPlan = {
167
195
  "ipFilterDenyEnabled": false
168
196
  }
169
197
 
170
- module.exports = { mockProjectUser, mockProjectFreeTrialPlan, mockProjectSandboxPlan, mockProjectBasicPlan, mockProjectPremiumPlan, mockProjectCustomPlan, mockOldProjecPlusPlan };
198
+ module.exports = { mockProjectUser, mockProjectFreeTrialPlan, mockProjectSandboxPlan, mockProjectBasicPlan, mockProjectPremiumPlan, mockProjectPremiumPlan2, mockProjectCustomPlan, mockOldProjecPlusPlan };
@@ -22,12 +22,13 @@ var Group = require('../models/group');
22
22
  var expect = chai.expect;
23
23
  var assert = chai.assert;
24
24
 
25
-
25
+ let operatingHours = '{"1":[{"start":"09:00","end":"13:00"},{"start":"14:00","end":"18:00"}],"2":[{"start":"09:00","end":"13:00"},{"start":"14:00","end":"18:00"}],"3":[{"start":"09:00","end":"11:00"},{"start":"14:00","end":"18:00"}],"4":[{"start":"09:00","end":"13:00"},{"start":"14:00","end":"18:00"}],"5":[{"start":"09:00","end":"13:00"},{"start":"14:00","end":"18:00"}],"tzname":"Europe/Rome"}';
26
26
  let timeSlotsSample = {
27
27
  "819559cc": {
28
28
  name: "Slot1",
29
29
  active: true,
30
- hours: "{\"1\":[{\"start\":\"09:00\",\"end\":\"13:00\"},{\"start\":\"14:00\",\"end\":\"18:00\"}],\"3\":[{\"start\":\"09:00\",\"end\":\"13:00\"},{\"start\":\"14:00\",\"end\":\"18:00\"}],\"5\":[{\"start\":\"09:00\",\"end\":\"13:00\"},{\"start\":\"14:00\",\"end\":\"18:00\"}],\"tzname\":\"Europe/Rome\"}"
30
+ //hours: "{\"1\":[{\"start\":\"09:00\",\"end\":\"13:00\"},{\"start\":\"14:00\",\"end\":\"18:00\"}],\"3\":[{\"start\":\"09:00\",\"end\":\"13:00\"},{\"start\":\"14:00\",\"end\":\"18:00\"}],\"5\":[{\"start\":\"09:00\",\"end\":\"13:00\"},{\"start\":\"14:00\",\"end\":\"18:00\"}],\"tzname\":\"America/Los_Angeles\"}"
31
+ hours: "{\"1\":[{\"start\":\"09:00\",\"end\":\"13:00\"},{\"start\":\"14:00\",\"end\":\"18:00\"}],\"3\":[{\"start\":\"09:00\",\"end\":\"15:00\"},{\"start\":\"17:00\",\"end\":\"18:00\"}],\"5\":[{\"start\":\"09:00\",\"end\":\"13:00\"},{\"start\":\"14:00\",\"end\":\"18:00\"}],\"tzname\":\"Europe/Rome\"}"
31
32
  },
32
33
  "5d4368de": {
33
34
  name: "Slot2",
@@ -115,6 +116,9 @@ describe('ProjectRoute', () => {
115
116
  })
116
117
  }).timeout(10000)
117
118
 
119
+
120
+
121
+
118
122
  it('updateProjectTimeSlots', (done) => {
119
123
 
120
124
  var email = "test-signup-" + Date.now() + "@email.com";
@@ -180,8 +184,47 @@ describe('ProjectRoute', () => {
180
184
  })
181
185
  }).timeout(10000)
182
186
 
183
- it('availableUsers', (done) => {
187
+ it('isOpenOperatingHours', (done) => {
188
+
189
+ var email = "test-signup-" + Date.now() + "@email.com";
190
+ var pwd = "pwd";
191
+
192
+ userService.signup(email, pwd, "Test Firstname", "Test Lastname").then((savedUser) => {
193
+ projectService.create("test-project-create", savedUser._id).then((savedProject) => {
194
+
195
+ chai.request(server)
196
+ // .put('/projects/' + savedProject._id + "/update")
197
+ .put('/projects/' + savedProject._id)
198
+ .auth(email, pwd)
199
+ .send({ activeOperatingHours: true, operatingHours: operatingHours })
200
+ .end((err, res) => {
201
+
202
+ if (log) { console.log("update project time slots res.body: ", res.body) };
203
+ res.should.have.status(200);
204
+ res.body.should.be.a('object');
205
+
206
+ chai.request(server)
207
+ .get('/projects/' + savedProject._id + '/isopen')
208
+ .auth(email, pwd)
209
+ .end((err, res) => {
210
+
211
+ if (err) { console.error("err: ", err) };
212
+ if (log) { console.log("res.body isopen: ", res.body) };
213
+
214
+ // Unable to do other checks due to currentTime change.
215
+ res.should.have.status(200);
216
+
217
+ done();
218
+
219
+ })
220
+ })
221
+
222
+
223
+ })
224
+ })
225
+ }).timeout(10000)
184
226
 
227
+ it('availableUsers', (done) => {
185
228
  var email = "test-signup-" + Date.now() + "@email.com";
186
229
  var pwd = "pwd";
187
230
 
@@ -198,6 +241,46 @@ describe('ProjectRoute', () => {
198
241
 
199
242
  done();
200
243
  })
244
+ })
245
+ })
246
+ })
247
+
248
+ it('utcChecker', (done) => {
249
+
250
+ var email = "test-signup-" + Date.now() + "@email.com";
251
+ var pwd = "pwd";
252
+
253
+ userService.signup(email, pwd, "Test Firstname", "Test Lastname").then((savedUser) => {
254
+ projectService.create("test-project-create", savedUser._id).then((savedProject) => {
255
+
256
+ chai.request(server)
257
+ // .put('/projects/' + savedProject._id + "/update")
258
+ .put('/projects/' + savedProject._id)
259
+ .auth(email, pwd)
260
+ .send({ timeSlots: timeSlotsSample })
261
+ .end((err, res) => {
262
+
263
+ if (log) { console.log("update project time slots res.body: ", res.body) };
264
+ res.should.have.status(200);
265
+ res.body.should.be.a('object');
266
+
267
+ chai.request(server)
268
+ .get('/projects/' + savedProject._id + '/isopen?timeSlot=819559cc')
269
+ .auth(email, pwd)
270
+ .end((err, res) => {
271
+
272
+ if (err) { console.error("err: ", err) };
273
+ if (log) { console.log("res.body isopen: ", res.body) };
274
+
275
+ // Unable to do other checks due to currentTime change.
276
+ res.should.have.status(200);
277
+
278
+ done();
279
+
280
+ })
281
+ })
282
+
283
+
201
284
  })
202
285
  })
203
286
  }).timeout(10000)