@oneuptime/common 14.0.6 → 14.0.8

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 (556) hide show
  1. package/Models/DatabaseModels/Incident.ts +267 -1
  2. package/Models/DatabaseModels/IncidentCustomField.ts +202 -0
  3. package/Models/DatabaseModels/IncidentEpisode.ts +30 -0
  4. package/Models/DatabaseModels/IncidentTemplate.ts +126 -1
  5. package/Models/DatabaseModels/InventoryItem.ts +23 -12
  6. package/Models/DatabaseModels/StatusPage.ts +55 -0
  7. package/Models/DatabaseModels/StatusPageSubscriber.ts +128 -0
  8. package/Server/API/IncidentAPI.ts +162 -0
  9. package/Server/API/StatusPageAPI.ts +487 -171
  10. package/Server/API/StatusPageSubscriberAPI.ts +63 -15
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.ts +217 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.ts +49 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.ts +108 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.ts +33 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.ts +51 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.ts +33 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.ts +168 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.ts +44 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +16 -0
  20. package/Server/Infrastructure/Semaphore.ts +10 -0
  21. package/Server/Middleware/IdentityRateLimit.ts +108 -3
  22. package/Server/Middleware/SubscriberNotificationPreviewRateLimit.ts +237 -0
  23. package/Server/Middleware/SubscriberNotificationTestSendRateLimit.ts +304 -0
  24. package/Server/Services/AlertService.ts +32 -36
  25. package/Server/Services/DatabaseService.ts +93 -10
  26. package/Server/Services/IncidentAlertService.ts +609 -2
  27. package/Server/Services/IncidentCustomFieldService.ts +276 -1
  28. package/Server/Services/IncidentEpisodePublicNoteService.ts +28 -0
  29. package/Server/Services/IncidentEpisodeService.ts +15 -0
  30. package/Server/Services/IncidentEpisodeStateTimelineService.ts +27 -1
  31. package/Server/Services/IncidentPublicNoteService.ts +30 -1
  32. package/Server/Services/IncidentService.ts +1663 -103
  33. package/Server/Services/IncidentStateTimelineService.ts +13 -0
  34. package/Server/Services/IncidentTemplateService.ts +109 -0
  35. package/Server/Services/InventoryItemService.ts +38 -0
  36. package/Server/Services/IoTDeviceCredentialService.ts +13 -4
  37. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +30 -1
  38. package/Server/Services/ScheduledMaintenanceService.ts +118 -64
  39. package/Server/Services/SmsService.ts +10 -0
  40. package/Server/Services/StatusPageService.ts +65 -8
  41. package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +299 -37
  42. package/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.ts +129 -0
  43. package/Server/Services/StatusPageSubscriberService.ts +1161 -64
  44. package/Server/Services/UserNotificationRuleService.ts +55 -53
  45. package/Server/Services/WorkspaceNotificationSummaryService.ts +38 -20
  46. package/Server/Types/Markdown.ts +153 -6
  47. package/Server/Types/Workflow/Components/Email.ts +61 -2
  48. package/Server/Utils/AffectedResources/LinkedAffectedResources.ts +576 -0
  49. package/Server/Utils/Alert/AlertStateChangeAuthorization.ts +129 -0
  50. package/Server/Utils/CustomField/CustomFieldRename.ts +378 -0
  51. package/Server/Utils/DataSource/EgressGuard.ts +66 -21
  52. package/Server/Utils/Inventory/InventoryOverviewAggregation.ts +111 -0
  53. package/Server/Utils/Mail/PinnedSmtpSocket.ts +130 -0
  54. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +10 -0
  55. package/Server/Utils/Response.ts +31 -0
  56. package/Server/Utils/SSRFProtection.ts +76 -16
  57. package/Server/Utils/StatusPage/IncidentStatusPageScope.ts +948 -0
  58. package/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.ts +656 -0
  59. package/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.ts +805 -0
  60. package/Server/Utils/StatusPage/StatusPageExclusion.ts +18 -0
  61. package/Server/Utils/StatusPage/StatusPageReadAccess.ts +125 -0
  62. package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.ts +151 -0
  63. package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.ts +70 -0
  64. package/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.ts +421 -0
  65. package/Server/Utils/StatusPage/SubscriberNotificationClaim.ts +107 -0
  66. package/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.ts +1049 -0
  67. package/Server/Utils/StatusPage/SubscriberNotificationFanOut.ts +180 -0
  68. package/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.ts +839 -0
  69. package/Server/Utils/StatusPage/SubscriberNotificationResendAccess.ts +393 -0
  70. package/Server/Utils/StatusPage/SubscriberNotificationRunLimit.ts +246 -0
  71. package/Server/Utils/StatusPage/SubscriberNotificationTiming.ts +133 -0
  72. package/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.ts +215 -0
  73. package/Server/Utils/StatusPageResource.ts +51 -20
  74. package/Server/Utils/Topology/TopologyConcurrencyLimiter.ts +229 -0
  75. package/Server/Utils/Topology/TopologyQueries.ts +1350 -0
  76. package/Server/Utils/Topology/TopologyRequest.ts +463 -0
  77. package/Server/Utils/Topology/TopologyResponseCache.ts +183 -0
  78. package/Server/Utils/Topology/TopologySql.ts +1024 -0
  79. package/Server/Utils/VM/VMAPI.ts +1 -0
  80. package/Server/Utils/VM/VMRunner.ts +10 -0
  81. package/Server/Utils/Workflow/OAuth2TokenClient.ts +10 -0
  82. package/Server/Views/Partials/SensitiveUrlToken.ejs +8 -5
  83. package/Tests/App/Accounts/RegisterVerifyEmail.test.tsx +748 -0
  84. package/Tests/App/Accounts/ResendVerificationEmail.test.tsx +1222 -0
  85. package/Tests/App/Accounts/VerificationEmailResendUtils.test.ts +797 -0
  86. package/Tests/App/Accounts/VerifyEmailPending.test.tsx +538 -0
  87. package/Tests/App/Accounts/VerifyEmailResend.test.tsx +779 -0
  88. package/Tests/App/Dashboard/AcknowledgeAlertsOnDeclare.test.ts +369 -0
  89. package/Tests/App/Dashboard/AffectedResourcesSlo.test.tsx +1 -1
  90. package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +510 -123
  91. package/Tests/App/Dashboard/AlertsTableAffectedResources.test.tsx +490 -0
  92. package/Tests/App/Dashboard/ChangeAlertStateDeclareIncident.test.tsx +886 -0
  93. package/Tests/App/Dashboard/CreateReviewStepRoleSummary.test.tsx +1250 -0
  94. package/Tests/App/Dashboard/CreateReviewStepSelectionSummary.test.tsx +738 -0
  95. package/Tests/App/Dashboard/CustomFieldsSettingsIncidentOptions.test.tsx +357 -0
  96. package/Tests/App/Dashboard/DeclareIncidentFromAlert.test.ts +417 -0
  97. package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +1948 -229
  98. package/Tests/App/Dashboard/EntityDetailPanelLifecycle.test.tsx +493 -0
  99. package/Tests/App/Dashboard/EntityDetailPanelNavigation.test.tsx +439 -0
  100. package/Tests/App/Dashboard/EventNotes.test.tsx +776 -0
  101. package/Tests/App/Dashboard/EventNotesPages.test.tsx +119 -0
  102. package/Tests/App/Dashboard/EventNotesUtil.test.ts +116 -17
  103. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +534 -6
  104. package/Tests/App/Dashboard/EventStatusPanel.test.tsx +798 -2
  105. package/Tests/App/Dashboard/FetchStateAndSeverityElements.test.tsx +669 -0
  106. package/Tests/App/Dashboard/FetchUsers.test.tsx +230 -0
  107. package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +91 -1
  108. package/Tests/App/Dashboard/IncidentCreateAcknowledgeAlerts.test.tsx +1611 -0
  109. package/Tests/App/Dashboard/IncidentCreateDetailsStep.test.tsx +643 -0
  110. package/Tests/App/Dashboard/IncidentCreateFromAlerts.test.tsx +8 -1
  111. package/Tests/App/Dashboard/IncidentCreateStatusPageScope.test.tsx +601 -0
  112. package/Tests/App/Dashboard/IncidentCustomFieldTemplateVariables.test.tsx +386 -0
  113. package/Tests/App/Dashboard/IncidentNoteTemplatePlaceholders.test.tsx +124 -0
  114. package/Tests/App/Dashboard/IncidentNoteTemplateVariablesLoader.test.ts +305 -0
  115. package/Tests/App/Dashboard/IncidentOverviewNotifyDefault.test.tsx +112 -0
  116. package/Tests/App/Dashboard/IncidentSettingsRenotifyOnPublish.test.tsx +674 -0
  117. package/Tests/App/Dashboard/IncidentSettingsStatusPageScope.test.tsx +1051 -0
  118. package/Tests/App/Dashboard/IncidentTemplateCustomFields.test.tsx +384 -0
  119. package/Tests/App/Dashboard/IncidentTemplateStatusPageScopeCard.test.tsx +215 -0
  120. package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +1365 -57
  121. package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +964 -23
  122. package/Tests/App/Dashboard/InventoryRelationships.test.tsx +885 -0
  123. package/Tests/App/Dashboard/PublicNotePagesNotifyDefault.test.tsx +113 -1
  124. package/Tests/App/Dashboard/ServiceMapDatabaseLink.test.tsx +201 -15
  125. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +508 -36
  126. package/Tests/App/Dashboard/SubscriberAudienceSummary.test.tsx +512 -0
  127. package/Tests/App/Dashboard/SubscriberNotificationPreviewModal.test.tsx +837 -0
  128. package/Tests/App/Dashboard/SubscriberNotificationStatusLabel.test.tsx +88 -0
  129. package/Tests/App/Dashboard/SubscriberNotificationStatusResend.test.tsx +473 -0
  130. package/Tests/App/Dashboard/SubscriberTemplateLivePreview.test.tsx +196 -0
  131. package/Tests/App/Dashboard/TeamAddedSubscribersUnsubscribedNotice.test.tsx +248 -0
  132. package/Tests/App/Dashboard/TopologyDataLifecycle.test.tsx +583 -0
  133. package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +982 -218
  134. package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +818 -114
  135. package/Tests/App/SensitiveUrlTokenBootstrap.test.ts +64 -3
  136. package/Tests/App/StatusPage/StatusPageUnsubscribePage.test.tsx +412 -0
  137. package/Tests/Models/IncidentCustomFieldApiSchemaContract.test.ts +217 -0
  138. package/Tests/Models/IncidentCustomFieldCreateSettingsColumns.test.ts +252 -0
  139. package/Tests/Models/StatusPageSubscriberUnsubscribeColumns.test.ts +218 -0
  140. package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +7 -0
  141. package/Tests/Server/API/IncidentSubscriberAudienceAPI.test.ts +1408 -0
  142. package/Tests/Server/API/StatusPageIncidentScope.test.ts +1868 -0
  143. package/Tests/Server/API/StatusPageIncidentScopePostgres.test.ts +790 -0
  144. package/Tests/Server/API/StatusPageManageSubscriptionRecipient.test.ts +10 -1
  145. package/Tests/Server/API/StatusPageManageSubscriptionTemplateVariables.test.ts +206 -18
  146. package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +14 -2
  147. package/Tests/Server/API/StatusPageSubscriberCreateResponse.test.ts +217 -0
  148. package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +103 -2
  149. package/Tests/Server/API/StatusPageUnsubscribeAPI.test.ts +396 -0
  150. package/Tests/Server/Infrastructure/Postgres/AddIncidentStatusPageScopeMigration.test.ts +395 -0
  151. package/Tests/Server/Infrastructure/Postgres/AddIncidentTemplateStatusPageScopeFlagMigration.test.ts +150 -0
  152. package/Tests/Server/Infrastructure/Postgres/AddSubscriberNotificationClaimedAtMigration.test.ts +156 -0
  153. package/Tests/Server/Infrastructure/Postgres/RenameInventoryItemMigration.test.ts +90 -21
  154. package/Tests/Server/Infrastructure/Postgres/SkipHiddenIncidentCreatedNotificationsMigration.test.ts +410 -0
  155. package/Tests/Server/Middleware/IdentityRateLimit.test.ts +551 -1
  156. package/Tests/Server/Middleware/SubscriberNotificationPreviewRateLimit.test.ts +238 -0
  157. package/Tests/Server/Middleware/SubscriberNotificationTestSendRateLimit.test.ts +245 -0
  158. package/Tests/Server/Services/AddStatusPageSubscriberUnsubscribeTokenMigration.test.ts +152 -0
  159. package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +221 -36
  160. package/Tests/Server/Services/DatabaseServiceCompareAndSetColumns.test.ts +191 -0
  161. package/Tests/Server/Services/DomainService.test.ts +525 -0
  162. package/Tests/Server/Services/IncidentAlertAcknowledgeOnDeclare.test.ts +2630 -0
  163. package/Tests/Server/Services/IncidentAlertPostgres.test.ts +1625 -1
  164. package/Tests/Server/Services/IncidentCreatedRenotifyOnPublish.test.ts +681 -0
  165. package/Tests/Server/Services/IncidentCreatedResend.test.ts +775 -0
  166. package/Tests/Server/Services/IncidentCustomFieldRenamePostgres.test.ts +713 -0
  167. package/Tests/Server/Services/IncidentCustomFieldService.test.ts +751 -0
  168. package/Tests/Server/Services/IncidentCustomFieldValueValidation.test.ts +556 -0
  169. package/Tests/Server/Services/IncidentDeclareAcknowledgeAlerts.test.ts +2189 -0
  170. package/Tests/Server/Services/IncidentStatusPageScope.test.ts +2971 -0
  171. package/Tests/Server/Services/IncidentStatusPageScopeFeed.test.ts +131 -0
  172. package/Tests/Server/Services/IncidentStatusPageScopePostgres.test.ts +625 -0
  173. package/Tests/Server/Services/IncidentTemplateCustomFieldsCopy.test.ts +311 -0
  174. package/Tests/Server/Services/IncidentTemplateStatusPageScope.test.ts +363 -0
  175. package/Tests/Server/Services/InventoryItemServiceOverviewCounts.test.ts +345 -0
  176. package/Tests/Server/Services/InventoryOverviewPostgres.test.ts +254 -0
  177. package/Tests/Server/Services/IoTDeviceCredentialService.test.ts +819 -0
  178. package/Tests/Server/Services/LinkedAffectedResourcesCreatedFeed.test.ts +420 -0
  179. package/Tests/Server/Services/PublicNoteEditFeedEventType.test.ts +213 -0
  180. package/Tests/Server/Services/PublicNoteResendAccess.test.ts +994 -0
  181. package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +289 -9
  182. package/Tests/Server/Services/SloAffectedResourceCreatedFeed.test.ts +92 -36
  183. package/Tests/Server/Services/SmsServiceFailIfNotSent.test.ts +67 -0
  184. package/Tests/Server/Services/StatusPageReportIncidentCounts.test.ts +610 -0
  185. package/Tests/Server/Services/StatusPageSubscriberAudienceCounts.test.ts +201 -0
  186. package/Tests/Server/Services/StatusPageSubscriberAudienceCountsPostgres.test.ts +271 -0
  187. package/Tests/Server/Services/StatusPageSubscriberConfirmationTemplateEscaping.test.ts +200 -0
  188. package/Tests/Server/Services/StatusPageSubscriberNotificationLogRedaction.test.ts +54 -0
  189. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateCompile.test.ts +37 -0
  190. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateIncidentRecordAccess.test.ts +772 -0
  191. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookup.test.ts +506 -0
  192. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookupPostgres.test.ts +261 -0
  193. package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +21 -5
  194. package/Tests/Server/Services/StatusPageSubscriberUnsubscribe.test.ts +1618 -0
  195. package/Tests/Server/Services/StatusPageSubscriberUnsubscribeLinkSenders.test.ts +373 -0
  196. package/Tests/Server/Services/StatusPageSubscriberUnsubscribePostgres.test.ts +388 -0
  197. package/Tests/Server/Services/SubscriberNotificationInFlightRequeue.test.ts +500 -0
  198. package/Tests/Server/Types/MarkdownEmailLinks.test.ts +320 -0
  199. package/Tests/Server/Types/Workflow/Components/Email.test.ts +491 -1
  200. package/Tests/Server/Types/Workflow/Components/EmailSmtpPinning.test.ts +398 -0
  201. package/Tests/Server/Utils/AffectedResources/LinkedAffectedResources.test.ts +858 -0
  202. package/Tests/Server/Utils/Alert/AlertStateChangeAuthorization.test.ts +1172 -0
  203. package/Tests/Server/Utils/CustomField/CustomFieldRename.test.ts +586 -0
  204. package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +246 -10
  205. package/Tests/Server/Utils/DataSource/EgressGuardDeploymentConfig.test.ts +226 -0
  206. package/Tests/Server/Utils/DataSource/EgressGuardPinning.test.ts +131 -0
  207. package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +4 -0
  208. package/Tests/Server/Utils/EgressPolicyEnvironment.ts +45 -0
  209. package/Tests/Server/Utils/Mail/PinnedSmtpSocket.test.ts +409 -0
  210. package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +23 -0
  211. package/Tests/Server/Utils/SSRFProtection.test.ts +4 -0
  212. package/Tests/Server/Utils/SSRFProtectionCloudServiceAddresses.test.ts +4 -0
  213. package/Tests/Server/Utils/SSRFProtectionPrivateNetwork.test.ts +4 -0
  214. package/Tests/Server/Utils/SSRFProtectionResolutionDetail.test.ts +314 -0
  215. package/Tests/Server/Utils/StatusPage/IncidentStatusPageScope.test.ts +1699 -0
  216. package/Tests/Server/Utils/StatusPage/IncidentStatusPageScopeQueriesPostgres.test.ts +508 -0
  217. package/Tests/Server/Utils/StatusPage/IncidentSubscriberAudienceCallerPermission.test.ts +246 -0
  218. package/Tests/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.test.ts +1348 -0
  219. package/Tests/Server/Utils/StatusPage/StatusPageReadAccess.test.ts +221 -0
  220. package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.test.ts +132 -0
  221. package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.test.ts +155 -0
  222. package/Tests/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.test.ts +708 -0
  223. package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaim.test.ts +255 -0
  224. package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaimPostgres.test.ts +528 -0
  225. package/Tests/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.test.ts +1238 -0
  226. package/Tests/Server/Utils/StatusPage/SubscriberNotificationFanOut.test.ts +472 -0
  227. package/Tests/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.test.ts +1168 -0
  228. package/Tests/Server/Utils/StatusPage/SubscriberNotificationRunLimit.test.ts +275 -0
  229. package/Tests/Server/Utils/StatusPage/SubscriberNotificationTiming.test.ts +131 -0
  230. package/Tests/Server/Utils/StatusPageResource.test.ts +98 -0
  231. package/Tests/Server/Utils/Topology/InventoryItemEntityKeyIndexMigration.test.ts +386 -0
  232. package/Tests/Server/Utils/Topology/InventoryItemEntityKeyIndexMigrationPostgres.test.ts +430 -0
  233. package/Tests/Server/Utils/Topology/TopologyActivityParityPostgres.test.ts +279 -0
  234. package/Tests/Server/Utils/Topology/TopologyConcurrencyLimiter.test.ts +534 -0
  235. package/Tests/Server/Utils/Topology/TopologyEstateGenerator.ts +624 -0
  236. package/Tests/Server/Utils/Topology/TopologyJsonResponse.test.ts +90 -0
  237. package/Tests/Server/Utils/Topology/TopologyQueries.test.ts +1634 -0
  238. package/Tests/Server/Utils/Topology/TopologyQueriesDifferentialPostgres.test.ts +683 -0
  239. package/Tests/Server/Utils/Topology/TopologyQueriesPostgres.test.ts +2624 -0
  240. package/Tests/Server/Utils/Topology/TopologyReference.ts +1018 -0
  241. package/Tests/Server/Utils/Topology/TopologyRequest.test.ts +643 -0
  242. package/Tests/Server/Utils/Topology/TopologyResponseCache.test.ts +305 -0
  243. package/Tests/Server/Utils/Topology/TopologySql.test.ts +973 -0
  244. package/Tests/Server/Utils/VM/VMRunnerPrivateNetworkWiring.test.ts +17 -0
  245. package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +75 -0
  246. package/Tests/Types/CustomField/CustomFieldSavedViews.test.ts +247 -0
  247. package/Tests/Types/CustomField/CustomFieldTemplateValues.test.ts +186 -0
  248. package/Tests/Types/CustomField/CustomFieldTypeCorrespondence.test.ts +48 -0
  249. package/Tests/Types/CustomField/CustomFieldValueFormat.test.ts +215 -0
  250. package/Tests/Types/CustomField/CustomFieldValueValidator.test.ts +343 -0
  251. package/Tests/Types/CustomField/CustomFieldVariableKey.test.ts +364 -0
  252. package/Tests/Types/SafeHtml.test.ts +102 -0
  253. package/Tests/Types/StatusPage/IncidentCreatedRenotify.test.ts +419 -0
  254. package/Tests/Types/StatusPage/IncidentCreatedResend.test.ts +118 -0
  255. package/Tests/Types/StatusPage/IncidentScopeAddedPagesNotification.test.ts +558 -0
  256. package/Tests/Types/StatusPage/IncidentSubscriberAudience.test.ts +284 -0
  257. package/Tests/Types/StatusPage/StatusPageSubscriberUnsubscribe.test.ts +341 -0
  258. package/Tests/Types/StatusPage/SubscriberNotificationPreview.test.ts +196 -0
  259. package/Tests/Types/StatusPage/SubscriberNotificationResend.test.ts +201 -0
  260. package/Tests/Types/StatusPage/SubscriberNotificationTemplateCompiler.test.ts +277 -0
  261. package/Tests/Types/StatusPage/SubscriberNotificationTemplatePreview.test.ts +179 -0
  262. package/Tests/Types/StatusPage/SubscriberNotificationTemplateVariables.test.ts +324 -0
  263. package/Tests/Types/StatusPage/SubscriberUpdateNotification.test.ts +60 -13
  264. package/Tests/Types/Topology/TopologyTypeRules.test.ts +176 -0
  265. package/Tests/UI/Components/CustomFields/CustomFieldFormFields.test.ts +432 -0
  266. package/Tests/UI/Components/CustomFields/CustomFieldModelFormFields.test.tsx +685 -0
  267. package/Tests/UI/Components/CustomFields/CustomFieldsDetailOrder.test.tsx +469 -0
  268. package/Tests/UI/Components/Forms/ModelFormCustomFieldInputs.test.tsx +325 -0
  269. package/Tests/UI/Components/Forms/ModelFormDropdownOptionsRefused.test.tsx +358 -0
  270. package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +22 -0
  271. package/Tests/UI/Monitor/NetworkPathView.test.tsx +289 -0
  272. package/Tests/Utils/Incident/IncidentFromAlerts.test.ts +302 -0
  273. package/Tests/Utils/Incident/IncidentNoteTemplateVariables.test.ts +623 -0
  274. package/Tests/Utils/Markdown/MarkdownEscape.test.ts +89 -0
  275. package/Tests/Utils/ProbeNetworkFailureUtil.test.ts +992 -0
  276. package/Tests/Utils/RequestFailedDetails.test.ts +575 -0
  277. package/Types/CustomField/CustomFieldOrder.ts +55 -0
  278. package/Types/CustomField/CustomFieldSavedViews.ts +225 -0
  279. package/Types/CustomField/CustomFieldTemplateMerge.ts +71 -0
  280. package/Types/CustomField/CustomFieldType.ts +14 -0
  281. package/Types/CustomField/CustomFieldValueFormat.ts +161 -0
  282. package/Types/CustomField/CustomFieldValueValidator.ts +488 -0
  283. package/Types/CustomField/CustomFieldVariableKey.ts +211 -0
  284. package/Types/Incident/IncidentAlertLink.ts +19 -0
  285. package/Types/SafeHtml.ts +93 -0
  286. package/Types/StatusPage/IncidentCreatedRenotify.ts +224 -0
  287. package/Types/StatusPage/IncidentCreatedResend.ts +108 -0
  288. package/Types/StatusPage/IncidentScopeAddedPagesNotification.ts +343 -0
  289. package/Types/StatusPage/IncidentSubscriberAudience.ts +332 -0
  290. package/Types/StatusPage/StatusPageSubscriberUnsubscribe.ts +405 -0
  291. package/Types/StatusPage/SubscriberNotificationInterruption.ts +32 -0
  292. package/Types/StatusPage/SubscriberNotificationPreview.ts +322 -0
  293. package/Types/StatusPage/SubscriberNotificationResend.ts +132 -0
  294. package/Types/StatusPage/SubscriberNotificationTemplateCompiler.ts +180 -0
  295. package/Types/StatusPage/SubscriberNotificationTemplatePreview.ts +301 -0
  296. package/Types/StatusPage/SubscriberNotificationTemplateVariables.ts +217 -4
  297. package/Types/StatusPage/SubscriberUpdateNotification.ts +32 -11
  298. package/Types/Telemetry/InventoryLiveness.ts +26 -0
  299. package/Types/Topology/TopologyApi.ts +428 -0
  300. package/Types/Topology/TopologyTypeRules.ts +191 -0
  301. package/UI/Components/CustomFields/CustomFieldFormFields.ts +317 -0
  302. package/UI/Components/CustomFields/CustomFieldModelFormFields.tsx +395 -0
  303. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +95 -55
  304. package/UI/Components/Forms/ModelForm.tsx +70 -8
  305. package/UI/Components/Forms/Validation.ts +1 -1
  306. package/UI/Components/ModelFormModal/ModelFormModal.tsx +2 -4
  307. package/UI/Components/ModelTable/AnalyticsModelTable.tsx +2 -6
  308. package/UI/Components/ModelTable/BaseModelTable.tsx +9 -8
  309. package/UI/Components/ModelTable/CustomFieldColumns.tsx +14 -1
  310. package/UI/Components/ModelTable/ModelTable.tsx +6 -7
  311. package/UI/Utils/SensitiveUrlToken.ts +10 -5
  312. package/Utils/API.ts +247 -2
  313. package/Utils/Incident/IncidentFromAlerts.ts +78 -0
  314. package/Utils/Incident/IncidentNoteTemplateVariables.ts +304 -0
  315. package/Utils/Markdown/MarkdownEscape.ts +65 -0
  316. package/Utils/ProbeNetworkFailureUtil.ts +673 -0
  317. package/build/dist/Models/DatabaseModels/Incident.js +267 -1
  318. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  319. package/build/dist/Models/DatabaseModels/IncidentCustomField.js +207 -1
  320. package/build/dist/Models/DatabaseModels/IncidentCustomField.js.map +1 -1
  321. package/build/dist/Models/DatabaseModels/IncidentEpisode.js +31 -0
  322. package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
  323. package/build/dist/Models/DatabaseModels/IncidentTemplate.js +124 -1
  324. package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
  325. package/build/dist/Models/DatabaseModels/InventoryItem.js +20 -11
  326. package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -1
  327. package/build/dist/Models/DatabaseModels/StatusPage.js +56 -0
  328. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  329. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js +131 -0
  330. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js.map +1 -1
  331. package/build/dist/Server/API/IncidentAPI.js +100 -1
  332. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  333. package/build/dist/Server/API/StatusPageAPI.js +417 -176
  334. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  335. package/build/dist/Server/API/StatusPageSubscriberAPI.js +52 -13
  336. package/build/dist/Server/API/StatusPageSubscriberAPI.js.map +1 -1
  337. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.js +165 -0
  338. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.js.map +1 -0
  339. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js +41 -0
  340. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js.map +1 -0
  341. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js +57 -0
  342. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js.map +1 -0
  343. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js +23 -0
  344. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js.map +1 -0
  345. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js +40 -0
  346. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js.map +1 -0
  347. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js +26 -0
  348. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js.map +1 -0
  349. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js +89 -0
  350. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js.map +1 -0
  351. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js +29 -0
  352. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js.map +1 -0
  353. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +16 -0
  354. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  355. package/build/dist/Server/Infrastructure/Semaphore.js +3 -0
  356. package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
  357. package/build/dist/Server/Middleware/IdentityRateLimit.js +89 -3
  358. package/build/dist/Server/Middleware/IdentityRateLimit.js.map +1 -1
  359. package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js +149 -0
  360. package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js.map +1 -0
  361. package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js +190 -0
  362. package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js.map +1 -0
  363. package/build/dist/Server/Services/AlertService.js +21 -33
  364. package/build/dist/Server/Services/AlertService.js.map +1 -1
  365. package/build/dist/Server/Services/DatabaseService.js +56 -7
  366. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  367. package/build/dist/Server/Services/IncidentAlertService.js +457 -3
  368. package/build/dist/Server/Services/IncidentAlertService.js.map +1 -1
  369. package/build/dist/Server/Services/IncidentCustomFieldService.js +204 -8
  370. package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
  371. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +24 -0
  372. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
  373. package/build/dist/Server/Services/IncidentEpisodeService.js +14 -0
  374. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  375. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +26 -0
  376. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
  377. package/build/dist/Server/Services/IncidentPublicNoteService.js +26 -1
  378. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  379. package/build/dist/Server/Services/IncidentService.js +1148 -42
  380. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  381. package/build/dist/Server/Services/IncidentStateTimelineService.js +12 -0
  382. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  383. package/build/dist/Server/Services/IncidentTemplateService.js +83 -0
  384. package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
  385. package/build/dist/Server/Services/InventoryItemService.js +31 -0
  386. package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
  387. package/build/dist/Server/Services/IoTDeviceCredentialService.js +12 -5
  388. package/build/dist/Server/Services/IoTDeviceCredentialService.js.map +1 -1
  389. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +26 -1
  390. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  391. package/build/dist/Server/Services/ScheduledMaintenanceService.js +84 -57
  392. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  393. package/build/dist/Server/Services/SmsService.js +2 -22
  394. package/build/dist/Server/Services/SmsService.js.map +1 -1
  395. package/build/dist/Server/Services/StatusPageService.js +47 -9
  396. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  397. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +237 -32
  398. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
  399. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js +109 -0
  400. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js.map +1 -1
  401. package/build/dist/Server/Services/StatusPageSubscriberService.js +925 -99
  402. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  403. package/build/dist/Server/Services/UserNotificationRuleService.js +55 -51
  404. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  405. package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js +42 -25
  406. package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js.map +1 -1
  407. package/build/dist/Server/Types/Markdown.js +110 -6
  408. package/build/dist/Server/Types/Markdown.js.map +1 -1
  409. package/build/dist/Server/Types/Workflow/Components/Email.js +55 -6
  410. package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
  411. package/build/dist/Server/Utils/AffectedResources/LinkedAffectedResources.js +405 -0
  412. package/build/dist/Server/Utils/AffectedResources/LinkedAffectedResources.js.map +1 -0
  413. package/build/dist/Server/Utils/Alert/AlertStateChangeAuthorization.js +112 -0
  414. package/build/dist/Server/Utils/Alert/AlertStateChangeAuthorization.js.map +1 -0
  415. package/build/dist/Server/Utils/CustomField/CustomFieldRename.js +211 -0
  416. package/build/dist/Server/Utils/CustomField/CustomFieldRename.js.map +1 -0
  417. package/build/dist/Server/Utils/DataSource/EgressGuard.js +41 -12
  418. package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
  419. package/build/dist/Server/Utils/Inventory/InventoryOverviewAggregation.js +81 -0
  420. package/build/dist/Server/Utils/Inventory/InventoryOverviewAggregation.js.map +1 -0
  421. package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js +101 -0
  422. package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js.map +1 -0
  423. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +9 -0
  424. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  425. package/build/dist/Server/Utils/Response.js +30 -0
  426. package/build/dist/Server/Utils/Response.js.map +1 -1
  427. package/build/dist/Server/Utils/SSRFProtection.js +48 -6
  428. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
  429. package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js +631 -0
  430. package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js.map +1 -0
  431. package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js +403 -0
  432. package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js.map +1 -0
  433. package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js +451 -0
  434. package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js.map +1 -0
  435. package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js +13 -0
  436. package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js.map +1 -0
  437. package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js +91 -0
  438. package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js.map +1 -0
  439. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js +101 -0
  440. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js.map +1 -0
  441. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js +47 -0
  442. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js.map +1 -0
  443. package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js +273 -0
  444. package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js.map +1 -0
  445. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js +73 -0
  446. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js.map +1 -0
  447. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js +722 -0
  448. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js.map +1 -0
  449. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js +102 -0
  450. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js.map +1 -0
  451. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js +527 -0
  452. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js.map +1 -0
  453. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js +278 -0
  454. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js.map +1 -0
  455. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js +137 -0
  456. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js.map +1 -0
  457. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js +103 -0
  458. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js.map +1 -0
  459. package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js +114 -0
  460. package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js.map +1 -0
  461. package/build/dist/Server/Utils/StatusPageResource.js +39 -7
  462. package/build/dist/Server/Utils/StatusPageResource.js.map +1 -1
  463. package/build/dist/Server/Utils/Topology/TopologyConcurrencyLimiter.js +178 -0
  464. package/build/dist/Server/Utils/Topology/TopologyConcurrencyLimiter.js.map +1 -0
  465. package/build/dist/Server/Utils/Topology/TopologyQueries.js +884 -0
  466. package/build/dist/Server/Utils/Topology/TopologyQueries.js.map +1 -0
  467. package/build/dist/Server/Utils/Topology/TopologyRequest.js +275 -0
  468. package/build/dist/Server/Utils/Topology/TopologyRequest.js.map +1 -0
  469. package/build/dist/Server/Utils/Topology/TopologyResponseCache.js +146 -0
  470. package/build/dist/Server/Utils/Topology/TopologyResponseCache.js.map +1 -0
  471. package/build/dist/Server/Utils/Topology/TopologySql.js +688 -0
  472. package/build/dist/Server/Utils/Topology/TopologySql.js.map +1 -0
  473. package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
  474. package/build/dist/Server/Utils/VM/VMRunner.js +1 -0
  475. package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
  476. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js +3 -0
  477. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js.map +1 -1
  478. package/build/dist/Types/CustomField/CustomFieldOrder.js +36 -0
  479. package/build/dist/Types/CustomField/CustomFieldOrder.js.map +1 -0
  480. package/build/dist/Types/CustomField/CustomFieldSavedViews.js +135 -0
  481. package/build/dist/Types/CustomField/CustomFieldSavedViews.js.map +1 -0
  482. package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js +27 -0
  483. package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js.map +1 -0
  484. package/build/dist/Types/CustomField/CustomFieldType.js +12 -0
  485. package/build/dist/Types/CustomField/CustomFieldType.js.map +1 -1
  486. package/build/dist/Types/CustomField/CustomFieldValueFormat.js +126 -0
  487. package/build/dist/Types/CustomField/CustomFieldValueFormat.js.map +1 -0
  488. package/build/dist/Types/CustomField/CustomFieldValueValidator.js +280 -0
  489. package/build/dist/Types/CustomField/CustomFieldValueValidator.js.map +1 -0
  490. package/build/dist/Types/CustomField/CustomFieldVariableKey.js +150 -0
  491. package/build/dist/Types/CustomField/CustomFieldVariableKey.js.map +1 -0
  492. package/build/dist/Types/Incident/IncidentAlertLink.js +17 -0
  493. package/build/dist/Types/Incident/IncidentAlertLink.js.map +1 -1
  494. package/build/dist/Types/SafeHtml.js +84 -0
  495. package/build/dist/Types/SafeHtml.js.map +1 -0
  496. package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js +117 -0
  497. package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js.map +1 -0
  498. package/build/dist/Types/StatusPage/IncidentCreatedResend.js +86 -0
  499. package/build/dist/Types/StatusPage/IncidentCreatedResend.js.map +1 -0
  500. package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js +246 -0
  501. package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js.map +1 -0
  502. package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js +183 -0
  503. package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js.map +1 -0
  504. package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js +277 -0
  505. package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js.map +1 -0
  506. package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js +27 -0
  507. package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js.map +1 -0
  508. package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js +190 -0
  509. package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js.map +1 -0
  510. package/build/dist/Types/StatusPage/SubscriberNotificationResend.js +102 -0
  511. package/build/dist/Types/StatusPage/SubscriberNotificationResend.js.map +1 -0
  512. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js +97 -0
  513. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js.map +1 -0
  514. package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js +175 -0
  515. package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js.map +1 -0
  516. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js +144 -4
  517. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js.map +1 -1
  518. package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js +29 -9
  519. package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js.map +1 -1
  520. package/build/dist/Types/Telemetry/InventoryLiveness.js +23 -0
  521. package/build/dist/Types/Telemetry/InventoryLiveness.js.map +1 -1
  522. package/build/dist/Types/Topology/TopologyApi.js +55 -0
  523. package/build/dist/Types/Topology/TopologyApi.js.map +1 -0
  524. package/build/dist/Types/Topology/TopologyTypeRules.js +165 -0
  525. package/build/dist/Types/Topology/TopologyTypeRules.js.map +1 -0
  526. package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js +169 -0
  527. package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js.map +1 -0
  528. package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js +212 -0
  529. package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js.map +1 -0
  530. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +64 -49
  531. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  532. package/build/dist/UI/Components/Forms/ModelForm.js +39 -5
  533. package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
  534. package/build/dist/UI/Components/Forms/Validation.js +1 -1
  535. package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
  536. package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
  537. package/build/dist/UI/Components/ModelTable/AnalyticsModelTable.js.map +1 -1
  538. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +2 -2
  539. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  540. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +13 -1
  541. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
  542. package/build/dist/UI/Components/ModelTable/ModelTable.js +1 -1
  543. package/build/dist/UI/Components/ModelTable/ModelTable.js.map +1 -1
  544. package/build/dist/UI/Utils/SensitiveUrlToken.js +10 -5
  545. package/build/dist/UI/Utils/SensitiveUrlToken.js.map +1 -1
  546. package/build/dist/Utils/API.js +177 -1
  547. package/build/dist/Utils/API.js.map +1 -1
  548. package/build/dist/Utils/Incident/IncidentFromAlerts.js +38 -0
  549. package/build/dist/Utils/Incident/IncidentFromAlerts.js.map +1 -1
  550. package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js +155 -0
  551. package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js.map +1 -0
  552. package/build/dist/Utils/Markdown/MarkdownEscape.js +48 -0
  553. package/build/dist/Utils/Markdown/MarkdownEscape.js.map +1 -1
  554. package/build/dist/Utils/ProbeNetworkFailureUtil.js +460 -0
  555. package/build/dist/Utils/ProbeNetworkFailureUtil.js.map +1 -0
  556. package/package.json +1 -1
@@ -0,0 +1,1051 @@
1
+ import "@testing-library/jest-dom";
2
+ import {
3
+ afterEach,
4
+ beforeEach,
5
+ describe,
6
+ expect,
7
+ jest,
8
+ test,
9
+ } from "@jest/globals";
10
+ import {
11
+ act,
12
+ cleanup,
13
+ fireEvent,
14
+ render,
15
+ screen,
16
+ waitFor,
17
+ } from "@testing-library/react";
18
+ import React, { ReactElement } from "react";
19
+ import { MemoryRouter } from "react-router-dom";
20
+ import getJestMockFunction, { MockFunction } from "../../MockType";
21
+
22
+ /*
23
+ * The incident's Settings tab: the 'Status Page Scope' card, where the status
24
+ * pages an incident is limited to are changed after it was declared.
25
+ *
26
+ * Three parts:
27
+ * - the page: what the card reads, which fields its edit form has once the
28
+ * incident is loaded (the added-pages checkbox only when the 'created'
29
+ * notification can go out), and what the picker's footer warns about -
30
+ * removing pages that were already told, clearing the scope, picking a
31
+ * page that lists none of the monitors, and having no status page to pick
32
+ * (the card itself is stubbed and its props recorded);
33
+ * - the read-only view the card shows, including the deleted-pages warning;
34
+ * - the added-pages checkbox inside the real ModelForm: shown only while the
35
+ * edit adds a page, and sent as a misc data prop - never a column - only
36
+ * while ticked.
37
+ */
38
+
39
+ const recordedCards: Array<Record<string, unknown>> = [];
40
+ let loadedIncident: unknown = null;
41
+ let savedRequests: Array<{
42
+ model: unknown;
43
+ miscDataProps: Record<string, unknown> | undefined;
44
+ }> = [];
45
+
46
+ const countMock: MockFunction = getJestMockFunction();
47
+ const postMock: MockFunction = getJestMockFunction();
48
+ const listMock: MockFunction = getJestMockFunction();
49
+
50
+ jest.mock("../../../UI/Components/ModelDetail/CardModelDetail", () => {
51
+ return {
52
+ __esModule: true,
53
+ default: (props: Record<string, unknown>): ReactElement => {
54
+ recordedCards.push(props);
55
+ return React.createElement("div", {
56
+ "data-testid": `stub-card-${props["name"] as string}`,
57
+ });
58
+ },
59
+ };
60
+ });
61
+
62
+ jest.mock(
63
+ "../../../../App/FeatureSet/Dashboard/src/Components/Reminders/NextReminderCountdown",
64
+ () => {
65
+ return {
66
+ __esModule: true,
67
+ ReminderRuleScope: { Incident: "Incident" },
68
+ default: (): ReactElement => {
69
+ return React.createElement("div");
70
+ },
71
+ };
72
+ },
73
+ );
74
+
75
+ jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
76
+ return {
77
+ __esModule: true,
78
+ default: {
79
+ getItem: async (): Promise<unknown> => {
80
+ return loadedIncident;
81
+ },
82
+ getList: (...args: Array<unknown>): unknown => {
83
+ return listMock(...args);
84
+ },
85
+ count: (...args: Array<unknown>): unknown => {
86
+ return countMock(...args);
87
+ },
88
+ getCommonHeaders: (): Record<string, unknown> => {
89
+ return { tenantid: "project-1" };
90
+ },
91
+ createOrUpdate: async (request: {
92
+ model: unknown;
93
+ miscDataProps?: Record<string, unknown>;
94
+ }): Promise<{ data: Record<string, unknown> }> => {
95
+ savedRequests.push({
96
+ model: request.model,
97
+ miscDataProps: request.miscDataProps,
98
+ });
99
+ return { data: {} };
100
+ },
101
+ },
102
+ };
103
+ });
104
+
105
+ jest.mock("../../../UI/Utils/API/API", () => {
106
+ return {
107
+ __esModule: true,
108
+ default: {
109
+ post: (...args: Array<unknown>): unknown => {
110
+ return postMock(...args);
111
+ },
112
+ getFriendlyMessage: (): string => {
113
+ return "Something went wrong";
114
+ },
115
+ },
116
+ };
117
+ });
118
+
119
+ jest.mock("../../../UI/Utils/Permission", () => {
120
+ return {
121
+ __esModule: true,
122
+ default: {
123
+ getAllPermissions: (): Array<string> => {
124
+ return ["IncidentMember"];
125
+ },
126
+ getProjectPermissions: (): null => {
127
+ return null;
128
+ },
129
+ getGlobalPermissions: (): { globalPermissions: Array<string> } => {
130
+ return { globalPermissions: ["IncidentMember"] };
131
+ },
132
+ },
133
+ };
134
+ });
135
+
136
+ jest.mock("../../../UI/Utils/User", () => {
137
+ return {
138
+ __esModule: true,
139
+ default: {
140
+ isMasterAdmin: (): boolean => {
141
+ return false;
142
+ },
143
+ getUserId: (): null => {
144
+ return null;
145
+ },
146
+ },
147
+ };
148
+ });
149
+
150
+ jest.mock("../../../UI/Utils/Translation", () => {
151
+ return {
152
+ __esModule: true,
153
+ default: () => {
154
+ return {
155
+ translateString: (value: string | undefined): string | undefined => {
156
+ return value;
157
+ },
158
+ translateValue: (value: unknown): unknown => {
159
+ return value;
160
+ },
161
+ };
162
+ },
163
+ };
164
+ });
165
+
166
+ import IncidentSettings from "../../../../App/FeatureSet/Dashboard/src/Pages/Incidents/View/Settings";
167
+ import { getIncidentScopeAddedPagesFormField } from "../../../../App/FeatureSet/Dashboard/src/Components/Incident/IncidentScopeAddedPagesFormField";
168
+ import IncidentStatusPageScopeCopy, {
169
+ formatScopeText,
170
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/Incident/IncidentStatusPageScopeCopy";
171
+ import IncidentStatusPageScopeView from "../../../../App/FeatureSet/Dashboard/src/Components/Incident/IncidentStatusPageScopeView";
172
+ import PageComponentProps from "../../../../App/FeatureSet/Dashboard/src/Pages/PageComponentProps";
173
+ import Incident from "../../../Models/DatabaseModels/Incident";
174
+ import Monitor from "../../../Models/DatabaseModels/Monitor";
175
+ import StatusPage from "../../../Models/DatabaseModels/StatusPage";
176
+ import HTTPResponse from "../../../Types/API/HTTPResponse";
177
+ import Route from "../../../Types/API/Route";
178
+ import { JSONObject } from "../../../Types/JSON";
179
+ import ObjectID from "../../../Types/ObjectID";
180
+ import IncidentScopeAddedPagesNotification from "../../../Types/StatusPage/IncidentScopeAddedPagesNotification";
181
+ import IncidentSubscriberAudience from "../../../Types/StatusPage/IncidentSubscriberAudience";
182
+ import StatusPageSubscriberNotificationStatus from "../../../Types/StatusPage/StatusPageSubscriberNotificationStatus";
183
+ import ModelForm, {
184
+ FormType,
185
+ ModelField,
186
+ } from "../../../UI/Components/Forms/ModelForm";
187
+ import Fields from "../../../UI/Components/Forms/Types/Fields";
188
+ import FormFieldSchemaType from "../../../UI/Components/Forms/Types/FormFieldSchemaType";
189
+ import FormValues from "../../../UI/Components/Forms/Types/FormValues";
190
+ import FieldType from "../../../UI/Components/Types/FieldType";
191
+ import Navigation from "../../../UI/Utils/Navigation";
192
+
193
+ const INCIDENT_ID: string = "11111111-1111-4111-8111-111111111111";
194
+ const MONITOR_ID: string = "c0000000-0000-4000-8000-000000000001";
195
+ const SITE_03: string = "b0000000-0000-4000-8000-000000000003";
196
+ const SITE_05: string = "b0000000-0000-4000-8000-000000000005";
197
+ const SITE_07: string = "b0000000-0000-4000-8000-000000000007";
198
+
199
+ const pageProps: PageComponentProps = {
200
+ pageRoute: new Route("/settings"),
201
+ currentProject: null,
202
+ hasPaymentMethod: false,
203
+ };
204
+
205
+ function page(id: string, name: string): StatusPage {
206
+ const statusPage: StatusPage = new StatusPage();
207
+ statusPage._id = id;
208
+ statusPage.name = name;
209
+ return statusPage;
210
+ }
211
+
212
+ interface ScopeShape {
213
+ statusPages?: Array<StatusPage> | undefined;
214
+ isScoped?: boolean | undefined;
215
+ // null: no record (told before the record existed, or never sent).
216
+ notified?: Array<string> | null | undefined;
217
+ status?: StatusPageSubscriberNotificationStatus | undefined;
218
+ notifyOnCreate?: boolean | undefined;
219
+ isVisible?: boolean | undefined;
220
+ isPrivate?: boolean | undefined;
221
+ }
222
+
223
+ // An incident on one monitor, limited to Site 03 and Site 07, told on Site 03.
224
+ function buildIncident(shape: ScopeShape = {}): Incident {
225
+ const incident: Incident = new Incident();
226
+ incident.id = new ObjectID(INCIDENT_ID);
227
+ incident.statusPages = shape.statusPages ?? [
228
+ page(SITE_03, "Site 03"),
229
+ page(SITE_07, "Site 07"),
230
+ ];
231
+ incident.isScopedToStatusPages = shape.isScoped ?? true;
232
+ incident.statusPagesNotifiedOnCreation = (
233
+ shape.notified === undefined ? [SITE_03] : shape.notified
234
+ ) as Array<string>;
235
+ incident.subscriberNotificationStatusOnIncidentCreated =
236
+ shape.status ?? StatusPageSubscriberNotificationStatus.Success;
237
+ incident.shouldStatusPageSubscribersBeNotifiedOnIncidentCreated =
238
+ shape.notifyOnCreate ?? true;
239
+ incident.isVisibleOnStatusPage = shape.isVisible ?? true;
240
+ incident.isPrivate = shape.isPrivate ?? false;
241
+
242
+ const monitor: Monitor = new Monitor();
243
+ monitor._id = MONITOR_ID;
244
+ incident.monitors = [monitor];
245
+
246
+ return incident;
247
+ }
248
+
249
+ interface ScopeCardProps {
250
+ cardProps: { title: string; description: string };
251
+ editButtonText: string;
252
+ formFields: Fields<Incident>;
253
+ modelDetailProps: {
254
+ fields: Array<{
255
+ field: Record<string, unknown>;
256
+ title: string;
257
+ fieldType: FieldType;
258
+ getElement: (item: Incident) => ReactElement;
259
+ }>;
260
+ selectMoreFields?: Record<string, unknown>;
261
+ onItemLoaded?: (item: Incident) => void;
262
+ modelId: ObjectID;
263
+ };
264
+ }
265
+
266
+ function scopeCard(): ScopeCardProps {
267
+ const cards: Array<Record<string, unknown>> = recordedCards.filter(
268
+ (props: Record<string, unknown>): boolean => {
269
+ return props["name"] === "Status Page Scope";
270
+ },
271
+ );
272
+
273
+ expect(cards.length).toBeGreaterThan(0);
274
+ return cards[cards.length - 1] as unknown as ScopeCardProps;
275
+ }
276
+
277
+ function fieldKeys(fields: Fields<Incident>): Array<string> {
278
+ return fields.map((field: Fields<Incident>[number]): string => {
279
+ return (
280
+ field.overrideFieldKey || Object.keys(field.field || {})[0] || "unknown"
281
+ );
282
+ });
283
+ }
284
+
285
+ function pickerField(): Fields<Incident>[number] {
286
+ return scopeCard().formFields[0]!;
287
+ }
288
+
289
+ async function renderSettings(): Promise<void> {
290
+ await act(async (): Promise<void> => {
291
+ render(
292
+ <MemoryRouter>
293
+ <IncidentSettings {...pageProps} />
294
+ </MemoryRouter>,
295
+ );
296
+ });
297
+ }
298
+
299
+ async function loadIncident(shape: ScopeShape = {}): Promise<void> {
300
+ await act(async (): Promise<void> => {
301
+ scopeCard().modelDetailProps.onItemLoaded!(buildIncident(shape));
302
+ });
303
+ }
304
+
305
+ // What the picker's footer says for these form values.
306
+ async function renderFooter(statusPages: unknown): Promise<void> {
307
+ const footer: ReactElement | undefined = pickerField().getFooterElement!({
308
+ statusPages: statusPages,
309
+ } as FormValues<Incident>);
310
+
311
+ await act(async (): Promise<void> => {
312
+ render(<MemoryRouter>{footer}</MemoryRouter>);
313
+ });
314
+ }
315
+
316
+ function audienceResponse(
317
+ notListing: Array<{ statusPageId: string; name: string }> = [],
318
+ ): HTTPResponse<JSONObject> {
319
+ return new HTTPResponse<JSONObject>(
320
+ 200,
321
+ IncidentSubscriberAudience.toJSON({
322
+ hasMonitors: true,
323
+ isScoped: true,
324
+ isHiddenFromStatusPages: false,
325
+ statusPages: [],
326
+ hiddenStatusPageCount: 0,
327
+ excludedStatusPages: [],
328
+ selectedStatusPagesNotListingMonitors: notListing,
329
+ }),
330
+ {},
331
+ );
332
+ }
333
+
334
+ beforeEach(() => {
335
+ jest
336
+ .spyOn(Navigation, "getLastParamAsObjectID")
337
+ .mockImplementation((): ObjectID => {
338
+ return new ObjectID(INCIDENT_ID);
339
+ });
340
+
341
+ countMock.mockReset();
342
+ countMock.mockResolvedValue(3 as never);
343
+ listMock.mockReset();
344
+ listMock.mockResolvedValue({
345
+ data: [],
346
+ count: 0,
347
+ skip: 0,
348
+ limit: 0,
349
+ } as never);
350
+ postMock.mockReset();
351
+ postMock.mockResolvedValue(audienceResponse() as never);
352
+ });
353
+
354
+ afterEach(() => {
355
+ cleanup();
356
+ recordedCards.length = 0;
357
+ loadedIncident = null;
358
+ savedRequests = [];
359
+ jest.restoreAllMocks();
360
+ });
361
+
362
+ describe("incident Settings tab: the 'Status Page Scope' card", () => {
363
+ test("titled from the shared copy, beside the other settings cards", async () => {
364
+ await renderSettings();
365
+
366
+ expect(scopeCard().cardProps.title).toBe(
367
+ IncidentStatusPageScopeCopy.settingsCardTitle,
368
+ );
369
+ expect(scopeCard().cardProps.description).toBe(
370
+ IncidentStatusPageScopeCopy.settingsCardDescription,
371
+ );
372
+ expect(scopeCard().editButtonText).toBe(
373
+ IncidentStatusPageScopeCopy.settingsEditButton,
374
+ );
375
+ expect(scopeCard().modelDetailProps.modelId.toString()).toBe(INCIDENT_ID);
376
+
377
+ const names: Array<string> = recordedCards.map(
378
+ (props: Record<string, unknown>): string => {
379
+ return props["name"] as string;
380
+ },
381
+ );
382
+ expect(names.indexOf("Status Page Scope")).toBe(
383
+ names.indexOf("Incident Settings") + 1,
384
+ );
385
+ });
386
+
387
+ test("reads what its warnings and checkbox need with the card's own item", async () => {
388
+ await renderSettings();
389
+
390
+ expect(scopeCard().modelDetailProps.selectMoreFields).toEqual({
391
+ isScopedToStatusPages: true,
392
+ statusPagesNotifiedOnCreation: true,
393
+ subscriberNotificationStatusOnIncidentCreated: true,
394
+ shouldStatusPageSubscribersBeNotifiedOnIncidentCreated: true,
395
+ isVisibleOnStatusPage: true,
396
+ isPrivate: true,
397
+ monitors: { _id: true },
398
+ });
399
+ expect(scopeCard().modelDetailProps.fields[0]!.field).toEqual({
400
+ statusPages: { _id: true, name: true },
401
+ });
402
+ });
403
+
404
+ test("the picker is a status page multi-select from the shared copy", async () => {
405
+ await renderSettings();
406
+
407
+ const picker: Fields<Incident>[number] = pickerField();
408
+
409
+ expect(picker.field).toEqual({ statusPages: true });
410
+ expect(picker.fieldType).toBe(FormFieldSchemaType.MultiSelectDropdown);
411
+ expect(picker.dropdownModal).toEqual({
412
+ type: StatusPage,
413
+ labelField: "name",
414
+ valueField: "_id",
415
+ });
416
+ expect(picker.title).toBe(IncidentStatusPageScopeCopy.pickerTitle);
417
+ expect(picker.description).toBe(
418
+ IncidentStatusPageScopeCopy.pickerDescription,
419
+ );
420
+ expect(picker.placeholder).toBe(
421
+ IncidentStatusPageScopeCopy.pickerPlaceholder,
422
+ );
423
+ expect(picker.required).toBe(false);
424
+ });
425
+
426
+ test("before the incident loads, only the picker", async () => {
427
+ await renderSettings();
428
+
429
+ expect(fieldKeys(scopeCard().formFields)).toEqual(["statusPages"]);
430
+ });
431
+
432
+ test("offers the added-pages checkbox when the 'created' notification can go out", async () => {
433
+ await renderSettings();
434
+ await loadIncident();
435
+
436
+ expect(fieldKeys(scopeCard().formFields)).toEqual([
437
+ "statusPages",
438
+ IncidentScopeAddedPagesNotification.miscDataKey,
439
+ ]);
440
+
441
+ const checkbox: Fields<Incident>[number] = scopeCard().formFields[1]!;
442
+ expect(checkbox.defaultValue).toBe(true);
443
+ expect(checkbox.title).toBe(
444
+ IncidentScopeAddedPagesNotification.formFieldTitle,
445
+ );
446
+ });
447
+
448
+ /*
449
+ * The box is offered exactly when ticking it would send something (the
450
+ * server's own rule), so for these it never shows, even for an edit that
451
+ * adds a page that was never told.
452
+ */
453
+ test.each([
454
+ ["notifying subscribers on creation is off", { notifyOnCreate: false }],
455
+ ["the incident is hidden from status pages", { isVisible: false }],
456
+ ["the incident is private", { isPrivate: true }],
457
+ [
458
+ "the incident was told before the record of told pages existed",
459
+ { notified: null },
460
+ ],
461
+ [
462
+ "the incident was told before the record existed, even after a failure",
463
+ { notified: null, status: StatusPageSubscriberNotificationStatus.Failed },
464
+ ],
465
+ [
466
+ "the notification is queued: it reaches the added page anyway",
467
+ { status: StatusPageSubscriberNotificationStatus.Pending },
468
+ ],
469
+ [
470
+ "the notification is being sent: it reaches the added page anyway",
471
+ { status: StatusPageSubscriberNotificationStatus.InProgress },
472
+ ],
473
+ ] as Array<[string, ScopeShape]>)(
474
+ "does not offer it when %s",
475
+ async (_label: string, shape: ScopeShape) => {
476
+ await renderSettings();
477
+ await loadIncident(shape);
478
+
479
+ const checkbox: Fields<Incident>[number] | undefined =
480
+ scopeCard().formFields[1];
481
+
482
+ expect(
483
+ checkbox?.showIf?.({
484
+ statusPages: [SITE_03, SITE_07, SITE_05],
485
+ } as FormValues<Incident>) ?? false,
486
+ ).toBe(false);
487
+ },
488
+ );
489
+
490
+ test("offers it for an incident that was never announced, for the pages added", async () => {
491
+ await renderSettings();
492
+ await loadIncident({
493
+ notified: null,
494
+ status: StatusPageSubscriberNotificationStatus.Skipped,
495
+ });
496
+
497
+ expect(
498
+ scopeCard().formFields[1]!.showIf!({
499
+ statusPages: [SITE_03, SITE_07, SITE_05],
500
+ } as FormValues<Incident>),
501
+ ).toBe(true);
502
+ });
503
+
504
+ test("does not offer it when every added page was told already", async () => {
505
+ // Narrowed to Site 03 after Site 07 was told; adding Site 07 back.
506
+ await renderSettings();
507
+ await loadIncident({
508
+ statusPages: [page(SITE_03, "Site 03")],
509
+ notified: [SITE_03, SITE_07],
510
+ });
511
+
512
+ expect(
513
+ scopeCard().formFields[1]!.showIf!({
514
+ statusPages: [SITE_03, SITE_07],
515
+ } as FormValues<Incident>),
516
+ ).toBe(false);
517
+ });
518
+
519
+ test("the checkbox compares the form against the pages the card loaded", async () => {
520
+ await renderSettings();
521
+ await loadIncident();
522
+
523
+ const checkbox: Fields<Incident>[number] = scopeCard().formFields[1]!;
524
+
525
+ expect(
526
+ checkbox.showIf!({
527
+ statusPages: [SITE_03, SITE_07],
528
+ } as FormValues<Incident>),
529
+ ).toBe(false);
530
+ expect(
531
+ checkbox.showIf!({
532
+ statusPages: [SITE_03, SITE_07, SITE_05],
533
+ } as FormValues<Incident>),
534
+ ).toBe(true);
535
+ expect(
536
+ checkbox.showIf!({ statusPages: [SITE_03] } as FormValues<Incident>),
537
+ ).toBe(false);
538
+ });
539
+ });
540
+
541
+ describe("the two settings cards keep each other current", () => {
542
+ function latestCard(name: string): Record<string, unknown> {
543
+ const cards: Array<Record<string, unknown>> = recordedCards.filter(
544
+ (props: Record<string, unknown>): boolean => {
545
+ return props["name"] === name;
546
+ },
547
+ );
548
+
549
+ return cards[cards.length - 1]!;
550
+ }
551
+
552
+ test("saving the scope reloads the visibility card, whose offer depends on the 'created' notification", async () => {
553
+ await renderSettings();
554
+
555
+ const before: unknown = latestCard("Incident Settings")["refresher"];
556
+
557
+ await act(async (): Promise<void> => {
558
+ (
559
+ latestCard("Status Page Scope")["onSaveSuccess"] as (
560
+ item: Incident,
561
+ ) => void
562
+ )(buildIncident());
563
+ });
564
+
565
+ expect(latestCard("Incident Settings")["refresher"]).toBe(!before);
566
+ });
567
+
568
+ test("publishing in the visibility card reloads the scope card, whose checkbox depends on it", async () => {
569
+ await renderSettings();
570
+
571
+ const before: unknown = latestCard("Status Page Scope")["refresher"];
572
+
573
+ await act(async (): Promise<void> => {
574
+ (
575
+ latestCard("Incident Settings")["onSaveSuccess"] as (
576
+ item: Incident,
577
+ ) => void
578
+ )(buildIncident());
579
+ });
580
+
581
+ expect(latestCard("Status Page Scope")["refresher"]).toBe(!before);
582
+ });
583
+ });
584
+
585
+ describe("the picker's warnings", () => {
586
+ test("removing a page that was already told warns it hears nothing more", async () => {
587
+ await renderSettings();
588
+ await loadIncident();
589
+
590
+ await renderFooter([SITE_07]);
591
+
592
+ expect(
593
+ screen.getByTestId("incident-scope-removing-notified-pages"),
594
+ ).toHaveTextContent(
595
+ formatScopeText(
596
+ IncidentStatusPageScopeCopy.removingNotifiedPagesWarning,
597
+ { names: "Site 03" },
598
+ ),
599
+ );
600
+ });
601
+
602
+ test("removing a page that was never told needs no warning", async () => {
603
+ await renderSettings();
604
+ await loadIncident();
605
+
606
+ await renderFooter([SITE_03]);
607
+
608
+ expect(
609
+ screen.queryByTestId("incident-scope-removing-notified-pages"),
610
+ ).toBeNull();
611
+ expect(screen.queryByTestId("incident-scope-clearing")).toBeNull();
612
+ });
613
+
614
+ test("clearing the scope warns the incident reaches every page again", async () => {
615
+ await renderSettings();
616
+ await loadIncident({ notified: [] });
617
+
618
+ await renderFooter([]);
619
+
620
+ expect(screen.getByTestId("incident-scope-clearing")).toHaveTextContent(
621
+ IncidentStatusPageScopeCopy.clearingScopeWarning,
622
+ );
623
+ expect(
624
+ screen.queryByTestId("incident-scope-removing-notified-pages"),
625
+ ).toBeNull();
626
+ });
627
+
628
+ /*
629
+ * An incident its monitors created is not limited, so it told every page
630
+ * that lists them - an internal 'All Sites' page next to the site pages,
631
+ * say. Limiting it to Site 03 drops All Sites, which then hears nothing
632
+ * more, not even that it was resolved; that page is in no list the card
633
+ * loaded, so its name is read for the warning.
634
+ */
635
+ test("limiting an unscoped incident warns about the told pages it leaves out", async () => {
636
+ const ALL_SITES: string = "b0000000-0000-4000-8000-0000000000aa";
637
+ listMock.mockResolvedValue({
638
+ data: [page(ALL_SITES, "All Sites")],
639
+ count: 1,
640
+ skip: 0,
641
+ limit: 1,
642
+ } as never);
643
+
644
+ await renderSettings();
645
+ await loadIncident({
646
+ isScoped: false,
647
+ statusPages: [],
648
+ notified: [ALL_SITES, SITE_03],
649
+ });
650
+
651
+ // The told pages' names, read by id.
652
+ expect(listMock).toHaveBeenCalledTimes(1);
653
+ const request: { modelType: unknown; select: Record<string, unknown> } =
654
+ listMock.mock.calls[0]![0] as {
655
+ modelType: unknown;
656
+ select: Record<string, unknown>;
657
+ };
658
+ expect(request.modelType).toBe(StatusPage);
659
+ expect(request.select).toEqual({ _id: true, name: true });
660
+
661
+ await renderFooter([SITE_03]);
662
+
663
+ expect(
664
+ screen.getByTestId("incident-scope-removing-notified-pages"),
665
+ ).toHaveTextContent(
666
+ formatScopeText(
667
+ IncidentStatusPageScopeCopy.removingNotifiedPagesWarning,
668
+ { names: "All Sites" },
669
+ ),
670
+ );
671
+ });
672
+
673
+ test("limiting an unscoped incident to every page it told needs no warning", async () => {
674
+ listMock.mockResolvedValue({
675
+ data: [page(SITE_03, "Site 03")],
676
+ count: 1,
677
+ skip: 0,
678
+ limit: 1,
679
+ } as never);
680
+
681
+ await renderSettings();
682
+ await loadIncident({
683
+ isScoped: false,
684
+ statusPages: [],
685
+ notified: [SITE_03],
686
+ });
687
+
688
+ await renderFooter([SITE_03, SITE_07]);
689
+
690
+ expect(
691
+ screen.queryByTestId("incident-scope-removing-notified-pages"),
692
+ ).toBeNull();
693
+ });
694
+
695
+ test("an unscoped incident left unscoped drops no told page", async () => {
696
+ listMock.mockResolvedValue({
697
+ data: [page(SITE_03, "Site 03")],
698
+ count: 1,
699
+ skip: 0,
700
+ limit: 1,
701
+ } as never);
702
+
703
+ await renderSettings();
704
+ await loadIncident({
705
+ isScoped: false,
706
+ statusPages: [],
707
+ notified: [SITE_03],
708
+ });
709
+
710
+ await renderFooter([]);
711
+
712
+ expect(
713
+ screen.queryByTestId("incident-scope-removing-notified-pages"),
714
+ ).toBeNull();
715
+ });
716
+
717
+ test("an unscoped incident that told nobody reads no names", async () => {
718
+ await renderSettings();
719
+ await loadIncident({ isScoped: false, statusPages: [], notified: [] });
720
+
721
+ expect(listMock).not.toHaveBeenCalled();
722
+ });
723
+
724
+ test("an unscoped incident left unscoped says nothing", async () => {
725
+ await renderSettings();
726
+ await loadIncident({ isScoped: false, statusPages: [], notified: [] });
727
+
728
+ await renderFooter([]);
729
+
730
+ expect(screen.queryByTestId("incident-scope-clearing")).toBeNull();
731
+ });
732
+
733
+ test("a picked page that lists none of the monitors is named, as the server says", async () => {
734
+ postMock.mockResolvedValue(
735
+ audienceResponse([{ statusPageId: SITE_05, name: "Site 05" }]) as never,
736
+ );
737
+
738
+ await renderSettings();
739
+ await loadIncident();
740
+
741
+ await renderFooter([SITE_03, SITE_05]);
742
+
743
+ expect(
744
+ await screen.findByTestId("status-pages-not-listing-monitors"),
745
+ ).toHaveTextContent(
746
+ formatScopeText(IncidentStatusPageScopeCopy.notListingMonitorsWarning, {
747
+ names: "Site 05",
748
+ }),
749
+ );
750
+
751
+ // Asked with the incident's monitors and the pages on the form.
752
+ const body: JSONObject = (
753
+ postMock.mock.calls[0]![0] as { data: JSONObject }
754
+ ).data;
755
+ expect(body).toEqual({
756
+ monitorIds: [MONITOR_ID],
757
+ statusPageIds: [SITE_03, SITE_05],
758
+ });
759
+ });
760
+
761
+ test("no page picked: nothing to check against the monitors", async () => {
762
+ await renderSettings();
763
+ await loadIncident({ isScoped: false, statusPages: [], notified: [] });
764
+
765
+ await renderFooter([]);
766
+ await new Promise((r: (value: unknown) => void) => {
767
+ setTimeout(r, 450);
768
+ });
769
+
770
+ expect(postMock).not.toHaveBeenCalled();
771
+ });
772
+
773
+ test("with no status page to pick from, the picker says why", async () => {
774
+ countMock.mockRejectedValue(
775
+ new Error("You do not have permissions to read Status Page.") as never,
776
+ );
777
+
778
+ await renderSettings();
779
+ await loadIncident();
780
+ await renderFooter([SITE_03]);
781
+
782
+ expect(
783
+ screen.getByTestId("status-page-picker-no-access"),
784
+ ).toHaveTextContent(IncidentStatusPageScopeCopy.pickerNoAccessHint);
785
+ });
786
+
787
+ test("a project whose visible status pages number zero gets the same hint", async () => {
788
+ countMock.mockResolvedValue(0 as never);
789
+
790
+ await renderSettings();
791
+ await loadIncident();
792
+ await renderFooter([]);
793
+
794
+ expect(
795
+ screen.getByTestId("status-page-picker-no-access"),
796
+ ).toBeInTheDocument();
797
+ });
798
+
799
+ test("with status pages to pick from, no hint", async () => {
800
+ await renderSettings();
801
+ await loadIncident();
802
+ await renderFooter([SITE_03]);
803
+
804
+ expect(screen.queryByTestId("status-page-picker-no-access")).toBeNull();
805
+ });
806
+ });
807
+
808
+ describe("the scope, read only", () => {
809
+ async function renderView(element: ReactElement): Promise<void> {
810
+ await act(async (): Promise<void> => {
811
+ render(<MemoryRouter>{element}</MemoryRouter>);
812
+ });
813
+ }
814
+
815
+ test("the card shows the pages the incident is limited to", async () => {
816
+ await renderSettings();
817
+
818
+ const element: ReactElement =
819
+ scopeCard().modelDetailProps.fields[0]!.getElement(buildIncident());
820
+
821
+ await renderView(element);
822
+
823
+ expect(screen.getByText("Site 03")).toBeInTheDocument();
824
+ expect(screen.getByText("Site 07")).toBeInTheDocument();
825
+ expect(screen.getByTestId("incident-status-page-scope")).toHaveAttribute(
826
+ "data-state",
827
+ "scoped",
828
+ );
829
+ });
830
+
831
+ test("an unscoped incident reaches every page that lists its monitors", async () => {
832
+ await renderView(
833
+ <IncidentStatusPageScopeView
834
+ isScopedToStatusPages={false}
835
+ statusPages={[]}
836
+ />,
837
+ );
838
+
839
+ expect(
840
+ screen.getByText(IncidentStatusPageScopeCopy.noScopeSummary),
841
+ ).toBeInTheDocument();
842
+ });
843
+
844
+ test("an incident whose pages were all deleted is hidden everywhere, and says so", async () => {
845
+ await renderView(
846
+ <IncidentStatusPageScopeView
847
+ isScopedToStatusPages={true}
848
+ statusPages={[]}
849
+ />,
850
+ );
851
+
852
+ expect(
853
+ screen.getByTestId("incident-scoped-to-deleted-pages"),
854
+ ).toHaveTextContent(
855
+ IncidentStatusPageScopeCopy.scopedToDeletedPagesWarning,
856
+ );
857
+ });
858
+
859
+ test("given only the incident, reads its pages itself, and only when it is scoped", async () => {
860
+ loadedIncident = buildIncident();
861
+
862
+ await renderView(
863
+ <IncidentStatusPageScopeView
864
+ isScopedToStatusPages={true}
865
+ incidentId={new ObjectID(INCIDENT_ID)}
866
+ editRoute={new Route("/incidents/1/settings")}
867
+ />,
868
+ );
869
+
870
+ expect(await screen.findByText("Site 03")).toBeInTheDocument();
871
+ expect(
872
+ screen
873
+ .getByText(IncidentStatusPageScopeCopy.overviewEditLink)
874
+ .closest("a"),
875
+ ).toHaveAttribute("href", "/incidents/1/settings");
876
+ });
877
+ });
878
+
879
+ describe("the added-pages field", () => {
880
+ const field: ModelField<Incident> = getIncidentScopeAddedPagesFormField({
881
+ loadedIncident: {
882
+ statusPages: [page(SITE_03, "Site 03")],
883
+ statusPagesNotifiedOnCreation: [SITE_03],
884
+ subscriberNotificationStatusOnIncidentCreated:
885
+ StatusPageSubscriberNotificationStatus.Success,
886
+ shouldStatusPageSubscribersBeNotifiedOnIncidentCreated: true,
887
+ isVisibleOnStatusPage: true,
888
+ isPrivate: false,
889
+ },
890
+ });
891
+
892
+ test("is sent as the misc data prop the server reads, not as a column", () => {
893
+ expect(field.overrideFieldKey).toBe(
894
+ IncidentScopeAddedPagesNotification.miscDataKey,
895
+ );
896
+ expect(field.overrideField).toEqual({
897
+ [IncidentScopeAddedPagesNotification.miscDataKey]: true,
898
+ });
899
+ expect(field.field).toBeUndefined();
900
+ expect(new Incident().getTableColumns().columns).not.toContain(
901
+ IncidentScopeAddedPagesNotification.miscDataKey,
902
+ );
903
+ });
904
+
905
+ test("an optional checkbox, ticked, on edit only, with no column permission to check", () => {
906
+ expect(field.fieldType).toBe(FormFieldSchemaType.Checkbox);
907
+ expect(field.required).toBe(false);
908
+ expect(field.defaultValue).toBe(true);
909
+ expect(field.doNotShowWhenCreating).toBe(true);
910
+ expect(field.showEvenIfPermissionDoesNotExist).toBe(true);
911
+ expect(field.description).toBe(
912
+ IncidentScopeAddedPagesNotification.formFieldDescription,
913
+ );
914
+ });
915
+
916
+ test.each([
917
+ [[SITE_03], false],
918
+ [[SITE_03, SITE_05], true],
919
+ [[SITE_05], true],
920
+ [[], false],
921
+ [undefined, false],
922
+ ] as Array<[Array<string> | undefined, boolean]>)(
923
+ "shows for form pages %j: %s",
924
+ (statusPages: Array<string> | undefined, shown: boolean) => {
925
+ const values: FormValues<Incident> = {
926
+ statusPages: statusPages,
927
+ } as FormValues<Incident>;
928
+
929
+ expect(field.showIf!(values)).toBe(shown);
930
+ },
931
+ );
932
+ });
933
+
934
+ /*
935
+ * The checkbox inside the real ModelForm, editing an incident whose card
936
+ * loaded it limited to Site 03. The form's own read of the incident is what
937
+ * the edit starts from, so a form that reads Site 03 and Site 05 is an edit
938
+ * that added Site 05. The picker is registered hidden: driving the entity
939
+ * dropdown is not what this is about.
940
+ */
941
+ describe("the added-pages checkbox in the edit form", () => {
942
+ function checkbox(): HTMLInputElement | null {
943
+ return screen.queryByRole("checkbox", {
944
+ name: new RegExp(IncidentScopeAddedPagesNotification.formFieldTitle),
945
+ }) as HTMLInputElement | null;
946
+ }
947
+
948
+ async function renderForm(formStatusPages: Array<string>): Promise<void> {
949
+ const incident: Incident = new Incident();
950
+ incident.id = new ObjectID(INCIDENT_ID);
951
+ incident.statusPages = formStatusPages.map((id: string): StatusPage => {
952
+ return page(id, id);
953
+ });
954
+ loadedIncident = incident;
955
+
956
+ const fields: Fields<Incident> = [
957
+ {
958
+ field: { statusPages: true },
959
+ title: IncidentStatusPageScopeCopy.pickerTitle,
960
+ fieldType: FormFieldSchemaType.MultiSelectDropdown,
961
+ required: false,
962
+ showIf: (): boolean => {
963
+ return false;
964
+ },
965
+ },
966
+ getIncidentScopeAddedPagesFormField({
967
+ loadedIncident: buildIncident({
968
+ statusPages: [page(SITE_03, "Site 03")],
969
+ notified: [SITE_03],
970
+ }),
971
+ }),
972
+ ];
973
+
974
+ await act(async (): Promise<void> => {
975
+ render(
976
+ <MemoryRouter>
977
+ <ModelForm<Incident>
978
+ id="incident-status-page-scope-form"
979
+ name="Status Page Scope"
980
+ modelType={Incident}
981
+ formType={FormType.Update}
982
+ modelIdToEdit={new ObjectID(INCIDENT_ID)}
983
+ fields={fields}
984
+ submitButtonText="Save"
985
+ />
986
+ </MemoryRouter>,
987
+ );
988
+ });
989
+
990
+ await screen.findByRole("button", { name: "Save" });
991
+ }
992
+
993
+ async function save(): Promise<{
994
+ model: Record<string, unknown>;
995
+ miscDataProps: Record<string, unknown>;
996
+ }> {
997
+ await act(async (): Promise<void> => {
998
+ fireEvent.click(screen.getByRole("button", { name: "Save" }));
999
+ });
1000
+
1001
+ await waitFor(() => {
1002
+ expect(savedRequests).toHaveLength(1);
1003
+ });
1004
+
1005
+ return {
1006
+ model: savedRequests[0]!.model as Record<string, unknown>,
1007
+ miscDataProps: savedRequests[0]!.miscDataProps || {},
1008
+ };
1009
+ }
1010
+
1011
+ test("an edit that adds a page shows it, ticked, and asks the server to tell the added page", async () => {
1012
+ await renderForm([SITE_03, SITE_05]);
1013
+
1014
+ expect(checkbox()).not.toBeNull();
1015
+ expect(checkbox()).toBeChecked();
1016
+
1017
+ const { model, miscDataProps } = await save();
1018
+
1019
+ expect(miscDataProps).toEqual(
1020
+ expect.objectContaining({
1021
+ [IncidentScopeAddedPagesNotification.miscDataKey]: true,
1022
+ }),
1023
+ );
1024
+ // Never written as a column.
1025
+ expect(
1026
+ model[IncidentScopeAddedPagesNotification.miscDataKey],
1027
+ ).toBeUndefined();
1028
+ });
1029
+
1030
+ test("unticked, it asks for nothing", async () => {
1031
+ await renderForm([SITE_03, SITE_05]);
1032
+
1033
+ await act(async (): Promise<void> => {
1034
+ fireEvent.click(checkbox()!);
1035
+ });
1036
+
1037
+ expect(checkbox()).not.toBeChecked();
1038
+
1039
+ const { miscDataProps } = await save();
1040
+
1041
+ expect(
1042
+ miscDataProps[IncidentScopeAddedPagesNotification.miscDataKey],
1043
+ ).toBeUndefined();
1044
+ });
1045
+
1046
+ test("an edit that adds no page does not show it", async () => {
1047
+ await renderForm([SITE_03]);
1048
+
1049
+ expect(checkbox()).toBeNull();
1050
+ });
1051
+ });