@oneuptime/common 9.4.12 → 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/UserOnCallLog.ts +48 -0
- package/Models/DatabaseModels/UserOnCallLogTimeline.ts +49 -0
- package/Models/DatabaseModels/WorkspaceNotificationLog.ts +57 -0
- package/Server/API/IncidentEpisodeAPI.ts +150 -0
- package/Server/API/SlackAPI.ts +23 -0
- package/Server/API/UserOnCallLogTimelineAPI.ts +24 -4
- 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 +6 -0
- package/Server/Services/BillingService.ts +1 -3
- 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/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/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/WhatsAppService.ts +1 -0
- package/Server/Services/WorkspaceNotificationLogService.ts +16 -0
- package/Server/Services/WorkspaceNotificationRuleService.ts +116 -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 +6 -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/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/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/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 +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/BillingService.js +1 -2
- 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/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/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/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/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 +5 -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,799 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import ObjectID from "../../Types/ObjectID";
|
|
11
|
+
import IncidentGroupingRule from "../../Models/DatabaseModels/IncidentGroupingRule";
|
|
12
|
+
import Incident from "../../Models/DatabaseModels/Incident";
|
|
13
|
+
import IncidentEpisode from "../../Models/DatabaseModels/IncidentEpisode";
|
|
14
|
+
import IncidentEpisodeMember, { IncidentEpisodeMemberAddedBy, } from "../../Models/DatabaseModels/IncidentEpisodeMember";
|
|
15
|
+
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
16
|
+
import logger from "../Utils/Logger";
|
|
17
|
+
import SortOrder from "../../Types/BaseDatabase/SortOrder";
|
|
18
|
+
import OneUptimeDate from "../../Types/Date";
|
|
19
|
+
import QueryHelper from "../Types/Database/QueryHelper";
|
|
20
|
+
import IncidentGroupingRuleService from "./IncidentGroupingRuleService";
|
|
21
|
+
import IncidentEpisodeService from "./IncidentEpisodeService";
|
|
22
|
+
import IncidentEpisodeMemberService from "./IncidentEpisodeMemberService";
|
|
23
|
+
import MonitorService from "./MonitorService";
|
|
24
|
+
import ServiceMonitorService from "./ServiceMonitorService";
|
|
25
|
+
import Semaphore from "../Infrastructure/Semaphore";
|
|
26
|
+
import IncidentEpisodeFeedService from "./IncidentEpisodeFeedService";
|
|
27
|
+
import { IncidentEpisodeFeedEventType } from "../../Models/DatabaseModels/IncidentEpisodeFeed";
|
|
28
|
+
import { Green500 } from "../../Types/BrandColors";
|
|
29
|
+
class IncidentGroupingEngineServiceClass {
|
|
30
|
+
async processIncident(incident) {
|
|
31
|
+
logger.debug(`Processing incident ${incident.id} for grouping`);
|
|
32
|
+
try {
|
|
33
|
+
if (!incident.id || !incident.projectId) {
|
|
34
|
+
logger.warn("Incident missing id or projectId, skipping grouping");
|
|
35
|
+
return { grouped: false };
|
|
36
|
+
}
|
|
37
|
+
// If incident already has an episode, don't reprocess
|
|
38
|
+
if (incident.incidentEpisodeId) {
|
|
39
|
+
return { grouped: true, episodeId: incident.incidentEpisodeId };
|
|
40
|
+
}
|
|
41
|
+
// Get enabled rules sorted by priority
|
|
42
|
+
const rules = await IncidentGroupingRuleService.findBy({
|
|
43
|
+
query: {
|
|
44
|
+
projectId: incident.projectId,
|
|
45
|
+
isEnabled: true,
|
|
46
|
+
},
|
|
47
|
+
sort: {
|
|
48
|
+
priority: SortOrder.Ascending,
|
|
49
|
+
},
|
|
50
|
+
props: {
|
|
51
|
+
isRoot: true,
|
|
52
|
+
},
|
|
53
|
+
select: {
|
|
54
|
+
_id: true,
|
|
55
|
+
name: true,
|
|
56
|
+
priority: true,
|
|
57
|
+
// Match criteria fields
|
|
58
|
+
monitors: {
|
|
59
|
+
_id: true,
|
|
60
|
+
},
|
|
61
|
+
incidentSeverities: {
|
|
62
|
+
_id: true,
|
|
63
|
+
},
|
|
64
|
+
incidentLabels: {
|
|
65
|
+
_id: true,
|
|
66
|
+
},
|
|
67
|
+
monitorLabels: {
|
|
68
|
+
_id: true,
|
|
69
|
+
},
|
|
70
|
+
incidentTitlePattern: true,
|
|
71
|
+
incidentDescriptionPattern: true,
|
|
72
|
+
monitorNamePattern: true,
|
|
73
|
+
monitorDescriptionPattern: true,
|
|
74
|
+
// Group by fields
|
|
75
|
+
groupByMonitor: true,
|
|
76
|
+
groupBySeverity: true,
|
|
77
|
+
groupByIncidentTitle: true,
|
|
78
|
+
groupByService: true,
|
|
79
|
+
// Time settings
|
|
80
|
+
enableTimeWindow: true,
|
|
81
|
+
timeWindowMinutes: true,
|
|
82
|
+
episodeTitleTemplate: true,
|
|
83
|
+
episodeDescriptionTemplate: true,
|
|
84
|
+
enableResolveDelay: true,
|
|
85
|
+
resolveDelayMinutes: true,
|
|
86
|
+
enableReopenWindow: true,
|
|
87
|
+
reopenWindowMinutes: true,
|
|
88
|
+
enableInactivityTimeout: true,
|
|
89
|
+
inactivityTimeoutMinutes: true,
|
|
90
|
+
defaultAssignToUserId: true,
|
|
91
|
+
defaultAssignToTeamId: true,
|
|
92
|
+
onCallDutyPolicies: {
|
|
93
|
+
_id: true,
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
limit: 100,
|
|
97
|
+
skip: 0,
|
|
98
|
+
});
|
|
99
|
+
if (rules.length === 0) {
|
|
100
|
+
logger.debug(`No enabled grouping rules found for project ${incident.projectId}`);
|
|
101
|
+
return { grouped: false };
|
|
102
|
+
}
|
|
103
|
+
logger.debug(`Found ${rules.length} enabled grouping rules for project ${incident.projectId}`);
|
|
104
|
+
// Find first matching rule
|
|
105
|
+
for (const rule of rules) {
|
|
106
|
+
const matches = await this.doesIncidentMatchRule(incident, rule);
|
|
107
|
+
if (matches) {
|
|
108
|
+
logger.debug(`Incident ${incident.id} matches rule ${rule.name || rule.id}`);
|
|
109
|
+
// Try to find existing episode or create new one
|
|
110
|
+
const result = await this.groupIncidentWithRule(incident, rule);
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
logger.debug(`Incident ${incident.id} did not match any grouping rules`);
|
|
115
|
+
return { grouped: false };
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
logger.error(`Error processing incident for grouping: ${error}`);
|
|
119
|
+
return { grouped: false };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
async doesIncidentMatchRule(incident, rule) {
|
|
123
|
+
logger.debug(`Checking if incident ${incident.id} matches rule ${rule.name || rule.id}`);
|
|
124
|
+
// Check monitor IDs - if monitors are specified, incident must be from one of them
|
|
125
|
+
if (rule.monitors && rule.monitors.length > 0) {
|
|
126
|
+
if (!incident.monitors || incident.monitors.length === 0) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
const ruleMonitorIds = rule.monitors.map((m) => {
|
|
130
|
+
var _a;
|
|
131
|
+
return ((_a = m.id) === null || _a === void 0 ? void 0 : _a.toString()) || "";
|
|
132
|
+
});
|
|
133
|
+
const incidentMonitorIds = incident.monitors.map((m) => {
|
|
134
|
+
var _a;
|
|
135
|
+
return ((_a = m.id) === null || _a === void 0 ? void 0 : _a.toString()) || "";
|
|
136
|
+
});
|
|
137
|
+
const hasMatchingMonitor = ruleMonitorIds.some((monitorId) => {
|
|
138
|
+
return incidentMonitorIds.includes(monitorId);
|
|
139
|
+
});
|
|
140
|
+
if (!hasMatchingMonitor) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Check incident severity IDs - if severities are specified, incident must have one of them
|
|
145
|
+
if (rule.incidentSeverities && rule.incidentSeverities.length > 0) {
|
|
146
|
+
if (!incident.incidentSeverityId) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
const severityIds = rule.incidentSeverities.map((s) => {
|
|
150
|
+
var _a;
|
|
151
|
+
return ((_a = s.id) === null || _a === void 0 ? void 0 : _a.toString()) || "";
|
|
152
|
+
});
|
|
153
|
+
const incidentSeverityIdStr = incident.incidentSeverityId.toString();
|
|
154
|
+
if (!severityIds.includes(incidentSeverityIdStr)) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Check incident label IDs - if incident labels are specified, incident must have at least one of them
|
|
159
|
+
if (rule.incidentLabels && rule.incidentLabels.length > 0) {
|
|
160
|
+
if (!incident.labels || incident.labels.length === 0) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
const ruleLabelIds = rule.incidentLabels.map((l) => {
|
|
164
|
+
var _a;
|
|
165
|
+
return ((_a = l.id) === null || _a === void 0 ? void 0 : _a.toString()) || "";
|
|
166
|
+
});
|
|
167
|
+
const incidentLabelIds = incident.labels.map((l) => {
|
|
168
|
+
var _a;
|
|
169
|
+
return ((_a = l.id) === null || _a === void 0 ? void 0 : _a.toString()) || "";
|
|
170
|
+
});
|
|
171
|
+
const hasMatchingLabel = ruleLabelIds.some((labelId) => {
|
|
172
|
+
return incidentLabelIds.includes(labelId);
|
|
173
|
+
});
|
|
174
|
+
if (!hasMatchingLabel) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Check monitor-related criteria (labels, name pattern, description pattern)
|
|
179
|
+
const hasMonitorCriteria = Boolean((rule.monitorLabels && rule.monitorLabels.length > 0) ||
|
|
180
|
+
rule.monitorNamePattern ||
|
|
181
|
+
rule.monitorDescriptionPattern);
|
|
182
|
+
if (hasMonitorCriteria) {
|
|
183
|
+
if (!incident.monitors || incident.monitors.length === 0) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
// Check at least one monitor matches all criteria
|
|
187
|
+
let anyMonitorMatches = false;
|
|
188
|
+
for (const incidentMonitor of incident.monitors) {
|
|
189
|
+
if (!incidentMonitor.id) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
// Load monitor with all needed fields
|
|
193
|
+
const monitor = await MonitorService.findOneById({
|
|
194
|
+
id: incidentMonitor.id,
|
|
195
|
+
select: {
|
|
196
|
+
name: true,
|
|
197
|
+
description: true,
|
|
198
|
+
labels: {
|
|
199
|
+
_id: true,
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
props: {
|
|
203
|
+
isRoot: true,
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
if (!monitor) {
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
let monitorMatches = true;
|
|
210
|
+
// Check monitor labels
|
|
211
|
+
if (rule.monitorLabels && rule.monitorLabels.length > 0) {
|
|
212
|
+
if (!monitor.labels || monitor.labels.length === 0) {
|
|
213
|
+
monitorMatches = false;
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
const ruleMonitorLabelIds = rule.monitorLabels.map((l) => {
|
|
217
|
+
var _a;
|
|
218
|
+
return ((_a = l.id) === null || _a === void 0 ? void 0 : _a.toString()) || "";
|
|
219
|
+
});
|
|
220
|
+
const monitorLabelIds = monitor.labels.map((l) => {
|
|
221
|
+
var _a;
|
|
222
|
+
return ((_a = l.id) === null || _a === void 0 ? void 0 : _a.toString()) || "";
|
|
223
|
+
});
|
|
224
|
+
const hasMatchingMonitorLabel = ruleMonitorLabelIds.some((labelId) => {
|
|
225
|
+
return monitorLabelIds.includes(labelId);
|
|
226
|
+
});
|
|
227
|
+
if (!hasMatchingMonitorLabel) {
|
|
228
|
+
monitorMatches = false;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// Check monitor name pattern (regex)
|
|
233
|
+
if (monitorMatches && rule.monitorNamePattern) {
|
|
234
|
+
if (!monitor.name) {
|
|
235
|
+
monitorMatches = false;
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
try {
|
|
239
|
+
const regex = new RegExp(rule.monitorNamePattern, "i");
|
|
240
|
+
if (!regex.test(monitor.name)) {
|
|
241
|
+
monitorMatches = false;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
catch (_a) {
|
|
245
|
+
logger.warn(`Invalid regex pattern in rule ${rule.id}: ${rule.monitorNamePattern}`);
|
|
246
|
+
monitorMatches = false;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// Check monitor description pattern (regex)
|
|
251
|
+
if (monitorMatches && rule.monitorDescriptionPattern) {
|
|
252
|
+
if (!monitor.description) {
|
|
253
|
+
monitorMatches = false;
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
try {
|
|
257
|
+
const regex = new RegExp(rule.monitorDescriptionPattern, "i");
|
|
258
|
+
if (!regex.test(monitor.description)) {
|
|
259
|
+
monitorMatches = false;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
catch (_b) {
|
|
263
|
+
logger.warn(`Invalid regex pattern in rule ${rule.id}: ${rule.monitorDescriptionPattern}`);
|
|
264
|
+
monitorMatches = false;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (monitorMatches) {
|
|
269
|
+
anyMonitorMatches = true;
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (!anyMonitorMatches) {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// Check incident title pattern (regex)
|
|
278
|
+
if (rule.incidentTitlePattern) {
|
|
279
|
+
if (!incident.title) {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
try {
|
|
283
|
+
const regex = new RegExp(rule.incidentTitlePattern, "i");
|
|
284
|
+
if (!regex.test(incident.title)) {
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
catch (_c) {
|
|
289
|
+
logger.warn(`Invalid regex pattern in rule ${rule.id}: ${rule.incidentTitlePattern}`);
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
// Check incident description pattern (regex)
|
|
294
|
+
if (rule.incidentDescriptionPattern) {
|
|
295
|
+
if (!incident.description) {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
try {
|
|
299
|
+
const regex = new RegExp(rule.incidentDescriptionPattern, "i");
|
|
300
|
+
if (!regex.test(incident.description)) {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
catch (_d) {
|
|
305
|
+
logger.warn(`Invalid regex pattern in rule ${rule.id}: ${rule.incidentDescriptionPattern}`);
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// If no criteria specified (all fields empty), rule matches all incidents
|
|
310
|
+
logger.debug(`Rule ${rule.name || rule.id} matched incident ${incident.id} (all criteria passed)`);
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
async groupIncidentWithRule(incident, rule) {
|
|
314
|
+
var _a, _b;
|
|
315
|
+
// Build the grouping key based on groupBy fields
|
|
316
|
+
const groupingKey = await this.buildGroupingKey(incident, rule);
|
|
317
|
+
// Create mutex key to prevent race conditions when creating episodes
|
|
318
|
+
const mutexKey = `${(_a = incident.projectId) === null || _a === void 0 ? void 0 : _a.toString()}-${(_b = rule.id) === null || _b === void 0 ? void 0 : _b.toString()}-${groupingKey}`;
|
|
319
|
+
let mutex = null;
|
|
320
|
+
try {
|
|
321
|
+
/*
|
|
322
|
+
* Acquire mutex to prevent concurrent episode creation for the same grouping key
|
|
323
|
+
* This is critical - we must have the lock before proceeding to prevent race conditions
|
|
324
|
+
*/
|
|
325
|
+
logger.debug(`Acquiring mutex for grouping key: ${mutexKey} for incident ${incident.id}`);
|
|
326
|
+
mutex = await Semaphore.lock({
|
|
327
|
+
key: mutexKey,
|
|
328
|
+
namespace: "IncidentGroupingEngine.groupIncidentWithRule",
|
|
329
|
+
lockTimeout: 30000, // 30 seconds - enough time to complete episode creation
|
|
330
|
+
acquireTimeout: 60000, // Wait up to 60 seconds to acquire the lock
|
|
331
|
+
});
|
|
332
|
+
logger.debug(`Acquired mutex for grouping key: ${mutexKey} for incident ${incident.id}`);
|
|
333
|
+
// Calculate time window cutoff (only if time window is enabled)
|
|
334
|
+
let timeWindowCutoff = null;
|
|
335
|
+
if (rule.enableTimeWindow) {
|
|
336
|
+
const timeWindowMinutes = rule.timeWindowMinutes || 60;
|
|
337
|
+
timeWindowCutoff = OneUptimeDate.getSomeMinutesAgo(timeWindowMinutes);
|
|
338
|
+
}
|
|
339
|
+
// Find existing active episode that matches
|
|
340
|
+
const existingEpisode = await this.findMatchingActiveEpisode(incident.projectId, rule.id, groupingKey, timeWindowCutoff);
|
|
341
|
+
if (existingEpisode && existingEpisode.id) {
|
|
342
|
+
// Add incident to existing episode
|
|
343
|
+
await this.addIncidentToEpisode(incident, existingEpisode.id, IncidentEpisodeMemberAddedBy.Rule, rule.id);
|
|
344
|
+
// Update episode severity if incident has higher severity
|
|
345
|
+
if (incident.incidentSeverityId) {
|
|
346
|
+
await IncidentEpisodeService.updateEpisodeSeverity(existingEpisode.id, incident.incidentSeverityId, true);
|
|
347
|
+
}
|
|
348
|
+
return {
|
|
349
|
+
grouped: true,
|
|
350
|
+
episodeId: existingEpisode.id,
|
|
351
|
+
isNewEpisode: false,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
// Check if we can reopen a recently resolved episode (only if enabled)
|
|
355
|
+
if (rule.enableReopenWindow) {
|
|
356
|
+
const reopenWindowMinutes = rule.reopenWindowMinutes || 0;
|
|
357
|
+
if (reopenWindowMinutes > 0) {
|
|
358
|
+
const reopenCutoff = OneUptimeDate.getSomeMinutesAgo(reopenWindowMinutes);
|
|
359
|
+
const recentlyResolvedEpisode = await this.findRecentlyResolvedEpisode(incident.projectId, rule.id, groupingKey, reopenCutoff);
|
|
360
|
+
if (recentlyResolvedEpisode && recentlyResolvedEpisode.id) {
|
|
361
|
+
// Reopen the episode
|
|
362
|
+
await IncidentEpisodeService.reopenEpisode(recentlyResolvedEpisode.id);
|
|
363
|
+
// Add incident to reopened episode
|
|
364
|
+
await this.addIncidentToEpisode(incident, recentlyResolvedEpisode.id, IncidentEpisodeMemberAddedBy.Rule, rule.id);
|
|
365
|
+
// Update episode severity if incident has higher severity
|
|
366
|
+
if (incident.incidentSeverityId) {
|
|
367
|
+
await IncidentEpisodeService.updateEpisodeSeverity(recentlyResolvedEpisode.id, incident.incidentSeverityId, true);
|
|
368
|
+
}
|
|
369
|
+
return {
|
|
370
|
+
grouped: true,
|
|
371
|
+
episodeId: recentlyResolvedEpisode.id,
|
|
372
|
+
isNewEpisode: false,
|
|
373
|
+
wasReopened: true,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
// Create new episode
|
|
379
|
+
const newEpisode = await this.createNewEpisode(incident, rule, groupingKey);
|
|
380
|
+
if (newEpisode && newEpisode.id) {
|
|
381
|
+
// Add incident to new episode
|
|
382
|
+
await this.addIncidentToEpisode(incident, newEpisode.id, IncidentEpisodeMemberAddedBy.Rule, rule.id);
|
|
383
|
+
return { grouped: true, episodeId: newEpisode.id, isNewEpisode: true };
|
|
384
|
+
}
|
|
385
|
+
return { grouped: false };
|
|
386
|
+
}
|
|
387
|
+
finally {
|
|
388
|
+
// Release mutex
|
|
389
|
+
if (mutex) {
|
|
390
|
+
try {
|
|
391
|
+
logger.debug(`Releasing mutex for grouping key: ${mutexKey} for incident ${incident.id}`);
|
|
392
|
+
await Semaphore.release(mutex);
|
|
393
|
+
logger.debug(`Released mutex for grouping key: ${mutexKey} for incident ${incident.id}`);
|
|
394
|
+
}
|
|
395
|
+
catch (err) {
|
|
396
|
+
logger.error(`Error releasing mutex for grouping key: ${mutexKey}: ${err}`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
async buildGroupingKey(incident, rule) {
|
|
402
|
+
const parts = [];
|
|
403
|
+
/*
|
|
404
|
+
* Group by service - only if explicitly enabled
|
|
405
|
+
* Must be checked before monitor since service contains multiple monitors
|
|
406
|
+
*/
|
|
407
|
+
if (rule.groupByService &&
|
|
408
|
+
incident.monitors &&
|
|
409
|
+
incident.monitors.length > 0) {
|
|
410
|
+
// Use the first monitor's service for grouping
|
|
411
|
+
const firstMonitor = incident.monitors[0];
|
|
412
|
+
if (firstMonitor && firstMonitor.id) {
|
|
413
|
+
const serviceMonitor = await ServiceMonitorService.findOneBy({
|
|
414
|
+
query: {
|
|
415
|
+
monitorId: firstMonitor.id,
|
|
416
|
+
},
|
|
417
|
+
select: {
|
|
418
|
+
serviceId: true,
|
|
419
|
+
},
|
|
420
|
+
props: {
|
|
421
|
+
isRoot: true,
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
if (serviceMonitor === null || serviceMonitor === void 0 ? void 0 : serviceMonitor.serviceId) {
|
|
425
|
+
parts.push(`service:${serviceMonitor.serviceId.toString()}`);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
// Group by monitor - only if explicitly enabled
|
|
430
|
+
if (rule.groupByMonitor &&
|
|
431
|
+
incident.monitors &&
|
|
432
|
+
incident.monitors.length > 0) {
|
|
433
|
+
// Use the first monitor for grouping key
|
|
434
|
+
const firstMonitor = incident.monitors[0];
|
|
435
|
+
if (firstMonitor && firstMonitor.id) {
|
|
436
|
+
parts.push(`monitor:${firstMonitor.id.toString()}`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
// Group by severity - only if explicitly enabled
|
|
440
|
+
if (rule.groupBySeverity && incident.incidentSeverityId) {
|
|
441
|
+
parts.push(`severity:${incident.incidentSeverityId.toString()}`);
|
|
442
|
+
}
|
|
443
|
+
// Group by incident title - only if explicitly enabled
|
|
444
|
+
if (rule.groupByIncidentTitle && incident.title) {
|
|
445
|
+
// Normalize title for grouping (remove numbers, etc.)
|
|
446
|
+
const normalizedTitle = incident.title
|
|
447
|
+
.toLowerCase()
|
|
448
|
+
.replace(/\d+/g, "X");
|
|
449
|
+
parts.push(`title:${normalizedTitle}`);
|
|
450
|
+
}
|
|
451
|
+
// If no group by options are enabled, all matching incidents go into a single episode
|
|
452
|
+
return parts.join("|") || "default";
|
|
453
|
+
}
|
|
454
|
+
async findMatchingActiveEpisode(projectId, ruleId, groupingKey, timeWindowCutoff) {
|
|
455
|
+
const query = {
|
|
456
|
+
projectId: projectId,
|
|
457
|
+
incidentGroupingRuleId: ruleId,
|
|
458
|
+
groupingKey: groupingKey,
|
|
459
|
+
resolvedAt: null, // Only find active (non-resolved) episodes
|
|
460
|
+
};
|
|
461
|
+
// Only add time window filter if enabled
|
|
462
|
+
if (timeWindowCutoff) {
|
|
463
|
+
query.lastIncidentAddedAt =
|
|
464
|
+
QueryHelper.greaterThanEqualTo(timeWindowCutoff);
|
|
465
|
+
}
|
|
466
|
+
const episode = await IncidentEpisodeService.findOneBy({
|
|
467
|
+
query: query,
|
|
468
|
+
sort: {
|
|
469
|
+
lastIncidentAddedAt: SortOrder.Descending,
|
|
470
|
+
},
|
|
471
|
+
select: {
|
|
472
|
+
_id: true,
|
|
473
|
+
lastIncidentAddedAt: true,
|
|
474
|
+
},
|
|
475
|
+
props: {
|
|
476
|
+
isRoot: true,
|
|
477
|
+
},
|
|
478
|
+
});
|
|
479
|
+
return episode;
|
|
480
|
+
}
|
|
481
|
+
async findRecentlyResolvedEpisode(projectId, ruleId, groupingKey, reopenCutoff) {
|
|
482
|
+
// Find recently resolved episode with matching rule and grouping key
|
|
483
|
+
const episode = await IncidentEpisodeService.findOneBy({
|
|
484
|
+
query: {
|
|
485
|
+
projectId: projectId,
|
|
486
|
+
incidentGroupingRuleId: ruleId,
|
|
487
|
+
groupingKey: groupingKey,
|
|
488
|
+
resolvedAt: QueryHelper.greaterThanEqualTo(reopenCutoff),
|
|
489
|
+
},
|
|
490
|
+
sort: {
|
|
491
|
+
resolvedAt: SortOrder.Descending,
|
|
492
|
+
},
|
|
493
|
+
select: {
|
|
494
|
+
_id: true,
|
|
495
|
+
resolvedAt: true,
|
|
496
|
+
},
|
|
497
|
+
props: {
|
|
498
|
+
isRoot: true,
|
|
499
|
+
},
|
|
500
|
+
});
|
|
501
|
+
return episode;
|
|
502
|
+
}
|
|
503
|
+
async createNewEpisode(incident, rule, groupingKey) {
|
|
504
|
+
// Generate episode title from template (with initial incidentCount of 1)
|
|
505
|
+
const title = this.generateEpisodeTitle(incident, rule.episodeTitleTemplate, 1);
|
|
506
|
+
// Generate episode description from template (with initial incidentCount of 1)
|
|
507
|
+
const description = this.generateEpisodeDescription(incident, rule.episodeDescriptionTemplate, 1);
|
|
508
|
+
const newEpisode = new IncidentEpisode();
|
|
509
|
+
newEpisode.projectId = incident.projectId;
|
|
510
|
+
newEpisode.title = title;
|
|
511
|
+
if (description) {
|
|
512
|
+
newEpisode.description = description;
|
|
513
|
+
}
|
|
514
|
+
/*
|
|
515
|
+
* Store preprocessed templates for dynamic variable updates
|
|
516
|
+
* Static variables are replaced, dynamic ones (like {{incidentCount}}) remain as placeholders
|
|
517
|
+
*/
|
|
518
|
+
if (rule.episodeTitleTemplate) {
|
|
519
|
+
newEpisode.titleTemplate = this.preprocessTemplate(incident, rule.episodeTitleTemplate);
|
|
520
|
+
}
|
|
521
|
+
if (rule.episodeDescriptionTemplate) {
|
|
522
|
+
newEpisode.descriptionTemplate = this.preprocessTemplate(incident, rule.episodeDescriptionTemplate);
|
|
523
|
+
}
|
|
524
|
+
newEpisode.incidentGroupingRuleId = rule.id;
|
|
525
|
+
newEpisode.groupingKey = groupingKey;
|
|
526
|
+
newEpisode.isManuallyCreated = false;
|
|
527
|
+
// Set severity from incident
|
|
528
|
+
if (incident.incidentSeverityId) {
|
|
529
|
+
newEpisode.incidentSeverityId = incident.incidentSeverityId;
|
|
530
|
+
}
|
|
531
|
+
// Set default ownership from rule
|
|
532
|
+
if (rule.defaultAssignToUserId) {
|
|
533
|
+
newEpisode.assignedToUserId = rule.defaultAssignToUserId;
|
|
534
|
+
}
|
|
535
|
+
if (rule.defaultAssignToTeamId) {
|
|
536
|
+
newEpisode.assignedToTeamId = rule.defaultAssignToTeamId;
|
|
537
|
+
}
|
|
538
|
+
// Copy on-call policies from rule
|
|
539
|
+
if (rule.onCallDutyPolicies && rule.onCallDutyPolicies.length > 0) {
|
|
540
|
+
newEpisode.onCallDutyPolicies = rule.onCallDutyPolicies;
|
|
541
|
+
}
|
|
542
|
+
try {
|
|
543
|
+
const createdEpisode = await IncidentEpisodeService.create({
|
|
544
|
+
data: newEpisode,
|
|
545
|
+
props: {
|
|
546
|
+
isRoot: true,
|
|
547
|
+
},
|
|
548
|
+
});
|
|
549
|
+
// Add episode feed entry for episode creation
|
|
550
|
+
if (createdEpisode.id) {
|
|
551
|
+
const groupByParts = [];
|
|
552
|
+
if (rule.groupByMonitor) {
|
|
553
|
+
groupByParts.push("Monitor");
|
|
554
|
+
}
|
|
555
|
+
if (rule.groupBySeverity) {
|
|
556
|
+
groupByParts.push("Severity");
|
|
557
|
+
}
|
|
558
|
+
if (rule.groupByIncidentTitle) {
|
|
559
|
+
groupByParts.push("Incident Title");
|
|
560
|
+
}
|
|
561
|
+
if (rule.groupByService) {
|
|
562
|
+
groupByParts.push("Service");
|
|
563
|
+
}
|
|
564
|
+
const groupByDescription = groupByParts.length > 0
|
|
565
|
+
? `Grouping by: ${groupByParts.join(", ")}`
|
|
566
|
+
: "Grouping all matching incidents together";
|
|
567
|
+
let moreInfo = `**Rule:** ${rule.name || "Unnamed Rule"}\n\n`;
|
|
568
|
+
moreInfo += `**Grouping Key:** \`${groupingKey}\`\n\n`;
|
|
569
|
+
moreInfo += `**${groupByDescription}**`;
|
|
570
|
+
if (rule.enableTimeWindow && rule.timeWindowMinutes) {
|
|
571
|
+
moreInfo += `\n\n**Time Window:** ${rule.timeWindowMinutes} minutes`;
|
|
572
|
+
}
|
|
573
|
+
try {
|
|
574
|
+
await IncidentEpisodeFeedService.createIncidentEpisodeFeedItem({
|
|
575
|
+
incidentEpisodeId: createdEpisode.id,
|
|
576
|
+
projectId: incident.projectId,
|
|
577
|
+
incidentEpisodeFeedEventType: IncidentEpisodeFeedEventType.EpisodeCreated,
|
|
578
|
+
displayColor: Green500,
|
|
579
|
+
feedInfoInMarkdown: `**Episode Created** by grouping rule **${rule.name || "Unnamed Rule"}**`,
|
|
580
|
+
moreInformationInMarkdown: moreInfo,
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
catch (feedError) {
|
|
584
|
+
logger.error(`Error creating episode feed for episode creation: ${feedError}`);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
return createdEpisode;
|
|
588
|
+
}
|
|
589
|
+
catch (error) {
|
|
590
|
+
logger.error(`Error creating new episode: ${error}`);
|
|
591
|
+
return null;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
generateEpisodeTitle(incident, template, incidentCount = 1) {
|
|
595
|
+
var _a;
|
|
596
|
+
if (!template) {
|
|
597
|
+
// Default title based on incident
|
|
598
|
+
if (incident.monitors &&
|
|
599
|
+
incident.monitors.length > 0 &&
|
|
600
|
+
((_a = incident.monitors[0]) === null || _a === void 0 ? void 0 : _a.name)) {
|
|
601
|
+
return `Incident Episode: ${incident.monitors[0].name}`;
|
|
602
|
+
}
|
|
603
|
+
if (incident.title) {
|
|
604
|
+
return `Incident Episode: ${incident.title.substring(0, 50)}`;
|
|
605
|
+
}
|
|
606
|
+
return "Incident Episode";
|
|
607
|
+
}
|
|
608
|
+
return (this.replaceTemplatePlaceholders(incident, template, incidentCount) ||
|
|
609
|
+
"Incident Episode");
|
|
610
|
+
}
|
|
611
|
+
generateEpisodeDescription(incident, template, incidentCount = 1) {
|
|
612
|
+
if (!template) {
|
|
613
|
+
return undefined;
|
|
614
|
+
}
|
|
615
|
+
return (this.replaceTemplatePlaceholders(incident, template, incidentCount) ||
|
|
616
|
+
undefined);
|
|
617
|
+
}
|
|
618
|
+
replaceTemplatePlaceholders(incident, template, incidentCount = 1) {
|
|
619
|
+
var _a, _b;
|
|
620
|
+
let result = template;
|
|
621
|
+
/*
|
|
622
|
+
* Static variables (from first incident)
|
|
623
|
+
* {{incidentTitle}}
|
|
624
|
+
*/
|
|
625
|
+
if (incident.title) {
|
|
626
|
+
result = result.replace(/\{\{incidentTitle\}\}/g, incident.title);
|
|
627
|
+
}
|
|
628
|
+
// {{incidentDescription}}
|
|
629
|
+
if (incident.description) {
|
|
630
|
+
result = result.replace(/\{\{incidentDescription\}\}/g, incident.description);
|
|
631
|
+
}
|
|
632
|
+
// {{monitorName}} - use first monitor's name
|
|
633
|
+
if (incident.monitors &&
|
|
634
|
+
incident.monitors.length > 0 &&
|
|
635
|
+
((_a = incident.monitors[0]) === null || _a === void 0 ? void 0 : _a.name)) {
|
|
636
|
+
result = result.replace(/\{\{monitorName\}\}/g, incident.monitors[0].name);
|
|
637
|
+
}
|
|
638
|
+
// {{incidentSeverity}}
|
|
639
|
+
if ((_b = incident.incidentSeverity) === null || _b === void 0 ? void 0 : _b.name) {
|
|
640
|
+
result = result.replace(/\{\{incidentSeverity\}\}/g, incident.incidentSeverity.name);
|
|
641
|
+
}
|
|
642
|
+
/*
|
|
643
|
+
* Dynamic variables (updated when incidents are added/removed)
|
|
644
|
+
* {{incidentCount}}
|
|
645
|
+
*/
|
|
646
|
+
result = result.replace(/\{\{incidentCount\}\}/g, incidentCount.toString());
|
|
647
|
+
// Clean up any remaining unknown placeholders
|
|
648
|
+
result = result.replace(/\{\{[^}]+\}\}/g, "");
|
|
649
|
+
return result;
|
|
650
|
+
}
|
|
651
|
+
/*
|
|
652
|
+
* Preprocess template: replace static variables but keep dynamic ones as placeholders
|
|
653
|
+
* This is stored on the episode so we can re-render with updated dynamic values later
|
|
654
|
+
*/
|
|
655
|
+
preprocessTemplate(incident, template) {
|
|
656
|
+
var _a, _b;
|
|
657
|
+
let result = template;
|
|
658
|
+
/*
|
|
659
|
+
* Replace static variables (from first incident)
|
|
660
|
+
* {{incidentTitle}}
|
|
661
|
+
*/
|
|
662
|
+
if (incident.title) {
|
|
663
|
+
result = result.replace(/\{\{incidentTitle\}\}/g, incident.title);
|
|
664
|
+
}
|
|
665
|
+
// {{incidentDescription}}
|
|
666
|
+
if (incident.description) {
|
|
667
|
+
result = result.replace(/\{\{incidentDescription\}\}/g, incident.description);
|
|
668
|
+
}
|
|
669
|
+
// {{monitorName}} - use first monitor's name
|
|
670
|
+
if (incident.monitors &&
|
|
671
|
+
incident.monitors.length > 0 &&
|
|
672
|
+
((_a = incident.monitors[0]) === null || _a === void 0 ? void 0 : _a.name)) {
|
|
673
|
+
result = result.replace(/\{\{monitorName\}\}/g, incident.monitors[0].name);
|
|
674
|
+
}
|
|
675
|
+
// {{incidentSeverity}}
|
|
676
|
+
if ((_b = incident.incidentSeverity) === null || _b === void 0 ? void 0 : _b.name) {
|
|
677
|
+
result = result.replace(/\{\{incidentSeverity\}\}/g, incident.incidentSeverity.name);
|
|
678
|
+
}
|
|
679
|
+
/*
|
|
680
|
+
* Keep dynamic variables as placeholders (e.g., {{incidentCount}})
|
|
681
|
+
* They will be replaced when title/description is re-rendered
|
|
682
|
+
*/
|
|
683
|
+
return result;
|
|
684
|
+
}
|
|
685
|
+
async addIncidentToEpisode(incident, episodeId, addedBy, ruleId) {
|
|
686
|
+
const member = new IncidentEpisodeMember();
|
|
687
|
+
member.projectId = incident.projectId;
|
|
688
|
+
member.incidentEpisodeId = episodeId;
|
|
689
|
+
member.incidentId = incident.id;
|
|
690
|
+
member.addedBy = addedBy;
|
|
691
|
+
if (ruleId) {
|
|
692
|
+
member.matchedRuleId = ruleId;
|
|
693
|
+
}
|
|
694
|
+
try {
|
|
695
|
+
await IncidentEpisodeMemberService.create({
|
|
696
|
+
data: member,
|
|
697
|
+
props: {
|
|
698
|
+
isRoot: true,
|
|
699
|
+
},
|
|
700
|
+
});
|
|
701
|
+
// Feed entries are created by IncidentEpisodeMemberService.onCreateSuccess
|
|
702
|
+
}
|
|
703
|
+
catch (error) {
|
|
704
|
+
// Check if it's a duplicate error (incident already in episode)
|
|
705
|
+
if (error instanceof Error &&
|
|
706
|
+
error.message.includes("already a member")) {
|
|
707
|
+
logger.debug(`Incident ${incident.id} is already in episode ${episodeId}`);
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
throw error;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
async addIncidentToEpisodeManually(incident, episodeId, addedByUserId) {
|
|
714
|
+
const member = new IncidentEpisodeMember();
|
|
715
|
+
member.projectId = incident.projectId;
|
|
716
|
+
member.incidentEpisodeId = episodeId;
|
|
717
|
+
member.incidentId = incident.id;
|
|
718
|
+
member.addedBy = IncidentEpisodeMemberAddedBy.Manual;
|
|
719
|
+
if (addedByUserId) {
|
|
720
|
+
member.addedByUserId = addedByUserId;
|
|
721
|
+
}
|
|
722
|
+
await IncidentEpisodeMemberService.create({
|
|
723
|
+
data: member,
|
|
724
|
+
props: {
|
|
725
|
+
isRoot: true,
|
|
726
|
+
},
|
|
727
|
+
});
|
|
728
|
+
// Feed entries are created by IncidentEpisodeMemberService.onCreateSuccess
|
|
729
|
+
// Update episode severity if needed
|
|
730
|
+
if (incident.incidentSeverityId) {
|
|
731
|
+
await IncidentEpisodeService.updateEpisodeSeverity(episodeId, incident.incidentSeverityId, true);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
__decorate([
|
|
736
|
+
CaptureSpan(),
|
|
737
|
+
__metadata("design:type", Function),
|
|
738
|
+
__metadata("design:paramtypes", [Incident]),
|
|
739
|
+
__metadata("design:returntype", Promise)
|
|
740
|
+
], IncidentGroupingEngineServiceClass.prototype, "processIncident", null);
|
|
741
|
+
__decorate([
|
|
742
|
+
CaptureSpan(),
|
|
743
|
+
__metadata("design:type", Function),
|
|
744
|
+
__metadata("design:paramtypes", [Incident,
|
|
745
|
+
IncidentGroupingRule]),
|
|
746
|
+
__metadata("design:returntype", Promise)
|
|
747
|
+
], IncidentGroupingEngineServiceClass.prototype, "doesIncidentMatchRule", null);
|
|
748
|
+
__decorate([
|
|
749
|
+
CaptureSpan(),
|
|
750
|
+
__metadata("design:type", Function),
|
|
751
|
+
__metadata("design:paramtypes", [Incident,
|
|
752
|
+
IncidentGroupingRule]),
|
|
753
|
+
__metadata("design:returntype", Promise)
|
|
754
|
+
], IncidentGroupingEngineServiceClass.prototype, "groupIncidentWithRule", null);
|
|
755
|
+
__decorate([
|
|
756
|
+
CaptureSpan(),
|
|
757
|
+
__metadata("design:type", Function),
|
|
758
|
+
__metadata("design:paramtypes", [Incident,
|
|
759
|
+
IncidentGroupingRule]),
|
|
760
|
+
__metadata("design:returntype", Promise)
|
|
761
|
+
], IncidentGroupingEngineServiceClass.prototype, "buildGroupingKey", null);
|
|
762
|
+
__decorate([
|
|
763
|
+
CaptureSpan(),
|
|
764
|
+
__metadata("design:type", Function),
|
|
765
|
+
__metadata("design:paramtypes", [ObjectID,
|
|
766
|
+
ObjectID, String, Object]),
|
|
767
|
+
__metadata("design:returntype", Promise)
|
|
768
|
+
], IncidentGroupingEngineServiceClass.prototype, "findMatchingActiveEpisode", null);
|
|
769
|
+
__decorate([
|
|
770
|
+
CaptureSpan(),
|
|
771
|
+
__metadata("design:type", Function),
|
|
772
|
+
__metadata("design:paramtypes", [ObjectID,
|
|
773
|
+
ObjectID, String, Date]),
|
|
774
|
+
__metadata("design:returntype", Promise)
|
|
775
|
+
], IncidentGroupingEngineServiceClass.prototype, "findRecentlyResolvedEpisode", null);
|
|
776
|
+
__decorate([
|
|
777
|
+
CaptureSpan(),
|
|
778
|
+
__metadata("design:type", Function),
|
|
779
|
+
__metadata("design:paramtypes", [Incident,
|
|
780
|
+
IncidentGroupingRule, String]),
|
|
781
|
+
__metadata("design:returntype", Promise)
|
|
782
|
+
], IncidentGroupingEngineServiceClass.prototype, "createNewEpisode", null);
|
|
783
|
+
__decorate([
|
|
784
|
+
CaptureSpan(),
|
|
785
|
+
__metadata("design:type", Function),
|
|
786
|
+
__metadata("design:paramtypes", [Incident,
|
|
787
|
+
ObjectID, String, ObjectID]),
|
|
788
|
+
__metadata("design:returntype", Promise)
|
|
789
|
+
], IncidentGroupingEngineServiceClass.prototype, "addIncidentToEpisode", null);
|
|
790
|
+
__decorate([
|
|
791
|
+
CaptureSpan(),
|
|
792
|
+
__metadata("design:type", Function),
|
|
793
|
+
__metadata("design:paramtypes", [Incident,
|
|
794
|
+
ObjectID,
|
|
795
|
+
ObjectID]),
|
|
796
|
+
__metadata("design:returntype", Promise)
|
|
797
|
+
], IncidentGroupingEngineServiceClass.prototype, "addIncidentToEpisodeManually", null);
|
|
798
|
+
export default new IncidentGroupingEngineServiceClass();
|
|
799
|
+
//# sourceMappingURL=IncidentGroupingEngineService.js.map
|