@oneuptime/common 9.5.2 → 9.5.4

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.
Files changed (218) hide show
  1. package/Models/DatabaseModels/Alert.ts +28 -0
  2. package/Models/DatabaseModels/AlertEpisode.ts +27 -0
  3. package/Models/DatabaseModels/AlertEpisodeStateTimeline.ts +1 -0
  4. package/Models/DatabaseModels/AlertStateTimeline.ts +1 -0
  5. package/Models/DatabaseModels/Incident.ts +28 -0
  6. package/Models/DatabaseModels/IncidentEpisode.ts +182 -0
  7. package/Models/DatabaseModels/IncidentEpisodeFeed.ts +2 -0
  8. package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +611 -0
  9. package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +84 -0
  10. package/Models/DatabaseModels/IncidentGroupingRule.ts +36 -0
  11. package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
  12. package/Models/DatabaseModels/Index.ts +2 -0
  13. package/Models/DatabaseModels/MonitorStatusTimeline.ts +1 -0
  14. package/Models/DatabaseModels/Project.ts +252 -1
  15. package/Models/DatabaseModels/ProjectCallSMSConfig.ts +1 -0
  16. package/Models/DatabaseModels/ScheduledMaintenance.ts +28 -0
  17. package/Models/DatabaseModels/ScheduledMaintenanceTemplate.ts +1 -0
  18. package/Models/DatabaseModels/StatusPage.ts +120 -0
  19. package/Server/API/IncidentEpisodePublicNoteAPI.ts +98 -0
  20. package/Server/API/StatusPageAPI.ts +1092 -45
  21. package/Server/Infrastructure/Postgres/SchemaMigrations/1770232207959-MigrationName.ts +181 -0
  22. package/Server/Infrastructure/Postgres/SchemaMigrations/1770237245069-MigrationName.ts +35 -0
  23. package/Server/Infrastructure/Postgres/SchemaMigrations/1770237245070-MigrationName.ts +57 -0
  24. package/Server/Infrastructure/Postgres/SchemaMigrations/1770407024682-MigrationName.ts +83 -0
  25. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
  26. package/Server/Services/AlertEpisodeMemberService.ts +6 -3
  27. package/Server/Services/AlertEpisodeService.ts +45 -97
  28. package/Server/Services/AlertEpisodeStateTimelineService.ts +4 -2
  29. package/Server/Services/AlertInternalNoteService.ts +5 -2
  30. package/Server/Services/AlertOwnerTeamService.ts +10 -4
  31. package/Server/Services/AlertOwnerUserService.ts +10 -4
  32. package/Server/Services/AlertService.ts +24 -38
  33. package/Server/Services/AlertStateTimelineService.ts +6 -3
  34. package/Server/Services/DatabaseService.ts +12 -0
  35. package/Server/Services/IncidentEpisodeMemberService.ts +8 -4
  36. package/Server/Services/IncidentEpisodePublicNoteService.ts +257 -0
  37. package/Server/Services/IncidentEpisodeService.ts +67 -93
  38. package/Server/Services/IncidentEpisodeStateTimelineService.ts +4 -2
  39. package/Server/Services/IncidentInternalNoteService.ts +10 -5
  40. package/Server/Services/IncidentMemberService.ts +20 -10
  41. package/Server/Services/IncidentOwnerTeamService.ts +20 -10
  42. package/Server/Services/IncidentOwnerUserService.ts +20 -10
  43. package/Server/Services/IncidentPublicNoteService.ts +10 -5
  44. package/Server/Services/IncidentService.ts +34 -110
  45. package/Server/Services/IncidentStateTimelineService.ts +11 -6
  46. package/Server/Services/Index.ts +2 -0
  47. package/Server/Services/OnCallDutyPolicyExecutionLogService.ts +61 -39
  48. package/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.ts +31 -19
  49. package/Server/Services/ProjectService.ts +227 -0
  50. package/Server/Services/ScheduledMaintenanceInternalNoteService.ts +9 -6
  51. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +9 -6
  52. package/Server/Services/ScheduledMaintenanceService.ts +27 -39
  53. package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +8 -6
  54. package/Server/Services/UserNotificationRuleService.ts +32 -21
  55. package/Server/Utils/AI/IncidentEpisodeAIContextBuilder.ts +4 -2
  56. package/Server/Utils/Browser.ts +28 -20
  57. package/Server/Utils/Monitor/MonitorAlert.ts +5 -0
  58. package/Server/Utils/Monitor/MonitorIncident.ts +13 -0
  59. package/Server/Utils/PushNotificationUtil.ts +69 -26
  60. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +8 -4
  61. package/Server/Utils/Workspace/Slack/Actions/Alert.ts +20 -8
  62. package/Server/Utils/Workspace/Slack/Actions/Incident.ts +42 -22
  63. package/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.ts +23 -17
  64. package/Server/Utils/Workspace/WorkspaceMessages/Alert.ts +1 -0
  65. package/Server/Utils/Workspace/WorkspaceMessages/Incident.ts +1 -0
  66. package/Server/Utils/Workspace/WorkspaceMessages/ScheduledMaintenance.ts +1 -0
  67. package/Types/Email/EmailTemplateType.ts +4 -0
  68. package/Types/Icon/IconProp.ts +172 -0
  69. package/Types/Monitor/CriteriaIncident.ts +2 -0
  70. package/Types/Monitor/MonitorEvaluationSummary.ts +2 -0
  71. package/Types/Permission.ts +40 -0
  72. package/Types/StatusPage/StatusPageSubscriberNotificationEventType.ts +5 -0
  73. package/UI/Components/Icon/Icon.tsx +1333 -1
  74. package/Utils/Analytics.ts +11 -0
  75. package/build/dist/Models/DatabaseModels/Alert.js +30 -0
  76. package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
  77. package/build/dist/Models/DatabaseModels/AlertEpisode.js +29 -0
  78. package/build/dist/Models/DatabaseModels/AlertEpisode.js.map +1 -1
  79. package/build/dist/Models/DatabaseModels/AlertEpisodeStateTimeline.js +1 -0
  80. package/build/dist/Models/DatabaseModels/AlertEpisodeStateTimeline.js.map +1 -1
  81. package/build/dist/Models/DatabaseModels/AlertStateTimeline.js +1 -0
  82. package/build/dist/Models/DatabaseModels/AlertStateTimeline.js.map +1 -1
  83. package/build/dist/Models/DatabaseModels/Incident.js +30 -0
  84. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  85. package/build/dist/Models/DatabaseModels/IncidentEpisode.js +189 -0
  86. package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
  87. package/build/dist/Models/DatabaseModels/IncidentEpisodeFeed.js +2 -0
  88. package/build/dist/Models/DatabaseModels/IncidentEpisodeFeed.js.map +1 -1
  89. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +626 -0
  90. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -0
  91. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +86 -0
  92. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
  93. package/build/dist/Models/DatabaseModels/IncidentGroupingRule.js +37 -0
  94. package/build/dist/Models/DatabaseModels/IncidentGroupingRule.js.map +1 -1
  95. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
  96. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
  97. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  98. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  99. package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js +1 -0
  100. package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js.map +1 -1
  101. package/build/dist/Models/DatabaseModels/Project.js +267 -1
  102. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  103. package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js +1 -0
  104. package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js.map +1 -1
  105. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +29 -0
  106. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  107. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js +1 -0
  108. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js.map +1 -1
  109. package/build/dist/Models/DatabaseModels/StatusPage.js +126 -0
  110. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  111. package/build/dist/Server/API/IncidentEpisodePublicNoteAPI.js +68 -0
  112. package/build/dist/Server/API/IncidentEpisodePublicNoteAPI.js.map +1 -0
  113. package/build/dist/Server/API/StatusPageAPI.js +874 -47
  114. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  115. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770232207959-MigrationName.js +68 -0
  116. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770232207959-MigrationName.js.map +1 -0
  117. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770237245069-MigrationName.js +18 -0
  118. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770237245069-MigrationName.js.map +1 -0
  119. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770237245070-MigrationName.js +27 -0
  120. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770237245070-MigrationName.js.map +1 -0
  121. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770407024682-MigrationName.js +34 -0
  122. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770407024682-MigrationName.js.map +1 -0
  123. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
  124. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  125. package/build/dist/Server/Services/AlertEpisodeMemberService.js +6 -3
  126. package/build/dist/Server/Services/AlertEpisodeMemberService.js.map +1 -1
  127. package/build/dist/Server/Services/AlertEpisodeService.js +33 -90
  128. package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
  129. package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js +3 -2
  130. package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js.map +1 -1
  131. package/build/dist/Server/Services/AlertInternalNoteService.js +2 -2
  132. package/build/dist/Server/Services/AlertInternalNoteService.js.map +1 -1
  133. package/build/dist/Server/Services/AlertOwnerTeamService.js +4 -4
  134. package/build/dist/Server/Services/AlertOwnerTeamService.js.map +1 -1
  135. package/build/dist/Server/Services/AlertOwnerUserService.js +4 -4
  136. package/build/dist/Server/Services/AlertOwnerUserService.js.map +1 -1
  137. package/build/dist/Server/Services/AlertService.js +16 -34
  138. package/build/dist/Server/Services/AlertService.js.map +1 -1
  139. package/build/dist/Server/Services/AlertStateTimelineService.js +3 -3
  140. package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
  141. package/build/dist/Server/Services/DatabaseService.js +9 -0
  142. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  143. package/build/dist/Server/Services/IncidentEpisodeMemberService.js +8 -4
  144. package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
  145. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +224 -0
  146. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -0
  147. package/build/dist/Server/Services/IncidentEpisodeService.js +47 -82
  148. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  149. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +3 -2
  150. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
  151. package/build/dist/Server/Services/IncidentInternalNoteService.js +4 -2
  152. package/build/dist/Server/Services/IncidentInternalNoteService.js.map +1 -1
  153. package/build/dist/Server/Services/IncidentMemberService.js +8 -4
  154. package/build/dist/Server/Services/IncidentMemberService.js.map +1 -1
  155. package/build/dist/Server/Services/IncidentOwnerTeamService.js +8 -4
  156. package/build/dist/Server/Services/IncidentOwnerTeamService.js.map +1 -1
  157. package/build/dist/Server/Services/IncidentOwnerUserService.js +8 -4
  158. package/build/dist/Server/Services/IncidentOwnerUserService.js.map +1 -1
  159. package/build/dist/Server/Services/IncidentPublicNoteService.js +4 -2
  160. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  161. package/build/dist/Server/Services/IncidentService.js +24 -94
  162. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  163. package/build/dist/Server/Services/IncidentStateTimelineService.js +5 -3
  164. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  165. package/build/dist/Server/Services/Index.js +2 -0
  166. package/build/dist/Server/Services/Index.js.map +1 -1
  167. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js +20 -16
  168. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js.map +1 -1
  169. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js +10 -8
  170. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js.map +1 -1
  171. package/build/dist/Server/Services/ProjectService.js +207 -0
  172. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  173. package/build/dist/Server/Services/ScheduledMaintenanceInternalNoteService.js +4 -3
  174. package/build/dist/Server/Services/ScheduledMaintenanceInternalNoteService.js.map +1 -1
  175. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +4 -3
  176. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  177. package/build/dist/Server/Services/ScheduledMaintenanceService.js +16 -37
  178. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  179. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +3 -3
  180. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
  181. package/build/dist/Server/Services/UserNotificationRuleService.js +33 -25
  182. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  183. package/build/dist/Server/Utils/AI/IncidentEpisodeAIContextBuilder.js +4 -2
  184. package/build/dist/Server/Utils/AI/IncidentEpisodeAIContextBuilder.js.map +1 -1
  185. package/build/dist/Server/Utils/Browser.js +19 -12
  186. package/build/dist/Server/Utils/Browser.js.map +1 -1
  187. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +4 -0
  188. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  189. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +9 -0
  190. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  191. package/build/dist/Server/Utils/PushNotificationUtil.js +36 -28
  192. package/build/dist/Server/Utils/PushNotificationUtil.js.map +1 -1
  193. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +8 -4
  194. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  195. package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js +8 -8
  196. package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js.map +1 -1
  197. package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js +18 -10
  198. package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js.map +1 -1
  199. package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js +8 -8
  200. package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js.map +1 -1
  201. package/build/dist/Server/Utils/Workspace/WorkspaceMessages/Alert.js.map +1 -1
  202. package/build/dist/Server/Utils/Workspace/WorkspaceMessages/Incident.js.map +1 -1
  203. package/build/dist/Server/Utils/Workspace/WorkspaceMessages/ScheduledMaintenance.js.map +1 -1
  204. package/build/dist/Types/Email/EmailTemplateType.js +3 -0
  205. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  206. package/build/dist/Types/Icon/IconProp.js +172 -0
  207. package/build/dist/Types/Icon/IconProp.js.map +1 -1
  208. package/build/dist/Types/Monitor/CriteriaIncident.js +1 -0
  209. package/build/dist/Types/Monitor/CriteriaIncident.js.map +1 -1
  210. package/build/dist/Types/Permission.js +34 -0
  211. package/build/dist/Types/Permission.js.map +1 -1
  212. package/build/dist/Types/StatusPage/StatusPageSubscriberNotificationEventType.js +4 -0
  213. package/build/dist/Types/StatusPage/StatusPageSubscriberNotificationEventType.js.map +1 -1
  214. package/build/dist/UI/Components/Icon/Icon.js +502 -1
  215. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  216. package/build/dist/Utils/Analytics.js +5 -0
  217. package/build/dist/Utils/Analytics.js.map +1 -1
  218. package/package.json +1 -1
@@ -0,0 +1,181 @@
1
+ import { MigrationInterface, QueryRunner } from "typeorm";
2
+
3
+ export class MigrationName1770232207959 implements MigrationInterface {
4
+ public name = "MigrationName1770232207959";
5
+
6
+ public async up(queryRunner: QueryRunner): Promise<void> {
7
+ await queryRunner.query(
8
+ `CREATE TABLE "IncidentEpisodePublicNote" ("_id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "version" integer NOT NULL, "projectId" uuid NOT NULL, "incidentEpisodeId" uuid NOT NULL, "createdByUserId" uuid, "deletedByUserId" uuid, "note" text NOT NULL, "subscriberNotificationStatusOnNoteCreated" character varying NOT NULL DEFAULT 'Pending', "subscriberNotificationStatusMessage" text, "shouldStatusPageSubscribersBeNotifiedOnNoteCreated" boolean NOT NULL DEFAULT true, "isOwnerNotified" boolean NOT NULL DEFAULT false, "postedAt" TIMESTAMP WITH TIME ZONE, "postedFromSlackMessageId" character varying, CONSTRAINT "PK_7ac3241d9cf7bdf5ecac42c9c98" PRIMARY KEY ("_id"))`,
9
+ );
10
+ await queryRunner.query(
11
+ `CREATE INDEX "IDX_46268b7b996f3b8129136d12d2" ON "IncidentEpisodePublicNote" ("projectId") `,
12
+ );
13
+ await queryRunner.query(
14
+ `CREATE INDEX "IDX_885de505291c2f6f4f86b988ca" ON "IncidentEpisodePublicNote" ("incidentEpisodeId") `,
15
+ );
16
+ await queryRunner.query(
17
+ `CREATE INDEX "IDX_2e0c6dd840bbc05b9a5117f882" ON "IncidentEpisodePublicNote" ("isOwnerNotified") `,
18
+ );
19
+ await queryRunner.query(
20
+ `CREATE INDEX "IDX_784fb865a857417194a37034ed" ON "IncidentEpisodePublicNote" ("postedFromSlackMessageId") `,
21
+ );
22
+ await queryRunner.query(
23
+ `CREATE TABLE "IncidentEpisodePublicNoteFile" ("incidentEpisodePublicNoteId" uuid NOT NULL, "fileId" uuid NOT NULL, CONSTRAINT "PK_546f0c2cf908ebf301fb164a1ed" PRIMARY KEY ("incidentEpisodePublicNoteId", "fileId"))`,
24
+ );
25
+ await queryRunner.query(
26
+ `CREATE INDEX "IDX_8e439d24c574141d9a66d74fa5" ON "IncidentEpisodePublicNoteFile" ("incidentEpisodePublicNoteId") `,
27
+ );
28
+ await queryRunner.query(
29
+ `CREATE INDEX "IDX_0dfcd7747b6e55239df9bbaac5" ON "IncidentEpisodePublicNoteFile" ("fileId") `,
30
+ );
31
+ await queryRunner.query(
32
+ `ALTER TABLE "IncidentGroupingRule" ADD "showEpisodeOnStatusPage" boolean NOT NULL DEFAULT false`,
33
+ );
34
+ await queryRunner.query(
35
+ `ALTER TABLE "IncidentEpisode" ADD "isVisibleOnStatusPage" boolean NOT NULL DEFAULT false`,
36
+ );
37
+ await queryRunner.query(
38
+ `ALTER TABLE "IncidentEpisode" ADD "declaredAt" TIMESTAMP WITH TIME ZONE`,
39
+ );
40
+ await queryRunner.query(
41
+ `ALTER TABLE "IncidentEpisode" ADD "shouldStatusPageSubscribersBeNotifiedOnEpisodeCreated" boolean NOT NULL DEFAULT true`,
42
+ );
43
+ await queryRunner.query(
44
+ `ALTER TABLE "IncidentEpisode" ADD "subscriberNotificationStatusOnEpisodeCreated" character varying NOT NULL DEFAULT 'Pending'`,
45
+ );
46
+ await queryRunner.query(
47
+ `ALTER TABLE "IncidentEpisode" ADD "subscriberNotificationStatusMessage" text`,
48
+ );
49
+ await queryRunner.query(
50
+ `ALTER TABLE "StatusPage" ADD "showEpisodesOnStatusPage" boolean NOT NULL DEFAULT true`,
51
+ );
52
+ await queryRunner.query(
53
+ `ALTER TABLE "IncidentEpisodeStateTimeline" ADD "shouldStatusPageSubscribersBeNotified" boolean NOT NULL DEFAULT true`,
54
+ );
55
+ await queryRunner.query(
56
+ `ALTER TABLE "IncidentEpisodeStateTimeline" ADD "subscriberNotificationStatus" character varying NOT NULL DEFAULT 'Pending'`,
57
+ );
58
+ await queryRunner.query(
59
+ `ALTER TABLE "IncidentEpisodeStateTimeline" ADD "subscriberNotificationStatusMessage" text`,
60
+ );
61
+ await queryRunner.query(
62
+ `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type":"Recurring","value":{"intervalType":"Day","intervalCount":{"_type":"PositiveNumber","value":1}}}'`,
63
+ );
64
+ await queryRunner.query(
65
+ `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type":"RestrictionTimes","value":{"restictionType":"None","dayRestrictionTimes":null,"weeklyRestrictionTimes":[]}}'`,
66
+ );
67
+ await queryRunner.query(
68
+ `CREATE INDEX "IDX_9d7bfebab97dc56583df143ae8" ON "IncidentGroupingRule" ("showEpisodeOnStatusPage") `,
69
+ );
70
+ await queryRunner.query(
71
+ `CREATE INDEX "IDX_298e3524e859ddb920c05e1072" ON "IncidentEpisode" ("isVisibleOnStatusPage") `,
72
+ );
73
+ await queryRunner.query(
74
+ `CREATE INDEX "IDX_30d4a06d51be505b6233185906" ON "IncidentEpisode" ("declaredAt") `,
75
+ );
76
+ await queryRunner.query(
77
+ `ALTER TABLE "IncidentEpisodePublicNote" ADD CONSTRAINT "FK_46268b7b996f3b8129136d12d21" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
78
+ );
79
+ await queryRunner.query(
80
+ `ALTER TABLE "IncidentEpisodePublicNote" ADD CONSTRAINT "FK_885de505291c2f6f4f86b988ca6" FOREIGN KEY ("incidentEpisodeId") REFERENCES "IncidentEpisode"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
81
+ );
82
+ await queryRunner.query(
83
+ `ALTER TABLE "IncidentEpisodePublicNote" ADD CONSTRAINT "FK_7711bde5254d7c1021bbbb0f944" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
84
+ );
85
+ await queryRunner.query(
86
+ `ALTER TABLE "IncidentEpisodePublicNote" ADD CONSTRAINT "FK_96d88a43c5479b67ddd7c70df16" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
87
+ );
88
+ await queryRunner.query(
89
+ `ALTER TABLE "IncidentEpisodePublicNoteFile" ADD CONSTRAINT "FK_8e439d24c574141d9a66d74fa5c" FOREIGN KEY ("incidentEpisodePublicNoteId") REFERENCES "IncidentEpisodePublicNote"("_id") ON DELETE CASCADE ON UPDATE CASCADE`,
90
+ );
91
+ await queryRunner.query(
92
+ `ALTER TABLE "IncidentEpisodePublicNoteFile" ADD CONSTRAINT "FK_0dfcd7747b6e55239df9bbaac59" FOREIGN KEY ("fileId") REFERENCES "File"("_id") ON DELETE CASCADE ON UPDATE CASCADE`,
93
+ );
94
+ }
95
+
96
+ public async down(queryRunner: QueryRunner): Promise<void> {
97
+ await queryRunner.query(
98
+ `ALTER TABLE "IncidentEpisodePublicNoteFile" DROP CONSTRAINT "FK_0dfcd7747b6e55239df9bbaac59"`,
99
+ );
100
+ await queryRunner.query(
101
+ `ALTER TABLE "IncidentEpisodePublicNoteFile" DROP CONSTRAINT "FK_8e439d24c574141d9a66d74fa5c"`,
102
+ );
103
+ await queryRunner.query(
104
+ `ALTER TABLE "IncidentEpisodePublicNote" DROP CONSTRAINT "FK_96d88a43c5479b67ddd7c70df16"`,
105
+ );
106
+ await queryRunner.query(
107
+ `ALTER TABLE "IncidentEpisodePublicNote" DROP CONSTRAINT "FK_7711bde5254d7c1021bbbb0f944"`,
108
+ );
109
+ await queryRunner.query(
110
+ `ALTER TABLE "IncidentEpisodePublicNote" DROP CONSTRAINT "FK_885de505291c2f6f4f86b988ca6"`,
111
+ );
112
+ await queryRunner.query(
113
+ `ALTER TABLE "IncidentEpisodePublicNote" DROP CONSTRAINT "FK_46268b7b996f3b8129136d12d21"`,
114
+ );
115
+ await queryRunner.query(
116
+ `DROP INDEX "public"."IDX_30d4a06d51be505b6233185906"`,
117
+ );
118
+ await queryRunner.query(
119
+ `DROP INDEX "public"."IDX_298e3524e859ddb920c05e1072"`,
120
+ );
121
+ await queryRunner.query(
122
+ `DROP INDEX "public"."IDX_9d7bfebab97dc56583df143ae8"`,
123
+ );
124
+ await queryRunner.query(
125
+ `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type": "RestrictionTimes", "value": {"restictionType": "None", "dayRestrictionTimes": null, "weeklyRestrictionTimes": []}}'`,
126
+ );
127
+ await queryRunner.query(
128
+ `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type": "Recurring", "value": {"intervalType": "Day", "intervalCount": {"_type": "PositiveNumber", "value": 1}}}'`,
129
+ );
130
+ await queryRunner.query(
131
+ `ALTER TABLE "IncidentEpisodeStateTimeline" DROP COLUMN "subscriberNotificationStatusMessage"`,
132
+ );
133
+ await queryRunner.query(
134
+ `ALTER TABLE "IncidentEpisodeStateTimeline" DROP COLUMN "subscriberNotificationStatus"`,
135
+ );
136
+ await queryRunner.query(
137
+ `ALTER TABLE "IncidentEpisodeStateTimeline" DROP COLUMN "shouldStatusPageSubscribersBeNotified"`,
138
+ );
139
+ await queryRunner.query(
140
+ `ALTER TABLE "StatusPage" DROP COLUMN "showEpisodesOnStatusPage"`,
141
+ );
142
+ await queryRunner.query(
143
+ `ALTER TABLE "IncidentEpisode" DROP COLUMN "subscriberNotificationStatusMessage"`,
144
+ );
145
+ await queryRunner.query(
146
+ `ALTER TABLE "IncidentEpisode" DROP COLUMN "subscriberNotificationStatusOnEpisodeCreated"`,
147
+ );
148
+ await queryRunner.query(
149
+ `ALTER TABLE "IncidentEpisode" DROP COLUMN "shouldStatusPageSubscribersBeNotifiedOnEpisodeCreated"`,
150
+ );
151
+ await queryRunner.query(
152
+ `ALTER TABLE "IncidentEpisode" DROP COLUMN "declaredAt"`,
153
+ );
154
+ await queryRunner.query(
155
+ `ALTER TABLE "IncidentEpisode" DROP COLUMN "isVisibleOnStatusPage"`,
156
+ );
157
+ await queryRunner.query(
158
+ `ALTER TABLE "IncidentGroupingRule" DROP COLUMN "showEpisodeOnStatusPage"`,
159
+ );
160
+ await queryRunner.query(
161
+ `DROP INDEX "public"."IDX_0dfcd7747b6e55239df9bbaac5"`,
162
+ );
163
+ await queryRunner.query(
164
+ `DROP INDEX "public"."IDX_8e439d24c574141d9a66d74fa5"`,
165
+ );
166
+ await queryRunner.query(`DROP TABLE "IncidentEpisodePublicNoteFile"`);
167
+ await queryRunner.query(
168
+ `DROP INDEX "public"."IDX_784fb865a857417194a37034ed"`,
169
+ );
170
+ await queryRunner.query(
171
+ `DROP INDEX "public"."IDX_2e0c6dd840bbc05b9a5117f882"`,
172
+ );
173
+ await queryRunner.query(
174
+ `DROP INDEX "public"."IDX_885de505291c2f6f4f86b988ca"`,
175
+ );
176
+ await queryRunner.query(
177
+ `DROP INDEX "public"."IDX_46268b7b996f3b8129136d12d2"`,
178
+ );
179
+ await queryRunner.query(`DROP TABLE "IncidentEpisodePublicNote"`);
180
+ }
181
+ }
@@ -0,0 +1,35 @@
1
+ import { MigrationInterface, QueryRunner } from "typeorm";
2
+
3
+ export class MigrationName1770237245069 implements MigrationInterface {
4
+ public name = "MigrationName1770237245069";
5
+
6
+ public async up(queryRunner: QueryRunner): Promise<void> {
7
+ await queryRunner.query(
8
+ `ALTER TABLE "StatusPage" ADD "showEpisodeHistoryInDays" integer NOT NULL DEFAULT '14'`,
9
+ );
10
+ await queryRunner.query(
11
+ `ALTER TABLE "StatusPage" ADD "showEpisodeLabelsOnStatusPage" boolean NOT NULL DEFAULT false`,
12
+ );
13
+ await queryRunner.query(
14
+ `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type":"Recurring","value":{"intervalType":"Day","intervalCount":{"_type":"PositiveNumber","value":1}}}'`,
15
+ );
16
+ await queryRunner.query(
17
+ `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type":"RestrictionTimes","value":{"restictionType":"None","dayRestrictionTimes":null,"weeklyRestrictionTimes":[]}}'`,
18
+ );
19
+ }
20
+
21
+ public async down(queryRunner: QueryRunner): Promise<void> {
22
+ await queryRunner.query(
23
+ `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type": "RestrictionTimes", "value": {"restictionType": "None", "dayRestrictionTimes": null, "weeklyRestrictionTimes": []}}'`,
24
+ );
25
+ await queryRunner.query(
26
+ `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type": "Recurring", "value": {"intervalType": "Day", "intervalCount": {"_type": "PositiveNumber", "value": 1}}}'`,
27
+ );
28
+ await queryRunner.query(
29
+ `ALTER TABLE "StatusPage" DROP COLUMN "showEpisodeLabelsOnStatusPage"`,
30
+ );
31
+ await queryRunner.query(
32
+ `ALTER TABLE "StatusPage" DROP COLUMN "showEpisodeHistoryInDays"`,
33
+ );
34
+ }
35
+ }
@@ -0,0 +1,57 @@
1
+ import { MigrationInterface, QueryRunner } from "typeorm";
2
+
3
+ export class MigrationName1770237245070 implements MigrationInterface {
4
+ public name = "MigrationName1770237245070";
5
+
6
+ public async up(queryRunner: QueryRunner): Promise<void> {
7
+ // Add counter columns with default 0
8
+ await queryRunner.query(
9
+ `ALTER TABLE "Project" ADD "incidentCounter" integer NOT NULL DEFAULT '0'`,
10
+ );
11
+ await queryRunner.query(
12
+ `ALTER TABLE "Project" ADD "alertCounter" integer NOT NULL DEFAULT '0'`,
13
+ );
14
+ await queryRunner.query(
15
+ `ALTER TABLE "Project" ADD "scheduledMaintenanceCounter" integer NOT NULL DEFAULT '0'`,
16
+ );
17
+ await queryRunner.query(
18
+ `ALTER TABLE "Project" ADD "incidentEpisodeCounter" integer NOT NULL DEFAULT '0'`,
19
+ );
20
+ await queryRunner.query(
21
+ `ALTER TABLE "Project" ADD "alertEpisodeCounter" integer NOT NULL DEFAULT '0'`,
22
+ );
23
+
24
+ // Backfill from MAX of each entity table (including soft-deleted rows)
25
+ await queryRunner.query(
26
+ `UPDATE "Project" SET "incidentCounter" = COALESCE((SELECT MAX("incidentNumber") FROM "Incident" WHERE "Incident"."projectId" = "Project"."_id"), 0)`,
27
+ );
28
+ await queryRunner.query(
29
+ `UPDATE "Project" SET "alertCounter" = COALESCE((SELECT MAX("alertNumber") FROM "Alert" WHERE "Alert"."projectId" = "Project"."_id"), 0)`,
30
+ );
31
+ await queryRunner.query(
32
+ `UPDATE "Project" SET "scheduledMaintenanceCounter" = COALESCE((SELECT MAX("scheduledMaintenanceNumber") FROM "ScheduledMaintenance" WHERE "ScheduledMaintenance"."projectId" = "Project"."_id"), 0)`,
33
+ );
34
+ await queryRunner.query(
35
+ `UPDATE "Project" SET "incidentEpisodeCounter" = COALESCE((SELECT MAX("episodeNumber") FROM "IncidentEpisode" WHERE "IncidentEpisode"."projectId" = "Project"."_id"), 0)`,
36
+ );
37
+ await queryRunner.query(
38
+ `UPDATE "Project" SET "alertEpisodeCounter" = COALESCE((SELECT MAX("episodeNumber") FROM "AlertEpisode" WHERE "AlertEpisode"."projectId" = "Project"."_id"), 0)`,
39
+ );
40
+ }
41
+
42
+ public async down(queryRunner: QueryRunner): Promise<void> {
43
+ await queryRunner.query(
44
+ `ALTER TABLE "Project" DROP COLUMN "alertEpisodeCounter"`,
45
+ );
46
+ await queryRunner.query(
47
+ `ALTER TABLE "Project" DROP COLUMN "incidentEpisodeCounter"`,
48
+ );
49
+ await queryRunner.query(
50
+ `ALTER TABLE "Project" DROP COLUMN "scheduledMaintenanceCounter"`,
51
+ );
52
+ await queryRunner.query(`ALTER TABLE "Project" DROP COLUMN "alertCounter"`);
53
+ await queryRunner.query(
54
+ `ALTER TABLE "Project" DROP COLUMN "incidentCounter"`,
55
+ );
56
+ }
57
+ }
@@ -0,0 +1,83 @@
1
+ import { MigrationInterface, QueryRunner } from "typeorm";
2
+
3
+ export class MigrationName1770407024682 implements MigrationInterface {
4
+ public name = "MigrationName1770407024682";
5
+
6
+ public async up(queryRunner: QueryRunner): Promise<void> {
7
+ await queryRunner.query(
8
+ `ALTER TABLE "Project" ADD "incidentNumberPrefix" character varying(100)`,
9
+ );
10
+ await queryRunner.query(
11
+ `ALTER TABLE "Project" ADD "alertNumberPrefix" character varying(100)`,
12
+ );
13
+ await queryRunner.query(
14
+ `ALTER TABLE "Project" ADD "scheduledMaintenanceNumberPrefix" character varying(100)`,
15
+ );
16
+ await queryRunner.query(
17
+ `ALTER TABLE "Project" ADD "incidentEpisodeNumberPrefix" character varying(100)`,
18
+ );
19
+ await queryRunner.query(
20
+ `ALTER TABLE "Project" ADD "alertEpisodeNumberPrefix" character varying(100)`,
21
+ );
22
+ await queryRunner.query(
23
+ `ALTER TABLE "IncidentEpisode" ADD "episodeNumberWithPrefix" character varying(100)`,
24
+ );
25
+ await queryRunner.query(
26
+ `ALTER TABLE "Incident" ADD "incidentNumberWithPrefix" character varying(100)`,
27
+ );
28
+ await queryRunner.query(
29
+ `ALTER TABLE "AlertEpisode" ADD "episodeNumberWithPrefix" character varying(100)`,
30
+ );
31
+ await queryRunner.query(
32
+ `ALTER TABLE "Alert" ADD "alertNumberWithPrefix" character varying(100)`,
33
+ );
34
+ await queryRunner.query(
35
+ `ALTER TABLE "ScheduledMaintenance" ADD "scheduledMaintenanceNumberWithPrefix" character varying(100)`,
36
+ );
37
+ await queryRunner.query(
38
+ `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type":"Recurring","value":{"intervalType":"Day","intervalCount":{"_type":"PositiveNumber","value":1}}}'`,
39
+ );
40
+ await queryRunner.query(
41
+ `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type":"RestrictionTimes","value":{"restictionType":"None","dayRestrictionTimes":null,"weeklyRestrictionTimes":[]}}'`,
42
+ );
43
+ }
44
+
45
+ public async down(queryRunner: QueryRunner): Promise<void> {
46
+ await queryRunner.query(
47
+ `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type": "RestrictionTimes", "value": {"restictionType": "None", "dayRestrictionTimes": null, "weeklyRestrictionTimes": []}}'`,
48
+ );
49
+ await queryRunner.query(
50
+ `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type": "Recurring", "value": {"intervalType": "Day", "intervalCount": {"_type": "PositiveNumber", "value": 1}}}'`,
51
+ );
52
+ await queryRunner.query(
53
+ `ALTER TABLE "ScheduledMaintenance" DROP COLUMN "scheduledMaintenanceNumberWithPrefix"`,
54
+ );
55
+ await queryRunner.query(
56
+ `ALTER TABLE "Alert" DROP COLUMN "alertNumberWithPrefix"`,
57
+ );
58
+ await queryRunner.query(
59
+ `ALTER TABLE "AlertEpisode" DROP COLUMN "episodeNumberWithPrefix"`,
60
+ );
61
+ await queryRunner.query(
62
+ `ALTER TABLE "Incident" DROP COLUMN "incidentNumberWithPrefix"`,
63
+ );
64
+ await queryRunner.query(
65
+ `ALTER TABLE "IncidentEpisode" DROP COLUMN "episodeNumberWithPrefix"`,
66
+ );
67
+ await queryRunner.query(
68
+ `ALTER TABLE "Project" DROP COLUMN "alertEpisodeNumberPrefix"`,
69
+ );
70
+ await queryRunner.query(
71
+ `ALTER TABLE "Project" DROP COLUMN "incidentEpisodeNumberPrefix"`,
72
+ );
73
+ await queryRunner.query(
74
+ `ALTER TABLE "Project" DROP COLUMN "scheduledMaintenanceNumberPrefix"`,
75
+ );
76
+ await queryRunner.query(
77
+ `ALTER TABLE "Project" DROP COLUMN "alertNumberPrefix"`,
78
+ );
79
+ await queryRunner.query(
80
+ `ALTER TABLE "Project" DROP COLUMN "incidentNumberPrefix"`,
81
+ );
82
+ }
83
+ }
@@ -249,6 +249,10 @@ import { MigrationName1769774527481 } from "./1769774527481-MigrationName";
249
249
  import { MigrationName1769780297584 } from "./1769780297584-MigrationName";
250
250
  import { MigrationName1769802715014 } from "./1769802715014-MigrationName";
251
251
  import { MigrationName1770054293299 } from "./1770054293299-MigrationName";
252
+ import { MigrationName1770232207959 } from "./1770232207959-MigrationName";
253
+ import { MigrationName1770237245069 } from "./1770237245069-MigrationName";
254
+ import { MigrationName1770237245070 } from "./1770237245070-MigrationName";
255
+ import { MigrationName1770407024682 } from "./1770407024682-MigrationName";
252
256
 
253
257
  export default [
254
258
  InitialMigration,
@@ -502,4 +506,8 @@ export default [
502
506
  MigrationName1769780297584,
503
507
  MigrationName1769802715014,
504
508
  MigrationName1770054293299,
509
+ MigrationName1770232207959,
510
+ MigrationName1770237245069,
511
+ MigrationName1770237245070,
512
+ MigrationName1770407024682,
505
513
  ];
@@ -120,6 +120,7 @@ export class Service extends DatabaseService<Model> {
120
120
  id: createdItem.alertId,
121
121
  select: {
122
122
  alertNumber: true,
123
+ alertNumberWithPrefix: true,
123
124
  title: true,
124
125
  },
125
126
  props: {
@@ -132,6 +133,7 @@ export class Service extends DatabaseService<Model> {
132
133
  id: createdItem.alertEpisodeId,
133
134
  select: {
134
135
  episodeNumber: true,
136
+ episodeNumberWithPrefix: true,
135
137
  title: true,
136
138
  },
137
139
  props: {
@@ -145,7 +147,7 @@ export class Service extends DatabaseService<Model> {
145
147
  projectId: createdItem.projectId,
146
148
  alertEpisodeFeedEventType: AlertEpisodeFeedEventType.AlertAdded,
147
149
  displayColor: Yellow500,
148
- feedInfoInMarkdown: `**Alert #${alert?.alertNumber || "N/A"}** added to episode: ${alert?.title || "No title"}`,
150
+ feedInfoInMarkdown: `**Alert ${alert?.alertNumberWithPrefix || "#" + (alert?.alertNumber || "N/A")}** added to episode: ${alert?.title || "No title"}`,
149
151
  userId: createdItem.addedByUserId || undefined,
150
152
  });
151
153
 
@@ -155,7 +157,7 @@ export class Service extends DatabaseService<Model> {
155
157
  projectId: createdItem.projectId,
156
158
  alertFeedEventType: AlertFeedEventType.AddedToEpisode,
157
159
  displayColor: Yellow500,
158
- feedInfoInMarkdown: `Added to **Episode #${episode?.episodeNumber || "N/A"}**: ${episode?.title || "No title"}`,
160
+ feedInfoInMarkdown: `Added to **Episode ${episode?.episodeNumberWithPrefix || "#" + (episode?.episodeNumber || "N/A")}**: ${episode?.title || "No title"}`,
159
161
  userId: createdItem.addedByUserId || undefined,
160
162
  });
161
163
 
@@ -228,6 +230,7 @@ export class Service extends DatabaseService<Model> {
228
230
  id: member.alertEpisodeId,
229
231
  select: {
230
232
  episodeNumber: true,
233
+ episodeNumberWithPrefix: true,
231
234
  title: true,
232
235
  },
233
236
  props: {
@@ -250,7 +253,7 @@ export class Service extends DatabaseService<Model> {
250
253
  projectId: member.projectId,
251
254
  alertFeedEventType: AlertFeedEventType.RemovedFromEpisode,
252
255
  displayColor: Green500,
253
- feedInfoInMarkdown: `Removed from **Episode #${episode?.episodeNumber || "N/A"}**: ${episode?.title || "No title"}`,
256
+ feedInfoInMarkdown: `Removed from **Episode ${episode?.episodeNumberWithPrefix || "#" + (episode?.episodeNumber || "N/A")}**: ${episode?.title || "No title"}`,
254
257
  });
255
258
  }
256
259
  }
@@ -40,10 +40,10 @@ import NotificationRuleWorkspaceChannel from "../../Types/Workspace/Notification
40
40
  import WorkspaceType from "../../Types/Workspace/WorkspaceType";
41
41
  import Typeof from "../../Types/Typeof";
42
42
  import AlertService from "./AlertService";
43
- import Semaphore, { SemaphoreMutex } from "../Infrastructure/Semaphore";
44
43
  import OnCallDutyPolicyService from "./OnCallDutyPolicyService";
45
44
  import OnCallDutyPolicy from "../../Models/DatabaseModels/OnCallDutyPolicy";
46
45
  import UserNotificationEventType from "../../Types/UserNotification/UserNotificationEventType";
46
+ import ProjectService from "./ProjectService";
47
47
 
48
48
  export class Service extends DatabaseService<Model> {
49
49
  public constructor() {
@@ -53,32 +53,6 @@ export class Service extends DatabaseService<Model> {
53
53
  }
54
54
  }
55
55
 
56
- @CaptureSpan()
57
- public async getExistingEpisodeNumberForProject(data: {
58
- projectId: ObjectID;
59
- }): Promise<number> {
60
- const lastEpisode: Model | null = await this.findOneBy({
61
- query: {
62
- projectId: data.projectId,
63
- },
64
- select: {
65
- episodeNumber: true,
66
- },
67
- sort: {
68
- episodeNumber: SortOrder.Descending,
69
- },
70
- props: {
71
- isRoot: true,
72
- },
73
- });
74
-
75
- if (!lastEpisode) {
76
- return 0;
77
- }
78
-
79
- return lastEpisode.episodeNumber ? Number(lastEpisode.episodeNumber) : 0;
80
- }
81
-
82
56
  @CaptureSpan()
83
57
  protected override async onBeforeCreate(
84
58
  createBy: CreateBy<Model>,
@@ -90,83 +64,52 @@ export class Service extends DatabaseService<Model> {
90
64
  const projectId: ObjectID =
91
65
  createBy.props.tenantId || createBy.data.projectId!;
92
66
 
93
- let mutex: SemaphoreMutex | null = null;
94
-
95
- try {
96
- // Acquire mutex to prevent race conditions when generating episode numbers
97
- try {
98
- mutex = await Semaphore.lock({
99
- key: projectId.toString(),
100
- namespace: "AlertEpisode.create",
101
- });
102
- } catch (err) {
103
- logger.error(err);
104
- }
105
-
106
- // Get the created state for episodes
107
- const alertState: AlertState | null = await AlertStateService.findOneBy({
108
- query: {
109
- projectId: projectId,
110
- isCreatedState: true,
111
- },
112
- select: {
113
- _id: true,
114
- },
115
- props: {
116
- isRoot: true,
117
- },
118
- });
119
-
120
- if (!alertState || !alertState.id) {
121
- throw new BadDataException(
122
- "Created alert state not found for this project. Please add created alert state from settings.",
123
- );
124
- }
67
+ // Get the created state for episodes
68
+ const alertState: AlertState | null = await AlertStateService.findOneBy({
69
+ query: {
70
+ projectId: projectId,
71
+ isCreatedState: true,
72
+ },
73
+ select: {
74
+ _id: true,
75
+ },
76
+ props: {
77
+ isRoot: true,
78
+ },
79
+ });
125
80
 
126
- createBy.data.currentAlertStateId = alertState.id;
81
+ if (!alertState || !alertState.id) {
82
+ throw new BadDataException(
83
+ "Created alert state not found for this project. Please add created alert state from settings.",
84
+ );
85
+ }
127
86
 
128
- // Auto-generate episode number
129
- const episodeNumberForThisEpisode: number =
130
- (await this.getExistingEpisodeNumberForProject({
131
- projectId: projectId,
132
- })) + 1;
87
+ createBy.data.currentAlertStateId = alertState.id;
133
88
 
134
- createBy.data.episodeNumber = episodeNumberForThisEpisode;
89
+ // Auto-generate episode number
90
+ const episodeCounterResult: {
91
+ counter: number;
92
+ prefix: string | undefined;
93
+ } = await ProjectService.incrementAndGetAlertEpisodeCounter(projectId);
135
94
 
136
- // Set initial lastAlertAddedAt
137
- if (!createBy.data.lastAlertAddedAt) {
138
- createBy.data.lastAlertAddedAt = OneUptimeDate.getCurrentDate();
139
- }
95
+ createBy.data.episodeNumber = episodeCounterResult.counter;
96
+ createBy.data.episodeNumberWithPrefix = episodeCounterResult.prefix
97
+ ? `${episodeCounterResult.prefix}${episodeCounterResult.counter}`
98
+ : `#${episodeCounterResult.counter}`;
140
99
 
141
- return { createBy, carryForward: { mutex } };
142
- } catch (error) {
143
- // Release the mutex if it was acquired and an error occurred
144
- if (mutex) {
145
- try {
146
- await Semaphore.release(mutex);
147
- } catch (err) {
148
- logger.error(err);
149
- }
150
- }
151
- throw error;
100
+ // Set initial lastAlertAddedAt
101
+ if (!createBy.data.lastAlertAddedAt) {
102
+ createBy.data.lastAlertAddedAt = OneUptimeDate.getCurrentDate();
152
103
  }
104
+
105
+ return { createBy, carryForward: null };
153
106
  }
154
107
 
155
108
  @CaptureSpan()
156
109
  protected override async onCreateSuccess(
157
- onCreate: OnCreate<Model>,
110
+ _onCreate: OnCreate<Model>,
158
111
  createdItem: Model,
159
112
  ): Promise<Model> {
160
- // Release the mutex acquired in onBeforeCreate
161
- const mutex: SemaphoreMutex | null = onCreate.carryForward?.mutex || null;
162
- if (mutex) {
163
- try {
164
- await Semaphore.release(mutex);
165
- } catch (err) {
166
- logger.error(err);
167
- }
168
- }
169
-
170
113
  if (!createdItem.projectId) {
171
114
  throw new BadDataException("projectId is required");
172
115
  }
@@ -233,7 +176,7 @@ export class Service extends DatabaseService<Model> {
233
176
  return;
234
177
  }
235
178
 
236
- let feedInfoInMarkdown: string = `#### Episode ${episode.episodeNumber?.toString()} Created
179
+ let feedInfoInMarkdown: string = `#### Episode ${episode.episodeNumberWithPrefix || "#" + episode.episodeNumber?.toString()} Created
237
180
 
238
181
  **${episode.title || "No title provided."}**
239
182
 
@@ -897,13 +840,15 @@ export class Service extends DatabaseService<Model> {
897
840
  }
898
841
 
899
842
  @CaptureSpan()
900
- public async getEpisodeNumber(data: {
901
- episodeId: ObjectID;
902
- }): Promise<number | null> {
843
+ public async getEpisodeNumber(data: { episodeId: ObjectID }): Promise<{
844
+ number: number | null;
845
+ numberWithPrefix: string | null;
846
+ }> {
903
847
  const episode: Model | null = await this.findOneById({
904
848
  id: data.episodeId,
905
849
  select: {
906
850
  episodeNumber: true,
851
+ episodeNumberWithPrefix: true,
907
852
  },
908
853
  props: {
909
854
  isRoot: true,
@@ -914,7 +859,10 @@ export class Service extends DatabaseService<Model> {
914
859
  throw new BadDataException("Episode not found.");
915
860
  }
916
861
 
917
- return episode.episodeNumber ? Number(episode.episodeNumber) : null;
862
+ return {
863
+ number: episode.episodeNumber ? Number(episode.episodeNumber) : null,
864
+ numberWithPrefix: episode.episodeNumberWithPrefix || null,
865
+ };
918
866
  }
919
867
 
920
868
  @CaptureSpan()
@@ -350,13 +350,15 @@ export class Service extends DatabaseService<AlertEpisodeStateTimeline> {
350
350
  id: createdItem.alertEpisodeId,
351
351
  select: {
352
352
  episodeNumber: true,
353
+ episodeNumberWithPrefix: true,
353
354
  },
354
355
  props: {
355
356
  isRoot: true,
356
357
  },
357
358
  });
358
359
 
359
- const episodeNumber: number = episode?.episodeNumber || 0;
360
+ const episodeDisplayNumber: string =
361
+ episode?.episodeNumberWithPrefix || "#" + (episode?.episodeNumber || 0);
360
362
 
361
363
  await AlertEpisodeFeedService.createAlertEpisodeFeedItem({
362
364
  alertEpisodeId: createdItem.alertEpisodeId!,
@@ -365,7 +367,7 @@ export class Service extends DatabaseService<AlertEpisodeStateTimeline> {
365
367
  displayColor: alertState?.color,
366
368
  feedInfoInMarkdown:
367
369
  stateEmoji +
368
- ` Changed **Episode ${episodeNumber} State** to **` +
370
+ ` Changed **Episode ${episodeDisplayNumber} State** to **` +
369
371
  stateName +
370
372
  "**",
371
373
  moreInformationInMarkdown: createdItem.rootCause