@oneuptime/common 9.5.2 → 9.5.3

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 (100) hide show
  1. package/Models/DatabaseModels/Alert.ts +1 -0
  2. package/Models/DatabaseModels/AlertEpisode.ts +1 -0
  3. package/Models/DatabaseModels/AlertEpisodeStateTimeline.ts +1 -0
  4. package/Models/DatabaseModels/AlertStateTimeline.ts +1 -0
  5. package/Models/DatabaseModels/Incident.ts +1 -0
  6. package/Models/DatabaseModels/IncidentEpisode.ts +156 -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 +2 -1
  15. package/Models/DatabaseModels/ProjectCallSMSConfig.ts +1 -0
  16. package/Models/DatabaseModels/ScheduledMaintenance.ts +1 -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/Index.ts +4 -0
  24. package/Server/Services/IncidentEpisodePublicNoteService.ts +254 -0
  25. package/Server/Services/IncidentEpisodeService.ts +26 -0
  26. package/Server/Services/Index.ts +2 -0
  27. package/Server/Utils/Monitor/MonitorIncident.ts +6 -0
  28. package/Types/Email/EmailTemplateType.ts +4 -0
  29. package/Types/Icon/IconProp.ts +172 -0
  30. package/Types/Monitor/CriteriaIncident.ts +2 -0
  31. package/Types/Permission.ts +40 -0
  32. package/Types/StatusPage/StatusPageSubscriberNotificationEventType.ts +5 -0
  33. package/UI/Components/Icon/Icon.tsx +1333 -1
  34. package/build/dist/Models/DatabaseModels/Alert.js +1 -0
  35. package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
  36. package/build/dist/Models/DatabaseModels/AlertEpisode.js +1 -0
  37. package/build/dist/Models/DatabaseModels/AlertEpisode.js.map +1 -1
  38. package/build/dist/Models/DatabaseModels/AlertEpisodeStateTimeline.js +1 -0
  39. package/build/dist/Models/DatabaseModels/AlertEpisodeStateTimeline.js.map +1 -1
  40. package/build/dist/Models/DatabaseModels/AlertStateTimeline.js +1 -0
  41. package/build/dist/Models/DatabaseModels/AlertStateTimeline.js.map +1 -1
  42. package/build/dist/Models/DatabaseModels/Incident.js +1 -0
  43. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  44. package/build/dist/Models/DatabaseModels/IncidentEpisode.js +161 -0
  45. package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
  46. package/build/dist/Models/DatabaseModels/IncidentEpisodeFeed.js +2 -0
  47. package/build/dist/Models/DatabaseModels/IncidentEpisodeFeed.js.map +1 -1
  48. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +626 -0
  49. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -0
  50. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +86 -0
  51. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
  52. package/build/dist/Models/DatabaseModels/IncidentGroupingRule.js +37 -0
  53. package/build/dist/Models/DatabaseModels/IncidentGroupingRule.js.map +1 -1
  54. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
  55. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
  56. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  57. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  58. package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js +1 -0
  59. package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js.map +1 -1
  60. package/build/dist/Models/DatabaseModels/Project.js +2 -1
  61. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  62. package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js +1 -0
  63. package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js.map +1 -1
  64. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +1 -0
  65. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  66. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js +1 -0
  67. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js.map +1 -1
  68. package/build/dist/Models/DatabaseModels/StatusPage.js +126 -0
  69. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  70. package/build/dist/Server/API/IncidentEpisodePublicNoteAPI.js +68 -0
  71. package/build/dist/Server/API/IncidentEpisodePublicNoteAPI.js.map +1 -0
  72. package/build/dist/Server/API/StatusPageAPI.js +874 -47
  73. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  74. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770232207959-MigrationName.js +68 -0
  75. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770232207959-MigrationName.js.map +1 -0
  76. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770237245069-MigrationName.js +18 -0
  77. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770237245069-MigrationName.js.map +1 -0
  78. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  79. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  80. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +223 -0
  81. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -0
  82. package/build/dist/Server/Services/IncidentEpisodeService.js +22 -0
  83. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  84. package/build/dist/Server/Services/Index.js +2 -0
  85. package/build/dist/Server/Services/Index.js.map +1 -1
  86. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +5 -0
  87. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  88. package/build/dist/Types/Email/EmailTemplateType.js +3 -0
  89. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  90. package/build/dist/Types/Icon/IconProp.js +172 -0
  91. package/build/dist/Types/Icon/IconProp.js.map +1 -1
  92. package/build/dist/Types/Monitor/CriteriaIncident.js +1 -0
  93. package/build/dist/Types/Monitor/CriteriaIncident.js.map +1 -1
  94. package/build/dist/Types/Permission.js +34 -0
  95. package/build/dist/Types/Permission.js.map +1 -1
  96. package/build/dist/Types/StatusPage/StatusPageSubscriberNotificationEventType.js +4 -0
  97. package/build/dist/Types/StatusPage/StatusPageSubscriberNotificationEventType.js.map +1 -1
  98. package/build/dist/UI/Components/Icon/Icon.js +502 -1
  99. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  100. 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
+ }
@@ -249,6 +249,8 @@ 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";
252
254
 
253
255
  export default [
254
256
  InitialMigration,
@@ -502,4 +504,6 @@ export default [
502
504
  MigrationName1769780297584,
503
505
  MigrationName1769802715014,
504
506
  MigrationName1770054293299,
507
+ MigrationName1770232207959,
508
+ MigrationName1770237245069,
505
509
  ];
@@ -0,0 +1,254 @@
1
+ import CreateBy from "../Types/Database/CreateBy";
2
+ import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
3
+ import DatabaseService from "./DatabaseService";
4
+ import OneUptimeDate from "../../Types/Date";
5
+ import Model from "../../Models/DatabaseModels/IncidentEpisodePublicNote";
6
+ import IncidentEpisodeFeedService from "./IncidentEpisodeFeedService";
7
+ import { IncidentEpisodeFeedEventType } from "../../Models/DatabaseModels/IncidentEpisodeFeed";
8
+ import { Blue500, Indigo500 } from "../../Types/BrandColors";
9
+ import ObjectID from "../../Types/ObjectID";
10
+ import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
11
+ import IncidentEpisodeService from "./IncidentEpisodeService";
12
+ import IncidentEpisode from "../../Models/DatabaseModels/IncidentEpisode";
13
+ import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
14
+ import StatusPageSubscriberNotificationStatus from "../../Types/StatusPage/StatusPageSubscriberNotificationStatus";
15
+ import File from "../../Models/DatabaseModels/File";
16
+ import FileAttachmentMarkdownUtil from "../Utils/FileAttachmentMarkdownUtil";
17
+
18
+ export class Service extends DatabaseService<Model> {
19
+ public constructor() {
20
+ super(Model);
21
+ }
22
+
23
+ @CaptureSpan()
24
+ public async addNote(data: {
25
+ userId: ObjectID;
26
+ incidentEpisodeId: ObjectID;
27
+ projectId: ObjectID;
28
+ note: string;
29
+ attachmentFileIds?: Array<ObjectID>;
30
+ postedFromSlackMessageId?: string;
31
+ }): Promise<Model> {
32
+ const publicNote: Model = new Model();
33
+ publicNote.createdByUserId = data.userId;
34
+ publicNote.incidentEpisodeId = data.incidentEpisodeId;
35
+ publicNote.projectId = data.projectId;
36
+ publicNote.note = data.note;
37
+ publicNote.postedAt = OneUptimeDate.getCurrentDate();
38
+
39
+ if (data.postedFromSlackMessageId) {
40
+ publicNote.postedFromSlackMessageId = data.postedFromSlackMessageId;
41
+ }
42
+
43
+ if (data.attachmentFileIds && data.attachmentFileIds.length > 0) {
44
+ publicNote.attachments = data.attachmentFileIds.map(
45
+ (fileId: ObjectID) => {
46
+ const file: File = new File();
47
+ file.id = fileId;
48
+ return file;
49
+ },
50
+ );
51
+ }
52
+
53
+ return this.create({
54
+ data: publicNote,
55
+ props: {
56
+ isRoot: true,
57
+ },
58
+ });
59
+ }
60
+
61
+ @CaptureSpan()
62
+ public async hasNoteFromSlackMessage(data: {
63
+ incidentEpisodeId: ObjectID;
64
+ postedFromSlackMessageId: string;
65
+ }): Promise<boolean> {
66
+ const existingNote: Model | null = await this.findOneBy({
67
+ query: {
68
+ incidentEpisodeId: data.incidentEpisodeId,
69
+ postedFromSlackMessageId: data.postedFromSlackMessageId,
70
+ },
71
+ select: {
72
+ _id: true,
73
+ },
74
+ props: {
75
+ isRoot: true,
76
+ },
77
+ });
78
+
79
+ return existingNote !== null;
80
+ }
81
+
82
+ @CaptureSpan()
83
+ protected override async onBeforeCreate(
84
+ createBy: CreateBy<Model>,
85
+ ): Promise<OnCreate<Model>> {
86
+ if (!createBy.data.postedAt) {
87
+ createBy.data.postedAt = OneUptimeDate.getCurrentDate();
88
+ }
89
+
90
+ // Set notification status based on shouldStatusPageSubscribersBeNotifiedOnNoteCreated
91
+ if (
92
+ createBy.data.shouldStatusPageSubscribersBeNotifiedOnNoteCreated === false
93
+ ) {
94
+ createBy.data.subscriberNotificationStatusOnNoteCreated =
95
+ StatusPageSubscriberNotificationStatus.Skipped;
96
+ createBy.data.subscriberNotificationStatusMessage =
97
+ "Notifications skipped as subscribers are not to be notified for this episode note.";
98
+ } else if (
99
+ createBy.data.shouldStatusPageSubscribersBeNotifiedOnNoteCreated === true
100
+ ) {
101
+ createBy.data.subscriberNotificationStatusOnNoteCreated =
102
+ StatusPageSubscriberNotificationStatus.Pending;
103
+ }
104
+
105
+ return {
106
+ createBy: createBy,
107
+ carryForward: null,
108
+ };
109
+ }
110
+
111
+ @CaptureSpan()
112
+ public override async onCreateSuccess(
113
+ _onCreate: OnCreate<Model>,
114
+ createdItem: Model,
115
+ ): Promise<Model> {
116
+ const userId: ObjectID | null | undefined =
117
+ createdItem.createdByUserId || createdItem.createdByUser?.id;
118
+
119
+ const incidentEpisodeId: ObjectID = createdItem.incidentEpisodeId!;
120
+ const projectId: ObjectID = createdItem.projectId!;
121
+ const episodeNumber: number | null =
122
+ await IncidentEpisodeService.getEpisodeNumber({
123
+ episodeId: incidentEpisodeId,
124
+ });
125
+
126
+ const attachmentsMarkdown: string = await this.getAttachmentsMarkdown(
127
+ createdItem.id!,
128
+ "/incident-episode-public-note/attachment",
129
+ );
130
+
131
+ await IncidentEpisodeFeedService.createIncidentEpisodeFeedItem({
132
+ incidentEpisodeId: createdItem.incidentEpisodeId!,
133
+ projectId: createdItem.projectId!,
134
+ incidentEpisodeFeedEventType: IncidentEpisodeFeedEventType.PublicNote,
135
+ displayColor: Indigo500,
136
+ userId: userId || undefined,
137
+ feedInfoInMarkdown: `📄 posted **public note** for this [Episode ${episodeNumber}](${(await IncidentEpisodeService.getEpisodeLinkInDashboard(projectId!, incidentEpisodeId!)).toString()}) on status page:
138
+
139
+ ${(createdItem.note || "") + attachmentsMarkdown}
140
+ `,
141
+ });
142
+
143
+ return createdItem;
144
+ }
145
+
146
+ @CaptureSpan()
147
+ public override async onUpdateSuccess(
148
+ onUpdate: OnUpdate<Model>,
149
+ _updatedItemIds: Array<ObjectID>,
150
+ ): Promise<OnUpdate<Model>> {
151
+ if (onUpdate.updateBy.data.note) {
152
+ const updatedItems: Array<Model> = await this.findBy({
153
+ query: onUpdate.updateBy.query,
154
+ limit: LIMIT_PER_PROJECT,
155
+ skip: 0,
156
+ props: {
157
+ isRoot: true,
158
+ },
159
+ select: {
160
+ incidentEpisodeId: true,
161
+ projectId: true,
162
+ incidentEpisode: {
163
+ _id: true,
164
+ episodeNumber: true,
165
+ projectId: true,
166
+ },
167
+ note: true,
168
+ createdByUserId: true,
169
+ createdByUser: {
170
+ _id: true,
171
+ },
172
+ },
173
+ });
174
+
175
+ const userId: ObjectID | null | undefined =
176
+ onUpdate.updateBy.props.userId;
177
+
178
+ for (const updatedItem of updatedItems) {
179
+ const episode: IncidentEpisode = updatedItem.incidentEpisode!;
180
+
181
+ const attachmentsMarkdown: string = await this.getAttachmentsMarkdown(
182
+ updatedItem.id!,
183
+ "/incident-episode-public-note/attachment",
184
+ );
185
+
186
+ await IncidentEpisodeFeedService.createIncidentEpisodeFeedItem({
187
+ incidentEpisodeId: updatedItem.incidentEpisodeId!,
188
+ projectId: updatedItem.projectId!,
189
+ incidentEpisodeFeedEventType: IncidentEpisodeFeedEventType.PublicNote,
190
+ displayColor: Blue500,
191
+ userId: userId || undefined,
192
+ feedInfoInMarkdown: `📄 updated **Public Note** for this [Episode ${episode.episodeNumber}](${(await IncidentEpisodeService.getEpisodeLinkInDashboard(episode.projectId!, episode.id!)).toString()})
193
+
194
+ ${(updatedItem.note || "") + attachmentsMarkdown}
195
+ `,
196
+ });
197
+ }
198
+ }
199
+ return onUpdate;
200
+ }
201
+
202
+ private async getAttachmentsMarkdown(
203
+ modelId: ObjectID,
204
+ attachmentApiPath: string,
205
+ ): Promise<string> {
206
+ if (!modelId) {
207
+ return "";
208
+ }
209
+
210
+ const noteWithAttachments: Model | null = await this.findOneById({
211
+ id: modelId,
212
+ select: {
213
+ attachments: {
214
+ _id: true,
215
+ },
216
+ },
217
+ props: {
218
+ isRoot: true,
219
+ },
220
+ });
221
+
222
+ if (!noteWithAttachments || !noteWithAttachments.attachments) {
223
+ return "";
224
+ }
225
+
226
+ const attachmentIds: Array<ObjectID> = noteWithAttachments.attachments
227
+ .map((file: File) => {
228
+ if (file.id) {
229
+ return file.id;
230
+ }
231
+
232
+ if (file._id) {
233
+ return new ObjectID(file._id);
234
+ }
235
+
236
+ return null;
237
+ })
238
+ .filter((id: ObjectID | null): id is ObjectID => {
239
+ return Boolean(id);
240
+ });
241
+
242
+ if (!attachmentIds.length) {
243
+ return "";
244
+ }
245
+
246
+ return await FileAttachmentMarkdownUtil.buildAttachmentMarkdown({
247
+ modelId,
248
+ attachmentIds,
249
+ attachmentApiPath,
250
+ });
251
+ }
252
+ }
253
+
254
+ export default new Service();
@@ -38,6 +38,8 @@ import Semaphore, { SemaphoreMutex } from "../Infrastructure/Semaphore";
38
38
  import OnCallDutyPolicyService from "./OnCallDutyPolicyService";
39
39
  import OnCallDutyPolicy from "../../Models/DatabaseModels/OnCallDutyPolicy";
40
40
  import UserNotificationEventType from "../../Types/UserNotification/UserNotificationEventType";
41
+ import IncidentGroupingRuleService from "./IncidentGroupingRuleService";
42
+ import IncidentGroupingRule from "../../Models/DatabaseModels/IncidentGroupingRule";
41
43
 
42
44
  export class Service extends DatabaseService<Model> {
43
45
  public constructor() {
@@ -135,6 +137,30 @@ export class Service extends DatabaseService<Model> {
135
137
  createBy.data.lastIncidentAddedAt = OneUptimeDate.getCurrentDate();
136
138
  }
137
139
 
140
+ // Set declaredAt if not provided
141
+ if (!createBy.data.declaredAt) {
142
+ createBy.data.declaredAt = OneUptimeDate.getCurrentDate();
143
+ }
144
+
145
+ // Copy showEpisodeOnStatusPage from grouping rule if available
146
+ if (createBy.data.incidentGroupingRuleId) {
147
+ const groupingRule: IncidentGroupingRule | null =
148
+ await IncidentGroupingRuleService.findOneById({
149
+ id: createBy.data.incidentGroupingRuleId,
150
+ select: {
151
+ showEpisodeOnStatusPage: true,
152
+ },
153
+ props: {
154
+ isRoot: true,
155
+ },
156
+ });
157
+
158
+ if (groupingRule) {
159
+ createBy.data.isVisibleOnStatusPage =
160
+ groupingRule.showEpisodeOnStatusPage ?? true;
161
+ }
162
+ }
163
+
138
164
  return { createBy, carryForward: { mutex } };
139
165
  } catch (error) {
140
166
  // Release the mutex if it was acquired and an error occurred
@@ -181,6 +181,7 @@ import IncidentEpisodeRoleMemberService from "./IncidentEpisodeRoleMemberService
181
181
  import IncidentEpisodeOwnerTeamService from "./IncidentEpisodeOwnerTeamService";
182
182
  import IncidentEpisodeOwnerUserService from "./IncidentEpisodeOwnerUserService";
183
183
  import IncidentEpisodeStateTimelineService from "./IncidentEpisodeStateTimelineService";
184
+ import IncidentEpisodePublicNoteService from "./IncidentEpisodePublicNoteService";
184
185
  import AlertGroupingRuleService from "./AlertGroupingRuleService";
185
186
  import IncidentSlaRuleService from "./IncidentSlaRuleService";
186
187
  import IncidentSlaService from "./IncidentSlaService";
@@ -400,6 +401,7 @@ const services: Array<BaseService> = [
400
401
  IncidentEpisodeOwnerTeamService,
401
402
  IncidentEpisodeOwnerUserService,
402
403
  IncidentEpisodeStateTimelineService,
404
+ IncidentEpisodePublicNoteService,
403
405
  AlertGroupingRuleService,
404
406
  IncidentSlaRuleService,
405
407
  IncidentSlaService,
@@ -242,6 +242,12 @@ export default class MonitorIncident {
242
242
 
243
243
  incident.isCreatedAutomatically = true;
244
244
 
245
+ // Set status page visibility (defaults to true if not specified)
246
+ if (criteriaIncident.showIncidentOnStatusPage !== undefined) {
247
+ incident.isVisibleOnStatusPage =
248
+ criteriaIncident.showIncidentOnStatusPage;
249
+ }
250
+
245
251
  if (input.props.telemetryQuery) {
246
252
  incident.telemetryQuery = input.props.telemetryQuery;
247
253
  }
@@ -53,6 +53,10 @@ enum EmailTemplateType {
53
53
  IncidentEpisodeOwnerNotePosted = "IncidentEpisodeOwnerNotePosted.hbs",
54
54
  IncidentEpisodeOwnerResourceCreated = "IncidentEpisodeOwnerResourceCreated.hbs",
55
55
 
56
+ SubscriberEpisodeCreated = "SubscriberEpisodeCreated.hbs",
57
+ SubscriberEpisodeStateChanged = "SubscriberEpisodeStateChanged.hbs",
58
+ SubscriberEpisodeNoteCreated = "SubscriberEpisodeNoteCreated.hbs",
59
+
56
60
  ScheduledMaintenanceOwnerNotePosted = "ScheduledMaintenanceOwnerNotePosted.hbs",
57
61
  ScheduledMaintenanceOwnerAdded = "ScheduledMaintenanceOwnerAdded.hbs",
58
62
  ScheduledMaintenanceOwnerStateChanged = "ScheduledMaintenanceOwnerStateChanged.hbs",