@oneuptime/common 11.3.28 → 11.4.1

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.
Files changed (244) hide show
  1. package/Models/DatabaseModels/AIRun.ts +50 -0
  2. package/Models/DatabaseModels/Project.ts +60 -0
  3. package/Server/API/AIAgentTaskAPI.ts +10 -24
  4. package/Server/API/AIInvestigationAPI.ts +148 -0
  5. package/Server/API/TelemetryAPI.ts +4 -3
  6. package/Server/Infrastructure/Postgres/SchemaMigrations/1783598145111-AddEnableAutomaticIncidentInvestigationToProject.ts +19 -0
  7. package/Server/Infrastructure/Postgres/SchemaMigrations/1783615884106-AddInvestigationSubjectToAIRun.ts +33 -0
  8. package/Server/Infrastructure/Postgres/SchemaMigrations/1783632447975-AddEnableAutomaticAlertInvestigationToProject.ts +19 -0
  9. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  10. package/Server/Services/AIAgentTaskService.ts +62 -0
  11. package/Server/Services/AlertService.ts +25 -0
  12. package/Server/Services/AnalyticsDatabaseService.ts +27 -0
  13. package/Server/Services/IncidentService.ts +25 -0
  14. package/Server/Services/IncidentStateTimelineService.ts +19 -0
  15. package/Server/Services/LogAggregationService.ts +29 -11
  16. package/Server/Services/MetricService.ts +603 -13
  17. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +119 -11
  18. package/Server/Utils/AI/Sentinel/AlertInvestigationRunner.ts +141 -0
  19. package/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.ts +178 -0
  20. package/Server/Utils/AI/Sentinel/IncidentPostmortemRunner.ts +105 -0
  21. package/Server/Utils/AI/Sentinel/README.md +10 -0
  22. package/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.ts +405 -0
  23. package/Server/Utils/AI/Sentinel/SentinelMemory.ts +167 -0
  24. package/Server/Utils/AI/Toolbox/Index.ts +11 -0
  25. package/Server/Utils/AI/Toolbox/SentinelActionTools.ts +577 -0
  26. package/Server/Utils/Monitor/Criteria/APIRequestCriteria.ts +44 -0
  27. package/Server/Utils/Monitor/MonitorMetricUtil.ts +108 -2
  28. package/Tests/Server/API/BaseAPI.test.ts +1 -0
  29. package/Tests/Server/Services/AIAgentTaskServiceClaim.test.ts +146 -0
  30. package/Tests/Server/Services/LogAggregationService.test.ts +22 -0
  31. package/Tests/Server/Services/MetricServiceAggregate.test.ts +194 -0
  32. package/Tests/Types/AI/AIAgentTaskStatus.test.ts +86 -0
  33. package/Tests/Types/BaseDatabase/AggregationIntervalUtil.test.ts +127 -0
  34. package/Tests/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.test.ts +152 -0
  35. package/Tests/Types/OnCallDutyPolicy/ScheduleShiftUtil.test.ts +260 -0
  36. package/Tests/Types/Trace/TraceRecordingRuleDefinition.test.ts +308 -0
  37. package/Tests/UI/Components/Dropdown.test.tsx +17 -0
  38. package/Tests/UI/Components/EntityDropdown.test.tsx +130 -0
  39. package/Tests/UI/Components/Modal.test.tsx +144 -2
  40. package/Tests/UI/Components/TelemetrySearchBar.test.tsx +90 -0
  41. package/Tests/UI/Utils/Theme.test.ts +161 -0
  42. package/Types/BaseDatabase/AggregateBy.ts +12 -0
  43. package/Types/Dashboard/DashboardComponentType.ts +1 -0
  44. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
  45. package/Types/Dashboard/DashboardComponents/DashboardLogChartComponent.ts +19 -0
  46. package/Types/Dashboard/DashboardTemplates.ts +9 -1
  47. package/Types/Date.ts +46 -0
  48. package/Types/Monitor/CriteriaFilter.ts +4 -0
  49. package/Types/Monitor/HttpPhaseTimings.ts +16 -0
  50. package/Types/Monitor/MonitorMetricType.ts +17 -0
  51. package/Types/Monitor/PingMonitor/PingMonitorResponse.ts +20 -0
  52. package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +257 -0
  53. package/Types/Probe/ProbeMonitorResponse.ts +14 -0
  54. package/UI/Components/Calendar/Calendar.tsx +8 -2
  55. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +5 -5
  56. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +8 -5
  57. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +4 -1
  58. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +6 -3
  59. package/UI/Components/CodeBlock/CodeBlock.tsx +2 -1
  60. package/UI/Components/CodeEditor/CodeEditor.tsx +3 -0
  61. package/UI/Components/Divider/Divider.tsx +1 -1
  62. package/UI/Components/Dropdown/Dropdown.tsx +13 -4
  63. package/UI/Components/EntityDropdown/EntityDropdown.tsx +110 -2
  64. package/UI/Components/ErrorBoundary.tsx +2 -1
  65. package/UI/Components/EventItem/EventItem.tsx +2 -2
  66. package/UI/Components/GanttChart/Bar/Index.tsx +1 -1
  67. package/UI/Components/Graphs/UptimeBarTooltip.tsx +30 -23
  68. package/UI/Components/Header/Header.tsx +7 -0
  69. package/UI/Components/Header/HeaderIconDropdownButton.tsx +2 -0
  70. package/UI/Components/Label/Label.tsx +2 -2
  71. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +22 -10
  72. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +9 -3
  73. package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +8 -8
  74. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +44 -20
  75. package/UI/Components/Modal/ConfirmModal.tsx +1 -1
  76. package/UI/Components/Modal/Modal.tsx +213 -121
  77. package/UI/Components/Modal/ModalFooter.tsx +33 -22
  78. package/UI/Components/ModelTable/TableView.tsx +1 -1
  79. package/UI/Components/MonitorGraphs/UptimeBarDayModal.tsx +20 -12
  80. package/UI/Components/Pill/Pill.tsx +21 -13
  81. package/UI/Components/StatusBubble/StatusBubble.tsx +3 -3
  82. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +7 -1
  83. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +9 -3
  84. package/UI/Components/TelemetryViewer/components/TelemetrySearchBar.tsx +62 -4
  85. package/UI/Components/Workflow/Component.tsx +53 -33
  86. package/UI/Components/Workflow/ComponentPortViewer.tsx +6 -6
  87. package/UI/Components/Workflow/ComponentReturnValueViewer.tsx +8 -8
  88. package/UI/Components/Workflow/ComponentsModal.tsx +16 -9
  89. package/UI/Components/Workflow/Workflow.tsx +13 -11
  90. package/UI/Utils/Theme.ts +140 -0
  91. package/Utils/Dashboard/Components/DashboardLogChartComponent.ts +104 -0
  92. package/Utils/Dashboard/Components/Index.ts +7 -0
  93. package/Utils/Monitor/MonitorMetricType.ts +70 -2
  94. package/build/dist/Models/DatabaseModels/AIRun.js +52 -0
  95. package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
  96. package/build/dist/Models/DatabaseModels/Project.js +62 -0
  97. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  98. package/build/dist/Server/API/AIAgentTaskAPI.js +8 -23
  99. package/build/dist/Server/API/AIAgentTaskAPI.js.map +1 -1
  100. package/build/dist/Server/API/AIInvestigationAPI.js +107 -0
  101. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -0
  102. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  103. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783598145111-AddEnableAutomaticIncidentInvestigationToProject.js +12 -0
  104. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783598145111-AddEnableAutomaticIncidentInvestigationToProject.js.map +1 -0
  105. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783615884106-AddInvestigationSubjectToAIRun.js +18 -0
  106. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783615884106-AddInvestigationSubjectToAIRun.js.map +1 -0
  107. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783632447975-AddEnableAutomaticAlertInvestigationToProject.js +12 -0
  108. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783632447975-AddEnableAutomaticAlertInvestigationToProject.js.map +1 -0
  109. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  110. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  111. package/build/dist/Server/Services/AIAgentTaskService.js +59 -0
  112. package/build/dist/Server/Services/AIAgentTaskService.js.map +1 -1
  113. package/build/dist/Server/Services/AlertService.js +24 -0
  114. package/build/dist/Server/Services/AlertService.js.map +1 -1
  115. package/build/dist/Server/Services/AnalyticsDatabaseService.js +19 -0
  116. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  117. package/build/dist/Server/Services/IncidentService.js +24 -0
  118. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  119. package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -0
  120. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  121. package/build/dist/Server/Services/LogAggregationService.js +21 -7
  122. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  123. package/build/dist/Server/Services/MetricService.js +449 -7
  124. package/build/dist/Server/Services/MetricService.js.map +1 -1
  125. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +59 -11
  126. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  127. package/build/dist/Server/Utils/AI/Sentinel/AlertInvestigationRunner.js +119 -0
  128. package/build/dist/Server/Utils/AI/Sentinel/AlertInvestigationRunner.js.map +1 -0
  129. package/build/dist/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.js +147 -0
  130. package/build/dist/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.js.map +1 -0
  131. package/build/dist/Server/Utils/AI/Sentinel/IncidentPostmortemRunner.js +93 -0
  132. package/build/dist/Server/Utils/AI/Sentinel/IncidentPostmortemRunner.js.map +1 -0
  133. package/build/dist/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.js +311 -0
  134. package/build/dist/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.js.map +1 -0
  135. package/build/dist/Server/Utils/AI/Sentinel/SentinelMemory.js +143 -0
  136. package/build/dist/Server/Utils/AI/Sentinel/SentinelMemory.js.map +1 -0
  137. package/build/dist/Server/Utils/AI/Toolbox/Index.js +6 -0
  138. package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
  139. package/build/dist/Server/Utils/AI/Toolbox/SentinelActionTools.js +451 -0
  140. package/build/dist/Server/Utils/AI/Toolbox/SentinelActionTools.js.map +1 -0
  141. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js +29 -0
  142. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js.map +1 -1
  143. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +86 -2
  144. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  145. package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
  146. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  147. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
  148. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  149. package/build/dist/Types/Dashboard/DashboardComponents/DashboardLogChartComponent.js +2 -0
  150. package/build/dist/Types/Dashboard/DashboardComponents/DashboardLogChartComponent.js.map +1 -0
  151. package/build/dist/Types/Dashboard/DashboardTemplates.js +9 -1
  152. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  153. package/build/dist/Types/Date.js +32 -0
  154. package/build/dist/Types/Date.js.map +1 -1
  155. package/build/dist/Types/Monitor/CriteriaFilter.js +4 -0
  156. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  157. package/build/dist/Types/Monitor/HttpPhaseTimings.js +2 -0
  158. package/build/dist/Types/Monitor/HttpPhaseTimings.js.map +1 -0
  159. package/build/dist/Types/Monitor/MonitorMetricType.js +15 -0
  160. package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
  161. package/build/dist/Types/Monitor/PingMonitor/PingMonitorResponse.js +2 -0
  162. package/build/dist/Types/Monitor/PingMonitor/PingMonitorResponse.js.map +1 -0
  163. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +159 -0
  164. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -0
  165. package/build/dist/UI/Components/Calendar/Calendar.js +2 -2
  166. package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
  167. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +2 -2
  168. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  169. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +7 -4
  170. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  171. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +4 -1
  172. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  173. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +6 -3
  174. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  175. package/build/dist/UI/Components/CodeBlock/CodeBlock.js +1 -1
  176. package/build/dist/UI/Components/CodeBlock/CodeBlock.js.map +1 -1
  177. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +3 -1
  178. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  179. package/build/dist/UI/Components/Divider/Divider.js +1 -1
  180. package/build/dist/UI/Components/Divider/Divider.js.map +1 -1
  181. package/build/dist/UI/Components/Dropdown/Dropdown.js +10 -4
  182. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  183. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +76 -3
  184. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
  185. package/build/dist/UI/Components/ErrorBoundary.js +2 -1
  186. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  187. package/build/dist/UI/Components/EventItem/EventItem.js +2 -2
  188. package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
  189. package/build/dist/UI/Components/GanttChart/Bar/Index.js +1 -1
  190. package/build/dist/UI/Components/GanttChart/Bar/Index.js.map +1 -1
  191. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js +29 -23
  192. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js.map +1 -1
  193. package/build/dist/UI/Components/Header/Header.js +1 -0
  194. package/build/dist/UI/Components/Header/Header.js.map +1 -1
  195. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +1 -1
  196. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  197. package/build/dist/UI/Components/Label/Label.js +2 -2
  198. package/build/dist/UI/Components/Label/Label.js.map +1 -1
  199. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +20 -8
  200. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  201. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +8 -2
  202. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  203. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +8 -8
  204. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +40 -20
  205. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  206. package/build/dist/UI/Components/Modal/ConfirmModal.js +1 -1
  207. package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
  208. package/build/dist/UI/Components/Modal/Modal.js +109 -51
  209. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  210. package/build/dist/UI/Components/Modal/ModalFooter.js +13 -10
  211. package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
  212. package/build/dist/UI/Components/ModelTable/TableView.js +1 -1
  213. package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
  214. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js +17 -12
  215. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js.map +1 -1
  216. package/build/dist/UI/Components/Pill/Pill.js +12 -13
  217. package/build/dist/UI/Components/Pill/Pill.js.map +1 -1
  218. package/build/dist/UI/Components/StatusBubble/StatusBubble.js +3 -3
  219. package/build/dist/UI/Components/StatusBubble/StatusBubble.js.map +1 -1
  220. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +1 -1
  221. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  222. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +8 -2
  223. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  224. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js +36 -6
  225. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js.map +1 -1
  226. package/build/dist/UI/Components/Workflow/Component.js +43 -33
  227. package/build/dist/UI/Components/Workflow/Component.js.map +1 -1
  228. package/build/dist/UI/Components/Workflow/ComponentPortViewer.js +6 -6
  229. package/build/dist/UI/Components/Workflow/ComponentPortViewer.js.map +1 -1
  230. package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js +8 -8
  231. package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js.map +1 -1
  232. package/build/dist/UI/Components/Workflow/ComponentsModal.js +15 -9
  233. package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
  234. package/build/dist/UI/Components/Workflow/Workflow.js +13 -11
  235. package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
  236. package/build/dist/UI/Utils/Theme.js +106 -0
  237. package/build/dist/UI/Utils/Theme.js.map +1 -0
  238. package/build/dist/Utils/Dashboard/Components/DashboardLogChartComponent.js +92 -0
  239. package/build/dist/Utils/Dashboard/Components/DashboardLogChartComponent.js.map +1 -0
  240. package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
  241. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  242. package/build/dist/Utils/Monitor/MonitorMetricType.js +70 -3
  243. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  244. package/package.json +1 -1
@@ -0,0 +1,127 @@
1
+ import AggregationInterval from "../../../Types/BaseDatabase/AggregationInterval";
2
+ import AggregationIntervalUtil from "../../../Types/BaseDatabase/AggregationIntervalUtil";
3
+
4
+ describe("AggregationIntervalUtil", () => {
5
+ const SECOND: number = 1000;
6
+ const HOUR: number = SECOND * 60 * 60;
7
+ const DAY: number = HOUR * 24;
8
+
9
+ const windowOf: (durationMs: number) => { startDate: Date; endDate: Date } = (
10
+ durationMs: number,
11
+ ): { startDate: Date; endDate: Date } => {
12
+ const startDate: Date = new Date(Date.UTC(2020, 0, 1, 0, 0, 0));
13
+ const endDate: Date = new Date(startDate.getTime() + durationMs);
14
+ return { startDate, endDate };
15
+ };
16
+
17
+ describe("getAggregationIntervalForWindow", () => {
18
+ const cases: Array<[string, number, AggregationInterval]> = [
19
+ ["2 hours -> Minute", 2 * HOUR, AggregationInterval.Minute],
20
+ [
21
+ "exactly 3 hours -> Minute (inclusive bound)",
22
+ 3 * HOUR,
23
+ AggregationInterval.Minute,
24
+ ],
25
+ ["3 hours + 1ms -> Hour", 3 * HOUR + 1, AggregationInterval.Hour],
26
+ ["1 day -> Hour", DAY, AggregationInterval.Hour],
27
+ [
28
+ "exactly 7 days -> Hour (inclusive bound)",
29
+ 7 * DAY,
30
+ AggregationInterval.Hour,
31
+ ],
32
+ ["7 days + 1ms -> Day", 7 * DAY + 1, AggregationInterval.Day],
33
+ ["14 days -> Day", 14 * DAY, AggregationInterval.Day],
34
+ [
35
+ "exactly 6 weeks -> Day (inclusive bound)",
36
+ 42 * DAY,
37
+ AggregationInterval.Day,
38
+ ],
39
+ ["6 weeks + 1ms -> Week", 42 * DAY + 1, AggregationInterval.Week],
40
+ ["60 days -> Week", 60 * DAY, AggregationInterval.Week],
41
+ ["200 days -> Month", 200 * DAY, AggregationInterval.Month],
42
+ ["8 years -> Year", 8 * 365 * DAY, AggregationInterval.Year],
43
+ ];
44
+
45
+ test.each(cases)(
46
+ "%s",
47
+ (_label: string, durationMs: number, expected: AggregationInterval) => {
48
+ expect(
49
+ AggregationIntervalUtil.getAggregationIntervalForWindow(
50
+ windowOf(durationMs),
51
+ ),
52
+ ).toBe(expected);
53
+ },
54
+ );
55
+
56
+ test("returns the widest interval as the window grows", () => {
57
+ const order: Array<AggregationInterval> = [
58
+ AggregationInterval.Minute,
59
+ AggregationInterval.Hour,
60
+ AggregationInterval.Day,
61
+ AggregationInterval.Week,
62
+ AggregationInterval.Month,
63
+ AggregationInterval.Year,
64
+ ];
65
+ const durations: Array<number> = [
66
+ HOUR,
67
+ 2 * DAY,
68
+ 30 * DAY,
69
+ 90 * DAY,
70
+ 365 * DAY,
71
+ 10 * 365 * DAY,
72
+ ];
73
+
74
+ const results: Array<AggregationInterval> = durations.map(
75
+ (d: number): AggregationInterval => {
76
+ return AggregationIntervalUtil.getAggregationIntervalForWindow(
77
+ windowOf(d),
78
+ );
79
+ },
80
+ );
81
+
82
+ // Each larger window is at least as wide an interval as the previous.
83
+ for (let i: number = 1; i < results.length; i++) {
84
+ expect(order.indexOf(results[i]!)).toBeGreaterThanOrEqual(
85
+ order.indexOf(results[i - 1]!),
86
+ );
87
+ }
88
+ });
89
+ });
90
+
91
+ describe("getAggregationIntervalMs", () => {
92
+ test.each([
93
+ [AggregationInterval.Minute, 1000 * 60],
94
+ [AggregationInterval.Hour, 1000 * 60 * 60],
95
+ [AggregationInterval.Day, 1000 * 60 * 60 * 24],
96
+ [AggregationInterval.Week, 1000 * 60 * 60 * 24 * 7],
97
+ [AggregationInterval.Month, 1000 * 60 * 60 * 24 * 30],
98
+ [AggregationInterval.Year, 1000 * 60 * 60 * 24 * 365],
99
+ ])(
100
+ "maps %s to its nominal width",
101
+ (interval: AggregationInterval, expectedMs: number) => {
102
+ expect(AggregationIntervalUtil.getAggregationIntervalMs(interval)).toBe(
103
+ expectedMs,
104
+ );
105
+ },
106
+ );
107
+
108
+ test("intervals are strictly increasing in width", () => {
109
+ const ordered: Array<AggregationInterval> = [
110
+ AggregationInterval.Minute,
111
+ AggregationInterval.Hour,
112
+ AggregationInterval.Day,
113
+ AggregationInterval.Week,
114
+ AggregationInterval.Month,
115
+ AggregationInterval.Year,
116
+ ];
117
+
118
+ for (let i: number = 1; i < ordered.length; i++) {
119
+ expect(
120
+ AggregationIntervalUtil.getAggregationIntervalMs(ordered[i]!),
121
+ ).toBeGreaterThan(
122
+ AggregationIntervalUtil.getAggregationIntervalMs(ordered[i - 1]!),
123
+ );
124
+ }
125
+ });
126
+ });
127
+ });
@@ -0,0 +1,152 @@
1
+ import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
2
+ import DatabaseCommonInteractionPropsUtil, {
3
+ PermissionType,
4
+ } from "../../../Types/BaseDatabase/DatabaseCommonInteractionPropsUtil";
5
+ import ObjectID from "../../../Types/ObjectID";
6
+ import Permission, {
7
+ UserPermission,
8
+ UserTenantAccessPermission,
9
+ } from "../../../Types/Permission";
10
+
11
+ describe("DatabaseCommonInteractionPropsUtil.getUserPermissions", () => {
12
+ const makePerm: (
13
+ permission: Permission,
14
+ isBlockPermission: boolean,
15
+ ) => UserPermission = (
16
+ permission: Permission,
17
+ isBlockPermission: boolean,
18
+ ): UserPermission => {
19
+ return {
20
+ _type: "UserPermission",
21
+ permission,
22
+ labelIds: [],
23
+ isBlockPermission,
24
+ };
25
+ };
26
+
27
+ const permissionsOf: (result: Array<UserPermission>) => Array<Permission> = (
28
+ result: Array<UserPermission>,
29
+ ): Array<Permission> => {
30
+ return result.map((p: UserPermission): Permission => {
31
+ return p.permission;
32
+ });
33
+ };
34
+
35
+ describe("global permissions", () => {
36
+ test("always grants Public for an anonymous Allow request", () => {
37
+ const props: DatabaseCommonInteractionProps = {};
38
+
39
+ const perms: Array<Permission> = permissionsOf(
40
+ DatabaseCommonInteractionPropsUtil.getUserPermissions(
41
+ props,
42
+ PermissionType.Allow,
43
+ ),
44
+ );
45
+
46
+ expect(perms).toContain(Permission.Public);
47
+ });
48
+
49
+ test("adds CurrentUser when a user is logged in", () => {
50
+ const props: DatabaseCommonInteractionProps = {
51
+ userId: ObjectID.generate(),
52
+ };
53
+
54
+ const perms: Array<Permission> = permissionsOf(
55
+ DatabaseCommonInteractionPropsUtil.getUserPermissions(
56
+ props,
57
+ PermissionType.Allow,
58
+ ),
59
+ );
60
+
61
+ expect(perms).toContain(Permission.Public);
62
+ expect(perms).toContain(Permission.CurrentUser);
63
+ });
64
+
65
+ test("preserves existing global permissions and appends Public", () => {
66
+ const props: DatabaseCommonInteractionProps = {
67
+ userGlobalAccessPermission: {
68
+ _type: "UserGlobalAccessPermission",
69
+ projectIds: [],
70
+ globalPermissions: [Permission.ProjectOwner],
71
+ },
72
+ };
73
+
74
+ const perms: Array<Permission> = permissionsOf(
75
+ DatabaseCommonInteractionPropsUtil.getUserPermissions(
76
+ props,
77
+ PermissionType.Allow,
78
+ ),
79
+ );
80
+
81
+ expect(perms).toContain(Permission.ProjectOwner);
82
+ expect(perms).toContain(Permission.Public);
83
+ });
84
+
85
+ test("does not include global permissions for a Block request", () => {
86
+ const props: DatabaseCommonInteractionProps = {};
87
+
88
+ expect(
89
+ DatabaseCommonInteractionPropsUtil.getUserPermissions(
90
+ props,
91
+ PermissionType.Block,
92
+ ),
93
+ ).toEqual([]);
94
+ });
95
+ });
96
+
97
+ describe("tenant permissions", () => {
98
+ const buildTenantProps: () => {
99
+ props: DatabaseCommonInteractionProps;
100
+ tenantId: ObjectID;
101
+ } = (): {
102
+ props: DatabaseCommonInteractionProps;
103
+ tenantId: ObjectID;
104
+ } => {
105
+ const tenantId: ObjectID = ObjectID.generate();
106
+ const tenantPermission: UserTenantAccessPermission = {
107
+ _type: "UserTenantAccessPermission",
108
+ projectId: tenantId,
109
+ permissions: [
110
+ makePerm(Permission.ProjectMember, false), // allow row
111
+ makePerm(Permission.ProjectOwner, true), // block row
112
+ ],
113
+ };
114
+
115
+ return {
116
+ tenantId,
117
+ props: {
118
+ tenantId,
119
+ userTenantAccessPermission: {
120
+ [tenantId.toString()]: tenantPermission,
121
+ },
122
+ },
123
+ };
124
+ };
125
+
126
+ test("Allow includes the tenant's non-block permissions (plus global)", () => {
127
+ const perms: Array<Permission> = permissionsOf(
128
+ DatabaseCommonInteractionPropsUtil.getUserPermissions(
129
+ buildTenantProps().props,
130
+ PermissionType.Allow,
131
+ ),
132
+ );
133
+
134
+ expect(perms).toContain(Permission.ProjectMember); // allow tenant row
135
+ expect(perms).toContain(Permission.Public); // global
136
+ expect(perms).not.toContain(Permission.ProjectOwner); // block row excluded
137
+ });
138
+
139
+ test("Block includes only the tenant's block permissions", () => {
140
+ const perms: Array<Permission> = permissionsOf(
141
+ DatabaseCommonInteractionPropsUtil.getUserPermissions(
142
+ buildTenantProps().props,
143
+ PermissionType.Block,
144
+ ),
145
+ );
146
+
147
+ expect(perms).toContain(Permission.ProjectOwner); // block tenant row
148
+ expect(perms).not.toContain(Permission.ProjectMember); // allow row excluded
149
+ expect(perms).not.toContain(Permission.Public); // no global for Block
150
+ });
151
+ });
152
+ });
@@ -0,0 +1,260 @@
1
+ import CalendarEvent from "../../../Types/Calendar/CalendarEvent";
2
+ import OneUptimeDate from "../../../Types/Date";
3
+ import ScheduleShiftUtil, {
4
+ CoverageGap,
5
+ CurrentAndNextShift,
6
+ OnCallShift,
7
+ } from "../../../Types/OnCallDutyPolicy/ScheduleShiftUtil";
8
+
9
+ /*
10
+ * These tests pin the pure event -> shift transformation the on-call schedule
11
+ * summaries depend on. They deliberately do NOT re-test LayerUtil (covered by
12
+ * its own exhaustive suites) — they only cover the grouping / gap / current-next
13
+ * logic layered on top of its output.
14
+ */
15
+
16
+ // Build a CalendarEvent the way LayerUtil emits one: user id in `title`.
17
+ const event: (userId: string, start: Date, end: Date) => CalendarEvent = (
18
+ userId: string,
19
+ start: Date,
20
+ end: Date,
21
+ ): CalendarEvent => {
22
+ return {
23
+ id: 0,
24
+ title: userId,
25
+ allDay: false,
26
+ start,
27
+ end,
28
+ };
29
+ };
30
+
31
+ const at: (iso: string) => Date = (iso: string): Date => {
32
+ return OneUptimeDate.fromString(iso);
33
+ };
34
+
35
+ /*
36
+ * Build an OnCallShift literal for the resolver/gap tests (coverageSeconds is
37
+ * irrelevant to those, so default it to the wall-clock span).
38
+ */
39
+ const mkShift: (userId: string, start: Date, end: Date) => OnCallShift = (
40
+ userId: string,
41
+ start: Date,
42
+ end: Date,
43
+ ): OnCallShift => {
44
+ return {
45
+ userId,
46
+ start,
47
+ end,
48
+ coverageSeconds: OneUptimeDate.getDifferenceInSeconds(end, start),
49
+ };
50
+ };
51
+
52
+ describe("ScheduleShiftUtil", () => {
53
+ describe("groupEventsIntoShifts", () => {
54
+ test("returns empty array for no events", () => {
55
+ expect(ScheduleShiftUtil.groupEventsIntoShifts([])).toEqual([]);
56
+ });
57
+
58
+ test("keeps distinct users as separate shifts (24/7 rotation)", () => {
59
+ const events: Array<CalendarEvent> = [
60
+ event("A", at("2024-01-01T09:00:00Z"), at("2024-01-02T09:00:00Z")),
61
+ event("B", at("2024-01-02T09:00:01Z"), at("2024-01-03T09:00:00Z")),
62
+ event("A", at("2024-01-03T09:00:01Z"), at("2024-01-04T09:00:00Z")),
63
+ ];
64
+
65
+ const shifts: Array<OnCallShift> =
66
+ ScheduleShiftUtil.groupEventsIntoShifts(events);
67
+
68
+ expect(shifts).toHaveLength(3);
69
+ expect(
70
+ shifts.map((s: OnCallShift) => {
71
+ return s.userId;
72
+ }),
73
+ ).toEqual(["A", "B", "A"]);
74
+ expect(shifts[0]!.end).toEqual(at("2024-01-02T09:00:00Z"));
75
+ });
76
+
77
+ test("merges touching same-user segments into one contiguous shift", () => {
78
+ // Two segments one second apart -> a single shift.
79
+ const events: Array<CalendarEvent> = [
80
+ event("A", at("2024-01-01T09:00:00Z"), at("2024-01-01T17:00:00Z")),
81
+ event("A", at("2024-01-01T17:00:01Z"), at("2024-01-02T09:00:00Z")),
82
+ ];
83
+
84
+ const shifts: Array<OnCallShift> =
85
+ ScheduleShiftUtil.groupEventsIntoShifts(events);
86
+
87
+ expect(shifts).toHaveLength(1);
88
+ expect(shifts[0]!.start).toEqual(at("2024-01-01T09:00:00Z"));
89
+ expect(shifts[0]!.end).toEqual(at("2024-01-02T09:00:00Z"));
90
+ });
91
+
92
+ test("does NOT merge same-user segments across a real gap by default", () => {
93
+ // Daily 9-5 restriction: same user, but a 16h nightly gap between days.
94
+ const events: Array<CalendarEvent> = [
95
+ event("A", at("2024-01-01T09:00:00Z"), at("2024-01-01T17:00:00Z")),
96
+ event("A", at("2024-01-02T09:00:00Z"), at("2024-01-02T17:00:00Z")),
97
+ ];
98
+
99
+ const shifts: Array<OnCallShift> =
100
+ ScheduleShiftUtil.groupEventsIntoShifts(events);
101
+
102
+ expect(shifts).toHaveLength(2);
103
+ });
104
+
105
+ test("merges same-user segments across gaps when mergeAcrossGaps=true", () => {
106
+ const events: Array<CalendarEvent> = [
107
+ event("A", at("2024-01-01T09:00:00Z"), at("2024-01-01T17:00:00Z")),
108
+ event("A", at("2024-01-02T09:00:00Z"), at("2024-01-02T17:00:00Z")),
109
+ event("B", at("2024-01-03T09:00:00Z"), at("2024-01-03T17:00:00Z")),
110
+ ];
111
+
112
+ const shifts: Array<OnCallShift> =
113
+ ScheduleShiftUtil.groupEventsIntoShifts(events, {
114
+ mergeAcrossGaps: true,
115
+ });
116
+
117
+ expect(shifts).toHaveLength(2);
118
+ expect(shifts[0]!.userId).toBe("A");
119
+ expect(shifts[0]!.start).toEqual(at("2024-01-01T09:00:00Z"));
120
+ // Turn end is the last covered instant of that user's run.
121
+ expect(shifts[0]!.end).toEqual(at("2024-01-02T17:00:00Z"));
122
+ /*
123
+ * coverageSeconds is the REAL active time (two 8h days = 16h), not the
124
+ * ~32h wall-clock span across the overnight gap.
125
+ */
126
+ expect(shifts[0]!.coverageSeconds).toBe(16 * 3600);
127
+ expect(shifts[1]!.userId).toBe("B");
128
+ });
129
+
130
+ test("sorts unsorted input by start time and never mutates the caller array", () => {
131
+ const events: Array<CalendarEvent> = [
132
+ event("B", at("2024-01-02T09:00:00Z"), at("2024-01-03T09:00:00Z")),
133
+ event("A", at("2024-01-01T09:00:00Z"), at("2024-01-02T09:00:00Z")),
134
+ ];
135
+ const snapshot: Array<string> = events.map((e: CalendarEvent) => {
136
+ return e.title;
137
+ });
138
+
139
+ const shifts: Array<OnCallShift> =
140
+ ScheduleShiftUtil.groupEventsIntoShifts(events);
141
+
142
+ expect(
143
+ shifts.map((s: OnCallShift) => {
144
+ return s.userId;
145
+ }),
146
+ ).toEqual(["A", "B"]);
147
+ // input order preserved (we copy before sorting)
148
+ expect(
149
+ events.map((e: CalendarEvent) => {
150
+ return e.title;
151
+ }),
152
+ ).toEqual(snapshot);
153
+ });
154
+ });
155
+
156
+ describe("getCurrentAndNextShift", () => {
157
+ const shifts: Array<OnCallShift> = [
158
+ mkShift("A", at("2024-01-01T09:00:00Z"), at("2024-01-02T09:00:00Z")),
159
+ mkShift("B", at("2024-01-02T09:00:00Z"), at("2024-01-03T09:00:00Z")),
160
+ ];
161
+
162
+ test("resolves the current and next shift when now is inside a shift", () => {
163
+ const result: CurrentAndNextShift =
164
+ ScheduleShiftUtil.getCurrentAndNextShift(
165
+ shifts,
166
+ at("2024-01-01T12:00:00Z"),
167
+ );
168
+
169
+ expect(result.current?.userId).toBe("A");
170
+ expect(result.next?.userId).toBe("B");
171
+ });
172
+
173
+ test("current is null inside a coverage gap but next is still found", () => {
174
+ const gapped: Array<OnCallShift> = [
175
+ mkShift("A", at("2024-01-01T09:00:00Z"), at("2024-01-01T17:00:00Z")),
176
+ mkShift("A", at("2024-01-02T09:00:00Z"), at("2024-01-02T17:00:00Z")),
177
+ ];
178
+
179
+ const result: CurrentAndNextShift =
180
+ ScheduleShiftUtil.getCurrentAndNextShift(
181
+ gapped,
182
+ at("2024-01-01T20:00:00Z"),
183
+ );
184
+
185
+ expect(result.current).toBeNull();
186
+ expect(result.next?.start).toEqual(at("2024-01-02T09:00:00Z"));
187
+ });
188
+
189
+ test("next is null when now is past the last shift", () => {
190
+ const result: CurrentAndNextShift =
191
+ ScheduleShiftUtil.getCurrentAndNextShift(
192
+ shifts,
193
+ at("2024-01-05T00:00:00Z"),
194
+ );
195
+
196
+ expect(result.current).toBeNull();
197
+ expect(result.next).toBeNull();
198
+ });
199
+ });
200
+
201
+ describe("getCoverageGaps", () => {
202
+ test("reports a leading gap before the first shift", () => {
203
+ const shifts: Array<OnCallShift> = [
204
+ mkShift("A", at("2024-01-01T12:00:00Z"), at("2024-01-01T18:00:00Z")),
205
+ ];
206
+
207
+ const gaps: Array<CoverageGap> = ScheduleShiftUtil.getCoverageGaps(
208
+ shifts,
209
+ at("2024-01-01T09:00:00Z"),
210
+ at("2024-01-01T20:00:00Z"),
211
+ );
212
+
213
+ expect(gaps).toHaveLength(1);
214
+ expect(gaps[0]!.start).toEqual(at("2024-01-01T09:00:00Z"));
215
+ expect(gaps[0]!.end).toEqual(at("2024-01-01T12:00:00Z"));
216
+ });
217
+
218
+ test("reports gaps between shifts but not trailing time after the last", () => {
219
+ const shifts: Array<OnCallShift> = [
220
+ mkShift("A", at("2024-01-01T09:00:00Z"), at("2024-01-01T17:00:00Z")),
221
+ mkShift("A", at("2024-01-02T09:00:00Z"), at("2024-01-02T17:00:00Z")),
222
+ ];
223
+
224
+ const gaps: Array<CoverageGap> = ScheduleShiftUtil.getCoverageGaps(
225
+ shifts,
226
+ at("2024-01-01T09:00:00Z"),
227
+ at("2024-01-03T00:00:00Z"),
228
+ );
229
+
230
+ expect(gaps).toHaveLength(1);
231
+ expect(gaps[0]!.start).toEqual(at("2024-01-01T17:00:00Z"));
232
+ expect(gaps[0]!.end).toEqual(at("2024-01-02T09:00:00Z"));
233
+ });
234
+
235
+ test("treats the whole window as a gap when there are no shifts", () => {
236
+ const gaps: Array<CoverageGap> = ScheduleShiftUtil.getCoverageGaps(
237
+ [],
238
+ at("2024-01-01T09:00:00Z"),
239
+ at("2024-01-02T09:00:00Z"),
240
+ );
241
+
242
+ expect(gaps).toHaveLength(1);
243
+ });
244
+
245
+ test("ignores the one-second boundaries between touching segments", () => {
246
+ const shifts: Array<OnCallShift> = [
247
+ mkShift("A", at("2024-01-01T09:00:00Z"), at("2024-01-01T17:00:00Z")),
248
+ mkShift("B", at("2024-01-01T17:00:01Z"), at("2024-01-02T09:00:00Z")),
249
+ ];
250
+
251
+ const gaps: Array<CoverageGap> = ScheduleShiftUtil.getCoverageGaps(
252
+ shifts,
253
+ at("2024-01-01T09:00:00Z"),
254
+ at("2024-01-02T09:00:00Z"),
255
+ );
256
+
257
+ expect(gaps).toHaveLength(0);
258
+ });
259
+ });
260
+ });