@oneuptime/common 14.0.7 → 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 (440) 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/StatusPage.ts +55 -0
  6. package/Models/DatabaseModels/StatusPageSubscriber.ts +128 -0
  7. package/Server/API/IncidentAPI.ts +162 -0
  8. package/Server/API/StatusPageAPI.ts +487 -171
  9. package/Server/API/StatusPageSubscriberAPI.ts +63 -15
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.ts +49 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.ts +108 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.ts +33 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.ts +51 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.ts +33 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.ts +168 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.ts +44 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +14 -0
  18. package/Server/Infrastructure/Semaphore.ts +10 -0
  19. package/Server/Middleware/IdentityRateLimit.ts +108 -3
  20. package/Server/Middleware/SubscriberNotificationPreviewRateLimit.ts +237 -0
  21. package/Server/Middleware/SubscriberNotificationTestSendRateLimit.ts +304 -0
  22. package/Server/Services/DatabaseService.ts +93 -10
  23. package/Server/Services/IncidentCustomFieldService.ts +276 -1
  24. package/Server/Services/IncidentEpisodePublicNoteService.ts +28 -0
  25. package/Server/Services/IncidentEpisodeService.ts +15 -0
  26. package/Server/Services/IncidentEpisodeStateTimelineService.ts +27 -1
  27. package/Server/Services/IncidentPublicNoteService.ts +30 -1
  28. package/Server/Services/IncidentService.ts +1535 -65
  29. package/Server/Services/IncidentStateTimelineService.ts +13 -0
  30. package/Server/Services/IncidentTemplateService.ts +109 -0
  31. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +30 -1
  32. package/Server/Services/ScheduledMaintenanceService.ts +68 -18
  33. package/Server/Services/SmsService.ts +10 -0
  34. package/Server/Services/StatusPageService.ts +65 -8
  35. package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +299 -37
  36. package/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.ts +129 -0
  37. package/Server/Services/StatusPageSubscriberService.ts +1161 -64
  38. package/Server/Types/Markdown.ts +153 -6
  39. package/Server/Types/Workflow/Components/Email.ts +61 -2
  40. package/Server/Utils/CustomField/CustomFieldRename.ts +378 -0
  41. package/Server/Utils/DataSource/EgressGuard.ts +66 -21
  42. package/Server/Utils/Mail/PinnedSmtpSocket.ts +130 -0
  43. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +10 -0
  44. package/Server/Utils/SSRFProtection.ts +76 -16
  45. package/Server/Utils/StatusPage/IncidentStatusPageScope.ts +948 -0
  46. package/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.ts +656 -0
  47. package/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.ts +805 -0
  48. package/Server/Utils/StatusPage/StatusPageExclusion.ts +18 -0
  49. package/Server/Utils/StatusPage/StatusPageReadAccess.ts +125 -0
  50. package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.ts +151 -0
  51. package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.ts +70 -0
  52. package/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.ts +421 -0
  53. package/Server/Utils/StatusPage/SubscriberNotificationClaim.ts +107 -0
  54. package/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.ts +1049 -0
  55. package/Server/Utils/StatusPage/SubscriberNotificationFanOut.ts +180 -0
  56. package/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.ts +839 -0
  57. package/Server/Utils/StatusPage/SubscriberNotificationResendAccess.ts +393 -0
  58. package/Server/Utils/StatusPage/SubscriberNotificationRunLimit.ts +246 -0
  59. package/Server/Utils/StatusPage/SubscriberNotificationTiming.ts +133 -0
  60. package/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.ts +215 -0
  61. package/Server/Utils/StatusPageResource.ts +51 -20
  62. package/Server/Utils/VM/VMAPI.ts +1 -0
  63. package/Server/Utils/VM/VMRunner.ts +10 -0
  64. package/Server/Utils/Workflow/OAuth2TokenClient.ts +10 -0
  65. package/Server/Views/Partials/SensitiveUrlToken.ejs +8 -5
  66. package/Tests/App/Accounts/RegisterVerifyEmail.test.tsx +748 -0
  67. package/Tests/App/Accounts/ResendVerificationEmail.test.tsx +1222 -0
  68. package/Tests/App/Accounts/VerificationEmailResendUtils.test.ts +797 -0
  69. package/Tests/App/Accounts/VerifyEmailPending.test.tsx +538 -0
  70. package/Tests/App/Accounts/VerifyEmailResend.test.tsx +779 -0
  71. package/Tests/App/Dashboard/CustomFieldsSettingsIncidentOptions.test.tsx +357 -0
  72. package/Tests/App/Dashboard/EventNotes.test.tsx +776 -0
  73. package/Tests/App/Dashboard/EventNotesPages.test.tsx +119 -0
  74. package/Tests/App/Dashboard/EventNotesUtil.test.ts +116 -17
  75. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +242 -5
  76. package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +91 -1
  77. package/Tests/App/Dashboard/IncidentCreateDetailsStep.test.tsx +643 -0
  78. package/Tests/App/Dashboard/IncidentCreateFromAlerts.test.tsx +8 -1
  79. package/Tests/App/Dashboard/IncidentCreateStatusPageScope.test.tsx +601 -0
  80. package/Tests/App/Dashboard/IncidentCustomFieldTemplateVariables.test.tsx +386 -0
  81. package/Tests/App/Dashboard/IncidentNoteTemplatePlaceholders.test.tsx +124 -0
  82. package/Tests/App/Dashboard/IncidentNoteTemplateVariablesLoader.test.ts +305 -0
  83. package/Tests/App/Dashboard/IncidentOverviewNotifyDefault.test.tsx +112 -0
  84. package/Tests/App/Dashboard/IncidentSettingsRenotifyOnPublish.test.tsx +674 -0
  85. package/Tests/App/Dashboard/IncidentSettingsStatusPageScope.test.tsx +1051 -0
  86. package/Tests/App/Dashboard/IncidentTemplateCustomFields.test.tsx +384 -0
  87. package/Tests/App/Dashboard/IncidentTemplateStatusPageScopeCard.test.tsx +215 -0
  88. package/Tests/App/Dashboard/PublicNotePagesNotifyDefault.test.tsx +113 -1
  89. package/Tests/App/Dashboard/SubscriberAudienceSummary.test.tsx +512 -0
  90. package/Tests/App/Dashboard/SubscriberNotificationPreviewModal.test.tsx +837 -0
  91. package/Tests/App/Dashboard/SubscriberNotificationStatusLabel.test.tsx +88 -0
  92. package/Tests/App/Dashboard/SubscriberNotificationStatusResend.test.tsx +473 -0
  93. package/Tests/App/Dashboard/SubscriberTemplateLivePreview.test.tsx +196 -0
  94. package/Tests/App/Dashboard/TeamAddedSubscribersUnsubscribedNotice.test.tsx +248 -0
  95. package/Tests/App/SensitiveUrlTokenBootstrap.test.ts +64 -3
  96. package/Tests/App/StatusPage/StatusPageUnsubscribePage.test.tsx +412 -0
  97. package/Tests/Models/IncidentCustomFieldApiSchemaContract.test.ts +217 -0
  98. package/Tests/Models/IncidentCustomFieldCreateSettingsColumns.test.ts +252 -0
  99. package/Tests/Models/StatusPageSubscriberUnsubscribeColumns.test.ts +218 -0
  100. package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +7 -0
  101. package/Tests/Server/API/IncidentSubscriberAudienceAPI.test.ts +1408 -0
  102. package/Tests/Server/API/StatusPageIncidentScope.test.ts +1868 -0
  103. package/Tests/Server/API/StatusPageIncidentScopePostgres.test.ts +790 -0
  104. package/Tests/Server/API/StatusPageManageSubscriptionRecipient.test.ts +10 -1
  105. package/Tests/Server/API/StatusPageManageSubscriptionTemplateVariables.test.ts +206 -18
  106. package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +14 -2
  107. package/Tests/Server/API/StatusPageSubscriberCreateResponse.test.ts +217 -0
  108. package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +103 -2
  109. package/Tests/Server/API/StatusPageUnsubscribeAPI.test.ts +396 -0
  110. package/Tests/Server/Infrastructure/Postgres/AddIncidentStatusPageScopeMigration.test.ts +395 -0
  111. package/Tests/Server/Infrastructure/Postgres/AddIncidentTemplateStatusPageScopeFlagMigration.test.ts +150 -0
  112. package/Tests/Server/Infrastructure/Postgres/AddSubscriberNotificationClaimedAtMigration.test.ts +156 -0
  113. package/Tests/Server/Infrastructure/Postgres/SkipHiddenIncidentCreatedNotificationsMigration.test.ts +410 -0
  114. package/Tests/Server/Middleware/IdentityRateLimit.test.ts +551 -1
  115. package/Tests/Server/Middleware/SubscriberNotificationPreviewRateLimit.test.ts +238 -0
  116. package/Tests/Server/Middleware/SubscriberNotificationTestSendRateLimit.test.ts +245 -0
  117. package/Tests/Server/Services/AddStatusPageSubscriberUnsubscribeTokenMigration.test.ts +152 -0
  118. package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +221 -36
  119. package/Tests/Server/Services/DatabaseServiceCompareAndSetColumns.test.ts +191 -0
  120. package/Tests/Server/Services/IncidentCreatedRenotifyOnPublish.test.ts +681 -0
  121. package/Tests/Server/Services/IncidentCreatedResend.test.ts +775 -0
  122. package/Tests/Server/Services/IncidentCustomFieldRenamePostgres.test.ts +713 -0
  123. package/Tests/Server/Services/IncidentCustomFieldService.test.ts +751 -0
  124. package/Tests/Server/Services/IncidentCustomFieldValueValidation.test.ts +556 -0
  125. package/Tests/Server/Services/IncidentStatusPageScope.test.ts +2971 -0
  126. package/Tests/Server/Services/IncidentStatusPageScopeFeed.test.ts +131 -0
  127. package/Tests/Server/Services/IncidentStatusPageScopePostgres.test.ts +625 -0
  128. package/Tests/Server/Services/IncidentTemplateCustomFieldsCopy.test.ts +311 -0
  129. package/Tests/Server/Services/IncidentTemplateStatusPageScope.test.ts +363 -0
  130. package/Tests/Server/Services/PublicNoteEditFeedEventType.test.ts +213 -0
  131. package/Tests/Server/Services/PublicNoteResendAccess.test.ts +994 -0
  132. package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +289 -9
  133. package/Tests/Server/Services/SmsServiceFailIfNotSent.test.ts +67 -0
  134. package/Tests/Server/Services/StatusPageReportIncidentCounts.test.ts +610 -0
  135. package/Tests/Server/Services/StatusPageSubscriberAudienceCounts.test.ts +201 -0
  136. package/Tests/Server/Services/StatusPageSubscriberAudienceCountsPostgres.test.ts +271 -0
  137. package/Tests/Server/Services/StatusPageSubscriberConfirmationTemplateEscaping.test.ts +200 -0
  138. package/Tests/Server/Services/StatusPageSubscriberNotificationLogRedaction.test.ts +54 -0
  139. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateCompile.test.ts +37 -0
  140. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateIncidentRecordAccess.test.ts +772 -0
  141. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookup.test.ts +506 -0
  142. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookupPostgres.test.ts +261 -0
  143. package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +21 -5
  144. package/Tests/Server/Services/StatusPageSubscriberUnsubscribe.test.ts +1618 -0
  145. package/Tests/Server/Services/StatusPageSubscriberUnsubscribeLinkSenders.test.ts +373 -0
  146. package/Tests/Server/Services/StatusPageSubscriberUnsubscribePostgres.test.ts +388 -0
  147. package/Tests/Server/Services/SubscriberNotificationInFlightRequeue.test.ts +500 -0
  148. package/Tests/Server/Types/MarkdownEmailLinks.test.ts +320 -0
  149. package/Tests/Server/Types/Workflow/Components/Email.test.ts +491 -1
  150. package/Tests/Server/Types/Workflow/Components/EmailSmtpPinning.test.ts +398 -0
  151. package/Tests/Server/Utils/CustomField/CustomFieldRename.test.ts +586 -0
  152. package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +246 -10
  153. package/Tests/Server/Utils/DataSource/EgressGuardDeploymentConfig.test.ts +226 -0
  154. package/Tests/Server/Utils/DataSource/EgressGuardPinning.test.ts +131 -0
  155. package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +4 -0
  156. package/Tests/Server/Utils/EgressPolicyEnvironment.ts +45 -0
  157. package/Tests/Server/Utils/Mail/PinnedSmtpSocket.test.ts +409 -0
  158. package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +23 -0
  159. package/Tests/Server/Utils/SSRFProtection.test.ts +4 -0
  160. package/Tests/Server/Utils/SSRFProtectionCloudServiceAddresses.test.ts +4 -0
  161. package/Tests/Server/Utils/SSRFProtectionPrivateNetwork.test.ts +4 -0
  162. package/Tests/Server/Utils/SSRFProtectionResolutionDetail.test.ts +314 -0
  163. package/Tests/Server/Utils/StatusPage/IncidentStatusPageScope.test.ts +1699 -0
  164. package/Tests/Server/Utils/StatusPage/IncidentStatusPageScopeQueriesPostgres.test.ts +508 -0
  165. package/Tests/Server/Utils/StatusPage/IncidentSubscriberAudienceCallerPermission.test.ts +246 -0
  166. package/Tests/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.test.ts +1348 -0
  167. package/Tests/Server/Utils/StatusPage/StatusPageReadAccess.test.ts +221 -0
  168. package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.test.ts +132 -0
  169. package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.test.ts +155 -0
  170. package/Tests/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.test.ts +708 -0
  171. package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaim.test.ts +255 -0
  172. package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaimPostgres.test.ts +528 -0
  173. package/Tests/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.test.ts +1238 -0
  174. package/Tests/Server/Utils/StatusPage/SubscriberNotificationFanOut.test.ts +472 -0
  175. package/Tests/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.test.ts +1168 -0
  176. package/Tests/Server/Utils/StatusPage/SubscriberNotificationRunLimit.test.ts +275 -0
  177. package/Tests/Server/Utils/StatusPage/SubscriberNotificationTiming.test.ts +131 -0
  178. package/Tests/Server/Utils/StatusPageResource.test.ts +98 -0
  179. package/Tests/Server/Utils/VM/VMRunnerPrivateNetworkWiring.test.ts +17 -0
  180. package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +75 -0
  181. package/Tests/Types/CustomField/CustomFieldSavedViews.test.ts +247 -0
  182. package/Tests/Types/CustomField/CustomFieldTemplateValues.test.ts +186 -0
  183. package/Tests/Types/CustomField/CustomFieldTypeCorrespondence.test.ts +48 -0
  184. package/Tests/Types/CustomField/CustomFieldValueFormat.test.ts +215 -0
  185. package/Tests/Types/CustomField/CustomFieldValueValidator.test.ts +343 -0
  186. package/Tests/Types/CustomField/CustomFieldVariableKey.test.ts +364 -0
  187. package/Tests/Types/SafeHtml.test.ts +102 -0
  188. package/Tests/Types/StatusPage/IncidentCreatedRenotify.test.ts +419 -0
  189. package/Tests/Types/StatusPage/IncidentCreatedResend.test.ts +118 -0
  190. package/Tests/Types/StatusPage/IncidentScopeAddedPagesNotification.test.ts +558 -0
  191. package/Tests/Types/StatusPage/IncidentSubscriberAudience.test.ts +284 -0
  192. package/Tests/Types/StatusPage/StatusPageSubscriberUnsubscribe.test.ts +341 -0
  193. package/Tests/Types/StatusPage/SubscriberNotificationPreview.test.ts +196 -0
  194. package/Tests/Types/StatusPage/SubscriberNotificationResend.test.ts +201 -0
  195. package/Tests/Types/StatusPage/SubscriberNotificationTemplateCompiler.test.ts +277 -0
  196. package/Tests/Types/StatusPage/SubscriberNotificationTemplatePreview.test.ts +179 -0
  197. package/Tests/Types/StatusPage/SubscriberNotificationTemplateVariables.test.ts +324 -0
  198. package/Tests/Types/StatusPage/SubscriberUpdateNotification.test.ts +60 -13
  199. package/Tests/UI/Components/CustomFields/CustomFieldFormFields.test.ts +432 -0
  200. package/Tests/UI/Components/CustomFields/CustomFieldModelFormFields.test.tsx +685 -0
  201. package/Tests/UI/Components/CustomFields/CustomFieldsDetailOrder.test.tsx +469 -0
  202. package/Tests/UI/Components/Forms/ModelFormCustomFieldInputs.test.tsx +325 -0
  203. package/Tests/UI/Components/Forms/ModelFormDropdownOptionsRefused.test.tsx +358 -0
  204. package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +22 -0
  205. package/Tests/UI/Monitor/NetworkPathView.test.tsx +289 -0
  206. package/Tests/Utils/Incident/IncidentNoteTemplateVariables.test.ts +623 -0
  207. package/Tests/Utils/Markdown/MarkdownEscape.test.ts +89 -0
  208. package/Tests/Utils/ProbeNetworkFailureUtil.test.ts +992 -0
  209. package/Tests/Utils/RequestFailedDetails.test.ts +575 -0
  210. package/Types/CustomField/CustomFieldOrder.ts +55 -0
  211. package/Types/CustomField/CustomFieldSavedViews.ts +225 -0
  212. package/Types/CustomField/CustomFieldTemplateMerge.ts +71 -0
  213. package/Types/CustomField/CustomFieldType.ts +14 -0
  214. package/Types/CustomField/CustomFieldValueFormat.ts +161 -0
  215. package/Types/CustomField/CustomFieldValueValidator.ts +488 -0
  216. package/Types/CustomField/CustomFieldVariableKey.ts +211 -0
  217. package/Types/SafeHtml.ts +93 -0
  218. package/Types/StatusPage/IncidentCreatedRenotify.ts +224 -0
  219. package/Types/StatusPage/IncidentCreatedResend.ts +108 -0
  220. package/Types/StatusPage/IncidentScopeAddedPagesNotification.ts +343 -0
  221. package/Types/StatusPage/IncidentSubscriberAudience.ts +332 -0
  222. package/Types/StatusPage/StatusPageSubscriberUnsubscribe.ts +405 -0
  223. package/Types/StatusPage/SubscriberNotificationInterruption.ts +32 -0
  224. package/Types/StatusPage/SubscriberNotificationPreview.ts +322 -0
  225. package/Types/StatusPage/SubscriberNotificationResend.ts +132 -0
  226. package/Types/StatusPage/SubscriberNotificationTemplateCompiler.ts +180 -0
  227. package/Types/StatusPage/SubscriberNotificationTemplatePreview.ts +301 -0
  228. package/Types/StatusPage/SubscriberNotificationTemplateVariables.ts +217 -4
  229. package/Types/StatusPage/SubscriberUpdateNotification.ts +32 -11
  230. package/UI/Components/CustomFields/CustomFieldFormFields.ts +317 -0
  231. package/UI/Components/CustomFields/CustomFieldModelFormFields.tsx +395 -0
  232. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +95 -55
  233. package/UI/Components/Forms/ModelForm.tsx +70 -8
  234. package/UI/Components/Forms/Validation.ts +1 -1
  235. package/UI/Components/ModelFormModal/ModelFormModal.tsx +2 -4
  236. package/UI/Components/ModelTable/AnalyticsModelTable.tsx +2 -6
  237. package/UI/Components/ModelTable/BaseModelTable.tsx +9 -8
  238. package/UI/Components/ModelTable/CustomFieldColumns.tsx +14 -1
  239. package/UI/Components/ModelTable/ModelTable.tsx +6 -7
  240. package/UI/Utils/SensitiveUrlToken.ts +10 -5
  241. package/Utils/API.ts +247 -2
  242. package/Utils/Incident/IncidentNoteTemplateVariables.ts +304 -0
  243. package/Utils/Markdown/MarkdownEscape.ts +65 -0
  244. package/Utils/ProbeNetworkFailureUtil.ts +673 -0
  245. package/build/dist/Models/DatabaseModels/Incident.js +267 -1
  246. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  247. package/build/dist/Models/DatabaseModels/IncidentCustomField.js +207 -1
  248. package/build/dist/Models/DatabaseModels/IncidentCustomField.js.map +1 -1
  249. package/build/dist/Models/DatabaseModels/IncidentEpisode.js +31 -0
  250. package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
  251. package/build/dist/Models/DatabaseModels/IncidentTemplate.js +124 -1
  252. package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
  253. package/build/dist/Models/DatabaseModels/StatusPage.js +56 -0
  254. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  255. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js +131 -0
  256. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js.map +1 -1
  257. package/build/dist/Server/API/IncidentAPI.js +100 -1
  258. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  259. package/build/dist/Server/API/StatusPageAPI.js +417 -176
  260. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  261. package/build/dist/Server/API/StatusPageSubscriberAPI.js +52 -13
  262. package/build/dist/Server/API/StatusPageSubscriberAPI.js.map +1 -1
  263. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js +41 -0
  264. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js.map +1 -0
  265. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js +57 -0
  266. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js.map +1 -0
  267. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js +23 -0
  268. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js.map +1 -0
  269. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js +40 -0
  270. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js.map +1 -0
  271. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js +26 -0
  272. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js.map +1 -0
  273. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js +89 -0
  274. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js.map +1 -0
  275. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js +29 -0
  276. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js.map +1 -0
  277. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +14 -0
  278. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  279. package/build/dist/Server/Infrastructure/Semaphore.js +3 -0
  280. package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
  281. package/build/dist/Server/Middleware/IdentityRateLimit.js +89 -3
  282. package/build/dist/Server/Middleware/IdentityRateLimit.js.map +1 -1
  283. package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js +149 -0
  284. package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js.map +1 -0
  285. package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js +190 -0
  286. package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js.map +1 -0
  287. package/build/dist/Server/Services/DatabaseService.js +56 -7
  288. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  289. package/build/dist/Server/Services/IncidentCustomFieldService.js +204 -8
  290. package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
  291. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +24 -0
  292. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
  293. package/build/dist/Server/Services/IncidentEpisodeService.js +14 -0
  294. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  295. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +26 -0
  296. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
  297. package/build/dist/Server/Services/IncidentPublicNoteService.js +26 -1
  298. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  299. package/build/dist/Server/Services/IncidentService.js +1052 -0
  300. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  301. package/build/dist/Server/Services/IncidentStateTimelineService.js +12 -0
  302. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  303. package/build/dist/Server/Services/IncidentTemplateService.js +83 -0
  304. package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
  305. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +26 -1
  306. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  307. package/build/dist/Server/Services/ScheduledMaintenanceService.js +47 -17
  308. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  309. package/build/dist/Server/Services/SmsService.js +2 -22
  310. package/build/dist/Server/Services/SmsService.js.map +1 -1
  311. package/build/dist/Server/Services/StatusPageService.js +47 -9
  312. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  313. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +237 -32
  314. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
  315. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js +109 -0
  316. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js.map +1 -1
  317. package/build/dist/Server/Services/StatusPageSubscriberService.js +925 -99
  318. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  319. package/build/dist/Server/Types/Markdown.js +110 -6
  320. package/build/dist/Server/Types/Markdown.js.map +1 -1
  321. package/build/dist/Server/Types/Workflow/Components/Email.js +55 -6
  322. package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
  323. package/build/dist/Server/Utils/CustomField/CustomFieldRename.js +211 -0
  324. package/build/dist/Server/Utils/CustomField/CustomFieldRename.js.map +1 -0
  325. package/build/dist/Server/Utils/DataSource/EgressGuard.js +41 -12
  326. package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
  327. package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js +101 -0
  328. package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js.map +1 -0
  329. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +9 -0
  330. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  331. package/build/dist/Server/Utils/SSRFProtection.js +48 -6
  332. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
  333. package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js +631 -0
  334. package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js.map +1 -0
  335. package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js +403 -0
  336. package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js.map +1 -0
  337. package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js +451 -0
  338. package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js.map +1 -0
  339. package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js +13 -0
  340. package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js.map +1 -0
  341. package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js +91 -0
  342. package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js.map +1 -0
  343. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js +101 -0
  344. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js.map +1 -0
  345. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js +47 -0
  346. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js.map +1 -0
  347. package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js +273 -0
  348. package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js.map +1 -0
  349. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js +73 -0
  350. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js.map +1 -0
  351. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js +722 -0
  352. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js.map +1 -0
  353. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js +102 -0
  354. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js.map +1 -0
  355. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js +527 -0
  356. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js.map +1 -0
  357. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js +278 -0
  358. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js.map +1 -0
  359. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js +137 -0
  360. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js.map +1 -0
  361. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js +103 -0
  362. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js.map +1 -0
  363. package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js +114 -0
  364. package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js.map +1 -0
  365. package/build/dist/Server/Utils/StatusPageResource.js +39 -7
  366. package/build/dist/Server/Utils/StatusPageResource.js.map +1 -1
  367. package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
  368. package/build/dist/Server/Utils/VM/VMRunner.js +1 -0
  369. package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
  370. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js +3 -0
  371. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js.map +1 -1
  372. package/build/dist/Types/CustomField/CustomFieldOrder.js +36 -0
  373. package/build/dist/Types/CustomField/CustomFieldOrder.js.map +1 -0
  374. package/build/dist/Types/CustomField/CustomFieldSavedViews.js +135 -0
  375. package/build/dist/Types/CustomField/CustomFieldSavedViews.js.map +1 -0
  376. package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js +27 -0
  377. package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js.map +1 -0
  378. package/build/dist/Types/CustomField/CustomFieldType.js +12 -0
  379. package/build/dist/Types/CustomField/CustomFieldType.js.map +1 -1
  380. package/build/dist/Types/CustomField/CustomFieldValueFormat.js +126 -0
  381. package/build/dist/Types/CustomField/CustomFieldValueFormat.js.map +1 -0
  382. package/build/dist/Types/CustomField/CustomFieldValueValidator.js +280 -0
  383. package/build/dist/Types/CustomField/CustomFieldValueValidator.js.map +1 -0
  384. package/build/dist/Types/CustomField/CustomFieldVariableKey.js +150 -0
  385. package/build/dist/Types/CustomField/CustomFieldVariableKey.js.map +1 -0
  386. package/build/dist/Types/SafeHtml.js +84 -0
  387. package/build/dist/Types/SafeHtml.js.map +1 -0
  388. package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js +117 -0
  389. package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js.map +1 -0
  390. package/build/dist/Types/StatusPage/IncidentCreatedResend.js +86 -0
  391. package/build/dist/Types/StatusPage/IncidentCreatedResend.js.map +1 -0
  392. package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js +246 -0
  393. package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js.map +1 -0
  394. package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js +183 -0
  395. package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js.map +1 -0
  396. package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js +277 -0
  397. package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js.map +1 -0
  398. package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js +27 -0
  399. package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js.map +1 -0
  400. package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js +190 -0
  401. package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js.map +1 -0
  402. package/build/dist/Types/StatusPage/SubscriberNotificationResend.js +102 -0
  403. package/build/dist/Types/StatusPage/SubscriberNotificationResend.js.map +1 -0
  404. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js +97 -0
  405. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js.map +1 -0
  406. package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js +175 -0
  407. package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js.map +1 -0
  408. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js +144 -4
  409. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js.map +1 -1
  410. package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js +29 -9
  411. package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js.map +1 -1
  412. package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js +169 -0
  413. package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js.map +1 -0
  414. package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js +212 -0
  415. package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js.map +1 -0
  416. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +64 -49
  417. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  418. package/build/dist/UI/Components/Forms/ModelForm.js +39 -5
  419. package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
  420. package/build/dist/UI/Components/Forms/Validation.js +1 -1
  421. package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
  422. package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
  423. package/build/dist/UI/Components/ModelTable/AnalyticsModelTable.js.map +1 -1
  424. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +2 -2
  425. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  426. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +13 -1
  427. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
  428. package/build/dist/UI/Components/ModelTable/ModelTable.js +1 -1
  429. package/build/dist/UI/Components/ModelTable/ModelTable.js.map +1 -1
  430. package/build/dist/UI/Utils/SensitiveUrlToken.js +10 -5
  431. package/build/dist/UI/Utils/SensitiveUrlToken.js.map +1 -1
  432. package/build/dist/Utils/API.js +177 -1
  433. package/build/dist/Utils/API.js.map +1 -1
  434. package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js +155 -0
  435. package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js.map +1 -0
  436. package/build/dist/Utils/Markdown/MarkdownEscape.js +48 -0
  437. package/build/dist/Utils/Markdown/MarkdownEscape.js.map +1 -1
  438. package/build/dist/Utils/ProbeNetworkFailureUtil.js +460 -0
  439. package/build/dist/Utils/ProbeNetworkFailureUtil.js.map +1 -0
  440. package/package.json +1 -1
@@ -0,0 +1,311 @@
1
+ import Incident from "../../../Models/DatabaseModels/Incident";
2
+ import IncidentCustomField from "../../../Models/DatabaseModels/IncidentCustomField";
3
+ import IncidentState from "../../../Models/DatabaseModels/IncidentState";
4
+ import IncidentTemplate from "../../../Models/DatabaseModels/IncidentTemplate";
5
+ import CustomFieldMappingService from "../../../Server/Services/CustomFieldMappingService";
6
+ import IncidentCustomFieldService from "../../../Server/Services/IncidentCustomFieldService";
7
+ import IncidentService from "../../../Server/Services/IncidentService";
8
+ import IncidentStateService from "../../../Server/Services/IncidentStateService";
9
+ import IncidentTemplateService from "../../../Server/Services/IncidentTemplateService";
10
+ import ProjectService from "../../../Server/Services/ProjectService";
11
+ import UserService from "../../../Server/Services/UserService";
12
+ import CreateBy from "../../../Server/Types/Database/CreateBy";
13
+ import { OnCreate } from "../../../Server/Types/Database/Hooks";
14
+ import ProjectScopedReferenceValidator from "../../../Server/Utils/Database/ProjectScopedReferenceValidator";
15
+ import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
16
+ import CustomFieldType from "../../../Types/CustomField/CustomFieldType";
17
+ import { JSONObject } from "../../../Types/JSON";
18
+ import ObjectID from "../../../Types/ObjectID";
19
+ import getJestMockFunction, { MockFunction } from "../../MockType";
20
+ import {
21
+ afterEach,
22
+ beforeEach,
23
+ describe,
24
+ expect,
25
+ jest,
26
+ test,
27
+ } from "@jest/globals";
28
+
29
+ /*
30
+ * An incident created with `createdIncidentTemplateId` (API callers that send
31
+ * only the template, and the root paths that declare from one) copies the
32
+ * template's custom field values - a MERGE into what the incident is given:
33
+ *
34
+ * - the template fills in the fields the incident leaves out;
35
+ * - a value the incident sets wins, 0, false and null included;
36
+ * - the template's values are not checked as the caller's (the check sees
37
+ * only what the caller sent), so a stale template value never refuses
38
+ * the incident;
39
+ * - the mapping from monitors still runs after the copy, and wins for a
40
+ * mapped field.
41
+ */
42
+
43
+ const projectId: ObjectID = new ObjectID(
44
+ "11111111-1111-4111-8111-111111111111",
45
+ );
46
+ const templateId: string = "a1b2c3d4-0000-4000-8000-0000000000aa";
47
+
48
+ const MEMBER_PROPS: DatabaseCommonInteractionProps = {
49
+ tenantId: projectId,
50
+ userId: new ObjectID("44444444-4444-4444-8444-444444444444"),
51
+ };
52
+
53
+ type OnBeforeCreate = (
54
+ createBy: CreateBy<Incident>,
55
+ ) => Promise<OnCreate<Incident>>;
56
+
57
+ function definition(
58
+ name: string,
59
+ customFieldType: CustomFieldType,
60
+ dropdownOptions?: string,
61
+ ): IncidentCustomField {
62
+ const field: IncidentCustomField = new IncidentCustomField();
63
+ field.name = name;
64
+ field.customFieldType = customFieldType;
65
+
66
+ if (dropdownOptions) {
67
+ field.dropdownOptions = dropdownOptions;
68
+ }
69
+
70
+ return field;
71
+ }
72
+
73
+ let template: IncidentTemplate | null = null;
74
+ let templateFindOneBy: MockFunction;
75
+ let definitionFindBy: MockFunction;
76
+ let applyMappingsToCreate: MockFunction;
77
+
78
+ function templateWith(customFields: unknown): IncidentTemplate {
79
+ const incidentTemplate: IncidentTemplate = new IncidentTemplate();
80
+ incidentTemplate._id = templateId;
81
+ incidentTemplate.customFields = customFields as JSONObject;
82
+ return incidentTemplate;
83
+ }
84
+
85
+ function newIncident(customFields?: unknown): Incident {
86
+ const incident: Incident = new Incident();
87
+ incident.title = "Site outage";
88
+ incident.incidentSeverityId = new ObjectID(
89
+ "d0000000-0000-4000-8000-000000000001",
90
+ );
91
+ incident.createdIncidentTemplateId = templateId;
92
+
93
+ if (customFields !== undefined) {
94
+ incident.customFields = customFields as JSONObject;
95
+ }
96
+
97
+ return incident;
98
+ }
99
+
100
+ async function runBeforeCreate(
101
+ data: Incident,
102
+ props: DatabaseCommonInteractionProps = MEMBER_PROPS,
103
+ ): Promise<Incident> {
104
+ const onCreate: OnCreate<Incident> = await (
105
+ IncidentService as unknown as { onBeforeCreate: OnBeforeCreate }
106
+ ).onBeforeCreate({ data, props });
107
+
108
+ return onCreate.createBy.data;
109
+ }
110
+
111
+ beforeEach(() => {
112
+ template = null;
113
+
114
+ const createdState: IncidentState = new IncidentState();
115
+ createdState._id = "c0000000-0000-4000-8000-000000000001";
116
+ jest
117
+ .spyOn(IncidentStateService, "findOneBy")
118
+ .mockResolvedValue(createdState as never);
119
+ jest
120
+ .spyOn(ProjectService, "incrementAndGetIncidentCounter")
121
+ .mockResolvedValue({ counter: 7, prefix: undefined } as never);
122
+ jest
123
+ .spyOn(ProjectScopedReferenceValidator, "validateReferencesBelongToProject")
124
+ .mockResolvedValue(undefined as never);
125
+ jest
126
+ .spyOn(UserService, "getUserMarkdownString")
127
+ .mockResolvedValue("Test User" as never);
128
+
129
+ templateFindOneBy = getJestMockFunction();
130
+ templateFindOneBy.mockImplementation(() => {
131
+ return Promise.resolve(template);
132
+ });
133
+ jest
134
+ .spyOn(IncidentTemplateService, "findOneBy")
135
+ .mockImplementation(templateFindOneBy as never);
136
+
137
+ definitionFindBy = getJestMockFunction();
138
+ definitionFindBy.mockResolvedValue([
139
+ definition("Impact", CustomFieldType.Dropdown, "Low\nMedium\nHigh"),
140
+ definition("Estimated Duration", CustomFieldType.Number),
141
+ definition("Acknowledgement", CustomFieldType.Boolean),
142
+ ] as never);
143
+ jest
144
+ .spyOn(IncidentCustomFieldService, "findBy")
145
+ .mockImplementation(definitionFindBy as never);
146
+
147
+ applyMappingsToCreate = getJestMockFunction();
148
+ applyMappingsToCreate.mockResolvedValue(undefined as never);
149
+ jest
150
+ .spyOn(CustomFieldMappingService, "applyMappingsToCreate")
151
+ .mockImplementation(applyMappingsToCreate as never);
152
+ });
153
+
154
+ afterEach(() => {
155
+ jest.restoreAllMocks();
156
+ });
157
+
158
+ describe("IncidentService.onBeforeCreate: a template's custom field values", () => {
159
+ test("are read with the template, as root and within the project", async () => {
160
+ template = templateWith({ Impact: "High" });
161
+
162
+ await runBeforeCreate(newIncident());
163
+
164
+ const findOneBy: {
165
+ query: JSONObject;
166
+ select: JSONObject;
167
+ props: DatabaseCommonInteractionProps;
168
+ } = templateFindOneBy.mock.calls[0]![0] as {
169
+ query: JSONObject;
170
+ select: JSONObject;
171
+ props: DatabaseCommonInteractionProps;
172
+ };
173
+
174
+ expect(findOneBy.select["customFields"]).toBe(true);
175
+ expect(findOneBy.query["projectId"]).toBe(projectId);
176
+ expect(findOneBy.query["_id"]).toBe(templateId);
177
+ expect(findOneBy.props).toEqual({ isRoot: true });
178
+ });
179
+
180
+ test("an incident created with none gets the template's", async () => {
181
+ template = templateWith({ Impact: "High", Category: "Network" });
182
+
183
+ const created: Incident = await runBeforeCreate(newIncident());
184
+
185
+ expect(created.customFields).toEqual({
186
+ Impact: "High",
187
+ Category: "Network",
188
+ });
189
+ });
190
+
191
+ test("merge into the incident's own values: the template fills only the gaps", async () => {
192
+ template = templateWith({ Impact: "High", Category: "Network" });
193
+
194
+ const created: Incident = await runBeforeCreate(
195
+ newIncident({ Impact: "Low", Ticket: "OPS-7" }),
196
+ );
197
+
198
+ expect(created.customFields).toEqual({
199
+ Impact: "Low",
200
+ Category: "Network",
201
+ Ticket: "OPS-7",
202
+ });
203
+ });
204
+
205
+ test("0, false and null from the caller are kept, not filled over", async () => {
206
+ template = templateWith({
207
+ "Estimated Duration": 5,
208
+ Acknowledgement: true,
209
+ Impact: "High",
210
+ });
211
+
212
+ const created: Incident = await runBeforeCreate(
213
+ newIncident({
214
+ "Estimated Duration": 0,
215
+ Acknowledgement: false,
216
+ Impact: null,
217
+ }),
218
+ );
219
+
220
+ expect(created.customFields).toEqual({
221
+ "Estimated Duration": 0,
222
+ Acknowledgement: false,
223
+ Impact: null,
224
+ });
225
+ });
226
+
227
+ test("a template value that no longer fits its field does not refuse the incident", async () => {
228
+ // "Critical" was removed from Impact after the template was saved.
229
+ template = templateWith({ Impact: "Critical" });
230
+
231
+ const created: Incident = await runBeforeCreate(
232
+ newIncident({ Acknowledgement: true }),
233
+ );
234
+
235
+ expect(created.customFields).toEqual({
236
+ Impact: "Critical",
237
+ Acknowledgement: true,
238
+ });
239
+
240
+ // Only the caller's own values were checked.
241
+ expect(definitionFindBy).toHaveBeenCalledTimes(1);
242
+ });
243
+
244
+ test("the caller's values are still checked", async () => {
245
+ template = templateWith({ Impact: "High" });
246
+
247
+ await expect(
248
+ runBeforeCreate(newIncident({ Impact: "Critical" })),
249
+ ).rejects.toThrow('"Critical" is not one of the options for "Impact"');
250
+ });
251
+
252
+ test("a template without values leaves the incident's as they are", async () => {
253
+ for (const templateCustomFields of [undefined, null, {}]) {
254
+ template = templateWith(templateCustomFields);
255
+
256
+ const withValues: Incident = await runBeforeCreate(
257
+ newIncident({ Ticket: "OPS-7" }),
258
+ );
259
+ expect(withValues.customFields).toEqual({ Ticket: "OPS-7" });
260
+
261
+ const withoutValues: Incident = await runBeforeCreate(newIncident());
262
+ expect(withoutValues.customFields).toBeUndefined();
263
+ }
264
+ });
265
+
266
+ test("no template found, nothing copied", async () => {
267
+ template = null;
268
+
269
+ const created: Incident = await runBeforeCreate(newIncident());
270
+
271
+ expect(created.customFields).toBeUndefined();
272
+ });
273
+
274
+ test("the mapping from monitors runs after the copy, and wins for a mapped field", async () => {
275
+ template = templateWith({ Impact: "High", Category: "Network" });
276
+
277
+ let seenByMapping: JSONObject | undefined = undefined;
278
+
279
+ applyMappingsToCreate.mockImplementation(((data: {
280
+ createBy: CreateBy<Incident>;
281
+ }) => {
282
+ seenByMapping = { ...(data.createBy.data.customFields || {}) };
283
+ data.createBy.data.customFields = {
284
+ ...(data.createBy.data.customFields || {}),
285
+ Impact: "Copied From Monitor",
286
+ };
287
+ return Promise.resolve();
288
+ }) as never);
289
+
290
+ const created: Incident = await runBeforeCreate(newIncident());
291
+
292
+ expect(seenByMapping).toEqual({ Impact: "High", Category: "Network" });
293
+ expect(created.customFields).toEqual({
294
+ Impact: "Copied From Monitor",
295
+ Category: "Network",
296
+ });
297
+ });
298
+
299
+ test("a root create from a template copies them too", async () => {
300
+ template = templateWith({ Impact: "High" });
301
+
302
+ const incident: Incident = newIncident();
303
+ incident.projectId = projectId;
304
+
305
+ const created: Incident = await runBeforeCreate(incident, {
306
+ isRoot: true,
307
+ });
308
+
309
+ expect(created.customFields).toEqual({ Impact: "High" });
310
+ });
311
+ });
@@ -0,0 +1,363 @@
1
+ import IncidentTemplate from "../../../Models/DatabaseModels/IncidentTemplate";
2
+ import StatusPage from "../../../Models/DatabaseModels/StatusPage";
3
+ import IncidentTemplateService from "../../../Server/Services/IncidentTemplateService";
4
+ import StatusPageService from "../../../Server/Services/StatusPageService";
5
+ import CreateBy from "../../../Server/Types/Database/CreateBy";
6
+ import { OnCreate, OnUpdate } from "../../../Server/Types/Database/Hooks";
7
+ import UpdateBy from "../../../Server/Types/Database/UpdateBy";
8
+ import ProjectScopedReferenceValidator from "../../../Server/Utils/Database/ProjectScopedReferenceValidator";
9
+ import StatusPageReadAccess from "../../../Server/Utils/StatusPage/StatusPageReadAccess";
10
+ import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
11
+ import Dictionary from "../../../Types/Dictionary";
12
+ import NotAuthorizedException from "../../../Types/Exception/NotAuthorizedException";
13
+ import ObjectID from "../../../Types/ObjectID";
14
+ import Permission from "../../../Types/Permission";
15
+ import getJestMockFunction, { MockFunction } from "../../MockType";
16
+ import {
17
+ afterEach,
18
+ beforeEach,
19
+ describe,
20
+ expect,
21
+ jest,
22
+ test,
23
+ } from "@jest/globals";
24
+
25
+ /*
26
+ * An incident template can limit the incidents declared from it to status
27
+ * pages (IncidentTemplate.statusPages). IncidentTemplateService owns two
28
+ * things about that list:
29
+ *
30
+ * - isScopedToStatusPages follows writes to it and nothing else. It is how
31
+ * a template whose pages were all deleted (the join rows cascade away)
32
+ * still declares incidents limited - to nothing - instead of incidents
33
+ * that reach every status page listing their monitors;
34
+ * - the pages a non-root caller adds must be pages they can read. Incident
35
+ * members may edit templates, and a template's pages are copied onto
36
+ * every incident declared from it, so this is the same rule as picking an
37
+ * incident's pages.
38
+ *
39
+ * The database is stubbed: stored templates by a stub of findBy, and which
40
+ * status pages the caller can read by a stub of the status page service.
41
+ */
42
+
43
+ const projectId: ObjectID = new ObjectID(
44
+ "5d7f3c0a-4c55-4d3e-9a3e-2d4a7d1c9e01",
45
+ );
46
+ const templateId: string = "a1b2c3d4-0000-4000-8000-0000000000aa";
47
+
48
+ const PAGE_A: string = "b0000000-0000-4000-8000-00000000000a";
49
+ const PAGE_B: string = "b0000000-0000-4000-8000-00000000000b";
50
+ const HIDDEN_PAGE_X: string = "b0000000-0000-4000-8000-0000000000f1";
51
+
52
+ const MEMBER_PROPS: DatabaseCommonInteractionProps = {
53
+ userId: new ObjectID("5d7f3c0a-4c55-4d3e-9a3e-2d4a7d1c9e02"),
54
+ tenantId: projectId,
55
+ };
56
+
57
+ type OnBeforeCreate = (
58
+ createBy: CreateBy<IncidentTemplate>,
59
+ ) => Promise<OnCreate<IncidentTemplate>>;
60
+ type OnBeforeUpdate = (
61
+ updateBy: UpdateBy<IncidentTemplate>,
62
+ ) => Promise<OnUpdate<IncidentTemplate>>;
63
+
64
+ function statusPage(id: string): StatusPage {
65
+ const page: StatusPage = new StatusPage();
66
+ page._id = id;
67
+ return page;
68
+ }
69
+
70
+ let storedTemplates: Array<IncidentTemplate> = [];
71
+ let readable: Array<string> | null = [];
72
+ let templateFindBy: MockFunction;
73
+ let statusPageFindBy: MockFunction;
74
+
75
+ function storedTemplate(statusPageIds: Array<string>): IncidentTemplate {
76
+ const template: IncidentTemplate = new IncidentTemplate();
77
+ template._id = templateId;
78
+ template.projectId = projectId;
79
+ template.statusPages = statusPageIds.map(statusPage);
80
+ return template;
81
+ }
82
+
83
+ async function runBeforeCreate(
84
+ data: IncidentTemplate,
85
+ props: DatabaseCommonInteractionProps = MEMBER_PROPS,
86
+ ): Promise<IncidentTemplate> {
87
+ const onCreate: OnCreate<IncidentTemplate> = await (
88
+ IncidentTemplateService as unknown as { onBeforeCreate: OnBeforeCreate }
89
+ ).onBeforeCreate({ data, props });
90
+
91
+ return onCreate.createBy.data;
92
+ }
93
+
94
+ async function runBeforeUpdate(
95
+ data: Record<string, unknown>,
96
+ props: DatabaseCommonInteractionProps = MEMBER_PROPS,
97
+ ): Promise<Record<string, unknown>> {
98
+ const onUpdate: OnUpdate<IncidentTemplate> = await (
99
+ IncidentTemplateService as unknown as { onBeforeUpdate: OnBeforeUpdate }
100
+ ).onBeforeUpdate({
101
+ query: { _id: templateId },
102
+ data: data as UpdateBy<IncidentTemplate>["data"],
103
+ props: props,
104
+ limit: 1,
105
+ skip: 0,
106
+ });
107
+
108
+ return onUpdate.updateBy.data as unknown as Record<string, unknown>;
109
+ }
110
+
111
+ beforeEach(() => {
112
+ storedTemplates = [storedTemplate([])];
113
+ readable = [PAGE_A, PAGE_B];
114
+
115
+ templateFindBy = getJestMockFunction();
116
+ templateFindBy.mockImplementation(() => {
117
+ return Promise.resolve(storedTemplates);
118
+ });
119
+ jest
120
+ .spyOn(IncidentTemplateService, "findBy")
121
+ .mockImplementation(templateFindBy as never);
122
+
123
+ statusPageFindBy = getJestMockFunction();
124
+ statusPageFindBy.mockImplementation(
125
+ (findBy: { query: { _id: unknown } }): Promise<Array<StatusPage>> => {
126
+ if (readable === null) {
127
+ return Promise.reject(
128
+ new NotAuthorizedException(
129
+ "You do not have permissions to read Status Page.",
130
+ ),
131
+ );
132
+ }
133
+
134
+ const operator: { objectLiteralParameters?: Dictionary<unknown> } = findBy
135
+ .query._id as { objectLiteralParameters?: Dictionary<unknown> };
136
+
137
+ return Promise.resolve(
138
+ (
139
+ Object.values(operator.objectLiteralParameters || {}) as Array<
140
+ Array<string>
141
+ >
142
+ )
143
+ .flat()
144
+ .map((id: string): string => {
145
+ return id.toLowerCase();
146
+ })
147
+ .filter((id: string): boolean => {
148
+ return readable!.includes(id);
149
+ })
150
+ .map(statusPage),
151
+ );
152
+ },
153
+ );
154
+ jest
155
+ .spyOn(StatusPageService, "findBy")
156
+ .mockImplementation(statusPageFindBy as never);
157
+
158
+ jest
159
+ .spyOn(ProjectScopedReferenceValidator, "validateReferencesBelongToProject")
160
+ .mockResolvedValue(undefined as never);
161
+ jest
162
+ .spyOn(ProjectScopedReferenceValidator, "getHeldRelationIds")
163
+ .mockResolvedValue(new Map() as never);
164
+ });
165
+
166
+ afterEach(() => {
167
+ jest.restoreAllMocks();
168
+ });
169
+
170
+ describe("IncidentTemplateService: isScopedToStatusPages follows the template's status pages", () => {
171
+ test("a template created with pages is scoped", async () => {
172
+ const created: IncidentTemplate = await runBeforeCreate(
173
+ Object.assign(new IncidentTemplate(), {
174
+ statusPages: [statusPage(PAGE_A)],
175
+ }),
176
+ );
177
+
178
+ expect(created.isScopedToStatusPages).toBe(true);
179
+ });
180
+
181
+ test("a template created without pages is not, whatever the client sent", async () => {
182
+ const created: IncidentTemplate = await runBeforeCreate(
183
+ Object.assign(new IncidentTemplate(), {
184
+ isScopedToStatusPages: true,
185
+ }),
186
+ );
187
+
188
+ expect(created.isScopedToStatusPages).toBe(false);
189
+
190
+ const empty: IncidentTemplate = await runBeforeCreate(
191
+ Object.assign(new IncidentTemplate(), {
192
+ statusPages: [],
193
+ isScopedToStatusPages: true,
194
+ }),
195
+ );
196
+
197
+ expect(empty.isScopedToStatusPages).toBe(false);
198
+ });
199
+
200
+ test("writing pages scopes it, and clearing them unscopes it", async () => {
201
+ expect(
202
+ (await runBeforeUpdate({ statusPages: [PAGE_A] }))[
203
+ "isScopedToStatusPages"
204
+ ],
205
+ ).toBe(true);
206
+
207
+ storedTemplates = [storedTemplate([PAGE_A])];
208
+
209
+ expect(
210
+ (await runBeforeUpdate({ statusPages: [] }))["isScopedToStatusPages"],
211
+ ).toBe(false);
212
+ });
213
+
214
+ test("a client's flag is dropped; an update that does not write the list leaves the flag alone", async () => {
215
+ const data: Record<string, unknown> = await runBeforeUpdate({
216
+ name: "Region East outage",
217
+ isScopedToStatusPages: false,
218
+ });
219
+
220
+ expect(data).not.toHaveProperty("isScopedToStatusPages");
221
+ });
222
+ });
223
+
224
+ describe("IncidentTemplateService: the status pages a caller adds must be pages they can read", () => {
225
+ test("creating a template with a page the caller cannot read is refused", async () => {
226
+ readable = [PAGE_A];
227
+
228
+ await expect(
229
+ runBeforeCreate(
230
+ Object.assign(new IncidentTemplate(), {
231
+ statusPages: [statusPage(PAGE_A), statusPage(PAGE_B)],
232
+ }),
233
+ ),
234
+ ).rejects.toThrow(
235
+ StatusPageReadAccess.getRefusalMessage("incident template"),
236
+ );
237
+ });
238
+
239
+ test("a caller without status page access cannot give a template pages", async () => {
240
+ readable = null;
241
+
242
+ await expect(
243
+ runBeforeCreate(
244
+ Object.assign(new IncidentTemplate(), {
245
+ statusPages: [statusPage(PAGE_A)],
246
+ }),
247
+ {
248
+ ...MEMBER_PROPS,
249
+ userTenantAccessPermission: {
250
+ [projectId.toString()]: {
251
+ projectId: projectId,
252
+ _type: "UserTenantAccessPermission",
253
+ permissions: [
254
+ {
255
+ _type: "UserPermission",
256
+ permission: Permission.IncidentMember,
257
+ labelIds: [],
258
+ isBlockPermission: false,
259
+ },
260
+ ],
261
+ },
262
+ },
263
+ },
264
+ ),
265
+ ).rejects.toBeInstanceOf(NotAuthorizedException);
266
+ });
267
+
268
+ test("adding a page the caller cannot read to a template is refused", async () => {
269
+ readable = [PAGE_A];
270
+
271
+ await expect(
272
+ runBeforeUpdate({ statusPages: [PAGE_A, PAGE_B] }),
273
+ ).rejects.toBeInstanceOf(NotAuthorizedException);
274
+ });
275
+
276
+ test("a page the template already holds is not added, so it is not checked", async () => {
277
+ readable = [PAGE_A];
278
+ storedTemplates = [storedTemplate([PAGE_A, HIDDEN_PAGE_X])];
279
+
280
+ const data: Record<string, unknown> = await runBeforeUpdate({
281
+ statusPages: [PAGE_A, HIDDEN_PAGE_X],
282
+ });
283
+
284
+ expect(data["isScopedToStatusPages"]).toBe(true);
285
+ });
286
+
287
+ test("reads the templates as root, limited to the caller's project", async () => {
288
+ await runBeforeUpdate({ statusPages: [PAGE_A] });
289
+
290
+ const findBy: {
291
+ query: Record<string, unknown>;
292
+ props: DatabaseCommonInteractionProps;
293
+ } = templateFindBy.mock.calls[0]![0];
294
+
295
+ expect(findBy.props).toEqual({ isRoot: true });
296
+ expect(findBy.query["projectId"]).toBe(projectId);
297
+ expect(findBy.query["_id"]).toBe(templateId);
298
+ });
299
+
300
+ test("a root caller adds any page", async () => {
301
+ readable = null;
302
+
303
+ const data: Record<string, unknown> = await runBeforeUpdate(
304
+ { statusPages: [PAGE_B] },
305
+ { isRoot: true },
306
+ );
307
+
308
+ expect(data["isScopedToStatusPages"]).toBe(true);
309
+ expect(statusPageFindBy).not.toHaveBeenCalled();
310
+ // Only the project lookup a root update without a tenant always makes.
311
+ for (const call of templateFindBy.mock.calls) {
312
+ expect(
313
+ (call[0] as { select: Record<string, unknown> }).select,
314
+ ).not.toHaveProperty("statusPages");
315
+ }
316
+ });
317
+
318
+ test("clearing the list checks nothing", async () => {
319
+ readable = null;
320
+ storedTemplates = [storedTemplate([PAGE_A])];
321
+
322
+ await runBeforeUpdate({ statusPages: [] });
323
+
324
+ expect(statusPageFindBy).not.toHaveBeenCalled();
325
+ });
326
+ });
327
+
328
+ describe("IncidentTemplate.isScopedToStatusPages column", () => {
329
+ function access(column: string): {
330
+ create: Array<Permission>;
331
+ read: Array<Permission>;
332
+ update: Array<Permission>;
333
+ } {
334
+ const columnAccess: {
335
+ create?: Array<Permission>;
336
+ read?: Array<Permission>;
337
+ update?: Array<Permission>;
338
+ } = new IncidentTemplate().getColumnAccessControlFor(column) || {};
339
+
340
+ return {
341
+ create: (columnAccess.create || []).slice(),
342
+ read: (columnAccess.read || []).slice(),
343
+ update: (columnAccess.update || []).slice(),
344
+ };
345
+ }
346
+
347
+ test("has the access control of the template's status pages", () => {
348
+ expect(access("isScopedToStatusPages")).toEqual(access("statusPages"));
349
+ });
350
+
351
+ test("is computed, required, false by default and hidden from the API docs", () => {
352
+ const template: IncidentTemplate = new IncidentTemplate();
353
+
354
+ expect(template.getTableColumnMetadata("isScopedToStatusPages")).toEqual(
355
+ expect.objectContaining({
356
+ computed: true,
357
+ required: true,
358
+ hideColumnInDocumentation: true,
359
+ defaultValue: false,
360
+ }),
361
+ );
362
+ });
363
+ });