@oneuptime/common 8.0.5139 → 8.0.5144
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/IncidentTemplate.ts +71 -0
- package/Models/DatabaseModels/StatusPageAnnouncement.ts +48 -0
- package/Models/DatabaseModels/StatusPageAnnouncementTemplate.ts +48 -0
- package/Server/API/StatusPageAPI.ts +65 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1757416939595-MigrationName.ts +41 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1757423505855-MigrationName.ts +79 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Services/IncidentService.ts +93 -18
- package/Server/Services/StatusPageSubscriberService.ts +4 -1
- package/UI/Utils/Markdown.tsx +1 -5
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js +70 -0
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +46 -0
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncementTemplate.js +46 -0
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncementTemplate.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +54 -0
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1757416939595-MigrationName.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1757416939595-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1757423505855-MigrationName.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1757423505855-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +77 -15
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +2 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/UI/Utils/Markdown.js +1 -1
- package/build/dist/UI/Utils/Markdown.js.map +1 -1
- package/package.json +1 -1
|
@@ -716,6 +716,77 @@ export default class IncidentTemplate extends BaseModel {
|
|
|
716
716
|
})
|
|
717
717
|
public changeMonitorStatusToId?: ObjectID = undefined;
|
|
718
718
|
|
|
719
|
+
@ColumnAccessControl({
|
|
720
|
+
create: [
|
|
721
|
+
Permission.ProjectOwner,
|
|
722
|
+
Permission.ProjectAdmin,
|
|
723
|
+
Permission.ProjectMember,
|
|
724
|
+
Permission.CreateIncidentTemplate,
|
|
725
|
+
],
|
|
726
|
+
read: [
|
|
727
|
+
Permission.ProjectOwner,
|
|
728
|
+
Permission.ProjectAdmin,
|
|
729
|
+
Permission.ProjectMember,
|
|
730
|
+
Permission.ReadIncidentTemplate,
|
|
731
|
+
],
|
|
732
|
+
update: [],
|
|
733
|
+
})
|
|
734
|
+
@TableColumn({
|
|
735
|
+
manyToOneRelationColumn: "initialIncidentStateId",
|
|
736
|
+
type: TableColumnType.Entity,
|
|
737
|
+
modelType: IncidentState,
|
|
738
|
+
title: "Initial Incident State",
|
|
739
|
+
description:
|
|
740
|
+
"Relation to Incident State Object. Incidents created from this template will start in this state.",
|
|
741
|
+
})
|
|
742
|
+
@ManyToOne(
|
|
743
|
+
() => {
|
|
744
|
+
return IncidentState;
|
|
745
|
+
},
|
|
746
|
+
{
|
|
747
|
+
eager: false,
|
|
748
|
+
nullable: true,
|
|
749
|
+
orphanedRowAction: "nullify",
|
|
750
|
+
},
|
|
751
|
+
)
|
|
752
|
+
@JoinColumn({ name: "initialIncidentStateId" })
|
|
753
|
+
public initialIncidentState?: IncidentState = undefined;
|
|
754
|
+
|
|
755
|
+
@ColumnAccessControl({
|
|
756
|
+
create: [
|
|
757
|
+
Permission.ProjectOwner,
|
|
758
|
+
Permission.ProjectAdmin,
|
|
759
|
+
Permission.ProjectMember,
|
|
760
|
+
Permission.CreateIncidentTemplate,
|
|
761
|
+
],
|
|
762
|
+
read: [
|
|
763
|
+
Permission.ProjectOwner,
|
|
764
|
+
Permission.ProjectAdmin,
|
|
765
|
+
Permission.ProjectMember,
|
|
766
|
+
Permission.ReadIncidentTemplate,
|
|
767
|
+
],
|
|
768
|
+
update: [
|
|
769
|
+
Permission.ProjectOwner,
|
|
770
|
+
Permission.ProjectAdmin,
|
|
771
|
+
Permission.ProjectMember,
|
|
772
|
+
Permission.EditIncidentTemplate,
|
|
773
|
+
],
|
|
774
|
+
})
|
|
775
|
+
@Index()
|
|
776
|
+
@TableColumn({
|
|
777
|
+
type: TableColumnType.ObjectID,
|
|
778
|
+
required: false,
|
|
779
|
+
title: "Initial Incident State ID",
|
|
780
|
+
description:
|
|
781
|
+
"Relation to Incident State Object ID. Incidents created from this template will start in this state.",
|
|
782
|
+
})
|
|
783
|
+
@Column({
|
|
784
|
+
type: ColumnType.ObjectID,
|
|
785
|
+
nullable: true,
|
|
786
|
+
transformer: ObjectID.getDatabaseTransformer(),
|
|
787
|
+
})
|
|
788
|
+
public initialIncidentStateId?: ObjectID = undefined;
|
|
789
|
+
|
|
719
790
|
@ColumnAccessControl({
|
|
720
791
|
create: [
|
|
721
792
|
Permission.ProjectOwner,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import Monitor from "./Monitor";
|
|
1
2
|
import Project from "./Project";
|
|
2
3
|
import StatusPage from "./StatusPage";
|
|
3
4
|
import User from "./User";
|
|
@@ -198,6 +199,53 @@ export default class StatusPageAnnouncement extends BaseModel {
|
|
|
198
199
|
})
|
|
199
200
|
public statusPages?: Array<StatusPage> = undefined;
|
|
200
201
|
|
|
202
|
+
@ColumnAccessControl({
|
|
203
|
+
create: [
|
|
204
|
+
Permission.ProjectOwner,
|
|
205
|
+
Permission.ProjectAdmin,
|
|
206
|
+
Permission.ProjectMember,
|
|
207
|
+
Permission.CreateStatusPageAnnouncement,
|
|
208
|
+
],
|
|
209
|
+
read: [
|
|
210
|
+
Permission.ProjectOwner,
|
|
211
|
+
Permission.ProjectAdmin,
|
|
212
|
+
Permission.ProjectMember,
|
|
213
|
+
Permission.ReadStatusPageAnnouncement,
|
|
214
|
+
],
|
|
215
|
+
update: [
|
|
216
|
+
Permission.ProjectOwner,
|
|
217
|
+
Permission.ProjectAdmin,
|
|
218
|
+
Permission.ProjectMember,
|
|
219
|
+
Permission.EditStatusPageAnnouncement,
|
|
220
|
+
],
|
|
221
|
+
})
|
|
222
|
+
@TableColumn({
|
|
223
|
+
required: false,
|
|
224
|
+
type: TableColumnType.EntityArray,
|
|
225
|
+
modelType: Monitor,
|
|
226
|
+
title: "Monitors",
|
|
227
|
+
description:
|
|
228
|
+
"List of monitors affected by this announcement. If none are selected, all subscribers will be notified.",
|
|
229
|
+
})
|
|
230
|
+
@ManyToMany(
|
|
231
|
+
() => {
|
|
232
|
+
return Monitor;
|
|
233
|
+
},
|
|
234
|
+
{ eager: false },
|
|
235
|
+
)
|
|
236
|
+
@JoinTable({
|
|
237
|
+
name: "AnnouncementMonitor",
|
|
238
|
+
inverseJoinColumn: {
|
|
239
|
+
name: "monitorId",
|
|
240
|
+
referencedColumnName: "_id",
|
|
241
|
+
},
|
|
242
|
+
joinColumn: {
|
|
243
|
+
name: "announcementId",
|
|
244
|
+
referencedColumnName: "_id",
|
|
245
|
+
},
|
|
246
|
+
})
|
|
247
|
+
public monitors?: Array<Monitor> = undefined;
|
|
248
|
+
|
|
201
249
|
@ColumnAccessControl({
|
|
202
250
|
create: [
|
|
203
251
|
Permission.ProjectOwner,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import Monitor from "./Monitor";
|
|
1
2
|
import Project from "./Project";
|
|
2
3
|
import StatusPage from "./StatusPage";
|
|
3
4
|
import User from "./User";
|
|
@@ -328,6 +329,53 @@ export default class StatusPageAnnouncementTemplate extends BaseModel {
|
|
|
328
329
|
})
|
|
329
330
|
public statusPages?: Array<StatusPage> = undefined;
|
|
330
331
|
|
|
332
|
+
@ColumnAccessControl({
|
|
333
|
+
create: [
|
|
334
|
+
Permission.ProjectOwner,
|
|
335
|
+
Permission.ProjectAdmin,
|
|
336
|
+
Permission.ProjectMember,
|
|
337
|
+
Permission.CreateStatusPageAnnouncementTemplate,
|
|
338
|
+
],
|
|
339
|
+
read: [
|
|
340
|
+
Permission.ProjectOwner,
|
|
341
|
+
Permission.ProjectAdmin,
|
|
342
|
+
Permission.ProjectMember,
|
|
343
|
+
Permission.ReadStatusPageAnnouncementTemplate,
|
|
344
|
+
],
|
|
345
|
+
update: [
|
|
346
|
+
Permission.ProjectOwner,
|
|
347
|
+
Permission.ProjectAdmin,
|
|
348
|
+
Permission.ProjectMember,
|
|
349
|
+
Permission.EditStatusPageAnnouncementTemplate,
|
|
350
|
+
],
|
|
351
|
+
})
|
|
352
|
+
@TableColumn({
|
|
353
|
+
required: false,
|
|
354
|
+
type: TableColumnType.EntityArray,
|
|
355
|
+
modelType: Monitor,
|
|
356
|
+
title: "Monitors",
|
|
357
|
+
description:
|
|
358
|
+
"List of monitors affected by this announcement template. If none are selected, all subscribers will be notified.",
|
|
359
|
+
})
|
|
360
|
+
@ManyToMany(
|
|
361
|
+
() => {
|
|
362
|
+
return Monitor;
|
|
363
|
+
},
|
|
364
|
+
{ eager: false },
|
|
365
|
+
)
|
|
366
|
+
@JoinTable({
|
|
367
|
+
name: "AnnouncementTemplateMonitor",
|
|
368
|
+
inverseJoinColumn: {
|
|
369
|
+
name: "monitorId",
|
|
370
|
+
referencedColumnName: "_id",
|
|
371
|
+
},
|
|
372
|
+
joinColumn: {
|
|
373
|
+
name: "announcementTemplateId",
|
|
374
|
+
referencedColumnName: "_id",
|
|
375
|
+
},
|
|
376
|
+
})
|
|
377
|
+
public monitors?: Array<Monitor> = undefined;
|
|
378
|
+
|
|
331
379
|
@ColumnAccessControl({
|
|
332
380
|
create: [
|
|
333
381
|
Permission.ProjectOwner,
|
|
@@ -2076,6 +2076,10 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
2076
2076
|
_id: true,
|
|
2077
2077
|
showAnnouncementAt: true,
|
|
2078
2078
|
endAnnouncementAt: true,
|
|
2079
|
+
monitors: {
|
|
2080
|
+
_id: true,
|
|
2081
|
+
name: true,
|
|
2082
|
+
},
|
|
2079
2083
|
},
|
|
2080
2084
|
skip: 0,
|
|
2081
2085
|
limit: LIMIT_PER_PROJECT,
|
|
@@ -2096,6 +2100,7 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
2096
2100
|
displayTooltip: true,
|
|
2097
2101
|
displayDescription: true,
|
|
2098
2102
|
displayName: true,
|
|
2103
|
+
monitorGroupId: true,
|
|
2099
2104
|
monitor: {
|
|
2100
2105
|
_id: true,
|
|
2101
2106
|
currentMonitorStatusId: true,
|
|
@@ -2109,6 +2114,65 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
2109
2114
|
},
|
|
2110
2115
|
});
|
|
2111
2116
|
|
|
2117
|
+
const monitorGroupIds: Array<ObjectID> = statusPageResources
|
|
2118
|
+
.map((resource: StatusPageResource) => {
|
|
2119
|
+
return resource.monitorGroupId!;
|
|
2120
|
+
})
|
|
2121
|
+
.filter((id: ObjectID) => {
|
|
2122
|
+
return Boolean(id); // remove nulls
|
|
2123
|
+
});
|
|
2124
|
+
|
|
2125
|
+
// get monitors in the group.
|
|
2126
|
+
const monitorsInGroup: Dictionary<Array<ObjectID>> = {};
|
|
2127
|
+
|
|
2128
|
+
// get monitor status charts.
|
|
2129
|
+
const monitorsOnStatusPage: Array<ObjectID> = statusPageResources
|
|
2130
|
+
.map((monitor: StatusPageResource) => {
|
|
2131
|
+
return monitor.monitorId!;
|
|
2132
|
+
})
|
|
2133
|
+
.filter((id: ObjectID) => {
|
|
2134
|
+
return Boolean(id); // remove nulls
|
|
2135
|
+
});
|
|
2136
|
+
|
|
2137
|
+
for (const monitorGroupId of monitorGroupIds) {
|
|
2138
|
+
// get monitors in the group.
|
|
2139
|
+
|
|
2140
|
+
const groupResources: Array<MonitorGroupResource> =
|
|
2141
|
+
await MonitorGroupResourceService.findBy({
|
|
2142
|
+
query: {
|
|
2143
|
+
monitorGroupId: monitorGroupId,
|
|
2144
|
+
},
|
|
2145
|
+
select: {
|
|
2146
|
+
monitorId: true,
|
|
2147
|
+
},
|
|
2148
|
+
props: {
|
|
2149
|
+
isRoot: true,
|
|
2150
|
+
},
|
|
2151
|
+
limit: LIMIT_PER_PROJECT,
|
|
2152
|
+
skip: 0,
|
|
2153
|
+
});
|
|
2154
|
+
|
|
2155
|
+
const monitorsInGroupIds: Array<ObjectID> = groupResources
|
|
2156
|
+
.map((resource: MonitorGroupResource) => {
|
|
2157
|
+
return resource.monitorId!;
|
|
2158
|
+
})
|
|
2159
|
+
.filter((id: ObjectID) => {
|
|
2160
|
+
return Boolean(id); // remove nulls
|
|
2161
|
+
});
|
|
2162
|
+
|
|
2163
|
+
for (const monitorId of monitorsInGroupIds) {
|
|
2164
|
+
if (
|
|
2165
|
+
!monitorsOnStatusPage.find((item: ObjectID) => {
|
|
2166
|
+
return item.toString() === monitorId.toString();
|
|
2167
|
+
})
|
|
2168
|
+
) {
|
|
2169
|
+
monitorsOnStatusPage.push(monitorId);
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
monitorsInGroup[monitorGroupId.toString()] = monitorsInGroupIds;
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2112
2176
|
const response: JSONObject = {
|
|
2113
2177
|
announcements: BaseModel.toJSONArray(
|
|
2114
2178
|
announcements,
|
|
@@ -2118,6 +2182,7 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
2118
2182
|
statusPageResources,
|
|
2119
2183
|
StatusPageResource,
|
|
2120
2184
|
),
|
|
2185
|
+
monitorsInGroup: JSONFunctions.serialize(monitorsInGroup),
|
|
2121
2186
|
};
|
|
2122
2187
|
|
|
2123
2188
|
return response;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class MigrationName1757416939595 implements MigrationInterface {
|
|
4
|
+
public name = "MigrationName1757416939595";
|
|
5
|
+
|
|
6
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
7
|
+
await queryRunner.query(
|
|
8
|
+
`ALTER TABLE "IncidentTemplate" ADD "initialIncidentStateId" uuid`,
|
|
9
|
+
);
|
|
10
|
+
await queryRunner.query(
|
|
11
|
+
`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type":"Recurring","value":{"intervalType":"Day","intervalCount":{"_type":"PositiveNumber","value":1}}}'`,
|
|
12
|
+
);
|
|
13
|
+
await queryRunner.query(
|
|
14
|
+
`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type":"RestrictionTimes","value":{"restictionType":"None","dayRestrictionTimes":null,"weeklyRestrictionTimes":[]}}'`,
|
|
15
|
+
);
|
|
16
|
+
await queryRunner.query(
|
|
17
|
+
`CREATE INDEX "IDX_36317c99429a40d3344d838223" ON "IncidentTemplate" ("initialIncidentStateId") `,
|
|
18
|
+
);
|
|
19
|
+
await queryRunner.query(
|
|
20
|
+
`ALTER TABLE "IncidentTemplate" ADD CONSTRAINT "FK_36317c99429a40d3344d838223f" FOREIGN KEY ("initialIncidentStateId") REFERENCES "IncidentState"("_id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
25
|
+
await queryRunner.query(
|
|
26
|
+
`ALTER TABLE "IncidentTemplate" DROP CONSTRAINT "FK_36317c99429a40d3344d838223f"`,
|
|
27
|
+
);
|
|
28
|
+
await queryRunner.query(
|
|
29
|
+
`DROP INDEX "public"."IDX_36317c99429a40d3344d838223"`,
|
|
30
|
+
);
|
|
31
|
+
await queryRunner.query(
|
|
32
|
+
`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type": "RestrictionTimes", "value": {"restictionType": "None", "dayRestrictionTimes": null, "weeklyRestrictionTimes": []}}'`,
|
|
33
|
+
);
|
|
34
|
+
await queryRunner.query(
|
|
35
|
+
`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type": "Recurring", "value": {"intervalType": "Day", "intervalCount": {"_type": "PositiveNumber", "value": 1}}}'`,
|
|
36
|
+
);
|
|
37
|
+
await queryRunner.query(
|
|
38
|
+
`ALTER TABLE "IncidentTemplate" DROP COLUMN "initialIncidentStateId"`,
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class MigrationName1757423505855 implements MigrationInterface {
|
|
4
|
+
public name = "MigrationName1757423505855";
|
|
5
|
+
|
|
6
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
7
|
+
await queryRunner.query(
|
|
8
|
+
`CREATE TABLE "AnnouncementMonitor" ("announcementId" uuid NOT NULL, "monitorId" uuid NOT NULL, CONSTRAINT "PK_7acb54ddede76e67b5e2eb84519" PRIMARY KEY ("announcementId", "monitorId"))`,
|
|
9
|
+
);
|
|
10
|
+
await queryRunner.query(
|
|
11
|
+
`CREATE INDEX "IDX_b43baa07f7be40b5cfb61153fd" ON "AnnouncementMonitor" ("announcementId") `,
|
|
12
|
+
);
|
|
13
|
+
await queryRunner.query(
|
|
14
|
+
`CREATE INDEX "IDX_751be8c61cfeb7e1a0af9fcc3a" ON "AnnouncementMonitor" ("monitorId") `,
|
|
15
|
+
);
|
|
16
|
+
await queryRunner.query(
|
|
17
|
+
`CREATE TABLE "AnnouncementTemplateMonitor" ("announcementTemplateId" uuid NOT NULL, "monitorId" uuid NOT NULL, CONSTRAINT "PK_ad19f2b65c1b6b77e7a8d80c028" PRIMARY KEY ("announcementTemplateId", "monitorId"))`,
|
|
18
|
+
);
|
|
19
|
+
await queryRunner.query(
|
|
20
|
+
`CREATE INDEX "IDX_46bee9106e631ebe9f6c95ff15" ON "AnnouncementTemplateMonitor" ("announcementTemplateId") `,
|
|
21
|
+
);
|
|
22
|
+
await queryRunner.query(
|
|
23
|
+
`CREATE INDEX "IDX_0d979cb538fde87c7441d7bc93" ON "AnnouncementTemplateMonitor" ("monitorId") `,
|
|
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 "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type":"RestrictionTimes","value":{"restictionType":"None","dayRestrictionTimes":null,"weeklyRestrictionTimes":[]}}'`,
|
|
30
|
+
);
|
|
31
|
+
await queryRunner.query(
|
|
32
|
+
`ALTER TABLE "AnnouncementMonitor" ADD CONSTRAINT "FK_b43baa07f7be40b5cfb61153fd3" FOREIGN KEY ("announcementId") REFERENCES "StatusPageAnnouncement"("_id") ON DELETE CASCADE ON UPDATE CASCADE`,
|
|
33
|
+
);
|
|
34
|
+
await queryRunner.query(
|
|
35
|
+
`ALTER TABLE "AnnouncementMonitor" ADD CONSTRAINT "FK_751be8c61cfeb7e1a0af9fcc3a0" FOREIGN KEY ("monitorId") REFERENCES "Monitor"("_id") ON DELETE CASCADE ON UPDATE CASCADE`,
|
|
36
|
+
);
|
|
37
|
+
await queryRunner.query(
|
|
38
|
+
`ALTER TABLE "AnnouncementTemplateMonitor" ADD CONSTRAINT "FK_46bee9106e631ebe9f6c95ff153" FOREIGN KEY ("announcementTemplateId") REFERENCES "StatusPageAnnouncementTemplate"("_id") ON DELETE CASCADE ON UPDATE CASCADE`,
|
|
39
|
+
);
|
|
40
|
+
await queryRunner.query(
|
|
41
|
+
`ALTER TABLE "AnnouncementTemplateMonitor" ADD CONSTRAINT "FK_0d979cb538fde87c7441d7bc936" FOREIGN KEY ("monitorId") REFERENCES "Monitor"("_id") ON DELETE CASCADE ON UPDATE CASCADE`,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
46
|
+
await queryRunner.query(
|
|
47
|
+
`ALTER TABLE "AnnouncementTemplateMonitor" DROP CONSTRAINT "FK_0d979cb538fde87c7441d7bc936"`,
|
|
48
|
+
);
|
|
49
|
+
await queryRunner.query(
|
|
50
|
+
`ALTER TABLE "AnnouncementTemplateMonitor" DROP CONSTRAINT "FK_46bee9106e631ebe9f6c95ff153"`,
|
|
51
|
+
);
|
|
52
|
+
await queryRunner.query(
|
|
53
|
+
`ALTER TABLE "AnnouncementMonitor" DROP CONSTRAINT "FK_751be8c61cfeb7e1a0af9fcc3a0"`,
|
|
54
|
+
);
|
|
55
|
+
await queryRunner.query(
|
|
56
|
+
`ALTER TABLE "AnnouncementMonitor" DROP CONSTRAINT "FK_b43baa07f7be40b5cfb61153fd3"`,
|
|
57
|
+
);
|
|
58
|
+
await queryRunner.query(
|
|
59
|
+
`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type": "RestrictionTimes", "value": {"restictionType": "None", "dayRestrictionTimes": null, "weeklyRestrictionTimes": []}}'`,
|
|
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
|
+
`DROP INDEX "public"."IDX_0d979cb538fde87c7441d7bc93"`,
|
|
66
|
+
);
|
|
67
|
+
await queryRunner.query(
|
|
68
|
+
`DROP INDEX "public"."IDX_46bee9106e631ebe9f6c95ff15"`,
|
|
69
|
+
);
|
|
70
|
+
await queryRunner.query(`DROP TABLE "AnnouncementTemplateMonitor"`);
|
|
71
|
+
await queryRunner.query(
|
|
72
|
+
`DROP INDEX "public"."IDX_751be8c61cfeb7e1a0af9fcc3a"`,
|
|
73
|
+
);
|
|
74
|
+
await queryRunner.query(
|
|
75
|
+
`DROP INDEX "public"."IDX_b43baa07f7be40b5cfb61153fd"`,
|
|
76
|
+
);
|
|
77
|
+
await queryRunner.query(`DROP TABLE "AnnouncementMonitor"`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -165,6 +165,8 @@ import { MigrationName1756293325324 } from "./1756293325324-MigrationName";
|
|
|
165
165
|
import { MigrationName1756296282627 } from "./1756296282627-MigrationName";
|
|
166
166
|
import { MigrationName1756300358095 } from "./1756300358095-MigrationName";
|
|
167
167
|
import { MigrationName1756821449686 } from "./1756821449686-MigrationName";
|
|
168
|
+
import { MigrationName1757416939595 } from "./1757416939595-MigrationName";
|
|
169
|
+
import { MigrationName1757423505855 } from "./1757423505855-MigrationName";
|
|
168
170
|
|
|
169
171
|
export default [
|
|
170
172
|
InitialMigration,
|
|
@@ -334,4 +336,6 @@ export default [
|
|
|
334
336
|
MigrationName1756296282627,
|
|
335
337
|
MigrationName1756300358095,
|
|
336
338
|
MigrationName1756821449686,
|
|
339
|
+
MigrationName1757416939595,
|
|
340
|
+
MigrationName1757423505855,
|
|
337
341
|
];
|
|
@@ -64,6 +64,8 @@ import MetricType from "../../Models/DatabaseModels/MetricType";
|
|
|
64
64
|
import UpdateBy from "../Types/Database/UpdateBy";
|
|
65
65
|
import OnCallDutyPolicy from "../../Models/DatabaseModels/OnCallDutyPolicy";
|
|
66
66
|
import Dictionary from "../../Types/Dictionary";
|
|
67
|
+
import IncidentTemplateService from "./IncidentTemplateService";
|
|
68
|
+
import IncidentTemplate from "../../Models/DatabaseModels/IncidentTemplate";
|
|
67
69
|
|
|
68
70
|
// key is incidentId for this dictionary.
|
|
69
71
|
type UpdateCarryForward = Dictionary<{
|
|
@@ -466,24 +468,97 @@ export class Service extends DatabaseService<Model> {
|
|
|
466
468
|
const projectId: ObjectID =
|
|
467
469
|
createBy.props.tenantId || createBy.data.projectId!;
|
|
468
470
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
query: {
|
|
472
|
-
projectId: projectId,
|
|
473
|
-
isCreatedState: true,
|
|
474
|
-
},
|
|
475
|
-
select: {
|
|
476
|
-
_id: true,
|
|
477
|
-
},
|
|
478
|
-
props: {
|
|
479
|
-
isRoot: true,
|
|
480
|
-
},
|
|
481
|
-
});
|
|
471
|
+
// Determine the initial incident state
|
|
472
|
+
let initialIncidentStateId: ObjectID | undefined = undefined;
|
|
482
473
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
474
|
+
// If currentIncidentStateId is already provided (manual selection), use it
|
|
475
|
+
if (createBy.data.currentIncidentStateId) {
|
|
476
|
+
initialIncidentStateId = createBy.data.currentIncidentStateId;
|
|
477
|
+
|
|
478
|
+
// Validate that the provided state exists and belongs to the project
|
|
479
|
+
const providedState: IncidentState | null =
|
|
480
|
+
await IncidentStateService.findOneBy({
|
|
481
|
+
query: {
|
|
482
|
+
_id: initialIncidentStateId.toString(),
|
|
483
|
+
projectId: projectId,
|
|
484
|
+
},
|
|
485
|
+
select: {
|
|
486
|
+
_id: true,
|
|
487
|
+
},
|
|
488
|
+
props: {
|
|
489
|
+
isRoot: true,
|
|
490
|
+
},
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
if (!providedState) {
|
|
494
|
+
throw new BadDataException(
|
|
495
|
+
"Invalid incident state provided. The state does not exist or does not belong to this project.",
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
} else if (createBy.data.createdIncidentTemplateId) {
|
|
499
|
+
// If created from a template, check if template has a custom initial state
|
|
500
|
+
const incidentTemplate: IncidentTemplate | null =
|
|
501
|
+
await IncidentTemplateService.findOneBy({
|
|
502
|
+
query: {
|
|
503
|
+
_id: createBy.data.createdIncidentTemplateId.toString(),
|
|
504
|
+
projectId: projectId,
|
|
505
|
+
},
|
|
506
|
+
select: {
|
|
507
|
+
initialIncidentStateId: true,
|
|
508
|
+
},
|
|
509
|
+
props: {
|
|
510
|
+
isRoot: true,
|
|
511
|
+
},
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
if (incidentTemplate?.initialIncidentStateId) {
|
|
515
|
+
initialIncidentStateId = incidentTemplate.initialIncidentStateId;
|
|
516
|
+
|
|
517
|
+
// Validate that the template's state exists and belongs to the project
|
|
518
|
+
const templateState: IncidentState | null =
|
|
519
|
+
await IncidentStateService.findOneBy({
|
|
520
|
+
query: {
|
|
521
|
+
_id: initialIncidentStateId.toString(),
|
|
522
|
+
projectId: projectId,
|
|
523
|
+
},
|
|
524
|
+
select: {
|
|
525
|
+
_id: true,
|
|
526
|
+
},
|
|
527
|
+
props: {
|
|
528
|
+
isRoot: true,
|
|
529
|
+
},
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
if (!templateState) {
|
|
533
|
+
// Fall back to default if template state is invalid
|
|
534
|
+
initialIncidentStateId = undefined;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// If no custom state is provided or found, fall back to default created state
|
|
540
|
+
if (!initialIncidentStateId) {
|
|
541
|
+
const incidentState: IncidentState | null =
|
|
542
|
+
await IncidentStateService.findOneBy({
|
|
543
|
+
query: {
|
|
544
|
+
projectId: projectId,
|
|
545
|
+
isCreatedState: true,
|
|
546
|
+
},
|
|
547
|
+
select: {
|
|
548
|
+
_id: true,
|
|
549
|
+
},
|
|
550
|
+
props: {
|
|
551
|
+
isRoot: true,
|
|
552
|
+
},
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
if (!incidentState || !incidentState.id) {
|
|
556
|
+
throw new BadDataException(
|
|
557
|
+
"Created incident state not found for this project. Please add created incident state from settings.",
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
initialIncidentStateId = incidentState.id;
|
|
487
562
|
}
|
|
488
563
|
|
|
489
564
|
let mutex: SemaphoreMutex | null = null;
|
|
@@ -517,7 +592,7 @@ export class Service extends DatabaseService<Model> {
|
|
|
517
592
|
projectId: projectId,
|
|
518
593
|
})) + 1;
|
|
519
594
|
|
|
520
|
-
createBy.data.currentIncidentStateId =
|
|
595
|
+
createBy.data.currentIncidentStateId = initialIncidentStateId;
|
|
521
596
|
createBy.data.incidentNumber = incidentNumberForThisIncident;
|
|
522
597
|
|
|
523
598
|
if (
|
|
@@ -804,7 +804,10 @@ Stay informed about service availability! 🚀`;
|
|
|
804
804
|
if (
|
|
805
805
|
data.statusPage.allowSubscribersToChooseResources &&
|
|
806
806
|
!data.subscriber.isSubscribedToAllResources &&
|
|
807
|
-
|
|
807
|
+
!(
|
|
808
|
+
data.eventType === StatusPageEventType.Announcement &&
|
|
809
|
+
data.statusPageResources.length === 0
|
|
810
|
+
) // announcements with no monitors don't use resource filtering
|
|
808
811
|
) {
|
|
809
812
|
logger.debug(
|
|
810
813
|
"Subscriber can choose resources and is not subscribed to all resources.",
|
package/UI/Utils/Markdown.tsx
CHANGED
|
@@ -3,10 +3,6 @@ import React, { ReactElement } from "react";
|
|
|
3
3
|
export default class MarkdownUtil {
|
|
4
4
|
public static getMarkdownCheatsheet(prefix?: string): ReactElement {
|
|
5
5
|
const prefixText: string = prefix ? `${prefix}. ` : "";
|
|
6
|
-
return
|
|
7
|
-
<p>
|
|
8
|
-
{prefixText}
|
|
9
|
-
</p>
|
|
10
|
-
);
|
|
6
|
+
return <p>{prefixText}</p>;
|
|
11
7
|
}
|
|
12
8
|
}
|
|
@@ -57,6 +57,8 @@ let IncidentTemplate = class IncidentTemplate extends BaseModel {
|
|
|
57
57
|
this.incidentSeverityId = undefined;
|
|
58
58
|
this.changeMonitorStatusTo = undefined;
|
|
59
59
|
this.changeMonitorStatusToId = undefined;
|
|
60
|
+
this.initialIncidentState = undefined;
|
|
61
|
+
this.initialIncidentStateId = undefined;
|
|
60
62
|
this.customFields = undefined;
|
|
61
63
|
}
|
|
62
64
|
};
|
|
@@ -670,6 +672,74 @@ __decorate([
|
|
|
670
672
|
}),
|
|
671
673
|
__metadata("design:type", ObjectID)
|
|
672
674
|
], IncidentTemplate.prototype, "changeMonitorStatusToId", void 0);
|
|
675
|
+
__decorate([
|
|
676
|
+
ColumnAccessControl({
|
|
677
|
+
create: [
|
|
678
|
+
Permission.ProjectOwner,
|
|
679
|
+
Permission.ProjectAdmin,
|
|
680
|
+
Permission.ProjectMember,
|
|
681
|
+
Permission.CreateIncidentTemplate,
|
|
682
|
+
],
|
|
683
|
+
read: [
|
|
684
|
+
Permission.ProjectOwner,
|
|
685
|
+
Permission.ProjectAdmin,
|
|
686
|
+
Permission.ProjectMember,
|
|
687
|
+
Permission.ReadIncidentTemplate,
|
|
688
|
+
],
|
|
689
|
+
update: [],
|
|
690
|
+
}),
|
|
691
|
+
TableColumn({
|
|
692
|
+
manyToOneRelationColumn: "initialIncidentStateId",
|
|
693
|
+
type: TableColumnType.Entity,
|
|
694
|
+
modelType: IncidentState,
|
|
695
|
+
title: "Initial Incident State",
|
|
696
|
+
description: "Relation to Incident State Object. Incidents created from this template will start in this state.",
|
|
697
|
+
}),
|
|
698
|
+
ManyToOne(() => {
|
|
699
|
+
return IncidentState;
|
|
700
|
+
}, {
|
|
701
|
+
eager: false,
|
|
702
|
+
nullable: true,
|
|
703
|
+
orphanedRowAction: "nullify",
|
|
704
|
+
}),
|
|
705
|
+
JoinColumn({ name: "initialIncidentStateId" }),
|
|
706
|
+
__metadata("design:type", IncidentState)
|
|
707
|
+
], IncidentTemplate.prototype, "initialIncidentState", void 0);
|
|
708
|
+
__decorate([
|
|
709
|
+
ColumnAccessControl({
|
|
710
|
+
create: [
|
|
711
|
+
Permission.ProjectOwner,
|
|
712
|
+
Permission.ProjectAdmin,
|
|
713
|
+
Permission.ProjectMember,
|
|
714
|
+
Permission.CreateIncidentTemplate,
|
|
715
|
+
],
|
|
716
|
+
read: [
|
|
717
|
+
Permission.ProjectOwner,
|
|
718
|
+
Permission.ProjectAdmin,
|
|
719
|
+
Permission.ProjectMember,
|
|
720
|
+
Permission.ReadIncidentTemplate,
|
|
721
|
+
],
|
|
722
|
+
update: [
|
|
723
|
+
Permission.ProjectOwner,
|
|
724
|
+
Permission.ProjectAdmin,
|
|
725
|
+
Permission.ProjectMember,
|
|
726
|
+
Permission.EditIncidentTemplate,
|
|
727
|
+
],
|
|
728
|
+
}),
|
|
729
|
+
Index(),
|
|
730
|
+
TableColumn({
|
|
731
|
+
type: TableColumnType.ObjectID,
|
|
732
|
+
required: false,
|
|
733
|
+
title: "Initial Incident State ID",
|
|
734
|
+
description: "Relation to Incident State Object ID. Incidents created from this template will start in this state.",
|
|
735
|
+
}),
|
|
736
|
+
Column({
|
|
737
|
+
type: ColumnType.ObjectID,
|
|
738
|
+
nullable: true,
|
|
739
|
+
transformer: ObjectID.getDatabaseTransformer(),
|
|
740
|
+
}),
|
|
741
|
+
__metadata("design:type", ObjectID)
|
|
742
|
+
], IncidentTemplate.prototype, "initialIncidentStateId", void 0);
|
|
673
743
|
__decorate([
|
|
674
744
|
ColumnAccessControl({
|
|
675
745
|
create: [
|