@oneuptime/common 9.5.6 → 9.5.7

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 (68) hide show
  1. package/Models/DatabaseModels/AlertEpisode.ts +25 -0
  2. package/Models/DatabaseModels/AlertGroupingRule.ts +2 -2
  3. package/Models/DatabaseModels/IncidentEpisode.ts +25 -0
  4. package/Models/DatabaseModels/IncidentGroupingRule.ts +2 -2
  5. package/Models/DatabaseModels/Index.ts +2 -0
  6. package/Models/DatabaseModels/OpenSourceDeployment.ts +140 -0
  7. package/Server/API/OpenSourceDeploymentAPI.ts +73 -0
  8. package/Server/EnvironmentConfig.ts +3 -0
  9. package/Server/Infrastructure/Postgres/SchemaMigrations/1770237245070-MigrationName.ts +7 -7
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1770668054908-MigrationName.ts +26 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/1770728946893-MigrationName.ts +47 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1770732721195-MigrationName.ts +27 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  14. package/Server/Services/AlertEpisodeMemberService.ts +10 -20
  15. package/Server/Services/AlertEpisodeService.ts +3 -2
  16. package/Server/Services/AlertGroupingEngineService.ts +1 -0
  17. package/Server/Services/IncidentEpisodeMemberService.ts +12 -20
  18. package/Server/Services/IncidentEpisodeService.ts +65 -3
  19. package/Server/Services/IncidentGroupingEngineService.ts +1 -0
  20. package/Server/Services/Index.ts +2 -0
  21. package/Server/Services/OpenSourceDeploymentService.ts +10 -0
  22. package/UI/Components/Forms/BasicForm.tsx +8 -1
  23. package/UI/Components/Forms/Types/Field.ts +3 -0
  24. package/build/dist/Models/DatabaseModels/AlertEpisode.js +26 -0
  25. package/build/dist/Models/DatabaseModels/AlertEpisode.js.map +1 -1
  26. package/build/dist/Models/DatabaseModels/AlertGroupingRule.js +2 -2
  27. package/build/dist/Models/DatabaseModels/AlertGroupingRule.js.map +1 -1
  28. package/build/dist/Models/DatabaseModels/IncidentEpisode.js +26 -0
  29. package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
  30. package/build/dist/Models/DatabaseModels/IncidentGroupingRule.js +2 -2
  31. package/build/dist/Models/DatabaseModels/IncidentGroupingRule.js.map +1 -1
  32. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  33. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  34. package/build/dist/Models/DatabaseModels/OpenSourceDeployment.js +167 -0
  35. package/build/dist/Models/DatabaseModels/OpenSourceDeployment.js.map +1 -0
  36. package/build/dist/Server/API/OpenSourceDeploymentAPI.js +55 -0
  37. package/build/dist/Server/API/OpenSourceDeploymentAPI.js.map +1 -0
  38. package/build/dist/Server/EnvironmentConfig.js +1 -0
  39. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  40. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770237245070-MigrationName.js +7 -7
  41. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770237245070-MigrationName.js.map +1 -1
  42. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770668054908-MigrationName.js +36 -0
  43. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770668054908-MigrationName.js.map +1 -0
  44. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770728946893-MigrationName.js +22 -0
  45. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770728946893-MigrationName.js.map +1 -0
  46. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770732721195-MigrationName.js +16 -0
  47. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770732721195-MigrationName.js.map +1 -0
  48. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  49. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  50. package/build/dist/Server/Services/AlertEpisodeMemberService.js +7 -13
  51. package/build/dist/Server/Services/AlertEpisodeMemberService.js.map +1 -1
  52. package/build/dist/Server/Services/AlertEpisodeService.js +3 -2
  53. package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
  54. package/build/dist/Server/Services/AlertGroupingEngineService.js +1 -0
  55. package/build/dist/Server/Services/AlertGroupingEngineService.js.map +1 -1
  56. package/build/dist/Server/Services/IncidentEpisodeMemberService.js +7 -13
  57. package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
  58. package/build/dist/Server/Services/IncidentEpisodeService.js +43 -3
  59. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  60. package/build/dist/Server/Services/IncidentGroupingEngineService.js +1 -0
  61. package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
  62. package/build/dist/Server/Services/Index.js +2 -0
  63. package/build/dist/Server/Services/Index.js.map +1 -1
  64. package/build/dist/Server/Services/OpenSourceDeploymentService.js +9 -0
  65. package/build/dist/Server/Services/OpenSourceDeploymentService.js.map +1 -0
  66. package/build/dist/UI/Components/Forms/BasicForm.js +3 -1
  67. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  68. package/package.json +1 -1
@@ -543,6 +543,31 @@ export default class AlertEpisode extends BaseModel {
543
543
  })
544
544
  public resolvedAt?: Date = undefined;
545
545
 
546
+ @ColumnAccessControl({
547
+ create: [],
548
+ read: [
549
+ Permission.ProjectOwner,
550
+ Permission.ProjectAdmin,
551
+ Permission.ProjectMember,
552
+ Permission.ReadAlertEpisode,
553
+ Permission.ReadAllProjectResources,
554
+ ],
555
+ update: [],
556
+ })
557
+ @Index()
558
+ @TableColumn({
559
+ type: TableColumnType.Date,
560
+ title: "All Alerts Resolved At",
561
+ description:
562
+ "When all alerts in this episode were first detected as resolved. Used for resolve delay calculation.",
563
+ })
564
+ @Column({
565
+ type: ColumnType.Date,
566
+ nullable: true,
567
+ unique: false,
568
+ })
569
+ public allAlertsResolvedAt?: Date = undefined;
570
+
546
571
  @ColumnAccessControl({
547
572
  create: [
548
573
  Permission.ProjectOwner,
@@ -674,13 +674,13 @@ export default class AlertGroupingRule extends BaseModel {
674
674
  title: "Group By Monitor",
675
675
  description:
676
676
  "When enabled, alerts from different monitors will be grouped into separate episodes. When disabled, alerts from any monitor can be grouped together.",
677
- defaultValue: true,
677
+ defaultValue: false,
678
678
  isDefaultValueColumn: true,
679
679
  })
680
680
  @Column({
681
681
  type: ColumnType.Boolean,
682
682
  nullable: false,
683
- default: true,
683
+ default: false,
684
684
  })
685
685
  public groupByMonitor?: boolean = undefined;
686
686
 
@@ -542,6 +542,31 @@ export default class IncidentEpisode extends BaseModel {
542
542
  })
543
543
  public resolvedAt?: Date = undefined;
544
544
 
545
+ @ColumnAccessControl({
546
+ create: [],
547
+ read: [
548
+ Permission.ProjectOwner,
549
+ Permission.ProjectAdmin,
550
+ Permission.ProjectMember,
551
+ Permission.ReadIncidentEpisode,
552
+ Permission.ReadAllProjectResources,
553
+ ],
554
+ update: [],
555
+ })
556
+ @Index()
557
+ @TableColumn({
558
+ type: TableColumnType.Date,
559
+ title: "All Incidents Resolved At",
560
+ description:
561
+ "When all incidents in this episode were first detected as resolved. Used for resolve delay calculation.",
562
+ })
563
+ @Column({
564
+ type: ColumnType.Date,
565
+ nullable: true,
566
+ unique: false,
567
+ })
568
+ public allIncidentsResolvedAt?: Date = undefined;
569
+
545
570
  @ColumnAccessControl({
546
571
  create: [
547
572
  Permission.ProjectOwner,
@@ -678,13 +678,13 @@ export default class IncidentGroupingRule extends BaseModel {
678
678
  title: "Group By Monitor",
679
679
  description:
680
680
  "When enabled, incidents from different monitors will be grouped into separate episodes. When disabled, incidents from any monitor can be grouped together.",
681
- defaultValue: true,
681
+ defaultValue: false,
682
682
  isDefaultValueColumn: true,
683
683
  })
684
684
  @Column({
685
685
  type: ColumnType.Boolean,
686
686
  nullable: false,
687
- default: true,
687
+ default: false,
688
688
  })
689
689
  public groupByMonitor?: boolean = undefined;
690
690
 
@@ -98,6 +98,7 @@ import ProjectSmtpConfig from "./ProjectSmtpConfig";
98
98
  import ProjectSSO from "./ProjectSso";
99
99
  import PromoCode from "./PromoCode";
100
100
  import EnterpriseLicense from "./EnterpriseLicense";
101
+ import OpenSourceDeployment from "./OpenSourceDeployment";
101
102
  import Reseller from "./Reseller";
102
103
  import ResellerPlan from "./ResellerPlan";
103
104
  // ScheduledMaintenances
@@ -411,6 +412,7 @@ const AllModelTypes: Array<{
411
412
 
412
413
  PromoCode,
413
414
  EnterpriseLicense,
415
+ OpenSourceDeployment,
414
416
 
415
417
  GlobalConfig,
416
418
 
@@ -0,0 +1,140 @@
1
+ import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
2
+ import Route from "../../Types/API/Route";
3
+ import ColumnAccessControl from "../../Types/Database/AccessControl/ColumnAccessControl";
4
+ import TableAccessControl from "../../Types/Database/AccessControl/TableAccessControl";
5
+ import ColumnLength from "../../Types/Database/ColumnLength";
6
+ import ColumnType from "../../Types/Database/ColumnType";
7
+ import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
8
+ import TableColumn from "../../Types/Database/TableColumn";
9
+ import TableColumnType from "../../Types/Database/TableColumnType";
10
+ import TableMetadata from "../../Types/Database/TableMetadata";
11
+ import IconProp from "../../Types/Icon/IconProp";
12
+ import { Column, Entity } from "typeorm";
13
+
14
+ @TableAccessControl({
15
+ create: [],
16
+ read: [],
17
+ update: [],
18
+ delete: [],
19
+ })
20
+ @CrudApiEndpoint(new Route("/open-source-deployment"))
21
+ @TableMetadata({
22
+ tableName: "OpenSourceDeployment",
23
+ singularName: "Open Source Deployment",
24
+ pluralName: "Open Source Deployments",
25
+ icon: IconProp.Globe,
26
+ tableDescription:
27
+ "Open source deployment registrations from self-hosted instances.",
28
+ })
29
+ @Entity({
30
+ name: "OpenSourceDeployment",
31
+ })
32
+ export default class OpenSourceDeployment extends BaseModel {
33
+ @ColumnAccessControl({
34
+ create: [],
35
+ read: [],
36
+ update: [],
37
+ })
38
+ @TableColumn({
39
+ required: true,
40
+ type: TableColumnType.Email,
41
+ title: "Email",
42
+ description: "Email address of the user who registered.",
43
+ })
44
+ @Column({
45
+ nullable: false,
46
+ type: ColumnType.Email,
47
+ length: ColumnLength.Email,
48
+ })
49
+ public email?: string = undefined;
50
+
51
+ @ColumnAccessControl({
52
+ create: [],
53
+ read: [],
54
+ update: [],
55
+ })
56
+ @TableColumn({
57
+ required: true,
58
+ type: TableColumnType.ShortText,
59
+ title: "Name",
60
+ description: "Full name of the user who registered.",
61
+ })
62
+ @Column({
63
+ nullable: false,
64
+ type: ColumnType.ShortText,
65
+ length: ColumnLength.ShortText,
66
+ })
67
+ public name?: string = undefined;
68
+
69
+ @ColumnAccessControl({
70
+ create: [],
71
+ read: [],
72
+ update: [],
73
+ })
74
+ @TableColumn({
75
+ required: false,
76
+ type: TableColumnType.ShortText,
77
+ title: "Company Name",
78
+ description: "Company name of the user who registered.",
79
+ })
80
+ @Column({
81
+ nullable: true,
82
+ type: ColumnType.ShortText,
83
+ length: ColumnLength.ShortText,
84
+ })
85
+ public companyName?: string = undefined;
86
+
87
+ @ColumnAccessControl({
88
+ create: [],
89
+ read: [],
90
+ update: [],
91
+ })
92
+ @TableColumn({
93
+ required: false,
94
+ type: TableColumnType.Phone,
95
+ title: "Company Phone Number",
96
+ description: "Phone number of the user who registered.",
97
+ })
98
+ @Column({
99
+ nullable: true,
100
+ type: ColumnType.Phone,
101
+ length: ColumnLength.Phone,
102
+ })
103
+ public companyPhoneNumber?: string = undefined;
104
+
105
+ @ColumnAccessControl({
106
+ create: [],
107
+ read: [],
108
+ update: [],
109
+ })
110
+ @TableColumn({
111
+ required: true,
112
+ type: TableColumnType.ShortText,
113
+ title: "Version",
114
+ description: "OneUptime version of the self-hosted instance.",
115
+ })
116
+ @Column({
117
+ nullable: false,
118
+ type: ColumnType.ShortText,
119
+ length: ColumnLength.ShortText,
120
+ })
121
+ public oneuptimeVersion?: string = undefined;
122
+
123
+ @ColumnAccessControl({
124
+ create: [],
125
+ read: [],
126
+ update: [],
127
+ })
128
+ @TableColumn({
129
+ required: false,
130
+ type: TableColumnType.ShortText,
131
+ title: "Instance URL",
132
+ description: "URL of the self-hosted instance.",
133
+ })
134
+ @Column({
135
+ nullable: true,
136
+ type: ColumnType.ShortText,
137
+ length: ColumnLength.ShortText,
138
+ })
139
+ public instanceUrl?: string = undefined;
140
+ }
@@ -0,0 +1,73 @@
1
+ import OpenSourceDeployment from "../../Models/DatabaseModels/OpenSourceDeployment";
2
+ import { JSONObject } from "../../Types/JSON";
3
+ import URL from "../../Types/API/URL";
4
+ import API from "../../Utils/API";
5
+ import OpenSourceDeploymentService, {
6
+ Service as OpenSourceDeploymentServiceType,
7
+ } from "../Services/OpenSourceDeploymentService";
8
+ import { OpenSourceDeploymentWebhookUrl } from "../EnvironmentConfig";
9
+ import logger from "../Utils/Logger";
10
+ import Response from "../Utils/Response";
11
+ import {
12
+ ExpressRequest,
13
+ ExpressResponse,
14
+ NextFunction,
15
+ } from "../Utils/Express";
16
+ import BaseAPI from "./BaseAPI";
17
+
18
+ export default class OpenSourceDeploymentAPI extends BaseAPI<
19
+ OpenSourceDeployment,
20
+ OpenSourceDeploymentServiceType
21
+ > {
22
+ public constructor() {
23
+ super(OpenSourceDeployment, OpenSourceDeploymentService);
24
+
25
+ this.router.post(
26
+ `${new this.entityType().getCrudApiPath()?.toString()}/register`,
27
+ async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
28
+ try {
29
+ const body: JSONObject = req.body;
30
+
31
+ const deployment: OpenSourceDeployment = new OpenSourceDeployment();
32
+
33
+ deployment.email = (body["email"] as string) || "";
34
+ deployment.name = (body["name"] as string) || "";
35
+ deployment.companyName = (body["companyName"] as string) || "";
36
+ deployment.companyPhoneNumber =
37
+ (body["companyPhoneNumber"] as string) || "";
38
+ deployment.oneuptimeVersion =
39
+ (body["oneuptimeVersion"] as string) || "unknown";
40
+ deployment.instanceUrl = (body["instanceUrl"] as string) || "";
41
+
42
+ await OpenSourceDeploymentService.create({
43
+ data: deployment,
44
+ props: {
45
+ isRoot: true,
46
+ },
47
+ });
48
+
49
+ if (OpenSourceDeploymentWebhookUrl) {
50
+ API.post({
51
+ url: URL.fromString(OpenSourceDeploymentWebhookUrl),
52
+ data: {
53
+ email: deployment.email?.toString() || "",
54
+ name: deployment.name?.toString() || "",
55
+ companyName: deployment.companyName?.toString() || "",
56
+ companyPhoneNumber:
57
+ deployment.companyPhoneNumber?.toString() || "",
58
+ oneuptimeVersion: deployment.oneuptimeVersion?.toString() || "",
59
+ instanceUrl: deployment.instanceUrl?.toString() || "",
60
+ },
61
+ }).catch((err: Error) => {
62
+ logger.error(err);
63
+ });
64
+ }
65
+
66
+ return Response.sendEmptySuccessResponse(req, res);
67
+ } catch (err) {
68
+ next(err);
69
+ }
70
+ },
71
+ );
72
+ }
73
+ }
@@ -148,6 +148,9 @@ export const EncryptionSecret: ObjectID = new ObjectID(
148
148
  process.env["ENCRYPTION_SECRET"] || "secret",
149
149
  );
150
150
 
151
+ export const OpenSourceDeploymentWebhookUrl: string =
152
+ process.env["OPEN_SOURCE_DEPLOYMENT_WEBHOOK_URL"] || "";
153
+
151
154
  export const AirtableApiKey: string = process.env["AIRTABLE_API_KEY"] || "";
152
155
 
153
156
  export const AirtableBaseId: string = process.env["AIRTABLE_BASE_ID"] || "";
@@ -22,23 +22,23 @@ export class MigrationName1770237245070 implements MigrationInterface {
22
22
  );
23
23
 
24
24
  /*
25
- * Backfill counters from COUNT of each entity table (including soft-deleted rows)
26
- * Using JOIN-based updates instead of correlated subqueries for performance
25
+ * Backfill counters from MAX number of each entity table (per project)
26
+ * Using MAX instead of COUNT to correctly handle deleted rows
27
27
  */
28
28
  await queryRunner.query(
29
- `UPDATE "Project" SET "incidentCounter" = sub.cnt FROM (SELECT "projectId", COUNT(*) as cnt FROM "Incident" GROUP BY "projectId") sub WHERE "Project"."_id" = sub."projectId"`,
29
+ `UPDATE "Project" SET "incidentCounter" = sub.max_num FROM (SELECT "projectId", COALESCE(MAX("incidentNumber"), 0) as max_num FROM "Incident" GROUP BY "projectId") sub WHERE "Project"."_id" = sub."projectId"`,
30
30
  );
31
31
  await queryRunner.query(
32
- `UPDATE "Project" SET "alertCounter" = sub.cnt FROM (SELECT "projectId", COUNT(*) as cnt FROM "Alert" GROUP BY "projectId") sub WHERE "Project"."_id" = sub."projectId"`,
32
+ `UPDATE "Project" SET "alertCounter" = sub.max_num FROM (SELECT "projectId", COALESCE(MAX("alertNumber"), 0) as max_num FROM "Alert" GROUP BY "projectId") sub WHERE "Project"."_id" = sub."projectId"`,
33
33
  );
34
34
  await queryRunner.query(
35
- `UPDATE "Project" SET "scheduledMaintenanceCounter" = sub.cnt FROM (SELECT "projectId", COUNT(*) as cnt FROM "ScheduledMaintenance" GROUP BY "projectId") sub WHERE "Project"."_id" = sub."projectId"`,
35
+ `UPDATE "Project" SET "scheduledMaintenanceCounter" = sub.max_num FROM (SELECT "projectId", COALESCE(MAX("scheduledMaintenanceNumber"), 0) as max_num FROM "ScheduledMaintenance" GROUP BY "projectId") sub WHERE "Project"."_id" = sub."projectId"`,
36
36
  );
37
37
  await queryRunner.query(
38
- `UPDATE "Project" SET "incidentEpisodeCounter" = sub.cnt FROM (SELECT "projectId", COUNT(*) as cnt FROM "IncidentEpisode" GROUP BY "projectId") sub WHERE "Project"."_id" = sub."projectId"`,
38
+ `UPDATE "Project" SET "incidentEpisodeCounter" = sub.max_num FROM (SELECT "projectId", COALESCE(MAX("episodeNumber"), 0) as max_num FROM "IncidentEpisode" GROUP BY "projectId") sub WHERE "Project"."_id" = sub."projectId"`,
39
39
  );
40
40
  await queryRunner.query(
41
- `UPDATE "Project" SET "alertEpisodeCounter" = sub.cnt FROM (SELECT "projectId", COUNT(*) as cnt FROM "AlertEpisode" GROUP BY "projectId") sub WHERE "Project"."_id" = sub."projectId"`,
41
+ `UPDATE "Project" SET "alertEpisodeCounter" = sub.max_num FROM (SELECT "projectId", COALESCE(MAX("episodeNumber"), 0) as max_num FROM "AlertEpisode" GROUP BY "projectId") sub WHERE "Project"."_id" = sub."projectId"`,
42
42
  );
43
43
  }
44
44
 
@@ -0,0 +1,26 @@
1
+ import { MigrationInterface, QueryRunner } from "typeorm";
2
+ import CaptureSpan from "../../../Utils/Telemetry/CaptureSpan";
3
+
4
+ export class MigrationName1770668054908 implements MigrationInterface {
5
+ public name = "MigrationName1770668054908";
6
+
7
+ @CaptureSpan()
8
+ public async up(queryRunner: QueryRunner): Promise<void> {
9
+ await queryRunner.query(
10
+ `ALTER TABLE "IncidentGroupingRule" ALTER COLUMN "groupByMonitor" SET DEFAULT false`,
11
+ );
12
+ await queryRunner.query(
13
+ `ALTER TABLE "AlertGroupingRule" ALTER COLUMN "groupByMonitor" SET DEFAULT false`,
14
+ );
15
+ }
16
+
17
+ @CaptureSpan()
18
+ public async down(queryRunner: QueryRunner): Promise<void> {
19
+ await queryRunner.query(
20
+ `ALTER TABLE "IncidentGroupingRule" ALTER COLUMN "groupByMonitor" SET DEFAULT true`,
21
+ );
22
+ await queryRunner.query(
23
+ `ALTER TABLE "AlertGroupingRule" ALTER COLUMN "groupByMonitor" SET DEFAULT true`,
24
+ );
25
+ }
26
+ }
@@ -0,0 +1,47 @@
1
+ import { MigrationInterface, QueryRunner } from "typeorm";
2
+
3
+ export class MigrationName1770728946893 implements MigrationInterface {
4
+ public name = "MigrationName1770728946893";
5
+
6
+ public async up(queryRunner: QueryRunner): Promise<void> {
7
+ await queryRunner.query(
8
+ `ALTER TABLE "IncidentEpisode" ADD "allIncidentsResolvedAt" TIMESTAMP WITH TIME ZONE`,
9
+ );
10
+ await queryRunner.query(
11
+ `ALTER TABLE "AlertEpisode" ADD "allAlertsResolvedAt" TIMESTAMP WITH TIME ZONE`,
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
+ await queryRunner.query(
20
+ `CREATE INDEX "IDX_0610406e5c436c20a5068b1006" ON "IncidentEpisode" ("allIncidentsResolvedAt") `,
21
+ );
22
+ await queryRunner.query(
23
+ `CREATE INDEX "IDX_ea5d1f899fe52445dd6e0d0d55" ON "AlertEpisode" ("allAlertsResolvedAt") `,
24
+ );
25
+ }
26
+
27
+ public async down(queryRunner: QueryRunner): Promise<void> {
28
+ await queryRunner.query(
29
+ `DROP INDEX "public"."IDX_ea5d1f899fe52445dd6e0d0d55"`,
30
+ );
31
+ await queryRunner.query(
32
+ `DROP INDEX "public"."IDX_0610406e5c436c20a5068b1006"`,
33
+ );
34
+ await queryRunner.query(
35
+ `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type": "RestrictionTimes", "value": {"restictionType": "None", "dayRestrictionTimes": null, "weeklyRestrictionTimes": []}}'`,
36
+ );
37
+ await queryRunner.query(
38
+ `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type": "Recurring", "value": {"intervalType": "Day", "intervalCount": {"_type": "PositiveNumber", "value": 1}}}'`,
39
+ );
40
+ await queryRunner.query(
41
+ `ALTER TABLE "AlertEpisode" DROP COLUMN "allAlertsResolvedAt"`,
42
+ );
43
+ await queryRunner.query(
44
+ `ALTER TABLE "IncidentEpisode" DROP COLUMN "allIncidentsResolvedAt"`,
45
+ );
46
+ }
47
+ }
@@ -0,0 +1,27 @@
1
+ import { MigrationInterface, QueryRunner } from "typeorm";
2
+
3
+ export class MigrationName1770732721195 implements MigrationInterface {
4
+ public name = "MigrationName1770732721195";
5
+
6
+ public async up(queryRunner: QueryRunner): Promise<void> {
7
+ await queryRunner.query(
8
+ `CREATE TABLE "OpenSourceDeployment" ("_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, "email" character varying(100) NOT NULL, "name" character varying(100) NOT NULL, "companyName" character varying(100), "companyPhoneNumber" character varying(30), "oneuptimeVersion" character varying(100) NOT NULL, "instanceUrl" character varying(100), CONSTRAINT "PK_cf6728c16db1c0c1b89f8ffc6dd" PRIMARY KEY ("_id"))`,
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
+ }
17
+
18
+ public async down(queryRunner: QueryRunner): Promise<void> {
19
+ await queryRunner.query(
20
+ `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type": "RestrictionTimes", "value": {"restictionType": "None", "dayRestrictionTimes": null, "weeklyRestrictionTimes": []}}'`,
21
+ );
22
+ await queryRunner.query(
23
+ `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type": "Recurring", "value": {"intervalType": "Day", "intervalCount": {"_type": "PositiveNumber", "value": 1}}}'`,
24
+ );
25
+ await queryRunner.query(`DROP TABLE "OpenSourceDeployment"`);
26
+ }
27
+ }
@@ -253,6 +253,9 @@ import { MigrationName1770232207959 } from "./1770232207959-MigrationName";
253
253
  import { MigrationName1770237245069 } from "./1770237245069-MigrationName";
254
254
  import { MigrationName1770237245070 } from "./1770237245070-MigrationName";
255
255
  import { MigrationName1770407024682 } from "./1770407024682-MigrationName";
256
+ import { MigrationName1770668054908 } from "./1770668054908-MigrationName";
257
+ import { MigrationName1770728946893 } from "./1770728946893-MigrationName";
258
+ import { MigrationName1770732721195 } from "./1770732721195-MigrationName";
256
259
 
257
260
  export default [
258
261
  InitialMigration,
@@ -510,4 +513,7 @@ export default [
510
513
  MigrationName1770237245069,
511
514
  MigrationName1770237245070,
512
515
  MigrationName1770407024682,
516
+ MigrationName1770668054908,
517
+ MigrationName1770728946893,
518
+ MigrationName1770732721195,
513
519
  ];
@@ -94,26 +94,16 @@ export class Service extends DatabaseService<Model> {
94
94
  });
95
95
 
96
96
  // Update episode's alertCount and lastAlertAddedAt
97
- Promise.resolve()
98
- .then(async () => {
99
- try {
100
- await AlertEpisodeService.updateAlertCount(
101
- createdItem.alertEpisodeId!,
102
- );
103
- await AlertEpisodeService.updateLastAlertAddedAt(
104
- createdItem.alertEpisodeId!,
105
- );
106
- } catch (error) {
107
- logger.error(
108
- `Error updating episode counts in AlertEpisodeMemberService.onCreateSuccess: ${error}`,
109
- );
110
- }
111
- })
112
- .catch((error: Error) => {
113
- logger.error(
114
- `Critical error in AlertEpisodeMemberService.onCreateSuccess: ${error}`,
115
- );
116
- });
97
+ try {
98
+ await AlertEpisodeService.updateAlertCount(createdItem.alertEpisodeId!);
99
+ await AlertEpisodeService.updateLastAlertAddedAt(
100
+ createdItem.alertEpisodeId!,
101
+ );
102
+ } catch (error) {
103
+ logger.error(
104
+ `Error updating episode counts in AlertEpisodeMemberService.onCreateSuccess: ${error}`,
105
+ );
106
+ }
117
107
 
118
108
  // Get alert details for feed
119
109
  const alert: Alert | null = await AlertService.findOneById({
@@ -815,11 +815,12 @@ export class Service extends DatabaseService<Model> {
815
815
  },
816
816
  });
817
817
 
818
- // Clear resolved timestamp
818
+ // Clear resolved timestamp and allAlertsResolvedAt
819
819
  await this.updateOneById({
820
820
  id: episodeId,
821
821
  data: {
822
- resolvedAt: undefined as any,
822
+ resolvedAt: null,
823
+ allAlertsResolvedAt: null,
823
824
  },
824
825
  props: {
825
826
  isRoot: true,
@@ -688,6 +688,7 @@ class AlertGroupingEngineServiceClass {
688
688
  newEpisode.alertGroupingRuleId = rule.id!;
689
689
  newEpisode.groupingKey = groupingKey;
690
690
  newEpisode.isManuallyCreated = false;
691
+ newEpisode.lastAlertAddedAt = OneUptimeDate.getCurrentDate();
691
692
 
692
693
  // Set severity from alert
693
694
  if (alert.alertSeverityId) {
@@ -96,26 +96,18 @@ export class Service extends DatabaseService<Model> {
96
96
  });
97
97
 
98
98
  // Update episode's incidentCount and lastIncidentAddedAt
99
- Promise.resolve()
100
- .then(async () => {
101
- try {
102
- await IncidentEpisodeService.updateIncidentCount(
103
- createdItem.incidentEpisodeId!,
104
- );
105
- await IncidentEpisodeService.updateLastIncidentAddedAt(
106
- createdItem.incidentEpisodeId!,
107
- );
108
- } catch (error) {
109
- logger.error(
110
- `Error updating episode counts in IncidentEpisodeMemberService.onCreateSuccess: ${error}`,
111
- );
112
- }
113
- })
114
- .catch((error: Error) => {
115
- logger.error(
116
- `Critical error in IncidentEpisodeMemberService.onCreateSuccess: ${error}`,
117
- );
118
- });
99
+ try {
100
+ await IncidentEpisodeService.updateIncidentCount(
101
+ createdItem.incidentEpisodeId!,
102
+ );
103
+ await IncidentEpisodeService.updateLastIncidentAddedAt(
104
+ createdItem.incidentEpisodeId!,
105
+ );
106
+ } catch (error) {
107
+ logger.error(
108
+ `Error updating episode counts in IncidentEpisodeMemberService.onCreateSuccess: ${error}`,
109
+ );
110
+ }
119
111
 
120
112
  // Get incident details for feed
121
113
  const incident: Incident | null = await IncidentService.findOneById({