@oneuptime/common 11.3.0 → 11.3.2
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/ProjectService.ts +429 -300
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +26 -4
- package/Server/Utils/Telemetry.ts +123 -67
- package/Tests/Server/Utils/AnalyticsDatabase/ClusterAwareSchema.test.ts +25 -12
- package/Tests/Server/Utils/Telemetry.test.ts +251 -0
- package/build/dist/Server/Services/ProjectService.js +371 -277
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +26 -4
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry.js +102 -50
- package/build/dist/Server/Utils/Telemetry.js.map +1 -1
- package/package.json +1 -1
|
@@ -450,67 +450,106 @@ export class ProjectService extends DatabaseService {
|
|
|
450
450
|
});
|
|
451
451
|
}
|
|
452
452
|
}
|
|
453
|
+
/**
|
|
454
|
+
* Names of every row of `service` already scoped to `projectId`.
|
|
455
|
+
*
|
|
456
|
+
* The addDefault* seeders below each create a fixed set of named rows on a
|
|
457
|
+
* column that is unique per project (@UniqueColumnBy("projectId") on `name`).
|
|
458
|
+
* The SAME defaults are created in two places: here, in the project-create
|
|
459
|
+
* hooks, AND by backfill data migrations (e.g.
|
|
460
|
+
* AddDefaultAlertSeverityAndStateToExistingProjects). Creating a name that
|
|
461
|
+
* already exists throws "<X> with the same name already exists" in
|
|
462
|
+
* DatabaseService.checkUniqueColumnBy — and because the DataMigration runner
|
|
463
|
+
* halts the whole chain at the first failure, that single throw freezes every
|
|
464
|
+
* later migration. Each seeder guards its creates against this set so both
|
|
465
|
+
* paths are idempotent / safe to re-run.
|
|
466
|
+
*/
|
|
467
|
+
async getExistingProjectScopedNames(service, projectId) {
|
|
468
|
+
const items = await service.findBy({
|
|
469
|
+
query: {
|
|
470
|
+
projectId: projectId,
|
|
471
|
+
},
|
|
472
|
+
select: {
|
|
473
|
+
name: true,
|
|
474
|
+
},
|
|
475
|
+
skip: 0,
|
|
476
|
+
limit: LIMIT_MAX,
|
|
477
|
+
props: {
|
|
478
|
+
isRoot: true,
|
|
479
|
+
},
|
|
480
|
+
});
|
|
481
|
+
return new Set(items.map((item) => {
|
|
482
|
+
return item.name;
|
|
483
|
+
}));
|
|
484
|
+
}
|
|
453
485
|
async addDefaultScheduledMaintenanceState(createdItem) {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
486
|
+
const projectId = createdItem.id;
|
|
487
|
+
// Idempotent — see getExistingProjectScopedNames.
|
|
488
|
+
const existingNames = await this.getExistingProjectScopedNames(ScheduledMaintenanceStateService, projectId);
|
|
489
|
+
if (!existingNames.has("Scheduled")) {
|
|
490
|
+
const createdScheduledMaintenanceState = new ScheduledMaintenanceState();
|
|
491
|
+
createdScheduledMaintenanceState.name = "Scheduled";
|
|
492
|
+
createdScheduledMaintenanceState.description =
|
|
493
|
+
"When an event is scheduled, it belongs to this state";
|
|
494
|
+
createdScheduledMaintenanceState.color = Black;
|
|
495
|
+
createdScheduledMaintenanceState.isScheduledState = true;
|
|
496
|
+
createdScheduledMaintenanceState.projectId = projectId;
|
|
497
|
+
createdScheduledMaintenanceState.order = 1;
|
|
463
498
|
await ScheduledMaintenanceStateService.create({
|
|
464
499
|
data: createdScheduledMaintenanceState,
|
|
465
500
|
props: {
|
|
466
501
|
isRoot: true,
|
|
467
502
|
},
|
|
468
503
|
});
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
504
|
+
}
|
|
505
|
+
if (!existingNames.has("Ongoing")) {
|
|
506
|
+
const ongoingScheduledMaintenanceState = new ScheduledMaintenanceState();
|
|
507
|
+
ongoingScheduledMaintenanceState.name = "Ongoing";
|
|
508
|
+
ongoingScheduledMaintenanceState.description =
|
|
509
|
+
"When an event is ongoing, it belongs to this state.";
|
|
510
|
+
ongoingScheduledMaintenanceState.color = Yellow;
|
|
511
|
+
ongoingScheduledMaintenanceState.isOngoingState = true;
|
|
512
|
+
ongoingScheduledMaintenanceState.projectId = projectId;
|
|
513
|
+
ongoingScheduledMaintenanceState.order = 2;
|
|
478
514
|
await ScheduledMaintenanceStateService.create({
|
|
479
515
|
data: ongoingScheduledMaintenanceState,
|
|
480
516
|
props: {
|
|
481
517
|
isRoot: true,
|
|
482
518
|
},
|
|
483
519
|
});
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
520
|
+
}
|
|
521
|
+
if (!existingNames.has("Ended")) {
|
|
522
|
+
const endedScheduledMaintenanceState = new ScheduledMaintenanceState();
|
|
523
|
+
endedScheduledMaintenanceState.name = "Ended";
|
|
524
|
+
endedScheduledMaintenanceState.description =
|
|
525
|
+
"Scheduled maintenance events switch to this state when they end.";
|
|
526
|
+
endedScheduledMaintenanceState.color = new Color("#4A4A4A");
|
|
527
|
+
endedScheduledMaintenanceState.isEndedState = true;
|
|
528
|
+
endedScheduledMaintenanceState.projectId = projectId;
|
|
529
|
+
endedScheduledMaintenanceState.order = 3;
|
|
493
530
|
await ScheduledMaintenanceStateService.create({
|
|
494
531
|
data: endedScheduledMaintenanceState,
|
|
495
532
|
props: {
|
|
496
533
|
isRoot: true,
|
|
497
534
|
},
|
|
498
535
|
});
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
536
|
+
}
|
|
537
|
+
if (!existingNames.has("Completed")) {
|
|
538
|
+
const completedScheduledMaintenanceState = new ScheduledMaintenanceState();
|
|
539
|
+
completedScheduledMaintenanceState.name = "Completed";
|
|
540
|
+
completedScheduledMaintenanceState.description =
|
|
541
|
+
"When an event is completed, it belongs to this state.";
|
|
542
|
+
completedScheduledMaintenanceState.color = Green;
|
|
543
|
+
completedScheduledMaintenanceState.isResolvedState = true;
|
|
544
|
+
completedScheduledMaintenanceState.projectId = projectId;
|
|
545
|
+
completedScheduledMaintenanceState.order = 4;
|
|
508
546
|
await ScheduledMaintenanceStateService.create({
|
|
509
547
|
data: completedScheduledMaintenanceState,
|
|
510
548
|
props: {
|
|
511
549
|
isRoot: true,
|
|
512
550
|
},
|
|
513
551
|
});
|
|
552
|
+
}
|
|
514
553
|
return createdItem;
|
|
515
554
|
}
|
|
516
555
|
async onCreateSuccess(_onCreate, createdItem) {
|
|
@@ -631,262 +670,317 @@ export class ProjectService extends DatabaseService {
|
|
|
631
670
|
return createdItem;
|
|
632
671
|
}
|
|
633
672
|
async addDefaultIncidentState(createdItem) {
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
673
|
+
const projectId = createdItem.id;
|
|
674
|
+
// Idempotent — see getExistingProjectScopedNames.
|
|
675
|
+
const existingNames = await this.getExistingProjectScopedNames(IncidentStateService, projectId);
|
|
676
|
+
if (!existingNames.has("Identified")) {
|
|
677
|
+
const createdIncidentState = new IncidentState();
|
|
678
|
+
createdIncidentState.name = "Identified";
|
|
679
|
+
createdIncidentState.description =
|
|
680
|
+
"When an incident is created, it belongs to this state";
|
|
681
|
+
createdIncidentState.color = Red;
|
|
682
|
+
createdIncidentState.isCreatedState = true;
|
|
683
|
+
createdIncidentState.projectId = projectId;
|
|
684
|
+
createdIncidentState.order = 1;
|
|
685
|
+
await IncidentStateService.create({
|
|
686
|
+
data: createdIncidentState,
|
|
687
|
+
props: {
|
|
688
|
+
isRoot: true,
|
|
689
|
+
},
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
if (!existingNames.has("Acknowledged")) {
|
|
693
|
+
const acknowledgedIncidentState = new IncidentState();
|
|
694
|
+
acknowledgedIncidentState.name = "Acknowledged";
|
|
695
|
+
acknowledgedIncidentState.description =
|
|
696
|
+
"When an incident is acknowledged, it belongs to this state.";
|
|
697
|
+
acknowledgedIncidentState.color = Yellow;
|
|
698
|
+
acknowledgedIncidentState.isAcknowledgedState = true;
|
|
699
|
+
acknowledgedIncidentState.projectId = projectId;
|
|
700
|
+
acknowledgedIncidentState.order = 2;
|
|
701
|
+
await IncidentStateService.create({
|
|
702
|
+
data: acknowledgedIncidentState,
|
|
703
|
+
props: {
|
|
704
|
+
isRoot: true,
|
|
705
|
+
},
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
if (!existingNames.has("Resolved")) {
|
|
709
|
+
const resolvedIncidentState = new IncidentState();
|
|
710
|
+
resolvedIncidentState.name = "Resolved";
|
|
711
|
+
resolvedIncidentState.description =
|
|
712
|
+
"When an incident is resolved, it belongs to this state.";
|
|
713
|
+
resolvedIncidentState.color = Green;
|
|
714
|
+
resolvedIncidentState.isResolvedState = true;
|
|
715
|
+
resolvedIncidentState.projectId = projectId;
|
|
716
|
+
resolvedIncidentState.order = 3;
|
|
717
|
+
await IncidentStateService.create({
|
|
718
|
+
data: resolvedIncidentState,
|
|
719
|
+
props: {
|
|
720
|
+
isRoot: true,
|
|
721
|
+
},
|
|
722
|
+
});
|
|
723
|
+
}
|
|
676
724
|
return createdItem;
|
|
677
725
|
}
|
|
678
726
|
async addDefaultAlertState(createdItem) {
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
727
|
+
const projectId = createdItem.id;
|
|
728
|
+
// Idempotent — see getExistingProjectScopedNames.
|
|
729
|
+
const existingNames = await this.getExistingProjectScopedNames(AlertStateService, projectId);
|
|
730
|
+
if (!existingNames.has("Identified")) {
|
|
731
|
+
const createdAlertState = new AlertState();
|
|
732
|
+
createdAlertState.name = "Identified";
|
|
733
|
+
createdAlertState.description =
|
|
734
|
+
"When an alert is created, it belongs to this state";
|
|
735
|
+
createdAlertState.color = Red;
|
|
736
|
+
createdAlertState.isCreatedState = true;
|
|
737
|
+
createdAlertState.projectId = projectId;
|
|
738
|
+
createdAlertState.order = 1;
|
|
739
|
+
await AlertStateService.create({
|
|
740
|
+
data: createdAlertState,
|
|
741
|
+
props: {
|
|
742
|
+
isRoot: true,
|
|
743
|
+
},
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
if (!existingNames.has("Acknowledged")) {
|
|
747
|
+
const acknowledgedAlertState = new AlertState();
|
|
748
|
+
acknowledgedAlertState.name = "Acknowledged";
|
|
749
|
+
acknowledgedAlertState.description =
|
|
750
|
+
"When an alert is acknowledged, it belongs to this state.";
|
|
751
|
+
acknowledgedAlertState.color = Yellow;
|
|
752
|
+
acknowledgedAlertState.isAcknowledgedState = true;
|
|
753
|
+
acknowledgedAlertState.projectId = projectId;
|
|
754
|
+
acknowledgedAlertState.order = 2;
|
|
755
|
+
await AlertStateService.create({
|
|
756
|
+
data: acknowledgedAlertState,
|
|
757
|
+
props: {
|
|
758
|
+
isRoot: true,
|
|
759
|
+
},
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
if (!existingNames.has("Resolved")) {
|
|
763
|
+
const resolvedAlertState = new AlertState();
|
|
764
|
+
resolvedAlertState.name = "Resolved";
|
|
765
|
+
resolvedAlertState.description =
|
|
766
|
+
"When an incident is resolved, it belongs to this state.";
|
|
767
|
+
resolvedAlertState.color = Green;
|
|
768
|
+
resolvedAlertState.isResolvedState = true;
|
|
769
|
+
resolvedAlertState.projectId = projectId;
|
|
770
|
+
resolvedAlertState.order = 3;
|
|
771
|
+
await AlertStateService.create({
|
|
772
|
+
data: resolvedAlertState,
|
|
773
|
+
props: {
|
|
774
|
+
isRoot: true,
|
|
775
|
+
},
|
|
776
|
+
});
|
|
777
|
+
}
|
|
721
778
|
return createdItem;
|
|
722
779
|
}
|
|
723
780
|
async addDefaultAlertSeverity(createdItem) {
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
781
|
+
const projectId = createdItem.id;
|
|
782
|
+
// Idempotent — see getExistingProjectScopedNames.
|
|
783
|
+
const existingNames = await this.getExistingProjectScopedNames(AlertSeverityService, projectId);
|
|
784
|
+
if (!existingNames.has("High")) {
|
|
785
|
+
const highSeverity = new AlertSeverity();
|
|
786
|
+
highSeverity.name = "High";
|
|
787
|
+
highSeverity.description =
|
|
788
|
+
"Issues causing very high impact to customers. Immediate attention is required.";
|
|
789
|
+
highSeverity.color = Moroon500;
|
|
790
|
+
highSeverity.projectId = projectId;
|
|
791
|
+
highSeverity.order = 1;
|
|
792
|
+
await AlertSeverityService.create({
|
|
793
|
+
data: highSeverity,
|
|
794
|
+
props: {
|
|
795
|
+
isRoot: true,
|
|
796
|
+
},
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
if (!existingNames.has("Low")) {
|
|
800
|
+
const lowSeverity = new AlertSeverity();
|
|
801
|
+
lowSeverity.name = "Low";
|
|
802
|
+
lowSeverity.description = "Issues causing low impact to customers.";
|
|
803
|
+
lowSeverity.color = Yellow500;
|
|
804
|
+
lowSeverity.projectId = projectId;
|
|
805
|
+
lowSeverity.order = 2;
|
|
806
|
+
await AlertSeverityService.create({
|
|
807
|
+
data: lowSeverity,
|
|
808
|
+
props: {
|
|
809
|
+
isRoot: true,
|
|
810
|
+
},
|
|
811
|
+
});
|
|
812
|
+
}
|
|
749
813
|
return createdItem;
|
|
750
814
|
}
|
|
751
815
|
async addDefaultIncidentSeverity(createdItem) {
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
816
|
+
const projectId = createdItem.id;
|
|
817
|
+
// Idempotent — see getExistingProjectScopedNames.
|
|
818
|
+
const existingNames = await this.getExistingProjectScopedNames(IncidentSeverityService, projectId);
|
|
819
|
+
if (!existingNames.has("Critical Incident")) {
|
|
820
|
+
const criticalIncident = new IncidentSeverity();
|
|
821
|
+
criticalIncident.name = "Critical Incident";
|
|
822
|
+
criticalIncident.description =
|
|
823
|
+
"Issues causing very high impact to customers. Immediate response is required. Examples include a full outage, or a data breach.";
|
|
824
|
+
criticalIncident.color = Moroon500;
|
|
825
|
+
criticalIncident.projectId = projectId;
|
|
826
|
+
criticalIncident.order = 1;
|
|
827
|
+
await IncidentSeverityService.create({
|
|
828
|
+
data: criticalIncident,
|
|
829
|
+
props: {
|
|
830
|
+
isRoot: true,
|
|
831
|
+
},
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
if (!existingNames.has("Major Incident")) {
|
|
835
|
+
const majorIncident = new IncidentSeverity();
|
|
836
|
+
majorIncident.name = "Major Incident";
|
|
837
|
+
majorIncident.description =
|
|
838
|
+
"Issues causing significant impact. Immediate response is usually required. We might have some workarounds that mitigate the impact on customers. Examples include an important sub-system failing.";
|
|
839
|
+
majorIncident.color = Red;
|
|
840
|
+
majorIncident.projectId = projectId;
|
|
841
|
+
majorIncident.order = 2;
|
|
842
|
+
await IncidentSeverityService.create({
|
|
843
|
+
data: majorIncident,
|
|
844
|
+
props: {
|
|
845
|
+
isRoot: true,
|
|
846
|
+
},
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
if (!existingNames.has("Minor Incident")) {
|
|
850
|
+
const minorIncident = new IncidentSeverity();
|
|
851
|
+
minorIncident.name = "Minor Incident";
|
|
852
|
+
minorIncident.description =
|
|
853
|
+
"Issues with low impact, which can usually be handled within working hours. Most customers are unlikely to notice any problems. Examples include a slight drop in application performance.";
|
|
854
|
+
minorIncident.color = Yellow;
|
|
855
|
+
minorIncident.projectId = projectId;
|
|
856
|
+
minorIncident.order = 3;
|
|
857
|
+
await IncidentSeverityService.create({
|
|
858
|
+
data: minorIncident,
|
|
859
|
+
props: {
|
|
860
|
+
isRoot: true,
|
|
861
|
+
},
|
|
862
|
+
});
|
|
863
|
+
}
|
|
791
864
|
return createdItem;
|
|
792
865
|
}
|
|
793
866
|
async addDefaultIncidentRoles(createdItem) {
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
867
|
+
const projectId = createdItem.id;
|
|
868
|
+
// Idempotent — see getExistingProjectScopedNames.
|
|
869
|
+
const existingNames = await this.getExistingProjectScopedNames(IncidentRoleService, projectId);
|
|
870
|
+
if (!existingNames.has("Incident Commander")) {
|
|
871
|
+
const incidentCommander = new IncidentRole();
|
|
872
|
+
incidentCommander.name = "Incident Commander";
|
|
873
|
+
incidentCommander.description =
|
|
874
|
+
"Primary decision maker during an incident. Responsible for coordinating the response and making final decisions.";
|
|
875
|
+
incidentCommander.color = Purple500;
|
|
876
|
+
incidentCommander.roleIcon = IconProp.ShieldCheck;
|
|
877
|
+
incidentCommander.projectId = projectId;
|
|
878
|
+
incidentCommander.isPrimaryRole = true;
|
|
879
|
+
incidentCommander.isDeleteable = false;
|
|
880
|
+
await IncidentRoleService.create({
|
|
881
|
+
data: incidentCommander,
|
|
882
|
+
props: {
|
|
883
|
+
isRoot: true,
|
|
884
|
+
},
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
if (!existingNames.has("Responder")) {
|
|
888
|
+
const responder = new IncidentRole();
|
|
889
|
+
responder.name = "Responder";
|
|
890
|
+
responder.description =
|
|
891
|
+
"Active participant in incident resolution. Performs hands-on work to resolve the incident.";
|
|
892
|
+
responder.color = Blue500;
|
|
893
|
+
responder.roleIcon = IconProp.Wrench;
|
|
894
|
+
responder.projectId = projectId;
|
|
895
|
+
await IncidentRoleService.create({
|
|
896
|
+
data: responder,
|
|
897
|
+
props: {
|
|
898
|
+
isRoot: true,
|
|
899
|
+
},
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
if (!existingNames.has("Communications Lead")) {
|
|
903
|
+
const communicationsLead = new IncidentRole();
|
|
904
|
+
communicationsLead.name = "Communications Lead";
|
|
905
|
+
communicationsLead.description =
|
|
906
|
+
"Handles stakeholder communication and status updates during an incident.";
|
|
907
|
+
communicationsLead.color = Teal500;
|
|
908
|
+
communicationsLead.roleIcon = IconProp.Announcement;
|
|
909
|
+
communicationsLead.projectId = projectId;
|
|
910
|
+
await IncidentRoleService.create({
|
|
911
|
+
data: communicationsLead,
|
|
912
|
+
props: {
|
|
913
|
+
isRoot: true,
|
|
914
|
+
},
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
if (!existingNames.has("Observer")) {
|
|
918
|
+
const observer = new IncidentRole();
|
|
919
|
+
observer.name = "Observer";
|
|
920
|
+
observer.description =
|
|
921
|
+
"Read-only participant who monitors the incident without active involvement.";
|
|
922
|
+
observer.color = Gray500;
|
|
923
|
+
observer.roleIcon = IconProp.Activity;
|
|
924
|
+
observer.projectId = projectId;
|
|
925
|
+
observer.canAssignMultipleUsers = true;
|
|
926
|
+
await IncidentRoleService.create({
|
|
927
|
+
data: observer,
|
|
928
|
+
props: {
|
|
929
|
+
isRoot: true,
|
|
930
|
+
},
|
|
931
|
+
});
|
|
932
|
+
}
|
|
849
933
|
return createdItem;
|
|
850
934
|
}
|
|
851
935
|
async addDefaultMonitorStatus(createdItem) {
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
936
|
+
const projectId = createdItem.id;
|
|
937
|
+
// Idempotent — see getExistingProjectScopedNames.
|
|
938
|
+
const existingNames = await this.getExistingProjectScopedNames(MonitorStatusService, projectId);
|
|
939
|
+
if (!existingNames.has("Operational")) {
|
|
940
|
+
const operationalStatus = new MonitorStatus();
|
|
941
|
+
operationalStatus.name = "Operational";
|
|
942
|
+
operationalStatus.description = "Monitor operating normally";
|
|
943
|
+
operationalStatus.projectId = projectId;
|
|
944
|
+
operationalStatus.priority = 1;
|
|
945
|
+
operationalStatus.isOperationalState = true;
|
|
946
|
+
operationalStatus.color = Green;
|
|
947
|
+
await MonitorStatusService.create({
|
|
948
|
+
data: operationalStatus,
|
|
949
|
+
props: {
|
|
950
|
+
isRoot: true,
|
|
951
|
+
},
|
|
952
|
+
});
|
|
953
|
+
}
|
|
954
|
+
if (!existingNames.has("Degraded")) {
|
|
955
|
+
const degradedStatus = new MonitorStatus();
|
|
956
|
+
degradedStatus.name = "Degraded";
|
|
957
|
+
degradedStatus.description =
|
|
958
|
+
"Monitor is operating at reduced performance.";
|
|
959
|
+
degradedStatus.priority = 2;
|
|
960
|
+
degradedStatus.projectId = projectId;
|
|
961
|
+
degradedStatus.color = Yellow;
|
|
962
|
+
await MonitorStatusService.create({
|
|
963
|
+
data: degradedStatus,
|
|
964
|
+
props: {
|
|
965
|
+
isRoot: true,
|
|
966
|
+
},
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
if (!existingNames.has("Offline")) {
|
|
970
|
+
const downStatus = new MonitorStatus();
|
|
971
|
+
downStatus.name = "Offline";
|
|
972
|
+
downStatus.description = "Monitor is offline.";
|
|
973
|
+
downStatus.isOfflineState = true;
|
|
974
|
+
downStatus.projectId = projectId;
|
|
975
|
+
downStatus.priority = 3;
|
|
976
|
+
downStatus.color = Red;
|
|
977
|
+
await MonitorStatusService.create({
|
|
978
|
+
data: downStatus,
|
|
979
|
+
props: {
|
|
980
|
+
isRoot: true,
|
|
981
|
+
},
|
|
982
|
+
});
|
|
983
|
+
}
|
|
890
984
|
return createdItem;
|
|
891
985
|
}
|
|
892
986
|
async addDefaultProjectTeams(createdItem) {
|