@oneuptime/common 9.4.11 → 9.4.13
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 +77 -0
- package/Models/DatabaseModels/IncidentEpisode.ts +1223 -0
- package/Models/DatabaseModels/IncidentEpisodeFeed.ts +533 -0
- package/Models/DatabaseModels/IncidentEpisodeInternalNote.ts +456 -0
- package/Models/DatabaseModels/IncidentEpisodeMember.ts +587 -0
- package/Models/DatabaseModels/IncidentEpisodeOwnerTeam.ts +421 -0
- package/Models/DatabaseModels/IncidentEpisodeOwnerUser.ts +419 -0
- package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +524 -0
- package/Models/DatabaseModels/IncidentGroupingRule.ts +1430 -0
- package/Models/DatabaseModels/Index.ts +18 -0
- package/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.ts +70 -0
- package/Models/DatabaseModels/OnCallDutyPolicyExecutionLogTimeline.ts +59 -0
- package/Models/DatabaseModels/Project.ts +29 -0
- package/Models/DatabaseModels/UserOnCallLog.ts +48 -0
- package/Models/DatabaseModels/UserOnCallLogTimeline.ts +49 -0
- package/Models/DatabaseModels/WorkspaceNotificationLog.ts +57 -0
- package/Server/API/BillingAPI.ts +78 -1
- package/Server/API/IncidentEpisodeAPI.ts +150 -0
- package/Server/API/SlackAPI.ts +23 -0
- package/Server/API/UserOnCallLogTimelineAPI.ts +24 -4
- package/Server/BillingConfig.ts +3 -0
- package/Server/EnvironmentConfig.ts +1 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1769599843642-MigrationName.ts +29 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1769626069479-MigrationName.ts +729 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1769629928240-MigrationName.ts +261 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1769676117342-RenameEvaluateOverTimeInCriteriaFilter.ts +28 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Services/AIBillingService.ts +10 -0
- package/Server/Services/BillingService.ts +349 -1
- package/Server/Services/CallService.ts +1 -0
- package/Server/Services/IncidentEpisodeFeedService.ts +94 -0
- package/Server/Services/IncidentEpisodeInternalNoteService.ts +71 -0
- package/Server/Services/IncidentEpisodeMemberService.ts +321 -0
- package/Server/Services/IncidentEpisodeOwnerTeamService.ts +10 -0
- package/Server/Services/IncidentEpisodeOwnerUserService.ts +10 -0
- package/Server/Services/IncidentEpisodeService.ts +1045 -0
- package/Server/Services/IncidentEpisodeStateTimelineService.ts +566 -0
- package/Server/Services/IncidentGroupingEngineService.ts +1047 -0
- package/Server/Services/IncidentGroupingRuleService.ts +14 -0
- package/Server/Services/IncidentService.ts +11 -0
- package/Server/Services/Index.ts +18 -0
- package/Server/Services/MailService.ts +1 -0
- package/Server/Services/MonitorService.ts +9 -0
- package/Server/Services/NotificationService.ts +10 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +18 -0
- package/Server/Services/OnCallDutyPolicyExecutionLogService.ts +64 -2
- package/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.ts +26 -1
- package/Server/Services/OnCallDutyPolicyService.ts +15 -0
- package/Server/Services/ProjectService.ts +33 -2
- package/Server/Services/SmsService.ts +1 -0
- package/Server/Services/UserNotificationRuleService.ts +48 -2
- package/Server/Services/UserNotificationSettingService.ts +23 -0
- package/Server/Services/UserOnCallLogService.ts +41 -4
- package/Server/Services/UserService.ts +45 -1
- package/Server/Services/WhatsAppService.ts +1 -0
- package/Server/Services/WorkspaceNotificationLogService.ts +16 -0
- package/Server/Services/WorkspaceNotificationRuleService.ts +116 -0
- package/Server/Types/Database/Permissions/TenantPermission.ts +20 -0
- package/Server/Utils/AI/IncidentEpisodeAIContextBuilder.ts +490 -0
- package/Server/Utils/Monitor/Criteria/APIRequestCriteria.ts +1 -1
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +1 -1
- package/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.ts +1 -1
- package/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.ts +1 -1
- package/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.ts +2 -2
- package/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.ts +182 -0
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +13 -0
- package/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.ts +1 -1
- package/Server/Utils/Monitor/MonitorTemplateUtil.ts +37 -0
- package/Server/Utils/PushNotificationUtil.ts +31 -0
- package/Server/Utils/WhatsAppTemplateUtil.ts +14 -0
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/ActionTypes.ts +18 -0
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +702 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +20 -0
- package/Server/Utils/Workspace/Slack/Actions/ActionTypes.ts +11 -0
- package/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.ts +918 -0
- package/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.ts +120 -0
- package/Server/Utils/Workspace/WorkspaceMessages/IncidentEpisode.ts +74 -0
- package/Types/Email/EmailTemplateType.ts +7 -0
- package/Types/Monitor/CriteriaFilter.ts +24 -4
- package/Types/Monitor/MonitorCriteriaInstance.ts +67 -0
- package/Types/Monitor/MonitorStep.ts +37 -0
- package/Types/Monitor/MonitorStepSnmpMonitor.ts +102 -0
- package/Types/Monitor/MonitorType.ts +15 -2
- package/Types/Monitor/SnmpMonitor/SnmpAuthProtocol.ts +8 -0
- package/Types/Monitor/SnmpMonitor/SnmpDataType.ts +21 -0
- package/Types/Monitor/SnmpMonitor/SnmpMonitorResponse.ts +16 -0
- package/Types/Monitor/SnmpMonitor/SnmpOid.ts +60 -0
- package/Types/Monitor/SnmpMonitor/SnmpPrivProtocol.ts +7 -0
- package/Types/Monitor/SnmpMonitor/SnmpSecurityLevel.ts +7 -0
- package/Types/Monitor/SnmpMonitor/SnmpV3Auth.ts +12 -0
- package/Types/Monitor/SnmpMonitor/SnmpVersion.ts +7 -0
- package/Types/NotificationSetting/NotificationSettingEventType.ts +7 -0
- package/Types/Permission.ts +311 -0
- package/Types/Probe/ProbeMonitorResponse.ts +2 -0
- package/Types/UserNotification/UserNotificationEventType.ts +1 -0
- package/Types/WhatsApp/WhatsAppTemplates.ts +24 -0
- package/Types/Workspace/NotificationRules/EventType.ts +1 -0
- package/Types/Workspace/NotificationRules/NotificationRuleCondition.ts +38 -1
- package/Utils/Monitor/MonitorMetricType.ts +2 -1
- package/build/dist/Models/DatabaseModels/Incident.js +78 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js +1250 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeFeed.js +555 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeFeed.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeInternalNote.js +467 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeInternalNote.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js +607 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeOwnerTeam.js +437 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeOwnerTeam.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeOwnerUser.js +436 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeOwnerUser.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +546 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentGroupingRule.js +1437 -0
- package/build/dist/Models/DatabaseModels/IncidentGroupingRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Index.js +16 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js +69 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLogTimeline.js +58 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLogTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +30 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLog.js +47 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +48 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceNotificationLog.js +58 -0
- package/build/dist/Models/DatabaseModels/WorkspaceNotificationLog.js.map +1 -1
- package/build/dist/Server/API/BillingAPI.js +44 -1
- package/build/dist/Server/API/BillingAPI.js.map +1 -1
- package/build/dist/Server/API/IncidentEpisodeAPI.js +97 -0
- package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -0
- package/build/dist/Server/API/SlackAPI.js +18 -0
- package/build/dist/Server/API/SlackAPI.js.map +1 -1
- package/build/dist/Server/API/UserOnCallLogTimelineAPI.js +30 -10
- package/build/dist/Server/API/UserOnCallLogTimelineAPI.js.map +1 -1
- package/build/dist/Server/BillingConfig.js +2 -0
- package/build/dist/Server/BillingConfig.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +1 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769599843642-MigrationName.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769599843642-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769626069479-MigrationName.js +256 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769626069479-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769629928240-MigrationName.js +96 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769629928240-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769676117342-RenameEvaluateOverTimeInCriteriaFilter.js +25 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769676117342-RenameEvaluateOverTimeInCriteriaFilter.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AIBillingService.js +10 -1
- package/build/dist/Server/Services/AIBillingService.js.map +1 -1
- package/build/dist/Server/Services/BillingService.js +224 -5
- package/build/dist/Server/Services/BillingService.js.map +1 -1
- package/build/dist/Server/Services/CallService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeFeedService.js +83 -0
- package/build/dist/Server/Services/IncidentEpisodeFeedService.js.map +1 -0
- package/build/dist/Server/Services/IncidentEpisodeInternalNoteService.js +70 -0
- package/build/dist/Server/Services/IncidentEpisodeInternalNoteService.js.map +1 -0
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js +298 -0
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -0
- package/build/dist/Server/Services/IncidentEpisodeOwnerTeamService.js +9 -0
- package/build/dist/Server/Services/IncidentEpisodeOwnerTeamService.js.map +1 -0
- package/build/dist/Server/Services/IncidentEpisodeOwnerUserService.js +9 -0
- package/build/dist/Server/Services/IncidentEpisodeOwnerUserService.js.map +1 -0
- package/build/dist/Server/Services/IncidentEpisodeService.js +933 -0
- package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -0
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +498 -0
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -0
- package/build/dist/Server/Services/IncidentGroupingEngineService.js +799 -0
- package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -0
- package/build/dist/Server/Services/IncidentGroupingRuleService.js +13 -0
- package/build/dist/Server/Services/IncidentGroupingRuleService.js.map +1 -0
- package/build/dist/Server/Services/IncidentService.js +10 -0
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +16 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/MailService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +9 -1
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/NotificationService.js +10 -1
- package/build/dist/Server/Services/NotificationService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +10 -0
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js +48 -2
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js +20 -1
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyService.js +8 -0
- package/build/dist/Server/Services/OnCallDutyPolicyService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +16 -3
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/SmsService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +39 -2
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationSettingService.js +9 -0
- package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
- package/build/dist/Server/Services/UserOnCallLogService.js +35 -3
- package/build/dist/Server/Services/UserOnCallLogService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +40 -0
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Services/WhatsAppService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationLogService.js +12 -0
- package/build/dist/Server/Services/WorkspaceNotificationLogService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +95 -1
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +17 -0
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
- package/build/dist/Server/Utils/AI/IncidentEpisodeAIContextBuilder.js +402 -0
- package/build/dist/Server/Utils/AI/IncidentEpisodeAIContextBuilder.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js +2 -2
- package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js +135 -0
- package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +10 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +26 -0
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
- package/build/dist/Server/Utils/PushNotificationUtil.js +20 -0
- package/build/dist/Server/Utils/PushNotificationUtil.js.map +1 -1
- package/build/dist/Server/Utils/WhatsAppTemplateUtil.js +8 -0
- package/build/dist/Server/Utils/WhatsAppTemplateUtil.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ActionTypes.js +17 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ActionTypes.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +547 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +15 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js +10 -0
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js +651 -0
- package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.js +100 -0
- package/build/dist/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/WorkspaceMessages/IncidentEpisode.js +70 -0
- package/build/dist/Server/Utils/Workspace/WorkspaceMessages/IncidentEpisode.js.map +1 -0
- package/build/dist/Types/Email/EmailTemplateType.js +6 -0
- package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
- package/build/dist/Types/Monitor/CriteriaFilter.js +16 -3
- package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +62 -0
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +26 -0
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepSnmpMonitor.js +77 -0
- package/build/dist/Types/Monitor/MonitorStepSnmpMonitor.js.map +1 -0
- package/build/dist/Types/Monitor/MonitorType.js +13 -2
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/Types/Monitor/SnmpMonitor/SnmpAuthProtocol.js +9 -0
- package/build/dist/Types/Monitor/SnmpMonitor/SnmpAuthProtocol.js.map +1 -0
- package/build/dist/Types/Monitor/SnmpMonitor/SnmpDataType.js +22 -0
- package/build/dist/Types/Monitor/SnmpMonitor/SnmpDataType.js.map +1 -0
- package/build/dist/Types/Monitor/SnmpMonitor/SnmpMonitorResponse.js +2 -0
- package/build/dist/Types/Monitor/SnmpMonitor/SnmpMonitorResponse.js.map +1 -0
- package/build/dist/Types/Monitor/SnmpMonitor/SnmpOid.js +55 -0
- package/build/dist/Types/Monitor/SnmpMonitor/SnmpOid.js.map +1 -0
- package/build/dist/Types/Monitor/SnmpMonitor/SnmpPrivProtocol.js +8 -0
- package/build/dist/Types/Monitor/SnmpMonitor/SnmpPrivProtocol.js.map +1 -0
- package/build/dist/Types/Monitor/SnmpMonitor/SnmpSecurityLevel.js +8 -0
- package/build/dist/Types/Monitor/SnmpMonitor/SnmpSecurityLevel.js.map +1 -0
- package/build/dist/Types/Monitor/SnmpMonitor/SnmpV3Auth.js +2 -0
- package/build/dist/Types/Monitor/SnmpMonitor/SnmpV3Auth.js.map +1 -0
- package/build/dist/Types/Monitor/SnmpMonitor/SnmpVersion.js +8 -0
- package/build/dist/Types/Monitor/SnmpMonitor/SnmpVersion.js.map +1 -0
- package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js +5 -0
- package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js.map +1 -1
- package/build/dist/Types/Permission.js +264 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/UserNotification/UserNotificationEventType.js +1 -0
- package/build/dist/Types/UserNotification/UserNotificationEventType.js.map +1 -1
- package/build/dist/Types/WhatsApp/WhatsAppTemplates.js +15 -0
- package/build/dist/Types/WhatsApp/WhatsAppTemplates.js.map +1 -1
- package/build/dist/Types/Workspace/NotificationRules/EventType.js +1 -0
- package/build/dist/Types/Workspace/NotificationRules/EventType.js.map +1 -1
- package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js +33 -1
- package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js.map +1 -1
- package/build/dist/Utils/Monitor/MonitorMetricType.js +2 -1
- package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,1045 @@
|
|
|
1
|
+
import CreateBy from "../Types/Database/CreateBy";
|
|
2
|
+
import { OnCreate } from "../Types/Database/Hooks";
|
|
3
|
+
import DatabaseService from "./DatabaseService";
|
|
4
|
+
import IncidentStateService from "./IncidentStateService";
|
|
5
|
+
import BadDataException from "../../Types/Exception/BadDataException";
|
|
6
|
+
import ObjectID from "../../Types/ObjectID";
|
|
7
|
+
import PositiveNumber from "../../Types/PositiveNumber";
|
|
8
|
+
import Model from "../../Models/DatabaseModels/IncidentEpisode";
|
|
9
|
+
import IncidentState from "../../Models/DatabaseModels/IncidentState";
|
|
10
|
+
import IncidentSeverity from "../../Models/DatabaseModels/IncidentSeverity";
|
|
11
|
+
import SortOrder from "../../Types/BaseDatabase/SortOrder";
|
|
12
|
+
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
13
|
+
import logger from "../Utils/Logger";
|
|
14
|
+
import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
15
|
+
import IncidentEpisodeStateTimeline from "../../Models/DatabaseModels/IncidentEpisodeStateTimeline";
|
|
16
|
+
import IncidentEpisodeStateTimelineService from "./IncidentEpisodeStateTimelineService";
|
|
17
|
+
import { IsBillingEnabled } from "../EnvironmentConfig";
|
|
18
|
+
import OneUptimeDate from "../../Types/Date";
|
|
19
|
+
import IncidentEpisodeFeedService from "./IncidentEpisodeFeedService";
|
|
20
|
+
import { IncidentEpisodeFeedEventType } from "../../Models/DatabaseModels/IncidentEpisodeFeed";
|
|
21
|
+
import { Red500, Yellow500, Purple500 } from "../../Types/BrandColors";
|
|
22
|
+
import URL from "../../Types/API/URL";
|
|
23
|
+
import DatabaseConfig from "../DatabaseConfig";
|
|
24
|
+
import IncidentSeverityService from "./IncidentSeverityService";
|
|
25
|
+
import IncidentEpisodeMemberService from "./IncidentEpisodeMemberService";
|
|
26
|
+
import IncidentEpisodeOwnerUserService from "./IncidentEpisodeOwnerUserService";
|
|
27
|
+
import IncidentEpisodeOwnerTeamService from "./IncidentEpisodeOwnerTeamService";
|
|
28
|
+
import TeamMemberService from "./TeamMemberService";
|
|
29
|
+
import IncidentEpisodeOwnerUser from "../../Models/DatabaseModels/IncidentEpisodeOwnerUser";
|
|
30
|
+
import IncidentEpisodeOwnerTeam from "../../Models/DatabaseModels/IncidentEpisodeOwnerTeam";
|
|
31
|
+
import IncidentEpisodeMember from "../../Models/DatabaseModels/IncidentEpisodeMember";
|
|
32
|
+
import User from "../../Models/DatabaseModels/User";
|
|
33
|
+
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
34
|
+
import NotificationRuleWorkspaceChannel from "../../Types/Workspace/NotificationRules/NotificationRuleWorkspaceChannel";
|
|
35
|
+
import WorkspaceType from "../../Types/Workspace/WorkspaceType";
|
|
36
|
+
import IncidentService from "./IncidentService";
|
|
37
|
+
import Semaphore, { SemaphoreMutex } from "../Infrastructure/Semaphore";
|
|
38
|
+
import OnCallDutyPolicyService from "./OnCallDutyPolicyService";
|
|
39
|
+
import OnCallDutyPolicy from "../../Models/DatabaseModels/OnCallDutyPolicy";
|
|
40
|
+
import UserNotificationEventType from "../../Types/UserNotification/UserNotificationEventType";
|
|
41
|
+
|
|
42
|
+
export class Service extends DatabaseService<Model> {
|
|
43
|
+
public constructor() {
|
|
44
|
+
super(Model);
|
|
45
|
+
if (IsBillingEnabled) {
|
|
46
|
+
this.hardDeleteItemsOlderThanInDays("createdAt", 3 * 365); // 3 years
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@CaptureSpan()
|
|
51
|
+
public async getExistingEpisodeNumberForProject(data: {
|
|
52
|
+
projectId: ObjectID;
|
|
53
|
+
}): Promise<number> {
|
|
54
|
+
const lastEpisode: Model | null = await this.findOneBy({
|
|
55
|
+
query: {
|
|
56
|
+
projectId: data.projectId,
|
|
57
|
+
},
|
|
58
|
+
select: {
|
|
59
|
+
episodeNumber: true,
|
|
60
|
+
},
|
|
61
|
+
sort: {
|
|
62
|
+
episodeNumber: SortOrder.Descending,
|
|
63
|
+
},
|
|
64
|
+
props: {
|
|
65
|
+
isRoot: true,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (!lastEpisode) {
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return lastEpisode.episodeNumber ? Number(lastEpisode.episodeNumber) : 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@CaptureSpan()
|
|
77
|
+
protected override async onBeforeCreate(
|
|
78
|
+
createBy: CreateBy<Model>,
|
|
79
|
+
): Promise<OnCreate<Model>> {
|
|
80
|
+
if (!createBy.props.tenantId && !createBy.props.isRoot) {
|
|
81
|
+
throw new BadDataException(
|
|
82
|
+
"ProjectId required to create incident episode.",
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const projectId: ObjectID =
|
|
87
|
+
createBy.props.tenantId || createBy.data.projectId!;
|
|
88
|
+
|
|
89
|
+
let mutex: SemaphoreMutex | null = null;
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
// Acquire mutex to prevent race conditions when generating episode numbers
|
|
93
|
+
try {
|
|
94
|
+
mutex = await Semaphore.lock({
|
|
95
|
+
key: projectId.toString(),
|
|
96
|
+
namespace: "IncidentEpisode.create",
|
|
97
|
+
});
|
|
98
|
+
} catch (err) {
|
|
99
|
+
logger.error(err);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Get the created state for episodes
|
|
103
|
+
const incidentState: IncidentState | null =
|
|
104
|
+
await IncidentStateService.findOneBy({
|
|
105
|
+
query: {
|
|
106
|
+
projectId: projectId,
|
|
107
|
+
isCreatedState: true,
|
|
108
|
+
},
|
|
109
|
+
select: {
|
|
110
|
+
_id: true,
|
|
111
|
+
},
|
|
112
|
+
props: {
|
|
113
|
+
isRoot: true,
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
if (!incidentState || !incidentState.id) {
|
|
118
|
+
throw new BadDataException(
|
|
119
|
+
"Created incident state not found for this project. Please add created incident state from settings.",
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
createBy.data.currentIncidentStateId = incidentState.id;
|
|
124
|
+
|
|
125
|
+
// Auto-generate episode number
|
|
126
|
+
const episodeNumberForThisEpisode: number =
|
|
127
|
+
(await this.getExistingEpisodeNumberForProject({
|
|
128
|
+
projectId: projectId,
|
|
129
|
+
})) + 1;
|
|
130
|
+
|
|
131
|
+
createBy.data.episodeNumber = episodeNumberForThisEpisode;
|
|
132
|
+
|
|
133
|
+
// Set initial lastIncidentAddedAt
|
|
134
|
+
if (!createBy.data.lastIncidentAddedAt) {
|
|
135
|
+
createBy.data.lastIncidentAddedAt = OneUptimeDate.getCurrentDate();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return { createBy, carryForward: { mutex } };
|
|
139
|
+
} catch (error) {
|
|
140
|
+
// Release the mutex if it was acquired and an error occurred
|
|
141
|
+
if (mutex) {
|
|
142
|
+
try {
|
|
143
|
+
await Semaphore.release(mutex);
|
|
144
|
+
} catch (err) {
|
|
145
|
+
logger.error(err);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@CaptureSpan()
|
|
153
|
+
protected override async onCreateSuccess(
|
|
154
|
+
onCreate: OnCreate<Model>,
|
|
155
|
+
createdItem: Model,
|
|
156
|
+
): Promise<Model> {
|
|
157
|
+
// Release the mutex acquired in onBeforeCreate
|
|
158
|
+
const mutex: SemaphoreMutex | null = onCreate.carryForward?.mutex || null;
|
|
159
|
+
if (mutex) {
|
|
160
|
+
try {
|
|
161
|
+
await Semaphore.release(mutex);
|
|
162
|
+
} catch (err) {
|
|
163
|
+
logger.error(err);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (!createdItem.projectId) {
|
|
168
|
+
throw new BadDataException("projectId is required");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (!createdItem.id) {
|
|
172
|
+
throw new BadDataException("id is required");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!createdItem.currentIncidentStateId) {
|
|
176
|
+
throw new BadDataException("currentIncidentStateId is required");
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Create initial state timeline entry
|
|
180
|
+
Promise.resolve()
|
|
181
|
+
.then(async () => {
|
|
182
|
+
try {
|
|
183
|
+
await this.changeEpisodeState({
|
|
184
|
+
projectId: createdItem.projectId!,
|
|
185
|
+
episodeId: createdItem.id!,
|
|
186
|
+
incidentStateId: createdItem.currentIncidentStateId!,
|
|
187
|
+
notifyOwners: false,
|
|
188
|
+
rootCause: undefined,
|
|
189
|
+
props: {
|
|
190
|
+
isRoot: true,
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
} catch (error) {
|
|
194
|
+
logger.error(
|
|
195
|
+
`Handle episode state change failed in IncidentEpisodeService.onCreateSuccess: ${error}`,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
.then(async () => {
|
|
200
|
+
try {
|
|
201
|
+
await this.createEpisodeCreatedFeed(createdItem);
|
|
202
|
+
} catch (error) {
|
|
203
|
+
logger.error(
|
|
204
|
+
`Create episode feed failed in IncidentEpisodeService.onCreateSuccess: ${error}`,
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
})
|
|
208
|
+
.then(async () => {
|
|
209
|
+
// Execute on-call duty policies
|
|
210
|
+
try {
|
|
211
|
+
await this.executeEpisodeOnCallDutyPoliciesAsync(createdItem);
|
|
212
|
+
} catch (error) {
|
|
213
|
+
logger.error(
|
|
214
|
+
`On-call duty policy execution failed in IncidentEpisodeService.onCreateSuccess: ${error}`,
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
})
|
|
218
|
+
.catch((error: Error) => {
|
|
219
|
+
logger.error(
|
|
220
|
+
`Critical error in IncidentEpisodeService.onCreateSuccess: ${error}`,
|
|
221
|
+
);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
return createdItem;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
@CaptureSpan()
|
|
228
|
+
private async createEpisodeCreatedFeed(episode: Model): Promise<void> {
|
|
229
|
+
if (!episode.id || !episode.projectId) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
let feedInfoInMarkdown: string = `#### Episode ${episode.episodeNumber?.toString()} Created
|
|
234
|
+
|
|
235
|
+
**${episode.title || "No title provided."}**
|
|
236
|
+
|
|
237
|
+
`;
|
|
238
|
+
|
|
239
|
+
if (episode.description) {
|
|
240
|
+
feedInfoInMarkdown += `${episode.description}\n\n`;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (episode.isManuallyCreated) {
|
|
244
|
+
feedInfoInMarkdown += `This episode was manually created.\n\n`;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
await IncidentEpisodeFeedService.createIncidentEpisodeFeedItem({
|
|
248
|
+
incidentEpisodeId: episode.id,
|
|
249
|
+
projectId: episode.projectId,
|
|
250
|
+
incidentEpisodeFeedEventType: IncidentEpisodeFeedEventType.EpisodeCreated,
|
|
251
|
+
displayColor: Red500,
|
|
252
|
+
feedInfoInMarkdown: feedInfoInMarkdown,
|
|
253
|
+
userId: episode.createdByUserId || undefined,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
@CaptureSpan()
|
|
258
|
+
private async executeEpisodeOnCallDutyPoliciesAsync(
|
|
259
|
+
createdItem: Model,
|
|
260
|
+
): Promise<void> {
|
|
261
|
+
if (!createdItem.id || !createdItem.projectId) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
try {
|
|
266
|
+
// Fetch the episode with on-call duty policies since they may not be loaded
|
|
267
|
+
const episodeWithPolicies: Model | null = await this.findOneById({
|
|
268
|
+
id: createdItem.id,
|
|
269
|
+
select: {
|
|
270
|
+
onCallDutyPolicies: {
|
|
271
|
+
_id: true,
|
|
272
|
+
name: true,
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
props: {
|
|
276
|
+
isRoot: true,
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
if (
|
|
281
|
+
!episodeWithPolicies?.onCallDutyPolicies?.length ||
|
|
282
|
+
episodeWithPolicies.onCallDutyPolicies.length === 0
|
|
283
|
+
) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Execute all on-call policies in parallel
|
|
288
|
+
const policyPromises: Promise<void>[] =
|
|
289
|
+
episodeWithPolicies.onCallDutyPolicies.map(
|
|
290
|
+
(policy: OnCallDutyPolicy) => {
|
|
291
|
+
return OnCallDutyPolicyService.executePolicy(
|
|
292
|
+
new ObjectID(policy._id as string),
|
|
293
|
+
{
|
|
294
|
+
triggeredByIncidentEpisodeId: createdItem.id!,
|
|
295
|
+
userNotificationEventType:
|
|
296
|
+
UserNotificationEventType.IncidentCreated,
|
|
297
|
+
},
|
|
298
|
+
);
|
|
299
|
+
},
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
await Promise.allSettled(policyPromises);
|
|
303
|
+
|
|
304
|
+
// Update the flag to indicate on-call policy has been executed
|
|
305
|
+
await this.updateOneById({
|
|
306
|
+
id: createdItem.id,
|
|
307
|
+
data: {
|
|
308
|
+
isOnCallPolicyExecuted: true,
|
|
309
|
+
},
|
|
310
|
+
props: {
|
|
311
|
+
isRoot: true,
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// Create feed entry for on-call policy execution
|
|
316
|
+
const policyNames: string[] = episodeWithPolicies.onCallDutyPolicies
|
|
317
|
+
.map((policy: OnCallDutyPolicy) => {
|
|
318
|
+
return policy.name || "Unnamed Policy";
|
|
319
|
+
})
|
|
320
|
+
.filter((name: string) => {
|
|
321
|
+
return Boolean(name);
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
let feedInfoInMarkdown: string = `#### On-Call Policy Executed\n\n`;
|
|
325
|
+
feedInfoInMarkdown += `The following on-call ${policyNames.length === 1 ? "policy has" : "policies have"} been executed for this episode:\n\n`;
|
|
326
|
+
|
|
327
|
+
for (const policyName of policyNames) {
|
|
328
|
+
feedInfoInMarkdown += `- ${policyName}\n`;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
await IncidentEpisodeFeedService.createIncidentEpisodeFeedItem({
|
|
332
|
+
incidentEpisodeId: createdItem.id,
|
|
333
|
+
projectId: createdItem.projectId,
|
|
334
|
+
incidentEpisodeFeedEventType: IncidentEpisodeFeedEventType.OnCallPolicy,
|
|
335
|
+
displayColor: Purple500,
|
|
336
|
+
feedInfoInMarkdown: feedInfoInMarkdown,
|
|
337
|
+
});
|
|
338
|
+
} catch (error) {
|
|
339
|
+
logger.error(`Error in executeEpisodeOnCallDutyPoliciesAsync: ${error}`);
|
|
340
|
+
throw error;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
@CaptureSpan()
|
|
345
|
+
public async changeEpisodeState(data: {
|
|
346
|
+
projectId: ObjectID;
|
|
347
|
+
episodeId: ObjectID;
|
|
348
|
+
incidentStateId: ObjectID;
|
|
349
|
+
notifyOwners: boolean;
|
|
350
|
+
rootCause: string | undefined;
|
|
351
|
+
props: DatabaseCommonInteractionProps;
|
|
352
|
+
cascadeToIncidents?: boolean;
|
|
353
|
+
}): Promise<void> {
|
|
354
|
+
const {
|
|
355
|
+
projectId,
|
|
356
|
+
episodeId,
|
|
357
|
+
incidentStateId,
|
|
358
|
+
notifyOwners,
|
|
359
|
+
rootCause,
|
|
360
|
+
props,
|
|
361
|
+
cascadeToIncidents,
|
|
362
|
+
} = data;
|
|
363
|
+
|
|
364
|
+
// Get last episode state timeline
|
|
365
|
+
const lastEpisodeStateTimeline: IncidentEpisodeStateTimeline | null =
|
|
366
|
+
await IncidentEpisodeStateTimelineService.findOneBy({
|
|
367
|
+
query: {
|
|
368
|
+
incidentEpisodeId: episodeId,
|
|
369
|
+
projectId: projectId,
|
|
370
|
+
},
|
|
371
|
+
select: {
|
|
372
|
+
_id: true,
|
|
373
|
+
incidentStateId: true,
|
|
374
|
+
},
|
|
375
|
+
sort: {
|
|
376
|
+
createdAt: SortOrder.Descending,
|
|
377
|
+
},
|
|
378
|
+
props: {
|
|
379
|
+
isRoot: true,
|
|
380
|
+
},
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
if (
|
|
384
|
+
lastEpisodeStateTimeline &&
|
|
385
|
+
lastEpisodeStateTimeline.incidentStateId &&
|
|
386
|
+
lastEpisodeStateTimeline.incidentStateId.toString() ===
|
|
387
|
+
incidentStateId.toString()
|
|
388
|
+
) {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const stateTimeline: IncidentEpisodeStateTimeline =
|
|
393
|
+
new IncidentEpisodeStateTimeline();
|
|
394
|
+
|
|
395
|
+
stateTimeline.incidentEpisodeId = episodeId;
|
|
396
|
+
stateTimeline.incidentStateId = incidentStateId;
|
|
397
|
+
stateTimeline.projectId = projectId;
|
|
398
|
+
stateTimeline.isOwnerNotified = !notifyOwners;
|
|
399
|
+
|
|
400
|
+
if (rootCause) {
|
|
401
|
+
stateTimeline.rootCause = rootCause;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
await IncidentEpisodeStateTimelineService.create({
|
|
405
|
+
data: stateTimeline,
|
|
406
|
+
props: props || {},
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
/*
|
|
410
|
+
* Note: resolvedAt is updated by IncidentEpisodeStateTimelineService.onCreateSuccess()
|
|
411
|
+
* to avoid duplicate updates.
|
|
412
|
+
*/
|
|
413
|
+
|
|
414
|
+
// Cascade state change to all member incidents if requested
|
|
415
|
+
if (cascadeToIncidents) {
|
|
416
|
+
await this.cascadeStateToMemberIncidents({
|
|
417
|
+
projectId,
|
|
418
|
+
episodeId,
|
|
419
|
+
incidentStateId,
|
|
420
|
+
props,
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
@CaptureSpan()
|
|
426
|
+
public async cascadeStateToMemberIncidents(data: {
|
|
427
|
+
projectId: ObjectID;
|
|
428
|
+
episodeId: ObjectID;
|
|
429
|
+
incidentStateId: ObjectID;
|
|
430
|
+
props: DatabaseCommonInteractionProps;
|
|
431
|
+
}): Promise<void> {
|
|
432
|
+
const { projectId, episodeId, incidentStateId, props } = data;
|
|
433
|
+
|
|
434
|
+
// Get all member incidents for this episode
|
|
435
|
+
const members: Array<IncidentEpisodeMember> =
|
|
436
|
+
await IncidentEpisodeMemberService.findBy({
|
|
437
|
+
query: {
|
|
438
|
+
incidentEpisodeId: episodeId,
|
|
439
|
+
projectId: projectId,
|
|
440
|
+
},
|
|
441
|
+
select: {
|
|
442
|
+
incidentId: true,
|
|
443
|
+
},
|
|
444
|
+
props: {
|
|
445
|
+
isRoot: true,
|
|
446
|
+
},
|
|
447
|
+
limit: LIMIT_PER_PROJECT,
|
|
448
|
+
skip: 0,
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
if (members.length === 0) {
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Update state for each member incident
|
|
456
|
+
for (const member of members) {
|
|
457
|
+
if (!member.incidentId) {
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
try {
|
|
462
|
+
await IncidentService.changeIncidentState({
|
|
463
|
+
projectId: projectId,
|
|
464
|
+
incidentId: member.incidentId,
|
|
465
|
+
incidentStateId: incidentStateId,
|
|
466
|
+
shouldNotifyStatusPageSubscribers: false,
|
|
467
|
+
isSubscribersNotified: false,
|
|
468
|
+
notifyOwners: false, // Don't send notifications for cascaded state changes
|
|
469
|
+
rootCause: "State changed by episode state cascade.",
|
|
470
|
+
stateChangeLog: undefined,
|
|
471
|
+
props: props,
|
|
472
|
+
});
|
|
473
|
+
} catch (error) {
|
|
474
|
+
logger.error(
|
|
475
|
+
`Failed to cascade state change to incident ${member.incidentId.toString()}: ${error}`,
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
@CaptureSpan()
|
|
482
|
+
public async acknowledgeEpisode(
|
|
483
|
+
episodeId: ObjectID,
|
|
484
|
+
acknowledgedByUserId?: ObjectID,
|
|
485
|
+
cascadeToIncidents: boolean = true,
|
|
486
|
+
): Promise<void> {
|
|
487
|
+
const episode: Model | null = await this.findOneById({
|
|
488
|
+
id: episodeId,
|
|
489
|
+
select: {
|
|
490
|
+
projectId: true,
|
|
491
|
+
},
|
|
492
|
+
props: {
|
|
493
|
+
isRoot: true,
|
|
494
|
+
},
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
if (!episode || !episode.projectId) {
|
|
498
|
+
throw new BadDataException("Episode not found.");
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
const incidentState: IncidentState | null =
|
|
502
|
+
await IncidentStateService.findOneBy({
|
|
503
|
+
query: {
|
|
504
|
+
projectId: episode.projectId,
|
|
505
|
+
isAcknowledgedState: true,
|
|
506
|
+
},
|
|
507
|
+
select: {
|
|
508
|
+
_id: true,
|
|
509
|
+
},
|
|
510
|
+
props: {
|
|
511
|
+
isRoot: true,
|
|
512
|
+
},
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
if (!incidentState || !incidentState.id) {
|
|
516
|
+
throw new BadDataException(
|
|
517
|
+
"Acknowledged incident state not found for this project.",
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
await this.changeEpisodeState({
|
|
522
|
+
projectId: episode.projectId,
|
|
523
|
+
episodeId: episodeId,
|
|
524
|
+
incidentStateId: incidentState.id,
|
|
525
|
+
notifyOwners: false,
|
|
526
|
+
rootCause: acknowledgedByUserId
|
|
527
|
+
? `Acknowledged by user.`
|
|
528
|
+
: "Acknowledged via API.",
|
|
529
|
+
props: {
|
|
530
|
+
isRoot: true,
|
|
531
|
+
userId: acknowledgedByUserId,
|
|
532
|
+
},
|
|
533
|
+
cascadeToIncidents: cascadeToIncidents,
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
@CaptureSpan()
|
|
538
|
+
public async resolveEpisode(
|
|
539
|
+
episodeId: ObjectID,
|
|
540
|
+
resolvedByUserId?: ObjectID,
|
|
541
|
+
cascadeToIncidents: boolean = true,
|
|
542
|
+
): Promise<void> {
|
|
543
|
+
const episode: Model | null = await this.findOneById({
|
|
544
|
+
id: episodeId,
|
|
545
|
+
select: {
|
|
546
|
+
projectId: true,
|
|
547
|
+
},
|
|
548
|
+
props: {
|
|
549
|
+
isRoot: true,
|
|
550
|
+
},
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
if (!episode || !episode.projectId) {
|
|
554
|
+
throw new BadDataException("Episode not found.");
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const incidentState: IncidentState | null =
|
|
558
|
+
await IncidentStateService.findOneBy({
|
|
559
|
+
query: {
|
|
560
|
+
projectId: episode.projectId,
|
|
561
|
+
isResolvedState: true,
|
|
562
|
+
},
|
|
563
|
+
select: {
|
|
564
|
+
_id: true,
|
|
565
|
+
},
|
|
566
|
+
props: {
|
|
567
|
+
isRoot: true,
|
|
568
|
+
},
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
if (!incidentState || !incidentState.id) {
|
|
572
|
+
throw new BadDataException(
|
|
573
|
+
"Resolved incident state not found for this project.",
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
await this.changeEpisodeState({
|
|
578
|
+
projectId: episode.projectId,
|
|
579
|
+
episodeId: episodeId,
|
|
580
|
+
incidentStateId: incidentState.id,
|
|
581
|
+
notifyOwners: false,
|
|
582
|
+
rootCause: resolvedByUserId ? `Resolved by user.` : "Resolved via API.",
|
|
583
|
+
props: {
|
|
584
|
+
isRoot: true,
|
|
585
|
+
userId: resolvedByUserId,
|
|
586
|
+
},
|
|
587
|
+
cascadeToIncidents: cascadeToIncidents,
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
@CaptureSpan()
|
|
592
|
+
public async reopenEpisode(
|
|
593
|
+
episodeId: ObjectID,
|
|
594
|
+
reopenedByUserId?: ObjectID,
|
|
595
|
+
cascadeToIncidents: boolean = true,
|
|
596
|
+
): Promise<void> {
|
|
597
|
+
const episode: Model | null = await this.findOneById({
|
|
598
|
+
id: episodeId,
|
|
599
|
+
select: {
|
|
600
|
+
projectId: true,
|
|
601
|
+
},
|
|
602
|
+
props: {
|
|
603
|
+
isRoot: true,
|
|
604
|
+
},
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
if (!episode || !episode.projectId) {
|
|
608
|
+
throw new BadDataException("Episode not found.");
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
const incidentState: IncidentState | null =
|
|
612
|
+
await IncidentStateService.findOneBy({
|
|
613
|
+
query: {
|
|
614
|
+
projectId: episode.projectId,
|
|
615
|
+
isCreatedState: true,
|
|
616
|
+
},
|
|
617
|
+
select: {
|
|
618
|
+
_id: true,
|
|
619
|
+
},
|
|
620
|
+
props: {
|
|
621
|
+
isRoot: true,
|
|
622
|
+
},
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
if (!incidentState || !incidentState.id) {
|
|
626
|
+
throw new BadDataException(
|
|
627
|
+
"Created incident state not found for this project.",
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
await this.changeEpisodeState({
|
|
632
|
+
projectId: episode.projectId,
|
|
633
|
+
episodeId: episodeId,
|
|
634
|
+
incidentStateId: incidentState.id,
|
|
635
|
+
notifyOwners: false,
|
|
636
|
+
rootCause: reopenedByUserId ? `Reopened by user.` : "Reopened via API.",
|
|
637
|
+
props: {
|
|
638
|
+
isRoot: true,
|
|
639
|
+
userId: reopenedByUserId,
|
|
640
|
+
},
|
|
641
|
+
cascadeToIncidents: cascadeToIncidents,
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
@CaptureSpan()
|
|
646
|
+
public async updateEpisodeSeverity(
|
|
647
|
+
episodeId: ObjectID,
|
|
648
|
+
severityId: ObjectID,
|
|
649
|
+
onlyIfHigher: boolean = false,
|
|
650
|
+
): Promise<void> {
|
|
651
|
+
const episode: Model | null = await this.findOneById({
|
|
652
|
+
id: episodeId,
|
|
653
|
+
select: {
|
|
654
|
+
projectId: true,
|
|
655
|
+
incidentSeverityId: true,
|
|
656
|
+
},
|
|
657
|
+
props: {
|
|
658
|
+
isRoot: true,
|
|
659
|
+
},
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
if (!episode || !episode.projectId) {
|
|
663
|
+
throw new BadDataException("Episode not found.");
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// If onlyIfHigher is true, check if the new severity is higher than the current
|
|
667
|
+
if (onlyIfHigher && episode.incidentSeverityId) {
|
|
668
|
+
const currentSeverity: IncidentSeverity | null =
|
|
669
|
+
await IncidentSeverityService.findOneById({
|
|
670
|
+
id: episode.incidentSeverityId,
|
|
671
|
+
select: {
|
|
672
|
+
order: true,
|
|
673
|
+
},
|
|
674
|
+
props: {
|
|
675
|
+
isRoot: true,
|
|
676
|
+
},
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
const newSeverity: IncidentSeverity | null =
|
|
680
|
+
await IncidentSeverityService.findOneById({
|
|
681
|
+
id: severityId,
|
|
682
|
+
select: {
|
|
683
|
+
order: true,
|
|
684
|
+
},
|
|
685
|
+
props: {
|
|
686
|
+
isRoot: true,
|
|
687
|
+
},
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
// Lower order number means higher severity
|
|
691
|
+
if (
|
|
692
|
+
currentSeverity?.order !== undefined &&
|
|
693
|
+
newSeverity?.order !== undefined &&
|
|
694
|
+
newSeverity.order >= currentSeverity.order
|
|
695
|
+
) {
|
|
696
|
+
return; // New severity is not higher, don't update
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
await this.updateOneById({
|
|
701
|
+
id: episodeId,
|
|
702
|
+
data: {
|
|
703
|
+
incidentSeverityId: severityId,
|
|
704
|
+
},
|
|
705
|
+
props: {
|
|
706
|
+
isRoot: true,
|
|
707
|
+
},
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
// Create feed entry for severity change
|
|
711
|
+
const newSeverity: IncidentSeverity | null =
|
|
712
|
+
await IncidentSeverityService.findOneById({
|
|
713
|
+
id: severityId,
|
|
714
|
+
select: {
|
|
715
|
+
name: true,
|
|
716
|
+
color: true,
|
|
717
|
+
},
|
|
718
|
+
props: {
|
|
719
|
+
isRoot: true,
|
|
720
|
+
},
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
if (newSeverity && episode.projectId) {
|
|
724
|
+
await IncidentEpisodeFeedService.createIncidentEpisodeFeedItem({
|
|
725
|
+
incidentEpisodeId: episodeId,
|
|
726
|
+
projectId: episode.projectId,
|
|
727
|
+
incidentEpisodeFeedEventType:
|
|
728
|
+
IncidentEpisodeFeedEventType.SeverityChanged,
|
|
729
|
+
displayColor: newSeverity.color || Yellow500,
|
|
730
|
+
feedInfoInMarkdown: `Episode severity changed to **${newSeverity.name || "Unknown"}**`,
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
@CaptureSpan()
|
|
736
|
+
public async updateIncidentCount(episodeId: ObjectID): Promise<void> {
|
|
737
|
+
const count: PositiveNumber = await IncidentEpisodeMemberService.countBy({
|
|
738
|
+
query: {
|
|
739
|
+
incidentEpisodeId: episodeId,
|
|
740
|
+
},
|
|
741
|
+
props: {
|
|
742
|
+
isRoot: true,
|
|
743
|
+
},
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
await this.updateOneById({
|
|
747
|
+
id: episodeId,
|
|
748
|
+
data: {
|
|
749
|
+
incidentCount: count.toNumber(),
|
|
750
|
+
},
|
|
751
|
+
props: {
|
|
752
|
+
isRoot: true,
|
|
753
|
+
},
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
@CaptureSpan()
|
|
758
|
+
public async updateLastIncidentAddedAt(episodeId: ObjectID): Promise<void> {
|
|
759
|
+
await this.updateOneById({
|
|
760
|
+
id: episodeId,
|
|
761
|
+
data: {
|
|
762
|
+
lastIncidentAddedAt: OneUptimeDate.getCurrentDate(),
|
|
763
|
+
},
|
|
764
|
+
props: {
|
|
765
|
+
isRoot: true,
|
|
766
|
+
},
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
@CaptureSpan()
|
|
771
|
+
public async findOwners(episodeId: ObjectID): Promise<Array<User>> {
|
|
772
|
+
// Get direct user owners
|
|
773
|
+
const userOwners: Array<IncidentEpisodeOwnerUser> =
|
|
774
|
+
await IncidentEpisodeOwnerUserService.findBy({
|
|
775
|
+
query: {
|
|
776
|
+
incidentEpisodeId: episodeId,
|
|
777
|
+
},
|
|
778
|
+
select: {
|
|
779
|
+
userId: true,
|
|
780
|
+
user: {
|
|
781
|
+
_id: true,
|
|
782
|
+
email: true,
|
|
783
|
+
name: true,
|
|
784
|
+
},
|
|
785
|
+
},
|
|
786
|
+
props: {
|
|
787
|
+
isRoot: true,
|
|
788
|
+
},
|
|
789
|
+
limit: LIMIT_PER_PROJECT,
|
|
790
|
+
skip: 0,
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
// Get team owners
|
|
794
|
+
const teamOwners: Array<IncidentEpisodeOwnerTeam> =
|
|
795
|
+
await IncidentEpisodeOwnerTeamService.findBy({
|
|
796
|
+
query: {
|
|
797
|
+
incidentEpisodeId: episodeId,
|
|
798
|
+
},
|
|
799
|
+
select: {
|
|
800
|
+
teamId: true,
|
|
801
|
+
},
|
|
802
|
+
props: {
|
|
803
|
+
isRoot: true,
|
|
804
|
+
},
|
|
805
|
+
limit: LIMIT_PER_PROJECT,
|
|
806
|
+
skip: 0,
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
// Collect all unique users
|
|
810
|
+
const usersMap: Map<string, User> = new Map();
|
|
811
|
+
|
|
812
|
+
// Add direct user owners
|
|
813
|
+
for (const owner of userOwners) {
|
|
814
|
+
if (owner.user && owner.userId) {
|
|
815
|
+
usersMap.set(owner.userId.toString(), owner.user);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// Add users from teams
|
|
820
|
+
for (const teamOwner of teamOwners) {
|
|
821
|
+
if (teamOwner.teamId) {
|
|
822
|
+
const teamMembers: Array<User> = await TeamMemberService.getUsersInTeam(
|
|
823
|
+
teamOwner.teamId,
|
|
824
|
+
);
|
|
825
|
+
for (const user of teamMembers) {
|
|
826
|
+
if (user.id) {
|
|
827
|
+
usersMap.set(user.id.toString(), user);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
return Array.from(usersMap.values());
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
@CaptureSpan()
|
|
837
|
+
public async addOwners(data: {
|
|
838
|
+
episodeId: ObjectID;
|
|
839
|
+
projectId: ObjectID;
|
|
840
|
+
userIds?: Array<ObjectID>;
|
|
841
|
+
teamIds?: Array<ObjectID>;
|
|
842
|
+
createdByUserId?: ObjectID;
|
|
843
|
+
}): Promise<void> {
|
|
844
|
+
const { episodeId, projectId, userIds, teamIds, createdByUserId } = data;
|
|
845
|
+
|
|
846
|
+
// Add user owners
|
|
847
|
+
if (userIds && userIds.length > 0) {
|
|
848
|
+
for (const userId of userIds) {
|
|
849
|
+
// Check if already exists
|
|
850
|
+
const existing: IncidentEpisodeOwnerUser | null =
|
|
851
|
+
await IncidentEpisodeOwnerUserService.findOneBy({
|
|
852
|
+
query: {
|
|
853
|
+
incidentEpisodeId: episodeId,
|
|
854
|
+
userId: userId,
|
|
855
|
+
},
|
|
856
|
+
props: {
|
|
857
|
+
isRoot: true,
|
|
858
|
+
},
|
|
859
|
+
select: {
|
|
860
|
+
_id: true,
|
|
861
|
+
},
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
if (!existing) {
|
|
865
|
+
const ownerUser: IncidentEpisodeOwnerUser =
|
|
866
|
+
new IncidentEpisodeOwnerUser();
|
|
867
|
+
ownerUser.incidentEpisodeId = episodeId;
|
|
868
|
+
ownerUser.userId = userId;
|
|
869
|
+
ownerUser.projectId = projectId;
|
|
870
|
+
if (createdByUserId) {
|
|
871
|
+
ownerUser.createdByUserId = createdByUserId;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
await IncidentEpisodeOwnerUserService.create({
|
|
875
|
+
data: ownerUser,
|
|
876
|
+
props: {
|
|
877
|
+
isRoot: true,
|
|
878
|
+
},
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
// Add team owners
|
|
885
|
+
if (teamIds && teamIds.length > 0) {
|
|
886
|
+
for (const teamId of teamIds) {
|
|
887
|
+
// Check if already exists
|
|
888
|
+
const existing: IncidentEpisodeOwnerTeam | null =
|
|
889
|
+
await IncidentEpisodeOwnerTeamService.findOneBy({
|
|
890
|
+
query: {
|
|
891
|
+
incidentEpisodeId: episodeId,
|
|
892
|
+
teamId: teamId,
|
|
893
|
+
},
|
|
894
|
+
props: {
|
|
895
|
+
isRoot: true,
|
|
896
|
+
},
|
|
897
|
+
select: {
|
|
898
|
+
_id: true,
|
|
899
|
+
},
|
|
900
|
+
});
|
|
901
|
+
|
|
902
|
+
if (!existing) {
|
|
903
|
+
const ownerTeam: IncidentEpisodeOwnerTeam =
|
|
904
|
+
new IncidentEpisodeOwnerTeam();
|
|
905
|
+
ownerTeam.incidentEpisodeId = episodeId;
|
|
906
|
+
ownerTeam.teamId = teamId;
|
|
907
|
+
ownerTeam.projectId = projectId;
|
|
908
|
+
if (createdByUserId) {
|
|
909
|
+
ownerTeam.createdByUserId = createdByUserId;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
await IncidentEpisodeOwnerTeamService.create({
|
|
913
|
+
data: ownerTeam,
|
|
914
|
+
props: {
|
|
915
|
+
isRoot: true,
|
|
916
|
+
},
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
@CaptureSpan()
|
|
924
|
+
public getWorkspaceChannelForEpisode(
|
|
925
|
+
episode: Model,
|
|
926
|
+
workspaceType: WorkspaceType,
|
|
927
|
+
): Array<NotificationRuleWorkspaceChannel> {
|
|
928
|
+
if (
|
|
929
|
+
!episode.postUpdatesToWorkspaceChannels ||
|
|
930
|
+
!Array.isArray(episode.postUpdatesToWorkspaceChannels) ||
|
|
931
|
+
episode.postUpdatesToWorkspaceChannels.length === 0
|
|
932
|
+
) {
|
|
933
|
+
return [];
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
return episode.postUpdatesToWorkspaceChannels.filter(
|
|
937
|
+
(channel: NotificationRuleWorkspaceChannel) => {
|
|
938
|
+
return channel.workspaceType === workspaceType;
|
|
939
|
+
},
|
|
940
|
+
);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
@CaptureSpan()
|
|
944
|
+
public async isEpisodeResolved(episodeId: ObjectID): Promise<boolean> {
|
|
945
|
+
const episode: Model | null = await this.findOneById({
|
|
946
|
+
id: episodeId,
|
|
947
|
+
select: {
|
|
948
|
+
projectId: true,
|
|
949
|
+
currentIncidentState: {
|
|
950
|
+
order: true,
|
|
951
|
+
},
|
|
952
|
+
},
|
|
953
|
+
props: {
|
|
954
|
+
isRoot: true,
|
|
955
|
+
},
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
if (!episode || !episode.projectId) {
|
|
959
|
+
throw new BadDataException("Episode not found.");
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
const resolvedState: IncidentState =
|
|
963
|
+
await IncidentStateService.getResolvedIncidentState({
|
|
964
|
+
projectId: episode.projectId,
|
|
965
|
+
props: {
|
|
966
|
+
isRoot: true,
|
|
967
|
+
},
|
|
968
|
+
});
|
|
969
|
+
|
|
970
|
+
const currentOrder: number = episode.currentIncidentState?.order || 0;
|
|
971
|
+
const resolvedOrder: number = resolvedState.order || 0;
|
|
972
|
+
|
|
973
|
+
return currentOrder >= resolvedOrder;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
@CaptureSpan()
|
|
977
|
+
public async isEpisodeAcknowledged(data: {
|
|
978
|
+
episodeId: ObjectID;
|
|
979
|
+
}): Promise<boolean> {
|
|
980
|
+
const episode: Model | null = await this.findOneById({
|
|
981
|
+
id: data.episodeId,
|
|
982
|
+
select: {
|
|
983
|
+
projectId: true,
|
|
984
|
+
currentIncidentState: {
|
|
985
|
+
order: true,
|
|
986
|
+
},
|
|
987
|
+
},
|
|
988
|
+
props: {
|
|
989
|
+
isRoot: true,
|
|
990
|
+
},
|
|
991
|
+
});
|
|
992
|
+
|
|
993
|
+
if (!episode || !episode.projectId) {
|
|
994
|
+
throw new BadDataException("Episode not found.");
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
const acknowledgedState: IncidentState =
|
|
998
|
+
await IncidentStateService.getAcknowledgedIncidentState({
|
|
999
|
+
projectId: episode.projectId,
|
|
1000
|
+
props: {
|
|
1001
|
+
isRoot: true,
|
|
1002
|
+
},
|
|
1003
|
+
});
|
|
1004
|
+
|
|
1005
|
+
const currentOrder: number = episode.currentIncidentState?.order || 0;
|
|
1006
|
+
const acknowledgedOrder: number = acknowledgedState.order || 0;
|
|
1007
|
+
|
|
1008
|
+
return currentOrder >= acknowledgedOrder;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
@CaptureSpan()
|
|
1012
|
+
public async getEpisodeLinkInDashboard(
|
|
1013
|
+
projectId: ObjectID,
|
|
1014
|
+
episodeId: ObjectID,
|
|
1015
|
+
): Promise<URL> {
|
|
1016
|
+
const dashboardUrl: URL = await DatabaseConfig.getDashboardUrl();
|
|
1017
|
+
|
|
1018
|
+
return URL.fromString(dashboardUrl.toString()).addRoute(
|
|
1019
|
+
`/${projectId.toString()}/incidents/episodes/${episodeId.toString()}`,
|
|
1020
|
+
);
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
@CaptureSpan()
|
|
1024
|
+
public async getEpisodeNumber(data: {
|
|
1025
|
+
episodeId: ObjectID;
|
|
1026
|
+
}): Promise<number | null> {
|
|
1027
|
+
const episode: Model | null = await this.findOneById({
|
|
1028
|
+
id: data.episodeId,
|
|
1029
|
+
select: {
|
|
1030
|
+
episodeNumber: true,
|
|
1031
|
+
},
|
|
1032
|
+
props: {
|
|
1033
|
+
isRoot: true,
|
|
1034
|
+
},
|
|
1035
|
+
});
|
|
1036
|
+
|
|
1037
|
+
if (!episode) {
|
|
1038
|
+
throw new BadDataException("Episode not found.");
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
return episode.episodeNumber ? Number(episode.episodeNumber) : null;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
export default new Service();
|