@oneuptime/common 12.0.34 → 13.0.0

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 (259) hide show
  1. package/Models/DatabaseModels/GlobalConfig.ts +12 -12
  2. package/Models/DatabaseModels/InventoryItem.ts +44 -1
  3. package/Models/DatabaseModels/NetworkDevice.ts +126 -0
  4. package/Server/EnvironmentConfig.ts +47 -16
  5. package/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.ts +25 -0
  6. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  7. package/Server/Infrastructure/Redis.ts +8 -8
  8. package/Server/Infrastructure/Status.ts +10 -4
  9. package/Server/Services/DatabaseService.ts +65 -13
  10. package/Server/Services/InventoryItemService.ts +31 -1
  11. package/Server/Services/MonitorTemplateService.ts +140 -19
  12. package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +349 -41
  13. package/Server/Services/NetworkDeviceDiscoveryScanService.ts +87 -2
  14. package/Server/Services/NetworkDeviceService.ts +69 -0
  15. package/Server/Services/UserNotificationRuleService.ts +76 -25
  16. package/Server/Types/Database/Permissions/ReadPermission.ts +45 -5
  17. package/Server/Types/Database/QueryUtil.ts +56 -0
  18. package/Server/Types/Markdown.ts +93 -1
  19. package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +82 -17
  20. package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +10 -1
  21. package/Server/Utils/EmailRollup/EmailRollupRenderer.ts +2 -2
  22. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +82 -35
  23. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +31 -5
  24. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +202 -37
  25. package/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.ts +82 -9
  26. package/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.ts +16 -7
  27. package/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.ts +29 -17
  28. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +107 -48
  29. package/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.ts +158 -0
  30. package/Server/Utils/Monitor/NetworkInventoryUtil.ts +27 -0
  31. package/Server/Utils/Telemetry/EntityRegistry.ts +11 -1
  32. package/Server/Utils/Telemetry/TelemetryEntity.ts +83 -9
  33. package/Tests/App/Dashboard/AddNeighborToMonitoringModal.test.tsx +32 -0
  34. package/Tests/App/Dashboard/AdoptedDeviceDrawer.test.tsx +374 -0
  35. package/Tests/App/Dashboard/DashboardLabelVariables.test.tsx +580 -0
  36. package/Tests/App/Dashboard/DashboardMonitorLabelVariable.test.tsx +438 -0
  37. package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +5 -2
  38. package/Tests/App/Dashboard/DeviceAttachmentCard.test.tsx +273 -0
  39. package/Tests/App/Dashboard/DiscoveryReviewInventoryRefresh.test.tsx +505 -0
  40. package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +163 -0
  41. package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +401 -0
  42. package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +232 -0
  43. package/Tests/App/Dashboard/InventoryItems.test.tsx +201 -0
  44. package/Tests/App/Dashboard/InventoryTypeAndStatusFacets.test.tsx +798 -0
  45. package/Tests/App/Dashboard/NetworkTopologyLiveView.test.tsx +320 -0
  46. package/Tests/App/Dashboard/NetworkTopologyToolbar.test.tsx +275 -0
  47. package/Tests/App/Dashboard/OnCallCalendarFeedEmptyState.test.tsx +975 -0
  48. package/Tests/App/Dashboard/OnCallCalendarFeedPlanGate.test.tsx +63 -0
  49. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +626 -0
  50. package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +348 -0
  51. package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +331 -0
  52. package/Tests/App/Dashboard/UserSettingsEmailPreferences.test.tsx +603 -0
  53. package/Tests/App/Dashboard/UserSettingsNotificationSettings.test.tsx +324 -0
  54. package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +13 -4
  55. package/Tests/App/StatusPage/StatusPageOidcOrigin.test.tsx +223 -0
  56. package/Tests/Models/InventoryItemStatus.test.ts +147 -0
  57. package/Tests/Models/NetworkDeviceMacAddressColumn.test.ts +294 -0
  58. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +129 -0
  59. package/Tests/Server/EnvironmentConfigValkey.test.ts +227 -0
  60. package/Tests/Server/Infrastructure/Postgres/AddMacAddressToNetworkDeviceMigration.test.ts +365 -0
  61. package/Tests/Server/Infrastructure/Postgres/CustomFieldValueMappingMigration.test.ts +62 -23
  62. package/Tests/Server/Infrastructure/ValkeyDeploymentConfig.test.ts +265 -0
  63. package/Tests/Server/Middleware/HttpMetricsMiddleware.test.ts +382 -0
  64. package/Tests/Server/Middleware/SCIMAuthorization.test.ts +525 -0
  65. package/Tests/Server/Services/DatabaseServiceAggregateBy.test.ts +291 -56
  66. package/Tests/Server/Services/DatabaseServiceSortTiebreaker.test.ts +151 -0
  67. package/Tests/Server/Services/InventoryItemManualCreate.test.ts +105 -0
  68. package/Tests/Server/Services/InventoryItemPromotionGate.test.ts +178 -0
  69. package/Tests/Server/Services/MonitorTemplateServiceCustomFieldSync.test.ts +502 -0
  70. package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +1133 -81
  71. package/Tests/Server/Services/NetworkDeviceDiscoveryScanRegistration.test.ts +520 -0
  72. package/Tests/Server/Services/NetworkDeviceMacAddressNormalization.test.ts +572 -0
  73. package/Tests/Server/Types/Database/InventoryStatusPostgres.test.ts +368 -0
  74. package/Tests/Server/Types/Database/Permissions/ReadBlockPermission.test.ts +261 -14
  75. package/Tests/Server/Types/Database/QueryUtilIncludesAnyOfGroups.test.ts +169 -0
  76. package/Tests/Server/Types/Database/QueryUtilIncludesAnyOfGroupsPostgres.test.ts +297 -0
  77. package/Tests/Server/Types/Markdown.test.ts +194 -1
  78. package/Tests/Server/Types/Workflow/Components/IncomingWebhookUtils.test.ts +279 -0
  79. package/Tests/Server/Types/Workflow/Components/TextToJson.test.ts +212 -0
  80. package/Tests/Server/Utils/CustomField/CustomFieldMappingRegistry.test.ts +436 -0
  81. package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +214 -0
  82. package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerBehaviour.test.ts +1 -1
  83. package/Tests/Server/Utils/EmailRollup/EmailRollupRenderer.test.ts +10 -3
  84. package/Tests/Server/Utils/EventLoop.test.ts +359 -0
  85. package/Tests/Server/Utils/JsonWebToken.test.ts +30 -0
  86. package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +242 -0
  87. package/Tests/Server/Utils/Monitor/Criteria/CompareCriteriaAggregation.test.ts +13 -7
  88. package/Tests/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.test.ts +29 -0
  89. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +30 -6
  90. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +246 -0
  91. package/Tests/Server/Utils/Monitor/MonitorCriteriaEmailEvalLogAgreement.test.ts +280 -0
  92. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +224 -10
  93. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +570 -0
  94. package/Tests/Server/Utils/Monitor/MonitorCriteriaExpectationBuilderUnits.test.ts +30 -5
  95. package/Tests/Server/Utils/Monitor/MonitorCriteriaMessageBuilderUnits.test.ts +68 -21
  96. package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +36 -19
  97. package/Tests/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.test.ts +573 -0
  98. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +317 -0
  99. package/Tests/Server/Utils/Telemetry/ContextSpanProcessor.test.ts +281 -0
  100. package/Tests/Server/Utils/Telemetry/InventoryDuplicateIdentity.test.ts +316 -0
  101. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +111 -8
  102. package/Tests/Types/Database/IncludesAnyOfGroups.test.ts +106 -0
  103. package/Tests/Types/JSONFunctions.test.ts +40 -0
  104. package/Tests/Types/NetworkAutomation/AutoImportRunMerge.test.ts +235 -0
  105. package/Tests/Types/NetworkAutomation/RuleRunResult.test.ts +66 -0
  106. package/Tests/Types/NetworkAutomation/RuleRunResultDescribe.test.ts +309 -1
  107. package/Tests/Types/Telemetry/InventoryLiveness.test.ts +99 -0
  108. package/Tests/UI/Components/CustomFields/CustomFieldsDetail.test.tsx +160 -0
  109. package/Tests/UI/ReactRouterSingletonBuild.test.ts +133 -0
  110. package/Tests/UI/Rum/ReplayHeader.test.tsx +10 -2
  111. package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +514 -0
  112. package/Tests/UI/Rum/ReplayTimeFormat.test.ts +94 -0
  113. package/Tests/UI/Rum/ReplayUi.test.tsx +559 -0
  114. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +5 -0
  115. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +1 -0
  116. package/Tests/Utils/Dashboard/Components/DashboardMonitorListComponent.test.ts +11 -1
  117. package/Tests/Utils/Dashboard/LabelVariable.test.ts +257 -0
  118. package/Tests/Utils/Dashboard/VariableUrlState.test.ts +144 -14
  119. package/Tests/Utils/MetricUnitUtil.test.ts +81 -0
  120. package/Tests/Utils/Monitor/DeviceMacLearningUtil.test.ts +497 -0
  121. package/Tests/Utils/Monitor/MetricValueFormatter.test.ts +546 -0
  122. package/Tests/Utils/Monitor/MonitorTemplateCustomFieldUtil.test.ts +218 -0
  123. package/Tests/Utils/Monitor/NetworkTopologyEndpointAdoption.test.ts +1755 -0
  124. package/Tests/Utils/NetworkAutomation/AutoImportRunChain.test.ts +320 -0
  125. package/Types/BaseDatabase/IncludesAnyOfGroups.ts +77 -0
  126. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
  127. package/Types/Dashboard/DashboardComponents/DashboardMonitorListComponent.ts +1 -0
  128. package/Types/Dashboard/DashboardVariable.ts +8 -0
  129. package/Types/JSON.ts +4 -0
  130. package/Types/JSONFunctions.ts +16 -0
  131. package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +13 -0
  132. package/Types/NetworkAutomation/RuleRunResult.ts +402 -13
  133. package/Types/SerializableObjectDictionary.ts +4 -0
  134. package/Types/Telemetry/InventoryLiveness.ts +56 -0
  135. package/UI/Components/CSVFileUpload/CSVFileUpload.tsx +9 -2
  136. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +46 -21
  137. package/UI/Components/Dashboard/DashboardVariableControl.tsx +285 -0
  138. package/UI/esbuild-config.js +6 -0
  139. package/Utils/Dashboard/Components/DashboardMonitorListComponent.ts +12 -1
  140. package/Utils/Dashboard/LabelVariable.ts +152 -0
  141. package/Utils/Dashboard/VariableUrlState.ts +27 -5
  142. package/Utils/MetricUnitUtil.ts +34 -0
  143. package/Utils/Monitor/DeviceMacLearningUtil.ts +177 -0
  144. package/Utils/Monitor/MetricValueFormatter.ts +342 -0
  145. package/Utils/Monitor/MonitorTemplateCustomFieldUtil.ts +124 -0
  146. package/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.ts +7 -2
  147. package/Utils/Monitor/NetworkTopologyUtil.ts +693 -0
  148. package/Utils/NetworkAutomation/AutoImportRunChain.ts +138 -0
  149. package/build/dist/Models/DatabaseModels/GlobalConfig.js +12 -12
  150. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  151. package/build/dist/Models/DatabaseModels/InventoryItem.js +34 -1
  152. package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -1
  153. package/build/dist/Models/DatabaseModels/NetworkDevice.js +128 -0
  154. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  155. package/build/dist/Server/EnvironmentConfig.js +44 -12
  156. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  157. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.js +14 -0
  158. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.js.map +1 -0
  159. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  160. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  161. package/build/dist/Server/Infrastructure/Redis.js +8 -8
  162. package/build/dist/Server/Infrastructure/Redis.js.map +1 -1
  163. package/build/dist/Server/Infrastructure/Status.js +10 -4
  164. package/build/dist/Server/Infrastructure/Status.js.map +1 -1
  165. package/build/dist/Server/Services/DatabaseService.js +58 -13
  166. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  167. package/build/dist/Server/Services/InventoryItemService.js +28 -1
  168. package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
  169. package/build/dist/Server/Services/MonitorTemplateService.js +116 -17
  170. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  171. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +270 -37
  172. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
  173. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +65 -0
  174. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
  175. package/build/dist/Server/Services/NetworkDeviceService.js +54 -0
  176. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  177. package/build/dist/Server/Services/UserNotificationRuleService.js +60 -25
  178. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  179. package/build/dist/Server/Types/Database/Permissions/ReadPermission.js +29 -4
  180. package/build/dist/Server/Types/Database/Permissions/ReadPermission.js.map +1 -1
  181. package/build/dist/Server/Types/Database/QueryUtil.js +36 -0
  182. package/build/dist/Server/Types/Database/QueryUtil.js.map +1 -1
  183. package/build/dist/Server/Types/Markdown.js +75 -0
  184. package/build/dist/Server/Types/Markdown.js.map +1 -1
  185. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +53 -18
  186. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -1
  187. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +10 -1
  188. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -1
  189. package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js +2 -2
  190. package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js.map +1 -1
  191. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +58 -35
  192. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
  193. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +29 -5
  194. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
  195. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +166 -35
  196. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  197. package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js +58 -9
  198. package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js.map +1 -1
  199. package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.js +9 -3
  200. package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.js.map +1 -1
  201. package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.js +26 -12
  202. package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.js.map +1 -1
  203. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +74 -33
  204. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  205. package/build/dist/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.js +114 -0
  206. package/build/dist/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.js.map +1 -0
  207. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +25 -0
  208. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
  209. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +9 -1
  210. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  211. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +70 -13
  212. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  213. package/build/dist/Types/BaseDatabase/IncludesAnyOfGroups.js +54 -0
  214. package/build/dist/Types/BaseDatabase/IncludesAnyOfGroups.js.map +1 -0
  215. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
  216. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  217. package/build/dist/Types/Dashboard/DashboardVariable.js +1 -0
  218. package/build/dist/Types/Dashboard/DashboardVariable.js.map +1 -1
  219. package/build/dist/Types/JSON.js +1 -0
  220. package/build/dist/Types/JSON.js.map +1 -1
  221. package/build/dist/Types/JSONFunctions.js +13 -0
  222. package/build/dist/Types/JSONFunctions.js.map +1 -1
  223. package/build/dist/Types/NetworkAutomation/RuleRunResult.js +291 -6
  224. package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -1
  225. package/build/dist/Types/SerializableObjectDictionary.js +4 -0
  226. package/build/dist/Types/SerializableObjectDictionary.js.map +1 -1
  227. package/build/dist/Types/Telemetry/InventoryLiveness.js +37 -0
  228. package/build/dist/Types/Telemetry/InventoryLiveness.js.map +1 -0
  229. package/build/dist/UI/Components/CSVFileUpload/CSVFileUpload.js +7 -0
  230. package/build/dist/UI/Components/CSVFileUpload/CSVFileUpload.js.map +1 -1
  231. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +9 -2
  232. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  233. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js +137 -0
  234. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js.map +1 -0
  235. package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js +9 -1
  236. package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js.map +1 -1
  237. package/build/dist/Utils/Dashboard/LabelVariable.js +96 -0
  238. package/build/dist/Utils/Dashboard/LabelVariable.js.map +1 -0
  239. package/build/dist/Utils/Dashboard/VariableUrlState.js +19 -4
  240. package/build/dist/Utils/Dashboard/VariableUrlState.js.map +1 -1
  241. package/build/dist/Utils/MetricUnitUtil.js +26 -0
  242. package/build/dist/Utils/MetricUnitUtil.js.map +1 -1
  243. package/build/dist/Utils/Monitor/DeviceMacLearningUtil.js +112 -0
  244. package/build/dist/Utils/Monitor/DeviceMacLearningUtil.js.map +1 -0
  245. package/build/dist/Utils/Monitor/MetricValueFormatter.js +276 -0
  246. package/build/dist/Utils/Monitor/MetricValueFormatter.js.map +1 -0
  247. package/build/dist/Utils/Monitor/MonitorTemplateCustomFieldUtil.js +101 -0
  248. package/build/dist/Utils/Monitor/MonitorTemplateCustomFieldUtil.js.map +1 -0
  249. package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js +7 -2
  250. package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js.map +1 -1
  251. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +484 -0
  252. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
  253. package/build/dist/Utils/NetworkAutomation/AutoImportRunChain.js +59 -0
  254. package/build/dist/Utils/NetworkAutomation/AutoImportRunChain.js.map +1 -0
  255. package/jest.config.json +2 -0
  256. package/package.json +1 -1
  257. package/test-setup.sh +9 -9
  258. package/tsconfig.json +51 -11
  259. package/Tests/App/Dashboard/NotificationEmailPreferences.test.tsx +0 -399
@@ -1,399 +0,0 @@
1
- import "@testing-library/jest-dom";
2
- import {
3
- act,
4
- cleanup,
5
- fireEvent,
6
- render,
7
- screen,
8
- waitFor,
9
- within,
10
- } from "@testing-library/react";
11
- import React from "react";
12
- import NotificationSettings from "../../../../App/FeatureSet/Dashboard/src/Pages/UserSettings/NotificationSettings";
13
- import EmailNoiseCard from "../../../../App/FeatureSet/Dashboard/src/Components/NotificationMethods/EmailNoiseCard";
14
- import UserNotificationSetting from "../../../Models/DatabaseModels/UserNotificationSetting";
15
- import UserNotificationEmailRollupSetting from "../../../Models/DatabaseModels/UserNotificationEmailRollupSetting";
16
- import Route from "../../../Types/API/Route";
17
- import HTTPResponse from "../../../Types/API/HTTPResponse";
18
- import HTTPErrorResponse from "../../../Types/API/HTTPErrorResponse";
19
- import Includes from "../../../Types/BaseDatabase/Includes";
20
- import Query from "../../../Types/BaseDatabase/Query";
21
- import NotificationSettingEventType from "../../../Types/NotificationSetting/NotificationSettingEventType";
22
- import { ROUTINE_EMAIL_EVENT_TYPES } from "../../../Types/NotificationSetting/RoutineEmailEvents";
23
- import ObjectID from "../../../Types/ObjectID";
24
- import API from "../../../UI/Utils/API/API";
25
- import ModelAPI from "../../../UI/Utils/ModelAPI/ModelAPI";
26
- import ProjectUtil from "../../../UI/Utils/Project";
27
- import User from "../../../UI/Utils/User";
28
-
29
- const PROJECT_ID: ObjectID = new ObjectID(
30
- "11111111-1111-4111-8111-111111111111",
31
- );
32
- const USER_ID: ObjectID = new ObjectID("22222222-2222-4222-8222-222222222222");
33
-
34
- describe("notification email preferences", () => {
35
- let rows: Array<UserNotificationSetting>;
36
- let post: jest.SpyInstance;
37
- let getList: jest.SpyInstance;
38
-
39
- beforeEach(() => {
40
- rows = Object.values(NotificationSettingEventType).map(
41
- (
42
- eventType: NotificationSettingEventType,
43
- index: number,
44
- ): UserNotificationSetting => {
45
- const row: UserNotificationSetting = new UserNotificationSetting();
46
- row._id = `33333333-3333-4333-8333-${String(index).padStart(12, "0")}`;
47
- row.projectId = PROJECT_ID;
48
- row.userId = USER_ID;
49
- row.eventType = eventType;
50
- row.alertByEmail = true;
51
- row.alertBySMS = true;
52
- return row;
53
- },
54
- );
55
- jest.spyOn(ProjectUtil, "getCurrentProjectId").mockReturnValue(PROJECT_ID);
56
- jest.spyOn(User, "getUserId").mockReturnValue(USER_ID);
57
- jest
58
- .spyOn(ModelAPI, "getCommonHeaders")
59
- .mockReturnValue({ tenantid: PROJECT_ID.toString() });
60
- jest
61
- .spyOn(API, "getFriendlyMessage")
62
- .mockImplementation((err: unknown): string => {
63
- return err instanceof Error || err instanceof HTTPErrorResponse
64
- ? err.message
65
- : "Request failed";
66
- });
67
- getList = jest
68
- .spyOn(ModelAPI, "getList")
69
- .mockImplementation(
70
- async (data: Parameters<typeof ModelAPI.getList>[0]): Promise<any> => {
71
- if (data.modelType === UserNotificationEmailRollupSetting) {
72
- return { data: [], count: 0 };
73
- }
74
- const query: Query<UserNotificationSetting> = data.query;
75
- const eventTypes: Array<string> = (query.eventType as Includes)
76
- .values as Array<string>;
77
- const settings: Array<UserNotificationSetting> = rows.filter(
78
- (row: UserNotificationSetting): boolean => {
79
- return (
80
- eventTypes.includes(row.eventType!) &&
81
- row.projectId?.toString() ===
82
- (query.projectId as ObjectID).toString() &&
83
- row.userId?.toString() === (query.userId as ObjectID).toString()
84
- );
85
- },
86
- );
87
- return {
88
- data: settings,
89
- count: settings.length,
90
- };
91
- },
92
- );
93
- post = jest
94
- .spyOn(API, "post")
95
- .mockImplementation(async (): Promise<any> => {
96
- rows = rows.map(
97
- (row: UserNotificationSetting): UserNotificationSetting => {
98
- const copy: UserNotificationSetting = Object.assign(
99
- new UserNotificationSetting(),
100
- row,
101
- );
102
- if (ROUTINE_EMAIL_EVENT_TYPES.includes(copy.eventType!)) {
103
- copy.alertByEmail = false;
104
- }
105
- return copy;
106
- },
107
- );
108
- return new HTTPResponse(200, { success: true }, {});
109
- });
110
- });
111
-
112
- afterEach(() => {
113
- cleanup();
114
- jest.restoreAllMocks();
115
- });
116
-
117
- function renderPage(): void {
118
- render(
119
- <NotificationSettings
120
- pageRoute={new Route("/user-settings/notification-settings")}
121
- currentProject={null}
122
- hasPaymentMethod={true}
123
- />,
124
- );
125
- }
126
-
127
- async function findEventRow(label: string): Promise<HTMLElement> {
128
- return (await screen.findByText(label)).closest("tr")!;
129
- }
130
-
131
- test("offers reduction without changing preferences on page load", async () => {
132
- renderPage();
133
- await findEventRow("Incident created");
134
- expect(
135
- screen.getByRole("button", { name: "Reduce routine emails" }),
136
- ).toBeEnabled();
137
- expect(screen.getByText(/Applies to you in this project/)).toBeVisible();
138
- expect(post).not.toHaveBeenCalled();
139
- });
140
-
141
- test("reloads preferences and clears the old success message when switching projects", async () => {
142
- const { rerender } = render(
143
- <NotificationSettings
144
- pageRoute={new Route("/user-settings/notification-settings")}
145
- currentProject={null}
146
- hasPaymentMethod={true}
147
- />,
148
- );
149
- await findEventRow("Incident created");
150
- fireEvent.click(
151
- screen.getByRole("button", { name: "Reduce routine emails" }),
152
- );
153
- await screen.findByRole("status");
154
- const otherProjectId: ObjectID = new ObjectID(
155
- "44444444-4444-4444-8444-444444444444",
156
- );
157
- jest
158
- .spyOn(ProjectUtil, "getCurrentProjectId")
159
- .mockReturnValue(otherProjectId);
160
- getList.mockClear();
161
- rerender(
162
- <NotificationSettings
163
- pageRoute={new Route("/user-settings/notification-settings")}
164
- currentProject={null}
165
- hasPaymentMethod={true}
166
- />,
167
- );
168
- const row: HTMLElement = await findEventRow("Incident created");
169
- expect(
170
- within(row).getByRole("switch", { name: /^Email:/ }),
171
- ).toHaveAttribute("aria-checked", "false");
172
- expect(
173
- screen.queryByText(
174
- "Routine emails turned off. Review or change individual preferences below.",
175
- ),
176
- ).not.toBeInTheDocument();
177
- expect(getList.mock.calls.length).toBeGreaterThan(0);
178
- for (const call of getList.mock.calls) {
179
- expect(call[0].query.projectId).toEqual(otherProjectId);
180
- expect(call[0].query.userId).toEqual(USER_ID);
181
- }
182
- });
183
-
184
- test("applies once, refreshes visible settings, and preserves urgent emails and SMS", async () => {
185
- renderPage();
186
- const before: HTMLElement = await findEventRow("Incident note posted");
187
- expect(
188
- within(before).getByRole("switch", { name: /^Email:/ }),
189
- ).toHaveAttribute("aria-checked", "true");
190
- fireEvent.click(
191
- screen.getByRole("button", { name: "Reduce routine emails" }),
192
- );
193
- await screen.findByRole("status");
194
- await waitFor(() => {
195
- const after: HTMLElement = screen
196
- .getByText("Incident note posted")
197
- .closest("tr")!;
198
- expect(
199
- within(after).getByRole("switch", { name: /^Email:/ }),
200
- ).toHaveAttribute("aria-checked", "false");
201
- expect(
202
- within(after).getByRole("switch", { name: /^SMS:/ }),
203
- ).toHaveAttribute("aria-checked", "true");
204
- });
205
- const urgent: HTMLElement = await findEventRow("Incident created");
206
- expect(
207
- within(urgent).getByRole("switch", { name: /^Email:/ }),
208
- ).toHaveAttribute("aria-checked", "true");
209
- expect(post).toHaveBeenCalledTimes(1);
210
- expect(post.mock.calls[0][0].url.toString()).toContain(
211
- "/user-notification-setting/reduce-routine-emails",
212
- );
213
- expect(post.mock.calls[0][0].data).toEqual({});
214
- expect(post.mock.calls[0][0].headers).toEqual({
215
- tenantid: PROJECT_ID.toString(),
216
- });
217
- });
218
-
219
- test("does not reenable an urgent email the user had already disabled", async () => {
220
- rows.find((row: UserNotificationSetting): boolean => {
221
- return (
222
- row.eventType ===
223
- NotificationSettingEventType.SEND_INCIDENT_CREATED_OWNER_NOTIFICATION
224
- );
225
- })!.alertByEmail = false;
226
- renderPage();
227
- await findEventRow("Incident created");
228
- fireEvent.click(
229
- screen.getByRole("button", { name: "Reduce routine emails" }),
230
- );
231
- await screen.findByRole("status");
232
- const urgent: HTMLElement = await findEventRow("Incident created");
233
- expect(
234
- within(urgent).getByRole("switch", { name: /^Email:/ }),
235
- ).toHaveAttribute("aria-checked", "false");
236
- });
237
-
238
- test("disables repeated submission and channel edits while the action is pending", async () => {
239
- let resolveRequest!: (response: HTTPResponse<any>) => void;
240
- post.mockReturnValue(
241
- new Promise<HTTPResponse<any>>(
242
- (resolve: (response: HTTPResponse<any>) => void): void => {
243
- resolveRequest = resolve;
244
- },
245
- ),
246
- );
247
- renderPage();
248
- const row: HTMLElement = await findEventRow("Incident note posted");
249
- fireEvent.click(
250
- screen.getByRole("button", { name: "Reduce routine emails" }),
251
- );
252
- const saving: HTMLElement = screen.getByRole("button", {
253
- name: "Saving email preferences…",
254
- });
255
- expect(saving).toBeDisabled();
256
- fireEvent.click(saving);
257
- expect(within(row).getByRole("switch", { name: /^Email:/ })).toBeDisabled();
258
- expect(post).toHaveBeenCalledTimes(1);
259
- expect(screen.queryByRole("status")).not.toBeInTheDocument();
260
- await act(async () => {
261
- resolveRequest(new HTTPResponse(200, { success: true }, {}));
262
- });
263
- await screen.findByRole("status");
264
- });
265
-
266
- test("waits for an earlier individual save before allowing the routine preset", async () => {
267
- rows.find((row: UserNotificationSetting): boolean => {
268
- return (
269
- row.eventType ===
270
- NotificationSettingEventType.SEND_INCIDENT_NOTE_POSTED_OWNER_NOTIFICATION
271
- );
272
- })!.alertByEmail = false;
273
- let finishSave!: (response: HTTPResponse<any>) => void;
274
- const save: jest.SpyInstance = jest
275
- .spyOn(ModelAPI, "updateById")
276
- .mockReturnValue(
277
- new Promise<HTTPResponse<any>>(
278
- (resolve: (response: HTTPResponse<any>) => void): void => {
279
- finishSave = resolve;
280
- },
281
- ),
282
- );
283
- renderPage();
284
- const row: HTMLElement = await findEventRow("Incident note posted");
285
- fireEvent.click(within(row).getByRole("switch", { name: /^Email:/ }));
286
- expect(save).toHaveBeenCalledTimes(1);
287
- const reduce: HTMLElement = screen.getByRole("button", {
288
- name: "Reduce routine emails",
289
- });
290
- expect(reduce).toBeDisabled();
291
- fireEvent.click(reduce);
292
- expect(post).not.toHaveBeenCalled();
293
- await act(async () => {
294
- finishSave(new HTTPResponse(200, {}, {}));
295
- });
296
- await waitFor(() => {
297
- expect(reduce).toBeEnabled();
298
- });
299
- fireEvent.click(reduce);
300
- await screen.findByRole("status");
301
- const updated: HTMLElement = await findEventRow("Incident note posted");
302
- expect(
303
- within(updated).getByRole("switch", { name: /^Email:/ }),
304
- ).toHaveAttribute("aria-checked", "false");
305
- expect(post).toHaveBeenCalledTimes(1);
306
- });
307
-
308
- test.each(["resolved HTTP error", "network rejection"])(
309
- "shows %s, keeps settings, and allows retry",
310
- async (kind: string) => {
311
- if (kind === "resolved HTTP error") {
312
- post.mockResolvedValueOnce(
313
- new HTTPErrorResponse(
314
- 503,
315
- { message: "Preferences could not be saved" },
316
- {},
317
- ),
318
- );
319
- } else {
320
- post.mockRejectedValueOnce(new Error("Preferences could not be saved"));
321
- }
322
- renderPage();
323
- await findEventRow("Incident note posted");
324
- const readsBefore: number = getList.mock.calls.length;
325
- fireEvent.click(
326
- screen.getByRole("button", { name: "Reduce routine emails" }),
327
- );
328
- expect(await screen.findByRole("alert")).toHaveTextContent(
329
- "Preferences could not be saved",
330
- );
331
- expect(screen.queryByRole("status")).not.toBeInTheDocument();
332
- expect(getList).toHaveBeenCalledTimes(readsBefore);
333
- const row: HTMLElement = await findEventRow("Incident note posted");
334
- expect(
335
- within(row).getByRole("switch", { name: /^Email:/ }),
336
- ).toHaveAttribute("aria-checked", "true");
337
- fireEvent.click(
338
- screen.getByRole("button", { name: "Reduce routine emails" }),
339
- );
340
- await screen.findByRole("status");
341
- expect(screen.queryByRole("alert")).not.toBeInTheDocument();
342
- },
343
- );
344
-
345
- test("every event has one reachable preference and reads are scoped to the current member", async () => {
346
- renderPage();
347
- await findEventRow("Assigned to an incident");
348
- for (const tabName of [
349
- "Alerts",
350
- "Monitoring",
351
- "Status Pages",
352
- "Scheduled Maintenance",
353
- "On-Call",
354
- ]) {
355
- fireEvent.click(screen.getByRole("tab", { name: tabName }));
356
- await waitFor(() => {
357
- expect(screen.getAllByRole("switch").length).toBeGreaterThan(1);
358
- });
359
- if (tabName === "Monitoring") {
360
- await screen.findByText("AI agent status changed");
361
- await screen.findByText("Added as AI agent owner");
362
- }
363
- }
364
- const eventReads: Array<any> = getList.mock.calls
365
- .map((call: Array<any>): any => {
366
- return call[0];
367
- })
368
- .filter((call: any): boolean => {
369
- return call.modelType === UserNotificationSetting;
370
- });
371
- const queriedTypes: Array<string> = eventReads.flatMap(
372
- (call: any): Array<string> => {
373
- return call.query.eventType.values;
374
- },
375
- );
376
- expect(queriedTypes.sort()).toEqual(
377
- Object.values(NotificationSettingEventType).sort(),
378
- );
379
- for (const call of eventReads) {
380
- expect(call.query.projectId).toEqual(PROJECT_ID);
381
- expect(call.query.userId).toEqual(USER_ID);
382
- }
383
- });
384
-
385
- test("the reduction card can be used again after an individual preference change", async () => {
386
- const apply: jest.Mock = jest.fn().mockResolvedValue(undefined);
387
- render(<EmailNoiseCard onApply={apply} />);
388
- fireEvent.click(
389
- screen.getByRole("button", { name: "Reduce routine emails" }),
390
- );
391
- await screen.findByRole("status");
392
- fireEvent.click(
393
- screen.getByRole("button", { name: "Reduce routine emails" }),
394
- );
395
- await waitFor(() => {
396
- expect(apply).toHaveBeenCalledTimes(2);
397
- });
398
- });
399
- });