@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
|
@@ -32,6 +32,9 @@ import Email from "../../../Types/Email";
|
|
|
32
32
|
import Phone from "../../../Types/Phone";
|
|
33
33
|
import ObjectID from "../../../Types/ObjectID";
|
|
34
34
|
import StatusPageSubscriberNotificationMethod from "../../../Types/StatusPage/StatusPageSubscriberNotificationMethod";
|
|
35
|
+
import StatusPageSubscriberNotificationEventType from "../../../Types/StatusPage/StatusPageSubscriberNotificationEventType";
|
|
36
|
+
import SubscriberNotificationTemplateVariables from "../../../Types/StatusPage/SubscriberNotificationTemplateVariables";
|
|
37
|
+
import SafeHtml from "../../../Types/SafeHtml";
|
|
35
38
|
import {
|
|
36
39
|
afterEach,
|
|
37
40
|
beforeEach,
|
|
@@ -69,6 +72,12 @@ const MONITOR_ID: ObjectID = new ObjectID(
|
|
|
69
72
|
"77777777-7777-4777-8777-777777777777",
|
|
70
73
|
);
|
|
71
74
|
|
|
75
|
+
// The secret in the subscriber's unsubscribe link, as its row carries it.
|
|
76
|
+
const UNSUBSCRIBE_TOKEN: string = "5e".repeat(32);
|
|
77
|
+
const STATUS_PAGE_URL: string = "https://status.example.com";
|
|
78
|
+
const UNSUBSCRIBE_URL: string = `${STATUS_PAGE_URL}/unsubscribe/${SUBSCRIBER_ID.toString()}-${UNSUBSCRIBE_TOKEN}`;
|
|
79
|
+
const MANAGE_URL: string = `${STATUS_PAGE_URL}/update-subscription/${SUBSCRIBER_ID.toString()}`;
|
|
80
|
+
|
|
72
81
|
const STARTS_AT: Date = new Date("2024-03-04T06:08:00.000Z");
|
|
73
82
|
const TITLE: string = "Quarterly database failover drill";
|
|
74
83
|
const DESCRIPTION: string = "Failing over the **primary**.";
|
|
@@ -167,6 +176,7 @@ function subscriber(id: ObjectID = SUBSCRIBER_ID): StatusPageSubscriber {
|
|
|
167
176
|
row.subscriberWebhook = URL.fromString(
|
|
168
177
|
"https://hooks.example.com/subscriber",
|
|
169
178
|
);
|
|
179
|
+
row.unsubscribeToken = UNSUBSCRIBE_TOKEN;
|
|
170
180
|
|
|
171
181
|
return row;
|
|
172
182
|
}
|
|
@@ -243,16 +253,40 @@ function sentWebhookData(): Record<string, string> {
|
|
|
243
253
|
.data;
|
|
244
254
|
}
|
|
245
255
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
256
|
+
/*
|
|
257
|
+
* The variables the service handed to the compile of this template, as it
|
|
258
|
+
* passed them. The email body goes through compileEmailBodyTemplate, where
|
|
259
|
+
* HTML values are SafeHtml; everything else through compileTemplate.
|
|
260
|
+
*/
|
|
261
|
+
function rawVariablesCompiledInto(
|
|
262
|
+
template: string,
|
|
263
|
+
): Record<string, string | SafeHtml> {
|
|
264
|
+
const calls: Array<Array<unknown>> = [
|
|
265
|
+
...mock(
|
|
266
|
+
StatusPageSubscriberNotificationTemplateServiceClass.compileTemplate,
|
|
267
|
+
).mock.calls,
|
|
268
|
+
...mock(
|
|
269
|
+
StatusPageSubscriberNotificationTemplateServiceClass.compileEmailBodyTemplate,
|
|
270
|
+
).mock.calls,
|
|
271
|
+
].filter((call: Array<unknown>): boolean => {
|
|
251
272
|
return call[0] === template;
|
|
252
273
|
});
|
|
253
274
|
|
|
254
275
|
expect(calls).toHaveLength(1);
|
|
255
|
-
return calls[0]![1] as Record<string, string>;
|
|
276
|
+
return calls[0]![1] as Record<string, string | SafeHtml>;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// The same, with every SafeHtml value as its HTML.
|
|
280
|
+
function variablesCompiledInto(template: string): Record<string, string> {
|
|
281
|
+
const variables: Record<string, string> = {};
|
|
282
|
+
|
|
283
|
+
for (const [name, value] of Object.entries(
|
|
284
|
+
rawVariablesCompiledInto(template),
|
|
285
|
+
)) {
|
|
286
|
+
variables[name] = SafeHtml.isSafeHtml(value) ? value.toHtml() : value;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return variables;
|
|
256
290
|
}
|
|
257
291
|
|
|
258
292
|
describe("scheduled maintenance subscriber notifications: template formats", () => {
|
|
@@ -297,6 +331,10 @@ describe("scheduled maintenance subscriber notifications: template formats", ()
|
|
|
297
331
|
StatusPageSubscriberNotificationTemplateServiceClass,
|
|
298
332
|
"compileTemplate",
|
|
299
333
|
);
|
|
334
|
+
jest.spyOn(
|
|
335
|
+
StatusPageSubscriberNotificationTemplateServiceClass,
|
|
336
|
+
"compileEmailBodyTemplate",
|
|
337
|
+
);
|
|
300
338
|
|
|
301
339
|
jest.spyOn(MailService, "sendMail").mockResolvedValue(accepted());
|
|
302
340
|
jest.spyOn(SmsService, "sendSms").mockResolvedValue(accepted());
|
|
@@ -368,19 +406,29 @@ describe("scheduled maintenance subscriber notifications: template formats", ()
|
|
|
368
406
|
OneUptimeDate.getDateAsUserFriendlyFormattedString(STARTS_AT),
|
|
369
407
|
scheduledEndTime: "",
|
|
370
408
|
resourcesAffected: "Primary database, Replica",
|
|
371
|
-
unsubscribeUrl: expect.any(String),
|
|
372
409
|
};
|
|
373
410
|
|
|
411
|
+
/*
|
|
412
|
+
* Every message links to the subscriber's unsubscribe page, token and
|
|
413
|
+
* all - except an SMS from this public page, which keeps the shorter
|
|
414
|
+
* manage link (see StatusPageSubscriberUnsubscribe.buildSmsLink).
|
|
415
|
+
*/
|
|
374
416
|
expect(sms).toEqual({
|
|
375
417
|
...shared,
|
|
376
418
|
scheduledMaintenanceDescription: DESCRIPTION_TEXT,
|
|
419
|
+
unsubscribeUrl: MANAGE_URL,
|
|
377
420
|
});
|
|
378
421
|
expect(slack).toEqual({
|
|
379
422
|
...shared,
|
|
380
423
|
scheduledMaintenanceDescription: DESCRIPTION,
|
|
424
|
+
unsubscribeUrl: UNSUBSCRIBE_URL,
|
|
381
425
|
});
|
|
382
|
-
expect(emailBody).toEqual(
|
|
383
|
-
|
|
426
|
+
expect(emailBody).toEqual(
|
|
427
|
+
expect.objectContaining({ ...shared, unsubscribeUrl: UNSUBSCRIBE_URL }),
|
|
428
|
+
);
|
|
429
|
+
expect(emailSubject).toEqual(
|
|
430
|
+
expect.objectContaining({ ...shared, unsubscribeUrl: UNSUBSCRIBE_URL }),
|
|
431
|
+
);
|
|
384
432
|
expect(Object.keys(emailSubject).sort()).toEqual(
|
|
385
433
|
Object.keys(emailBody).sort(),
|
|
386
434
|
);
|
|
@@ -515,4 +563,236 @@ describe("scheduled maintenance subscriber notifications: template formats", ()
|
|
|
515
563
|
}),
|
|
516
564
|
);
|
|
517
565
|
});
|
|
566
|
+
describe("the unsubscribe link", () => {
|
|
567
|
+
beforeEach(() => {
|
|
568
|
+
jest
|
|
569
|
+
.spyOn(
|
|
570
|
+
StatusPageSubscriberNotificationTemplateService,
|
|
571
|
+
"getTemplateForStatusPage",
|
|
572
|
+
)
|
|
573
|
+
.mockResolvedValue(null);
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
test("the default email, Slack and webhook carry the token link; a public page's SMS the manage link", async () => {
|
|
577
|
+
await ScheduledMaintenanceService.notififySubscribersOnEventScheduled([
|
|
578
|
+
scheduledEvent(),
|
|
579
|
+
]);
|
|
580
|
+
|
|
581
|
+
expect(sentMail()[0]!.vars["unsubscribeUrl"]).toBe(UNSUBSCRIBE_URL);
|
|
582
|
+
expect(sentSlack()[0]).toContain(`[Unsubscribe](${UNSUBSCRIBE_URL})`);
|
|
583
|
+
expect(
|
|
584
|
+
(
|
|
585
|
+
mock(StatusPageSubscriberWebhookUtil.sendWebhookNotification).mock
|
|
586
|
+
.calls[0]![0] as { payload: { unsubscribeUrl: string } }
|
|
587
|
+
).payload.unsubscribeUrl,
|
|
588
|
+
).toBe(UNSUBSCRIBE_URL);
|
|
589
|
+
expect(sentSms()[0]).toContain(`Unsub: ${MANAGE_URL}`);
|
|
590
|
+
expect(sentSms()[0]).not.toContain(UNSUBSCRIBE_TOKEN);
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
test("a private page's SMS carries the token link: its manage page needs a signed-in visitor", async () => {
|
|
594
|
+
const privatePage: StatusPage = statusPage({ withCustomProviders: true });
|
|
595
|
+
privatePage.isPublicStatusPage = false;
|
|
596
|
+
|
|
597
|
+
jest
|
|
598
|
+
.spyOn(StatusPageSubscriberService, "getStatusPagesToSendNotification")
|
|
599
|
+
.mockResolvedValue([privatePage]);
|
|
600
|
+
|
|
601
|
+
await ScheduledMaintenanceService.notififySubscribersOnEventScheduled([
|
|
602
|
+
scheduledEvent(),
|
|
603
|
+
]);
|
|
604
|
+
|
|
605
|
+
expect(sentSms()[0]).toContain(`Unsub: ${UNSUBSCRIBE_URL}`);
|
|
606
|
+
expect(sentMail()[0]!.vars["unsubscribeUrl"]).toBe(UNSUBSCRIBE_URL);
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
test("a private page's custom SMS template gets the token link as {{unsubscribeUrl}}", async () => {
|
|
610
|
+
const privatePage: StatusPage = statusPage({ withCustomProviders: true });
|
|
611
|
+
privatePage.isPublicStatusPage = false;
|
|
612
|
+
|
|
613
|
+
jest
|
|
614
|
+
.spyOn(StatusPageSubscriberService, "getStatusPagesToSendNotification")
|
|
615
|
+
.mockResolvedValue([privatePage]);
|
|
616
|
+
useCustomTemplates(CUSTOM_SUBJECT);
|
|
617
|
+
|
|
618
|
+
await ScheduledMaintenanceService.notififySubscribersOnEventScheduled([
|
|
619
|
+
scheduledEvent(),
|
|
620
|
+
]);
|
|
621
|
+
|
|
622
|
+
expect(
|
|
623
|
+
variablesCompiledInto(
|
|
624
|
+
CUSTOM_BODIES[StatusPageSubscriberNotificationMethod.SMS]!,
|
|
625
|
+
)["unsubscribeUrl"],
|
|
626
|
+
).toBe(UNSUBSCRIBE_URL);
|
|
627
|
+
});
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
/*
|
|
631
|
+
* Escaping. The maintenance title, the status page's name and the names of
|
|
632
|
+
* its resources are plain text a project member typed. In an email body
|
|
633
|
+
* they must read as those characters; the description and the scheduled
|
|
634
|
+
* time are HTML the service rendered, and stay HTML. The subject, SMS,
|
|
635
|
+
* Slack and webhooks show text as written.
|
|
636
|
+
*/
|
|
637
|
+
describe("escaping", () => {
|
|
638
|
+
const HOSTILE_TITLE: string = "<script>alert('x')</script> Failover";
|
|
639
|
+
const HOSTILE_TITLE_HTML: string =
|
|
640
|
+
"<script>alert('x')</script> Failover";
|
|
641
|
+
const HOSTILE_PAGE_NAME: string = '<a href="https://evil.example">Acme</a>';
|
|
642
|
+
const HOSTILE_PAGE_NAME_HTML: string =
|
|
643
|
+
"<a href="https://evil.example">Acme</a>";
|
|
644
|
+
const HOSTILE_RESOURCE: string = '<img src=x onerror="alert(1)"> & DB';
|
|
645
|
+
const HOSTILE_RESOURCE_HTML: string =
|
|
646
|
+
"<img src=x onerror="alert(1)"> & DB";
|
|
647
|
+
|
|
648
|
+
const ESCAPING_EMAIL_BODY: string =
|
|
649
|
+
"<h1>{{scheduledMaintenanceTitle}}</h1><p>{{statusPageName}}</p><div>{{resourcesAffected}}</div><div>{{scheduledMaintenanceDescription}}</div>";
|
|
650
|
+
const ESCAPING_TEXT: string =
|
|
651
|
+
"{{scheduledMaintenanceTitle}} on {{statusPageName}}: {{resourcesAffected}}";
|
|
652
|
+
|
|
653
|
+
function hostileEvent(): ScheduledMaintenance {
|
|
654
|
+
const event: ScheduledMaintenance = scheduledEvent();
|
|
655
|
+
event.title = HOSTILE_TITLE;
|
|
656
|
+
return event;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
function hostilePage(withCustomProviders: boolean): StatusPage {
|
|
660
|
+
const page: StatusPage = statusPage({
|
|
661
|
+
withCustomProviders: withCustomProviders,
|
|
662
|
+
});
|
|
663
|
+
page.pageTitle = HOSTILE_PAGE_NAME;
|
|
664
|
+
return page;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
beforeEach(() => {
|
|
668
|
+
// The service reads resources without their groups: a list of names.
|
|
669
|
+
jest
|
|
670
|
+
.spyOn(StatusPageResourceService, "findByMonitors")
|
|
671
|
+
.mockResolvedValue([resource(HOSTILE_RESOURCE), resource("Replica")]);
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
test("a custom email body escapes the title, the page name and the resource names", async () => {
|
|
675
|
+
jest
|
|
676
|
+
.spyOn(StatusPageSubscriberService, "getStatusPagesToSendNotification")
|
|
677
|
+
.mockResolvedValue([hostilePage(true)]);
|
|
678
|
+
jest
|
|
679
|
+
.spyOn(
|
|
680
|
+
StatusPageSubscriberNotificationTemplateService,
|
|
681
|
+
"getTemplateForStatusPage",
|
|
682
|
+
)
|
|
683
|
+
.mockImplementation(
|
|
684
|
+
async (data: {
|
|
685
|
+
notificationMethod: StatusPageSubscriberNotificationMethod;
|
|
686
|
+
}): Promise<StatusPageSubscriberNotificationTemplate | null> => {
|
|
687
|
+
const template: StatusPageSubscriberNotificationTemplate =
|
|
688
|
+
new StatusPageSubscriberNotificationTemplate();
|
|
689
|
+
template.notificationMethod = data.notificationMethod;
|
|
690
|
+
if (
|
|
691
|
+
data.notificationMethod ===
|
|
692
|
+
StatusPageSubscriberNotificationMethod.Email
|
|
693
|
+
) {
|
|
694
|
+
template.templateBody = ESCAPING_EMAIL_BODY;
|
|
695
|
+
template.emailSubject = ESCAPING_TEXT;
|
|
696
|
+
} else {
|
|
697
|
+
template.templateBody = `${data.notificationMethod}: ${ESCAPING_TEXT}`;
|
|
698
|
+
}
|
|
699
|
+
return template;
|
|
700
|
+
},
|
|
701
|
+
);
|
|
702
|
+
|
|
703
|
+
await ScheduledMaintenanceService.notififySubscribersOnEventScheduled([
|
|
704
|
+
hostileEvent(),
|
|
705
|
+
]);
|
|
706
|
+
|
|
707
|
+
expect(sentMail()).toHaveLength(1);
|
|
708
|
+
expect(sentMail()[0]!.vars).toEqual({
|
|
709
|
+
body: `<h1>${HOSTILE_TITLE_HTML}</h1><p>${HOSTILE_PAGE_NAME_HTML}</p><div>${HOSTILE_RESOURCE_HTML}, Replica</div><div>${DESCRIPTION_HTML}</div>`,
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
const text: string = `${HOSTILE_TITLE} on ${HOSTILE_PAGE_NAME}: ${HOSTILE_RESOURCE}, Replica`;
|
|
713
|
+
expect(sentMail()[0]!.subject).toBe(text);
|
|
714
|
+
expect(sentSms()).toEqual([
|
|
715
|
+
`${StatusPageSubscriberNotificationMethod.SMS}: ${text}`,
|
|
716
|
+
]);
|
|
717
|
+
expect(sentSlack()).toEqual([
|
|
718
|
+
`${StatusPageSubscriberNotificationMethod.Slack}: ${text}`,
|
|
719
|
+
]);
|
|
720
|
+
expect(sentWebhookData()["scheduledMaintenanceTitle"]).toBe(
|
|
721
|
+
HOSTILE_TITLE,
|
|
722
|
+
);
|
|
723
|
+
expect(sentWebhookData()["resourcesAffected"]).toBe(
|
|
724
|
+
`${HOSTILE_RESOURCE}, Replica`,
|
|
725
|
+
);
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
/*
|
|
729
|
+
* Only the advertised HTML variables, and the email-only HTML ones the
|
|
730
|
+
* default template also receives (the multi-timezone time, the rendered
|
|
731
|
+
* description under its old name, and the footer the page's admins
|
|
732
|
+
* wrote), go into the body as HTML.
|
|
733
|
+
*/
|
|
734
|
+
test("only HTML values reach the email body as HTML", async () => {
|
|
735
|
+
jest
|
|
736
|
+
.spyOn(StatusPageSubscriberService, "getStatusPagesToSendNotification")
|
|
737
|
+
.mockResolvedValue([hostilePage(true)]);
|
|
738
|
+
|
|
739
|
+
await ScheduledMaintenanceService.notififySubscribersOnEventScheduled([
|
|
740
|
+
hostileEvent(),
|
|
741
|
+
]);
|
|
742
|
+
|
|
743
|
+
const variables: Record<string, string | SafeHtml> =
|
|
744
|
+
rawVariablesCompiledInto(
|
|
745
|
+
CUSTOM_BODIES[StatusPageSubscriberNotificationMethod.Email]!,
|
|
746
|
+
);
|
|
747
|
+
const html: Array<string> = Object.keys(variables)
|
|
748
|
+
.filter((name: string): boolean => {
|
|
749
|
+
return SafeHtml.isSafeHtml(variables[name]);
|
|
750
|
+
})
|
|
751
|
+
.sort();
|
|
752
|
+
|
|
753
|
+
expect(html).toEqual(
|
|
754
|
+
[
|
|
755
|
+
...SubscriberNotificationTemplateVariables.getEmailBodyHtmlVariableNamesForEventType(
|
|
756
|
+
StatusPageSubscriberNotificationEventType.SubscriberScheduledMaintenanceCreated,
|
|
757
|
+
),
|
|
758
|
+
"eventDescription",
|
|
759
|
+
"scheduledAt",
|
|
760
|
+
"subscriberEmailNotificationFooterText",
|
|
761
|
+
].sort(),
|
|
762
|
+
);
|
|
763
|
+
expect(variables["scheduledMaintenanceTitle"]).toBe(HOSTILE_TITLE);
|
|
764
|
+
expect(variables["statusPageName"]).toBe(HOSTILE_PAGE_NAME);
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
test("the default email gets the resource names escaped, and SMS and Slack get them as written", async () => {
|
|
768
|
+
jest
|
|
769
|
+
.spyOn(StatusPageSubscriberService, "getStatusPagesToSendNotification")
|
|
770
|
+
.mockResolvedValue([hostilePage(false)]);
|
|
771
|
+
jest
|
|
772
|
+
.spyOn(
|
|
773
|
+
StatusPageSubscriberNotificationTemplateService,
|
|
774
|
+
"getTemplateForStatusPage",
|
|
775
|
+
)
|
|
776
|
+
.mockResolvedValue(null);
|
|
777
|
+
|
|
778
|
+
await ScheduledMaintenanceService.notififySubscribersOnEventScheduled([
|
|
779
|
+
hostileEvent(),
|
|
780
|
+
]);
|
|
781
|
+
|
|
782
|
+
expect(sentMail()[0]!.templateType).toBe(
|
|
783
|
+
EmailTemplateType.SubscriberScheduledMaintenanceEventCreated,
|
|
784
|
+
);
|
|
785
|
+
expect(sentMail()[0]!.vars["resourcesAffected"]).toBe(
|
|
786
|
+
`${HOSTILE_RESOURCE_HTML}, Replica`,
|
|
787
|
+
);
|
|
788
|
+
expect(sentMail()[0]!.vars["eventTitle"]).toBe(HOSTILE_TITLE);
|
|
789
|
+
expect(sentSms()[0]).toContain(`Impact: ${HOSTILE_RESOURCE}, Replica.`);
|
|
790
|
+
expect(sentSlack()[0]).toContain(
|
|
791
|
+
`**Resources Affected:** ${HOSTILE_RESOURCE}, Replica`,
|
|
792
|
+
);
|
|
793
|
+
for (const message of [...sentSms(), ...sentSlack()]) {
|
|
794
|
+
expect(message).not.toMatch(/&(?:amp|lt|gt|quot|#39);/);
|
|
795
|
+
}
|
|
796
|
+
});
|
|
797
|
+
});
|
|
518
798
|
});
|
|
@@ -3,7 +3,6 @@ import AlertFeedService from "../../../Server/Services/AlertFeedService";
|
|
|
3
3
|
import AlertService from "../../../Server/Services/AlertService";
|
|
4
4
|
import IncidentFeedService from "../../../Server/Services/IncidentFeedService";
|
|
5
5
|
import IncidentService from "../../../Server/Services/IncidentService";
|
|
6
|
-
import MonitorService from "../../../Server/Services/MonitorService";
|
|
7
6
|
import ProductAnalytics from "../../../Server/Utils/ProductAnalytics";
|
|
8
7
|
import AlertWorkspaceMessages from "../../../Server/Utils/Workspace/WorkspaceMessages/Alert";
|
|
9
8
|
import IncidentWorkspaceMessages from "../../../Server/Utils/Workspace/WorkspaceMessages/Incident";
|
|
@@ -26,6 +25,11 @@ import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
|
|
|
26
25
|
*
|
|
27
26
|
* The same markdown is posted to Slack and Teams, and the feed renders it
|
|
28
27
|
* without safe mode, so a hostile SLO name is checked end to end here too.
|
|
28
|
+
*
|
|
29
|
+
* The feed builders read those relations themselves, one per query, through
|
|
30
|
+
* LinkedAffectedResources; the fixtures below answer each of those reads the
|
|
31
|
+
* way the database would. Every other relation the section now lists is
|
|
32
|
+
* covered in LinkedAffectedResourcesCreatedFeed.test.ts.
|
|
29
33
|
*/
|
|
30
34
|
|
|
31
35
|
const DASHBOARD: string = "https://oneuptime.example/dashboard";
|
|
@@ -52,8 +56,40 @@ type OnCreateSuccessFunction = (
|
|
|
52
56
|
createdItem: Incident,
|
|
53
57
|
) => Promise<Incident>;
|
|
54
58
|
|
|
59
|
+
/*
|
|
60
|
+
* Answers the feed builder's per-relation reads from `record`: one row
|
|
61
|
+
* carrying only the relation that was selected, as the database would.
|
|
62
|
+
*/
|
|
63
|
+
function answerRelationReads(
|
|
64
|
+
service: { findAllBy: unknown },
|
|
65
|
+
record: Incident | Alert,
|
|
66
|
+
): jest.SpyInstance {
|
|
67
|
+
return jest
|
|
68
|
+
.spyOn(service as typeof IncidentService, "findAllBy")
|
|
69
|
+
.mockImplementation((async (findAllBy: {
|
|
70
|
+
select: JSONObject;
|
|
71
|
+
}): Promise<Array<JSONObject>> => {
|
|
72
|
+
const row: JSONObject = {
|
|
73
|
+
_id: record._id!,
|
|
74
|
+
projectId: record.projectId as unknown as JSONObject,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
for (const column of Object.keys(findAllBy.select)) {
|
|
78
|
+
if (column !== "_id" && column !== "projectId") {
|
|
79
|
+
row[column] = (record as unknown as JSONObject)[column]!;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return [row];
|
|
84
|
+
}) as never);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let incidentRelationReads: jest.SpyInstance | undefined;
|
|
88
|
+
|
|
55
89
|
// The feed writers are private; they are reached the way the create hooks reach them.
|
|
56
90
|
function createIncidentFeed(incident: Incident): Promise<void> {
|
|
91
|
+
incidentRelationReads = answerRelationReads(IncidentService, incident);
|
|
92
|
+
|
|
57
93
|
return (
|
|
58
94
|
IncidentService as unknown as {
|
|
59
95
|
createIncidentFeedAsync: CreateIncidentFeedAsyncFunction;
|
|
@@ -97,6 +133,8 @@ function buildMonitor(id: string, name: string): Monitor {
|
|
|
97
133
|
const monitor: Monitor = new Monitor();
|
|
98
134
|
monitor._id = id;
|
|
99
135
|
monitor.name = name;
|
|
136
|
+
// The relation reads select it; a monitor of no known project is not named.
|
|
137
|
+
monitor.projectId = PROJECT_ID;
|
|
100
138
|
return monitor;
|
|
101
139
|
}
|
|
102
140
|
|
|
@@ -163,7 +201,6 @@ function resourcesAffectedLines(markdown: string): Array<string> | null {
|
|
|
163
201
|
|
|
164
202
|
let incidentFeedItem: jest.SpyInstance;
|
|
165
203
|
let alertFeedItem: jest.SpyInstance;
|
|
166
|
-
let dashboardUrlLookup: jest.SpyInstance;
|
|
167
204
|
|
|
168
205
|
function postedMarkdown(spy: jest.SpyInstance): string {
|
|
169
206
|
expect(spy).toHaveBeenCalledTimes(1);
|
|
@@ -177,22 +214,12 @@ beforeEach(() => {
|
|
|
177
214
|
* A fresh URL per call: URL.addRoute mutates, so a shared instance would
|
|
178
215
|
* hide a builder that forgot to copy it.
|
|
179
216
|
*/
|
|
180
|
-
|
|
217
|
+
jest
|
|
181
218
|
.spyOn(DatabaseConfig, "getDashboardUrl")
|
|
182
219
|
.mockImplementation(async (): Promise<URL> => {
|
|
183
220
|
return URL.fromString(DASHBOARD);
|
|
184
221
|
});
|
|
185
222
|
|
|
186
|
-
jest
|
|
187
|
-
.spyOn(MonitorService, "getMonitorLinkInDashboard")
|
|
188
|
-
.mockImplementation(
|
|
189
|
-
async (projectId: ObjectID, monitorId: ObjectID): Promise<URL> => {
|
|
190
|
-
return URL.fromString(
|
|
191
|
-
`${DASHBOARD}/${projectId.toString()}/monitors/${monitorId.toString()}`,
|
|
192
|
-
);
|
|
193
|
-
},
|
|
194
|
-
);
|
|
195
|
-
|
|
196
223
|
jest
|
|
197
224
|
.spyOn(IncidentWorkspaceMessages, "getIncidentCreateMessageBlocks")
|
|
198
225
|
.mockResolvedValue([] as never);
|
|
@@ -246,7 +273,7 @@ describe("incident created feed item", () => {
|
|
|
246
273
|
]);
|
|
247
274
|
});
|
|
248
275
|
|
|
249
|
-
test("a monitors-only incident reads exactly as before
|
|
276
|
+
test("a monitors-only incident reads exactly as before", async () => {
|
|
250
277
|
await createIncidentFeed(
|
|
251
278
|
buildIncident({ monitors: [buildMonitor(MONITOR_ID, "checkout-web")] }),
|
|
252
279
|
);
|
|
@@ -254,7 +281,6 @@ describe("incident created feed item", () => {
|
|
|
254
281
|
expect(resourcesAffectedLines(postedMarkdown(incidentFeedItem))).toEqual([
|
|
255
282
|
`- [checkout-web](${monitorLink(MONITOR_ID)})`,
|
|
256
283
|
]);
|
|
257
|
-
expect(dashboardUrlLookup).not.toHaveBeenCalled();
|
|
258
284
|
});
|
|
259
285
|
|
|
260
286
|
test("an incident with neither monitors nor SLOs has no Resources Affected section", async () => {
|
|
@@ -328,7 +354,31 @@ describe("incident created feed item", () => {
|
|
|
328
354
|
expect(resourcesAffectedLines(postedMarkdown(incidentFeedItem))).toBeNull();
|
|
329
355
|
});
|
|
330
356
|
|
|
331
|
-
test("
|
|
357
|
+
test("reads the incident's SLOs with their project, as root", async () => {
|
|
358
|
+
await createIncidentFeed(buildIncident({}));
|
|
359
|
+
|
|
360
|
+
const sloRead: JSONObject | undefined = incidentRelationReads!.mock.calls
|
|
361
|
+
.map((call: Array<unknown>): JSONObject => {
|
|
362
|
+
return call[0] as JSONObject;
|
|
363
|
+
})
|
|
364
|
+
.find((findAllBy: JSONObject): boolean => {
|
|
365
|
+
return Boolean(
|
|
366
|
+
(findAllBy["select"] as JSONObject)["serviceLevelObjectives"],
|
|
367
|
+
);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
/*
|
|
371
|
+
* projectId too: the read runs as root, and the feed names only SLOs of
|
|
372
|
+
* the record's own project.
|
|
373
|
+
*/
|
|
374
|
+
expect(
|
|
375
|
+
(sloRead!["select"] as JSONObject)["serviceLevelObjectives"],
|
|
376
|
+
).toEqual({ _id: true, name: true, projectId: true });
|
|
377
|
+
expect(sloRead!["props"]).toEqual({ isRoot: true });
|
|
378
|
+
expect((sloRead!["query"] as JSONObject)["projectId"]).toBe(PROJECT_ID);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
test("onCreateSuccess leaves the relations to the feed builder's own reads", async () => {
|
|
332
382
|
jest.spyOn(ProductAnalytics, "captureForUser").mockImplementation((() => {
|
|
333
383
|
// no analytics in tests
|
|
334
384
|
}) as never);
|
|
@@ -362,45 +412,52 @@ describe("incident created feed item", () => {
|
|
|
362
412
|
).select;
|
|
363
413
|
|
|
364
414
|
/*
|
|
365
|
-
*
|
|
366
|
-
* the
|
|
415
|
+
* Joining every many-to-many relation into this one read would return
|
|
416
|
+
* the product of their sizes; LinkedAffectedResources reads them one at
|
|
417
|
+
* a time instead.
|
|
367
418
|
*/
|
|
368
|
-
expect(select["
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
projectId: true,
|
|
372
|
-
});
|
|
373
|
-
// The monitors the section already listed are still read.
|
|
374
|
-
expect(select["monitors"]).toEqual({ name: true, _id: true });
|
|
419
|
+
expect(select["monitors"]).toBeUndefined();
|
|
420
|
+
expect(select["serviceLevelObjectives"]).toBeUndefined();
|
|
421
|
+
expect(select["projectId"]).toBe(true);
|
|
375
422
|
});
|
|
376
423
|
});
|
|
377
424
|
|
|
378
425
|
describe("alert created feed item", () => {
|
|
426
|
+
let alertRelationReads: jest.SpyInstance;
|
|
427
|
+
|
|
379
428
|
function mockAlertRow(alert: Alert): jest.SpyInstance {
|
|
429
|
+
alertRelationReads = answerRelationReads(AlertService, alert);
|
|
430
|
+
|
|
380
431
|
return jest
|
|
381
432
|
.spyOn(AlertService, "findOneById")
|
|
382
433
|
.mockResolvedValue(alert as never);
|
|
383
434
|
}
|
|
384
435
|
|
|
385
|
-
test("reads the alert's SLOs alongside its monitor", async () => {
|
|
386
|
-
|
|
436
|
+
test("reads the alert's SLOs alongside its monitor, with their project", async () => {
|
|
437
|
+
mockAlertRow(buildAlert({}));
|
|
387
438
|
|
|
388
439
|
await createAlertFeed(ALERT_ID);
|
|
389
440
|
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
|
|
441
|
+
const selects: Array<JSONObject> = alertRelationReads.mock.calls.map(
|
|
442
|
+
(call: Array<unknown>): JSONObject => {
|
|
443
|
+
return (call[0] as { select: JSONObject }).select;
|
|
444
|
+
},
|
|
445
|
+
);
|
|
393
446
|
|
|
394
447
|
/*
|
|
395
|
-
* projectId too: the
|
|
396
|
-
* the record's own project.
|
|
448
|
+
* projectId too: the reads run as root, and the feed names only
|
|
449
|
+
* resources of the record's own project.
|
|
397
450
|
*/
|
|
398
|
-
expect(
|
|
399
|
-
|
|
451
|
+
expect(selects).toContainEqual({
|
|
452
|
+
_id: true,
|
|
453
|
+
projectId: true,
|
|
454
|
+
serviceLevelObjectives: { _id: true, name: true, projectId: true },
|
|
455
|
+
});
|
|
456
|
+
expect(selects).toContainEqual({
|
|
400
457
|
_id: true,
|
|
401
458
|
projectId: true,
|
|
459
|
+
monitor: { _id: true, name: true, projectId: true },
|
|
402
460
|
});
|
|
403
|
-
expect(select["monitor"]).toEqual({ name: true, _id: true });
|
|
404
461
|
});
|
|
405
462
|
|
|
406
463
|
test("a burn-rate alert with no monitor names its SLO under Resources Affected", async () => {
|
|
@@ -446,7 +503,6 @@ describe("alert created feed item", () => {
|
|
|
446
503
|
expect(resourcesAffectedLines(postedMarkdown(alertFeedItem))).toEqual([
|
|
447
504
|
`- [checkout-web](${monitorLink(MONITOR_ID)})`,
|
|
448
505
|
]);
|
|
449
|
-
expect(dashboardUrlLookup).not.toHaveBeenCalled();
|
|
450
506
|
});
|
|
451
507
|
|
|
452
508
|
test("an alert with neither monitor nor SLOs has no Resources Affected section", async () => {
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import SmsService from "../../../Server/Services/SmsService";
|
|
2
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
3
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
4
|
+
import Phone from "../../../Types/Phone";
|
|
5
|
+
import API from "../../../Utils/API";
|
|
6
|
+
import getJestMockFunction, { MockFunction } from "../../MockType";
|
|
7
|
+
import {
|
|
8
|
+
afterEach,
|
|
9
|
+
beforeEach,
|
|
10
|
+
describe,
|
|
11
|
+
expect,
|
|
12
|
+
jest,
|
|
13
|
+
test,
|
|
14
|
+
} from "@jest/globals";
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
* Common's SmsService posts every SMS to the Notification service's
|
|
18
|
+
* /api/notification/sms/send. failIfNotSent asks that route to answer with an
|
|
19
|
+
* error, not success, for an SMS it deliberately does not send (SMS turned
|
|
20
|
+
* off, too little balance); the status page subscriber jobs set it so such an
|
|
21
|
+
* SMS counts as failed. It is sent only when asked for, so every other
|
|
22
|
+
* caller's request is unchanged.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
describe("SmsService.sendSms and failIfNotSent", () => {
|
|
26
|
+
let post: MockFunction;
|
|
27
|
+
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
post = getJestMockFunction();
|
|
30
|
+
post.mockResolvedValue({} as never);
|
|
31
|
+
jest.spyOn(API, "post").mockImplementation(post as never);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
jest.restoreAllMocks();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
function postedBody(): JSONObject {
|
|
39
|
+
return (post.mock.calls[0]![0] as { data: JSONObject }).data;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
test("sends failIfNotSent when the caller asks", async () => {
|
|
43
|
+
await SmsService.sendSms(
|
|
44
|
+
{ to: new Phone("+15555550123"), message: "Hello" },
|
|
45
|
+
{ projectId: ObjectID.generate(), failIfNotSent: true },
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
expect(postedBody()["failIfNotSent"]).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("leaves it out otherwise, so other callers' requests are unchanged", async () => {
|
|
52
|
+
await SmsService.sendSms(
|
|
53
|
+
{ to: new Phone("+15555550123"), message: "Hello" },
|
|
54
|
+
{ projectId: ObjectID.generate() },
|
|
55
|
+
);
|
|
56
|
+
await SmsService.sendSms(
|
|
57
|
+
{ to: new Phone("+15555550123"), message: "Hello" },
|
|
58
|
+
{ projectId: ObjectID.generate(), failIfNotSent: false },
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
for (const call of post.mock.calls) {
|
|
62
|
+
expect("failIfNotSent" in (call[0] as { data: JSONObject }).data).toBe(
|
|
63
|
+
false,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
});
|