@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
@@ -81,6 +81,7 @@ import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
81
81
  import DatabaseConfig from "../DatabaseConfig";
82
82
  import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
83
83
  import PositiveNumber from "../../Types/PositiveNumber";
84
+ import Semaphore, { SemaphoreMutex } from "../Infrastructure/Semaphore";
84
85
 
85
86
  export interface CurrentPlan {
86
87
  plan: PlanType | null;
@@ -277,6 +278,27 @@ export class ProjectService extends DatabaseService<Model> {
277
278
  data.data.utmContent = user.utmContent!;
278
279
  data.data.utmUrl = user.utmUrl!;
279
280
 
281
+ // Set default number prefixes.
282
+ if (!data.data.incidentNumberPrefix) {
283
+ data.data.incidentNumberPrefix = "INC-";
284
+ }
285
+
286
+ if (!data.data.alertNumberPrefix) {
287
+ data.data.alertNumberPrefix = "ALT-";
288
+ }
289
+
290
+ if (!data.data.scheduledMaintenanceNumberPrefix) {
291
+ data.data.scheduledMaintenanceNumberPrefix = "SM-";
292
+ }
293
+
294
+ if (!data.data.incidentEpisodeNumberPrefix) {
295
+ data.data.incidentEpisodeNumberPrefix = "IE-";
296
+ }
297
+
298
+ if (!data.data.alertEpisodeNumberPrefix) {
299
+ data.data.alertEpisodeNumberPrefix = "AE-";
300
+ }
301
+
280
302
  return Promise.resolve({ createBy: data, carryForward: null });
281
303
  }
282
304
 
@@ -1589,6 +1611,211 @@ export class ProjectService extends DatabaseService<Model> {
1589
1611
  );
1590
1612
  }
1591
1613
 
1614
+ @CaptureSpan()
1615
+ public async incrementAndGetIncidentCounter(
1616
+ projectId: ObjectID,
1617
+ ): Promise<{ counter: number; prefix: string | undefined }> {
1618
+ const mutex: SemaphoreMutex = await Semaphore.lock({
1619
+ key: projectId.toString(),
1620
+ namespace: "ProjectService.incidentCounter",
1621
+ });
1622
+
1623
+ try {
1624
+ await this.atomicIncrementColumnValueByOne({
1625
+ id: projectId,
1626
+ columnName: "incidentCounter",
1627
+ });
1628
+
1629
+ const project: Model | null = await this.findOneById({
1630
+ id: projectId,
1631
+ select: {
1632
+ incidentCounter: true,
1633
+ incidentNumberPrefix: true,
1634
+ },
1635
+ props: {
1636
+ isRoot: true,
1637
+ },
1638
+ });
1639
+
1640
+ if (!project || project.incidentCounter === undefined) {
1641
+ throw new BadDataException(
1642
+ `Could not read incidentCounter for project ${projectId.toString()}`,
1643
+ );
1644
+ }
1645
+
1646
+ return {
1647
+ counter: project.incidentCounter,
1648
+ prefix: project.incidentNumberPrefix,
1649
+ };
1650
+ } finally {
1651
+ await Semaphore.release(mutex);
1652
+ }
1653
+ }
1654
+
1655
+ @CaptureSpan()
1656
+ public async incrementAndGetAlertCounter(
1657
+ projectId: ObjectID,
1658
+ ): Promise<{ counter: number; prefix: string | undefined }> {
1659
+ const mutex: SemaphoreMutex = await Semaphore.lock({
1660
+ key: projectId.toString(),
1661
+ namespace: "ProjectService.alertCounter",
1662
+ });
1663
+
1664
+ try {
1665
+ await this.atomicIncrementColumnValueByOne({
1666
+ id: projectId,
1667
+ columnName: "alertCounter",
1668
+ });
1669
+
1670
+ const project: Model | null = await this.findOneById({
1671
+ id: projectId,
1672
+ select: {
1673
+ alertCounter: true,
1674
+ alertNumberPrefix: true,
1675
+ },
1676
+ props: {
1677
+ isRoot: true,
1678
+ },
1679
+ });
1680
+
1681
+ if (!project || project.alertCounter === undefined) {
1682
+ throw new BadDataException(
1683
+ `Could not read alertCounter for project ${projectId.toString()}`,
1684
+ );
1685
+ }
1686
+
1687
+ return {
1688
+ counter: project.alertCounter,
1689
+ prefix: project.alertNumberPrefix,
1690
+ };
1691
+ } finally {
1692
+ await Semaphore.release(mutex);
1693
+ }
1694
+ }
1695
+
1696
+ @CaptureSpan()
1697
+ public async incrementAndGetScheduledMaintenanceCounter(
1698
+ projectId: ObjectID,
1699
+ ): Promise<{ counter: number; prefix: string | undefined }> {
1700
+ const mutex: SemaphoreMutex = await Semaphore.lock({
1701
+ key: projectId.toString(),
1702
+ namespace: "ProjectService.scheduledMaintenanceCounter",
1703
+ });
1704
+
1705
+ try {
1706
+ await this.atomicIncrementColumnValueByOne({
1707
+ id: projectId,
1708
+ columnName: "scheduledMaintenanceCounter",
1709
+ });
1710
+
1711
+ const project: Model | null = await this.findOneById({
1712
+ id: projectId,
1713
+ select: {
1714
+ scheduledMaintenanceCounter: true,
1715
+ scheduledMaintenanceNumberPrefix: true,
1716
+ },
1717
+ props: {
1718
+ isRoot: true,
1719
+ },
1720
+ });
1721
+
1722
+ if (!project || project.scheduledMaintenanceCounter === undefined) {
1723
+ throw new BadDataException(
1724
+ `Could not read scheduledMaintenanceCounter for project ${projectId.toString()}`,
1725
+ );
1726
+ }
1727
+
1728
+ return {
1729
+ counter: project.scheduledMaintenanceCounter,
1730
+ prefix: project.scheduledMaintenanceNumberPrefix,
1731
+ };
1732
+ } finally {
1733
+ await Semaphore.release(mutex);
1734
+ }
1735
+ }
1736
+
1737
+ @CaptureSpan()
1738
+ public async incrementAndGetIncidentEpisodeCounter(
1739
+ projectId: ObjectID,
1740
+ ): Promise<{ counter: number; prefix: string | undefined }> {
1741
+ const mutex: SemaphoreMutex = await Semaphore.lock({
1742
+ key: projectId.toString(),
1743
+ namespace: "ProjectService.incidentEpisodeCounter",
1744
+ });
1745
+
1746
+ try {
1747
+ await this.atomicIncrementColumnValueByOne({
1748
+ id: projectId,
1749
+ columnName: "incidentEpisodeCounter",
1750
+ });
1751
+
1752
+ const project: Model | null = await this.findOneById({
1753
+ id: projectId,
1754
+ select: {
1755
+ incidentEpisodeCounter: true,
1756
+ incidentEpisodeNumberPrefix: true,
1757
+ },
1758
+ props: {
1759
+ isRoot: true,
1760
+ },
1761
+ });
1762
+
1763
+ if (!project || project.incidentEpisodeCounter === undefined) {
1764
+ throw new BadDataException(
1765
+ `Could not read incidentEpisodeCounter for project ${projectId.toString()}`,
1766
+ );
1767
+ }
1768
+
1769
+ return {
1770
+ counter: project.incidentEpisodeCounter,
1771
+ prefix: project.incidentEpisodeNumberPrefix,
1772
+ };
1773
+ } finally {
1774
+ await Semaphore.release(mutex);
1775
+ }
1776
+ }
1777
+
1778
+ @CaptureSpan()
1779
+ public async incrementAndGetAlertEpisodeCounter(
1780
+ projectId: ObjectID,
1781
+ ): Promise<{ counter: number; prefix: string | undefined }> {
1782
+ const mutex: SemaphoreMutex = await Semaphore.lock({
1783
+ key: projectId.toString(),
1784
+ namespace: "ProjectService.alertEpisodeCounter",
1785
+ });
1786
+
1787
+ try {
1788
+ await this.atomicIncrementColumnValueByOne({
1789
+ id: projectId,
1790
+ columnName: "alertEpisodeCounter",
1791
+ });
1792
+
1793
+ const project: Model | null = await this.findOneById({
1794
+ id: projectId,
1795
+ select: {
1796
+ alertEpisodeCounter: true,
1797
+ alertEpisodeNumberPrefix: true,
1798
+ },
1799
+ props: {
1800
+ isRoot: true,
1801
+ },
1802
+ });
1803
+
1804
+ if (!project || project.alertEpisodeCounter === undefined) {
1805
+ throw new BadDataException(
1806
+ `Could not read alertEpisodeCounter for project ${projectId.toString()}`,
1807
+ );
1808
+ }
1809
+
1810
+ return {
1811
+ counter: project.alertEpisodeCounter,
1812
+ prefix: project.alertEpisodeNumberPrefix,
1813
+ };
1814
+ } finally {
1815
+ await Semaphore.release(mutex);
1816
+ }
1817
+ }
1818
+
1592
1819
  @CaptureSpan()
1593
1820
  public async isSMSNotificationsEnabled(
1594
1821
  projectId: ObjectID,
@@ -86,10 +86,12 @@ export class Service extends DatabaseService<Model> {
86
86
  const scheduledMaintenanceId: ObjectID =
87
87
  createdItem.scheduledMaintenanceId!;
88
88
 
89
- const scheduledMaintenanceNumber: number | null =
90
- await ScheduledMaintenanceService.getScheduledMaintenanceNumber({
91
- scheduledMaintenanceId: scheduledMaintenanceId,
92
- });
89
+ const scheduledMaintenanceNumberResult: {
90
+ number: number | null;
91
+ numberWithPrefix: string | null;
92
+ } = await ScheduledMaintenanceService.getScheduledMaintenanceNumber({
93
+ scheduledMaintenanceId: scheduledMaintenanceId,
94
+ });
93
95
 
94
96
  const attachmentsMarkdown: string = await this.getAttachmentsMarkdown(
95
97
  createdItem.id!,
@@ -104,7 +106,7 @@ export class Service extends DatabaseService<Model> {
104
106
  displayColor: Blue500,
105
107
  userId: userId || undefined,
106
108
 
107
- feedInfoInMarkdown: `📄 posted **private note** for this [Scheduled Maintenance ${scheduledMaintenanceNumber}](${(await ScheduledMaintenanceService.getScheduledMaintenanceLinkInDashboard(createdItem.projectId!, scheduledMaintenanceId)).toString()}):
109
+ feedInfoInMarkdown: `📄 posted **private note** for this [Scheduled Maintenance ${scheduledMaintenanceNumberResult.numberWithPrefix || "#" + scheduledMaintenanceNumberResult.number}](${(await ScheduledMaintenanceService.getScheduledMaintenanceLinkInDashboard(createdItem.projectId!, scheduledMaintenanceId)).toString()}):
108
110
 
109
111
  ${(createdItem.note || "") + attachmentsMarkdown}
110
112
  `,
@@ -140,6 +142,7 @@ export class Service extends DatabaseService<Model> {
140
142
  },
141
143
  scheduledMaintenance: {
142
144
  scheduledMaintenanceNumber: true,
145
+ scheduledMaintenanceNumberWithPrefix: true,
143
146
  projectId: true,
144
147
  _id: true,
145
148
  },
@@ -167,7 +170,7 @@ export class Service extends DatabaseService<Model> {
167
170
  displayColor: Blue500,
168
171
  userId: userId || undefined,
169
172
 
170
- feedInfoInMarkdown: `📄 updated **Private Note** for this [Scheduled Maintenance ${scheduledMaintenance.scheduledMaintenanceNumber}](${(await ScheduledMaintenanceService.getScheduledMaintenanceLinkInDashboard(scheduledMaintenance.projectId!, scheduledMaintenance.id!)).toString()})
173
+ feedInfoInMarkdown: `📄 updated **Private Note** for this [Scheduled Maintenance ${scheduledMaintenance.scheduledMaintenanceNumberWithPrefix || "#" + scheduledMaintenance.scheduledMaintenanceNumber}](${(await ScheduledMaintenanceService.getScheduledMaintenanceLinkInDashboard(scheduledMaintenance.projectId!, scheduledMaintenance.id!)).toString()})
171
174
 
172
175
  ${(updatedItem.note || "") + attachmentsMarkdown}
173
176
  `,
@@ -60,10 +60,12 @@ export class Service extends DatabaseService<Model> {
60
60
  const scheduledMaintenanceId: ObjectID =
61
61
  createdItem.scheduledMaintenanceId!;
62
62
  const projectId: ObjectID = createdItem.projectId!;
63
- const scheduledMaintenanceNumber: number | null =
64
- await ScheduledMaintenanceService.getScheduledMaintenanceNumber({
65
- scheduledMaintenanceId: scheduledMaintenanceId,
66
- });
63
+ const scheduledMaintenanceNumberResult: {
64
+ number: number | null;
65
+ numberWithPrefix: string | null;
66
+ } = await ScheduledMaintenanceService.getScheduledMaintenanceNumber({
67
+ scheduledMaintenanceId: scheduledMaintenanceId,
68
+ });
67
69
 
68
70
  const attachmentsMarkdown: string = await this.getAttachmentsMarkdown(
69
71
  createdItem.id!,
@@ -77,7 +79,7 @@ export class Service extends DatabaseService<Model> {
77
79
  ScheduledMaintenanceFeedEventType.PublicNote,
78
80
  displayColor: Indigo500,
79
81
  userId: userId || undefined,
80
- feedInfoInMarkdown: `📄 posted **public note** for this [Scheduled Maintenance ${scheduledMaintenanceNumber}](${(await ScheduledMaintenanceService.getScheduledMaintenanceLinkInDashboard(projectId!, scheduledMaintenanceId!)).toString()}) on status page:
82
+ feedInfoInMarkdown: `📄 posted **public note** for this [Scheduled Maintenance ${scheduledMaintenanceNumberResult.numberWithPrefix || "#" + scheduledMaintenanceNumberResult.number}](${(await ScheduledMaintenanceService.getScheduledMaintenanceLinkInDashboard(projectId!, scheduledMaintenanceId!)).toString()}) on status page:
81
83
 
82
84
  ${(createdItem.note || "") + attachmentsMarkdown}
83
85
  `,
@@ -108,6 +110,7 @@ ${(createdItem.note || "") + attachmentsMarkdown}
108
110
  scheduledMaintenance: {
109
111
  _id: true,
110
112
  scheduledMaintenanceNumber: true,
113
+ scheduledMaintenanceNumberWithPrefix: true,
111
114
  projectId: true,
112
115
  },
113
116
  projectId: true,
@@ -140,7 +143,7 @@ ${(createdItem.note || "") + attachmentsMarkdown}
140
143
  displayColor: Blue500,
141
144
  userId: userId || undefined,
142
145
 
143
- feedInfoInMarkdown: `📄 updated **Public Note** for this [Scheduled Maintenance ${scheduledMaintenance.scheduledMaintenanceNumber}](${(await ScheduledMaintenanceService.getScheduledMaintenanceLinkInDashboard(scheduledMaintenance.projectId!, scheduledMaintenance.id!)).toString()})
146
+ feedInfoInMarkdown: `📄 updated **Public Note** for this [Scheduled Maintenance ${scheduledMaintenance.scheduledMaintenanceNumberWithPrefix || "#" + scheduledMaintenance.scheduledMaintenanceNumber}](${(await ScheduledMaintenanceService.getScheduledMaintenanceLinkInDashboard(scheduledMaintenance.projectId!, scheduledMaintenance.id!)).toString()})
144
147
 
145
148
  ${(updatedItem.note || "") + attachmentsMarkdown}
146
149
  `,
@@ -59,6 +59,7 @@ import NotificationRuleWorkspaceChannel from "../../Types/Workspace/Notification
59
59
  import { MessageBlocksByWorkspaceType } from "./WorkspaceNotificationRuleService";
60
60
  import ScheduledMaintenanceWorkspaceMessages from "../Utils/Workspace/WorkspaceMessages/ScheduledMaintenance";
61
61
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
62
+ import ProjectService from "./ProjectService";
62
63
  import StatusPageSubscriberNotificationTemplateService, {
63
64
  Service as StatusPageSubscriberNotificationTemplateServiceClass,
64
65
  } from "./StatusPageSubscriberNotificationTemplateService";
@@ -74,35 +75,6 @@ export class Service extends DatabaseService<Model> {
74
75
  }
75
76
  }
76
77
 
77
- @CaptureSpan()
78
- public async getExistingScheduledMaintenanceNumberForProject(data: {
79
- projectId: ObjectID;
80
- }): Promise<number> {
81
- // get last scheduledMaintenance number.
82
- const lastScheduledMaintenance: Model | null = await this.findOneBy({
83
- query: {
84
- projectId: data.projectId,
85
- },
86
- select: {
87
- scheduledMaintenanceNumber: true,
88
- },
89
- sort: {
90
- createdAt: SortOrder.Descending,
91
- },
92
- props: {
93
- isRoot: true,
94
- },
95
- });
96
-
97
- if (!lastScheduledMaintenance) {
98
- return 0;
99
- }
100
-
101
- return lastScheduledMaintenance.scheduledMaintenanceNumber
102
- ? Number(lastScheduledMaintenance.scheduledMaintenanceNumber)
103
- : 0;
104
- }
105
-
106
78
  @CaptureSpan()
107
79
  public async notififySubscribersOnEventScheduled(
108
80
  scheduledEvents: Array<Model>,
@@ -751,13 +723,20 @@ ${resourcesAffected ? `**Resources Affected:** ${resourcesAffected}` : ""}
751
723
  createBy.data.currentScheduledMaintenanceStateId =
752
724
  scheduledMaintenanceState.id;
753
725
 
754
- const scheduledMaintenanceNumberForThisScheduledMaintenance: number =
755
- (await this.getExistingScheduledMaintenanceNumberForProject({
756
- projectId: projectId,
757
- })) + 1;
726
+ const scheduledMaintenanceCounterResult: {
727
+ counter: number;
728
+ prefix: string | undefined;
729
+ } =
730
+ await ProjectService.incrementAndGetScheduledMaintenanceCounter(
731
+ projectId,
732
+ );
758
733
 
759
734
  createBy.data.scheduledMaintenanceNumber =
760
- scheduledMaintenanceNumberForThisScheduledMaintenance;
735
+ scheduledMaintenanceCounterResult.counter;
736
+ createBy.data.scheduledMaintenanceNumberWithPrefix =
737
+ scheduledMaintenanceCounterResult.prefix
738
+ ? `${scheduledMaintenanceCounterResult.prefix}${scheduledMaintenanceCounterResult.counter}`
739
+ : `#${scheduledMaintenanceCounterResult.counter}`;
761
740
 
762
741
  // get next notification date.
763
742
 
@@ -808,6 +787,7 @@ ${resourcesAffected ? `**Resources Affected:** ${resourcesAffected}` : ""}
808
787
  select: {
809
788
  projectId: true,
810
789
  scheduledMaintenanceNumber: true,
790
+ scheduledMaintenanceNumberWithPrefix: true,
811
791
  title: true,
812
792
  description: true,
813
793
  currentScheduledMaintenanceState: {
@@ -971,7 +951,7 @@ ${resourcesAffected ? `**Resources Affected:** ${resourcesAffected}` : ""}
971
951
  scheduledMaintenance.createdByUserId ||
972
952
  scheduledMaintenance.createdByUser?.id;
973
953
 
974
- let feedInfoInMarkdown: string = `#### 🕒 Scheduled Maintenance ${scheduledMaintenance.scheduledMaintenanceNumber?.toString()} Created:
954
+ let feedInfoInMarkdown: string = `#### 🕒 Scheduled Maintenance ${scheduledMaintenance.scheduledMaintenanceNumberWithPrefix || "#" + scheduledMaintenance.scheduledMaintenanceNumber?.toString()} Created:
975
955
 
976
956
  **${scheduledMaintenance.title || "No title provided."}**:
977
957
 
@@ -1641,11 +1621,15 @@ ${labels
1641
1621
  @CaptureSpan()
1642
1622
  public async getScheduledMaintenanceNumber(data: {
1643
1623
  scheduledMaintenanceId: ObjectID;
1644
- }): Promise<number | null> {
1624
+ }): Promise<{
1625
+ number: number | null;
1626
+ numberWithPrefix: string | null;
1627
+ }> {
1645
1628
  const scheduledMaintenance: Model | null = await this.findOneById({
1646
1629
  id: data.scheduledMaintenanceId,
1647
1630
  select: {
1648
1631
  scheduledMaintenanceNumber: true,
1632
+ scheduledMaintenanceNumberWithPrefix: true,
1649
1633
  },
1650
1634
  props: {
1651
1635
  isRoot: true,
@@ -1656,9 +1640,13 @@ ${labels
1656
1640
  throw new BadDataException("ScheduledMaintenance not found.");
1657
1641
  }
1658
1642
 
1659
- return scheduledMaintenance.scheduledMaintenanceNumber
1660
- ? Number(scheduledMaintenance.scheduledMaintenanceNumber)
1661
- : null;
1643
+ return {
1644
+ number: scheduledMaintenance.scheduledMaintenanceNumber
1645
+ ? Number(scheduledMaintenance.scheduledMaintenanceNumber)
1646
+ : null,
1647
+ numberWithPrefix:
1648
+ scheduledMaintenance.scheduledMaintenanceNumberWithPrefix || null,
1649
+ };
1662
1650
  }
1663
1651
 
1664
1652
  @CaptureSpan()
@@ -429,10 +429,12 @@ export class Service extends DatabaseService<ScheduledMaintenanceStateTimeline>
429
429
  stateEmoji = "🕒";
430
430
  }
431
431
 
432
- const scheduledMaintenanceNumber: number | null =
433
- await ScheduledMaintenanceService.getScheduledMaintenanceNumber({
434
- scheduledMaintenanceId: createdItem.scheduledMaintenanceId,
435
- });
432
+ const scheduledMaintenanceNumberResult: {
433
+ number: number | null;
434
+ numberWithPrefix: string | null;
435
+ } = await ScheduledMaintenanceService.getScheduledMaintenanceNumber({
436
+ scheduledMaintenanceId: createdItem.scheduledMaintenanceId,
437
+ });
436
438
 
437
439
  const projectId: ObjectID = createdItem.projectId!;
438
440
  const scheduledMaintenanceId: ObjectID =
@@ -446,7 +448,7 @@ export class Service extends DatabaseService<ScheduledMaintenanceStateTimeline>
446
448
  displayColor: scheduledMaintenanceState?.color,
447
449
  feedInfoInMarkdown:
448
450
  stateEmoji +
449
- ` Changed **[Scheduled Maintenance ${scheduledMaintenanceNumber}](${(await ScheduledMaintenanceService.getScheduledMaintenanceLinkInDashboard(projectId!, scheduledMaintenanceId!)).toString()}) State** to **` +
451
+ ` Changed **[Scheduled Maintenance ${scheduledMaintenanceNumberResult.numberWithPrefix || "#" + scheduledMaintenanceNumberResult.number}](${(await ScheduledMaintenanceService.getScheduledMaintenanceLinkInDashboard(projectId!, scheduledMaintenanceId!)).toString()}) State** to **` +
450
452
  stateName +
451
453
  "**",
452
454
  userId: createdItem.createdByUserId || onCreate.createBy.props.userId,
@@ -573,7 +575,7 @@ export class Service extends DatabaseService<ScheduledMaintenanceStateTimeline>
573
575
  },
574
576
  sendMessageBeforeArchiving: {
575
577
  _type: "WorkspacePayloadMarkdown",
576
- text: `**[Scheduled Event ${scheduledMaintenanceNumber}](${(
578
+ text: `**[Scheduled Event ${scheduledMaintenanceNumberResult.numberWithPrefix || "#" + scheduledMaintenanceNumberResult.number}](${(
577
579
  await ScheduledMaintenanceService.getScheduledMaintenanceLinkInDashboard(
578
580
  createdItem.projectId!,
579
581
  createdItem.scheduledMaintenanceId!,
@@ -257,6 +257,7 @@ export class Service extends DatabaseService<Model> {
257
257
  },
258
258
  rootCause: true,
259
259
  incidentNumber: true,
260
+ incidentNumberWithPrefix: true,
260
261
  },
261
262
  });
262
263
  }
@@ -286,6 +287,7 @@ export class Service extends DatabaseService<Model> {
286
287
  name: true,
287
288
  },
288
289
  alertNumber: true,
290
+ alertNumberWithPrefix: true,
289
291
  },
290
292
  });
291
293
  }
@@ -315,6 +317,7 @@ export class Service extends DatabaseService<Model> {
315
317
  name: true,
316
318
  },
317
319
  episodeNumber: true,
320
+ episodeNumberWithPrefix: true,
318
321
  rootCause: true,
319
322
  },
320
323
  });
@@ -347,6 +350,7 @@ export class Service extends DatabaseService<Model> {
347
350
  name: true,
348
351
  },
349
352
  episodeNumber: true,
353
+ episodeNumberWithPrefix: true,
350
354
  rootCause: true,
351
355
  },
352
356
  });
@@ -1244,6 +1248,9 @@ export class Service extends DatabaseService<Model> {
1244
1248
  ...(alertEpisode.episodeNumber !== undefined && {
1245
1249
  episodeNumber: alertEpisode.episodeNumber,
1246
1250
  }),
1251
+ ...(alertEpisode.episodeNumberWithPrefix && {
1252
+ episodeNumberWithPrefix: alertEpisode.episodeNumberWithPrefix,
1253
+ }),
1247
1254
  });
1248
1255
 
1249
1256
  PushNotificationService.sendPushNotification(
@@ -1371,7 +1378,7 @@ export class Service extends DatabaseService<Model> {
1371
1378
 
1372
1379
  const incidentIdentifier: string =
1373
1380
  incident.incidentNumber !== undefined
1374
- ? `Incident number ${incident.incidentNumber}, ${incident.title || "Incident"}`
1381
+ ? `Incident number ${incident.incidentNumberWithPrefix || incident.incidentNumber}, ${incident.title || "Incident"}`
1375
1382
  : incident.title || "Incident";
1376
1383
 
1377
1384
  const callRequest: CallRequest = {
@@ -1425,8 +1432,9 @@ export class Service extends DatabaseService<Model> {
1425
1432
 
1426
1433
  const httpProtocol: Protocol = await DatabaseConfig.getHttpProtocol();
1427
1434
 
1428
- const episodeIdentifier: string =
1429
- alertEpisode.episodeNumber !== undefined
1435
+ const episodeIdentifier: string = alertEpisode.episodeNumberWithPrefix
1436
+ ? `Alert episode ${alertEpisode.episodeNumberWithPrefix}, ${alertEpisode.title || "Alert Episode"}`
1437
+ : alertEpisode.episodeNumber !== undefined
1430
1438
  ? `Alert episode number ${alertEpisode.episodeNumber}, ${alertEpisode.title || "Alert Episode"}`
1431
1439
  : alertEpisode.title || "Alert Episode";
1432
1440
 
@@ -1493,7 +1501,7 @@ export class Service extends DatabaseService<Model> {
1493
1501
 
1494
1502
  const alertIdentifier: string =
1495
1503
  alert.alertNumber !== undefined
1496
- ? `#${alert.alertNumber} (${alert.title || "Alert"})`
1504
+ ? `${alert.alertNumberWithPrefix || "#" + alert.alertNumber} (${alert.title || "Alert"})`
1497
1505
  : alert.title || "Alert";
1498
1506
 
1499
1507
  const sms: SMS = {
@@ -1526,7 +1534,7 @@ export class Service extends DatabaseService<Model> {
1526
1534
 
1527
1535
  const incidentIdentifier: string =
1528
1536
  incident.incidentNumber !== undefined
1529
- ? `#${incident.incidentNumber} (${incident.title || "Incident"})`
1537
+ ? `${incident.incidentNumberWithPrefix || "#" + incident.incidentNumber} (${incident.title || "Incident"})`
1530
1538
  : incident.title || "Incident";
1531
1539
 
1532
1540
  const sms: SMS = {
@@ -1557,8 +1565,9 @@ export class Service extends DatabaseService<Model> {
1557
1565
  );
1558
1566
  const url: URL = await ShortLinkService.getShortenedUrl(shortUrl);
1559
1567
 
1560
- const episodeIdentifier: string =
1561
- alertEpisode.episodeNumber !== undefined
1568
+ const episodeIdentifier: string = alertEpisode.episodeNumberWithPrefix
1569
+ ? `${alertEpisode.episodeNumberWithPrefix} (${alertEpisode.title || "Alert Episode"})`
1570
+ : alertEpisode.episodeNumber !== undefined
1562
1571
  ? `#${alertEpisode.episodeNumber} (${alertEpisode.title || "Alert Episode"})`
1563
1572
  : alertEpisode.title || "Alert Episode";
1564
1573
 
@@ -1726,9 +1735,10 @@ export class Service extends DatabaseService<Model> {
1726
1735
  episode_title: alertEpisode.title || "",
1727
1736
  acknowledge_url: acknowledgeUrl.toString(),
1728
1737
  episode_number:
1729
- alertEpisode.episodeNumber !== undefined
1738
+ alertEpisode.episodeNumberWithPrefix ||
1739
+ (alertEpisode.episodeNumber !== undefined
1730
1740
  ? alertEpisode.episodeNumber.toString()
1731
- : "",
1741
+ : ""),
1732
1742
  episode_link: episodeLinkOnDashboard,
1733
1743
  };
1734
1744
 
@@ -1752,9 +1762,9 @@ export class Service extends DatabaseService<Model> {
1752
1762
  const host: Hostname = await DatabaseConfig.getHost();
1753
1763
  const httpProtocol: Protocol = await DatabaseConfig.getHttpProtocol();
1754
1764
 
1755
- const alertNumber: string = alert.alertNumber
1756
- ? `#${alert.alertNumber}`
1757
- : "";
1765
+ const alertNumber: string =
1766
+ alert.alertNumberWithPrefix ||
1767
+ (alert.alertNumber ? `#${alert.alertNumber}` : "");
1758
1768
 
1759
1769
  const vars: Dictionary<string> = {
1760
1770
  alertTitle: alert.title!,
@@ -1797,9 +1807,9 @@ export class Service extends DatabaseService<Model> {
1797
1807
  const host: Hostname = await DatabaseConfig.getHost();
1798
1808
  const httpProtocol: Protocol = await DatabaseConfig.getHttpProtocol();
1799
1809
 
1800
- const incidentNumber: string = incident.incidentNumber
1801
- ? `#${incident.incidentNumber}`
1802
- : "";
1810
+ const incidentNumber: string =
1811
+ incident.incidentNumberWithPrefix ||
1812
+ (incident.incidentNumber ? `#${incident.incidentNumber}` : "");
1803
1813
 
1804
1814
  const vars: Dictionary<string> = {
1805
1815
  incidentTitle: incident.title!,
@@ -1883,6 +1893,7 @@ export class Service extends DatabaseService<Model> {
1883
1893
  _id: true,
1884
1894
  title: true,
1885
1895
  alertNumber: true,
1896
+ alertNumberWithPrefix: true,
1886
1897
  monitor: {
1887
1898
  _id: true,
1888
1899
  name: true,
@@ -1915,9 +1926,9 @@ export class Service extends DatabaseService<Model> {
1915
1926
  const alertRows: string[] = [];
1916
1927
  for (const alert of alerts) {
1917
1928
  const alertTitle: string = alert.title || "Untitled Alert";
1918
- const alertNumber: string = alert.alertNumber
1919
- ? `#${alert.alertNumber}`
1920
- : "";
1929
+ const alertNumber: string =
1930
+ alert.alertNumberWithPrefix ||
1931
+ (alert.alertNumber ? `#${alert.alertNumber}` : "");
1921
1932
  const alertLink: string = (
1922
1933
  await AlertService.getAlertLinkInDashboard(
1923
1934
  alertEpisode.projectId!,
@@ -1956,9 +1967,9 @@ export class Service extends DatabaseService<Model> {
1956
1967
  }
1957
1968
  }
1958
1969
 
1959
- const episodeNumber: string = alertEpisode.episodeNumber
1960
- ? `#${alertEpisode.episodeNumber}`
1961
- : "";
1970
+ const episodeNumber: string =
1971
+ alertEpisode.episodeNumberWithPrefix ||
1972
+ (alertEpisode.episodeNumber ? `#${alertEpisode.episodeNumber}` : "");
1962
1973
 
1963
1974
  const vars: Dictionary<string> = {
1964
1975
  alertEpisodeTitle: alertEpisode.title!,
@@ -48,6 +48,7 @@ export default class IncidentEpisodeAIContextBuilder {
48
48
  rootCause: true,
49
49
  projectId: true,
50
50
  episodeNumber: true,
51
+ episodeNumberWithPrefix: true,
51
52
  incidentSeverity: {
52
53
  name: true,
53
54
  color: true,
@@ -141,6 +142,7 @@ export default class IncidentEpisodeAIContextBuilder {
141
142
  title: true,
142
143
  description: true,
143
144
  incidentNumber: true,
145
+ incidentNumberWithPrefix: true,
144
146
  createdAt: true,
145
147
  rootCause: true,
146
148
  remediationNotes: true,
@@ -230,7 +232,7 @@ export default class IncidentEpisodeAIContextBuilder {
230
232
 
231
233
  // Basic episode information
232
234
  contextText += "# Incident Episode Information\n\n";
233
- contextText += `**Episode Number:** #${episode.episodeNumber || "N/A"}\n\n`;
235
+ contextText += `**Episode Number:** ${episode.episodeNumberWithPrefix || "#" + (episode.episodeNumber || "N/A")}\n\n`;
234
236
  contextText += `**Title:** ${episode.title || "N/A"}\n\n`;
235
237
  contextText += `**Description:** ${episode.description || "N/A"}\n\n`;
236
238
  contextText += `**Severity:** ${episode.incidentSeverity?.name || "N/A"}\n\n`;
@@ -269,7 +271,7 @@ export default class IncidentEpisodeAIContextBuilder {
269
271
  continue;
270
272
  }
271
273
 
272
- contextText += `## Incident #${incident.incidentNumber || "N/A"}: ${incident.title || "Untitled"}\n\n`;
274
+ contextText += `## Incident ${incident.incidentNumberWithPrefix || "#" + (incident.incidentNumber || "N/A")}: ${incident.title || "Untitled"}\n\n`;
273
275
  contextText += `- **Severity:** ${incident.incidentSeverity?.name || "N/A"}\n`;
274
276
  contextText += `- **State:** ${incident.currentIncidentState?.name || "N/A"}\n`;
275
277
  contextText += `- **Created:** ${incident.createdAt ? OneUptimeDate.getDateAsFormattedString(incident.createdAt) : "N/A"}\n`;