@oneuptime/common 14.0.7 → 14.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/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/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 +14 -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/DatabaseService.ts +93 -10
- 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 +1535 -65
- package/Server/Services/IncidentStateTimelineService.ts +13 -0
- package/Server/Services/IncidentTemplateService.ts +109 -0
- package/Server/Services/ScheduledMaintenancePublicNoteService.ts +30 -1
- package/Server/Services/ScheduledMaintenanceService.ts +68 -18
- 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/Types/Markdown.ts +153 -6
- package/Server/Types/Workflow/Components/Email.ts +61 -2
- package/Server/Utils/CustomField/CustomFieldRename.ts +378 -0
- package/Server/Utils/DataSource/EgressGuard.ts +66 -21
- package/Server/Utils/Mail/PinnedSmtpSocket.ts +130 -0
- package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +10 -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/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/CustomFieldsSettingsIncidentOptions.test.tsx +357 -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 +242 -5
- package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +91 -1
- 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/PublicNotePagesNotifyDefault.test.tsx +113 -1
- 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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 +14 -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/DatabaseService.js +56 -7
- package/build/dist/Server/Services/DatabaseService.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 +1052 -0
- 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/ScheduledMaintenancePublicNoteService.js +26 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +47 -17
- 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/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/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/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/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/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/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/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/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
|
@@ -109,6 +109,15 @@ const RULE_CRITERIA_RELATION_OPERATORS: ReadonlySet<RuleCriteriaOperator> =
|
|
|
109
109
|
RuleCriteriaOperator.HasNoneOf,
|
|
110
110
|
]);
|
|
111
111
|
|
|
112
|
+
// A hook-free write to one row by id, ready to run (see buildColumnsByIdUpdateStatement).
|
|
113
|
+
interface ColumnsByIdUpdateStatement {
|
|
114
|
+
tableName: string;
|
|
115
|
+
primaryColumnName: string;
|
|
116
|
+
setSql: string;
|
|
117
|
+
whereSql: string;
|
|
118
|
+
params: Array<unknown>;
|
|
119
|
+
}
|
|
120
|
+
|
|
112
121
|
class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
113
122
|
public modelType!: { new (): TBaseModel };
|
|
114
123
|
private model!: TBaseModel;
|
|
@@ -3821,6 +3830,78 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
3821
3830
|
*/
|
|
3822
3831
|
skipUpdateDateColumn?: boolean;
|
|
3823
3832
|
}): Promise<void> {
|
|
3833
|
+
const statement: ColumnsByIdUpdateStatement | null =
|
|
3834
|
+
this.buildColumnsByIdUpdateStatement(
|
|
3835
|
+
input,
|
|
3836
|
+
"updateColumnsByIdWithoutHooks",
|
|
3837
|
+
);
|
|
3838
|
+
|
|
3839
|
+
if (!statement) {
|
|
3840
|
+
return;
|
|
3841
|
+
}
|
|
3842
|
+
|
|
3843
|
+
const sql: string = `UPDATE "${statement.tableName}" SET ${statement.setSql} WHERE ${statement.whereSql}`;
|
|
3844
|
+
|
|
3845
|
+
await this.getRepository().manager.query(sql, statement.params);
|
|
3846
|
+
}
|
|
3847
|
+
|
|
3848
|
+
/*
|
|
3849
|
+
* The same single-statement, hook-free, no-version-bump write as
|
|
3850
|
+
* `updateColumnsByIdWithoutHooks` with its `expectedData` guard, which
|
|
3851
|
+
* also says whether it happened: true when the row matched every expected
|
|
3852
|
+
* value and was written, false when another writer had changed it first
|
|
3853
|
+
* (or it no longer exists).
|
|
3854
|
+
*
|
|
3855
|
+
* This is what lets two workers race for one row and have exactly one of
|
|
3856
|
+
* them win - a job claiming a Pending notification by moving it to
|
|
3857
|
+
* InProgress, say, where both would otherwise send it. The check and the
|
|
3858
|
+
* write are one statement, so there is no window between them.
|
|
3859
|
+
*
|
|
3860
|
+
* The UPDATE sits in a CTE so the statement is a SELECT of the rows it
|
|
3861
|
+
* wrote (see updateColumnsByIdIfUnlockedWithoutHooks for why a bare
|
|
3862
|
+
* UPDATE ... RETURNING cannot tell the two cases apart through TypeORM).
|
|
3863
|
+
*/
|
|
3864
|
+
@CaptureSpan()
|
|
3865
|
+
public async compareAndSetColumnsByIdWithoutHooks(input: {
|
|
3866
|
+
id: ObjectID;
|
|
3867
|
+
data: PartialEntity<TBaseModel>;
|
|
3868
|
+
expectedData: PartialEntity<TBaseModel>;
|
|
3869
|
+
skipUpdateDateColumn?: boolean;
|
|
3870
|
+
}): Promise<boolean> {
|
|
3871
|
+
const statement: ColumnsByIdUpdateStatement | null =
|
|
3872
|
+
this.buildColumnsByIdUpdateStatement(
|
|
3873
|
+
input,
|
|
3874
|
+
"compareAndSetColumnsByIdWithoutHooks",
|
|
3875
|
+
);
|
|
3876
|
+
|
|
3877
|
+
if (!statement) {
|
|
3878
|
+
return false;
|
|
3879
|
+
}
|
|
3880
|
+
|
|
3881
|
+
const sql: string = `WITH "updated" AS (UPDATE "${statement.tableName}" SET ${statement.setSql} WHERE ${statement.whereSql} RETURNING "${statement.primaryColumnName}") SELECT "${statement.primaryColumnName}" FROM "updated"`;
|
|
3882
|
+
|
|
3883
|
+
const result: unknown = await this.getRepository().manager.query(
|
|
3884
|
+
sql,
|
|
3885
|
+
statement.params,
|
|
3886
|
+
);
|
|
3887
|
+
|
|
3888
|
+
return Array.isArray(result) && result.length > 0;
|
|
3889
|
+
}
|
|
3890
|
+
|
|
3891
|
+
/*
|
|
3892
|
+
* The SET and WHERE clauses of a hook-free write to one row by id, with
|
|
3893
|
+
* every value bound as a parameter. Null when there is nothing to set.
|
|
3894
|
+
* `methodName` is the public method the errors are reported under.
|
|
3895
|
+
*/
|
|
3896
|
+
private buildColumnsByIdUpdateStatement(
|
|
3897
|
+
input: {
|
|
3898
|
+
id: ObjectID;
|
|
3899
|
+
data: PartialEntity<TBaseModel>;
|
|
3900
|
+
expectedData?: PartialEntity<TBaseModel> | undefined;
|
|
3901
|
+
skipUpdateDateColumn?: boolean | undefined;
|
|
3902
|
+
},
|
|
3903
|
+
methodName: string,
|
|
3904
|
+
): ColumnsByIdUpdateStatement | null {
|
|
3824
3905
|
if (!input.id) {
|
|
3825
3906
|
throw new BadDataException("id is required");
|
|
3826
3907
|
}
|
|
@@ -3846,7 +3927,7 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
3846
3927
|
metadata.findColumnWithPropertyName(propertyName);
|
|
3847
3928
|
if (!column) {
|
|
3848
3929
|
throw new BadDataException(
|
|
3849
|
-
|
|
3930
|
+
`${methodName}: unknown column "${propertyName}" on "${metadata.tableName}"`,
|
|
3850
3931
|
);
|
|
3851
3932
|
}
|
|
3852
3933
|
/*
|
|
@@ -3857,7 +3938,7 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
3857
3938
|
*/
|
|
3858
3939
|
if (typeof value === "function") {
|
|
3859
3940
|
throw new BadDataException(
|
|
3860
|
-
|
|
3941
|
+
`${methodName}: SQL-expression values are not supported (column "${propertyName}"); pass a literal value.`,
|
|
3861
3942
|
);
|
|
3862
3943
|
}
|
|
3863
3944
|
/*
|
|
@@ -3872,7 +3953,7 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
3872
3953
|
}
|
|
3873
3954
|
|
|
3874
3955
|
if (setClauses.length === 0) {
|
|
3875
|
-
return;
|
|
3956
|
+
return null;
|
|
3876
3957
|
}
|
|
3877
3958
|
|
|
3878
3959
|
// The raw path has no entity machinery to touch updateDate — do it here.
|
|
@@ -3898,13 +3979,13 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
3898
3979
|
|
|
3899
3980
|
if (!column) {
|
|
3900
3981
|
throw new BadDataException(
|
|
3901
|
-
|
|
3982
|
+
`${methodName}: unknown expected column "${propertyName}" on "${metadata.tableName}"`,
|
|
3902
3983
|
);
|
|
3903
3984
|
}
|
|
3904
3985
|
|
|
3905
3986
|
if (typeof value === "function") {
|
|
3906
3987
|
throw new BadDataException(
|
|
3907
|
-
|
|
3988
|
+
`${methodName}: SQL-expression expected values are not supported (column "${propertyName}"); pass a literal value.`,
|
|
3908
3989
|
);
|
|
3909
3990
|
}
|
|
3910
3991
|
|
|
@@ -3914,11 +3995,13 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
3914
3995
|
);
|
|
3915
3996
|
}
|
|
3916
3997
|
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3998
|
+
return {
|
|
3999
|
+
tableName: metadata.tableName,
|
|
4000
|
+
primaryColumnName: primaryColumnName,
|
|
4001
|
+
setSql: setClauses.join(", "),
|
|
4002
|
+
whereSql: whereClauses.join(" AND "),
|
|
4003
|
+
params: params,
|
|
4004
|
+
};
|
|
3922
4005
|
}
|
|
3923
4006
|
|
|
3924
4007
|
/*
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import DatabaseService from "./DatabaseService";
|
|
2
|
+
import IncidentService from "./IncidentService";
|
|
3
|
+
import IncidentTemplateService from "./IncidentTemplateService";
|
|
2
4
|
import Model from "../../Models/DatabaseModels/IncidentCustomField";
|
|
5
|
+
import BadDataException from "../../Types/Exception/BadDataException";
|
|
6
|
+
import { INCIDENT_CUSTOM_FIELD_TABLE_VIEW_IDS } from "../../Types/CustomField/CustomFieldSavedViews";
|
|
7
|
+
import { generateCustomFieldVariableKey } from "../../Types/CustomField/CustomFieldVariableKey";
|
|
8
|
+
import LIMIT_MAX from "../../Types/Database/LimitMax";
|
|
9
|
+
import Dictionary from "../../Types/Dictionary";
|
|
3
10
|
import ObjectID from "../../Types/ObjectID";
|
|
4
11
|
import CreateBy from "../Types/Database/CreateBy";
|
|
12
|
+
import QueryHelper from "../Types/Database/QueryHelper";
|
|
13
|
+
import Query from "../Types/Database/Query";
|
|
5
14
|
import UpdateBy from "../Types/Database/UpdateBy";
|
|
6
15
|
import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
|
|
7
16
|
import { backfillMappedCustomFieldValues } from "../Utils/CustomField/CustomFieldDefinitionMappingHooks";
|
|
@@ -9,6 +18,7 @@ import {
|
|
|
9
18
|
validateCustomFieldMappingOnCreate,
|
|
10
19
|
validateCustomFieldMappingOnUpdate,
|
|
11
20
|
} from "../Utils/CustomField/CustomFieldMappingValidator";
|
|
21
|
+
import { renameCustomField } from "../Utils/CustomField/CustomFieldRename";
|
|
12
22
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
13
23
|
|
|
14
24
|
/*
|
|
@@ -17,7 +27,27 @@ import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
|
17
27
|
* (OneUptime/oneuptime#3549). These hooks are the two halves of that
|
|
18
28
|
* configuration being saved: refusing a mapping that could never resolve, and
|
|
19
29
|
* filling in the incidents that already exist once one is saved.
|
|
30
|
+
*
|
|
31
|
+
* They also look after the two things an incident field is known by besides
|
|
32
|
+
* its settings:
|
|
33
|
+
*
|
|
34
|
+
* - its template key (variableKey), made from the name on create and never
|
|
35
|
+
* changed afterwards, so templates that use {{customFields.<key>}} keep
|
|
36
|
+
* working whatever the field is renamed to;
|
|
37
|
+
* - its name, which is what incidents store its values under. Renaming a
|
|
38
|
+
* field moves those values, and the saved views that name it, to the new
|
|
39
|
+
* name (see onUpdateSuccess).
|
|
20
40
|
*/
|
|
41
|
+
|
|
42
|
+
/*
|
|
43
|
+
* What onBeforeUpdate hands to onUpdateSuccess: for a write that renames a
|
|
44
|
+
* field, the name each field had before it, by field id.
|
|
45
|
+
*/
|
|
46
|
+
type RenameCarryForward = Dictionary<{
|
|
47
|
+
oldName: string;
|
|
48
|
+
projectId: ObjectID;
|
|
49
|
+
}> | null;
|
|
50
|
+
|
|
21
51
|
export class Service extends DatabaseService<Model> {
|
|
22
52
|
public constructor() {
|
|
23
53
|
super(Model);
|
|
@@ -32,6 +62,8 @@ export class Service extends DatabaseService<Model> {
|
|
|
32
62
|
createBy: createBy,
|
|
33
63
|
});
|
|
34
64
|
|
|
65
|
+
createBy.data.variableKey = await this.generateVariableKey(createBy);
|
|
66
|
+
|
|
35
67
|
return { createBy, carryForward: null };
|
|
36
68
|
}
|
|
37
69
|
|
|
@@ -39,13 +71,22 @@ export class Service extends DatabaseService<Model> {
|
|
|
39
71
|
protected override async onBeforeUpdate(
|
|
40
72
|
updateBy: UpdateBy<Model>,
|
|
41
73
|
): Promise<OnUpdate<Model>> {
|
|
74
|
+
/*
|
|
75
|
+
* The template key never changes after create. Dropped rather than
|
|
76
|
+
* refused, so a client that saves back the whole record it read (key
|
|
77
|
+
* included) is not rejected for it.
|
|
78
|
+
*/
|
|
79
|
+
delete (updateBy.data as Dictionary<unknown>)["variableKey"];
|
|
80
|
+
|
|
42
81
|
await validateCustomFieldMappingOnUpdate({
|
|
43
82
|
definitionModelType: Model,
|
|
44
83
|
definitionService: this,
|
|
45
84
|
updateBy: updateBy,
|
|
46
85
|
});
|
|
47
86
|
|
|
48
|
-
|
|
87
|
+
const carryForward: RenameCarryForward = await this.prepareRename(updateBy);
|
|
88
|
+
|
|
89
|
+
return { updateBy, carryForward: carryForward };
|
|
49
90
|
}
|
|
50
91
|
|
|
51
92
|
@CaptureSpan()
|
|
@@ -67,6 +108,16 @@ export class Service extends DatabaseService<Model> {
|
|
|
67
108
|
onUpdate: OnUpdate<Model>,
|
|
68
109
|
updatedItemIds: Array<ObjectID>,
|
|
69
110
|
): Promise<OnUpdate<Model>> {
|
|
111
|
+
/*
|
|
112
|
+
* Before the mapping backfill below, which writes mapped values under the
|
|
113
|
+
* field's name: by then the values already stored have to be under the
|
|
114
|
+
* new name too.
|
|
115
|
+
*/
|
|
116
|
+
await this.moveValuesOfRenamedFields({
|
|
117
|
+
carryForward: onUpdate.carryForward as RenameCarryForward,
|
|
118
|
+
updatedItemIds: updatedItemIds,
|
|
119
|
+
});
|
|
120
|
+
|
|
70
121
|
if (updatedItemIds.length > 0) {
|
|
71
122
|
backfillMappedCustomFieldValues({
|
|
72
123
|
definitionModelType: Model,
|
|
@@ -78,6 +129,230 @@ export class Service extends DatabaseService<Model> {
|
|
|
78
129
|
return onUpdate;
|
|
79
130
|
}
|
|
80
131
|
|
|
132
|
+
/*
|
|
133
|
+
* The key for a new field: made from its name, and not one another field of
|
|
134
|
+
* the project already has. Whatever the client sent is replaced, like the
|
|
135
|
+
* other columns this service owns.
|
|
136
|
+
*
|
|
137
|
+
* Two fields created at the same moment could both be handed the same key;
|
|
138
|
+
* the unique (projectId, variableKey) index then refuses the second create,
|
|
139
|
+
* which can simply be retried, rather than letting two fields share a key.
|
|
140
|
+
*/
|
|
141
|
+
private async generateVariableKey(
|
|
142
|
+
createBy: CreateBy<Model>,
|
|
143
|
+
): Promise<string> {
|
|
144
|
+
const projectId: ObjectID | undefined =
|
|
145
|
+
(createBy.props.tenantId as ObjectID | undefined) ||
|
|
146
|
+
createBy.data.projectId;
|
|
147
|
+
|
|
148
|
+
const existing: Array<Model> = projectId
|
|
149
|
+
? await this.findBy({
|
|
150
|
+
query: {
|
|
151
|
+
projectId: projectId,
|
|
152
|
+
},
|
|
153
|
+
select: {
|
|
154
|
+
variableKey: true,
|
|
155
|
+
},
|
|
156
|
+
limit: LIMIT_MAX,
|
|
157
|
+
skip: 0,
|
|
158
|
+
props: {
|
|
159
|
+
isRoot: true,
|
|
160
|
+
},
|
|
161
|
+
})
|
|
162
|
+
: [];
|
|
163
|
+
|
|
164
|
+
return generateCustomFieldVariableKey({
|
|
165
|
+
name: createBy.data.name || "",
|
|
166
|
+
existingKeys: existing.map((field: Model) => {
|
|
167
|
+
return field.variableKey;
|
|
168
|
+
}),
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/*
|
|
173
|
+
* For a write that sets a name: the fields it renames, with the names they
|
|
174
|
+
* had, and a refusal when the rename cannot be done safely.
|
|
175
|
+
*
|
|
176
|
+
* The store is read as root, like IncidentService reads incidents for its
|
|
177
|
+
* update hooks, and limited to the caller's project: the update's own
|
|
178
|
+
* permission check has not run yet, and a non-root caller must not learn
|
|
179
|
+
* anything about another project's fields from a refusal.
|
|
180
|
+
*/
|
|
181
|
+
private async prepareRename(
|
|
182
|
+
updateBy: UpdateBy<Model>,
|
|
183
|
+
): Promise<RenameCarryForward> {
|
|
184
|
+
const newName: unknown = updateBy.data.name;
|
|
185
|
+
|
|
186
|
+
if (typeof newName !== "string") {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const query: Query<Model> =
|
|
191
|
+
!updateBy.props.isRoot && updateBy.props.tenantId
|
|
192
|
+
? {
|
|
193
|
+
...updateBy.query,
|
|
194
|
+
projectId: updateBy.props.tenantId,
|
|
195
|
+
}
|
|
196
|
+
: updateBy.query;
|
|
197
|
+
|
|
198
|
+
const fields: Array<Model> = await this.findBy({
|
|
199
|
+
query: query,
|
|
200
|
+
select: {
|
|
201
|
+
_id: true,
|
|
202
|
+
name: true,
|
|
203
|
+
projectId: true,
|
|
204
|
+
},
|
|
205
|
+
limit: LIMIT_MAX,
|
|
206
|
+
skip: 0,
|
|
207
|
+
props: {
|
|
208
|
+
isRoot: true,
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const renamed: Array<Model> = fields.filter((field: Model) => {
|
|
213
|
+
return Boolean(field.id && field.projectId) && field.name !== newName;
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
if (renamed.length === 0) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/*
|
|
221
|
+
* One name for several fields would leave them all storing their values
|
|
222
|
+
* under the same key, merged beyond repair.
|
|
223
|
+
*/
|
|
224
|
+
if (fields.length > 1) {
|
|
225
|
+
throw new BadDataException(
|
|
226
|
+
"Incident custom fields can only be renamed one at a time. Update a single field to change its name.",
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const field: Model = renamed[0]!;
|
|
231
|
+
|
|
232
|
+
/*
|
|
233
|
+
* Creating a field already refuses a name another field has
|
|
234
|
+
* (UniqueColumnBy, case-insensitively), but an update does not check it,
|
|
235
|
+
* and a rename onto another field's name would move this field's values
|
|
236
|
+
* over that field's.
|
|
237
|
+
*/
|
|
238
|
+
const clashes: number = (
|
|
239
|
+
await this.countBy({
|
|
240
|
+
query: {
|
|
241
|
+
projectId: field.projectId!,
|
|
242
|
+
name: QueryHelper.findWithSameText(newName),
|
|
243
|
+
_id: QueryHelper.notEquals(field.id!.toString()),
|
|
244
|
+
},
|
|
245
|
+
props: {
|
|
246
|
+
isRoot: true,
|
|
247
|
+
},
|
|
248
|
+
})
|
|
249
|
+
).toNumber();
|
|
250
|
+
|
|
251
|
+
if (clashes > 0) {
|
|
252
|
+
throw new BadDataException(
|
|
253
|
+
"Another incident custom field already has this name. Choose a different name.",
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
[field.id!.toString()]: {
|
|
259
|
+
oldName: field.name || "",
|
|
260
|
+
projectId: field.projectId!,
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/*
|
|
266
|
+
* Incidents store a field's values under its name, and incident templates
|
|
267
|
+
* store the values they fill in the same way, so a renamed field's values
|
|
268
|
+
* are moved to the new name in both, together with the saved views of the
|
|
269
|
+
* incidents table that name it. Values a record held under the new name
|
|
270
|
+
* without holding the old one - a deleted field's, which deleting leaves
|
|
271
|
+
* behind - are cleared, so the renamed field never shows (or sends to
|
|
272
|
+
* subscribers) answers that were never its own.
|
|
273
|
+
*
|
|
274
|
+
* With raw SQL (CustomFieldRename), not an update through this or any
|
|
275
|
+
* other service: that would start the "On Update Incident" workflow for
|
|
276
|
+
* every incident holding a value, and a rename changes no incident's
|
|
277
|
+
* answers.
|
|
278
|
+
*
|
|
279
|
+
* The name is read back from the store rather than taken from the request,
|
|
280
|
+
* so the values move to exactly the name that was saved. Awaited, so the
|
|
281
|
+
* card reloading after the save already finds them under the new name.
|
|
282
|
+
*/
|
|
283
|
+
private async moveValuesOfRenamedFields(data: {
|
|
284
|
+
carryForward: RenameCarryForward;
|
|
285
|
+
updatedItemIds: Array<ObjectID>;
|
|
286
|
+
}): Promise<void> {
|
|
287
|
+
if (!data.carryForward || data.updatedItemIds.length === 0) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const renamedIds: Array<ObjectID> = data.updatedItemIds.filter(
|
|
292
|
+
(id: ObjectID) => {
|
|
293
|
+
return Boolean(data.carryForward![id.toString()]);
|
|
294
|
+
},
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
if (renamedIds.length === 0) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const fields: Array<Model> = await this.findBy({
|
|
302
|
+
query: {
|
|
303
|
+
_id: QueryHelper.any(
|
|
304
|
+
renamedIds.map((id: ObjectID) => {
|
|
305
|
+
return id.toString();
|
|
306
|
+
}),
|
|
307
|
+
),
|
|
308
|
+
},
|
|
309
|
+
select: {
|
|
310
|
+
_id: true,
|
|
311
|
+
name: true,
|
|
312
|
+
},
|
|
313
|
+
limit: renamedIds.length,
|
|
314
|
+
skip: 0,
|
|
315
|
+
props: {
|
|
316
|
+
isRoot: true,
|
|
317
|
+
},
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
for (const field of fields) {
|
|
321
|
+
const before: { oldName: string; projectId: ObjectID } | undefined =
|
|
322
|
+
data.carryForward[field.id!.toString()];
|
|
323
|
+
|
|
324
|
+
if (!before || !field.name || field.name === before.oldName) {
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const newName: string = field.name;
|
|
329
|
+
|
|
330
|
+
await renameCustomField({
|
|
331
|
+
projectId: before.projectId,
|
|
332
|
+
oldName: before.oldName,
|
|
333
|
+
newName: newName,
|
|
334
|
+
valueServices: [IncidentService, IncidentTemplateService],
|
|
335
|
+
tableViewIds: INCIDENT_CUSTOM_FIELD_TABLE_VIEW_IDS,
|
|
336
|
+
definitionName: "incident custom field",
|
|
337
|
+
/*
|
|
338
|
+
* The values are all still under the old name: so is the field,
|
|
339
|
+
* again, and the save fails. Left renamed, the field would show
|
|
340
|
+
* none of its values, and renaming it back would clear them (the
|
|
341
|
+
* move clears stale values under the name it moves to). Hook-free,
|
|
342
|
+
* so the rename is not attempted again, and only if nobody has
|
|
343
|
+
* renamed the field since.
|
|
344
|
+
*/
|
|
345
|
+
onValuesNotMoved: async (): Promise<void> => {
|
|
346
|
+
await this.updateColumnsByIdWithoutHooks({
|
|
347
|
+
id: field.id!,
|
|
348
|
+
data: { name: before.oldName },
|
|
349
|
+
expectedData: { name: newName },
|
|
350
|
+
});
|
|
351
|
+
},
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
81
356
|
/*
|
|
82
357
|
* `props.tenantId` is set on every dashboard write but not on internal
|
|
83
358
|
* root-scoped ones, so fall back to the row itself rather than skipping the
|
|
@@ -19,6 +19,7 @@ import Query from "../Types/Database/Query";
|
|
|
19
19
|
import File from "../../Models/DatabaseModels/File";
|
|
20
20
|
import FileAttachmentMarkdownUtil from "../Utils/FileAttachmentMarkdownUtil";
|
|
21
21
|
import { syncIsPublicForMarkdownImages } from "../Utils/InlineImageAccessTokenSync";
|
|
22
|
+
import SubscriberNotificationResendAccess from "../Utils/StatusPage/SubscriberNotificationResendAccess";
|
|
22
23
|
|
|
23
24
|
export class Service extends DatabaseService<Model> {
|
|
24
25
|
public constructor() {
|
|
@@ -181,11 +182,24 @@ export class Service extends DatabaseService<Model> {
|
|
|
181
182
|
* An edit tells subscribers nothing unless the editor asked for it on this
|
|
182
183
|
* edit (see SubscriberUpdateNotification). When they did, queue the update
|
|
183
184
|
* notification; the IncidentEpisodePublicNote worker job sends it.
|
|
185
|
+
*
|
|
186
|
+
* Sending the note's 'posted' notification again - its status written back
|
|
187
|
+
* to Pending, as the dashboard's Retry does - needs the permission to post
|
|
188
|
+
* a note that notifies subscribers, and a note whose notification can go
|
|
189
|
+
* out again (see SubscriberNotificationResendAccess). So does telling
|
|
190
|
+
* subscribers about an edit, which is also refused while that update
|
|
191
|
+
* notification is being sent.
|
|
184
192
|
*/
|
|
185
193
|
@CaptureSpan()
|
|
186
194
|
protected override async onBeforeUpdate(
|
|
187
195
|
updateBy: UpdateBy<Model>,
|
|
188
196
|
): Promise<OnUpdate<Model>> {
|
|
197
|
+
await SubscriberNotificationResendAccess.assertPublicNoteResendAllowed({
|
|
198
|
+
modelType: Model,
|
|
199
|
+
service: this,
|
|
200
|
+
updateBy: updateBy,
|
|
201
|
+
});
|
|
202
|
+
|
|
189
203
|
if (SubscriberUpdateNotification.isRequested(updateBy.miscDataProps)) {
|
|
190
204
|
updateBy.data.subscriberNotificationStatusOnNoteUpdated =
|
|
191
205
|
StatusPageSubscriberNotificationStatus.Pending;
|
|
@@ -193,6 +207,20 @@ export class Service extends DatabaseService<Model> {
|
|
|
193
207
|
SubscriberUpdateNotification.queuedMessage;
|
|
194
208
|
}
|
|
195
209
|
|
|
210
|
+
/*
|
|
211
|
+
* Telling subscribers about the edit - asked for above, or written as
|
|
212
|
+
* Pending directly (the dashboard's Retry of a failed update) - needs
|
|
213
|
+
* the permission to post a note that notifies subscribers too, and is
|
|
214
|
+
* refused while the update notification is being sent.
|
|
215
|
+
*/
|
|
216
|
+
await SubscriberNotificationResendAccess.assertPublicNoteUpdateNotificationAllowed(
|
|
217
|
+
{
|
|
218
|
+
modelType: Model,
|
|
219
|
+
service: this,
|
|
220
|
+
updateBy: updateBy,
|
|
221
|
+
},
|
|
222
|
+
);
|
|
223
|
+
|
|
196
224
|
return {
|
|
197
225
|
updateBy: updateBy,
|
|
198
226
|
carryForward: null,
|
|
@@ -15,6 +15,7 @@ import IncidentState from "../../Models/DatabaseModels/IncidentState";
|
|
|
15
15
|
import IncidentSeverity from "../../Models/DatabaseModels/IncidentSeverity";
|
|
16
16
|
import SortOrder from "../../Types/BaseDatabase/SortOrder";
|
|
17
17
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
18
|
+
import SubscriberNotificationResendAccess from "../Utils/StatusPage/SubscriberNotificationResendAccess";
|
|
18
19
|
import logger, { LogAttributes } from "../Utils/Logger";
|
|
19
20
|
import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
20
21
|
import IncidentEpisodeStateTimeline from "../../Models/DatabaseModels/IncidentEpisodeStateTimeline";
|
|
@@ -95,6 +96,20 @@ export class Service extends DatabaseService<Model> {
|
|
|
95
96
|
updateBy.props,
|
|
96
97
|
);
|
|
97
98
|
|
|
99
|
+
/*
|
|
100
|
+
* Sending the episode's created notification again while it is being
|
|
101
|
+
* sent would let a second run send it alongside, or be overwritten when
|
|
102
|
+
* the send settles (SubscriberNotificationResendAccess). No user role may
|
|
103
|
+
* write its status (update: []), so this only ever stops a master admin;
|
|
104
|
+
* it is here so the rule holds for every notification the same way.
|
|
105
|
+
*/
|
|
106
|
+
await SubscriberNotificationResendAccess.assertNotQueuedWhileBeingSent({
|
|
107
|
+
modelType: Model,
|
|
108
|
+
service: this,
|
|
109
|
+
updateBy: updateBy,
|
|
110
|
+
statusColumns: ["subscriberNotificationStatusOnEpisodeCreated"],
|
|
111
|
+
});
|
|
112
|
+
|
|
98
113
|
/*
|
|
99
114
|
* An episode carries the same project-scoped state and severity an
|
|
100
115
|
* incident does, on FKs that are equally ON DELETE NO ACTION, so an id
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import CreateBy from "../Types/Database/CreateBy";
|
|
2
2
|
import DeleteBy from "../Types/Database/DeleteBy";
|
|
3
|
-
import { OnCreate, OnDelete } from "../Types/Database/Hooks";
|
|
3
|
+
import { OnCreate, OnDelete, OnUpdate } from "../Types/Database/Hooks";
|
|
4
|
+
import UpdateBy from "../Types/Database/UpdateBy";
|
|
4
5
|
import QueryHelper from "../Types/Database/QueryHelper";
|
|
5
6
|
import DatabaseService from "./DatabaseService";
|
|
6
7
|
import IncidentStateService from "./IncidentStateService";
|
|
@@ -15,6 +16,7 @@ import IncidentEpisode from "../../Models/DatabaseModels/IncidentEpisode";
|
|
|
15
16
|
import IncidentEpisodeStateTimeline from "../../Models/DatabaseModels/IncidentEpisodeStateTimeline";
|
|
16
17
|
import { IsBillingEnabled } from "../EnvironmentConfig";
|
|
17
18
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
19
|
+
import SubscriberNotificationResendAccess from "../Utils/StatusPage/SubscriberNotificationResendAccess";
|
|
18
20
|
import logger, { LogAttributes } from "../Utils/Logger";
|
|
19
21
|
import IncidentEpisodeFeedService from "./IncidentEpisodeFeedService";
|
|
20
22
|
import { IncidentEpisodeFeedEventType } from "../../Models/DatabaseModels/IncidentEpisodeFeed";
|
|
@@ -474,6 +476,30 @@ export class Service extends DatabaseService<IncidentEpisodeStateTimeline> {
|
|
|
474
476
|
return createdItem;
|
|
475
477
|
}
|
|
476
478
|
|
|
479
|
+
/*
|
|
480
|
+
* Sending a state change notification again while it is being sent would
|
|
481
|
+
* let a second run send it alongside, or be overwritten when the send
|
|
482
|
+
* settles (SubscriberNotificationResendAccess). No user role may write its
|
|
483
|
+
* status (update: []), so this only ever stops a master admin; it is here
|
|
484
|
+
* so the rule holds for every notification the same way.
|
|
485
|
+
*/
|
|
486
|
+
@CaptureSpan()
|
|
487
|
+
protected override async onBeforeUpdate(
|
|
488
|
+
updateBy: UpdateBy<IncidentEpisodeStateTimeline>,
|
|
489
|
+
): Promise<OnUpdate<IncidentEpisodeStateTimeline>> {
|
|
490
|
+
await SubscriberNotificationResendAccess.assertNotQueuedWhileBeingSent({
|
|
491
|
+
modelType: IncidentEpisodeStateTimeline,
|
|
492
|
+
service: this,
|
|
493
|
+
updateBy: updateBy,
|
|
494
|
+
statusColumns: ["subscriberNotificationStatus"],
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
return {
|
|
498
|
+
updateBy: updateBy,
|
|
499
|
+
carryForward: null,
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
|
|
477
503
|
@CaptureSpan()
|
|
478
504
|
protected override async onBeforeDelete(
|
|
479
505
|
deleteBy: DeleteBy<IncidentEpisodeStateTimeline>,
|
|
@@ -19,6 +19,7 @@ import Query from "../Types/Database/Query";
|
|
|
19
19
|
import File from "../../Models/DatabaseModels/File";
|
|
20
20
|
import FileAttachmentMarkdownUtil from "../Utils/FileAttachmentMarkdownUtil";
|
|
21
21
|
import { syncIsPublicForMarkdownImages } from "../Utils/InlineImageAccessTokenSync";
|
|
22
|
+
import SubscriberNotificationResendAccess from "../Utils/StatusPage/SubscriberNotificationResendAccess";
|
|
22
23
|
|
|
23
24
|
export class Service extends DatabaseService<Model> {
|
|
24
25
|
public constructor() {
|
|
@@ -178,11 +179,24 @@ export class Service extends DatabaseService<Model> {
|
|
|
178
179
|
* An edit tells subscribers nothing unless the editor asked for it on this
|
|
179
180
|
* edit (see SubscriberUpdateNotification). When they did, queue the update
|
|
180
181
|
* notification; the IncidentPublicNote worker job sends it.
|
|
182
|
+
*
|
|
183
|
+
* Sending the note's 'posted' notification again - its status written back
|
|
184
|
+
* to Pending, as the dashboard's Retry does - needs the permission to post
|
|
185
|
+
* a note that notifies subscribers, and a note whose notification can go
|
|
186
|
+
* out again (see SubscriberNotificationResendAccess). So does telling
|
|
187
|
+
* subscribers about an edit, which is also refused while that update
|
|
188
|
+
* notification is being sent.
|
|
181
189
|
*/
|
|
182
190
|
@CaptureSpan()
|
|
183
191
|
protected override async onBeforeUpdate(
|
|
184
192
|
updateBy: UpdateBy<Model>,
|
|
185
193
|
): Promise<OnUpdate<Model>> {
|
|
194
|
+
await SubscriberNotificationResendAccess.assertPublicNoteResendAllowed({
|
|
195
|
+
modelType: Model,
|
|
196
|
+
service: this,
|
|
197
|
+
updateBy: updateBy,
|
|
198
|
+
});
|
|
199
|
+
|
|
186
200
|
if (SubscriberUpdateNotification.isRequested(updateBy.miscDataProps)) {
|
|
187
201
|
updateBy.data.subscriberNotificationStatusOnNoteUpdated =
|
|
188
202
|
StatusPageSubscriberNotificationStatus.Pending;
|
|
@@ -190,6 +204,20 @@ export class Service extends DatabaseService<Model> {
|
|
|
190
204
|
SubscriberUpdateNotification.queuedMessage;
|
|
191
205
|
}
|
|
192
206
|
|
|
207
|
+
/*
|
|
208
|
+
* Telling subscribers about the edit - asked for above, or written as
|
|
209
|
+
* Pending directly (the dashboard's Retry of a failed update) - needs
|
|
210
|
+
* the permission to post a note that notifies subscribers too, and is
|
|
211
|
+
* refused while the update notification is being sent.
|
|
212
|
+
*/
|
|
213
|
+
await SubscriberNotificationResendAccess.assertPublicNoteUpdateNotificationAllowed(
|
|
214
|
+
{
|
|
215
|
+
modelType: Model,
|
|
216
|
+
service: this,
|
|
217
|
+
updateBy: updateBy,
|
|
218
|
+
},
|
|
219
|
+
);
|
|
220
|
+
|
|
193
221
|
return {
|
|
194
222
|
updateBy: updateBy,
|
|
195
223
|
carryForward: null,
|
|
@@ -300,7 +328,8 @@ ${(createdItem.note || "") + attachmentsMarkdown}
|
|
|
300
328
|
await IncidentFeedService.createIncidentFeedItem({
|
|
301
329
|
incidentId: updatedItem.incidentId!,
|
|
302
330
|
projectId: updatedItem.projectId!,
|
|
303
|
-
|
|
331
|
+
// An edit to a public note is a public note event, not a private one.
|
|
332
|
+
incidentFeedEventType: IncidentFeedEventType.PublicNote,
|
|
304
333
|
displayColor: Blue500,
|
|
305
334
|
userId: userId || undefined,
|
|
306
335
|
|