@oneuptime/common 9.1.3 → 9.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/Incident.ts +112 -0
- package/Server/API/IncidentAPI.ts +106 -0
- package/Server/API/StatusPageAPI.ts +134 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1764762146063-MigrationName.ts +45 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1764767371788-MigrationName.ts +23 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Types/Icon/IconProp.ts +1 -0
- package/UI/Components/EventItem/EventItem.tsx +32 -14
- package/UI/Components/Icon/Icon.tsx +8 -0
- package/build/dist/Models/DatabaseModels/Incident.js +115 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Server/API/IncidentAPI.js +76 -0
- package/build/dist/Server/API/IncidentAPI.js.map +1 -0
- package/build/dist/Server/API/StatusPageAPI.js +154 -45
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1764762146063-MigrationName.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1764762146063-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1764767371788-MigrationName.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1764767371788-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/Types/Icon/IconProp.js +1 -0
- package/build/dist/Types/Icon/IconProp.js.map +1 -1
- package/build/dist/UI/Components/EventItem/EventItem.js +10 -6
- package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +3 -0
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@ import OnCallDutyPolicy from "./OnCallDutyPolicy";
|
|
|
7
7
|
import Probe from "./Probe";
|
|
8
8
|
import Project from "./Project";
|
|
9
9
|
import User from "./User";
|
|
10
|
+
import File from "./File";
|
|
10
11
|
import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
|
|
11
12
|
import Route from "../../Types/API/Route";
|
|
12
13
|
import ColumnAccessControl from "../../Types/Database/AccessControl/ColumnAccessControl";
|
|
@@ -996,6 +997,117 @@ export default class Incident extends BaseModel {
|
|
|
996
997
|
})
|
|
997
998
|
public postmortemNote?: string = undefined;
|
|
998
999
|
|
|
1000
|
+
@ColumnAccessControl({
|
|
1001
|
+
create: [
|
|
1002
|
+
Permission.ProjectOwner,
|
|
1003
|
+
Permission.ProjectAdmin,
|
|
1004
|
+
Permission.ProjectMember,
|
|
1005
|
+
Permission.CreateProjectIncident,
|
|
1006
|
+
],
|
|
1007
|
+
read: [
|
|
1008
|
+
Permission.ProjectOwner,
|
|
1009
|
+
Permission.ProjectAdmin,
|
|
1010
|
+
Permission.ProjectMember,
|
|
1011
|
+
Permission.ReadProjectIncident,
|
|
1012
|
+
],
|
|
1013
|
+
update: [
|
|
1014
|
+
Permission.ProjectOwner,
|
|
1015
|
+
Permission.ProjectAdmin,
|
|
1016
|
+
Permission.ProjectMember,
|
|
1017
|
+
Permission.EditProjectIncident,
|
|
1018
|
+
],
|
|
1019
|
+
})
|
|
1020
|
+
@TableColumn({
|
|
1021
|
+
type: TableColumnType.Boolean,
|
|
1022
|
+
title: "Show postmortem on status page?",
|
|
1023
|
+
description:
|
|
1024
|
+
"Should the postmortem note and attachments be visible on the status page once published?",
|
|
1025
|
+
defaultValue: false,
|
|
1026
|
+
isDefaultValueColumn: true,
|
|
1027
|
+
})
|
|
1028
|
+
@Column({
|
|
1029
|
+
type: ColumnType.Boolean,
|
|
1030
|
+
default: false,
|
|
1031
|
+
})
|
|
1032
|
+
public showPostmortemOnStatusPage?: boolean = undefined;
|
|
1033
|
+
|
|
1034
|
+
@ColumnAccessControl({
|
|
1035
|
+
create: [
|
|
1036
|
+
Permission.ProjectOwner,
|
|
1037
|
+
Permission.ProjectAdmin,
|
|
1038
|
+
Permission.ProjectMember,
|
|
1039
|
+
Permission.CreateProjectIncident,
|
|
1040
|
+
],
|
|
1041
|
+
read: [
|
|
1042
|
+
Permission.ProjectOwner,
|
|
1043
|
+
Permission.ProjectAdmin,
|
|
1044
|
+
Permission.ProjectMember,
|
|
1045
|
+
Permission.ReadProjectIncident,
|
|
1046
|
+
],
|
|
1047
|
+
update: [
|
|
1048
|
+
Permission.ProjectOwner,
|
|
1049
|
+
Permission.ProjectAdmin,
|
|
1050
|
+
Permission.ProjectMember,
|
|
1051
|
+
Permission.EditProjectIncident,
|
|
1052
|
+
],
|
|
1053
|
+
})
|
|
1054
|
+
@TableColumn({
|
|
1055
|
+
type: TableColumnType.Date,
|
|
1056
|
+
title: "Postmortem Posted At",
|
|
1057
|
+
description:
|
|
1058
|
+
"Timestamp that will be shown alongside the published postmortem on the status page.",
|
|
1059
|
+
required: false,
|
|
1060
|
+
})
|
|
1061
|
+
@Column({
|
|
1062
|
+
type: ColumnType.Date,
|
|
1063
|
+
nullable: true,
|
|
1064
|
+
})
|
|
1065
|
+
public postmortemPostedAt?: Date = undefined;
|
|
1066
|
+
|
|
1067
|
+
@ColumnAccessControl({
|
|
1068
|
+
create: [
|
|
1069
|
+
Permission.ProjectOwner,
|
|
1070
|
+
Permission.ProjectAdmin,
|
|
1071
|
+
Permission.ProjectMember,
|
|
1072
|
+
Permission.CreateProjectIncident,
|
|
1073
|
+
],
|
|
1074
|
+
read: [
|
|
1075
|
+
Permission.ProjectOwner,
|
|
1076
|
+
Permission.ProjectAdmin,
|
|
1077
|
+
Permission.ProjectMember,
|
|
1078
|
+
Permission.ReadProjectIncident,
|
|
1079
|
+
],
|
|
1080
|
+
update: [
|
|
1081
|
+
Permission.ProjectOwner,
|
|
1082
|
+
Permission.ProjectAdmin,
|
|
1083
|
+
Permission.ProjectMember,
|
|
1084
|
+
Permission.EditProjectIncident,
|
|
1085
|
+
],
|
|
1086
|
+
})
|
|
1087
|
+
@TableColumn({
|
|
1088
|
+
type: TableColumnType.EntityArray,
|
|
1089
|
+
modelType: File,
|
|
1090
|
+
title: "Postmortem Attachments",
|
|
1091
|
+
description:
|
|
1092
|
+
"Files that accompany the postmortem note and can be shared publicly when enabled.",
|
|
1093
|
+
required: false,
|
|
1094
|
+
})
|
|
1095
|
+
@ManyToMany(() => {
|
|
1096
|
+
return File;
|
|
1097
|
+
})
|
|
1098
|
+
@JoinTable({
|
|
1099
|
+
name: "IncidentPostmortemAttachmentFile",
|
|
1100
|
+
joinColumn: {
|
|
1101
|
+
name: "incidentId",
|
|
1102
|
+
referencedColumnName: "_id",
|
|
1103
|
+
},
|
|
1104
|
+
inverseJoinColumn: {
|
|
1105
|
+
name: "fileId",
|
|
1106
|
+
referencedColumnName: "_id",
|
|
1107
|
+
},
|
|
1108
|
+
})
|
|
1109
|
+
public postmortemAttachments?: Array<File> = undefined;
|
|
1110
|
+
|
|
999
1111
|
@ColumnAccessControl({
|
|
1000
1112
|
create: [],
|
|
1001
1113
|
read: [
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import Incident from "../../Models/DatabaseModels/Incident";
|
|
2
|
+
import File from "../../Models/DatabaseModels/File";
|
|
3
|
+
import NotFoundException from "../../Types/Exception/NotFoundException";
|
|
4
|
+
import ObjectID from "../../Types/ObjectID";
|
|
5
|
+
import IncidentService, {
|
|
6
|
+
Service as IncidentServiceType,
|
|
7
|
+
} from "../Services/IncidentService";
|
|
8
|
+
import UserMiddleware from "../Middleware/UserAuthorization";
|
|
9
|
+
import Response from "../Utils/Response";
|
|
10
|
+
import BaseAPI from "./BaseAPI";
|
|
11
|
+
import {
|
|
12
|
+
ExpressRequest,
|
|
13
|
+
ExpressResponse,
|
|
14
|
+
NextFunction,
|
|
15
|
+
} from "../Utils/Express";
|
|
16
|
+
import CommonAPI from "./CommonAPI";
|
|
17
|
+
import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
18
|
+
|
|
19
|
+
export default class IncidentAPI extends BaseAPI<
|
|
20
|
+
Incident,
|
|
21
|
+
IncidentServiceType
|
|
22
|
+
> {
|
|
23
|
+
public constructor() {
|
|
24
|
+
super(Incident, IncidentService);
|
|
25
|
+
|
|
26
|
+
this.router.get(
|
|
27
|
+
`${new this.entityType()
|
|
28
|
+
.getCrudApiPath()
|
|
29
|
+
?.toString()}/postmortem/attachment/:projectId/:incidentId/:fileId`,
|
|
30
|
+
UserMiddleware.getUserMiddleware,
|
|
31
|
+
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
32
|
+
try {
|
|
33
|
+
await this.getPostmortemAttachment(req, res);
|
|
34
|
+
} catch (err) {
|
|
35
|
+
next(err);
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private async getPostmortemAttachment(
|
|
42
|
+
req: ExpressRequest,
|
|
43
|
+
res: ExpressResponse,
|
|
44
|
+
): Promise<void> {
|
|
45
|
+
const projectIdParam: string | undefined = req.params["projectId"];
|
|
46
|
+
const incidentIdParam: string | undefined = req.params["incidentId"];
|
|
47
|
+
const fileIdParam: string | undefined = req.params["fileId"];
|
|
48
|
+
|
|
49
|
+
if (!projectIdParam || !incidentIdParam || !fileIdParam) {
|
|
50
|
+
throw new NotFoundException("Attachment not found");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let incidentId: ObjectID;
|
|
54
|
+
let fileId: ObjectID;
|
|
55
|
+
let projectId: ObjectID;
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
incidentId = new ObjectID(incidentIdParam);
|
|
59
|
+
fileId = new ObjectID(fileIdParam);
|
|
60
|
+
projectId = new ObjectID(projectIdParam);
|
|
61
|
+
} catch {
|
|
62
|
+
throw new NotFoundException("Attachment not found");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const props: DatabaseCommonInteractionProps =
|
|
66
|
+
await CommonAPI.getDatabaseCommonInteractionProps(req);
|
|
67
|
+
|
|
68
|
+
const incident: Incident | null = await this.service.findOneBy({
|
|
69
|
+
query: {
|
|
70
|
+
_id: incidentId,
|
|
71
|
+
projectId,
|
|
72
|
+
},
|
|
73
|
+
select: {
|
|
74
|
+
postmortemAttachments: {
|
|
75
|
+
_id: true,
|
|
76
|
+
file: true,
|
|
77
|
+
fileType: true,
|
|
78
|
+
name: true,
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
props,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (!incident) {
|
|
85
|
+
throw new NotFoundException("Attachment not found");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const attachment: File | undefined = incident.postmortemAttachments?.find(
|
|
89
|
+
(file: File) => {
|
|
90
|
+
const attachmentId: string | null = file._id
|
|
91
|
+
? file._id.toString()
|
|
92
|
+
: file.id
|
|
93
|
+
? file.id.toString()
|
|
94
|
+
: null;
|
|
95
|
+
return attachmentId === fileId.toString();
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
if (!attachment || !attachment.file) {
|
|
100
|
+
throw new NotFoundException("Attachment not found");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
Response.setNoCacheHeaders(res);
|
|
104
|
+
return Response.sendFileResponse(req, res, attachment);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -408,6 +408,20 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
408
408
|
},
|
|
409
409
|
);
|
|
410
410
|
|
|
411
|
+
this.router.get(
|
|
412
|
+
`${new this.entityType()
|
|
413
|
+
.getCrudApiPath()
|
|
414
|
+
?.toString()}/incident/postmortem/attachment/:statusPageId/:incidentId/:fileId`,
|
|
415
|
+
UserMiddleware.getUserMiddleware,
|
|
416
|
+
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
417
|
+
try {
|
|
418
|
+
await this.getIncidentPostmortemAttachment(req, res);
|
|
419
|
+
} catch (err) {
|
|
420
|
+
next(err);
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
);
|
|
424
|
+
|
|
411
425
|
this.router.get(
|
|
412
426
|
`${new this.entityType()
|
|
413
427
|
.getCrudApiPath()
|
|
@@ -1422,9 +1436,17 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
1422
1436
|
let select: Select<Incident> = {
|
|
1423
1437
|
createdAt: true,
|
|
1424
1438
|
declaredAt: true,
|
|
1439
|
+
updatedAt: true,
|
|
1425
1440
|
title: true,
|
|
1426
1441
|
description: true,
|
|
1427
1442
|
_id: true,
|
|
1443
|
+
postmortemNote: true,
|
|
1444
|
+
postmortemPostedAt: true,
|
|
1445
|
+
showPostmortemOnStatusPage: true,
|
|
1446
|
+
postmortemAttachments: {
|
|
1447
|
+
_id: true,
|
|
1448
|
+
name: true,
|
|
1449
|
+
},
|
|
1428
1450
|
incidentSeverity: {
|
|
1429
1451
|
name: true,
|
|
1430
1452
|
color: true,
|
|
@@ -3306,9 +3328,17 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
3306
3328
|
let selectIncidents: Select<Incident> = {
|
|
3307
3329
|
createdAt: true,
|
|
3308
3330
|
declaredAt: true,
|
|
3331
|
+
updatedAt: true,
|
|
3309
3332
|
title: true,
|
|
3310
3333
|
description: true,
|
|
3311
3334
|
_id: true,
|
|
3335
|
+
postmortemNote: true,
|
|
3336
|
+
postmortemPostedAt: true,
|
|
3337
|
+
showPostmortemOnStatusPage: true,
|
|
3338
|
+
postmortemAttachments: {
|
|
3339
|
+
_id: true,
|
|
3340
|
+
name: true,
|
|
3341
|
+
},
|
|
3312
3342
|
incidentSeverity: {
|
|
3313
3343
|
name: true,
|
|
3314
3344
|
color: true,
|
|
@@ -3969,6 +3999,110 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
3969
3999
|
return Response.sendFileResponse(req, res, attachment);
|
|
3970
4000
|
}
|
|
3971
4001
|
|
|
4002
|
+
private async getIncidentPostmortemAttachment(
|
|
4003
|
+
req: ExpressRequest,
|
|
4004
|
+
res: ExpressResponse,
|
|
4005
|
+
): Promise<void> {
|
|
4006
|
+
const statusPageIdParam: string | undefined = req.params["statusPageId"];
|
|
4007
|
+
const incidentIdParam: string | undefined = req.params["incidentId"];
|
|
4008
|
+
const fileIdParam: string | undefined = req.params["fileId"];
|
|
4009
|
+
|
|
4010
|
+
if (!statusPageIdParam || !incidentIdParam || !fileIdParam) {
|
|
4011
|
+
throw new NotFoundException("Attachment not found");
|
|
4012
|
+
}
|
|
4013
|
+
|
|
4014
|
+
let statusPageId: ObjectID;
|
|
4015
|
+
let incidentId: ObjectID;
|
|
4016
|
+
let fileId: ObjectID;
|
|
4017
|
+
|
|
4018
|
+
try {
|
|
4019
|
+
statusPageId = new ObjectID(statusPageIdParam);
|
|
4020
|
+
incidentId = new ObjectID(incidentIdParam);
|
|
4021
|
+
fileId = new ObjectID(fileIdParam);
|
|
4022
|
+
} catch {
|
|
4023
|
+
throw new NotFoundException("Attachment not found");
|
|
4024
|
+
}
|
|
4025
|
+
|
|
4026
|
+
await this.checkHasReadAccess({
|
|
4027
|
+
statusPageId,
|
|
4028
|
+
req,
|
|
4029
|
+
});
|
|
4030
|
+
|
|
4031
|
+
const statusPage: StatusPage | null = await StatusPageService.findOneBy({
|
|
4032
|
+
query: {
|
|
4033
|
+
_id: statusPageId.toString(),
|
|
4034
|
+
},
|
|
4035
|
+
select: {
|
|
4036
|
+
_id: true,
|
|
4037
|
+
projectId: true,
|
|
4038
|
+
showIncidentsOnStatusPage: true,
|
|
4039
|
+
},
|
|
4040
|
+
props: {
|
|
4041
|
+
isRoot: true,
|
|
4042
|
+
},
|
|
4043
|
+
});
|
|
4044
|
+
|
|
4045
|
+
if (
|
|
4046
|
+
!statusPage ||
|
|
4047
|
+
!statusPage.projectId ||
|
|
4048
|
+
!statusPage.showIncidentsOnStatusPage
|
|
4049
|
+
) {
|
|
4050
|
+
throw new NotFoundException("Attachment not found");
|
|
4051
|
+
}
|
|
4052
|
+
|
|
4053
|
+
const { monitorsOnStatusPage } =
|
|
4054
|
+
await StatusPageService.getMonitorIdsOnStatusPage({
|
|
4055
|
+
statusPageId,
|
|
4056
|
+
});
|
|
4057
|
+
|
|
4058
|
+
if (!monitorsOnStatusPage || monitorsOnStatusPage.length === 0) {
|
|
4059
|
+
throw new NotFoundException("Attachment not found");
|
|
4060
|
+
}
|
|
4061
|
+
|
|
4062
|
+
const incident: Incident | null = await IncidentService.findOneBy({
|
|
4063
|
+
query: {
|
|
4064
|
+
_id: incidentId.toString(),
|
|
4065
|
+
projectId: statusPage.projectId!,
|
|
4066
|
+
isVisibleOnStatusPage: true,
|
|
4067
|
+
showPostmortemOnStatusPage: true,
|
|
4068
|
+
monitors: monitorsOnStatusPage as any,
|
|
4069
|
+
},
|
|
4070
|
+
select: {
|
|
4071
|
+
postmortemAttachments: {
|
|
4072
|
+
_id: true,
|
|
4073
|
+
file: true,
|
|
4074
|
+
fileType: true,
|
|
4075
|
+
name: true,
|
|
4076
|
+
},
|
|
4077
|
+
},
|
|
4078
|
+
props: {
|
|
4079
|
+
isRoot: true,
|
|
4080
|
+
},
|
|
4081
|
+
});
|
|
4082
|
+
|
|
4083
|
+
if (!incident) {
|
|
4084
|
+
throw new NotFoundException("Attachment not found");
|
|
4085
|
+
}
|
|
4086
|
+
|
|
4087
|
+
const attachment: File | undefined = incident.postmortemAttachments?.find(
|
|
4088
|
+
(file: File) => {
|
|
4089
|
+
const attachmentId: string | null = file._id
|
|
4090
|
+
? file._id.toString()
|
|
4091
|
+
: file.id
|
|
4092
|
+
? file.id.toString()
|
|
4093
|
+
: null;
|
|
4094
|
+
return attachmentId === fileId.toString();
|
|
4095
|
+
},
|
|
4096
|
+
);
|
|
4097
|
+
|
|
4098
|
+
if (!attachment || !attachment.file) {
|
|
4099
|
+
throw new NotFoundException("Attachment not found");
|
|
4100
|
+
}
|
|
4101
|
+
|
|
4102
|
+
Response.setNoCacheHeaders(res);
|
|
4103
|
+
return Response.sendFileResponse(req, res, attachment);
|
|
4104
|
+
}
|
|
4105
|
+
|
|
3972
4106
|
private async getIncidentPublicNoteAttachment(
|
|
3973
4107
|
req: ExpressRequest,
|
|
3974
4108
|
res: ExpressResponse,
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class MigrationName1764762146063 implements MigrationInterface {
|
|
4
|
+
public name = "MigrationName1764762146063";
|
|
5
|
+
|
|
6
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
7
|
+
await queryRunner.query(
|
|
8
|
+
`CREATE TABLE "IncidentPostmortemAttachmentFile" ("incidentId" uuid NOT NULL, "fileId" uuid NOT NULL, CONSTRAINT "PK_40b17c7d5bcfbde48d7ebab4130" PRIMARY KEY ("incidentId", "fileId"))`,
|
|
9
|
+
);
|
|
10
|
+
await queryRunner.query(
|
|
11
|
+
`CREATE INDEX "IDX_62b9c09c42e05df3f134aa14a4" ON "IncidentPostmortemAttachmentFile" ("incidentId") `,
|
|
12
|
+
);
|
|
13
|
+
await queryRunner.query(
|
|
14
|
+
`CREATE INDEX "IDX_7e09116a3b9672622bba9f8b2e" ON "IncidentPostmortemAttachmentFile" ("fileId") `,
|
|
15
|
+
);
|
|
16
|
+
await queryRunner.query(
|
|
17
|
+
`ALTER TABLE "Incident" ADD "showPostmortemOnStatusPage" boolean NOT NULL DEFAULT false`,
|
|
18
|
+
);
|
|
19
|
+
await queryRunner.query(
|
|
20
|
+
`ALTER TABLE "IncidentPostmortemAttachmentFile" ADD CONSTRAINT "FK_62b9c09c42e05df3f134aa14a46" FOREIGN KEY ("incidentId") REFERENCES "Incident"("_id") ON DELETE CASCADE ON UPDATE CASCADE`,
|
|
21
|
+
);
|
|
22
|
+
await queryRunner.query(
|
|
23
|
+
`ALTER TABLE "IncidentPostmortemAttachmentFile" ADD CONSTRAINT "FK_7e09116a3b9672622bba9f8b2e3" FOREIGN KEY ("fileId") REFERENCES "File"("_id") ON DELETE CASCADE ON UPDATE CASCADE`,
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
28
|
+
await queryRunner.query(
|
|
29
|
+
`ALTER TABLE "IncidentPostmortemAttachmentFile" DROP CONSTRAINT "FK_7e09116a3b9672622bba9f8b2e3"`,
|
|
30
|
+
);
|
|
31
|
+
await queryRunner.query(
|
|
32
|
+
`ALTER TABLE "IncidentPostmortemAttachmentFile" DROP CONSTRAINT "FK_62b9c09c42e05df3f134aa14a46"`,
|
|
33
|
+
);
|
|
34
|
+
await queryRunner.query(
|
|
35
|
+
`ALTER TABLE "Incident" DROP COLUMN "showPostmortemOnStatusPage"`,
|
|
36
|
+
);
|
|
37
|
+
await queryRunner.query(
|
|
38
|
+
`DROP INDEX "public"."IDX_7e09116a3b9672622bba9f8b2e"`,
|
|
39
|
+
);
|
|
40
|
+
await queryRunner.query(
|
|
41
|
+
`DROP INDEX "public"."IDX_62b9c09c42e05df3f134aa14a4"`,
|
|
42
|
+
);
|
|
43
|
+
await queryRunner.query(`DROP TABLE "IncidentPostmortemAttachmentFile"`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class MigrationName1764767371788 implements MigrationInterface {
|
|
4
|
+
public name = "MigrationName1764767371788";
|
|
5
|
+
|
|
6
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
7
|
+
await queryRunner.query(
|
|
8
|
+
`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type":"Recurring","value":{"intervalType":"Day","intervalCount":{"_type":"PositiveNumber","value":1}}}'`,
|
|
9
|
+
);
|
|
10
|
+
await queryRunner.query(
|
|
11
|
+
`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type":"RestrictionTimes","value":{"restictionType":"None","dayRestrictionTimes":null,"weeklyRestrictionTimes":[]}}'`,
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
16
|
+
await queryRunner.query(
|
|
17
|
+
`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type": "RestrictionTimes", "value": {"restictionType": "None", "dayRestrictionTimes": null, "weeklyRestrictionTimes": []}}'`,
|
|
18
|
+
);
|
|
19
|
+
await queryRunner.query(
|
|
20
|
+
`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type": "Recurring", "value": {"intervalType": "Day", "intervalCount": {"_type": "PositiveNumber", "value": 1}}}'`,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -187,6 +187,8 @@ import { MigrationName1763477560906 } from "./1763477560906-MigrationName";
|
|
|
187
187
|
import { MigrationName1763480947474 } from "./1763480947474-MigrationName";
|
|
188
188
|
import { MigrationName1763643080445 } from "./1763643080445-MigrationName";
|
|
189
189
|
import { MigrationName1764324618043 } from "./1764324618043-MigrationName";
|
|
190
|
+
import { MigrationName1764762146063 } from "./1764762146063-MigrationName";
|
|
191
|
+
import { MigrationName1764767371788 } from "./1764767371788-MigrationName";
|
|
190
192
|
|
|
191
193
|
export default [
|
|
192
194
|
InitialMigration,
|
|
@@ -378,4 +380,6 @@ export default [
|
|
|
378
380
|
MigrationName1763480947474,
|
|
379
381
|
MigrationName1763643080445,
|
|
380
382
|
MigrationName1764324618043,
|
|
383
|
+
MigrationName1764762146063,
|
|
384
|
+
MigrationName1764767371788,
|
|
381
385
|
];
|
package/Types/Icon/IconProp.ts
CHANGED
|
@@ -30,6 +30,8 @@ export interface TimelineItem {
|
|
|
30
30
|
icon: IconProp;
|
|
31
31
|
iconColor: Color;
|
|
32
32
|
attachments?: Array<TimelineAttachment>;
|
|
33
|
+
title?: string;
|
|
34
|
+
highlight?: boolean;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
export interface EventItemLabel {
|
|
@@ -258,25 +260,41 @@ const EventItem: FunctionComponent<ComponentProps> = (
|
|
|
258
260
|
aria-hidden="true"
|
|
259
261
|
></span>
|
|
260
262
|
)}
|
|
261
|
-
<div
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
263
|
+
<div
|
|
264
|
+
className={`relative flex items-start space-x-3 ${
|
|
265
|
+
item.highlight
|
|
266
|
+
? "rounded-2xl border border-gray-200 bg-gray-50 px-4 py-4 shadow-sm"
|
|
267
|
+
: ""
|
|
268
|
+
}`}
|
|
269
|
+
>
|
|
270
|
+
{!item.highlight && (
|
|
271
|
+
<div>
|
|
272
|
+
<div className="relative px-1">
|
|
273
|
+
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-gray-100 ring-8 ring-white">
|
|
274
|
+
<Icon
|
|
275
|
+
icon={item.icon}
|
|
276
|
+
className="h-5 w-5 text-gray-500"
|
|
277
|
+
style={{
|
|
278
|
+
color: item.iconColor.toString(),
|
|
279
|
+
}}
|
|
280
|
+
/>
|
|
281
|
+
</div>
|
|
272
282
|
</div>
|
|
273
283
|
</div>
|
|
274
|
-
|
|
284
|
+
)}
|
|
275
285
|
<div className="min-w-0 flex-1">
|
|
276
286
|
<div>
|
|
277
287
|
<div className="text-sm">
|
|
278
|
-
<span
|
|
279
|
-
|
|
288
|
+
<span
|
|
289
|
+
className={`font-medium ${
|
|
290
|
+
item.highlight
|
|
291
|
+
? "text-base text-gray-900"
|
|
292
|
+
: "text-sm text-gray-900"
|
|
293
|
+
}`}
|
|
294
|
+
>
|
|
295
|
+
{item.title
|
|
296
|
+
? item.title
|
|
297
|
+
: `Update to this ${props.eventType}`}
|
|
280
298
|
</span>
|
|
281
299
|
</div>
|
|
282
300
|
<p className="mt-0.5 text-sm text-gray-500">
|
|
@@ -1130,6 +1130,14 @@ const Icon: FunctionComponent<ComponentProps> = ({
|
|
|
1130
1130
|
d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z"
|
|
1131
1131
|
/>,
|
|
1132
1132
|
);
|
|
1133
|
+
} else if (icon === IconProp.DocumentCheck) {
|
|
1134
|
+
return getSvgWrapper(
|
|
1135
|
+
<path
|
|
1136
|
+
strokeLinecap="round"
|
|
1137
|
+
strokeLinejoin="round"
|
|
1138
|
+
d="M10.125 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.625M10.125 2.25H10.5c4.971 0 9 4.029 9 9v.375M10.125 2.25c1.864 0 3.375 1.511 3.375 3.375V7.125c0 .621.504 1.125 1.125 1.125h1.5c1.864 0 3.375 1.511 3.375 3.375M9 15l2.25 2.25L15 12"
|
|
1139
|
+
/>,
|
|
1140
|
+
);
|
|
1133
1141
|
} else if (icon === IconProp.TextFile || icon === IconProp.File) {
|
|
1134
1142
|
return getSvgWrapper(
|
|
1135
1143
|
<path
|