@oneuptime/common 8.0.5161 → 8.0.5167

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -756,9 +756,14 @@ export default class SlackUtil extends WorkspaceBase {
756
756
  throw new BadRequestException("Error from Slack " + messageFromSlack);
757
757
  }
758
758
 
759
+ logger.debug("Searching for "+normalizedChannelName);
760
+ logger.debug("Searching channels in current page...");
761
+ logger.debug((response.jsonData as JSONObject)["channels"]);
762
+
759
763
  for (const channel of (response.jsonData as JSONObject)[
760
764
  "channels"
761
765
  ] as Array<JSONObject>) {
766
+
762
767
  if (!channel["id"] || !channel["name"]) {
763
768
  continue;
764
769
  }
@@ -0,0 +1,479 @@
1
+ import LayerUtil, { LayerProps } from "../../../Types/OnCallDutyPolicy/Layer";
2
+ import CalendarEvent from "../../../Types/Calendar/CalendarEvent";
3
+ import RestrictionTimes, {
4
+ RestrictionType,
5
+ WeeklyResctriction,
6
+ } from "../../../Types/OnCallDutyPolicy/RestrictionTimes";
7
+ import Recurring from "../../../Types/Events/Recurring";
8
+ import OneUptimeDate from "../../../Types/Date";
9
+ import User from "../../../Models/DatabaseModels/User";
10
+ import EventInterval from "../../../Types/Events/EventInterval";
11
+ import DayOfWeek, { DayOfWeekUtil } from "../../../Types/Day/DayOfWeek";
12
+
13
+ // Helper to create a user model with id only.
14
+ function user(id: string): User {
15
+ return {
16
+ id: {
17
+ toString: () => {
18
+ return id;
19
+ },
20
+ } as any,
21
+ } as User;
22
+ }
23
+
24
+ function buildLayerProps(data: {
25
+ users: string[];
26
+ start: Date;
27
+ handoff: Date;
28
+ restriction?: { type: RestrictionType; start?: string; end?: string };
29
+ weeklyRestrictions?: Array<{
30
+ startDay: DayOfWeek;
31
+ endDay: DayOfWeek;
32
+ start: string; // HH:mm
33
+ end: string; // HH:mm
34
+ }>;
35
+ rotation?: { intervalType: EventInterval; intervalCount: number };
36
+ }): LayerProps {
37
+ const restrictionTimes: RestrictionTimes = new RestrictionTimes();
38
+
39
+ if (data.restriction) {
40
+ restrictionTimes.restictionType = data.restriction.type;
41
+ if (
42
+ data.restriction.type === RestrictionType.Daily &&
43
+ data.restriction.start &&
44
+ data.restriction.end
45
+ ) {
46
+ restrictionTimes.dayRestrictionTimes = {
47
+ startTime: OneUptimeDate.getDateWithCustomTime({
48
+ hours: parseInt(data.restriction.start.split(":")[0] || "0"),
49
+ minutes: parseInt(data.restriction.start.split(":")[1] || "0"),
50
+ seconds: 0,
51
+ }),
52
+ endTime: OneUptimeDate.getDateWithCustomTime({
53
+ hours: parseInt(data.restriction.end.split(":")[0] || "0"),
54
+ minutes: parseInt(data.restriction.end.split(":")[1] || "0"),
55
+ seconds: 0,
56
+ }),
57
+ };
58
+ }
59
+ } else if (data.weeklyRestrictions && data.weeklyRestrictions.length > 0) {
60
+ restrictionTimes.restictionType = RestrictionType.Weekly;
61
+ const weekly: Array<WeeklyResctriction> = [];
62
+ // Base week anchor (start of week for provided start date)
63
+ const baseWeekStart: Date = OneUptimeDate.getStartOfTheWeek(data.start);
64
+ const baseWeekDay: DayOfWeek = OneUptimeDate.getDayOfWeek(baseWeekStart);
65
+ const baseWeekDayNumber: number =
66
+ DayOfWeekUtil.getNumberOfDayOfWeek(baseWeekDay);
67
+
68
+ for (const r of data.weeklyRestrictions) {
69
+ const desiredStartDayNum: number = DayOfWeekUtil.getNumberOfDayOfWeek(
70
+ r.startDay,
71
+ );
72
+ const desiredEndDayNum: number = DayOfWeekUtil.getNumberOfDayOfWeek(
73
+ r.endDay,
74
+ );
75
+
76
+ const startOffsetDays: number = desiredStartDayNum - baseWeekDayNumber;
77
+ const endOffsetDays: number = desiredEndDayNum - baseWeekDayNumber;
78
+
79
+ const startDate: Date = OneUptimeDate.addRemoveDays(
80
+ baseWeekStart,
81
+ startOffsetDays,
82
+ );
83
+ const endDate: Date = OneUptimeDate.addRemoveDays(
84
+ baseWeekStart,
85
+ endOffsetDays,
86
+ );
87
+
88
+ const startTime: Date = OneUptimeDate.keepTimeButMoveDay(
89
+ OneUptimeDate.getDateWithCustomTime({
90
+ hours: parseInt(r.start.split(":")[0] || "0"),
91
+ minutes: parseInt(r.start.split(":")[1] || "0"),
92
+ seconds: 0,
93
+ }),
94
+ startDate,
95
+ );
96
+
97
+ const endTime: Date = OneUptimeDate.keepTimeButMoveDay(
98
+ OneUptimeDate.getDateWithCustomTime({
99
+ hours: parseInt(r.end.split(":")[0] || "0"),
100
+ minutes: parseInt(r.end.split(":")[1] || "0"),
101
+ seconds: 0,
102
+ }),
103
+ endDate,
104
+ );
105
+
106
+ weekly.push({
107
+ startDay: r.startDay,
108
+ endDay: r.endDay,
109
+ startTime,
110
+ endTime,
111
+ });
112
+ }
113
+
114
+ restrictionTimes.weeklyRestrictionTimes = weekly;
115
+ } else {
116
+ restrictionTimes.restictionType = RestrictionType.None;
117
+ restrictionTimes.dayRestrictionTimes = null;
118
+ }
119
+
120
+ const rotation: Recurring = data.rotation
121
+ ? Recurring.fromJSON({
122
+ _type: "Recurring",
123
+ value: {
124
+ intervalType: data.rotation.intervalType,
125
+ intervalCount: {
126
+ _type: "PositiveNumber",
127
+ value: data.rotation.intervalCount,
128
+ },
129
+ },
130
+ } as any)
131
+ : Recurring.fromJSON({
132
+ _type: "Recurring",
133
+ value: {
134
+ intervalType: EventInterval.Day,
135
+ intervalCount: { _type: "PositiveNumber", value: 1 },
136
+ },
137
+ } as any);
138
+
139
+ return {
140
+ users: data.users.map(user),
141
+ startDateTimeOfLayer: data.start,
142
+ handOffTime: data.handoff,
143
+ restrictionTimes,
144
+ rotation,
145
+ };
146
+ }
147
+
148
+ describe("LayerUtil getEvents - Daily Restrictions", () => {
149
+ test("Should return full-day events when no restriction", () => {
150
+ const util: LayerUtil = new LayerUtil();
151
+ const start: Date = OneUptimeDate.getStartOfDay(new Date());
152
+ const end: Date = OneUptimeDate.addRemoveDays(start, 1); // one day calendar
153
+
154
+ const layer: LayerProps = buildLayerProps({
155
+ users: ["u1"],
156
+ start: start,
157
+ handoff: OneUptimeDate.addRemoveDays(start, 10),
158
+ });
159
+
160
+ const events: Array<CalendarEvent> = util.getEvents({
161
+ ...layer,
162
+ calendarStartDate: start,
163
+ calendarEndDate: end,
164
+ });
165
+
166
+ expect(events.length).toBe(1);
167
+ const only: CalendarEvent = events[0]!;
168
+ expect(only.start.getTime()).toBe(start.getTime());
169
+ expect(only.end.getTime()).toBe(end.getTime());
170
+ });
171
+
172
+ test("Should trim to same-day restriction window (11:00-23:00)", () => {
173
+ const util: LayerUtil = new LayerUtil();
174
+ const start: Date = OneUptimeDate.getStartOfDay(new Date());
175
+ const calendarEnd: Date = OneUptimeDate.addRemoveDays(start, 1);
176
+
177
+ const layer: LayerProps = buildLayerProps({
178
+ users: ["u1"],
179
+ start: start,
180
+ handoff: OneUptimeDate.addRemoveDays(start, 2),
181
+ restriction: {
182
+ type: RestrictionType.Daily,
183
+ start: "11:00",
184
+ end: "23:00",
185
+ },
186
+ });
187
+
188
+ const events: Array<CalendarEvent> = util.getEvents({
189
+ ...layer,
190
+ calendarStartDate: start,
191
+ calendarEndDate: calendarEnd,
192
+ });
193
+
194
+ expect(events.length).toBe(1);
195
+ const ev: CalendarEvent = events[0]!;
196
+ expect(OneUptimeDate.getLocalHourAndMinuteFromDate(ev.start)).toBe("11:00");
197
+ expect(OneUptimeDate.getLocalHourAndMinuteFromDate(ev.end)).toBe("23:00");
198
+ });
199
+
200
+ test("Should produce two segments for overnight window (23:00-11:00 next day)", () => {
201
+ const util: LayerUtil = new LayerUtil();
202
+ const todayStart: Date = OneUptimeDate.getStartOfDay(new Date());
203
+ // Extend calendar to cover next day morning (till at least 12:00) so both segments can appear.
204
+ const calendarEnd: Date = OneUptimeDate.addRemoveHours(todayStart, 36); // 24h + 12h
205
+
206
+ const layer: LayerProps = buildLayerProps({
207
+ users: ["u1"],
208
+ start: todayStart,
209
+ handoff: OneUptimeDate.addRemoveDays(todayStart, 2),
210
+ restriction: {
211
+ type: RestrictionType.Daily,
212
+ start: "23:00",
213
+ end: "11:00",
214
+ },
215
+ });
216
+
217
+ const events: Array<CalendarEvent> = util.getEvents({
218
+ ...layer,
219
+ calendarStartDate: todayStart,
220
+ calendarEndDate: calendarEnd,
221
+ });
222
+
223
+ // Expect two events: 23:00 -> 23:59:59 (approx) and 00:00 -> 11:00 next day (depending on trimming logic)
224
+ // We simplify by checking presence of one starting at 23:00 and one ending at 11:00.
225
+ expect(events.length).toBeGreaterThanOrEqual(2); // Expect at least two distinct segments across midnight.
226
+ const has23Window: boolean = events.some((e: CalendarEvent) => {
227
+ return OneUptimeDate.getLocalHourAndMinuteFromDate(e.start) === "23:00";
228
+ });
229
+ // End might be 10:59 or 11:00 depending on second trimming; allow both 10 or 11 hour boundary.
230
+ const hasMorningCoverage: boolean = events.some((e: CalendarEvent) => {
231
+ const startHM: string = OneUptimeDate.getLocalHourAndMinuteFromDate(
232
+ e.start,
233
+ );
234
+ const endHM: string = OneUptimeDate.getLocalHourAndMinuteFromDate(e.end);
235
+ // Morning segment should end at or near 11:00 and start at or near 00:00
236
+ return (
237
+ (startHM === "00:00" || startHM === "00:01" || startHM === "23:59") &&
238
+ (endHM === "11:00" || endHM === "10:59" || endHM === "10:58")
239
+ );
240
+ });
241
+
242
+ expect(has23Window).toBeTruthy();
243
+ expect(hasMorningCoverage).toBeTruthy();
244
+ });
245
+ });
246
+
247
+ describe("LayerUtil getEvents - Multi-day Daily Windows", () => {
248
+ test("Daily restriction (09:00-17:00) over 3 day calendar produces one window per day", () => {
249
+ const util: LayerUtil = new LayerUtil();
250
+ const day1: Date = OneUptimeDate.getStartOfDay(new Date());
251
+ const calendarEnd: Date = OneUptimeDate.addRemoveDays(day1, 3); // 3 days window
252
+
253
+ const layer: LayerProps = buildLayerProps({
254
+ users: ["u1"],
255
+ start: day1,
256
+ handoff: OneUptimeDate.addRemoveDays(day1, 1), // initial handoff end of day1
257
+ restriction: {
258
+ type: RestrictionType.Daily,
259
+ start: "09:00",
260
+ end: "17:00",
261
+ },
262
+ rotation: { intervalType: EventInterval.Day, intervalCount: 1 },
263
+ });
264
+
265
+ const events: Array<CalendarEvent> = util.getEvents({
266
+ ...layer,
267
+ calendarStartDate: day1,
268
+ calendarEndDate: calendarEnd,
269
+ });
270
+
271
+ const windowsStartingAtNine: Array<CalendarEvent> = events.filter(
272
+ (e: CalendarEvent) => {
273
+ return OneUptimeDate.getLocalHourAndMinuteFromDate(e.start) === "09:00";
274
+ },
275
+ );
276
+ expect(windowsStartingAtNine.length).toBeGreaterThanOrEqual(2);
277
+ });
278
+ });
279
+
280
+ describe("LayerUtil getEvents - Weekly Restrictions", () => {
281
+ test("Simple weekly window Monday 09:00 to Wednesday 17:00 yields trimmed events", () => {
282
+ const util: LayerUtil = new LayerUtil();
283
+ const monday: Date = OneUptimeDate.getStartOfTheWeek(new Date());
284
+ const calendarEnd: Date = OneUptimeDate.addRemoveDays(monday, 7);
285
+
286
+ const layer: LayerProps = buildLayerProps({
287
+ users: ["u1"],
288
+ start: monday,
289
+ handoff: OneUptimeDate.addRemoveWeeks(monday, 1),
290
+ weeklyRestrictions: [
291
+ {
292
+ startDay: DayOfWeek.Monday,
293
+ endDay: DayOfWeek.Wednesday,
294
+ start: "09:00",
295
+ end: "17:00",
296
+ },
297
+ ],
298
+ rotation: { intervalType: EventInterval.Week, intervalCount: 1 },
299
+ });
300
+
301
+ const events: Array<CalendarEvent> = util.getEvents({
302
+ ...layer,
303
+ calendarStartDate: monday,
304
+ calendarEndDate: calendarEnd,
305
+ });
306
+
307
+ const hasStartNine: boolean = events.some((e: CalendarEvent) => {
308
+ return OneUptimeDate.getLocalHourAndMinuteFromDate(e.start) === "09:00";
309
+ });
310
+ const hasEndSeventeen: boolean = events.some((e: CalendarEvent) => {
311
+ return OneUptimeDate.getLocalHourAndMinuteFromDate(e.end) === "17:00";
312
+ });
313
+ expect(hasStartNine).toBeTruthy();
314
+ expect(hasEndSeventeen).toBeTruthy();
315
+ });
316
+
317
+ test("Weekly wrap-around Friday 22:00 to Monday 06:00 produces appropriate segments", () => {
318
+ const util: LayerUtil = new LayerUtil();
319
+ const monday: Date = OneUptimeDate.getStartOfTheWeek(new Date());
320
+ const calendarEnd: Date = OneUptimeDate.addRemoveDays(monday, 7);
321
+
322
+ const layer: LayerProps = buildLayerProps({
323
+ users: ["u1"],
324
+ start: monday,
325
+ handoff: OneUptimeDate.addRemoveWeeks(monday, 1),
326
+ weeklyRestrictions: [
327
+ {
328
+ startDay: DayOfWeek.Friday,
329
+ endDay: DayOfWeek.Monday,
330
+ start: "22:00",
331
+ end: "06:00",
332
+ },
333
+ ],
334
+ rotation: { intervalType: EventInterval.Week, intervalCount: 1 },
335
+ });
336
+
337
+ const events: Array<CalendarEvent> = util.getEvents({
338
+ ...layer,
339
+ calendarStartDate: monday,
340
+ calendarEndDate: calendarEnd,
341
+ });
342
+
343
+ const has22: boolean = events.some((e: CalendarEvent) => {
344
+ return OneUptimeDate.getLocalHourAndMinuteFromDate(e.start) === "22:00";
345
+ });
346
+ const has06: boolean = events.some((e: CalendarEvent) => {
347
+ return OneUptimeDate.getLocalHourAndMinuteFromDate(e.end) === "06:00";
348
+ });
349
+ expect(has22).toBeTruthy();
350
+ expect(has06).toBeTruthy();
351
+ });
352
+ });
353
+
354
+ describe("LayerUtil getEvents - Daily Rotation Across Users", () => {
355
+ test("Daily rotation cycles users", () => {
356
+ const util: LayerUtil = new LayerUtil();
357
+ const day1: Date = OneUptimeDate.getStartOfDay(new Date());
358
+ const calendarEnd: Date = OneUptimeDate.addRemoveDays(day1, 3); // 3 days
359
+
360
+ const layer: LayerProps = buildLayerProps({
361
+ users: ["a", "b"],
362
+ start: day1,
363
+ handoff: OneUptimeDate.addRemoveDays(day1, 1),
364
+ rotation: { intervalType: EventInterval.Day, intervalCount: 1 },
365
+ });
366
+
367
+ const events: Array<CalendarEvent> = util.getEvents({
368
+ ...layer,
369
+ calendarStartDate: day1,
370
+ calendarEndDate: calendarEnd,
371
+ });
372
+
373
+ if (events.length >= 2) {
374
+ expect(events[0]!.title).not.toBe(events[1]!.title);
375
+ }
376
+ });
377
+ });
378
+
379
+ describe("LayerUtil getMultiLayerEvents - Partial Overlap Trimming", () => {
380
+ test("Primary layer inside backup trims backup", () => {
381
+ const util: LayerUtil = new LayerUtil();
382
+ const start: Date = OneUptimeDate.getStartOfDay(new Date());
383
+ const calendarEnd: Date = OneUptimeDate.addRemoveHours(start, 6);
384
+
385
+ const primary: LayerProps = buildLayerProps({
386
+ users: ["primary"],
387
+ start: OneUptimeDate.addRemoveHours(start, 2),
388
+ handoff: OneUptimeDate.addRemoveHours(start, 4),
389
+ });
390
+
391
+ const backup: LayerProps = buildLayerProps({
392
+ users: ["backup"],
393
+ start: start,
394
+ handoff: OneUptimeDate.addRemoveHours(start, 6),
395
+ });
396
+
397
+ const events: Array<CalendarEvent> = util.getMultiLayerEvents({
398
+ layers: [primary, backup],
399
+ calendarStartDate: start,
400
+ calendarEndDate: calendarEnd,
401
+ });
402
+
403
+ const containsPrimary: boolean = events.some((e: CalendarEvent) => {
404
+ return e.title === "primary";
405
+ });
406
+ const containsBackup: boolean = events.some((e: CalendarEvent) => {
407
+ return e.title === "backup";
408
+ });
409
+ expect(containsPrimary).toBeTruthy();
410
+ expect(containsBackup).toBeTruthy();
411
+ });
412
+ });
413
+
414
+ describe("LayerUtil getEvents - Rotation Handoff", () => {
415
+ test("Hourly rotation changes user after each hour", () => {
416
+ const util: LayerUtil = new LayerUtil();
417
+ const start: Date = OneUptimeDate.getStartOfDay(new Date());
418
+ const calendarEnd: Date = OneUptimeDate.addRemoveHours(start, 5); // 5 hours window
419
+
420
+ const layer: LayerProps = buildLayerProps({
421
+ users: ["u1", "u2", "u3"],
422
+ start: start,
423
+ handoff: OneUptimeDate.addRemoveHours(start, 1), // first handoff at +1h
424
+ rotation: { intervalType: EventInterval.Hour, intervalCount: 1 },
425
+ });
426
+
427
+ const events: Array<CalendarEvent> = util.getEvents({
428
+ ...layer,
429
+ calendarStartDate: start,
430
+ calendarEndDate: calendarEnd,
431
+ });
432
+
433
+ // Expect roughly 5 events (one per hour) and user IDs rotate in sequence.
434
+ expect(events.length).toBeGreaterThanOrEqual(4);
435
+ const userSequence: Array<string> = events.map((e: CalendarEvent) => {
436
+ return e.title;
437
+ });
438
+
439
+ // Titles are user ids (strings we passed) according to implementation.
440
+ // Check that at least first three rotate u1 -> u2 -> u3
441
+ expect(userSequence.slice(0, 3)).toEqual(["u1", "u2", "u3"]);
442
+ });
443
+ });
444
+
445
+ describe("LayerUtil getMultiLayerEvents - Overlap Priority", () => {
446
+ test("Higher priority (lower index) layer should trim overlapping lower priority events", () => {
447
+ const util: LayerUtil = new LayerUtil();
448
+ const start: Date = OneUptimeDate.getStartOfDay(new Date());
449
+ const calendarEnd: Date = OneUptimeDate.addRemoveHours(start, 6);
450
+
451
+ const layer1: LayerProps = buildLayerProps({
452
+ users: ["primary"],
453
+ start: start,
454
+ handoff: OneUptimeDate.addRemoveHours(start, 6),
455
+ });
456
+
457
+ const layer2: LayerProps = buildLayerProps({
458
+ users: ["backup"],
459
+ start: start,
460
+ handoff: OneUptimeDate.addRemoveHours(start, 6),
461
+ });
462
+
463
+ const events: Array<CalendarEvent> = util.getMultiLayerEvents({
464
+ layers: [layer1, layer2],
465
+ calendarStartDate: start,
466
+ calendarEndDate: calendarEnd,
467
+ });
468
+
469
+ // All events should belong to primary (priority 1) with no backup overlapping intervals left.
470
+ const titles: Array<string> = events.map((e: CalendarEvent) => {
471
+ return e.title;
472
+ });
473
+ expect(
474
+ titles.every((t: string) => {
475
+ return t === "primary";
476
+ }),
477
+ ).toBeTruthy();
478
+ });
479
+ });
@@ -718,6 +718,79 @@ export default class LayerUtil {
718
718
  let restrictionStartTime: Date = dayRestrictionTimes.startTime;
719
719
  let restrictionEndTime: Date = dayRestrictionTimes.endTime;
720
720
 
721
+ // Special Case: Overnight (wrap-around) window where end time is logically on the next day.
722
+ // Example: 23:00 -> 11:00 (next day). Existing algorithm assumed end >= start within same day
723
+ // and returned no events. We explicitly expand such windows into two segments per day:
724
+ // 1) start -> endOfDay(start)
725
+ // 2) startOfNextDay -> end (moved to next day)
726
+ if (OneUptimeDate.isBefore(restrictionEndTime, restrictionStartTime)) {
727
+ const results: Array<StartAndEndTime> = [];
728
+
729
+ // We'll iterate day-by-day within the event range (max 31 iterations safeguard)
730
+ let currentDayStart: Date = OneUptimeDate.getStartOfDay(
731
+ data.eventStartTime,
732
+ );
733
+ const absoluteEventEnd: Date = data.eventEndTime;
734
+ let safetyCounter: number = 0;
735
+ const maxDays: number = 62; // generous safeguard
736
+
737
+ while (
738
+ OneUptimeDate.isOnOrBefore(currentDayStart, absoluteEventEnd) &&
739
+ safetyCounter < maxDays
740
+ ) {
741
+ safetyCounter++;
742
+
743
+ const segmentNightStart: Date = OneUptimeDate.keepTimeButMoveDay(
744
+ restrictionStartTime,
745
+ currentDayStart,
746
+ );
747
+ const segmentNightEnd: Date =
748
+ OneUptimeDate.getEndOfDay(segmentNightStart);
749
+
750
+ const nextDayStart: Date = OneUptimeDate.addRemoveDays(
751
+ currentDayStart,
752
+ 1,
753
+ );
754
+ const segmentMorningStart: Date =
755
+ OneUptimeDate.getStartOfDay(nextDayStart);
756
+ const segmentMorningEnd: Date = OneUptimeDate.keepTimeButMoveDay(
757
+ restrictionEndTime,
758
+ nextDayStart,
759
+ );
760
+
761
+ // helper to add intersection if it overlaps the event window
762
+ const addIntersection: (segStart: Date, segEnd: Date) => void = (
763
+ segStart: Date,
764
+ segEnd: Date,
765
+ ): void => {
766
+ // normalize zero / invalid lengths
767
+ if (OneUptimeDate.isOnOrBefore(segEnd, segStart)) {
768
+ return; // no length
769
+ }
770
+ // intersect with [eventStart, eventEnd]
771
+ const start: Date = OneUptimeDate.getGreaterDate(
772
+ segStart,
773
+ data.eventStartTime,
774
+ );
775
+ const end: Date = OneUptimeDate.getLesserDate(
776
+ segEnd,
777
+ data.eventEndTime,
778
+ );
779
+ if (OneUptimeDate.isAfter(end, start)) {
780
+ results.push({ startTime: start, endTime: end });
781
+ }
782
+ };
783
+
784
+ addIntersection(segmentNightStart, segmentNightEnd);
785
+ addIntersection(segmentMorningStart, segmentMorningEnd);
786
+
787
+ // advance a day
788
+ currentDayStart = nextDayStart;
789
+ }
790
+
791
+ return results;
792
+ }
793
+
721
794
  let currentStartTime: Date = data.eventStartTime;
722
795
  const currentEndTime: Date = data.eventEndTime;
723
796
 
@@ -83,7 +83,9 @@ const JSONTable: FunctionComponent<JSONTableProps> = (
83
83
  if (!groupMap[prefix]) {
84
84
  groupMap[prefix] = [];
85
85
  }
86
- groupMap[prefix].push({ index, value: working[key] });
86
+ // At this point groupMap[prefix] is initialized. Use a local ref so TS can narrow.
87
+ const entries: Array<GroupEntry> = groupMap[prefix]!;
88
+ entries.push({ index, value: working[key] });
87
89
  keysToRemove.add(key);
88
90
  }
89
91
 
@@ -497,6 +497,9 @@ export default class SlackUtil extends WorkspaceBase {
497
497
  const messageFromSlack = (_b = response.jsonData) === null || _b === void 0 ? void 0 : _b["error"];
498
498
  throw new BadRequestException("Error from Slack " + messageFromSlack);
499
499
  }
500
+ logger.debug("Searching for " + normalizedChannelName);
501
+ logger.debug("Searching channels in current page...");
502
+ logger.debug(response.jsonData["channels"]);
500
503
  for (const channel of response.jsonData["channels"]) {
501
504
  if (!channel["id"] || !channel["name"]) {
502
505
  continue;