@oneuptime/common 12.0.29 → 12.0.31

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 (664) hide show
  1. package/Models/AnalyticsModels/SecurityEvent.ts +48 -16
  2. package/Models/AnalyticsModels/ThreatIntelIndicator.ts +10 -5
  3. package/Models/DatabaseModels/AlertSeverity.ts +44 -0
  4. package/Models/DatabaseModels/DetectionRule.ts +16 -5
  5. package/Models/DatabaseModels/GoogleSecOpsConnection.ts +14 -5
  6. package/Models/DatabaseModels/IncidentSeverity.ts +44 -0
  7. package/Models/DatabaseModels/Index.ts +13 -0
  8. package/Models/DatabaseModels/NetworkDevice.ts +113 -3
  9. package/Models/DatabaseModels/NetworkDeviceAutoImportRule.ts +88 -0
  10. package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +22 -0
  11. package/Models/DatabaseModels/NetworkDeviceOidTemplate.ts +499 -0
  12. package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +43 -0
  13. package/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.ts +662 -0
  14. package/Models/DatabaseModels/ProjectOnCallCalendarFeed.ts +584 -0
  15. package/Models/DatabaseModels/ThreatIntelFeed.ts +16 -5
  16. package/Models/DatabaseModels/UserOnCallCalendarFeed.ts +546 -0
  17. package/Models/DatabaseModels/UserOnCallShiftReminder.ts +231 -0
  18. package/Models/DatabaseModels/UserOnCallShiftReminderLog.ts +331 -0
  19. package/Server/API/GlobalConfigAPI.ts +362 -183
  20. package/Server/API/OnCallCalendarAPI.ts +2194 -0
  21. package/Server/API/TelemetryAPI.ts +42 -4
  22. package/Server/EnvironmentConfig.ts +250 -0
  23. package/Server/Infrastructure/OnCallCalendarFeedCache.ts +1050 -0
  24. package/Server/Infrastructure/Postgres/SchemaMigrations/1790400000000-AddOnCallCalendarFeeds.ts +253 -0
  25. package/Server/Infrastructure/Postgres/SchemaMigrations/1790500000000-AddNetworkDeviceOidTemplate.ts +59 -0
  26. package/Server/Infrastructure/Postgres/SchemaMigrations/1790600000000-AddAutoImportRuleOidTemplate.ts +25 -0
  27. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  28. package/Server/Middleware/MultipartFormData.ts +118 -32
  29. package/Server/Middleware/OnCallCalendarFeedRateLimit.ts +487 -0
  30. package/Server/Services/AlertEpisodeLabelRuleEngineService.ts +9 -1
  31. package/Server/Services/AlertEpisodeOnCallRuleEngineService.ts +9 -1
  32. package/Server/Services/AlertEpisodeOwnerRuleEngineService.ts +9 -1
  33. package/Server/Services/AlertEpisodePrivacyRuleEngineService.ts +9 -1
  34. package/Server/Services/AlertGroupingEngineService.ts +9 -1
  35. package/Server/Services/AlertLabelRuleEngineService.ts +9 -1
  36. package/Server/Services/AlertOnCallRuleEngineService.ts +9 -1
  37. package/Server/Services/AlertOwnerRuleEngineService.ts +9 -1
  38. package/Server/Services/AlertPrivacyRuleEngineService.ts +9 -1
  39. package/Server/Services/AlertReminderRuleService.ts +9 -1
  40. package/Server/Services/AutoRemediationRuleEngineService.ts +9 -1
  41. package/Server/Services/CephClusterLabelRuleEngineService.ts +9 -1
  42. package/Server/Services/CephClusterOwnerRuleEngineService.ts +9 -1
  43. package/Server/Services/CloudResourceLabelRuleEngineService.ts +9 -1
  44. package/Server/Services/CloudResourceOwnerRuleEngineService.ts +9 -1
  45. package/Server/Services/DashboardLabelRuleEngineService.ts +9 -1
  46. package/Server/Services/DashboardOwnerRuleEngineService.ts +9 -1
  47. package/Server/Services/DockerHostLabelRuleEngineService.ts +9 -1
  48. package/Server/Services/DockerHostOwnerRuleEngineService.ts +9 -1
  49. package/Server/Services/DockerSwarmClusterLabelRuleEngineService.ts +9 -1
  50. package/Server/Services/DockerSwarmClusterOwnerRuleEngineService.ts +9 -1
  51. package/Server/Services/HostLabelRuleEngineService.ts +9 -1
  52. package/Server/Services/HostOwnerRuleEngineService.ts +9 -1
  53. package/Server/Services/IncidentEpisodeLabelRuleEngineService.ts +9 -1
  54. package/Server/Services/IncidentEpisodeOnCallRuleEngineService.ts +9 -1
  55. package/Server/Services/IncidentEpisodeOwnerRuleEngineService.ts +9 -1
  56. package/Server/Services/IncidentEpisodePrivacyRuleEngineService.ts +9 -1
  57. package/Server/Services/IncidentGroupingEngineService.ts +9 -1
  58. package/Server/Services/IncidentLabelRuleEngineService.ts +9 -1
  59. package/Server/Services/IncidentOnCallRuleEngineService.ts +9 -1
  60. package/Server/Services/IncidentOwnerRuleEngineService.ts +9 -1
  61. package/Server/Services/IncidentPrivacyRuleEngineService.ts +9 -1
  62. package/Server/Services/IncidentReminderRuleService.ts +9 -1
  63. package/Server/Services/IncidentSlaRuleService.ts +9 -1
  64. package/Server/Services/IncomingCallPolicyLabelRuleEngineService.ts +9 -1
  65. package/Server/Services/IncomingCallPolicyOwnerRuleEngineService.ts +9 -1
  66. package/Server/Services/Index.ts +14 -0
  67. package/Server/Services/IoTFleetLabelRuleEngineService.ts +9 -1
  68. package/Server/Services/IoTFleetOwnerRuleEngineService.ts +9 -1
  69. package/Server/Services/KubernetesClusterLabelRuleEngineService.ts +9 -1
  70. package/Server/Services/KubernetesClusterOwnerRuleEngineService.ts +9 -1
  71. package/Server/Services/KubernetesResourceService.ts +5 -1
  72. package/Server/Services/MonitorLabelRuleEngineService.ts +9 -1
  73. package/Server/Services/MonitorOwnerRuleEngineService.ts +9 -1
  74. package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +35 -0
  75. package/Server/Services/NetworkDeviceAutoImportRuleService.ts +96 -0
  76. package/Server/Services/NetworkDeviceLabelRuleEngineService.ts +9 -1
  77. package/Server/Services/NetworkDeviceOidTemplateService.ts +201 -0
  78. package/Server/Services/NetworkDeviceOwnerRuleEngineService.ts +9 -1
  79. package/Server/Services/NetworkDeviceService.ts +209 -1
  80. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +32 -0
  81. package/Server/Services/OnCallDutyPolicyLabelRuleEngineService.ts +9 -1
  82. package/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.ts +9 -1
  83. package/Server/Services/OnCallDutyPolicyScheduleCalendarFeedService.ts +260 -0
  84. package/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.ts +9 -1
  85. package/Server/Services/OnCallDutyPolicyScheduleLayerService.ts +70 -3
  86. package/Server/Services/OnCallDutyPolicyScheduleLayerUserService.ts +138 -3
  87. package/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.ts +9 -1
  88. package/Server/Services/OnCallDutyPolicyScheduleService.ts +1465 -134
  89. package/Server/Services/OnCallDutyPolicyUserOverrideService.ts +167 -31
  90. package/Server/Services/PodmanHostLabelRuleEngineService.ts +9 -1
  91. package/Server/Services/PodmanHostOwnerRuleEngineService.ts +9 -1
  92. package/Server/Services/ProjectOnCallCalendarFeedService.ts +192 -0
  93. package/Server/Services/ProxmoxClusterLabelRuleEngineService.ts +9 -1
  94. package/Server/Services/ProxmoxClusterOwnerRuleEngineService.ts +9 -1
  95. package/Server/Services/RumApplicationLabelRuleEngineService.ts +9 -1
  96. package/Server/Services/RumApplicationOwnerRuleEngineService.ts +9 -1
  97. package/Server/Services/RunbookLabelRuleEngineService.ts +9 -1
  98. package/Server/Services/RunbookOwnerRuleEngineService.ts +9 -1
  99. package/Server/Services/RunbookRuleEngineService.ts +9 -1
  100. package/Server/Services/ScheduledMaintenanceLabelRuleEngineService.ts +9 -1
  101. package/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.ts +9 -1
  102. package/Server/Services/ScheduledMaintenanceReminderRuleService.ts +9 -1
  103. package/Server/Services/ServerlessFunctionLabelRuleEngineService.ts +9 -1
  104. package/Server/Services/ServerlessFunctionOwnerRuleEngineService.ts +9 -1
  105. package/Server/Services/ServiceLabelRuleEngineService.ts +9 -1
  106. package/Server/Services/ServiceOwnerRuleEngineService.ts +9 -1
  107. package/Server/Services/StatusPageLabelRuleEngineService.ts +9 -1
  108. package/Server/Services/StatusPageMonitorRuleEngineService.ts +7 -0
  109. package/Server/Services/StatusPageOwnerRuleEngineService.ts +9 -1
  110. package/Server/Services/TeamMemberService.ts +489 -1
  111. package/Server/Services/TeamService.ts +50 -0
  112. package/Server/Services/TelemetrySourceMapService.ts +266 -23
  113. package/Server/Services/UserNotificationSettingService.ts +44 -1
  114. package/Server/Services/UserOnCallCalendarFeedService.ts +159 -0
  115. package/Server/Services/UserOnCallShiftReminderLogService.ts +151 -0
  116. package/Server/Services/UserOnCallShiftReminderService.ts +112 -0
  117. package/Server/Services/UserService.ts +27 -0
  118. package/Server/Services/WorkflowLabelRuleEngineService.ts +9 -1
  119. package/Server/Services/WorkflowOwnerRuleEngineService.ts +9 -1
  120. package/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.ts +186 -0
  121. package/Server/Utils/Monitor/CapturedMetricAttributeUtil.ts +245 -0
  122. package/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.ts +21 -2
  123. package/Server/Utils/Monitor/MonitorMetricUtil.ts +55 -21
  124. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +32 -0
  125. package/Server/Utils/Monitor/NetworkDeviceMetricUtil.ts +15 -2
  126. package/Server/Utils/Monitor/NetworkDeviceWalkUtil.ts +18 -1
  127. package/Server/Utils/Monitor/NetworkInventoryUtil.ts +10 -0
  128. package/Server/Utils/Monitor/SeriesResourceLabels.ts +34 -0
  129. package/Server/Utils/OnCall/CalendarFeedToken.ts +241 -0
  130. package/Server/Utils/OnCall/OnCallCalendarFeedRenderer.ts +1488 -0
  131. package/Server/Utils/OnCall/OnCallCalendarFeedUrls.ts +230 -0
  132. package/Server/Utils/OnCall/OnCallShiftChangeListeners.ts +227 -0
  133. package/Server/Utils/OnCall/OnCallShiftMaterializer.ts +1127 -0
  134. package/Server/Utils/OnCall/OnCallShiftReminderListener.ts +174 -0
  135. package/Server/Utils/OnCall/OnCallShiftReminderRunner.ts +2584 -0
  136. package/Server/Utils/Response.ts +134 -0
  137. package/Server/Utils/Rules/RuleEngineRuleRead.ts +48 -0
  138. package/Server/Utils/StartServer.ts +10 -0
  139. package/Server/Utils/Telemetry/AppMetrics.ts +51 -0
  140. package/Server/Utils/Telemetry/ExceptionSanitizer.ts +14 -1
  141. package/Server/Utils/Telemetry/LogExceptionExtractor.ts +14 -2
  142. package/Server/Utils/Telemetry/SourceMapResolver.ts +16 -5
  143. package/Server/Utils/Telemetry/StackTraceParser.ts +399 -0
  144. package/Tests/App/Dashboard/EventOverlayHook.test.tsx +125 -0
  145. package/Tests/App/Dashboard/HeaderImageAssets.test.tsx +93 -0
  146. package/Tests/App/Dashboard/OnCallCalendarFeedLinks.test.tsx +229 -0
  147. package/Tests/App/Dashboard/OnCallCalendarFeedPlanGate.test.tsx +489 -0
  148. package/Tests/App/Dashboard/OnCallCalendarFeedSideMenus.test.tsx +149 -0
  149. package/Tests/App/Dashboard/OnCallCalendarFeedUtil.test.ts +855 -0
  150. package/Tests/App/Dashboard/OnCallPersonalCalendarFeed.test.tsx +1209 -0
  151. package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +3 -3
  152. package/Tests/App/Dashboard/OnCallSharedCalendarFeedCard.test.tsx +1019 -0
  153. package/Tests/App/Dashboard/OnCallShiftRemindersAndUpcomingShifts.test.tsx +655 -0
  154. package/Tests/App/Dashboard/UserElementEmail.test.tsx +12 -4
  155. package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +1 -1
  156. package/Tests/App/Dashboard/UserSettingsSetupChecklistModel.test.ts +73 -0
  157. package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +14 -0
  158. package/Tests/Models/DatabaseModels/ModelRegistryInvariants.test.ts +469 -0
  159. package/Tests/Models/OnCallCalendarFeedModels.test.ts +642 -0
  160. package/Tests/Models/OnCallDutyPolicyScheduleCalendarFeed.test.ts +288 -0
  161. package/Tests/Models/OnCallDutyPolicyScheduleShiftConfigVersion.test.ts +88 -0
  162. package/Tests/Models/SecurityDomainAccessControl.test.ts +429 -0
  163. package/Tests/Models/UserOnCallShiftReminderModels.test.ts +368 -0
  164. package/Tests/Server/API/GlobalConfigLicense.test.ts +657 -0
  165. package/Tests/Server/API/Helpers.ts +8 -0
  166. package/Tests/Server/API/OnCallCalendarAPI.test.ts +4461 -0
  167. package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +84 -6
  168. package/Tests/Server/API/TelemetryResolveStackTraceAPI.test.ts +805 -0
  169. package/Tests/Server/EnvironmentConfigOnCallCalendarFeed.test.ts +357 -0
  170. package/Tests/Server/Infrastructure/OnCallCalendarFeedCache.test.ts +1589 -0
  171. package/Tests/Server/Infrastructure/Postgres/AddOnCallCalendarFeedsMigration.test.ts +394 -0
  172. package/Tests/Server/Middleware/IdentityRateLimit.test.ts +1144 -0
  173. package/Tests/Server/Middleware/MultipartFormData.test.ts +618 -3
  174. package/Tests/Server/Middleware/OnCallCalendarFeedRateLimit.test.ts +1365 -0
  175. package/Tests/Server/Services/FeedRetentionConsistency.test.ts +70 -0
  176. package/Tests/Server/Services/MonitorLabelRuleEngineScale.test.ts +520 -0
  177. package/Tests/Server/Services/NetworkDeviceOidTemplateService.test.ts +332 -0
  178. package/Tests/Server/Services/NetworkDeviceOidTemplateWriteGuards.test.ts +409 -0
  179. package/Tests/Server/Services/OnCallCalendarFeedServices.test.ts +1103 -0
  180. package/Tests/Server/Services/OnCallDutyPolicyScheduleResolver.test.ts +1037 -0
  181. package/Tests/Server/Services/OnCallShiftConfigPropagation.test.ts +1270 -0
  182. package/Tests/Server/Services/RuleEngineRuleFetchLimit.test.ts +239 -0
  183. package/Tests/Server/Services/TeamDeleteMemberCleanup.test.ts +156 -0
  184. package/Tests/Server/Services/TeamMemberOnCallCleanup.test.ts +755 -0
  185. package/Tests/Server/Services/TelemetrySourceMapService.test.ts +1210 -2
  186. package/Tests/Server/Services/UserNotificationSettingShiftReminderDefaults.test.ts +267 -0
  187. package/Tests/Server/Services/UserOnCallShiftReminderServices.test.ts +583 -0
  188. package/Tests/Server/Services/UserServiceSeatLimit.test.ts +182 -0
  189. package/Tests/Server/Types/AnalyticsDatabase/SecurityEventModelPermission.test.ts +255 -0
  190. package/Tests/Server/Types/Database/Permissions/OnCallCalendarFeedPermissions.test.ts +254 -0
  191. package/Tests/Server/Types/Database/Permissions/ReadBlockPermission.test.ts +212 -0
  192. package/Tests/Server/Types/Database/Permissions/SecurityRoleAccess.test.ts +413 -0
  193. package/Tests/Server/Utils/CodeRepository.test.ts +689 -0
  194. package/Tests/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.test.ts +495 -0
  195. package/Tests/Server/Utils/ExceptionSanitizer.test.ts +77 -0
  196. package/Tests/Server/Utils/Monitor/CapturedMetricAttributeUtil.test.ts +284 -0
  197. package/Tests/Server/Utils/Monitor/MonitorCustomMetricAttributes.test.ts +503 -0
  198. package/Tests/Server/Utils/Monitor/MonitorTemplatePrototypePollution.test.ts +106 -0
  199. package/Tests/Server/Utils/Monitor/NetworkDeviceWalkLogWritePath.test.ts +33 -4
  200. package/Tests/Server/Utils/Monitor/NetworkDeviceWalkUtil.test.ts +14 -4
  201. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +44 -0
  202. package/Tests/Server/Utils/OnCall/CalendarFeedToken.test.ts +467 -0
  203. package/Tests/Server/Utils/OnCall/OnCallCalendarFeedRenderer.test.ts +2142 -0
  204. package/Tests/Server/Utils/OnCall/OnCallCalendarFeedUrls.test.ts +278 -0
  205. package/Tests/Server/Utils/OnCall/OnCallResolverTestHarness.ts +440 -0
  206. package/Tests/Server/Utils/OnCall/OnCallShiftChangeListeners.test.ts +314 -0
  207. package/Tests/Server/Utils/OnCall/OnCallShiftMaterializer.test.ts +1502 -0
  208. package/Tests/Server/Utils/OnCall/OnCallShiftReminderListener.test.ts +491 -0
  209. package/Tests/Server/Utils/OnCall/OnCallShiftReminderRunner.test.ts +3088 -0
  210. package/Tests/Server/Utils/OnCall/OnCallShiftReminderTestHarness.ts +910 -0
  211. package/Tests/Server/Utils/OpenAPISpec.test.ts +536 -0
  212. package/Tests/Server/Utils/ResponseSendCalendarResponse.test.ts +561 -0
  213. package/Tests/Server/Utils/Rules/RuleEngineRuleRead.test.ts +115 -0
  214. package/Tests/Server/Utils/StackTraceParser.test.ts +610 -0
  215. package/Tests/Server/Utils/StartServerEncryptionSecretWarning.test.ts +64 -0
  216. package/Tests/Server/Utils/Telemetry/AppMetrics.test.ts +29 -0
  217. package/Tests/Server/Utils/Telemetry/LogExceptionExtractor.test.ts +0 -0
  218. package/Tests/Server/Utils/Telemetry/SourceMapLimits.test.ts +656 -0
  219. package/Tests/Server/Utils/Telemetry/SourceMapResolver.test.ts +100 -0
  220. package/Tests/Types/Calendar/ICalendar.test.ts +488 -0
  221. package/Tests/Types/Monitor/SnmpMonitor/SnmpOidListUtil.test.ts +454 -0
  222. package/Tests/Types/NotificationSetting/NotificationSettingEventType.test.ts +53 -0
  223. package/Tests/Types/OnCallDutyPolicy/CalendarFeedTestFixtures.ts +310 -0
  224. package/Tests/Types/OnCallDutyPolicy/CalendarFeedWindow.test.ts +205 -0
  225. package/Tests/Types/OnCallDutyPolicy/LayerUtilLayerMetaAndIterationCap.test.ts +458 -0
  226. package/Tests/Types/OnCallDutyPolicy/MaterializedShift.test.ts +197 -0
  227. package/Tests/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.test.ts +1986 -0
  228. package/Tests/Types/OnCallDutyPolicy/ScheduleShiftUtilGroupKey.test.ts +678 -0
  229. package/Tests/Types/OnCallDutyPolicy/ShiftSeamUtil.test.ts +417 -0
  230. package/Tests/Types/SecurityRolePermissions.test.ts +193 -0
  231. package/Tests/UI/Components/Charts/AnnotationLayout.test.ts +773 -0
  232. package/Tests/UI/Components/Charts/ChartAnnotationRail.test.tsx +789 -0
  233. package/Tests/UI/Components/Charts/ChartBucketIdentity.test.ts +254 -0
  234. package/Tests/UI/Components/Charts/TimeAnnotation.test.ts +471 -0
  235. package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +405 -0
  236. package/Tests/UI/Components/ErrorMessageRefreshControl.test.tsx +83 -0
  237. package/Tests/UI/Components/MarkdownConverters.test.ts +686 -0
  238. package/Tests/UI/Components/ModelTable/BaseModelTableDeleteAndEmptyStates.test.tsx +341 -0
  239. package/Tests/UI/Components/OrderedStatesList.test.tsx +5 -5
  240. package/Tests/UI/Components/PendingProjectInvitations.test.tsx +1 -1
  241. package/Tests/UI/Components/TableSortAndMobileColumns.test.tsx +195 -0
  242. package/Tests/UI/Components/XAxis.test.ts +270 -5
  243. package/Tests/UI/Components/XAxisDaylightSaving.test.ts +113 -0
  244. package/Tests/UI/Styles/ChartFocusRing.test.ts +153 -0
  245. package/Tests/UI/Styles/ChartFocusRingCoverage.test.tsx +377 -0
  246. package/Tests/UI/Styles/ThemeStylesheet.ts +162 -0
  247. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
  248. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
  249. package/Tests/Utils/EnterpriseLicenseSeats.test.ts +507 -0
  250. package/Tests/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.test.ts +951 -1
  251. package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +238 -0
  252. package/Tests/Utils/NetworkDiscovery/DiscoveryReverseDnsChain.test.ts +1056 -0
  253. package/Tests/Utils/NetworkDiscovery/ReverseDnsNameUtil.test.ts +882 -0
  254. package/Types/Calendar/ICalendar.ts +442 -0
  255. package/Types/Date.ts +39 -0
  256. package/Types/Email/EmailTemplateType.ts +2 -0
  257. package/Types/Monitor/SnmpMonitor/SnmpOidListUtil.ts +333 -0
  258. package/Types/NotificationSetting/NotificationSettingEventType.ts +4 -0
  259. package/Types/OnCallDutyPolicy/CalendarFeedWindow.ts +140 -0
  260. package/Types/OnCallDutyPolicy/Layer.ts +190 -35
  261. package/Types/OnCallDutyPolicy/MaterializedShift.ts +274 -0
  262. package/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.ts +1415 -0
  263. package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +159 -4
  264. package/Types/OnCallDutyPolicy/ShiftSeamUtil.ts +106 -0
  265. package/Types/Permission.ts +96 -0
  266. package/Types/Rum/SessionReplay.ts +21 -0
  267. package/Types/Telemetry/SourceMap.ts +8 -0
  268. package/UI/Components/Charts/ChartLibrary/Annotations/AnnotationHoverCard.tsx +176 -0
  269. package/UI/Components/Charts/ChartLibrary/Annotations/AnnotationLayout.ts +745 -0
  270. package/UI/Components/Charts/ChartLibrary/Annotations/ChartAnnotationLayer.tsx +576 -0
  271. package/UI/Components/Charts/ChartLibrary/Annotations/UseChartAnnotations.tsx +204 -0
  272. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +491 -544
  273. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +324 -372
  274. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +487 -540
  275. package/UI/Components/Charts/ChartLibrary/Types/FormattedReferenceRegion.ts +7 -3
  276. package/UI/Components/Charts/ChartLibrary/Types/FormattedTimeReferenceLine.ts +14 -3
  277. package/UI/Components/Charts/Types/ChartEventKind.ts +13 -0
  278. package/UI/Components/Charts/Types/ReferenceRegionProps.ts +6 -0
  279. package/UI/Components/Charts/Types/TimeReferenceLineProps.ts +20 -3
  280. package/UI/Components/Charts/Utils/TimeAnnotation.ts +69 -35
  281. package/UI/Components/Charts/Utils/XAxis.ts +125 -18
  282. package/UI/Components/EditionLabel/EditionLabel.tsx +280 -26
  283. package/UI/Components/ErrorMessage/ErrorMessage.tsx +21 -10
  284. package/UI/Components/Feed/FeedItem.tsx +4 -5
  285. package/UI/Components/Header/HeaderIconDropdownButton.tsx +1 -2
  286. package/UI/Components/Header/UserProfile/UserProfile.tsx +1 -2
  287. package/UI/Components/Image/Image.tsx +20 -3
  288. package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
  289. package/UI/Components/MemberRoleAssignment/MemberRoleAssignment.tsx +1 -2
  290. package/UI/Components/ModelTable/BaseModelTable.tsx +150 -24
  291. package/UI/Components/Table/TableHeader.tsx +64 -48
  292. package/UI/Components/Table/TableRow.tsx +13 -3
  293. package/UI/Styles/Theme.css +38 -0
  294. package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +271 -0
  295. package/Utils/NetworkDiscovery/AutoImportRuleMatcher.ts +6 -0
  296. package/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.ts +141 -10
  297. package/Utils/NetworkDiscovery/DiscoveredHostUtil.ts +55 -3
  298. package/Utils/NetworkDiscovery/ReverseDnsNameUtil.ts +151 -0
  299. package/Utils/Rules/RuleEngineLimits.ts +32 -0
  300. package/build/dist/Models/AnalyticsModels/SecurityEvent.js +48 -16
  301. package/build/dist/Models/AnalyticsModels/SecurityEvent.js.map +1 -1
  302. package/build/dist/Models/AnalyticsModels/ThreatIntelIndicator.js +10 -5
  303. package/build/dist/Models/AnalyticsModels/ThreatIntelIndicator.js.map +1 -1
  304. package/build/dist/Models/DatabaseModels/AlertSeverity.js +46 -1
  305. package/build/dist/Models/DatabaseModels/AlertSeverity.js.map +1 -1
  306. package/build/dist/Models/DatabaseModels/DetectionRule.js +16 -5
  307. package/build/dist/Models/DatabaseModels/DetectionRule.js.map +1 -1
  308. package/build/dist/Models/DatabaseModels/GoogleSecOpsConnection.js +14 -5
  309. package/build/dist/Models/DatabaseModels/GoogleSecOpsConnection.js.map +1 -1
  310. package/build/dist/Models/DatabaseModels/IncidentSeverity.js +46 -1
  311. package/build/dist/Models/DatabaseModels/IncidentSeverity.js.map +1 -1
  312. package/build/dist/Models/DatabaseModels/Index.js +13 -0
  313. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  314. package/build/dist/Models/DatabaseModels/NetworkDevice.js +115 -3
  315. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  316. package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js +88 -0
  317. package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js.map +1 -1
  318. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
  319. package/build/dist/Models/DatabaseModels/NetworkDeviceOidTemplate.js +519 -0
  320. package/build/dist/Models/DatabaseModels/NetworkDeviceOidTemplate.js.map +1 -0
  321. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +44 -0
  322. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
  323. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js +699 -0
  324. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js.map +1 -0
  325. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js +620 -0
  326. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js.map +1 -0
  327. package/build/dist/Models/DatabaseModels/ThreatIntelFeed.js +16 -5
  328. package/build/dist/Models/DatabaseModels/ThreatIntelFeed.js.map +1 -1
  329. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js +585 -0
  330. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js.map +1 -0
  331. package/build/dist/Models/DatabaseModels/UserOnCallShiftReminder.js +247 -0
  332. package/build/dist/Models/DatabaseModels/UserOnCallShiftReminder.js.map +1 -0
  333. package/build/dist/Models/DatabaseModels/UserOnCallShiftReminderLog.js +355 -0
  334. package/build/dist/Models/DatabaseModels/UserOnCallShiftReminderLog.js.map +1 -0
  335. package/build/dist/Server/API/GlobalConfigAPI.js +293 -176
  336. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  337. package/build/dist/Server/API/OnCallCalendarAPI.js +1371 -0
  338. package/build/dist/Server/API/OnCallCalendarAPI.js.map +1 -0
  339. package/build/dist/Server/API/TelemetryAPI.js +31 -3
  340. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  341. package/build/dist/Server/EnvironmentConfig.js +185 -0
  342. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  343. package/build/dist/Server/Infrastructure/OnCallCalendarFeedCache.js +663 -0
  344. package/build/dist/Server/Infrastructure/OnCallCalendarFeedCache.js.map +1 -0
  345. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790400000000-AddOnCallCalendarFeeds.js +103 -0
  346. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790400000000-AddOnCallCalendarFeeds.js.map +1 -0
  347. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790500000000-AddNetworkDeviceOidTemplate.js +26 -0
  348. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790500000000-AddNetworkDeviceOidTemplate.js.map +1 -0
  349. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790600000000-AddAutoImportRuleOidTemplate.js +14 -0
  350. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790600000000-AddAutoImportRuleOidTemplate.js.map +1 -0
  351. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  352. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  353. package/build/dist/Server/Middleware/MultipartFormData.js +93 -28
  354. package/build/dist/Server/Middleware/MultipartFormData.js.map +1 -1
  355. package/build/dist/Server/Middleware/OnCallCalendarFeedRateLimit.js +342 -0
  356. package/build/dist/Server/Middleware/OnCallCalendarFeedRateLimit.js.map +1 -0
  357. package/build/dist/Server/Services/AlertEpisodeLabelRuleEngineService.js +8 -1
  358. package/build/dist/Server/Services/AlertEpisodeLabelRuleEngineService.js.map +1 -1
  359. package/build/dist/Server/Services/AlertEpisodeOnCallRuleEngineService.js +8 -1
  360. package/build/dist/Server/Services/AlertEpisodeOnCallRuleEngineService.js.map +1 -1
  361. package/build/dist/Server/Services/AlertEpisodeOwnerRuleEngineService.js +8 -1
  362. package/build/dist/Server/Services/AlertEpisodeOwnerRuleEngineService.js.map +1 -1
  363. package/build/dist/Server/Services/AlertEpisodePrivacyRuleEngineService.js +8 -1
  364. package/build/dist/Server/Services/AlertEpisodePrivacyRuleEngineService.js.map +1 -1
  365. package/build/dist/Server/Services/AlertGroupingEngineService.js +8 -1
  366. package/build/dist/Server/Services/AlertGroupingEngineService.js.map +1 -1
  367. package/build/dist/Server/Services/AlertLabelRuleEngineService.js +8 -1
  368. package/build/dist/Server/Services/AlertLabelRuleEngineService.js.map +1 -1
  369. package/build/dist/Server/Services/AlertOnCallRuleEngineService.js +8 -1
  370. package/build/dist/Server/Services/AlertOnCallRuleEngineService.js.map +1 -1
  371. package/build/dist/Server/Services/AlertOwnerRuleEngineService.js +8 -1
  372. package/build/dist/Server/Services/AlertOwnerRuleEngineService.js.map +1 -1
  373. package/build/dist/Server/Services/AlertPrivacyRuleEngineService.js +8 -1
  374. package/build/dist/Server/Services/AlertPrivacyRuleEngineService.js.map +1 -1
  375. package/build/dist/Server/Services/AlertReminderRuleService.js +8 -1
  376. package/build/dist/Server/Services/AlertReminderRuleService.js.map +1 -1
  377. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +8 -1
  378. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
  379. package/build/dist/Server/Services/CephClusterLabelRuleEngineService.js +8 -1
  380. package/build/dist/Server/Services/CephClusterLabelRuleEngineService.js.map +1 -1
  381. package/build/dist/Server/Services/CephClusterOwnerRuleEngineService.js +8 -1
  382. package/build/dist/Server/Services/CephClusterOwnerRuleEngineService.js.map +1 -1
  383. package/build/dist/Server/Services/CloudResourceLabelRuleEngineService.js +8 -1
  384. package/build/dist/Server/Services/CloudResourceLabelRuleEngineService.js.map +1 -1
  385. package/build/dist/Server/Services/CloudResourceOwnerRuleEngineService.js +8 -1
  386. package/build/dist/Server/Services/CloudResourceOwnerRuleEngineService.js.map +1 -1
  387. package/build/dist/Server/Services/DashboardLabelRuleEngineService.js +8 -1
  388. package/build/dist/Server/Services/DashboardLabelRuleEngineService.js.map +1 -1
  389. package/build/dist/Server/Services/DashboardOwnerRuleEngineService.js +8 -1
  390. package/build/dist/Server/Services/DashboardOwnerRuleEngineService.js.map +1 -1
  391. package/build/dist/Server/Services/DockerHostLabelRuleEngineService.js +8 -1
  392. package/build/dist/Server/Services/DockerHostLabelRuleEngineService.js.map +1 -1
  393. package/build/dist/Server/Services/DockerHostOwnerRuleEngineService.js +8 -1
  394. package/build/dist/Server/Services/DockerHostOwnerRuleEngineService.js.map +1 -1
  395. package/build/dist/Server/Services/DockerSwarmClusterLabelRuleEngineService.js +8 -1
  396. package/build/dist/Server/Services/DockerSwarmClusterLabelRuleEngineService.js.map +1 -1
  397. package/build/dist/Server/Services/DockerSwarmClusterOwnerRuleEngineService.js +8 -1
  398. package/build/dist/Server/Services/DockerSwarmClusterOwnerRuleEngineService.js.map +1 -1
  399. package/build/dist/Server/Services/HostLabelRuleEngineService.js +8 -1
  400. package/build/dist/Server/Services/HostLabelRuleEngineService.js.map +1 -1
  401. package/build/dist/Server/Services/HostOwnerRuleEngineService.js +8 -1
  402. package/build/dist/Server/Services/HostOwnerRuleEngineService.js.map +1 -1
  403. package/build/dist/Server/Services/IncidentEpisodeLabelRuleEngineService.js +8 -1
  404. package/build/dist/Server/Services/IncidentEpisodeLabelRuleEngineService.js.map +1 -1
  405. package/build/dist/Server/Services/IncidentEpisodeOnCallRuleEngineService.js +8 -1
  406. package/build/dist/Server/Services/IncidentEpisodeOnCallRuleEngineService.js.map +1 -1
  407. package/build/dist/Server/Services/IncidentEpisodeOwnerRuleEngineService.js +8 -1
  408. package/build/dist/Server/Services/IncidentEpisodeOwnerRuleEngineService.js.map +1 -1
  409. package/build/dist/Server/Services/IncidentEpisodePrivacyRuleEngineService.js +8 -1
  410. package/build/dist/Server/Services/IncidentEpisodePrivacyRuleEngineService.js.map +1 -1
  411. package/build/dist/Server/Services/IncidentGroupingEngineService.js +8 -1
  412. package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
  413. package/build/dist/Server/Services/IncidentLabelRuleEngineService.js +8 -1
  414. package/build/dist/Server/Services/IncidentLabelRuleEngineService.js.map +1 -1
  415. package/build/dist/Server/Services/IncidentOnCallRuleEngineService.js +8 -1
  416. package/build/dist/Server/Services/IncidentOnCallRuleEngineService.js.map +1 -1
  417. package/build/dist/Server/Services/IncidentOwnerRuleEngineService.js +8 -1
  418. package/build/dist/Server/Services/IncidentOwnerRuleEngineService.js.map +1 -1
  419. package/build/dist/Server/Services/IncidentPrivacyRuleEngineService.js +8 -1
  420. package/build/dist/Server/Services/IncidentPrivacyRuleEngineService.js.map +1 -1
  421. package/build/dist/Server/Services/IncidentReminderRuleService.js +8 -1
  422. package/build/dist/Server/Services/IncidentReminderRuleService.js.map +1 -1
  423. package/build/dist/Server/Services/IncidentSlaRuleService.js +8 -1
  424. package/build/dist/Server/Services/IncidentSlaRuleService.js.map +1 -1
  425. package/build/dist/Server/Services/IncomingCallPolicyLabelRuleEngineService.js +8 -1
  426. package/build/dist/Server/Services/IncomingCallPolicyLabelRuleEngineService.js.map +1 -1
  427. package/build/dist/Server/Services/IncomingCallPolicyOwnerRuleEngineService.js +8 -1
  428. package/build/dist/Server/Services/IncomingCallPolicyOwnerRuleEngineService.js.map +1 -1
  429. package/build/dist/Server/Services/Index.js +14 -0
  430. package/build/dist/Server/Services/Index.js.map +1 -1
  431. package/build/dist/Server/Services/IoTFleetLabelRuleEngineService.js +8 -1
  432. package/build/dist/Server/Services/IoTFleetLabelRuleEngineService.js.map +1 -1
  433. package/build/dist/Server/Services/IoTFleetOwnerRuleEngineService.js +8 -1
  434. package/build/dist/Server/Services/IoTFleetOwnerRuleEngineService.js.map +1 -1
  435. package/build/dist/Server/Services/KubernetesClusterLabelRuleEngineService.js +8 -1
  436. package/build/dist/Server/Services/KubernetesClusterLabelRuleEngineService.js.map +1 -1
  437. package/build/dist/Server/Services/KubernetesClusterOwnerRuleEngineService.js +8 -1
  438. package/build/dist/Server/Services/KubernetesClusterOwnerRuleEngineService.js.map +1 -1
  439. package/build/dist/Server/Services/KubernetesResourceService.js +5 -1
  440. package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -1
  441. package/build/dist/Server/Services/MonitorLabelRuleEngineService.js +8 -1
  442. package/build/dist/Server/Services/MonitorLabelRuleEngineService.js.map +1 -1
  443. package/build/dist/Server/Services/MonitorOwnerRuleEngineService.js +8 -1
  444. package/build/dist/Server/Services/MonitorOwnerRuleEngineService.js.map +1 -1
  445. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +30 -21
  446. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
  447. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js +64 -0
  448. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js.map +1 -1
  449. package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js +8 -1
  450. package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js.map +1 -1
  451. package/build/dist/Server/Services/NetworkDeviceOidTemplateService.js +180 -0
  452. package/build/dist/Server/Services/NetworkDeviceOidTemplateService.js.map +1 -0
  453. package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js +8 -1
  454. package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js.map +1 -1
  455. package/build/dist/Server/Services/NetworkDeviceService.js +154 -1
  456. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  457. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +20 -0
  458. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  459. package/build/dist/Server/Services/OnCallDutyPolicyLabelRuleEngineService.js +8 -1
  460. package/build/dist/Server/Services/OnCallDutyPolicyLabelRuleEngineService.js.map +1 -1
  461. package/build/dist/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.js +8 -1
  462. package/build/dist/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.js.map +1 -1
  463. package/build/dist/Server/Services/OnCallDutyPolicyScheduleCalendarFeedService.js +219 -0
  464. package/build/dist/Server/Services/OnCallDutyPolicyScheduleCalendarFeedService.js.map +1 -0
  465. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.js +8 -1
  466. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.js.map +1 -1
  467. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerService.js +47 -3
  468. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerService.js.map +1 -1
  469. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerUserService.js +112 -3
  470. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerUserService.js.map +1 -1
  471. package/build/dist/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.js +8 -1
  472. package/build/dist/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.js.map +1 -1
  473. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +955 -9
  474. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
  475. package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js +93 -6
  476. package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js.map +1 -1
  477. package/build/dist/Server/Services/PodmanHostLabelRuleEngineService.js +8 -1
  478. package/build/dist/Server/Services/PodmanHostLabelRuleEngineService.js.map +1 -1
  479. package/build/dist/Server/Services/PodmanHostOwnerRuleEngineService.js +8 -1
  480. package/build/dist/Server/Services/PodmanHostOwnerRuleEngineService.js.map +1 -1
  481. package/build/dist/Server/Services/ProjectOnCallCalendarFeedService.js +157 -0
  482. package/build/dist/Server/Services/ProjectOnCallCalendarFeedService.js.map +1 -0
  483. package/build/dist/Server/Services/ProxmoxClusterLabelRuleEngineService.js +8 -1
  484. package/build/dist/Server/Services/ProxmoxClusterLabelRuleEngineService.js.map +1 -1
  485. package/build/dist/Server/Services/ProxmoxClusterOwnerRuleEngineService.js +8 -1
  486. package/build/dist/Server/Services/ProxmoxClusterOwnerRuleEngineService.js.map +1 -1
  487. package/build/dist/Server/Services/RumApplicationLabelRuleEngineService.js +8 -1
  488. package/build/dist/Server/Services/RumApplicationLabelRuleEngineService.js.map +1 -1
  489. package/build/dist/Server/Services/RumApplicationOwnerRuleEngineService.js +8 -1
  490. package/build/dist/Server/Services/RumApplicationOwnerRuleEngineService.js.map +1 -1
  491. package/build/dist/Server/Services/RunbookLabelRuleEngineService.js +8 -1
  492. package/build/dist/Server/Services/RunbookLabelRuleEngineService.js.map +1 -1
  493. package/build/dist/Server/Services/RunbookOwnerRuleEngineService.js +8 -1
  494. package/build/dist/Server/Services/RunbookOwnerRuleEngineService.js.map +1 -1
  495. package/build/dist/Server/Services/RunbookRuleEngineService.js +8 -1
  496. package/build/dist/Server/Services/RunbookRuleEngineService.js.map +1 -1
  497. package/build/dist/Server/Services/ScheduledMaintenanceLabelRuleEngineService.js +8 -1
  498. package/build/dist/Server/Services/ScheduledMaintenanceLabelRuleEngineService.js.map +1 -1
  499. package/build/dist/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.js +8 -1
  500. package/build/dist/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.js.map +1 -1
  501. package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js +8 -1
  502. package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js.map +1 -1
  503. package/build/dist/Server/Services/ServerlessFunctionLabelRuleEngineService.js +8 -1
  504. package/build/dist/Server/Services/ServerlessFunctionLabelRuleEngineService.js.map +1 -1
  505. package/build/dist/Server/Services/ServerlessFunctionOwnerRuleEngineService.js +8 -1
  506. package/build/dist/Server/Services/ServerlessFunctionOwnerRuleEngineService.js.map +1 -1
  507. package/build/dist/Server/Services/ServiceLabelRuleEngineService.js +8 -1
  508. package/build/dist/Server/Services/ServiceLabelRuleEngineService.js.map +1 -1
  509. package/build/dist/Server/Services/ServiceOwnerRuleEngineService.js +8 -1
  510. package/build/dist/Server/Services/ServiceOwnerRuleEngineService.js.map +1 -1
  511. package/build/dist/Server/Services/StatusPageLabelRuleEngineService.js +8 -1
  512. package/build/dist/Server/Services/StatusPageLabelRuleEngineService.js.map +1 -1
  513. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +6 -0
  514. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -1
  515. package/build/dist/Server/Services/StatusPageOwnerRuleEngineService.js +8 -1
  516. package/build/dist/Server/Services/StatusPageOwnerRuleEngineService.js.map +1 -1
  517. package/build/dist/Server/Services/TeamMemberService.js +396 -1
  518. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  519. package/build/dist/Server/Services/TeamService.js +43 -0
  520. package/build/dist/Server/Services/TeamService.js.map +1 -1
  521. package/build/dist/Server/Services/TelemetrySourceMapService.js +213 -19
  522. package/build/dist/Server/Services/TelemetrySourceMapService.js.map +1 -1
  523. package/build/dist/Server/Services/UserNotificationSettingService.js +26 -1
  524. package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
  525. package/build/dist/Server/Services/UserOnCallCalendarFeedService.js +140 -0
  526. package/build/dist/Server/Services/UserOnCallCalendarFeedService.js.map +1 -0
  527. package/build/dist/Server/Services/UserOnCallShiftReminderLogService.js +116 -0
  528. package/build/dist/Server/Services/UserOnCallShiftReminderLogService.js.map +1 -0
  529. package/build/dist/Server/Services/UserOnCallShiftReminderService.js +92 -0
  530. package/build/dist/Server/Services/UserOnCallShiftReminderService.js.map +1 -0
  531. package/build/dist/Server/Services/UserService.js +25 -0
  532. package/build/dist/Server/Services/UserService.js.map +1 -1
  533. package/build/dist/Server/Services/WorkflowLabelRuleEngineService.js +8 -1
  534. package/build/dist/Server/Services/WorkflowLabelRuleEngineService.js.map +1 -1
  535. package/build/dist/Server/Services/WorkflowOwnerRuleEngineService.js +8 -1
  536. package/build/dist/Server/Services/WorkflowOwnerRuleEngineService.js.map +1 -1
  537. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js +145 -0
  538. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js.map +1 -0
  539. package/build/dist/Server/Utils/Monitor/CapturedMetricAttributeUtil.js +201 -0
  540. package/build/dist/Server/Utils/Monitor/CapturedMetricAttributeUtil.js.map +1 -0
  541. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js +19 -2
  542. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js.map +1 -1
  543. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +42 -23
  544. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  545. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +29 -0
  546. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  547. package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js +11 -2
  548. package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js.map +1 -1
  549. package/build/dist/Server/Utils/Monitor/NetworkDeviceWalkUtil.js +16 -1
  550. package/build/dist/Server/Utils/Monitor/NetworkDeviceWalkUtil.js.map +1 -1
  551. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +10 -0
  552. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
  553. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +33 -0
  554. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
  555. package/build/dist/Server/Utils/OnCall/CalendarFeedToken.js +161 -0
  556. package/build/dist/Server/Utils/OnCall/CalendarFeedToken.js.map +1 -0
  557. package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedRenderer.js +983 -0
  558. package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedRenderer.js.map +1 -0
  559. package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedUrls.js +155 -0
  560. package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedUrls.js.map +1 -0
  561. package/build/dist/Server/Utils/OnCall/OnCallShiftChangeListeners.js +145 -0
  562. package/build/dist/Server/Utils/OnCall/OnCallShiftChangeListeners.js.map +1 -0
  563. package/build/dist/Server/Utils/OnCall/OnCallShiftMaterializer.js +752 -0
  564. package/build/dist/Server/Utils/OnCall/OnCallShiftMaterializer.js.map +1 -0
  565. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderListener.js +92 -0
  566. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderListener.js.map +1 -0
  567. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js +1786 -0
  568. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js.map +1 -0
  569. package/build/dist/Server/Utils/Response.js +97 -0
  570. package/build/dist/Server/Utils/Response.js.map +1 -1
  571. package/build/dist/Server/Utils/Rules/RuleEngineRuleRead.js +37 -0
  572. package/build/dist/Server/Utils/Rules/RuleEngineRuleRead.js.map +1 -0
  573. package/build/dist/Server/Utils/StartServer.js +9 -1
  574. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  575. package/build/dist/Server/Utils/Telemetry/AppMetrics.js +41 -0
  576. package/build/dist/Server/Utils/Telemetry/AppMetrics.js.map +1 -1
  577. package/build/dist/Server/Utils/Telemetry/ExceptionSanitizer.js +12 -1
  578. package/build/dist/Server/Utils/Telemetry/ExceptionSanitizer.js.map +1 -1
  579. package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js +14 -2
  580. package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js.map +1 -1
  581. package/build/dist/Server/Utils/Telemetry/SourceMapResolver.js +15 -5
  582. package/build/dist/Server/Utils/Telemetry/SourceMapResolver.js.map +1 -1
  583. package/build/dist/Server/Utils/Telemetry/StackTraceParser.js +307 -0
  584. package/build/dist/Server/Utils/Telemetry/StackTraceParser.js.map +1 -1
  585. package/build/dist/Types/Calendar/ICalendar.js +290 -0
  586. package/build/dist/Types/Calendar/ICalendar.js.map +1 -0
  587. package/build/dist/Types/Date.js +27 -0
  588. package/build/dist/Types/Date.js.map +1 -1
  589. package/build/dist/Types/Email/EmailTemplateType.js +2 -0
  590. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  591. package/build/dist/Types/Monitor/SnmpMonitor/SnmpOidListUtil.js +253 -0
  592. package/build/dist/Types/Monitor/SnmpMonitor/SnmpOidListUtil.js.map +1 -0
  593. package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js +3 -0
  594. package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js.map +1 -1
  595. package/build/dist/Types/OnCallDutyPolicy/CalendarFeedWindow.js +97 -0
  596. package/build/dist/Types/OnCallDutyPolicy/CalendarFeedWindow.js.map +1 -0
  597. package/build/dist/Types/OnCallDutyPolicy/Layer.js +113 -19
  598. package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
  599. package/build/dist/Types/OnCallDutyPolicy/MaterializedShift.js +134 -0
  600. package/build/dist/Types/OnCallDutyPolicy/MaterializedShift.js.map +1 -0
  601. package/build/dist/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.js +800 -0
  602. package/build/dist/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.js.map +1 -0
  603. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +97 -3
  604. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -1
  605. package/build/dist/Types/OnCallDutyPolicy/ShiftSeamUtil.js +77 -0
  606. package/build/dist/Types/OnCallDutyPolicy/ShiftSeamUtil.js.map +1 -0
  607. package/build/dist/Types/Permission.js +86 -0
  608. package/build/dist/Types/Permission.js.map +1 -1
  609. package/build/dist/Types/Rum/SessionReplay.js +19 -0
  610. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  611. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/AnnotationHoverCard.js +83 -0
  612. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/AnnotationHoverCard.js.map +1 -0
  613. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/AnnotationLayout.js +531 -0
  614. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/AnnotationLayout.js.map +1 -0
  615. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/ChartAnnotationLayer.js +264 -0
  616. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/ChartAnnotationLayer.js.map +1 -0
  617. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/UseChartAnnotations.js +122 -0
  618. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/UseChartAnnotations.js.map +1 -0
  619. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +213 -233
  620. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  621. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +139 -152
  622. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  623. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +212 -232
  624. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  625. package/build/dist/UI/Components/Charts/Types/ChartEventKind.js +14 -0
  626. package/build/dist/UI/Components/Charts/Types/ChartEventKind.js.map +1 -0
  627. package/build/dist/UI/Components/Charts/Utils/TimeAnnotation.js +57 -28
  628. package/build/dist/UI/Components/Charts/Utils/TimeAnnotation.js.map +1 -1
  629. package/build/dist/UI/Components/Charts/Utils/XAxis.js +103 -17
  630. package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
  631. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +198 -23
  632. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  633. package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js +15 -5
  634. package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js.map +1 -1
  635. package/build/dist/UI/Components/Feed/FeedItem.js +4 -5
  636. package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
  637. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +1 -2
  638. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  639. package/build/dist/UI/Components/Header/UserProfile/UserProfile.js.map +1 -1
  640. package/build/dist/UI/Components/Image/Image.js +6 -1
  641. package/build/dist/UI/Components/Image/Image.js.map +1 -1
  642. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
  643. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
  644. package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js +1 -2
  645. package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js.map +1 -1
  646. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +104 -23
  647. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  648. package/build/dist/UI/Components/Table/TableHeader.js +20 -22
  649. package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
  650. package/build/dist/UI/Components/Table/TableRow.js +13 -4
  651. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  652. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +153 -0
  653. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +1 -0
  654. package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js.map +1 -1
  655. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js +127 -7
  656. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js.map +1 -1
  657. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js +50 -3
  658. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js.map +1 -1
  659. package/build/dist/Utils/NetworkDiscovery/ReverseDnsNameUtil.js +132 -0
  660. package/build/dist/Utils/NetworkDiscovery/ReverseDnsNameUtil.js.map +1 -0
  661. package/build/dist/Utils/Rules/RuleEngineLimits.js +31 -0
  662. package/build/dist/Utils/Rules/RuleEngineLimits.js.map +1 -0
  663. package/jest.config.json +1 -0
  664. package/package.json +2 -2
@@ -0,0 +1,1056 @@
1
+ import {
2
+ DiscoveredDeviceScanSource,
3
+ buildDeviceName,
4
+ buildFallbackDeviceName,
5
+ buildNetworkDeviceFromDiscoveredHost,
6
+ getDiscoveredHostDisplayName,
7
+ } from "../../../Utils/NetworkDiscovery/DiscoveredDeviceBuilder";
8
+ import { normalizeDiscoveredHosts } from "../../../Utils/NetworkDiscovery/DiscoveredHostUtil";
9
+ import NetworkDevice from "../../../Models/DatabaseModels/NetworkDevice";
10
+ import { DiscoveredNetworkDevice } from "../../../Models/DatabaseModels/NetworkDeviceDiscoveryScan";
11
+ import NetworkDeviceMonitoringMethod from "../../../Types/NetworkDevice/NetworkDeviceMonitoringMethod";
12
+ import ObjectID from "../../../Types/ObjectID";
13
+ import { describe, expect, it } from "@jest/globals";
14
+
15
+ /*
16
+ * The WHOLE journey a PTR record makes, end to end, in one place.
17
+ *
18
+ * Every other suite around OneUptime issue #3529 tests one link:
19
+ * ReverseDnsNameUtil pins what a name may contain, DiscoveredHostUtil pins
20
+ * what normalisation does to a row, DiscoveredDeviceBuilder pins which field
21
+ * becomes which device column. Each of those can be green while the chain
22
+ * they form is broken, because the interesting failures live in the JOINTS —
23
+ * a value that survives normalisation but is re-read differently by the
24
+ * builder, a name one module accepts at its own ceiling and the next must
25
+ * cut down, or a row two layers deliberately disagree about.
26
+ *
27
+ * So this file runs the real path a resolved hostname actually travels:
28
+ *
29
+ * probe payload
30
+ * -> stored jsonb (modelled honestly as JSON.parse(JSON.stringify(...)),
31
+ * because `discoveredDevices` is written VERBATIM from the probe's
32
+ * payload by ProbeIngest/DiscoveryScan.ts — no schema, no coercion,
33
+ * so the column is exactly a JSON round trip of whatever was sent)
34
+ * -> normalizeDiscoveredHosts
35
+ * -> getDiscoveredHostDisplayName / buildDeviceName
36
+ * -> buildNetworkDeviceFromDiscoveredHost
37
+ *
38
+ * Nothing here is mocked. The point is that these five steps agree, and a
39
+ * double or a stub would be agreeing with itself.
40
+ *
41
+ * The JSON round trip is not ceremony. It is what erases `undefined`-valued
42
+ * keys, and what leaves numbers, nulls, objects and arrays sitting in fields
43
+ * the TypeScript interface declares as `string | undefined` — every hostile
44
+ * shape below reaches the readers only because the column cannot refuse it.
45
+ *
46
+ * SCOPE NOTE, so no comment here over-claims: the Dashboard's Discovery page
47
+ * is NOT imported by this file, and nothing below observes what the Review
48
+ * dialog renders. What is pinned is that the functions the dialog and the
49
+ * import loop both call return the same answers on the same row. Where a
50
+ * comment mentions the dialog it is explaining WHY a case matters, not
51
+ * claiming the dialog is under test.
52
+ */
53
+
54
+ const PROJECT_ID: ObjectID = new ObjectID(
55
+ "33333333-3333-4333-8333-333333333333",
56
+ );
57
+
58
+ /*
59
+ * A scan with no credentials at all — the shape a ping-only sweep really has.
60
+ *
61
+ * The reporter's hosts answer ICMP and nothing else, so they import down the
62
+ * Monitor branch of the builder, which returns before it ever asks for a
63
+ * credential set. Supplying a rich SNMP fixture here would only invite a
64
+ * reader to think credentials were part of what these tests pin; they are
65
+ * pinned in DiscoveredDeviceBuilder.test.ts and AutoImportScanCredentialSelect
66
+ * .test.ts instead.
67
+ */
68
+ const PING_ONLY_SCAN: DiscoveredDeviceScanSource = {};
69
+
70
+ /*
71
+ * The two ceilings this chain has to reconcile, written as literals rather
72
+ * than imported from the modules under test.
73
+ *
74
+ * ReverseDnsNameUtil accepts a presentation-form DNS name up to 253
75
+ * characters; DiscoveredDeviceBuilder clamps a device name to 80 because the
76
+ * slug is slugify(name) plus eleven characters into its own varchar(100).
77
+ * Importing MAX_DEVICE_NAME_LENGTH and asserting against it would make the
78
+ * length tests below survive someone raising the constant to 253 — which is
79
+ * exactly the change that starts failing device creates with a slug-length
80
+ * error. The numbers are the contract, so the numbers are what is asserted.
81
+ */
82
+ const MAX_DNS_NAME_LENGTH: number = 253;
83
+ const MAX_NAME_LENGTH: number = 80;
84
+
85
+ /*
86
+ * A PTR name that is perfectly legal DNS and too long to be a device name:
87
+ * one label at RFC 1035's 63-octet ceiling, in a three-label zone. 85
88
+ * characters — under 253, over 80 — so it is accepted whole by one module and
89
+ * cut down by the next. Long labels like this are ordinary on estates that
90
+ * encode rack, unit and role into the hostname.
91
+ */
92
+ const LONG_LABEL: string = `hq-fileserver-${"a".repeat(49)}`;
93
+ const LONG_PTR_NAME: string = `${LONG_LABEL}.dc14.corp.example.net`;
94
+
95
+ /**
96
+ * The payload as it comes back OUT of the jsonb column.
97
+ *
98
+ * Typed `Array<unknown>` on the way in on purpose: the probe payloads below
99
+ * deliberately hold values the `DiscoveredNetworkDevice` interface forbids,
100
+ * and pretending otherwise would be testing a payload the column cannot
101
+ * actually contain. The cast on the way out is exactly the lie the real code
102
+ * lives with — `getDiscoveredHosts` checks that the VALUE is an array and
103
+ * hands the elements on as hosts without looking at them.
104
+ */
105
+ function storedScanResults(
106
+ payload: Array<unknown>,
107
+ ): Array<DiscoveredNetworkDevice> {
108
+ return JSON.parse(JSON.stringify(payload)) as Array<DiscoveredNetworkDevice>;
109
+ }
110
+
111
+ /** Store the payload and normalise it, exactly as every reader does. */
112
+ function hostsFromPayload(
113
+ payload: Array<unknown>,
114
+ ): Array<DiscoveredNetworkDevice> {
115
+ return normalizeDiscoveredHosts(storedScanResults(payload));
116
+ }
117
+
118
+ /** Store the payload, normalise it, and name every surviving row. */
119
+ function namesFromPayload(payload: Array<unknown>): Array<string> {
120
+ return hostsFromPayload(payload).map(
121
+ (host: DiscoveredNetworkDevice): string => {
122
+ return buildDeviceName(host);
123
+ },
124
+ );
125
+ }
126
+
127
+ /** Store the payload, normalise it, and build the devices it imports as. */
128
+ function devicesFromPayload(payload: Array<unknown>): Array<NetworkDevice> {
129
+ return hostsFromPayload(payload).map(
130
+ (host: DiscoveredNetworkDevice): NetworkDevice => {
131
+ return buildNetworkDeviceFromDiscoveredHost({
132
+ projectId: PROJECT_ID,
133
+ host: host,
134
+ scan: PING_ONLY_SCAN,
135
+ });
136
+ },
137
+ );
138
+ }
139
+
140
+ /** The names of a built batch, which is what an operator ticks. */
141
+ function deviceNames(devices: Array<NetworkDevice>): Array<string | undefined> {
142
+ return devices.map((device: NetworkDevice): string | undefined => {
143
+ return device.name;
144
+ });
145
+ }
146
+
147
+ /** The addresses of a built batch, which is what the poller dials. */
148
+ function deviceHostnames(
149
+ devices: Array<NetworkDevice>,
150
+ ): Array<string | undefined> {
151
+ return devices.map((device: NetworkDevice): string | undefined => {
152
+ return device.hostname;
153
+ });
154
+ }
155
+
156
+ describe("Reverse DNS, probe payload to created NetworkDevice", () => {
157
+ it("names the reporter's four ping-only hosts by PTR and still addresses them by IP", () => {
158
+ /*
159
+ * Issue #3529 as filed. Four hosts on 10.18.166.0/24 that answer ping and
160
+ * have nothing readable over SNMP, on an estate that publishes a DNS
161
+ * record for each. Before the feature the name line was
162
+ * `sysName || ipAddress`, and with no sysName that is four bare
163
+ * addresses — the exact screenshot in the issue.
164
+ *
165
+ * .55's PTR carries the trailing root label. Resolvers differ on whether
166
+ * they include it, so the fully qualified spelling is in the payload
167
+ * rather than in a unit test of the normaliser alone: if the root-dot
168
+ * strip were ever dropped, THIS is where an operator would see it, as a
169
+ * device named "hq-camera-11.corp.example.net." with a stray dot.
170
+ */
171
+ const payload: Array<unknown> = [
172
+ {
173
+ ipAddress: "10.18.166.51",
174
+ snmpReachable: false,
175
+ dnsHostname: "hq-fileserver.corp.example.net",
176
+ },
177
+ {
178
+ ipAddress: "10.18.166.53",
179
+ snmpReachable: false,
180
+ dnsHostname: "hq-printer-02.corp.example.net",
181
+ },
182
+ // No PTR record at all: this one must still read as its address.
183
+ { ipAddress: "10.18.166.54", snmpReachable: false },
184
+ {
185
+ ipAddress: "10.18.166.55",
186
+ snmpReachable: false,
187
+ dnsHostname: "hq-camera-11.corp.example.net.",
188
+ },
189
+ ];
190
+
191
+ const devices: Array<NetworkDevice> = devicesFromPayload(payload);
192
+
193
+ expect(deviceNames(devices)).toEqual([
194
+ "hq-fileserver.corp.example.net",
195
+ "hq-printer-02.corp.example.net",
196
+ "10.18.166.54",
197
+ "hq-camera-11.corp.example.net",
198
+ ]);
199
+
200
+ /*
201
+ * The other half of what the issue asked for, in its own words: "retain
202
+ * the IP address as the address/IP field". `hostname` is what the SNMP
203
+ * poller dials, what a trap's source address is correlated against, and
204
+ * the dedup key NetworkDeviceService.getRegisteredHostnames matches a
205
+ * later scan's results on. A device that stored its PTR name here would
206
+ * stop polling the day its reverse zone changed, and would be discovered
207
+ * as a second, "unregistered" host on the very next sweep.
208
+ */
209
+ expect(deviceHostnames(devices)).toEqual([
210
+ "10.18.166.51",
211
+ "10.18.166.53",
212
+ "10.18.166.54",
213
+ "10.18.166.55",
214
+ ]);
215
+
216
+ // Naming a host by DNS must not change how it is monitored.
217
+ for (const device of devices) {
218
+ expect(device.monitoringMethod).toBe(
219
+ NetworkDeviceMonitoringMethod.Monitor,
220
+ );
221
+ expect(device.isPollingEnabled).toBe(false);
222
+ }
223
+ });
224
+
225
+ it("names a payload that predates the feature exactly as it always did", () => {
226
+ /*
227
+ * The back-compat guarantee, taken end to end rather than at one function.
228
+ *
229
+ * Two populations send payloads with no `dnsHostname` key: probes running
230
+ * a build from before #3529, and every row already sitting in a
231
+ * `discoveredDevices` column. Both must keep producing the names they
232
+ * produced yesterday — `sysName` when there is one, the address when
233
+ * there is not. A regression here is worse than a missing feature: it
234
+ * renames devices that import correctly today.
235
+ */
236
+ const payload: Array<unknown> = [
237
+ { ipAddress: "10.18.166.51", sysName: "core-switch-01" },
238
+ { ipAddress: "10.18.166.53", snmpReachable: false },
239
+ /*
240
+ * `dnsHostname: undefined` is what a NEW probe sends for a host with no
241
+ * PTR record, and JSON.stringify erases the key entirely. So the modern
242
+ * no-record row and the legacy row are byte-identical in the column,
243
+ * which is precisely why no reader needs to tell them apart.
244
+ */
245
+ {
246
+ ipAddress: "10.18.166.54",
247
+ snmpReachable: false,
248
+ dnsHostname: undefined,
249
+ },
250
+ ];
251
+
252
+ const hosts: Array<DiscoveredNetworkDevice> = hostsFromPayload(payload);
253
+
254
+ expect(
255
+ hosts.map((host: DiscoveredNetworkDevice): string => {
256
+ return buildDeviceName(host);
257
+ }),
258
+ ).toEqual(["core-switch-01", "10.18.166.53", "10.18.166.54"]);
259
+
260
+ /*
261
+ * Normalisation must not INVENT the key either. Readers are entitled to
262
+ * ask `"dnsHostname" in host`, and a blank-but-present key would show an
263
+ * empty DNS line under every legacy host. `toHaveProperty` rather than a
264
+ * value comparison on purpose: `toEqual` treats an absent key and a key
265
+ * set to `undefined` as the same thing, and that is the exact distinction
266
+ * DiscoveredHostUtil promises to keep.
267
+ */
268
+ for (const host of hosts) {
269
+ expect(host).not.toHaveProperty("dnsHostname");
270
+ }
271
+ });
272
+
273
+ it("falls back to the address for every hostile PTR value the column can hold", () => {
274
+ /*
275
+ * `dnsHostname` is the only field in a scan result whose value is chosen
276
+ * by the scanned network. A discovery sweep is routinely pointed at a
277
+ * subnet nobody in the project administers, so "whoever runs that
278
+ * resolver picks a device's name, its slug and a line in the dashboard"
279
+ * is the actual threat model — not a hypothetical one.
280
+ *
281
+ * Deliberately a SHORT list. ReverseDnsNameUtil.test.ts and
282
+ * DiscoveredDeviceBuilder.test.ts already enumerate hostile spellings one
283
+ * function at a time; repeating all of them here would buy coverage the
284
+ * chain does not add anything to. What is kept is the rows where the
285
+ * chain itself is the interesting part:
286
+ *
287
+ * - markup, the payload that makes a name dangerous rather than merely
288
+ * ugly once it is rendered and slugified;
289
+ * - a name with interior spaces, which would become a device whose name
290
+ * and slug disagree;
291
+ * - the bare "in-addr.arpa" apex, which a suffix-only zone check lets
292
+ * straight through and which is a strictly WORSE label than the
293
+ * address it was derived from;
294
+ * - the address restated as a dotted quad, which defeats the entire
295
+ * purpose of the feature by dressing an address up as a resolved name;
296
+ * - `null`, which the interface forbids and the jsonb column will
297
+ * happily store.
298
+ *
299
+ * The assertion is the same for all of them: name the device by its
300
+ * ADDRESS, and do not throw. Throwing matters as much as the value does —
301
+ * these functions run inside a React render, where a TypeError takes out
302
+ * the whole modal rather than one row, so a thrown error fails the
303
+ * `toEqual` below and that is the intended way for it to be caught.
304
+ */
305
+ const payload: Array<unknown> = [
306
+ {
307
+ ipAddress: "10.18.166.60",
308
+ snmpReachable: false,
309
+ dnsHostname: '<script>alert("pwned")</script>',
310
+ },
311
+ {
312
+ ipAddress: "10.18.166.61",
313
+ snmpReachable: false,
314
+ dnsHostname: " hq server 01 ",
315
+ },
316
+ {
317
+ ipAddress: "10.18.166.63",
318
+ snmpReachable: false,
319
+ dnsHostname: "in-addr.arpa",
320
+ },
321
+ {
322
+ ipAddress: "10.18.166.64",
323
+ snmpReachable: false,
324
+ dnsHostname: "10.18.166.64",
325
+ },
326
+ { ipAddress: "10.18.166.68", snmpReachable: false, dnsHostname: null },
327
+ ];
328
+
329
+ const addresses: Array<string> = [
330
+ "10.18.166.60",
331
+ "10.18.166.61",
332
+ "10.18.166.63",
333
+ "10.18.166.64",
334
+ "10.18.166.68",
335
+ ];
336
+
337
+ const hosts: Array<DiscoveredNetworkDevice> = hostsFromPayload(payload);
338
+
339
+ expect(
340
+ hosts.map((host: DiscoveredNetworkDevice): string => {
341
+ return buildDeviceName(host);
342
+ }),
343
+ ).toEqual(addresses);
344
+
345
+ // Nothing survives into the column's cleaned reading either.
346
+ for (const host of hosts) {
347
+ expect(host).not.toHaveProperty("dnsHostname");
348
+ }
349
+
350
+ expect(deviceHostnames(devicesFromPayload(payload))).toEqual(addresses);
351
+
352
+ /*
353
+ * The SECOND line of defence, pinned separately because it is the one a
354
+ * refactor is most likely to remove as redundant.
355
+ *
356
+ * `getDiscoveredHostDisplayName` re-normalises `dnsHostname` itself
357
+ * rather than trusting the row it was handed. That is what protects a row
358
+ * written straight through the API, and a row stored by a probe whose
359
+ * version never heard of these rules — neither of which passed through
360
+ * normalizeDiscoveredHosts on the way in. Building names from the RAW
361
+ * stored payload must give the same answers.
362
+ */
363
+ expect(
364
+ storedScanResults(payload).map(
365
+ (host: DiscoveredNetworkDevice): string => {
366
+ return getDiscoveredHostDisplayName(host);
367
+ },
368
+ ),
369
+ ).toEqual(addresses);
370
+ });
371
+
372
+ it("cuts a legal-but-long PTR name down to the device ceiling and refuses one no resolver could return", () => {
373
+ /*
374
+ * THE sharpest joint in the chain: two modules with different ceilings.
375
+ *
376
+ * ReverseDnsNameUtil accepts up to 253 characters, because that is what a
377
+ * DNS presentation-form name may be. DiscoveredDeviceBuilder clamps to
378
+ * 80, because the name is slugified into its own varchar(100) and the
379
+ * create path THROWS on overflow rather than truncating. So a name in
380
+ * between is accepted whole by the first module and MUST be cut by the
381
+ * second — and it is the only place in the feature where the string an
382
+ * operator reads and the string written to the column are not identical.
383
+ *
384
+ * Both rows are needed, and they pull in opposite directions:
385
+ *
386
+ * - 85 characters is a real hostname, so it is TRUNCATED. If the clamp
387
+ * were dropped the device create would fail on the slug and the host
388
+ * would silently never import.
389
+ * - 304 characters was never a DNS name, so it is REJECTED outright and
390
+ * the host falls back to its address. Truncating it instead would put
391
+ * an 80-character fragment of an untrusted string into the name
392
+ * column and present it as a resolved hostname.
393
+ */
394
+ const tooLongForDns: string = ["a", "b", "c", "d", "e"]
395
+ .map((letter: string): string => {
396
+ return letter.repeat(60);
397
+ })
398
+ .join(".");
399
+
400
+ // Fixture self-check: labels stay legal, only the total length offends.
401
+ expect(LONG_PTR_NAME.length).toBe(85);
402
+ expect(tooLongForDns.length).toBeGreaterThan(MAX_DNS_NAME_LENGTH);
403
+
404
+ const payload: Array<unknown> = [
405
+ {
406
+ ipAddress: "10.18.166.51",
407
+ snmpReachable: false,
408
+ dnsHostname: LONG_PTR_NAME,
409
+ },
410
+ {
411
+ ipAddress: "10.18.166.53",
412
+ snmpReachable: false,
413
+ dnsHostname: tooLongForDns,
414
+ },
415
+ ];
416
+
417
+ const hosts: Array<DiscoveredNetworkDevice> = hostsFromPayload(payload);
418
+
419
+ /*
420
+ * The 85-character name survives normalisation INTACT — the column keeps
421
+ * the resolver's answer, not a device-shaped abbreviation of it, so a
422
+ * later reader with a wider column is not stuck with this one's ceiling.
423
+ */
424
+ expect(hosts[0]?.dnsHostname).toBe(LONG_PTR_NAME);
425
+ expect(getDiscoveredHostDisplayName(hosts[0]!)).toBe(LONG_PTR_NAME);
426
+
427
+ // The 304-character one is not a name at all, so the key is gone.
428
+ expect(hosts[1]).not.toHaveProperty("dnsHostname");
429
+
430
+ /*
431
+ * The clamped name spelled out rather than derived with `.substring(80)`,
432
+ * so this asserts the ANSWER instead of restating the implementation:
433
+ * the 63-character label, then ".dc14.corp." and the first six letters of
434
+ * "example", which is where character 80 lands.
435
+ */
436
+ const clampedName: string = `${LONG_LABEL}.dc14.corp.exampl`;
437
+
438
+ expect(clampedName.length).toBe(MAX_NAME_LENGTH);
439
+
440
+ const devices: Array<NetworkDevice> = devicesFromPayload(payload);
441
+
442
+ expect(deviceNames(devices)).toEqual([clampedName, "10.18.166.53"]);
443
+ expect(deviceHostnames(devices)).toEqual(["10.18.166.51", "10.18.166.53"]);
444
+ });
445
+
446
+ it("keeps naming the good rows when the payload contains junk rows", () => {
447
+ /*
448
+ * One bad element must not poison the batch.
449
+ *
450
+ * The only guard on `discoveredDevices` checks that the VALUE is an
451
+ * array; it never looks at the elements. A single `null` row used to
452
+ * throw a TypeError the moment the operator clicked a filter button —
453
+ * inside the modal body, during render. This pins that the rows around
454
+ * the junk still name and address correctly.
455
+ *
456
+ * `null`, the number and the string are dropped because they are not
457
+ * objects. The address-less OBJECT is kept, and the contract for it is
458
+ * the second assertion below, not its mere survival: it becomes a device
459
+ * with `hostname: ""`, which is a row that carries its sysName as a name
460
+ * and collides on the dedup key with every other address-less row. That
461
+ * is the deliberate choice — a visibly broken row beats a vanished one —
462
+ * and it is stated here so a change to it has to be a decision.
463
+ */
464
+ const payload: Array<unknown> = [
465
+ null,
466
+ 42,
467
+ "10.18.166.51",
468
+ { sysName: "orphan-no-address" },
469
+ {
470
+ ipAddress: "10.18.166.53",
471
+ snmpReachable: false,
472
+ dnsHostname: "hq-ups-01.corp.example.net",
473
+ },
474
+ { ipAddress: "10.18.166.54", snmpReachable: false },
475
+ ];
476
+
477
+ const devices: Array<NetworkDevice> = devicesFromPayload(payload);
478
+
479
+ expect(deviceNames(devices)).toEqual([
480
+ "orphan-no-address",
481
+ "hq-ups-01.corp.example.net",
482
+ "10.18.166.54",
483
+ ]);
484
+
485
+ expect(deviceHostnames(devices)).toEqual([
486
+ "",
487
+ "10.18.166.53",
488
+ "10.18.166.54",
489
+ ]);
490
+ });
491
+
492
+ it("normalises to a fixed point, so every reader of a row agrees", () => {
493
+ /*
494
+ * The chain has to be IDEMPOTENT, because it is not run once.
495
+ *
496
+ * The dashboard normalises the scan's rows when the Review dialog opens
497
+ * and again on re-render; the server-side auto-import rule engine
498
+ * normalises its own copy of the same jsonb; the import loop names a host
499
+ * that has already been through both. If a second pass could change an
500
+ * answer, the name an operator ticked and the name the device was created
501
+ * with could differ — which is the exact class of bug the shared
502
+ * normaliser was written to end.
503
+ *
504
+ * The payload is chosen so the first pass genuinely CHANGES every row:
505
+ * a root dot is stripped, a padded address is trimmed, a numeric sysName
506
+ * is blanked, and a rejected PTR key is deleted. A payload that was
507
+ * already clean would make this test pass no matter what.
508
+ */
509
+ const payload: Array<unknown> = [
510
+ {
511
+ ipAddress: "10.18.166.51",
512
+ snmpReachable: false,
513
+ dnsHostname: "hq-router-01.corp.example.net.",
514
+ },
515
+ { ipAddress: " 10.18.166.53 ", sysName: 42 },
516
+ {
517
+ ipAddress: "10.18.166.54",
518
+ snmpReachable: false,
519
+ dnsHostname: "in-addr.arpa",
520
+ },
521
+ ];
522
+
523
+ const stored: Array<DiscoveredNetworkDevice> = storedScanResults(payload);
524
+ const once: Array<DiscoveredNetworkDevice> =
525
+ normalizeDiscoveredHosts(stored);
526
+ const twice: Array<DiscoveredNetworkDevice> =
527
+ normalizeDiscoveredHosts(once);
528
+
529
+ /*
530
+ * `toStrictEqual`, NOT `toEqual`. `toEqual` ignores keys whose value is
531
+ * `undefined`, so it cannot tell `delete row.dnsHostname` from
532
+ * `row.dnsHostname = undefined` — and that distinction IS the contract
533
+ * ("a reader that checks `if (host.dnsHostname)` and one that checks
534
+ * `"dnsHostname" in host` cannot disagree"). With `toEqual` this
535
+ * assertion would stay green through exactly the regression it is here to
536
+ * catch. The key lists are compared as well, so the failure message names
537
+ * the key that appeared or vanished rather than just the row.
538
+ */
539
+ expect(twice).toStrictEqual(once);
540
+ expect(
541
+ twice.map((host: DiscoveredNetworkDevice): Array<string> => {
542
+ return Object.keys(host).sort();
543
+ }),
544
+ ).toEqual(
545
+ once.map((host: DiscoveredNetworkDevice): Array<string> => {
546
+ return Object.keys(host).sort();
547
+ }),
548
+ );
549
+
550
+ // The rejected PTR key is absent after BOTH passes, not present-and-blank.
551
+ expect(once[2]).not.toHaveProperty("dnsHostname");
552
+ expect(twice[2]).not.toHaveProperty("dnsHostname");
553
+
554
+ const namesOnce: Array<string> = once.map(
555
+ (host: DiscoveredNetworkDevice): string => {
556
+ return buildDeviceName(host);
557
+ },
558
+ );
559
+ const namesTwice: Array<string> = twice.map(
560
+ (host: DiscoveredNetworkDevice): string => {
561
+ return buildDeviceName(host);
562
+ },
563
+ );
564
+
565
+ expect(namesOnce).toEqual([
566
+ "hq-router-01.corp.example.net",
567
+ "10.18.166.53",
568
+ "10.18.166.54",
569
+ ]);
570
+ expect(namesTwice).toEqual(namesOnce);
571
+ });
572
+
573
+ it("gives a wildcard PTR zone one shared name and three distinct fallbacks", () => {
574
+ /*
575
+ * A wildcard reverse zone — "*.166.18.10.in-addr.arpa PTR
576
+ * unassigned.dhcp.corp.example.net" — is common on DHCP ranges, and it is
577
+ * what makes the naming feature collide with a database constraint:
578
+ * NetworkDevice names are unique per project, so the first host imports
579
+ * and the next two fail the create.
580
+ *
581
+ * Both halves are load-bearing and pull in opposite directions. The
582
+ * primary name must be SHARED (it is what the resolver actually said, and
583
+ * showing three different names for one PTR record would be a fiction),
584
+ * while the retry name must be DISTINCT or the retry is pointless — the
585
+ * second create fails exactly as the first did. The address is the only
586
+ * thing that tells these three hosts apart, which is why it is what the
587
+ * fallback appends.
588
+ */
589
+ const payload: Array<unknown> = [
590
+ {
591
+ ipAddress: "10.18.166.51",
592
+ snmpReachable: false,
593
+ dnsHostname: "unassigned.dhcp.corp.example.net",
594
+ },
595
+ {
596
+ ipAddress: "10.18.166.53",
597
+ snmpReachable: false,
598
+ dnsHostname: "unassigned.dhcp.corp.example.net",
599
+ },
600
+ {
601
+ ipAddress: "10.18.166.54",
602
+ snmpReachable: false,
603
+ dnsHostname: "unassigned.dhcp.corp.example.net",
604
+ },
605
+ ];
606
+
607
+ const hosts: Array<DiscoveredNetworkDevice> = hostsFromPayload(payload);
608
+
609
+ expect(
610
+ hosts.map((host: DiscoveredNetworkDevice): string => {
611
+ return buildDeviceName(host);
612
+ }),
613
+ ).toEqual([
614
+ "unassigned.dhcp.corp.example.net",
615
+ "unassigned.dhcp.corp.example.net",
616
+ "unassigned.dhcp.corp.example.net",
617
+ ]);
618
+
619
+ const fallbacks: Array<string> = hosts.map(
620
+ (host: DiscoveredNetworkDevice): string => {
621
+ return buildFallbackDeviceName(host);
622
+ },
623
+ );
624
+
625
+ expect(fallbacks).toEqual([
626
+ "unassigned.dhcp.corp.example.net (10.18.166.51)",
627
+ "unassigned.dhcp.corp.example.net (10.18.166.53)",
628
+ "unassigned.dhcp.corp.example.net (10.18.166.54)",
629
+ ]);
630
+
631
+ // The property the retry depends on, stated as a property.
632
+ expect(new Set<string>(fallbacks).size).toBe(3);
633
+ });
634
+
635
+ it("keeps the collision fallback under the name ceiling however long the PTR name and the address are", () => {
636
+ /*
637
+ * The wildcard case above with a name that is already AT the ceiling,
638
+ * which is the realistic version: long DHCP zone names are the norm, so
639
+ * "shared name" and "long name" arrive together, and this is the only
640
+ * shape where `buildFallbackDeviceName`'s own truncation does anything.
641
+ *
642
+ * The fallback appends " (address)" to a name that is already the full 80
643
+ * characters, so the base has to be cut AGAIN — by exactly the suffix's
644
+ * width — or the composed name overflows the slug and the retry fails for
645
+ * a different reason than the collision it was meant to fix. Distinctness
646
+ * has to survive that second cut, which is why the suffix goes on the END:
647
+ * the two names are identical for their first 65 characters.
648
+ */
649
+ const payload: Array<unknown> = [
650
+ {
651
+ ipAddress: "10.18.166.51",
652
+ snmpReachable: false,
653
+ dnsHostname: LONG_PTR_NAME,
654
+ },
655
+ {
656
+ ipAddress: "10.18.166.53",
657
+ snmpReachable: false,
658
+ dnsHostname: LONG_PTR_NAME,
659
+ },
660
+ ];
661
+
662
+ const hosts: Array<DiscoveredNetworkDevice> = hostsFromPayload(payload);
663
+
664
+ const fallbacks: Array<string> = hosts.map(
665
+ (host: DiscoveredNetworkDevice): string => {
666
+ return buildFallbackDeviceName(host);
667
+ },
668
+ );
669
+
670
+ /*
671
+ * 65 characters of name (80 minus the 15-character suffix) plus the
672
+ * suffix. Written out rather than computed so a regression that cut to
673
+ * the wrong width is a visible diff and not an arithmetic identity.
674
+ */
675
+ expect(fallbacks).toEqual([
676
+ `${LONG_LABEL}.d (10.18.166.51)`,
677
+ `${LONG_LABEL}.d (10.18.166.53)`,
678
+ ]);
679
+
680
+ for (const fallback of fallbacks) {
681
+ expect(fallback.length).toBe(MAX_NAME_LENGTH);
682
+ }
683
+
684
+ expect(new Set<string>(fallbacks).size).toBe(2);
685
+
686
+ /*
687
+ * The degenerate end of the same branch: an "address" longer than the
688
+ * whole name ceiling, so the suffix alone overflows it and the width left
689
+ * for the base name goes NEGATIVE. `ipAddress` is not validated anywhere
690
+ * on this path — it is whatever the probe wrote into jsonb — so a broken
691
+ * probe build really can put a line of sweep output here.
692
+ *
693
+ * TWO floors have to hold at once, and they pull against each other.
694
+ * Without the `Math.max(1, ...)`, `substring(0, -6)` returns "" and the
695
+ * device is named for the junk address alone, with nothing of the
696
+ * resolver's answer left in it. But that floor is also what used to let
697
+ * the COMPOSED name run past the ceiling — one base character plus an
698
+ * 83-character suffix is 84 — and a name over the ceiling fails the create
699
+ * on the slug's own length, which is a different error than the collision
700
+ * the retry was for, on a path whose whole job is to recover from one.
701
+ *
702
+ * So the composition is clamped as a whole: the first character of the
703
+ * name survives, and the result still fits.
704
+ */
705
+ const junkAddress: string = `10.18.166.55-${"z".repeat(70)}`;
706
+ const junkHosts: Array<DiscoveredNetworkDevice> = hostsFromPayload([
707
+ {
708
+ ipAddress: junkAddress,
709
+ snmpReachable: false,
710
+ dnsHostname: "hq-nas-01.corp.example.net",
711
+ },
712
+ ]);
713
+
714
+ const junkFallback: string = buildFallbackDeviceName(junkHosts[0]!);
715
+
716
+ expect(junkFallback.length).toBe(MAX_NAME_LENGTH);
717
+ expect(junkFallback.startsWith("h (10.18.166.55-")).toBe(true);
718
+ });
719
+
720
+ it("creates the retried device under the fallback name while still addressing it by IP", () => {
721
+ /*
722
+ * The retry as `importSelectedDevices` and the rule engine actually
723
+ * perform it, rather than as a string.
724
+ *
725
+ * Both paths build the device, catch the unique-name failure, and rebuild
726
+ * it with `name: buildFallbackDeviceName(host)`. Two things have to hold
727
+ * for that to work and neither is visible from the naming functions
728
+ * alone: the builder must PREFER the supplied name over its own default
729
+ * (otherwise the retry recreates the colliding name and fails again), and
730
+ * the retry must change nothing else — above all not `hostname`, because
731
+ * a retried device that addressed itself by name would poll the wrong
732
+ * thing forever.
733
+ *
734
+ * The default half is pinned on the same host in the same test, so the
735
+ * two names are visibly different strings produced by one builder.
736
+ */
737
+ const host: DiscoveredNetworkDevice = hostsFromPayload([
738
+ {
739
+ ipAddress: "10.18.166.51",
740
+ snmpReachable: false,
741
+ dnsHostname: "unassigned.dhcp.corp.example.net",
742
+ },
743
+ ])[0]!;
744
+
745
+ const firstAttempt: NetworkDevice = buildNetworkDeviceFromDiscoveredHost({
746
+ projectId: PROJECT_ID,
747
+ host: host,
748
+ scan: PING_ONLY_SCAN,
749
+ });
750
+
751
+ // No name supplied: the builder's default IS the displayed name.
752
+ expect(firstAttempt.name).toBe("unassigned.dhcp.corp.example.net");
753
+
754
+ const retry: NetworkDevice = buildNetworkDeviceFromDiscoveredHost({
755
+ projectId: PROJECT_ID,
756
+ host: host,
757
+ scan: PING_ONLY_SCAN,
758
+ name: buildFallbackDeviceName(host),
759
+ });
760
+
761
+ expect(retry.name).toBe("unassigned.dhcp.corp.example.net (10.18.166.51)");
762
+ expect(retry.name).not.toBe(firstAttempt.name);
763
+
764
+ // Everything the retry must NOT have changed.
765
+ expect(retry.hostname).toBe("10.18.166.51");
766
+ expect(retry.hostname).toBe(firstAttempt.hostname);
767
+ expect(retry.monitoringMethod).toBe(NetworkDeviceMonitoringMethod.Monitor);
768
+ expect(retry.isPollingEnabled).toBe(false);
769
+ expect(retry.projectId).toBe(PROJECT_ID);
770
+ });
771
+
772
+ it("stringifies a numeric address while naming the host by its PTR record", () => {
773
+ /*
774
+ * A number in `ipAddress` is a shipped probe bug the normaliser exists to
775
+ * absorb: the selection record keys the host as the string "10" (object
776
+ * keys always are), while the imported-set is matched with `Set.has(10)`,
777
+ * which does not coerce — so the host imported and then could never be
778
+ * retired, and pressing Import again duplicated it.
779
+ *
780
+ * With a PTR name on the same row it becomes a chain claim, and the two
781
+ * fields must be treated OPPOSITELY: `ipAddress` is stringified (it is
782
+ * still an address, just badly typed) while `sysName` and `dnsHostname`
783
+ * are not (see the sysName test below). `hostname` must come out as the
784
+ * string "10", because NetworkDeviceService.getRegisteredHostnames dedups
785
+ * against a Set of strings and a number would silently never match.
786
+ */
787
+ const hosts: Array<DiscoveredNetworkDevice> = hostsFromPayload([
788
+ {
789
+ ipAddress: 10,
790
+ snmpReachable: false,
791
+ dnsHostname: "hq-gw-01.corp.example.net",
792
+ },
793
+ ]);
794
+
795
+ expect(hosts[0]?.ipAddress).toBe("10");
796
+
797
+ const device: NetworkDevice = devicesFromPayload([
798
+ {
799
+ ipAddress: 10,
800
+ snmpReachable: false,
801
+ dnsHostname: "hq-gw-01.corp.example.net",
802
+ },
803
+ ])[0]!;
804
+
805
+ expect(device.name).toBe("hq-gw-01.corp.example.net");
806
+ expect(device.hostname).toBe("10");
807
+ // `toBe("10")` would also pass for the number under `==`, so say it.
808
+ expect(typeof device.hostname).toBe("string");
809
+ });
810
+
811
+ it("preserves a PTR record's own capitalisation and still rejects an upper-case in-addr.arpa echo", () => {
812
+ /*
813
+ * Case is a joint, in both directions, and neither direction is visible
814
+ * from one module.
815
+ *
816
+ * (a) ReverseDnsNameUtil PRESERVES case deliberately — the reverse zone's
817
+ * spelling is as close to the operator's intent as this data gets —
818
+ * so a device must be created as "HQ-FileServer.Corp.Example.NET".
819
+ * Any lower-casing added for "consistency" between here and the
820
+ * device name would be caught here.
821
+ * (b) The zone check compares the LOWER-CASED name but the function
822
+ * returns the original. A resolver that echoes the query name back in
823
+ * upper case (they exist; DNS is case-insensitive on the wire and
824
+ * 0x20 encoding makes mixed-case echoes ordinary) must still be
825
+ * rejected. Comparing the un-lowered string against the zone list is
826
+ * the classic version of this bug, and every other hostile-value case
827
+ * in this file spells the zone in lower case, so this row is the only
828
+ * one that would catch it.
829
+ */
830
+ const payload: Array<unknown> = [
831
+ {
832
+ ipAddress: "10.18.166.51",
833
+ snmpReachable: false,
834
+ dnsHostname: "HQ-FileServer.Corp.Example.NET",
835
+ },
836
+ {
837
+ ipAddress: "10.18.166.53",
838
+ snmpReachable: false,
839
+ dnsHostname: "53.166.18.10.IN-ADDR.ARPA",
840
+ },
841
+ // The apex spelled the same way, which a suffix-only check misses.
842
+ {
843
+ ipAddress: "10.18.166.54",
844
+ snmpReachable: false,
845
+ dnsHostname: "In-Addr.Arpa.",
846
+ },
847
+ ];
848
+
849
+ const hosts: Array<DiscoveredNetworkDevice> = hostsFromPayload(payload);
850
+
851
+ expect(hosts[0]?.dnsHostname).toBe("HQ-FileServer.Corp.Example.NET");
852
+ expect(hosts[1]).not.toHaveProperty("dnsHostname");
853
+ expect(hosts[2]).not.toHaveProperty("dnsHostname");
854
+
855
+ expect(deviceNames(devicesFromPayload(payload))).toEqual([
856
+ "HQ-FileServer.Corp.Example.NET",
857
+ "10.18.166.53",
858
+ "10.18.166.54",
859
+ ]);
860
+ });
861
+
862
+ it("blanks a non-string sysName so the PTR name wins, rather than stringifying it", () => {
863
+ /*
864
+ * `sysName` comes out of the same verbatim jsonb blob as everything else,
865
+ * so its declared `string | undefined` type describes what the probe
866
+ * SHOULD send, not what is stored. `(42).trim()` is a TypeError thrown
867
+ * during render, which takes out the whole modal rather than one row.
868
+ *
869
+ * EXPECTED BEHAVIOUR, stated plainly because the interesting part is what
870
+ * does NOT happen. A non-string sysName is BLANKED, not coerced, and that
871
+ * is the difference between this field and `ipAddress` (which IS
872
+ * stringified, as the numeric-address test above pins). Blanking is what
873
+ * these rows prove is right:
874
+ *
875
+ * - sysName 42 -> "" -> the PTR name wins. Not "42".
876
+ * - sysName {...} -> "" -> the PTR name wins. NOT "[object Object]",
877
+ * which is what `String()` returns and which, being
878
+ * truthy, would beat a perfectly good PTR record on
879
+ * the very same row.
880
+ * - sysName null -> "" -> the PTR name wins. NOT "null" (typeof null
881
+ * is "object", so it is not the `undefined` the
882
+ * guard already skips).
883
+ *
884
+ * The blanking is asserted on the ROW as well as through the name,
885
+ * because the two layers guard it independently: delete the blanking from
886
+ * normalizeDiscoveredHosts and getDiscoveredHostDisplayName's own `typeof`
887
+ * check still produces every name below. Only `hosts[n].sysName` can tell
888
+ * that the normalised row — which is what an auto-import rule matches on
889
+ * and what the selection record holds — was cleaned too.
890
+ *
891
+ * The fourth row is the one that makes blanking visible in the NAME as
892
+ * well: junk in `sysName` and no PTR record either, so the name has
893
+ * nowhere to fall but the address.
894
+ */
895
+ const payload: Array<unknown> = [
896
+ {
897
+ ipAddress: "10.18.166.51",
898
+ sysName: 42,
899
+ dnsHostname: "hq-switch-01.corp.example.net",
900
+ },
901
+ {
902
+ ipAddress: "10.18.166.53",
903
+ sysName: { oid: "1.3.6.1.2.1.1.5.0" },
904
+ dnsHostname: "hq-switch-02.corp.example.net",
905
+ },
906
+ {
907
+ ipAddress: "10.18.166.54",
908
+ sysName: null,
909
+ dnsHostname: "hq-switch-03.corp.example.net",
910
+ },
911
+ { ipAddress: "10.18.166.56", sysName: 42 },
912
+ /*
913
+ * An ABSENT sysName must not GAIN the key: `sysName` is optional and
914
+ * `"sysName" in host` is a question other code is entitled to ask. Note
915
+ * `sysName: undefined` is erased by the JSON round trip, so "absent" is
916
+ * the only form the column can actually store.
917
+ */
918
+ {
919
+ ipAddress: "10.18.166.57",
920
+ sysName: undefined,
921
+ dnsHostname: "hq-switch-04.corp.example.net",
922
+ },
923
+ ];
924
+
925
+ const hosts: Array<DiscoveredNetworkDevice> = hostsFromPayload(payload);
926
+
927
+ expect(hosts[0]?.sysName).toBe("");
928
+ expect(hosts[1]?.sysName).toBe("");
929
+ expect(hosts[2]?.sysName).toBe("");
930
+ expect(hosts[3]?.sysName).toBe("");
931
+ expect(hosts[4]).not.toHaveProperty("sysName");
932
+
933
+ const devices: Array<NetworkDevice> = devicesFromPayload(payload);
934
+
935
+ expect(deviceNames(devices)).toEqual([
936
+ "hq-switch-01.corp.example.net",
937
+ "hq-switch-02.corp.example.net",
938
+ "hq-switch-03.corp.example.net",
939
+ "10.18.166.56",
940
+ "hq-switch-04.corp.example.net",
941
+ ]);
942
+
943
+ expect(deviceHostnames(devices)).toEqual([
944
+ "10.18.166.51",
945
+ "10.18.166.53",
946
+ "10.18.166.54",
947
+ "10.18.166.56",
948
+ "10.18.166.57",
949
+ ]);
950
+ });
951
+
952
+ it("leaves a whitespace-only sysName on the row but never lets it become a name", () => {
953
+ /*
954
+ * The one place the two layers deliberately DISAGREE, which is why it can
955
+ * only be pinned end to end.
956
+ *
957
+ * normalizeDiscoveredHosts passes a string sysName through untouched — it
958
+ * only rewrites non-strings — so " " is still " " on the row an
959
+ * auto-import rule matches against. getDiscoveredHostDisplayName trims
960
+ * before testing for emptiness, so the same value does not name anything
961
+ * and the PTR record wins.
962
+ *
963
+ * Both halves are asserted because either one alone is satisfiable by the
964
+ * wrong implementation: if the normaliser started trimming, the row
965
+ * assertion fails; if the display stopped trimming, " " is truthy, it
966
+ * beats the PTR record, and a device is created whose name is three
967
+ * spaces and whose slug is empty.
968
+ */
969
+ const payload: Array<unknown> = [
970
+ {
971
+ ipAddress: "10.18.166.51",
972
+ sysName: " ",
973
+ dnsHostname: "hq-esxi-04.corp.example.net",
974
+ },
975
+ // The same blank sysName with nothing to fall through to but the address.
976
+ { ipAddress: "10.18.166.53", sysName: " " },
977
+ ];
978
+
979
+ const hosts: Array<DiscoveredNetworkDevice> = hostsFromPayload(payload);
980
+
981
+ expect(hosts[0]?.sysName).toBe(" ");
982
+ expect(hosts[1]?.sysName).toBe(" ");
983
+
984
+ expect(namesFromPayload(payload)).toEqual([
985
+ "hq-esxi-04.corp.example.net",
986
+ "10.18.166.53",
987
+ ]);
988
+
989
+ expect(deviceNames(devicesFromPayload(payload))).toEqual([
990
+ "hq-esxi-04.corp.example.net",
991
+ "10.18.166.53",
992
+ ]);
993
+ });
994
+
995
+ it("gives two rows for one address a single registration state but two different PTR names", () => {
996
+ /*
997
+ * The dedup pass and the naming feature meet here, and the meeting is
998
+ * only visible in the chain.
999
+ *
1000
+ * A scan can list the same address twice — two interfaces answering, a
1001
+ * re-scan merged in, a probe retry — and the rows need not agree. The
1002
+ * normaliser fixes ONE of those disagreements: `isAlreadyRegistered` is a
1003
+ * property of the ADDRESS, so if any row says the address is registered,
1004
+ * every row for it does. Without that pass one checkbox governs both rows
1005
+ * while only one of them refuses to import, and whether an inventoried
1006
+ * device got created a second time depended on the probe's ordering.
1007
+ *
1008
+ * It deliberately does NOT unify the names, and it cannot: the two rows
1009
+ * hold two different PTR answers and neither is more true than the other.
1010
+ * So the operator ticks one row, sees one name, and — if the import walks
1011
+ * the other row — creates a device under the other name. Pinning the
1012
+ * disagreement is the point: it is a real hazard of naming devices by
1013
+ * DNS, and this assertion is where anyone who later tries to dedup rows
1014
+ * by address will find out that the names were never dedupable.
1015
+ */
1016
+ const payload: Array<unknown> = [
1017
+ {
1018
+ ipAddress: "10.18.166.51",
1019
+ snmpReachable: false,
1020
+ dnsHostname: "hq-nas-01.corp.example.net",
1021
+ isAlreadyRegistered: true,
1022
+ },
1023
+ {
1024
+ ipAddress: "10.18.166.51",
1025
+ snmpReachable: false,
1026
+ dnsHostname: "hq-nas-01-old.corp.example.net",
1027
+ },
1028
+ ];
1029
+
1030
+ const hosts: Array<DiscoveredNetworkDevice> = hostsFromPayload(payload);
1031
+
1032
+ // One address, one registration state, whatever order the probe sent.
1033
+ expect(
1034
+ hosts.map((host: DiscoveredNetworkDevice): boolean | undefined => {
1035
+ return host.isAlreadyRegistered;
1036
+ }),
1037
+ ).toEqual([true, true]);
1038
+
1039
+ const names: Array<string | undefined> = deviceNames(
1040
+ devicesFromPayload(payload),
1041
+ );
1042
+
1043
+ expect(names).toEqual([
1044
+ "hq-nas-01.corp.example.net",
1045
+ "hq-nas-01-old.corp.example.net",
1046
+ ]);
1047
+ // Two names for one checkbox: the hazard, stated as a property.
1048
+ expect(new Set<string | undefined>(names).size).toBe(2);
1049
+
1050
+ // Both still address the one host they are, which is the dedup key.
1051
+ expect(deviceHostnames(devicesFromPayload(payload))).toEqual([
1052
+ "10.18.166.51",
1053
+ "10.18.166.51",
1054
+ ]);
1055
+ });
1056
+ });