@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.
- package/Models/DatabaseModels/Incident.ts +267 -1
- package/Models/DatabaseModels/IncidentCustomField.ts +202 -0
- package/Models/DatabaseModels/IncidentEpisode.ts +30 -0
- package/Models/DatabaseModels/IncidentTemplate.ts +126 -1
- package/Models/DatabaseModels/InventoryItem.ts +23 -12
- package/Models/DatabaseModels/StatusPage.ts +55 -0
- package/Models/DatabaseModels/StatusPageSubscriber.ts +128 -0
- package/Server/API/IncidentAPI.ts +162 -0
- package/Server/API/StatusPageAPI.ts +487 -171
- package/Server/API/StatusPageSubscriberAPI.ts +63 -15
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.ts +217 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.ts +49 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.ts +108 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.ts +51 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.ts +168 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.ts +44 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +16 -0
- package/Server/Infrastructure/Semaphore.ts +10 -0
- package/Server/Middleware/IdentityRateLimit.ts +108 -3
- package/Server/Middleware/SubscriberNotificationPreviewRateLimit.ts +237 -0
- package/Server/Middleware/SubscriberNotificationTestSendRateLimit.ts +304 -0
- package/Server/Services/AlertService.ts +32 -36
- package/Server/Services/DatabaseService.ts +93 -10
- package/Server/Services/IncidentAlertService.ts +609 -2
- package/Server/Services/IncidentCustomFieldService.ts +276 -1
- package/Server/Services/IncidentEpisodePublicNoteService.ts +28 -0
- package/Server/Services/IncidentEpisodeService.ts +15 -0
- package/Server/Services/IncidentEpisodeStateTimelineService.ts +27 -1
- package/Server/Services/IncidentPublicNoteService.ts +30 -1
- package/Server/Services/IncidentService.ts +1663 -103
- package/Server/Services/IncidentStateTimelineService.ts +13 -0
- package/Server/Services/IncidentTemplateService.ts +109 -0
- package/Server/Services/InventoryItemService.ts +38 -0
- package/Server/Services/IoTDeviceCredentialService.ts +13 -4
- package/Server/Services/ScheduledMaintenancePublicNoteService.ts +30 -1
- package/Server/Services/ScheduledMaintenanceService.ts +118 -64
- package/Server/Services/SmsService.ts +10 -0
- package/Server/Services/StatusPageService.ts +65 -8
- package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +299 -37
- package/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.ts +129 -0
- package/Server/Services/StatusPageSubscriberService.ts +1161 -64
- package/Server/Services/UserNotificationRuleService.ts +55 -53
- package/Server/Services/WorkspaceNotificationSummaryService.ts +38 -20
- package/Server/Types/Markdown.ts +153 -6
- package/Server/Types/Workflow/Components/Email.ts +61 -2
- package/Server/Utils/AffectedResources/LinkedAffectedResources.ts +576 -0
- package/Server/Utils/Alert/AlertStateChangeAuthorization.ts +129 -0
- package/Server/Utils/CustomField/CustomFieldRename.ts +378 -0
- package/Server/Utils/DataSource/EgressGuard.ts +66 -21
- package/Server/Utils/Inventory/InventoryOverviewAggregation.ts +111 -0
- package/Server/Utils/Mail/PinnedSmtpSocket.ts +130 -0
- package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +10 -0
- package/Server/Utils/Response.ts +31 -0
- package/Server/Utils/SSRFProtection.ts +76 -16
- package/Server/Utils/StatusPage/IncidentStatusPageScope.ts +948 -0
- package/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.ts +656 -0
- package/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.ts +805 -0
- package/Server/Utils/StatusPage/StatusPageExclusion.ts +18 -0
- package/Server/Utils/StatusPage/StatusPageReadAccess.ts +125 -0
- package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.ts +151 -0
- package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.ts +70 -0
- package/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.ts +421 -0
- package/Server/Utils/StatusPage/SubscriberNotificationClaim.ts +107 -0
- package/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.ts +1049 -0
- package/Server/Utils/StatusPage/SubscriberNotificationFanOut.ts +180 -0
- package/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.ts +839 -0
- package/Server/Utils/StatusPage/SubscriberNotificationResendAccess.ts +393 -0
- package/Server/Utils/StatusPage/SubscriberNotificationRunLimit.ts +246 -0
- package/Server/Utils/StatusPage/SubscriberNotificationTiming.ts +133 -0
- package/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.ts +215 -0
- package/Server/Utils/StatusPageResource.ts +51 -20
- package/Server/Utils/Topology/TopologyConcurrencyLimiter.ts +229 -0
- package/Server/Utils/Topology/TopologyQueries.ts +1350 -0
- package/Server/Utils/Topology/TopologyRequest.ts +463 -0
- package/Server/Utils/Topology/TopologyResponseCache.ts +183 -0
- package/Server/Utils/Topology/TopologySql.ts +1024 -0
- package/Server/Utils/VM/VMAPI.ts +1 -0
- package/Server/Utils/VM/VMRunner.ts +10 -0
- package/Server/Utils/Workflow/OAuth2TokenClient.ts +10 -0
- package/Server/Views/Partials/SensitiveUrlToken.ejs +8 -5
- package/Tests/App/Accounts/RegisterVerifyEmail.test.tsx +748 -0
- package/Tests/App/Accounts/ResendVerificationEmail.test.tsx +1222 -0
- package/Tests/App/Accounts/VerificationEmailResendUtils.test.ts +797 -0
- package/Tests/App/Accounts/VerifyEmailPending.test.tsx +538 -0
- package/Tests/App/Accounts/VerifyEmailResend.test.tsx +779 -0
- package/Tests/App/Dashboard/AcknowledgeAlertsOnDeclare.test.ts +369 -0
- package/Tests/App/Dashboard/AffectedResourcesSlo.test.tsx +1 -1
- package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +510 -123
- package/Tests/App/Dashboard/AlertsTableAffectedResources.test.tsx +490 -0
- package/Tests/App/Dashboard/ChangeAlertStateDeclareIncident.test.tsx +886 -0
- package/Tests/App/Dashboard/CreateReviewStepRoleSummary.test.tsx +1250 -0
- package/Tests/App/Dashboard/CreateReviewStepSelectionSummary.test.tsx +738 -0
- package/Tests/App/Dashboard/CustomFieldsSettingsIncidentOptions.test.tsx +357 -0
- package/Tests/App/Dashboard/DeclareIncidentFromAlert.test.ts +417 -0
- package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +1948 -229
- package/Tests/App/Dashboard/EntityDetailPanelLifecycle.test.tsx +493 -0
- package/Tests/App/Dashboard/EntityDetailPanelNavigation.test.tsx +439 -0
- package/Tests/App/Dashboard/EventNotes.test.tsx +776 -0
- package/Tests/App/Dashboard/EventNotesPages.test.tsx +119 -0
- package/Tests/App/Dashboard/EventNotesUtil.test.ts +116 -17
- package/Tests/App/Dashboard/EventOverviewPages.test.tsx +534 -6
- package/Tests/App/Dashboard/EventStatusPanel.test.tsx +798 -2
- package/Tests/App/Dashboard/FetchStateAndSeverityElements.test.tsx +669 -0
- package/Tests/App/Dashboard/FetchUsers.test.tsx +230 -0
- package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +91 -1
- package/Tests/App/Dashboard/IncidentCreateAcknowledgeAlerts.test.tsx +1611 -0
- package/Tests/App/Dashboard/IncidentCreateDetailsStep.test.tsx +643 -0
- package/Tests/App/Dashboard/IncidentCreateFromAlerts.test.tsx +8 -1
- package/Tests/App/Dashboard/IncidentCreateStatusPageScope.test.tsx +601 -0
- package/Tests/App/Dashboard/IncidentCustomFieldTemplateVariables.test.tsx +386 -0
- package/Tests/App/Dashboard/IncidentNoteTemplatePlaceholders.test.tsx +124 -0
- package/Tests/App/Dashboard/IncidentNoteTemplateVariablesLoader.test.ts +305 -0
- package/Tests/App/Dashboard/IncidentOverviewNotifyDefault.test.tsx +112 -0
- package/Tests/App/Dashboard/IncidentSettingsRenotifyOnPublish.test.tsx +674 -0
- package/Tests/App/Dashboard/IncidentSettingsStatusPageScope.test.tsx +1051 -0
- package/Tests/App/Dashboard/IncidentTemplateCustomFields.test.tsx +384 -0
- package/Tests/App/Dashboard/IncidentTemplateStatusPageScopeCard.test.tsx +215 -0
- package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +1365 -57
- package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +964 -23
- package/Tests/App/Dashboard/InventoryRelationships.test.tsx +885 -0
- package/Tests/App/Dashboard/PublicNotePagesNotifyDefault.test.tsx +113 -1
- package/Tests/App/Dashboard/ServiceMapDatabaseLink.test.tsx +201 -15
- package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +508 -36
- package/Tests/App/Dashboard/SubscriberAudienceSummary.test.tsx +512 -0
- package/Tests/App/Dashboard/SubscriberNotificationPreviewModal.test.tsx +837 -0
- package/Tests/App/Dashboard/SubscriberNotificationStatusLabel.test.tsx +88 -0
- package/Tests/App/Dashboard/SubscriberNotificationStatusResend.test.tsx +473 -0
- package/Tests/App/Dashboard/SubscriberTemplateLivePreview.test.tsx +196 -0
- package/Tests/App/Dashboard/TeamAddedSubscribersUnsubscribedNotice.test.tsx +248 -0
- package/Tests/App/Dashboard/TopologyDataLifecycle.test.tsx +583 -0
- package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +982 -218
- package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +818 -114
- package/Tests/App/SensitiveUrlTokenBootstrap.test.ts +64 -3
- package/Tests/App/StatusPage/StatusPageUnsubscribePage.test.tsx +412 -0
- package/Tests/Models/IncidentCustomFieldApiSchemaContract.test.ts +217 -0
- package/Tests/Models/IncidentCustomFieldCreateSettingsColumns.test.ts +252 -0
- package/Tests/Models/StatusPageSubscriberUnsubscribeColumns.test.ts +218 -0
- package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +7 -0
- package/Tests/Server/API/IncidentSubscriberAudienceAPI.test.ts +1408 -0
- package/Tests/Server/API/StatusPageIncidentScope.test.ts +1868 -0
- package/Tests/Server/API/StatusPageIncidentScopePostgres.test.ts +790 -0
- package/Tests/Server/API/StatusPageManageSubscriptionRecipient.test.ts +10 -1
- package/Tests/Server/API/StatusPageManageSubscriptionTemplateVariables.test.ts +206 -18
- package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +14 -2
- package/Tests/Server/API/StatusPageSubscriberCreateResponse.test.ts +217 -0
- package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +103 -2
- package/Tests/Server/API/StatusPageUnsubscribeAPI.test.ts +396 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentStatusPageScopeMigration.test.ts +395 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentTemplateStatusPageScopeFlagMigration.test.ts +150 -0
- package/Tests/Server/Infrastructure/Postgres/AddSubscriberNotificationClaimedAtMigration.test.ts +156 -0
- package/Tests/Server/Infrastructure/Postgres/RenameInventoryItemMigration.test.ts +90 -21
- package/Tests/Server/Infrastructure/Postgres/SkipHiddenIncidentCreatedNotificationsMigration.test.ts +410 -0
- package/Tests/Server/Middleware/IdentityRateLimit.test.ts +551 -1
- package/Tests/Server/Middleware/SubscriberNotificationPreviewRateLimit.test.ts +238 -0
- package/Tests/Server/Middleware/SubscriberNotificationTestSendRateLimit.test.ts +245 -0
- package/Tests/Server/Services/AddStatusPageSubscriberUnsubscribeTokenMigration.test.ts +152 -0
- package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +221 -36
- package/Tests/Server/Services/DatabaseServiceCompareAndSetColumns.test.ts +191 -0
- package/Tests/Server/Services/DomainService.test.ts +525 -0
- package/Tests/Server/Services/IncidentAlertAcknowledgeOnDeclare.test.ts +2630 -0
- package/Tests/Server/Services/IncidentAlertPostgres.test.ts +1625 -1
- package/Tests/Server/Services/IncidentCreatedRenotifyOnPublish.test.ts +681 -0
- package/Tests/Server/Services/IncidentCreatedResend.test.ts +775 -0
- package/Tests/Server/Services/IncidentCustomFieldRenamePostgres.test.ts +713 -0
- package/Tests/Server/Services/IncidentCustomFieldService.test.ts +751 -0
- package/Tests/Server/Services/IncidentCustomFieldValueValidation.test.ts +556 -0
- package/Tests/Server/Services/IncidentDeclareAcknowledgeAlerts.test.ts +2189 -0
- package/Tests/Server/Services/IncidentStatusPageScope.test.ts +2971 -0
- package/Tests/Server/Services/IncidentStatusPageScopeFeed.test.ts +131 -0
- package/Tests/Server/Services/IncidentStatusPageScopePostgres.test.ts +625 -0
- package/Tests/Server/Services/IncidentTemplateCustomFieldsCopy.test.ts +311 -0
- package/Tests/Server/Services/IncidentTemplateStatusPageScope.test.ts +363 -0
- package/Tests/Server/Services/InventoryItemServiceOverviewCounts.test.ts +345 -0
- package/Tests/Server/Services/InventoryOverviewPostgres.test.ts +254 -0
- package/Tests/Server/Services/IoTDeviceCredentialService.test.ts +819 -0
- package/Tests/Server/Services/LinkedAffectedResourcesCreatedFeed.test.ts +420 -0
- package/Tests/Server/Services/PublicNoteEditFeedEventType.test.ts +213 -0
- package/Tests/Server/Services/PublicNoteResendAccess.test.ts +994 -0
- package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +289 -9
- package/Tests/Server/Services/SloAffectedResourceCreatedFeed.test.ts +92 -36
- package/Tests/Server/Services/SmsServiceFailIfNotSent.test.ts +67 -0
- package/Tests/Server/Services/StatusPageReportIncidentCounts.test.ts +610 -0
- package/Tests/Server/Services/StatusPageSubscriberAudienceCounts.test.ts +201 -0
- package/Tests/Server/Services/StatusPageSubscriberAudienceCountsPostgres.test.ts +271 -0
- package/Tests/Server/Services/StatusPageSubscriberConfirmationTemplateEscaping.test.ts +200 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationLogRedaction.test.ts +54 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateCompile.test.ts +37 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateIncidentRecordAccess.test.ts +772 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookup.test.ts +506 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookupPostgres.test.ts +261 -0
- package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +21 -5
- package/Tests/Server/Services/StatusPageSubscriberUnsubscribe.test.ts +1618 -0
- package/Tests/Server/Services/StatusPageSubscriberUnsubscribeLinkSenders.test.ts +373 -0
- package/Tests/Server/Services/StatusPageSubscriberUnsubscribePostgres.test.ts +388 -0
- package/Tests/Server/Services/SubscriberNotificationInFlightRequeue.test.ts +500 -0
- package/Tests/Server/Types/MarkdownEmailLinks.test.ts +320 -0
- package/Tests/Server/Types/Workflow/Components/Email.test.ts +491 -1
- package/Tests/Server/Types/Workflow/Components/EmailSmtpPinning.test.ts +398 -0
- package/Tests/Server/Utils/AffectedResources/LinkedAffectedResources.test.ts +858 -0
- package/Tests/Server/Utils/Alert/AlertStateChangeAuthorization.test.ts +1172 -0
- package/Tests/Server/Utils/CustomField/CustomFieldRename.test.ts +586 -0
- package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +246 -10
- package/Tests/Server/Utils/DataSource/EgressGuardDeploymentConfig.test.ts +226 -0
- package/Tests/Server/Utils/DataSource/EgressGuardPinning.test.ts +131 -0
- package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +4 -0
- package/Tests/Server/Utils/EgressPolicyEnvironment.ts +45 -0
- package/Tests/Server/Utils/Mail/PinnedSmtpSocket.test.ts +409 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +23 -0
- package/Tests/Server/Utils/SSRFProtection.test.ts +4 -0
- package/Tests/Server/Utils/SSRFProtectionCloudServiceAddresses.test.ts +4 -0
- package/Tests/Server/Utils/SSRFProtectionPrivateNetwork.test.ts +4 -0
- package/Tests/Server/Utils/SSRFProtectionResolutionDetail.test.ts +314 -0
- package/Tests/Server/Utils/StatusPage/IncidentStatusPageScope.test.ts +1699 -0
- package/Tests/Server/Utils/StatusPage/IncidentStatusPageScopeQueriesPostgres.test.ts +508 -0
- package/Tests/Server/Utils/StatusPage/IncidentSubscriberAudienceCallerPermission.test.ts +246 -0
- package/Tests/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.test.ts +1348 -0
- package/Tests/Server/Utils/StatusPage/StatusPageReadAccess.test.ts +221 -0
- package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.test.ts +132 -0
- package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.test.ts +155 -0
- package/Tests/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.test.ts +708 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaim.test.ts +255 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaimPostgres.test.ts +528 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.test.ts +1238 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationFanOut.test.ts +472 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.test.ts +1168 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationRunLimit.test.ts +275 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationTiming.test.ts +131 -0
- package/Tests/Server/Utils/StatusPageResource.test.ts +98 -0
- package/Tests/Server/Utils/Topology/InventoryItemEntityKeyIndexMigration.test.ts +386 -0
- package/Tests/Server/Utils/Topology/InventoryItemEntityKeyIndexMigrationPostgres.test.ts +430 -0
- package/Tests/Server/Utils/Topology/TopologyActivityParityPostgres.test.ts +279 -0
- package/Tests/Server/Utils/Topology/TopologyConcurrencyLimiter.test.ts +534 -0
- package/Tests/Server/Utils/Topology/TopologyEstateGenerator.ts +624 -0
- package/Tests/Server/Utils/Topology/TopologyJsonResponse.test.ts +90 -0
- package/Tests/Server/Utils/Topology/TopologyQueries.test.ts +1634 -0
- package/Tests/Server/Utils/Topology/TopologyQueriesDifferentialPostgres.test.ts +683 -0
- package/Tests/Server/Utils/Topology/TopologyQueriesPostgres.test.ts +2624 -0
- package/Tests/Server/Utils/Topology/TopologyReference.ts +1018 -0
- package/Tests/Server/Utils/Topology/TopologyRequest.test.ts +643 -0
- package/Tests/Server/Utils/Topology/TopologyResponseCache.test.ts +305 -0
- package/Tests/Server/Utils/Topology/TopologySql.test.ts +973 -0
- package/Tests/Server/Utils/VM/VMRunnerPrivateNetworkWiring.test.ts +17 -0
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +75 -0
- package/Tests/Types/CustomField/CustomFieldSavedViews.test.ts +247 -0
- package/Tests/Types/CustomField/CustomFieldTemplateValues.test.ts +186 -0
- package/Tests/Types/CustomField/CustomFieldTypeCorrespondence.test.ts +48 -0
- package/Tests/Types/CustomField/CustomFieldValueFormat.test.ts +215 -0
- package/Tests/Types/CustomField/CustomFieldValueValidator.test.ts +343 -0
- package/Tests/Types/CustomField/CustomFieldVariableKey.test.ts +364 -0
- package/Tests/Types/SafeHtml.test.ts +102 -0
- package/Tests/Types/StatusPage/IncidentCreatedRenotify.test.ts +419 -0
- package/Tests/Types/StatusPage/IncidentCreatedResend.test.ts +118 -0
- package/Tests/Types/StatusPage/IncidentScopeAddedPagesNotification.test.ts +558 -0
- package/Tests/Types/StatusPage/IncidentSubscriberAudience.test.ts +284 -0
- package/Tests/Types/StatusPage/StatusPageSubscriberUnsubscribe.test.ts +341 -0
- package/Tests/Types/StatusPage/SubscriberNotificationPreview.test.ts +196 -0
- package/Tests/Types/StatusPage/SubscriberNotificationResend.test.ts +201 -0
- package/Tests/Types/StatusPage/SubscriberNotificationTemplateCompiler.test.ts +277 -0
- package/Tests/Types/StatusPage/SubscriberNotificationTemplatePreview.test.ts +179 -0
- package/Tests/Types/StatusPage/SubscriberNotificationTemplateVariables.test.ts +324 -0
- package/Tests/Types/StatusPage/SubscriberUpdateNotification.test.ts +60 -13
- package/Tests/Types/Topology/TopologyTypeRules.test.ts +176 -0
- package/Tests/UI/Components/CustomFields/CustomFieldFormFields.test.ts +432 -0
- package/Tests/UI/Components/CustomFields/CustomFieldModelFormFields.test.tsx +685 -0
- package/Tests/UI/Components/CustomFields/CustomFieldsDetailOrder.test.tsx +469 -0
- package/Tests/UI/Components/Forms/ModelFormCustomFieldInputs.test.tsx +325 -0
- package/Tests/UI/Components/Forms/ModelFormDropdownOptionsRefused.test.tsx +358 -0
- package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +22 -0
- package/Tests/UI/Monitor/NetworkPathView.test.tsx +289 -0
- package/Tests/Utils/Incident/IncidentFromAlerts.test.ts +302 -0
- package/Tests/Utils/Incident/IncidentNoteTemplateVariables.test.ts +623 -0
- package/Tests/Utils/Markdown/MarkdownEscape.test.ts +89 -0
- package/Tests/Utils/ProbeNetworkFailureUtil.test.ts +992 -0
- package/Tests/Utils/RequestFailedDetails.test.ts +575 -0
- package/Types/CustomField/CustomFieldOrder.ts +55 -0
- package/Types/CustomField/CustomFieldSavedViews.ts +225 -0
- package/Types/CustomField/CustomFieldTemplateMerge.ts +71 -0
- package/Types/CustomField/CustomFieldType.ts +14 -0
- package/Types/CustomField/CustomFieldValueFormat.ts +161 -0
- package/Types/CustomField/CustomFieldValueValidator.ts +488 -0
- package/Types/CustomField/CustomFieldVariableKey.ts +211 -0
- package/Types/Incident/IncidentAlertLink.ts +19 -0
- package/Types/SafeHtml.ts +93 -0
- package/Types/StatusPage/IncidentCreatedRenotify.ts +224 -0
- package/Types/StatusPage/IncidentCreatedResend.ts +108 -0
- package/Types/StatusPage/IncidentScopeAddedPagesNotification.ts +343 -0
- package/Types/StatusPage/IncidentSubscriberAudience.ts +332 -0
- package/Types/StatusPage/StatusPageSubscriberUnsubscribe.ts +405 -0
- package/Types/StatusPage/SubscriberNotificationInterruption.ts +32 -0
- package/Types/StatusPage/SubscriberNotificationPreview.ts +322 -0
- package/Types/StatusPage/SubscriberNotificationResend.ts +132 -0
- package/Types/StatusPage/SubscriberNotificationTemplateCompiler.ts +180 -0
- package/Types/StatusPage/SubscriberNotificationTemplatePreview.ts +301 -0
- package/Types/StatusPage/SubscriberNotificationTemplateVariables.ts +217 -4
- package/Types/StatusPage/SubscriberUpdateNotification.ts +32 -11
- package/Types/Telemetry/InventoryLiveness.ts +26 -0
- package/Types/Topology/TopologyApi.ts +428 -0
- package/Types/Topology/TopologyTypeRules.ts +191 -0
- package/UI/Components/CustomFields/CustomFieldFormFields.ts +317 -0
- package/UI/Components/CustomFields/CustomFieldModelFormFields.tsx +395 -0
- package/UI/Components/CustomFields/CustomFieldsDetail.tsx +95 -55
- package/UI/Components/Forms/ModelForm.tsx +70 -8
- package/UI/Components/Forms/Validation.ts +1 -1
- package/UI/Components/ModelFormModal/ModelFormModal.tsx +2 -4
- package/UI/Components/ModelTable/AnalyticsModelTable.tsx +2 -6
- package/UI/Components/ModelTable/BaseModelTable.tsx +9 -8
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +14 -1
- package/UI/Components/ModelTable/ModelTable.tsx +6 -7
- package/UI/Utils/SensitiveUrlToken.ts +10 -5
- package/Utils/API.ts +247 -2
- package/Utils/Incident/IncidentFromAlerts.ts +78 -0
- package/Utils/Incident/IncidentNoteTemplateVariables.ts +304 -0
- package/Utils/Markdown/MarkdownEscape.ts +65 -0
- package/Utils/ProbeNetworkFailureUtil.ts +673 -0
- package/build/dist/Models/DatabaseModels/Incident.js +267 -1
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentCustomField.js +207 -1
- package/build/dist/Models/DatabaseModels/IncidentCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js +31 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js +124 -1
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/InventoryItem.js +20 -11
- package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +56 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js +131 -0
- package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js.map +1 -1
- package/build/dist/Server/API/IncidentAPI.js +100 -1
- package/build/dist/Server/API/IncidentAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +417 -176
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageSubscriberAPI.js +52 -13
- package/build/dist/Server/API/StatusPageSubscriberAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.js +165 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js +41 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js +57 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js +23 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js +40 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js +89 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js +29 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Semaphore.js +3 -0
- package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
- package/build/dist/Server/Middleware/IdentityRateLimit.js +89 -3
- package/build/dist/Server/Middleware/IdentityRateLimit.js.map +1 -1
- package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js +149 -0
- package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js.map +1 -0
- package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js +190 -0
- package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js.map +1 -0
- package/build/dist/Server/Services/AlertService.js +21 -33
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +56 -7
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentAlertService.js +457 -3
- package/build/dist/Server/Services/IncidentAlertService.js.map +1 -1
- package/build/dist/Server/Services/IncidentCustomFieldService.js +204 -8
- package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +24 -0
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js +14 -0
- package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +26 -0
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js +26 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +1148 -42
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +12 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentTemplateService.js +83 -0
- package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
- package/build/dist/Server/Services/InventoryItemService.js +31 -0
- package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
- package/build/dist/Server/Services/IoTDeviceCredentialService.js +12 -5
- package/build/dist/Server/Services/IoTDeviceCredentialService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +26 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +84 -57
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/SmsService.js +2 -22
- package/build/dist/Server/Services/SmsService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +47 -9
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +237 -32
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js +109 -0
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +925 -99
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +55 -51
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js +42 -25
- package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +110 -6
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Email.js +55 -6
- package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
- package/build/dist/Server/Utils/AffectedResources/LinkedAffectedResources.js +405 -0
- package/build/dist/Server/Utils/AffectedResources/LinkedAffectedResources.js.map +1 -0
- package/build/dist/Server/Utils/Alert/AlertStateChangeAuthorization.js +112 -0
- package/build/dist/Server/Utils/Alert/AlertStateChangeAuthorization.js.map +1 -0
- package/build/dist/Server/Utils/CustomField/CustomFieldRename.js +211 -0
- package/build/dist/Server/Utils/CustomField/CustomFieldRename.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/EgressGuard.js +41 -12
- package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
- package/build/dist/Server/Utils/Inventory/InventoryOverviewAggregation.js +81 -0
- package/build/dist/Server/Utils/Inventory/InventoryOverviewAggregation.js.map +1 -0
- package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js +101 -0
- package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +9 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Response.js +30 -0
- package/build/dist/Server/Utils/Response.js.map +1 -1
- package/build/dist/Server/Utils/SSRFProtection.js +48 -6
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
- package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js +631 -0
- package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js +403 -0
- package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js +451 -0
- package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js +13 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js +91 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js +101 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js +47 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js +273 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js +73 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js +722 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js +102 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js +527 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js +278 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js +137 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js +103 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js +114 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js.map +1 -0
- package/build/dist/Server/Utils/StatusPageResource.js +39 -7
- package/build/dist/Server/Utils/StatusPageResource.js.map +1 -1
- package/build/dist/Server/Utils/Topology/TopologyConcurrencyLimiter.js +178 -0
- package/build/dist/Server/Utils/Topology/TopologyConcurrencyLimiter.js.map +1 -0
- package/build/dist/Server/Utils/Topology/TopologyQueries.js +884 -0
- package/build/dist/Server/Utils/Topology/TopologyQueries.js.map +1 -0
- package/build/dist/Server/Utils/Topology/TopologyRequest.js +275 -0
- package/build/dist/Server/Utils/Topology/TopologyRequest.js.map +1 -0
- package/build/dist/Server/Utils/Topology/TopologyResponseCache.js +146 -0
- package/build/dist/Server/Utils/Topology/TopologyResponseCache.js.map +1 -0
- package/build/dist/Server/Utils/Topology/TopologySql.js +688 -0
- package/build/dist/Server/Utils/Topology/TopologySql.js.map +1 -0
- package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
- package/build/dist/Server/Utils/VM/VMRunner.js +1 -0
- package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
- package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js +3 -0
- package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js.map +1 -1
- package/build/dist/Types/CustomField/CustomFieldOrder.js +36 -0
- package/build/dist/Types/CustomField/CustomFieldOrder.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldSavedViews.js +135 -0
- package/build/dist/Types/CustomField/CustomFieldSavedViews.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js +27 -0
- package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldType.js +12 -0
- package/build/dist/Types/CustomField/CustomFieldType.js.map +1 -1
- package/build/dist/Types/CustomField/CustomFieldValueFormat.js +126 -0
- package/build/dist/Types/CustomField/CustomFieldValueFormat.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldValueValidator.js +280 -0
- package/build/dist/Types/CustomField/CustomFieldValueValidator.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldVariableKey.js +150 -0
- package/build/dist/Types/CustomField/CustomFieldVariableKey.js.map +1 -0
- package/build/dist/Types/Incident/IncidentAlertLink.js +17 -0
- package/build/dist/Types/Incident/IncidentAlertLink.js.map +1 -1
- package/build/dist/Types/SafeHtml.js +84 -0
- package/build/dist/Types/SafeHtml.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js +117 -0
- package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentCreatedResend.js +86 -0
- package/build/dist/Types/StatusPage/IncidentCreatedResend.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js +246 -0
- package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js +183 -0
- package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js.map +1 -0
- package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js +277 -0
- package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js +27 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js +190 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationResend.js +102 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationResend.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js +97 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js +175 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js +144 -4
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js.map +1 -1
- package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js +29 -9
- package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js.map +1 -1
- package/build/dist/Types/Telemetry/InventoryLiveness.js +23 -0
- package/build/dist/Types/Telemetry/InventoryLiveness.js.map +1 -1
- package/build/dist/Types/Topology/TopologyApi.js +55 -0
- package/build/dist/Types/Topology/TopologyApi.js.map +1 -0
- package/build/dist/Types/Topology/TopologyTypeRules.js +165 -0
- package/build/dist/Types/Topology/TopologyTypeRules.js.map +1 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js +169 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js.map +1 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js +212 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js.map +1 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +64 -49
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
- package/build/dist/UI/Components/Forms/ModelForm.js +39 -5
- package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/Validation.js +1 -1
- package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
- package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/AnalyticsModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +2 -2
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +13 -1
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ModelTable.js +1 -1
- package/build/dist/UI/Components/ModelTable/ModelTable.js.map +1 -1
- package/build/dist/UI/Utils/SensitiveUrlToken.js +10 -5
- package/build/dist/UI/Utils/SensitiveUrlToken.js.map +1 -1
- package/build/dist/Utils/API.js +177 -1
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/Incident/IncidentFromAlerts.js +38 -0
- package/build/dist/Utils/Incident/IncidentFromAlerts.js.map +1 -1
- package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js +155 -0
- package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js.map +1 -0
- package/build/dist/Utils/Markdown/MarkdownEscape.js +48 -0
- package/build/dist/Utils/Markdown/MarkdownEscape.js.map +1 -1
- package/build/dist/Utils/ProbeNetworkFailureUtil.js +460 -0
- package/build/dist/Utils/ProbeNetworkFailureUtil.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,839 @@
|
|
|
1
|
+
import Incident from "../../../Models/DatabaseModels/Incident";
|
|
2
|
+
import IncidentCustomField from "../../../Models/DatabaseModels/IncidentCustomField";
|
|
3
|
+
import IncidentSeverity from "../../../Models/DatabaseModels/IncidentSeverity";
|
|
4
|
+
import Label from "../../../Models/DatabaseModels/Label";
|
|
5
|
+
import Monitor from "../../../Models/DatabaseModels/Monitor";
|
|
6
|
+
import StatusPage from "../../../Models/DatabaseModels/StatusPage";
|
|
7
|
+
import Hostname from "../../../Types/API/Hostname";
|
|
8
|
+
import Protocol from "../../../Types/API/Protocol";
|
|
9
|
+
import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
10
|
+
import { CustomFieldDefinition } from "../../../Types/CustomField/CustomFieldDefinition";
|
|
11
|
+
import {
|
|
12
|
+
formatCustomFieldValueValidationErrors,
|
|
13
|
+
validateCustomFieldValues,
|
|
14
|
+
} from "../../../Types/CustomField/CustomFieldValueValidator";
|
|
15
|
+
import { LIMIT_PER_PROJECT } from "../../../Types/Database/LimitMax";
|
|
16
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
17
|
+
import NotFoundException from "../../../Types/Exception/NotFoundException";
|
|
18
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
19
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
20
|
+
import IncidentScopeAddedPagesNotification from "../../../Types/StatusPage/IncidentScopeAddedPagesNotification";
|
|
21
|
+
import IncidentSubscriberAudience, {
|
|
22
|
+
IncidentSubscriberAudienceCounts,
|
|
23
|
+
IncidentSubscriberAudienceResult,
|
|
24
|
+
IncidentSubscriberAudienceStatusPage,
|
|
25
|
+
} from "../../../Types/StatusPage/IncidentSubscriberAudience";
|
|
26
|
+
import SubscriberNotificationPreview, {
|
|
27
|
+
SubscriberEmailTemplateChoice,
|
|
28
|
+
SubscriberNotificationPreviewEvent,
|
|
29
|
+
SubscriberNotificationPreviewIncidentDraft,
|
|
30
|
+
SubscriberNotificationPreviewNothingSentReason,
|
|
31
|
+
SubscriberNotificationPreviewRequest,
|
|
32
|
+
SubscriberNotificationSendTestRequest,
|
|
33
|
+
} from "../../../Types/StatusPage/SubscriberNotificationPreview";
|
|
34
|
+
import DatabaseConfig from "../../DatabaseConfig";
|
|
35
|
+
import CustomFieldMappingService from "../../Services/CustomFieldMappingService";
|
|
36
|
+
import IncidentCustomFieldService from "../../Services/IncidentCustomFieldService";
|
|
37
|
+
import IncidentService from "../../Services/IncidentService";
|
|
38
|
+
import IncidentSeverityService from "../../Services/IncidentSeverityService";
|
|
39
|
+
import LabelService from "../../Services/LabelService";
|
|
40
|
+
import StatusPageService from "../../Services/StatusPageService";
|
|
41
|
+
import QueryHelper from "../../Types/Database/QueryHelper";
|
|
42
|
+
import {
|
|
43
|
+
IncidentStatusPageTemplateVariables,
|
|
44
|
+
IncidentTemplateVariables,
|
|
45
|
+
} from "./IncidentTemplateVariableBuilder";
|
|
46
|
+
import IncidentSubscriberAudienceBuilder, {
|
|
47
|
+
IncidentSubscriberAudienceRequest,
|
|
48
|
+
IncidentSubscriberAudienceWithStatusPages,
|
|
49
|
+
} from "./IncidentSubscriberAudienceBuilder";
|
|
50
|
+
import SubscriberIncidentEmailBuilder, {
|
|
51
|
+
SubscriberIncidentEmail,
|
|
52
|
+
SubscriberIncidentEmailEvent,
|
|
53
|
+
SubscriberIncidentStatusPageEmail,
|
|
54
|
+
} from "./SubscriberIncidentEmailBuilder";
|
|
55
|
+
|
|
56
|
+
/*
|
|
57
|
+
* What "Preview notification" and "Send test to me" show and send: for each
|
|
58
|
+
* status page an incident's email would go to, the email its subscribers
|
|
59
|
+
* would get (see Common/Types/StatusPage/SubscriberNotificationPreview).
|
|
60
|
+
*
|
|
61
|
+
* The emails come from SubscriberIncidentEmailBuilder, the one code path the
|
|
62
|
+
* 'incident created' and public note jobs send through, fed the way those
|
|
63
|
+
* jobs feed it; the router renders them with the mailer's own
|
|
64
|
+
* MailService.render. So what is previewed is what is sent.
|
|
65
|
+
*
|
|
66
|
+
* What the caller learns is bounded exactly as the "Will notify" audience
|
|
67
|
+
* bounds it (IncidentSubscriberAudienceBuilder, which this builds on):
|
|
68
|
+
*
|
|
69
|
+
* - the caller must hold one of the roles that may see the audience;
|
|
70
|
+
* - an incident that exists is read with the caller's own permissions,
|
|
71
|
+
* inside the caller's project, so another project's incident and a
|
|
72
|
+
* private one the caller may not read are "not found";
|
|
73
|
+
* - the monitors, status pages, labels and severity named for an incident
|
|
74
|
+
* being declared must all be the caller's project's;
|
|
75
|
+
* - only the pages the caller may read are previewed or named - in the
|
|
76
|
+
* preview, {{affectedStatusPages}} names only those pages too - and the
|
|
77
|
+
* others are one number (audience.hiddenStatusPageCount). That bound is
|
|
78
|
+
* for looking ahead: once a notification has gone out, its own record
|
|
79
|
+
* names every page it reached to whoever can read the incident (see
|
|
80
|
+
* IncidentSubscriberAudienceBuilder);
|
|
81
|
+
* - subscribers are counted, never read: no address leaves the server.
|
|
82
|
+
*
|
|
83
|
+
* Building a preview has no side effects: nothing is sent, and nothing about
|
|
84
|
+
* the incident - its custom fields' images among them - is changed.
|
|
85
|
+
*/
|
|
86
|
+
|
|
87
|
+
// One status page's email, before it is rendered.
|
|
88
|
+
export interface SubscriberNotificationPreviewPage {
|
|
89
|
+
/*
|
|
90
|
+
* The page as the jobs load it, mail settings included: for sending a
|
|
91
|
+
* test through the page's own SMTP server. Never serialized.
|
|
92
|
+
*/
|
|
93
|
+
statusPage: StatusPage;
|
|
94
|
+
statusPageId: string;
|
|
95
|
+
// The name the caller knows the page by.
|
|
96
|
+
name: string;
|
|
97
|
+
subscriberCounts: IncidentSubscriberAudienceCounts;
|
|
98
|
+
templateChoice: SubscriberEmailTemplateChoice;
|
|
99
|
+
email: SubscriberIncidentEmail;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface SubscriberNotificationPreviewBuild {
|
|
103
|
+
event: SubscriberNotificationPreviewEvent;
|
|
104
|
+
nothingSentReason: SubscriberNotificationPreviewNothingSentReason | null;
|
|
105
|
+
audience: IncidentSubscriberAudienceResult;
|
|
106
|
+
statusPages: Array<SubscriberNotificationPreviewPage>;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// An incident and what its email needs, once the request is resolved.
|
|
110
|
+
interface PreviewIncident {
|
|
111
|
+
event: SubscriberIncidentEmailEvent;
|
|
112
|
+
incident: Incident;
|
|
113
|
+
note?:
|
|
114
|
+
| {
|
|
115
|
+
text: string;
|
|
116
|
+
postedAt: Date | null;
|
|
117
|
+
}
|
|
118
|
+
| undefined;
|
|
119
|
+
audienceRequest: IncidentSubscriberAudienceRequest;
|
|
120
|
+
// Why nothing will be sent, whatever the audience, for a draft.
|
|
121
|
+
draftNothingSentReason: SubscriberNotificationPreviewNothingSentReason | null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export default class SubscriberNotificationPreviewBuilder {
|
|
125
|
+
/**
|
|
126
|
+
* The emails for a request. With onlyStatusPageId, only that page's email
|
|
127
|
+
* is built (for "Send test to me"); the audience still covers every page.
|
|
128
|
+
*/
|
|
129
|
+
public static async build(data: {
|
|
130
|
+
projectId: ObjectID;
|
|
131
|
+
props: DatabaseCommonInteractionProps;
|
|
132
|
+
request: SubscriberNotificationPreviewRequest;
|
|
133
|
+
onlyStatusPageId?: string | undefined;
|
|
134
|
+
}): Promise<SubscriberNotificationPreviewBuild> {
|
|
135
|
+
IncidentSubscriberAudienceBuilder.assertCallerMaySeeAudience(data.props);
|
|
136
|
+
|
|
137
|
+
const previewIncident: PreviewIncident =
|
|
138
|
+
data.request.event === SubscriberNotificationPreviewEvent.IncidentCreated
|
|
139
|
+
? await this.resolveDraft({
|
|
140
|
+
projectId: data.projectId,
|
|
141
|
+
props: data.props,
|
|
142
|
+
draft: data.request.incident,
|
|
143
|
+
})
|
|
144
|
+
: await this.resolvePublicNote({
|
|
145
|
+
projectId: data.projectId,
|
|
146
|
+
props: data.props,
|
|
147
|
+
incidentId: data.request.incidentId,
|
|
148
|
+
note: data.request.note,
|
|
149
|
+
postedAt: data.request.postedAt,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
/*
|
|
153
|
+
* Who is told, worked out as the "Will notify" summary works it out -
|
|
154
|
+
* and, for an incident that exists, the read that proves the caller may
|
|
155
|
+
* see it. Nothing about the incident is read before this.
|
|
156
|
+
*/
|
|
157
|
+
const withStatusPages: IncidentSubscriberAudienceWithStatusPages =
|
|
158
|
+
await IncidentSubscriberAudienceBuilder.buildWithStatusPages(
|
|
159
|
+
previewIncident.audienceRequest,
|
|
160
|
+
);
|
|
161
|
+
const audience: IncidentSubscriberAudienceResult = withStatusPages.audience;
|
|
162
|
+
|
|
163
|
+
const nothingSentReason: SubscriberNotificationPreviewNothingSentReason | null =
|
|
164
|
+
this.getNothingSentReason({
|
|
165
|
+
audience: audience,
|
|
166
|
+
draftNothingSentReason: previewIncident.draftNothingSentReason,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const result: SubscriberNotificationPreviewBuild = {
|
|
170
|
+
event: data.request.event,
|
|
171
|
+
nothingSentReason: nothingSentReason,
|
|
172
|
+
audience: audience,
|
|
173
|
+
statusPages: [],
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
if (nothingSentReason) {
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// The incident's content, now that the caller is known to read it.
|
|
181
|
+
const incident: Incident =
|
|
182
|
+
"incidentId" in previewIncident.audienceRequest
|
|
183
|
+
? await this.readIncidentContent({
|
|
184
|
+
projectId: data.projectId,
|
|
185
|
+
incidentId: previewIncident.audienceRequest.incidentId,
|
|
186
|
+
})
|
|
187
|
+
: previewIncident.incident;
|
|
188
|
+
|
|
189
|
+
// The pages that will be told that the caller can see, in name order.
|
|
190
|
+
const readableStatusPages: Array<StatusPage> =
|
|
191
|
+
withStatusPages.resolved.statusPages.filter(
|
|
192
|
+
(statusPage: StatusPage): boolean => {
|
|
193
|
+
return Boolean(this.findAudiencePage(audience, statusPage));
|
|
194
|
+
},
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
const statusPagesToBuild: Array<StatusPage> = data.onlyStatusPageId
|
|
198
|
+
? readableStatusPages.filter((statusPage: StatusPage): boolean => {
|
|
199
|
+
return (
|
|
200
|
+
this.getId(statusPage) === data.onlyStatusPageId!.toLowerCase()
|
|
201
|
+
);
|
|
202
|
+
})
|
|
203
|
+
: readableStatusPages;
|
|
204
|
+
|
|
205
|
+
if (statusPagesToBuild.length === 0) {
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const incidentTemplateVariables: IncidentTemplateVariables =
|
|
210
|
+
await SubscriberIncidentEmailBuilder.buildTemplateVariables({
|
|
211
|
+
event: previewIncident.event,
|
|
212
|
+
incident: incident,
|
|
213
|
+
statusPages: readableStatusPages,
|
|
214
|
+
note: previewIncident.note,
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const host: Hostname = await DatabaseConfig.getHost();
|
|
218
|
+
const httpProtocol: Protocol = await DatabaseConfig.getHttpProtocol();
|
|
219
|
+
|
|
220
|
+
for (const statusPage of statusPagesToBuild) {
|
|
221
|
+
const audiencePage: IncidentSubscriberAudienceStatusPage =
|
|
222
|
+
this.findAudiencePage(audience, statusPage)!;
|
|
223
|
+
|
|
224
|
+
const statusPageUrl: string = await StatusPageService.getStatusPageURL(
|
|
225
|
+
statusPage.id!,
|
|
226
|
+
);
|
|
227
|
+
const detailsUrl: string = SubscriberIncidentEmailBuilder.getDetailsUrl({
|
|
228
|
+
statusPageUrl: statusPageUrl,
|
|
229
|
+
incidentId: incident.id,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const pageTemplateVariables: IncidentStatusPageTemplateVariables =
|
|
233
|
+
incidentTemplateVariables.forStatusPage({
|
|
234
|
+
statusPage: statusPage,
|
|
235
|
+
statusPageUrl: statusPageUrl,
|
|
236
|
+
detailsUrl: detailsUrl,
|
|
237
|
+
resources:
|
|
238
|
+
withStatusPages.resolved.statusPageToResources[statusPage._id!] ||
|
|
239
|
+
[],
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
const pageEmail: SubscriberIncidentStatusPageEmail =
|
|
243
|
+
await SubscriberIncidentEmailBuilder.forStatusPage({
|
|
244
|
+
event: previewIncident.event,
|
|
245
|
+
incident: incident,
|
|
246
|
+
incidentTemplateVariables: incidentTemplateVariables,
|
|
247
|
+
statusPage: statusPage,
|
|
248
|
+
statusPageUrl: statusPageUrl,
|
|
249
|
+
detailsUrl: detailsUrl,
|
|
250
|
+
pageTemplateVariables: pageTemplateVariables,
|
|
251
|
+
host: host,
|
|
252
|
+
httpProtocol: httpProtocol,
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
result.statusPages.push({
|
|
256
|
+
statusPage: statusPage,
|
|
257
|
+
statusPageId: audiencePage.statusPageId,
|
|
258
|
+
name: audiencePage.name,
|
|
259
|
+
subscriberCounts: { ...audiencePage.subscriberCounts },
|
|
260
|
+
templateChoice: pageEmail.templateChoice,
|
|
261
|
+
/*
|
|
262
|
+
* Every subscriber's link carries their own token; a preview has no
|
|
263
|
+
* subscriber, so it carries a link that names none.
|
|
264
|
+
*/
|
|
265
|
+
email: pageEmail.forSubscriber({
|
|
266
|
+
unsubscribeUrl:
|
|
267
|
+
SubscriberNotificationPreview.getPreviewUnsubscribeUrl(
|
|
268
|
+
statusPageUrl,
|
|
269
|
+
),
|
|
270
|
+
}),
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return result;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/*
|
|
278
|
+
* Why nothing will be sent, in the order the jobs find it out: no monitor,
|
|
279
|
+
* an incident hidden from status pages, a draft that will be private or
|
|
280
|
+
* does not notify, and no page that will show it.
|
|
281
|
+
*/
|
|
282
|
+
public static getNothingSentReason(data: {
|
|
283
|
+
audience: IncidentSubscriberAudienceResult;
|
|
284
|
+
draftNothingSentReason: SubscriberNotificationPreviewNothingSentReason | null;
|
|
285
|
+
}): SubscriberNotificationPreviewNothingSentReason | null {
|
|
286
|
+
if (!data.audience.hasMonitors) {
|
|
287
|
+
return SubscriberNotificationPreviewNothingSentReason.NoMonitors;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (data.audience.isHiddenFromStatusPages) {
|
|
291
|
+
return SubscriberNotificationPreviewNothingSentReason.HiddenFromStatusPages;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (data.draftNothingSentReason) {
|
|
295
|
+
return data.draftNothingSentReason;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (
|
|
299
|
+
data.audience.statusPages.length === 0 &&
|
|
300
|
+
data.audience.hiddenStatusPageCount === 0
|
|
301
|
+
) {
|
|
302
|
+
return SubscriberNotificationPreviewNothingSentReason.NoStatusPages;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/*
|
|
309
|
+
* The request body of POST /preview. Anything that is not a request is
|
|
310
|
+
* refused with what is wrong, rather than previewed as something else.
|
|
311
|
+
*/
|
|
312
|
+
public static parseRequest(
|
|
313
|
+
body: unknown,
|
|
314
|
+
): SubscriberNotificationPreviewRequest {
|
|
315
|
+
const json: JSONObject = this.asObject(body, "The request");
|
|
316
|
+
|
|
317
|
+
if (json["event"] === SubscriberNotificationPreviewEvent.IncidentCreated) {
|
|
318
|
+
const incident: JSONObject = this.asObject(json["incident"], "incident");
|
|
319
|
+
|
|
320
|
+
const draft: SubscriberNotificationPreviewIncidentDraft = {
|
|
321
|
+
title: this.parseText(
|
|
322
|
+
incident["title"],
|
|
323
|
+
"incident.title",
|
|
324
|
+
SubscriberNotificationPreview.maxTitleLength,
|
|
325
|
+
),
|
|
326
|
+
description: this.parseText(
|
|
327
|
+
incident["description"],
|
|
328
|
+
"incident.description",
|
|
329
|
+
SubscriberNotificationPreview.maxTextLength,
|
|
330
|
+
),
|
|
331
|
+
incidentSeverityId:
|
|
332
|
+
incident["incidentSeverityId"] === undefined ||
|
|
333
|
+
incident["incidentSeverityId"] === null ||
|
|
334
|
+
incident["incidentSeverityId"] === ""
|
|
335
|
+
? null
|
|
336
|
+
: this.parseId(
|
|
337
|
+
incident["incidentSeverityId"],
|
|
338
|
+
"incident.incidentSeverityId",
|
|
339
|
+
),
|
|
340
|
+
monitorIds: this.parseIds(
|
|
341
|
+
incident["monitorIds"],
|
|
342
|
+
"incident.monitorIds",
|
|
343
|
+
),
|
|
344
|
+
statusPageIds: this.parseIds(
|
|
345
|
+
incident["statusPageIds"],
|
|
346
|
+
"incident.statusPageIds",
|
|
347
|
+
),
|
|
348
|
+
labelIds: this.parseIds(incident["labelIds"], "incident.labelIds"),
|
|
349
|
+
customFields: this.parseCustomFields(
|
|
350
|
+
incident["customFields"],
|
|
351
|
+
"incident.customFields",
|
|
352
|
+
),
|
|
353
|
+
isPrivate: incident["isPrivate"] === true,
|
|
354
|
+
// On unless it is switched off, as it is on the form.
|
|
355
|
+
shouldStatusPageSubscribersBeNotifiedOnIncidentCreated:
|
|
356
|
+
incident["shouldStatusPageSubscribersBeNotifiedOnIncidentCreated"] !==
|
|
357
|
+
false,
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
return {
|
|
361
|
+
event: SubscriberNotificationPreviewEvent.IncidentCreated,
|
|
362
|
+
incident: draft,
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (
|
|
367
|
+
json["event"] ===
|
|
368
|
+
SubscriberNotificationPreviewEvent.IncidentPublicNoteCreated
|
|
369
|
+
) {
|
|
370
|
+
return {
|
|
371
|
+
event: SubscriberNotificationPreviewEvent.IncidentPublicNoteCreated,
|
|
372
|
+
incidentId: this.parseId(json["incidentId"], "incidentId"),
|
|
373
|
+
note: this.parseText(
|
|
374
|
+
json["note"],
|
|
375
|
+
"note",
|
|
376
|
+
SubscriberNotificationPreview.maxTextLength,
|
|
377
|
+
),
|
|
378
|
+
postedAt: this.parseDate(json["postedAt"], "postedAt"),
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
throw new BadDataException(
|
|
383
|
+
`event must be one of: ${Object.values(
|
|
384
|
+
SubscriberNotificationPreviewEvent,
|
|
385
|
+
).join(", ")}.`,
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/*
|
|
390
|
+
* The request body of POST /send-test: a preview request and the status
|
|
391
|
+
* page whose email to send. It takes no address: the test goes to the
|
|
392
|
+
* caller's own, and a toEmail in the body is not read.
|
|
393
|
+
*/
|
|
394
|
+
public static parseSendTestRequest(
|
|
395
|
+
body: unknown,
|
|
396
|
+
): SubscriberNotificationSendTestRequest {
|
|
397
|
+
const request: SubscriberNotificationPreviewRequest =
|
|
398
|
+
this.parseRequest(body);
|
|
399
|
+
const json: JSONObject = this.asObject(body, "The request");
|
|
400
|
+
|
|
401
|
+
return {
|
|
402
|
+
...request,
|
|
403
|
+
statusPageId: this.parseId(json["statusPageId"], "statusPageId"),
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// An incident being declared, from the form's values.
|
|
408
|
+
private static async resolveDraft(data: {
|
|
409
|
+
projectId: ObjectID;
|
|
410
|
+
props: DatabaseCommonInteractionProps;
|
|
411
|
+
draft: SubscriberNotificationPreviewIncidentDraft;
|
|
412
|
+
}): Promise<PreviewIncident> {
|
|
413
|
+
const draft: SubscriberNotificationPreviewIncidentDraft = data.draft;
|
|
414
|
+
|
|
415
|
+
const [severity, labels, customFieldDefinitions]: [
|
|
416
|
+
IncidentSeverity | null,
|
|
417
|
+
Array<Label>,
|
|
418
|
+
Array<CustomFieldDefinition>,
|
|
419
|
+
] = await Promise.all([
|
|
420
|
+
this.readSeverity({
|
|
421
|
+
projectId: data.projectId,
|
|
422
|
+
incidentSeverityId: draft.incidentSeverityId,
|
|
423
|
+
}),
|
|
424
|
+
this.readLabels({
|
|
425
|
+
projectId: data.projectId,
|
|
426
|
+
labelIds: draft.labelIds,
|
|
427
|
+
}),
|
|
428
|
+
this.readCustomFieldDefinitions(data.projectId),
|
|
429
|
+
]);
|
|
430
|
+
|
|
431
|
+
/*
|
|
432
|
+
* The values must fit their fields, as they must when the incident is
|
|
433
|
+
* declared: a preview of an incident that cannot be declared says why.
|
|
434
|
+
*/
|
|
435
|
+
const customFieldErrors: ReturnType<typeof validateCustomFieldValues> =
|
|
436
|
+
validateCustomFieldValues({
|
|
437
|
+
definitions: customFieldDefinitions,
|
|
438
|
+
customFields: draft.customFields,
|
|
439
|
+
storedCustomFields: {},
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
if (customFieldErrors.length > 0) {
|
|
443
|
+
throw new BadDataException(
|
|
444
|
+
formatCustomFieldValueValidationErrors(customFieldErrors),
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const incident: Incident = new Incident();
|
|
449
|
+
incident.projectId = data.projectId;
|
|
450
|
+
incident.title = draft.title;
|
|
451
|
+
incident.description = draft.description;
|
|
452
|
+
if (severity) {
|
|
453
|
+
incident.incidentSeverity = severity;
|
|
454
|
+
}
|
|
455
|
+
incident.labels = labels;
|
|
456
|
+
incident.customFields = { ...draft.customFields };
|
|
457
|
+
incident.monitors = draft.monitorIds.map((id: string): Monitor => {
|
|
458
|
+
const monitor: Monitor = new Monitor();
|
|
459
|
+
monitor._id = id;
|
|
460
|
+
return monitor;
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
/*
|
|
464
|
+
* The values a custom field mapping copies from the monitors, as
|
|
465
|
+
* declaring the incident adds them (IncidentService.onBeforeCreate).
|
|
466
|
+
*/
|
|
467
|
+
await CustomFieldMappingService.applyMappingsToCreate({
|
|
468
|
+
definitionModelType: IncidentCustomField,
|
|
469
|
+
createBy: {
|
|
470
|
+
data: incident,
|
|
471
|
+
props: {
|
|
472
|
+
tenantId: data.projectId,
|
|
473
|
+
isRoot: true,
|
|
474
|
+
},
|
|
475
|
+
},
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
let draftNothingSentReason: SubscriberNotificationPreviewNothingSentReason | null =
|
|
479
|
+
null;
|
|
480
|
+
|
|
481
|
+
if (draft.isPrivate) {
|
|
482
|
+
draftNothingSentReason =
|
|
483
|
+
SubscriberNotificationPreviewNothingSentReason.PrivateIncident;
|
|
484
|
+
} else if (!draft.shouldStatusPageSubscribersBeNotifiedOnIncidentCreated) {
|
|
485
|
+
draftNothingSentReason =
|
|
486
|
+
SubscriberNotificationPreviewNothingSentReason.NotifyOff;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return {
|
|
490
|
+
event: SubscriberIncidentEmailEvent.IncidentCreated,
|
|
491
|
+
incident: incident,
|
|
492
|
+
audienceRequest: {
|
|
493
|
+
projectId: data.projectId,
|
|
494
|
+
props: data.props,
|
|
495
|
+
monitorIds: draft.monitorIds.map((id: string): ObjectID => {
|
|
496
|
+
return new ObjectID(id);
|
|
497
|
+
}),
|
|
498
|
+
statusPageIds: draft.statusPageIds.map((id: string): ObjectID => {
|
|
499
|
+
return new ObjectID(id);
|
|
500
|
+
}),
|
|
501
|
+
},
|
|
502
|
+
draftNothingSentReason: draftNothingSentReason,
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/*
|
|
507
|
+
* A public note being written on an incident that exists. The incident is
|
|
508
|
+
* not read here: the audience reads it first, with the caller's own
|
|
509
|
+
* permissions (see build).
|
|
510
|
+
*/
|
|
511
|
+
private static async resolvePublicNote(data: {
|
|
512
|
+
projectId: ObjectID;
|
|
513
|
+
props: DatabaseCommonInteractionProps;
|
|
514
|
+
incidentId: string;
|
|
515
|
+
note: string;
|
|
516
|
+
postedAt: Date | null;
|
|
517
|
+
}): Promise<PreviewIncident> {
|
|
518
|
+
return {
|
|
519
|
+
event: SubscriberIncidentEmailEvent.IncidentPublicNoteCreated,
|
|
520
|
+
incident: new Incident(),
|
|
521
|
+
note: {
|
|
522
|
+
text: data.note,
|
|
523
|
+
postedAt: data.postedAt,
|
|
524
|
+
},
|
|
525
|
+
audienceRequest: {
|
|
526
|
+
projectId: data.projectId,
|
|
527
|
+
props: data.props,
|
|
528
|
+
incidentId: new ObjectID(data.incidentId),
|
|
529
|
+
},
|
|
530
|
+
draftNothingSentReason: null,
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/*
|
|
535
|
+
* What the public note job reads of the incident. Read as root: the caller
|
|
536
|
+
* has been shown to read the incident (the audience read it with their
|
|
537
|
+
* permissions), and these are its own title, severity, state, labels and
|
|
538
|
+
* custom field values.
|
|
539
|
+
*/
|
|
540
|
+
private static async readIncidentContent(data: {
|
|
541
|
+
projectId: ObjectID;
|
|
542
|
+
incidentId: ObjectID;
|
|
543
|
+
}): Promise<Incident> {
|
|
544
|
+
const incident: Incident | null = await IncidentService.findOneBy({
|
|
545
|
+
query: {
|
|
546
|
+
_id: data.incidentId,
|
|
547
|
+
projectId: data.projectId,
|
|
548
|
+
},
|
|
549
|
+
select: {
|
|
550
|
+
_id: true,
|
|
551
|
+
title: true,
|
|
552
|
+
description: true,
|
|
553
|
+
projectId: true,
|
|
554
|
+
monitors: {
|
|
555
|
+
_id: true,
|
|
556
|
+
},
|
|
557
|
+
incidentSeverity: {
|
|
558
|
+
name: true,
|
|
559
|
+
},
|
|
560
|
+
currentIncidentState: {
|
|
561
|
+
name: true,
|
|
562
|
+
},
|
|
563
|
+
labels: {
|
|
564
|
+
name: true,
|
|
565
|
+
},
|
|
566
|
+
customFields: true,
|
|
567
|
+
},
|
|
568
|
+
props: {
|
|
569
|
+
isRoot: true,
|
|
570
|
+
},
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
// Deleted since the caller's read.
|
|
574
|
+
if (!incident) {
|
|
575
|
+
throw new NotFoundException("Incident not found");
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
return incident;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
private static async readSeverity(data: {
|
|
582
|
+
projectId: ObjectID;
|
|
583
|
+
incidentSeverityId: string | null;
|
|
584
|
+
}): Promise<IncidentSeverity | null> {
|
|
585
|
+
if (!data.incidentSeverityId) {
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const severity: IncidentSeverity | null =
|
|
590
|
+
await IncidentSeverityService.findOneBy({
|
|
591
|
+
query: {
|
|
592
|
+
_id: data.incidentSeverityId,
|
|
593
|
+
projectId: data.projectId,
|
|
594
|
+
},
|
|
595
|
+
select: {
|
|
596
|
+
_id: true,
|
|
597
|
+
name: true,
|
|
598
|
+
},
|
|
599
|
+
props: {
|
|
600
|
+
isRoot: true,
|
|
601
|
+
},
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
if (!severity) {
|
|
605
|
+
throw new BadDataException(
|
|
606
|
+
"This incident severity was not found in this project.",
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
return severity;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
private static async readLabels(data: {
|
|
614
|
+
projectId: ObjectID;
|
|
615
|
+
labelIds: Array<string>;
|
|
616
|
+
}): Promise<Array<Label>> {
|
|
617
|
+
if (data.labelIds.length === 0) {
|
|
618
|
+
return [];
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const labels: Array<Label> = await LabelService.findBy({
|
|
622
|
+
query: {
|
|
623
|
+
_id: QueryHelper.any(data.labelIds),
|
|
624
|
+
projectId: data.projectId,
|
|
625
|
+
},
|
|
626
|
+
select: {
|
|
627
|
+
_id: true,
|
|
628
|
+
name: true,
|
|
629
|
+
},
|
|
630
|
+
limit: data.labelIds.length,
|
|
631
|
+
skip: 0,
|
|
632
|
+
props: {
|
|
633
|
+
isRoot: true,
|
|
634
|
+
},
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
const found: Array<string> = labels.map((label: Label): string => {
|
|
638
|
+
return this.getId(label);
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
const isEveryLabelFound: boolean = data.labelIds.every(
|
|
642
|
+
(id: string): boolean => {
|
|
643
|
+
return found.includes(id);
|
|
644
|
+
},
|
|
645
|
+
);
|
|
646
|
+
|
|
647
|
+
if (!isEveryLabelFound) {
|
|
648
|
+
throw new BadDataException(
|
|
649
|
+
"One or more of these labels were not found in this project.",
|
|
650
|
+
);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
return labels;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// The project's incident custom fields, as the values are checked against.
|
|
657
|
+
private static async readCustomFieldDefinitions(
|
|
658
|
+
projectId: ObjectID,
|
|
659
|
+
): Promise<Array<CustomFieldDefinition>> {
|
|
660
|
+
const fields: Array<IncidentCustomField> =
|
|
661
|
+
await IncidentCustomFieldService.findBy({
|
|
662
|
+
query: {
|
|
663
|
+
projectId: projectId,
|
|
664
|
+
},
|
|
665
|
+
select: {
|
|
666
|
+
name: true,
|
|
667
|
+
customFieldType: true,
|
|
668
|
+
dropdownOptions: true,
|
|
669
|
+
},
|
|
670
|
+
limit: LIMIT_PER_PROJECT,
|
|
671
|
+
skip: 0,
|
|
672
|
+
props: {
|
|
673
|
+
isRoot: true,
|
|
674
|
+
},
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
const definitions: Array<CustomFieldDefinition> = [];
|
|
678
|
+
|
|
679
|
+
for (const field of fields) {
|
|
680
|
+
if (typeof field.name === "string") {
|
|
681
|
+
definitions.push({
|
|
682
|
+
name: field.name,
|
|
683
|
+
customFieldType: field.customFieldType,
|
|
684
|
+
dropdownOptions: field.dropdownOptions,
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
return definitions;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
private static findAudiencePage(
|
|
693
|
+
audience: IncidentSubscriberAudienceResult,
|
|
694
|
+
statusPage: StatusPage,
|
|
695
|
+
): IncidentSubscriberAudienceStatusPage | undefined {
|
|
696
|
+
const id: string = this.getId(statusPage);
|
|
697
|
+
|
|
698
|
+
return audience.statusPages.find(
|
|
699
|
+
(page: IncidentSubscriberAudienceStatusPage): boolean => {
|
|
700
|
+
return page.statusPageId.toLowerCase() === id;
|
|
701
|
+
},
|
|
702
|
+
);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
private static getId(model: StatusPage | Label): string {
|
|
706
|
+
return (model._id || model.id?.toString() || "").toString().toLowerCase();
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
private static asObject(value: unknown, name: string): JSONObject {
|
|
710
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
711
|
+
throw new BadDataException(`${name} must be an object.`);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
return value as JSONObject;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/*
|
|
718
|
+
* An incident's custom field values, bounded (see
|
|
719
|
+
* SubscriberNotificationPreview.maxCustomFieldCount): every value is
|
|
720
|
+
* rendered and goes into every page's email. Whether each value fits its
|
|
721
|
+
* field is checked with the field definitions, as declaring checks it.
|
|
722
|
+
*/
|
|
723
|
+
private static parseCustomFields(value: unknown, name: string): JSONObject {
|
|
724
|
+
if (value === undefined || value === null) {
|
|
725
|
+
return {};
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
const customFields: JSONObject = this.asObject(value, name);
|
|
729
|
+
|
|
730
|
+
if (
|
|
731
|
+
Object.keys(customFields).length >
|
|
732
|
+
SubscriberNotificationPreview.maxCustomFieldCount
|
|
733
|
+
) {
|
|
734
|
+
throw new BadDataException(
|
|
735
|
+
`${name} can have at most ${SubscriberNotificationPreview.maxCustomFieldCount} values.`,
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
let length: number = 0;
|
|
740
|
+
|
|
741
|
+
try {
|
|
742
|
+
length = JSON.stringify(customFields).length;
|
|
743
|
+
} catch {
|
|
744
|
+
throw new BadDataException(`${name} must be plain values.`);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
if (length > SubscriberNotificationPreview.maxCustomFieldsLength) {
|
|
748
|
+
throw new BadDataException(
|
|
749
|
+
`${name} can be at most ${SubscriberNotificationPreview.maxCustomFieldsLength} characters long in all.`,
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
return customFields;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
private static parseText(
|
|
757
|
+
value: unknown,
|
|
758
|
+
name: string,
|
|
759
|
+
maxLength: number,
|
|
760
|
+
): string {
|
|
761
|
+
if (value === undefined || value === null) {
|
|
762
|
+
return "";
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
if (typeof value !== "string") {
|
|
766
|
+
throw new BadDataException(`${name} must be text.`);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
if (value.length > maxLength) {
|
|
770
|
+
throw new BadDataException(
|
|
771
|
+
`${name} can be at most ${maxLength} characters long.`,
|
|
772
|
+
);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
return value;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// An id, lower-cased as the database returns ids.
|
|
779
|
+
private static parseId(value: unknown, name: string): string {
|
|
780
|
+
let id: string = "";
|
|
781
|
+
|
|
782
|
+
if (typeof value === "string") {
|
|
783
|
+
id = value;
|
|
784
|
+
} else if (value instanceof ObjectID) {
|
|
785
|
+
id = value.toString();
|
|
786
|
+
} else if (value && typeof value === "object") {
|
|
787
|
+
id = new ObjectID(value as JSONObject).toString();
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
id = id.trim();
|
|
791
|
+
|
|
792
|
+
if (!id || !ObjectID.isValidUUID(id)) {
|
|
793
|
+
throw new BadDataException(`${name} must be a valid ID.`);
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
return id.toLowerCase();
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
private static parseIds(value: unknown, name: string): Array<string> {
|
|
800
|
+
if (value === undefined || value === null) {
|
|
801
|
+
return [];
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
if (!Array.isArray(value)) {
|
|
805
|
+
throw new BadDataException(`${name} must be a list of IDs.`);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
if (value.length > IncidentSubscriberAudience.maxIdsPerRequest) {
|
|
809
|
+
throw new BadDataException(
|
|
810
|
+
`${name} can list at most ${IncidentSubscriberAudience.maxIdsPerRequest} IDs.`,
|
|
811
|
+
);
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
return IncidentScopeAddedPagesNotification.normalizeStatusPageIds(
|
|
815
|
+
value.map((item: unknown): string => {
|
|
816
|
+
return this.parseId(item, name);
|
|
817
|
+
}),
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
private static parseDate(value: unknown, name: string): Date | null {
|
|
822
|
+
if (value === undefined || value === null || value === "") {
|
|
823
|
+
return null;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
if (typeof value !== "string" && !(value instanceof Date)) {
|
|
827
|
+
throw new BadDataException(`${name} must be a date.`);
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// An ISO date as the dashboard sends it (Date.toISOString).
|
|
831
|
+
const date: Date = value instanceof Date ? value : new Date(value);
|
|
832
|
+
|
|
833
|
+
if (Number.isNaN(date.getTime())) {
|
|
834
|
+
throw new BadDataException(`${name} must be a date.`);
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
return date;
|
|
838
|
+
}
|
|
839
|
+
}
|