@oneuptime/common 7.0.3827 → 7.0.3840

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 (74) hide show
  1. package/Models/DatabaseModels/GlobalConfig.ts +22 -4
  2. package/Models/DatabaseModels/Index.ts +2 -0
  3. package/Models/DatabaseModels/OnCallDutyPolicyExecutionLogTimeline.ts +48 -0
  4. package/Models/DatabaseModels/OnCallDutyPolicyUserOverride.ts +502 -0
  5. package/Models/DatabaseModels/ProjectCallSMSConfig.ts +35 -4
  6. package/Models/DatabaseModels/UserOnCallLog.ts +43 -0
  7. package/Server/Infrastructure/Postgres/SchemaMigrations/1741904597606-MigrationName.ts +69 -0
  8. package/Server/Infrastructure/Postgres/SchemaMigrations/1741908200702-MigrationName.ts +29 -0
  9. package/Server/Infrastructure/Postgres/SchemaMigrations/1741955609393-MigrationName.ts +35 -0
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1741955752685-MigrationName.ts +23 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/1741957080431-MigrationName.ts +23 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1741959216297-MigrationName.ts +23 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
  14. package/Server/Services/CallService.ts +4 -1
  15. package/Server/Services/Index.ts +2 -0
  16. package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +90 -3
  17. package/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.ts +17 -1
  18. package/Server/Services/OnCallDutyPolicyUserOverrideService.ts +77 -0
  19. package/Server/Services/ProjectCallSMSConfigService.ts +12 -2
  20. package/Server/Services/SmsService.ts +4 -1
  21. package/Server/Services/StatusPageSubscriberService.ts +4 -2
  22. package/Server/Services/UserNotificationRuleService.ts +5 -0
  23. package/Types/CallAndSMS/TwilioConfig.ts +2 -1
  24. package/Types/Permission.ts +38 -0
  25. package/Types/Phone.ts +61 -1
  26. package/build/dist/Models/DatabaseModels/GlobalConfig.js +25 -5
  27. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  28. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  29. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  30. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLogTimeline.js +49 -0
  31. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLogTimeline.js.map +1 -1
  32. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyUserOverride.js +516 -0
  33. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyUserOverride.js.map +1 -0
  34. package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js +38 -5
  35. package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js.map +1 -1
  36. package/build/dist/Models/DatabaseModels/UserOnCallLog.js +44 -0
  37. package/build/dist/Models/DatabaseModels/UserOnCallLog.js.map +1 -1
  38. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1741904597606-MigrationName.js +30 -0
  39. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1741904597606-MigrationName.js.map +1 -0
  40. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1741908200702-MigrationName.js +16 -0
  41. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1741908200702-MigrationName.js.map +1 -0
  42. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1741955609393-MigrationName.js +18 -0
  43. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1741955609393-MigrationName.js.map +1 -0
  44. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1741955752685-MigrationName.js +14 -0
  45. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1741955752685-MigrationName.js.map +1 -0
  46. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1741957080431-MigrationName.js +14 -0
  47. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1741957080431-MigrationName.js.map +1 -0
  48. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1741959216297-MigrationName.js +14 -0
  49. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1741959216297-MigrationName.js.map +1 -0
  50. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -0
  51. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  52. package/build/dist/Server/Services/CallService.js +3 -2
  53. package/build/dist/Server/Services/CallService.js.map +1 -1
  54. package/build/dist/Server/Services/Index.js +2 -0
  55. package/build/dist/Server/Services/Index.js.map +1 -1
  56. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +59 -3
  57. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
  58. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js +14 -1
  59. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js.map +1 -1
  60. package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js +49 -0
  61. package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js.map +1 -0
  62. package/build/dist/Server/Services/ProjectCallSMSConfigService.js +11 -2
  63. package/build/dist/Server/Services/ProjectCallSMSConfigService.js.map +1 -1
  64. package/build/dist/Server/Services/SmsService.js +3 -2
  65. package/build/dist/Server/Services/SmsService.js.map +1 -1
  66. package/build/dist/Server/Services/StatusPageSubscriberService.js +4 -2
  67. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  68. package/build/dist/Server/Services/UserNotificationRuleService.js +3 -0
  69. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  70. package/build/dist/Types/Permission.js +32 -0
  71. package/build/dist/Types/Permission.js.map +1 -1
  72. package/build/dist/Types/Phone.js +32 -0
  73. package/build/dist/Types/Phone.js.map +1 -1
  74. package/package.json +2 -2
@@ -0,0 +1,69 @@
1
+ import { MigrationInterface, QueryRunner } from "typeorm";
2
+
3
+ export class MigrationName1741904597606 implements MigrationInterface {
4
+ public name = "MigrationName1741904597606";
5
+
6
+ public async up(queryRunner: QueryRunner): Promise<void> {
7
+ await queryRunner.query(
8
+ `CREATE TABLE "OnCallDutyPolicyUserOverride" ("_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, "onCallDutyPolicyId" uuid, "name" character varying(100) NOT NULL, "description" character varying(500), "createdByUserId" uuid, "overrideUserId" uuid NOT NULL, "routeAlertsToUserId" uuid NOT NULL, "startsAt" TIMESTAMP WITH TIME ZONE NOT NULL, "endsAt" TIMESTAMP WITH TIME ZONE NOT NULL, "deletedByUserId" uuid, CONSTRAINT "PK_41b216c8e71d15182fe67b75fec" PRIMARY KEY ("_id"))`,
9
+ );
10
+ await queryRunner.query(
11
+ `CREATE INDEX "IDX_75b4e0f9bc53be5cdaa55b2936" ON "OnCallDutyPolicyUserOverride" ("projectId") `,
12
+ );
13
+ await queryRunner.query(
14
+ `CREATE INDEX "IDX_6a13404edca5d177b3ad539995" ON "OnCallDutyPolicyUserOverride" ("onCallDutyPolicyId") `,
15
+ );
16
+ await queryRunner.query(
17
+ `CREATE INDEX "IDX_b0bdac6c10d7ed30e696aded2c" ON "OnCallDutyPolicyUserOverride" ("name") `,
18
+ );
19
+ await queryRunner.query(
20
+ `ALTER TABLE "OnCallDutyPolicyUserOverride" ADD CONSTRAINT "FK_75b4e0f9bc53be5cdaa55b29361" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
21
+ );
22
+ await queryRunner.query(
23
+ `ALTER TABLE "OnCallDutyPolicyUserOverride" ADD CONSTRAINT "FK_6a13404edca5d177b3ad539995d" FOREIGN KEY ("onCallDutyPolicyId") REFERENCES "OnCallDutyPolicy"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
24
+ );
25
+ await queryRunner.query(
26
+ `ALTER TABLE "OnCallDutyPolicyUserOverride" ADD CONSTRAINT "FK_c21b1df32e40e739a66d638a3c7" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
27
+ );
28
+ await queryRunner.query(
29
+ `ALTER TABLE "OnCallDutyPolicyUserOverride" ADD CONSTRAINT "FK_4b3f696aaaf327b245ebeb3d146" FOREIGN KEY ("overrideUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
30
+ );
31
+ await queryRunner.query(
32
+ `ALTER TABLE "OnCallDutyPolicyUserOverride" ADD CONSTRAINT "FK_8771068ec4c16763a7ff796895d" FOREIGN KEY ("routeAlertsToUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
33
+ );
34
+ await queryRunner.query(
35
+ `ALTER TABLE "OnCallDutyPolicyUserOverride" ADD CONSTRAINT "FK_810a8cd7f838a8e141fd750a9d5" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
36
+ );
37
+ }
38
+
39
+ public async down(queryRunner: QueryRunner): Promise<void> {
40
+ await queryRunner.query(
41
+ `ALTER TABLE "OnCallDutyPolicyUserOverride" DROP CONSTRAINT "FK_810a8cd7f838a8e141fd750a9d5"`,
42
+ );
43
+ await queryRunner.query(
44
+ `ALTER TABLE "OnCallDutyPolicyUserOverride" DROP CONSTRAINT "FK_8771068ec4c16763a7ff796895d"`,
45
+ );
46
+ await queryRunner.query(
47
+ `ALTER TABLE "OnCallDutyPolicyUserOverride" DROP CONSTRAINT "FK_4b3f696aaaf327b245ebeb3d146"`,
48
+ );
49
+ await queryRunner.query(
50
+ `ALTER TABLE "OnCallDutyPolicyUserOverride" DROP CONSTRAINT "FK_c21b1df32e40e739a66d638a3c7"`,
51
+ );
52
+ await queryRunner.query(
53
+ `ALTER TABLE "OnCallDutyPolicyUserOverride" DROP CONSTRAINT "FK_6a13404edca5d177b3ad539995d"`,
54
+ );
55
+ await queryRunner.query(
56
+ `ALTER TABLE "OnCallDutyPolicyUserOverride" DROP CONSTRAINT "FK_75b4e0f9bc53be5cdaa55b29361"`,
57
+ );
58
+ await queryRunner.query(
59
+ `DROP INDEX "public"."IDX_b0bdac6c10d7ed30e696aded2c"`,
60
+ );
61
+ await queryRunner.query(
62
+ `DROP INDEX "public"."IDX_6a13404edca5d177b3ad539995"`,
63
+ );
64
+ await queryRunner.query(
65
+ `DROP INDEX "public"."IDX_75b4e0f9bc53be5cdaa55b2936"`,
66
+ );
67
+ await queryRunner.query(`DROP TABLE "OnCallDutyPolicyUserOverride"`);
68
+ }
69
+ }
@@ -0,0 +1,29 @@
1
+ import { MigrationInterface, QueryRunner } from "typeorm";
2
+
3
+ export class MigrationName1741908200702 implements MigrationInterface {
4
+ public name = "MigrationName1741908200702";
5
+
6
+ public async up(queryRunner: QueryRunner): Promise<void> {
7
+ await queryRunner.query(
8
+ `DROP INDEX "public"."IDX_b0bdac6c10d7ed30e696aded2c"`,
9
+ );
10
+ await queryRunner.query(
11
+ `ALTER TABLE "OnCallDutyPolicyUserOverride" DROP COLUMN "name"`,
12
+ );
13
+ await queryRunner.query(
14
+ `ALTER TABLE "OnCallDutyPolicyUserOverride" DROP COLUMN "description"`,
15
+ );
16
+ }
17
+
18
+ public async down(queryRunner: QueryRunner): Promise<void> {
19
+ await queryRunner.query(
20
+ `ALTER TABLE "OnCallDutyPolicyUserOverride" ADD "description" character varying(500)`,
21
+ );
22
+ await queryRunner.query(
23
+ `ALTER TABLE "OnCallDutyPolicyUserOverride" ADD "name" character varying(100) NOT NULL`,
24
+ );
25
+ await queryRunner.query(
26
+ `CREATE INDEX "IDX_b0bdac6c10d7ed30e696aded2c" ON "OnCallDutyPolicyUserOverride" ("name") `,
27
+ );
28
+ }
29
+ }
@@ -0,0 +1,35 @@
1
+ import { MigrationInterface, QueryRunner } from "typeorm";
2
+
3
+ export class MigrationName1741955609393 implements MigrationInterface {
4
+ public name = "MigrationName1741955609393";
5
+
6
+ public async up(queryRunner: QueryRunner): Promise<void> {
7
+ await queryRunner.query(
8
+ `ALTER TABLE "GlobalConfig" RENAME COLUMN "twilioPhoneNumber" TO "twilioPrimaryPhoneNumber"`,
9
+ );
10
+ await queryRunner.query(
11
+ `ALTER TABLE "ProjectCallSMSConfig" RENAME COLUMN "twilioPhoneNumber" TO "twilioPrimaryPhoneNumber"`,
12
+ );
13
+ await queryRunner.query(
14
+ `ALTER TABLE "GlobalConfig" ADD "twilioSecondaryPhoneNumbers" character varying(500)`,
15
+ );
16
+ await queryRunner.query(
17
+ `ALTER TABLE "ProjectCallSMSConfig" ADD "twilioSecondaryPhoneNumbers" character varying(500)`,
18
+ );
19
+ }
20
+
21
+ public async down(queryRunner: QueryRunner): Promise<void> {
22
+ await queryRunner.query(
23
+ `ALTER TABLE "ProjectCallSMSConfig" DROP COLUMN "twilioSecondaryPhoneNumbers"`,
24
+ );
25
+ await queryRunner.query(
26
+ `ALTER TABLE "GlobalConfig" DROP COLUMN "twilioSecondaryPhoneNumbers"`,
27
+ );
28
+ await queryRunner.query(
29
+ `ALTER TABLE "ProjectCallSMSConfig" RENAME COLUMN "twilioPrimaryPhoneNumber" TO "twilioPhoneNumber"`,
30
+ );
31
+ await queryRunner.query(
32
+ `ALTER TABLE "GlobalConfig" RENAME COLUMN "twilioPrimaryPhoneNumber" TO "twilioPhoneNumber"`,
33
+ );
34
+ }
35
+ }
@@ -0,0 +1,23 @@
1
+ import { MigrationInterface, QueryRunner } from "typeorm";
2
+
3
+ export class MigrationName1741955752685 implements MigrationInterface {
4
+ public name = "MigrationName1741955752685";
5
+
6
+ public async up(queryRunner: QueryRunner): Promise<void> {
7
+ await queryRunner.query(
8
+ `ALTER TABLE "GlobalConfig" DROP CONSTRAINT "UQ_c223b66a0ca2fa8095cb7a6c7cc"`,
9
+ );
10
+ await queryRunner.query(
11
+ `ALTER TABLE "ProjectCallSMSConfig" DROP CONSTRAINT "UQ_50235223d7fd7b0c27063bfb08e"`,
12
+ );
13
+ }
14
+
15
+ public async down(queryRunner: QueryRunner): Promise<void> {
16
+ await queryRunner.query(
17
+ `ALTER TABLE "ProjectCallSMSConfig" ADD CONSTRAINT "UQ_50235223d7fd7b0c27063bfb08e" UNIQUE ("twilioPrimaryPhoneNumber")`,
18
+ );
19
+ await queryRunner.query(
20
+ `ALTER TABLE "GlobalConfig" ADD CONSTRAINT "UQ_c223b66a0ca2fa8095cb7a6c7cc" UNIQUE ("twilioPrimaryPhoneNumber")`,
21
+ );
22
+ }
23
+ }
@@ -0,0 +1,23 @@
1
+ import { MigrationInterface, QueryRunner } from "typeorm";
2
+
3
+ export class MigrationName1741957080431 implements MigrationInterface {
4
+ public name = "MigrationName1741957080431";
5
+
6
+ public async up(queryRunner: QueryRunner): Promise<void> {
7
+ await queryRunner.query(
8
+ `ALTER TABLE "UserOnCallLog" ADD "overridedByUserId" uuid`,
9
+ );
10
+ await queryRunner.query(
11
+ `ALTER TABLE "UserOnCallLog" ADD CONSTRAINT "FK_702b8c74c8f0d7fb220bc407776" FOREIGN KEY ("overridedByUserId") REFERENCES "User"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
12
+ );
13
+ }
14
+
15
+ public async down(queryRunner: QueryRunner): Promise<void> {
16
+ await queryRunner.query(
17
+ `ALTER TABLE "UserOnCallLog" DROP CONSTRAINT "FK_702b8c74c8f0d7fb220bc407776"`,
18
+ );
19
+ await queryRunner.query(
20
+ `ALTER TABLE "UserOnCallLog" DROP COLUMN "overridedByUserId"`,
21
+ );
22
+ }
23
+ }
@@ -0,0 +1,23 @@
1
+ import { MigrationInterface, QueryRunner } from "typeorm";
2
+
3
+ export class MigrationName1741959216297 implements MigrationInterface {
4
+ public name = "MigrationName1741959216297";
5
+
6
+ public async up(queryRunner: QueryRunner): Promise<void> {
7
+ await queryRunner.query(
8
+ `ALTER TABLE "OnCallDutyPolicyExecutionLogTimeline" ADD "overridedByUserId" uuid`,
9
+ );
10
+ await queryRunner.query(
11
+ `ALTER TABLE "OnCallDutyPolicyExecutionLogTimeline" ADD CONSTRAINT "FK_356ab0badd7e70f4d25045dcbf3" FOREIGN KEY ("overridedByUserId") REFERENCES "User"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
12
+ );
13
+ }
14
+
15
+ public async down(queryRunner: QueryRunner): Promise<void> {
16
+ await queryRunner.query(
17
+ `ALTER TABLE "OnCallDutyPolicyExecutionLogTimeline" DROP CONSTRAINT "FK_356ab0badd7e70f4d25045dcbf3"`,
18
+ );
19
+ await queryRunner.query(
20
+ `ALTER TABLE "OnCallDutyPolicyExecutionLogTimeline" DROP COLUMN "overridedByUserId"`,
21
+ );
22
+ }
23
+ }
@@ -112,6 +112,12 @@ import { MigrationName1740597525803 } from "./1740597525803-MigrationName";
112
112
  import { MigrationName1740598793630 } from "./1740598793630-MigrationName";
113
113
  import { MigrationName1741031019972 } from "./1741031019972-MigrationName";
114
114
  import { MigrationName1741209339971 } from "./1741209339971-MigrationName";
115
+ import { MigrationName1741904597606 } from "./1741904597606-MigrationName";
116
+ import { MigrationName1741908200702 } from "./1741908200702-MigrationName";
117
+ import { MigrationName1741955609393 } from "./1741955609393-MigrationName";
118
+ import { MigrationName1741955752685 } from "./1741955752685-MigrationName";
119
+ import { MigrationName1741957080431 } from "./1741957080431-MigrationName";
120
+ import { MigrationName1741959216297 } from "./1741959216297-MigrationName";
115
121
 
116
122
  export default [
117
123
  InitialMigration,
@@ -228,4 +234,10 @@ export default [
228
234
  MigrationName1740598793630,
229
235
  MigrationName1741031019972,
230
236
  MigrationName1741209339971,
237
+ MigrationName1741904597606,
238
+ MigrationName1741908200702,
239
+ MigrationName1741955609393,
240
+ MigrationName1741955752685,
241
+ MigrationName1741957080431,
242
+ MigrationName1741959216297,
231
243
  ];
@@ -35,7 +35,10 @@ export class CallService extends BaseService {
35
35
  ? {
36
36
  accountSid: options.customTwilioConfig.accountSid!,
37
37
  authToken: options.customTwilioConfig.authToken!,
38
- phoneNumber: options.customTwilioConfig.phoneNumber.toString(),
38
+ primaryPhoneNumber:
39
+ options.customTwilioConfig.primaryPhoneNumber.toString(),
40
+ secondaryPhoneNumbers:
41
+ options.customTwilioConfig.secondaryPhoneNumbers?.toString(),
39
42
  }
40
43
  : undefined,
41
44
  };
@@ -156,6 +156,7 @@ import WorkspaceProjectAuthTokenService from "./WorkspaceProjectAuthTokenService
156
156
  import WorkspaceUserAuthTokenService from "./WorkspaceUserAuthTokenService";
157
157
  import WorkspaceSettingService from "./WorkspaceSettingService";
158
158
  import WorkspaceNotificationRuleService from "./WorkspaceNotificationRuleService";
159
+ import OnCallDutyPolicyUserOverrideService from "./OnCallDutyPolicyUserOverrideService";
159
160
 
160
161
  const services: Array<BaseService> = [
161
162
  AcmeCertificateService,
@@ -218,6 +219,7 @@ const services: Array<BaseService> = [
218
219
  OnCallDutyPolicyExecutionLogService,
219
220
  OnCallDutyPolicyExecutionLogTimelineService,
220
221
  OnCallDutyPolicyService,
222
+ OnCallDutyPolicyUserOverrideService,
221
223
 
222
224
  ProjectService,
223
225
  ProjectSmtpConfigService,
@@ -31,8 +31,76 @@ import OnCallDutyPolicyEscalationRuleUser from "Common/Models/DatabaseModels/OnC
31
31
  import OnCallDutyPolicyExecutionLogTimeline from "Common/Models/DatabaseModels/OnCallDutyPolicyExecutionLogTimeline";
32
32
  import User from "Common/Models/DatabaseModels/User";
33
33
  import logger from "../Utils/Logger";
34
+ import OnCallDutyPolicyUserOverride from "../../Models/DatabaseModels/OnCallDutyPolicyUserOverride";
35
+ import OnCallDutyPolicyUserOverrideService from "./OnCallDutyPolicyUserOverrideService";
34
36
 
35
37
  export class Service extends DatabaseService<Model> {
38
+ public async getRouteAlertToUserId(data: {
39
+ userId: ObjectID;
40
+ onCallDutyPolicyId: ObjectID;
41
+ projectId: ObjectID;
42
+ }): Promise<ObjectID | null> {
43
+ logger.debug(
44
+ `Getting route alert to user id for userId: ${data.userId.toString()}`,
45
+ );
46
+
47
+ const currentDate: Date = OneUptimeDate.getCurrentDate();
48
+
49
+ const alertRoutedTo: Array<OnCallDutyPolicyUserOverride> =
50
+ await OnCallDutyPolicyUserOverrideService.findBy({
51
+ query: {
52
+ overrideUserId: data.userId,
53
+ onCallDutyPolicyId: QueryHelper.equalToOrNull(
54
+ data.onCallDutyPolicyId,
55
+ ), // find global overrides as well. If this is null, then it will find global overrides.
56
+ projectId: data.projectId,
57
+ startsAt: QueryHelper.lessThanEqualTo(currentDate),
58
+ endsAt: QueryHelper.greaterThanEqualTo(currentDate),
59
+ },
60
+ props: {
61
+ isRoot: true,
62
+ },
63
+ limit: LIMIT_PER_PROJECT,
64
+ skip: 0,
65
+ select: {
66
+ routeAlertsToUserId: true,
67
+ onCallDutyPolicyId: true,
68
+ },
69
+ });
70
+
71
+ logger.debug(`Found alert routed to: ${JSON.stringify(alertRoutedTo)}`);
72
+
73
+ // local override takes precedence over global override.
74
+ const localOverride: OnCallDutyPolicyUserOverride | undefined =
75
+ alertRoutedTo.find((item: OnCallDutyPolicyUserOverride) => {
76
+ return (
77
+ item.onCallDutyPolicyId?.toString() ===
78
+ data.onCallDutyPolicyId.toString()
79
+ );
80
+ });
81
+
82
+ if (localOverride && localOverride.routeAlertsToUserId) {
83
+ logger.debug(
84
+ `Route alert to user id found: ${localOverride.routeAlertsToUserId.toString()}`,
85
+ );
86
+ return localOverride.routeAlertsToUserId;
87
+ }
88
+
89
+ const globalOverride: OnCallDutyPolicyUserOverride | undefined =
90
+ alertRoutedTo.find((item: OnCallDutyPolicyUserOverride) => {
91
+ return !item.onCallDutyPolicyId;
92
+ });
93
+
94
+ if (globalOverride && globalOverride.routeAlertsToUserId) {
95
+ logger.debug(
96
+ `Route alert to user id found: ${globalOverride.routeAlertsToUserId.toString()}`,
97
+ );
98
+ return globalOverride.routeAlertsToUserId;
99
+ }
100
+
101
+ return null;
102
+ }
103
+
36
104
  public async startRuleExecution(
37
105
  ruleId: ObjectID,
38
106
  options: {
@@ -190,13 +258,31 @@ export class Service extends DatabaseService<Model> {
190
258
  ): Promise<void> => {
191
259
  // This is where user is notified.
192
260
 
261
+ // get route alert to user id.
262
+ let routeAlertToUserId: ObjectID | null = null;
263
+
264
+ if (options.onCallPolicyId) {
265
+ routeAlertToUserId = await this.getRouteAlertToUserId({
266
+ userId,
267
+ onCallDutyPolicyId: options.onCallPolicyId,
268
+ projectId: options.projectId,
269
+ });
270
+ }
271
+
272
+ const alertSentToUserId: ObjectID = routeAlertToUserId || userId;
273
+
193
274
  logger.debug(
194
- `Starting notification rule execution for userId: ${userId.toString()}`,
275
+ `Starting notification rule execution for userId: ${alertSentToUserId.toString()}`,
195
276
  );
196
277
  let log: OnCallDutyPolicyExecutionLogTimeline = getNewLog();
197
278
  log.statusMessage = "Sending notification to user.";
198
279
  log.status = OnCallDutyExecutionLogTimelineStatus.Executing;
199
- log.alertSentToUserId = userId;
280
+ log.alertSentToUserId = alertSentToUserId;
281
+
282
+ if (routeAlertToUserId) {
283
+ log.overridedByUserId = userId;
284
+ }
285
+
200
286
  if (teamId) {
201
287
  log.userBelongsToTeamId = teamId;
202
288
  }
@@ -213,7 +299,7 @@ export class Service extends DatabaseService<Model> {
213
299
  });
214
300
 
215
301
  await UserNotificationRuleService.startUserNotificationRulesExecution(
216
- userId,
302
+ alertSentToUserId,
217
303
  {
218
304
  userNotificationEventType: options.userNotificationEventType!,
219
305
  triggeredByIncidentId: options.triggeredByIncidentId || undefined,
@@ -225,6 +311,7 @@ export class Service extends DatabaseService<Model> {
225
311
  onCallDutyPolicyExecutionLogTimelineId: log.id!,
226
312
  projectId: options.projectId,
227
313
  onCallScheduleId: scheduleId || undefined,
314
+ overridedByUserId: routeAlertToUserId ? userId : undefined,
228
315
  },
229
316
  );
230
317
  };
@@ -104,6 +104,10 @@ export class Service extends DatabaseService<Model> {
104
104
  name: true,
105
105
  _id: true,
106
106
  },
107
+ overridedByUser: {
108
+ name: true,
109
+ _id: true,
110
+ },
107
111
  },
108
112
  props: {
109
113
  isRoot: true,
@@ -166,7 +170,7 @@ export class Service extends DatabaseService<Model> {
166
170
  incidentOrAlertLink = `[Alert ${alertNumber}](${(await AlertService.getAlertLinkInDashboard(onCallDutyPolicyExecutionLogTimeline.projectId!, onCallDutyPolicyExecutionLogTimeline.triggeredByAlertId)).toString()})`;
167
171
  }
168
172
 
169
- const feedInfoInMarkdown: string = `**${this.getEmojiBasedOnStatus(status)} ${incidentOrAlertLink} On-Call Alert ${status} to ${await UserService.getUserMarkdownString(
173
+ let feedInfoInMarkdown: string = `**${this.getEmojiBasedOnStatus(status)} ${incidentOrAlertLink} On-Call Alert ${status} to ${await UserService.getUserMarkdownString(
170
174
  {
171
175
  userId: onCallDutyPolicyExecutionLogTimeline.alertSentToUserId!,
172
176
  projectId: onCallDutyPolicyExecutionLogTimeline.projectId!,
@@ -180,6 +184,18 @@ The on-call policy **[${onCallDutyPolicyExecutionLogTimeline.onCallDutyPolicy.na
180
184
  },
181
185
  )} was alerted. The status of this alert is **${status}** with the message: \`${onCallDutyPolicyExecutionLogTimeline.statusMessage}\`. ${onCallDutyPolicyExecutionLogTimeline.userBelongsToTeam?.name ? "The alert was sent because the user belogs to the team **" + onCallDutyPolicyExecutionLogTimeline.userBelongsToTeam?.name + "**" : ""} ${onCallDutyPolicyExecutionLogTimeline.isAcknowledged ? "The alert was acknowledged at **" + onCallDutyPolicyExecutionLogTimeline.acknowledgedAt + "**" : ""}`;
182
186
 
187
+ if (onCallDutyPolicyExecutionLogTimeline.overridedByUser) {
188
+ feedInfoInMarkdown += `The alert was supposed to be sent to **${await UserService.getUserMarkdownString(
189
+ {
190
+ userId: onCallDutyPolicyExecutionLogTimeline.overridedByUser.id!,
191
+ projectId: onCallDutyPolicyExecutionLogTimeline.projectId!,
192
+ },
193
+ )}** but was routed to **${await UserService.getUserMarkdownString({
194
+ userId: onCallDutyPolicyExecutionLogTimeline.alertSentToUserId!,
195
+ projectId: onCallDutyPolicyExecutionLogTimeline.projectId!,
196
+ })}** instead, because of an override rule.`;
197
+ }
198
+
183
199
  logger.debug("Feed Info in Markdown: " + feedInfoInMarkdown);
184
200
 
185
201
  if (onCallDutyPolicyExecutionLogTimeline.triggeredByIncidentId) {
@@ -0,0 +1,77 @@
1
+ import DatabaseService from "./DatabaseService";
2
+ import ObjectID from "../../Types/ObjectID";
3
+ import DatabaseConfig from "../DatabaseConfig";
4
+ import URL from "../../Types/API/URL";
5
+ import OnCallDutyPolicyUserOverride from "../../Models/DatabaseModels/OnCallDutyPolicyUserOverride";
6
+ import CreateBy from "../Types/Database/CreateBy";
7
+ import { OnCreate } from "../Types/Database/Hooks";
8
+ import OneUptimeDate from "../../Types/Date";
9
+ import BadDataException from "../../Types/Exception/BadDataException";
10
+
11
+ export class Service extends DatabaseService<OnCallDutyPolicyUserOverride> {
12
+ public constructor() {
13
+ super(OnCallDutyPolicyUserOverride);
14
+ }
15
+
16
+ protected override async onBeforeCreate(
17
+ createBy: CreateBy<OnCallDutyPolicyUserOverride>,
18
+ ): Promise<OnCreate<OnCallDutyPolicyUserOverride>> {
19
+ if (!createBy.data.startsAt || !createBy.data.endsAt) {
20
+ throw new BadDataException("Start time and end time are required");
21
+ }
22
+
23
+ // make sure start time is before end time
24
+ if (OneUptimeDate.isAfter(createBy.data.startsAt, createBy.data.endsAt)) {
25
+ throw new BadDataException("Start time must be before end time");
26
+ }
27
+
28
+ // make sure overrideUser and routealertsToUser are not the same
29
+ const overrideUserId: ObjectID | undefined | null =
30
+ createBy.data.overrideUserId || createBy.data.overrideUser?.id;
31
+
32
+ if (!overrideUserId) {
33
+ throw new BadDataException("Override user is required");
34
+ }
35
+
36
+ const routeAlertsToUserId: ObjectID | undefined | null =
37
+ createBy.data.routeAlertsToUserId || createBy.data.routeAlertsToUser?.id;
38
+
39
+ if (!routeAlertsToUserId) {
40
+ throw new BadDataException("Route alerts to user is required");
41
+ }
42
+
43
+ if (overrideUserId.toString() === routeAlertsToUserId.toString()) {
44
+ throw new BadDataException(
45
+ "Override user and route alerts to user cannot be the same",
46
+ );
47
+ }
48
+
49
+ return {
50
+ createBy,
51
+ carryForward: null,
52
+ };
53
+ }
54
+
55
+ public async getOnCallDutyPolicyUserOverrideLinkInDashboard(data: {
56
+ projectId: ObjectID;
57
+ onCallDutyPolicyId?: ObjectID | undefined; // if this is null then this is a global override
58
+ onCallDutyPolicyUserOverrideId: ObjectID;
59
+ }): Promise<URL> {
60
+ const projectId: ObjectID = data.projectId;
61
+ const onCallDutyPolicyId: ObjectID | undefined = data.onCallDutyPolicyId;
62
+ const onCallDutyPolicyUserOverrideId: ObjectID =
63
+ data.onCallDutyPolicyUserOverrideId;
64
+
65
+ const dashboardUrl: URL = await DatabaseConfig.getDashboardUrl();
66
+
67
+ if (!onCallDutyPolicyId) {
68
+ return URL.fromString(dashboardUrl.toString()).addRoute(
69
+ `/${projectId.toString()}/on-call-duty/user-overrides/${onCallDutyPolicyUserOverrideId.toString()}`,
70
+ );
71
+ }
72
+ return URL.fromString(dashboardUrl.toString()).addRoute(
73
+ `/${projectId.toString()}/on-call-duty/policies/${onCallDutyPolicyId.toString()}/user-overrides/${onCallDutyPolicyUserOverrideId.toString()}`,
74
+ );
75
+ }
76
+ }
77
+ export default new Service();
@@ -2,6 +2,7 @@ import DatabaseService from "./DatabaseService";
2
2
  import TwilioConfig from "../../Types/CallAndSMS/TwilioConfig";
3
3
  import BadDataException from "../../Types/Exception/BadDataException";
4
4
  import Model from "Common/Models/DatabaseModels/ProjectCallSMSConfig";
5
+ import Phone from "../../Types/Phone";
5
6
 
6
7
  export class Service extends DatabaseService<Model> {
7
8
  public constructor() {
@@ -25,7 +26,7 @@ export class Service extends DatabaseService<Model> {
25
26
  );
26
27
  }
27
28
 
28
- if (!projectCallSmsConfig.twilioPhoneNumber) {
29
+ if (!projectCallSmsConfig.twilioPrimaryPhoneNumber) {
29
30
  throw new BadDataException(
30
31
  "Project Call and SMS Config twilio phone number is not set",
31
32
  );
@@ -40,7 +41,16 @@ export class Service extends DatabaseService<Model> {
40
41
  return {
41
42
  accountSid: projectCallSmsConfig.twilioAccountSID.toString(),
42
43
  authToken: projectCallSmsConfig.twilioAuthToken.toString(),
43
- phoneNumber: projectCallSmsConfig.twilioPhoneNumber,
44
+ primaryPhoneNumber: projectCallSmsConfig.twilioPrimaryPhoneNumber,
45
+ secondaryPhoneNumbers:
46
+ projectCallSmsConfig.twilioSecondaryPhoneNumbers &&
47
+ projectCallSmsConfig.twilioSecondaryPhoneNumbers.length > 0
48
+ ? projectCallSmsConfig.twilioSecondaryPhoneNumbers
49
+ .split(",")
50
+ .map((phone: string) => {
51
+ return new Phone(phone);
52
+ })
53
+ : [],
44
54
  };
45
55
  }
46
56
  }
@@ -36,7 +36,10 @@ export class SmsService extends BaseService {
36
36
  ? {
37
37
  accountSid: options.customTwilioConfig.accountSid!,
38
38
  authToken: options.customTwilioConfig.authToken!,
39
- phoneNumber: options.customTwilioConfig.phoneNumber.toString(),
39
+ primaryPhoneNumber:
40
+ options.customTwilioConfig.primaryPhoneNumber.toString(),
41
+ secondaryPhoneNumbers:
42
+ options.customTwilioConfig.secondaryPhoneNumbers?.toString(),
40
43
  }
41
44
  : undefined,
42
45
  };
@@ -262,7 +262,8 @@ export class Service extends DatabaseService<Model> {
262
262
  _id: true,
263
263
  twilioAccountSID: true,
264
264
  twilioAuthToken: true,
265
- twilioPhoneNumber: true,
265
+ twilioPrimaryPhoneNumber: true,
266
+ twilioSecondaryPhoneNumbers: true,
266
267
  },
267
268
  },
268
269
  props: {
@@ -795,7 +796,8 @@ export class Service extends DatabaseService<Model> {
795
796
  _id: true,
796
797
  twilioAccountSID: true,
797
798
  twilioAuthToken: true,
798
- twilioPhoneNumber: true,
799
+ twilioPrimaryPhoneNumber: true,
800
+ twilioSecondaryPhoneNumbers: true,
799
801
  },
800
802
  subscriberTimezones: true,
801
803
  reportDataInDays: true,
@@ -839,6 +839,7 @@ export class Service extends DatabaseService<Model> {
839
839
  userBelongsToTeamId?: ObjectID | undefined;
840
840
  onCallDutyPolicyExecutionLogTimelineId?: ObjectID | undefined;
841
841
  onCallScheduleId?: ObjectID | undefined;
842
+ overridedByUserId?: ObjectID | undefined;
842
843
  },
843
844
  ): Promise<void> {
844
845
  // add user notification log.
@@ -887,6 +888,10 @@ export class Service extends DatabaseService<Model> {
887
888
  userOnCallLog.status = UserNotificationExecutionStatus.Scheduled;
888
889
  userOnCallLog.statusMessage = "Scheduled";
889
890
 
891
+ if (options.overridedByUserId) {
892
+ userOnCallLog.overridedByUserId = options.overridedByUserId;
893
+ }
894
+
890
895
  await UserOnCallLogService.create({
891
896
  data: userOnCallLog,
892
897
  props: {
@@ -3,5 +3,6 @@ import Phone from "../Phone";
3
3
  export default interface TwilioConfig {
4
4
  accountSid: string;
5
5
  authToken: string;
6
- phoneNumber: Phone;
6
+ primaryPhoneNumber: Phone;
7
+ secondaryPhoneNumbers: Phone[];
7
8
  }
@@ -512,6 +512,11 @@ enum Permission {
512
512
  DeleteProjectOnCallDutyPolicyEscalationRule = "DeleteProjectOnCallDutyPolicyEscalationRule",
513
513
  ReadProjectOnCallDutyPolicyEscalationRule = "ReadProjectOnCallDutyPolicyEscalationRule",
514
514
 
515
+ CreateOnCallDutyPolicyUserOverride = "CreateOnCallDutyPolicyUserOverride",
516
+ EditOnCallDutyPolicyUserOverride = "EditOnCallDutyPolicyUserOverride",
517
+ DeleteOnCallDutyPolicyUserOverride = "DeleteOnCallDutyPolicyUserOverride",
518
+ ReadOnCallDutyPolicyUserOverride = "ReadOnCallDutyPolicyUserOverride",
519
+
515
520
  // Resource Permissions (Team Permission)
516
521
  CreateProjectOnCallDutyPolicyEscalationRuleUser = "CreateProjectOnCallDutyPolicyEscalationRuleUser",
517
522
  EditProjectOnCallDutyPolicyEscalationRuleUser = "EditProjectOnCallDutyPolicyEscalationRuleUser",
@@ -2378,6 +2383,39 @@ export class PermissionHelper {
2378
2383
  isAccessControlPermission: false,
2379
2384
  },
2380
2385
 
2386
+ {
2387
+ permission: Permission.CreateOnCallDutyPolicyUserOverride,
2388
+ title: "Create On-Call Duty Policy User Override",
2389
+ description:
2390
+ "This permission can create on-call duty policy user override this project.",
2391
+ isAssignableToTenant: true,
2392
+ isAccessControlPermission: false,
2393
+ },
2394
+ {
2395
+ permission: Permission.DeleteOnCallDutyPolicyUserOverride,
2396
+ title: "Delete On-Call Duty Policy User Override",
2397
+ description:
2398
+ "This permission can delete on-call duty policy user override of this project.",
2399
+ isAssignableToTenant: true,
2400
+ isAccessControlPermission: false,
2401
+ },
2402
+ {
2403
+ permission: Permission.EditOnCallDutyPolicyUserOverride,
2404
+ title: "Edit On-Call Duty Policy User Override",
2405
+ description:
2406
+ "This permission can edit on-call duty policy user override of this project.",
2407
+ isAssignableToTenant: true,
2408
+ isAccessControlPermission: false,
2409
+ },
2410
+ {
2411
+ permission: Permission.ReadOnCallDutyPolicyUserOverride,
2412
+ title: "Read On-Call Duty Policy User Override",
2413
+ description:
2414
+ "This permission can read on-call duty policy user override of this project.",
2415
+ isAssignableToTenant: true,
2416
+ isAccessControlPermission: false,
2417
+ },
2418
+
2381
2419
  {
2382
2420
  permission: Permission.CreateProjectOnCallDutyPolicy,
2383
2421
  title: "Create On-Call Duty Policy",