@oneuptime/common 8.0.5581 → 8.0.5583
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/Server/Services/ScheduledMaintenanceService.ts +73 -16
- package/Server/Services/StatusPageService.ts +1 -0
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +171 -0
- package/Server/Utils/OpenAPI.ts +35 -16
- package/Types/Permission.ts +4 -4
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +43 -11
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +1 -0
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +107 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/OpenAPI.js +17 -16
- package/build/dist/Server/Utils/OpenAPI.js.map +1 -1
- package/build/dist/Types/Permission.js +4 -4
- package/build/dist/Types/Permission.js.map +1 -1
- package/package.json +1 -1
|
@@ -193,6 +193,13 @@ export class Service extends DatabaseService<Model> {
|
|
|
193
193
|
const statusPageName: string =
|
|
194
194
|
statuspage.pageTitle || statuspage.name || "Status Page";
|
|
195
195
|
|
|
196
|
+
const scheduledEventDetailsUrl: string =
|
|
197
|
+
event.id && statusPageURL
|
|
198
|
+
? URL.fromString(statusPageURL)
|
|
199
|
+
.addRoute(`/scheduled-events/${event.id.toString()}`)
|
|
200
|
+
.toString()
|
|
201
|
+
: statusPageURL;
|
|
202
|
+
|
|
196
203
|
// Send email to Email subscribers.
|
|
197
204
|
|
|
198
205
|
const resourcesAffected: string =
|
|
@@ -291,6 +298,7 @@ ${resourcesAffected ? `**Resources Affected:** ${resourcesAffected}` : ""}
|
|
|
291
298
|
vars: {
|
|
292
299
|
statusPageName: statusPageName,
|
|
293
300
|
statusPageUrl: statusPageURL,
|
|
301
|
+
detailsUrl: scheduledEventDetailsUrl,
|
|
294
302
|
logoUrl:
|
|
295
303
|
statuspage.logoFileId && statusPageIdString
|
|
296
304
|
? new URL(httpProtocol, host)
|
|
@@ -377,27 +385,60 @@ ${resourcesAffected ? `**Resources Affected:** ${resourcesAffected}` : ""}
|
|
|
377
385
|
(updateBy.data.startsAt as Date) ||
|
|
378
386
|
(scheduledMaintenance.startsAt! as Date);
|
|
379
387
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
388
|
+
let notificationSettings: Array<Recurring> | null = null;
|
|
389
|
+
|
|
390
|
+
const updatedNotificationSettings: Array<Recurring> | null | undefined =
|
|
391
|
+
updateBy.data.sendSubscriberNotificationsOnBeforeTheEvent as
|
|
392
|
+
| Array<Recurring>
|
|
393
|
+
| null
|
|
394
|
+
| undefined;
|
|
395
|
+
|
|
396
|
+
if (
|
|
397
|
+
updatedNotificationSettings !== null &&
|
|
398
|
+
updatedNotificationSettings !== undefined
|
|
399
|
+
) {
|
|
400
|
+
notificationSettings = updatedNotificationSettings;
|
|
401
|
+
} else {
|
|
402
|
+
const existingNotificationSettings:
|
|
403
|
+
| Array<Recurring>
|
|
404
|
+
| null
|
|
405
|
+
| undefined =
|
|
406
|
+
scheduledMaintenance.sendSubscriberNotificationsOnBeforeTheEvent as
|
|
407
|
+
| Array<Recurring>
|
|
408
|
+
| null
|
|
409
|
+
| undefined;
|
|
410
|
+
|
|
411
|
+
if (
|
|
412
|
+
existingNotificationSettings !== null &&
|
|
413
|
+
existingNotificationSettings !== undefined
|
|
414
|
+
) {
|
|
415
|
+
notificationSettings = existingNotificationSettings;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
384
418
|
|
|
385
419
|
logger.debug(
|
|
386
420
|
`Using startsAt: ${startsAt} and notificationSettings: ${JSON.stringify(notificationSettings)}`,
|
|
387
421
|
);
|
|
388
422
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
423
|
+
if (!notificationSettings || notificationSettings.length === 0) {
|
|
424
|
+
logger.debug(
|
|
425
|
+
"No subscriber notification schedule configured. Clearing nextSubscriberNotificationBeforeTheEventAt.",
|
|
426
|
+
);
|
|
427
|
+
updateBy.data.nextSubscriberNotificationBeforeTheEventAt = null;
|
|
428
|
+
} else {
|
|
429
|
+
const nextTimeToNotifyBeforeTheEvent: Date | null =
|
|
430
|
+
this.getNextTimeToNotify({
|
|
431
|
+
eventScheduledDate: startsAt,
|
|
432
|
+
sendSubscriberNotifiationsOn: notificationSettings,
|
|
433
|
+
});
|
|
394
434
|
|
|
395
|
-
|
|
396
|
-
|
|
435
|
+
updateBy.data.nextSubscriberNotificationBeforeTheEventAt =
|
|
436
|
+
nextTimeToNotifyBeforeTheEvent;
|
|
397
437
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
438
|
+
logger.debug(
|
|
439
|
+
`nextSubscriberNotificationBeforeTheEventAt set to: ${nextTimeToNotifyBeforeTheEvent}`,
|
|
440
|
+
);
|
|
441
|
+
}
|
|
401
442
|
}
|
|
402
443
|
|
|
403
444
|
// Set notification status based on shouldStatusPageSubscribersBeNotifiedOnEventCreated if it's being updated
|
|
@@ -475,7 +516,7 @@ ${resourcesAffected ? `**Resources Affected:** ${resourcesAffected}` : ""}
|
|
|
475
516
|
|
|
476
517
|
public getNextTimeToNotify(data: {
|
|
477
518
|
eventScheduledDate: Date;
|
|
478
|
-
sendSubscriberNotifiationsOn
|
|
519
|
+
sendSubscriberNotifiationsOn?: Array<Recurring> | null | undefined;
|
|
479
520
|
}): Date | null {
|
|
480
521
|
let recurringDate: Date | null = null;
|
|
481
522
|
|
|
@@ -486,7 +527,23 @@ ${resourcesAffected ? `**Resources Affected:** ${resourcesAffected}` : ""}
|
|
|
486
527
|
`Calculating next time to notify for event scheduled date: ${data.eventScheduledDate}`,
|
|
487
528
|
);
|
|
488
529
|
|
|
489
|
-
|
|
530
|
+
const notificationSchedules: Array<Recurring> = Array.isArray(
|
|
531
|
+
data.sendSubscriberNotifiationsOn,
|
|
532
|
+
)
|
|
533
|
+
? (data.sendSubscriberNotifiationsOn as Array<Recurring>)
|
|
534
|
+
: [];
|
|
535
|
+
|
|
536
|
+
if (notificationSchedules.length === 0) {
|
|
537
|
+
logger.debug(
|
|
538
|
+
"No sendSubscriberNotifiationsOn entries. Returning null for next notification time.",
|
|
539
|
+
);
|
|
540
|
+
return null;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
for (const recurringItem of notificationSchedules) {
|
|
544
|
+
if (!recurringItem) {
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
490
547
|
const notificationDate: Date = Recurring.getNextDateInterval(
|
|
491
548
|
data.eventScheduledDate,
|
|
492
549
|
recurringItem,
|
|
@@ -778,6 +778,7 @@ export class Service extends DatabaseService<StatusPage> {
|
|
|
778
778
|
subscriberEmailNotificationFooterText:
|
|
779
779
|
Service.getSubscriberEmailFooterText(statuspage),
|
|
780
780
|
statusPageUrl: statusPageURL,
|
|
781
|
+
detailsUrl: statusPageURL,
|
|
781
782
|
hasResources: report.totalResources > 0 ? "true" : "false",
|
|
782
783
|
report: report as any,
|
|
783
784
|
logoUrl:
|
|
@@ -11,6 +11,8 @@ import MetricMonitorCriteria from "./Criteria/MetricMonitorCriteria";
|
|
|
11
11
|
import TraceMonitorCriteria from "./Criteria/TraceMonitorCriteria";
|
|
12
12
|
import ExceptionMonitorCriteria from "./Criteria/ExceptionMonitorCriteria";
|
|
13
13
|
import MonitorCriteriaMessageBuilder from "./MonitorCriteriaMessageBuilder";
|
|
14
|
+
import MonitorCriteriaDataExtractor from "./MonitorCriteriaDataExtractor";
|
|
15
|
+
import MonitorCriteriaMessageFormatter from "./MonitorCriteriaMessageFormatter";
|
|
14
16
|
import DataToProcess from "./DataToProcess";
|
|
15
17
|
import Monitor from "../../../Models/DatabaseModels/Monitor";
|
|
16
18
|
import MonitorCriteria from "../../../Types/Monitor/MonitorCriteria";
|
|
@@ -98,6 +100,19 @@ export default class MonitorCriteriaEvaluator {
|
|
|
98
100
|
**Filter Conditions Met**: ${rootCause}
|
|
99
101
|
`;
|
|
100
102
|
|
|
103
|
+
const contextBlock: string | null =
|
|
104
|
+
MonitorCriteriaEvaluator.buildRootCauseContext({
|
|
105
|
+
dataToProcess: input.dataToProcess,
|
|
106
|
+
monitorStep: input.monitorStep,
|
|
107
|
+
monitor: input.monitor,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
if (contextBlock) {
|
|
111
|
+
input.probeApiIngestResponse.rootCause += `
|
|
112
|
+
${contextBlock}
|
|
113
|
+
`;
|
|
114
|
+
}
|
|
115
|
+
|
|
101
116
|
if ((input.dataToProcess as ProbeMonitorResponse).failureCause) {
|
|
102
117
|
input.probeApiIngestResponse.rootCause += `
|
|
103
118
|
**Cause**: ${(input.dataToProcess as ProbeMonitorResponse).failureCause || ""}
|
|
@@ -449,4 +464,160 @@ export default class MonitorCriteriaEvaluator {
|
|
|
449
464
|
|
|
450
465
|
return null;
|
|
451
466
|
}
|
|
467
|
+
|
|
468
|
+
private static buildRootCauseContext(input: {
|
|
469
|
+
dataToProcess: DataToProcess;
|
|
470
|
+
monitorStep: MonitorStep;
|
|
471
|
+
monitor: Monitor;
|
|
472
|
+
}): string | null {
|
|
473
|
+
const requestDetails: Array<string> = [];
|
|
474
|
+
const responseDetails: Array<string> = [];
|
|
475
|
+
|
|
476
|
+
const probeResponse: ProbeMonitorResponse | null =
|
|
477
|
+
MonitorCriteriaDataExtractor.getProbeMonitorResponse(input.dataToProcess);
|
|
478
|
+
|
|
479
|
+
const destination: string | null =
|
|
480
|
+
MonitorCriteriaEvaluator.getMonitorDestinationString({
|
|
481
|
+
monitorStep: input.monitorStep,
|
|
482
|
+
probeResponse: probeResponse,
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
if (destination) {
|
|
486
|
+
requestDetails.push(`- Destination: ${destination}`);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const port: string | null = MonitorCriteriaEvaluator.getMonitorPortString({
|
|
490
|
+
monitorStep: input.monitorStep,
|
|
491
|
+
probeResponse: probeResponse,
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
if (port) {
|
|
495
|
+
requestDetails.push(`- Destination Port: ${port}`);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const requestMethod: string | null =
|
|
499
|
+
MonitorCriteriaEvaluator.getRequestMethodString({
|
|
500
|
+
monitor: input.monitor,
|
|
501
|
+
monitorStep: input.monitorStep,
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
if (requestMethod) {
|
|
505
|
+
requestDetails.push(`- Request Method: ${requestMethod}`);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
if (probeResponse?.responseCode !== undefined) {
|
|
509
|
+
responseDetails.push(
|
|
510
|
+
`- Response Status Code: ${probeResponse.responseCode}`,
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const responseTime: string | null =
|
|
515
|
+
MonitorCriteriaEvaluator.formatMilliseconds(
|
|
516
|
+
probeResponse?.responseTimeInMs,
|
|
517
|
+
);
|
|
518
|
+
|
|
519
|
+
if (responseTime) {
|
|
520
|
+
responseDetails.push(`- Response Time: ${responseTime}`);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (probeResponse?.isTimeout !== undefined) {
|
|
524
|
+
responseDetails.push(
|
|
525
|
+
`- Timed Out: ${probeResponse.isTimeout ? "Yes" : "No"}`,
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
const sections: Array<string> = [];
|
|
530
|
+
|
|
531
|
+
if (requestDetails.length > 0) {
|
|
532
|
+
sections.push(`**Request Details**\n${requestDetails.join("\n")}`);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if (responseDetails.length > 0) {
|
|
536
|
+
sections.push(`\n\n**Response Snapshot**\n${responseDetails.join("\n")}`);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
if (!sections.length) {
|
|
540
|
+
return null;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
return sections.join("\n");
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
private static getMonitorDestinationString(input: {
|
|
547
|
+
monitorStep: MonitorStep;
|
|
548
|
+
probeResponse: ProbeMonitorResponse | null;
|
|
549
|
+
}): string | null {
|
|
550
|
+
if (input.probeResponse?.monitorDestination) {
|
|
551
|
+
return MonitorCriteriaEvaluator.stringifyValue(
|
|
552
|
+
input.probeResponse.monitorDestination,
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
if (input.monitorStep.data?.monitorDestination) {
|
|
557
|
+
return MonitorCriteriaEvaluator.stringifyValue(
|
|
558
|
+
input.monitorStep.data.monitorDestination,
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
private static getMonitorPortString(input: {
|
|
566
|
+
monitorStep: MonitorStep;
|
|
567
|
+
probeResponse: ProbeMonitorResponse | null;
|
|
568
|
+
}): string | null {
|
|
569
|
+
if (input.probeResponse?.monitorDestinationPort) {
|
|
570
|
+
return MonitorCriteriaEvaluator.stringifyValue(
|
|
571
|
+
input.probeResponse.monitorDestinationPort,
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
if (input.monitorStep.data?.monitorDestinationPort) {
|
|
576
|
+
return MonitorCriteriaEvaluator.stringifyValue(
|
|
577
|
+
input.monitorStep.data.monitorDestinationPort,
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
return null;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
private static getRequestMethodString(input: {
|
|
585
|
+
monitor: Monitor;
|
|
586
|
+
monitorStep: MonitorStep;
|
|
587
|
+
}): string | null {
|
|
588
|
+
if (
|
|
589
|
+
input.monitor.monitorType === MonitorType.API &&
|
|
590
|
+
input.monitorStep.data
|
|
591
|
+
) {
|
|
592
|
+
return `${input.monitorStep.data.requestType}`;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
return null;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
private static formatMilliseconds(value?: number): string | null {
|
|
599
|
+
if (value === undefined || value === null || isNaN(value)) {
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
const formatted: string | null =
|
|
604
|
+
MonitorCriteriaMessageFormatter.formatNumber(value, {
|
|
605
|
+
maximumFractionDigits: value < 100 ? 2 : value < 1000 ? 1 : 0,
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
return `${formatted ?? value} ms`;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
private static stringifyValue(value: unknown): string | null {
|
|
612
|
+
if (value === null || value === undefined) {
|
|
613
|
+
return null;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
try {
|
|
617
|
+
return String(value).trim();
|
|
618
|
+
} catch (err) {
|
|
619
|
+
logger.error(err);
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
452
623
|
}
|
package/Server/Utils/OpenAPI.ts
CHANGED
|
@@ -669,32 +669,42 @@ export default class OpenAPIUtil {
|
|
|
669
669
|
modelType: new () => DatabaseBaseModel,
|
|
670
670
|
model: DatabaseBaseModel,
|
|
671
671
|
): void {
|
|
672
|
-
|
|
673
|
-
|
|
672
|
+
const canCreate: boolean = !this.shouldExcludeApiForPermissions(
|
|
673
|
+
model.createRecordPermissions,
|
|
674
|
+
);
|
|
675
|
+
const canRead: boolean = !this.shouldExcludeApiForPermissions(
|
|
676
|
+
model.readRecordPermissions,
|
|
677
|
+
);
|
|
678
|
+
const canUpdate: boolean = !this.shouldExcludeApiForPermissions(
|
|
679
|
+
model.updateRecordPermissions,
|
|
680
|
+
);
|
|
681
|
+
const canDelete: boolean = !this.shouldExcludeApiForPermissions(
|
|
682
|
+
model.deleteRecordPermissions,
|
|
683
|
+
);
|
|
684
|
+
|
|
685
|
+
if (canCreate) {
|
|
674
686
|
const createSchema: ModelSchemaType = ModelSchema.getCreateModelSchema({
|
|
675
687
|
modelType,
|
|
676
688
|
});
|
|
677
689
|
registry.register(`${tableName}CreateSchema`, createSchema);
|
|
678
690
|
}
|
|
679
691
|
|
|
680
|
-
//
|
|
681
|
-
if (
|
|
692
|
+
// Register read schema whenever any operation might return it (create/update/read)
|
|
693
|
+
if (canRead || canCreate || canUpdate) {
|
|
682
694
|
const readSchema: ModelSchemaType = ModelSchema.getReadModelSchema({
|
|
683
695
|
modelType,
|
|
684
696
|
});
|
|
685
697
|
registry.register(`${tableName}ReadSchema`, readSchema);
|
|
686
698
|
}
|
|
687
699
|
|
|
688
|
-
|
|
689
|
-
if (!this.shouldExcludeApiForPermissions(model.updateRecordPermissions)) {
|
|
700
|
+
if (canUpdate) {
|
|
690
701
|
const updateSchema: ModelSchemaType = ModelSchema.getUpdateModelSchema({
|
|
691
702
|
modelType,
|
|
692
703
|
});
|
|
693
704
|
registry.register(`${tableName}UpdateSchema`, updateSchema);
|
|
694
705
|
}
|
|
695
706
|
|
|
696
|
-
|
|
697
|
-
if (!this.shouldExcludeApiForPermissions(model.deleteRecordPermissions)) {
|
|
707
|
+
if (canDelete) {
|
|
698
708
|
const deleteSchema: ModelSchemaType = ModelSchema.getDeleteModelSchema({
|
|
699
709
|
modelType,
|
|
700
710
|
});
|
|
@@ -766,8 +776,20 @@ export default class OpenAPIUtil {
|
|
|
766
776
|
modelType: new () => AnalyticsBaseModel,
|
|
767
777
|
model: AnalyticsBaseModel,
|
|
768
778
|
): void {
|
|
769
|
-
|
|
770
|
-
|
|
779
|
+
const canCreate: boolean = !this.shouldExcludeApiForPermissions(
|
|
780
|
+
model.getCreatePermissions(),
|
|
781
|
+
);
|
|
782
|
+
const canRead: boolean = !this.shouldExcludeApiForPermissions(
|
|
783
|
+
model.getReadPermissions(),
|
|
784
|
+
);
|
|
785
|
+
const canUpdate: boolean = !this.shouldExcludeApiForPermissions(
|
|
786
|
+
model.getUpdatePermissions(),
|
|
787
|
+
);
|
|
788
|
+
const canDelete: boolean = !this.shouldExcludeApiForPermissions(
|
|
789
|
+
model.getDeletePermissions(),
|
|
790
|
+
);
|
|
791
|
+
|
|
792
|
+
if (canCreate) {
|
|
771
793
|
const createSchema: AnalyticsModelSchemaType =
|
|
772
794
|
AnalyticsModelSchema.getCreateModelSchema({
|
|
773
795
|
modelType,
|
|
@@ -775,8 +797,7 @@ export default class OpenAPIUtil {
|
|
|
775
797
|
registry.register(`${tableName}CreateSchema`, createSchema);
|
|
776
798
|
}
|
|
777
799
|
|
|
778
|
-
|
|
779
|
-
if (!this.shouldExcludeApiForPermissions(model.getReadPermissions())) {
|
|
800
|
+
if (canRead || canCreate || canUpdate) {
|
|
780
801
|
const readSchema: AnalyticsModelSchemaType =
|
|
781
802
|
AnalyticsModelSchema.getModelSchema({
|
|
782
803
|
modelType,
|
|
@@ -784,8 +805,7 @@ export default class OpenAPIUtil {
|
|
|
784
805
|
registry.register(`${tableName}ReadSchema`, readSchema);
|
|
785
806
|
}
|
|
786
807
|
|
|
787
|
-
|
|
788
|
-
if (!this.shouldExcludeApiForPermissions(model.getUpdatePermissions())) {
|
|
808
|
+
if (canUpdate) {
|
|
789
809
|
const updateSchema: AnalyticsModelSchemaType =
|
|
790
810
|
AnalyticsModelSchema.getCreateModelSchema({
|
|
791
811
|
modelType,
|
|
@@ -793,8 +813,7 @@ export default class OpenAPIUtil {
|
|
|
793
813
|
registry.register(`${tableName}UpdateSchema`, updateSchema);
|
|
794
814
|
}
|
|
795
815
|
|
|
796
|
-
|
|
797
|
-
if (!this.shouldExcludeApiForPermissions(model.getDeletePermissions())) {
|
|
816
|
+
if (canDelete) {
|
|
798
817
|
const deleteSchema: AnalyticsModelSchemaType =
|
|
799
818
|
AnalyticsModelSchema.getModelSchema({
|
|
800
819
|
modelType,
|
package/Types/Permission.ts
CHANGED
|
@@ -922,28 +922,28 @@ export class PermissionHelper {
|
|
|
922
922
|
title: "Create Dashboard",
|
|
923
923
|
description: "This permission can create Dashboards of this project",
|
|
924
924
|
isAssignableToTenant: true,
|
|
925
|
-
isAccessControlPermission:
|
|
925
|
+
isAccessControlPermission: true,
|
|
926
926
|
},
|
|
927
927
|
{
|
|
928
928
|
permission: Permission.DeleteDashboard,
|
|
929
929
|
title: "Delete Dashboard",
|
|
930
930
|
description: "This permission can delete Dashboard of this project.",
|
|
931
931
|
isAssignableToTenant: true,
|
|
932
|
-
isAccessControlPermission:
|
|
932
|
+
isAccessControlPermission: true,
|
|
933
933
|
},
|
|
934
934
|
{
|
|
935
935
|
permission: Permission.EditDashboard,
|
|
936
936
|
title: "Edit Dashboard",
|
|
937
937
|
description: "This permission can edit Dashboards of this project.",
|
|
938
938
|
isAssignableToTenant: true,
|
|
939
|
-
isAccessControlPermission:
|
|
939
|
+
isAccessControlPermission: true,
|
|
940
940
|
},
|
|
941
941
|
{
|
|
942
942
|
permission: Permission.ReadDashboard,
|
|
943
943
|
title: "Read Dashboard",
|
|
944
944
|
description: "This permission can read Dashboards of this project.",
|
|
945
945
|
isAssignableToTenant: true,
|
|
946
|
-
isAccessControlPermission:
|
|
946
|
+
isAccessControlPermission: true,
|
|
947
947
|
},
|
|
948
948
|
|
|
949
949
|
// Table view permissions
|
|
@@ -139,6 +139,11 @@ export class Service extends DatabaseService {
|
|
|
139
139
|
});
|
|
140
140
|
const statusPageURL = await StatusPageService.getStatusPageURL(statuspage.id);
|
|
141
141
|
const statusPageName = statuspage.pageTitle || statuspage.name || "Status Page";
|
|
142
|
+
const scheduledEventDetailsUrl = event.id && statusPageURL
|
|
143
|
+
? URL.fromString(statusPageURL)
|
|
144
|
+
.addRoute(`/scheduled-events/${event.id.toString()}`)
|
|
145
|
+
.toString()
|
|
146
|
+
: statusPageURL;
|
|
142
147
|
// Send email to Email subscribers.
|
|
143
148
|
const resourcesAffected = ((_f = statusPageToResources[statuspage._id]) === null || _f === void 0 ? void 0 : _f.map((r) => {
|
|
144
149
|
return r.displayName;
|
|
@@ -211,6 +216,7 @@ ${resourcesAffected ? `**Resources Affected:** ${resourcesAffected}` : ""}
|
|
|
211
216
|
vars: {
|
|
212
217
|
statusPageName: statusPageName,
|
|
213
218
|
statusPageUrl: statusPageURL,
|
|
219
|
+
detailsUrl: scheduledEventDetailsUrl,
|
|
214
220
|
logoUrl: statuspage.logoFileId && statusPageIdString
|
|
215
221
|
? new URL(httpProtocol, host)
|
|
216
222
|
.addRoute(StatusPageApiRoute)
|
|
@@ -267,17 +273,33 @@ ${resourcesAffected ? `**Resources Affected:** ${resourcesAffected}` : ""}
|
|
|
267
273
|
}
|
|
268
274
|
const startsAt = updateBy.data.startsAt ||
|
|
269
275
|
scheduledMaintenance.startsAt;
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
276
|
+
let notificationSettings = null;
|
|
277
|
+
const updatedNotificationSettings = updateBy.data.sendSubscriberNotificationsOnBeforeTheEvent;
|
|
278
|
+
if (updatedNotificationSettings !== null &&
|
|
279
|
+
updatedNotificationSettings !== undefined) {
|
|
280
|
+
notificationSettings = updatedNotificationSettings;
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
const existingNotificationSettings = scheduledMaintenance.sendSubscriberNotificationsOnBeforeTheEvent;
|
|
284
|
+
if (existingNotificationSettings !== null &&
|
|
285
|
+
existingNotificationSettings !== undefined) {
|
|
286
|
+
notificationSettings = existingNotificationSettings;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
273
289
|
logger.debug(`Using startsAt: ${startsAt} and notificationSettings: ${JSON.stringify(notificationSettings)}`);
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
nextTimeToNotifyBeforeTheEvent
|
|
280
|
-
|
|
290
|
+
if (!notificationSettings || notificationSettings.length === 0) {
|
|
291
|
+
logger.debug("No subscriber notification schedule configured. Clearing nextSubscriberNotificationBeforeTheEventAt.");
|
|
292
|
+
updateBy.data.nextSubscriberNotificationBeforeTheEventAt = null;
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
const nextTimeToNotifyBeforeTheEvent = this.getNextTimeToNotify({
|
|
296
|
+
eventScheduledDate: startsAt,
|
|
297
|
+
sendSubscriberNotifiationsOn: notificationSettings,
|
|
298
|
+
});
|
|
299
|
+
updateBy.data.nextSubscriberNotificationBeforeTheEventAt =
|
|
300
|
+
nextTimeToNotifyBeforeTheEvent;
|
|
301
|
+
logger.debug(`nextSubscriberNotificationBeforeTheEventAt set to: ${nextTimeToNotifyBeforeTheEvent}`);
|
|
302
|
+
}
|
|
281
303
|
}
|
|
282
304
|
// Set notification status based on shouldStatusPageSubscribersBeNotifiedOnEventCreated if it's being updated
|
|
283
305
|
if (updateBy.data.shouldStatusPageSubscribersBeNotifiedOnEventCreated !==
|
|
@@ -337,7 +359,17 @@ ${resourcesAffected ? `**Resources Affected:** ${resourcesAffected}` : ""}
|
|
|
337
359
|
logger.debug(`getNextTimeToNotify: `);
|
|
338
360
|
logger.debug(data);
|
|
339
361
|
logger.debug(`Calculating next time to notify for event scheduled date: ${data.eventScheduledDate}`);
|
|
340
|
-
|
|
362
|
+
const notificationSchedules = Array.isArray(data.sendSubscriberNotifiationsOn)
|
|
363
|
+
? data.sendSubscriberNotifiationsOn
|
|
364
|
+
: [];
|
|
365
|
+
if (notificationSchedules.length === 0) {
|
|
366
|
+
logger.debug("No sendSubscriberNotifiationsOn entries. Returning null for next notification time.");
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
for (const recurringItem of notificationSchedules) {
|
|
370
|
+
if (!recurringItem) {
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
341
373
|
const notificationDate = Recurring.getNextDateInterval(data.eventScheduledDate, recurringItem, true);
|
|
342
374
|
logger.debug(`Notification date calculated: ${notificationDate} for recurring item: ${recurringItem}`);
|
|
343
375
|
// if this date is in the future. set it to recurring date.
|