@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
|
@@ -62,6 +62,7 @@ import IncidentState from "../../Models/DatabaseModels/IncidentState";
|
|
|
62
62
|
import IncidentRole from "../../Models/DatabaseModels/IncidentRole";
|
|
63
63
|
import MonitorStatus from "../../Models/DatabaseModels/MonitorStatus";
|
|
64
64
|
import Model from "../../Models/DatabaseModels/Project";
|
|
65
|
+
import BaseModel from "../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
65
66
|
import PromoCode from "../../Models/DatabaseModels/PromoCode";
|
|
66
67
|
import ScheduledMaintenanceState from "../../Models/DatabaseModels/ScheduledMaintenanceState";
|
|
67
68
|
import Team from "../../Models/DatabaseModels/Team";
|
|
@@ -638,80 +639,132 @@ export class ProjectService extends DatabaseService<Model> {
|
|
|
638
639
|
}
|
|
639
640
|
}
|
|
640
641
|
|
|
642
|
+
/**
|
|
643
|
+
* Names of every row of `service` already scoped to `projectId`.
|
|
644
|
+
*
|
|
645
|
+
* The addDefault* seeders below each create a fixed set of named rows on a
|
|
646
|
+
* column that is unique per project (@UniqueColumnBy("projectId") on `name`).
|
|
647
|
+
* The SAME defaults are created in two places: here, in the project-create
|
|
648
|
+
* hooks, AND by backfill data migrations (e.g.
|
|
649
|
+
* AddDefaultAlertSeverityAndStateToExistingProjects). Creating a name that
|
|
650
|
+
* already exists throws "<X> with the same name already exists" in
|
|
651
|
+
* DatabaseService.checkUniqueColumnBy — and because the DataMigration runner
|
|
652
|
+
* halts the whole chain at the first failure, that single throw freezes every
|
|
653
|
+
* later migration. Each seeder guards its creates against this set so both
|
|
654
|
+
* paths are idempotent / safe to re-run.
|
|
655
|
+
*/
|
|
656
|
+
private async getExistingProjectScopedNames<TBaseModel extends BaseModel>(
|
|
657
|
+
service: DatabaseService<TBaseModel>,
|
|
658
|
+
projectId: ObjectID,
|
|
659
|
+
): Promise<Set<string | undefined>> {
|
|
660
|
+
const items: Array<TBaseModel> = await service.findBy({
|
|
661
|
+
query: {
|
|
662
|
+
projectId: projectId,
|
|
663
|
+
} as Query<TBaseModel>,
|
|
664
|
+
select: {
|
|
665
|
+
name: true,
|
|
666
|
+
} as Select<TBaseModel>,
|
|
667
|
+
skip: 0,
|
|
668
|
+
limit: LIMIT_MAX,
|
|
669
|
+
props: {
|
|
670
|
+
isRoot: true,
|
|
671
|
+
},
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
return new Set(
|
|
675
|
+
items.map((item: TBaseModel): string | undefined => {
|
|
676
|
+
return (item as { name?: string | undefined }).name;
|
|
677
|
+
}),
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
|
|
641
681
|
private async addDefaultScheduledMaintenanceState(
|
|
642
682
|
createdItem: Model,
|
|
643
683
|
): Promise<Model> {
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
684
|
+
const projectId: ObjectID = createdItem.id!;
|
|
685
|
+
|
|
686
|
+
// Idempotent — see getExistingProjectScopedNames.
|
|
687
|
+
const existingNames: Set<string | undefined> =
|
|
688
|
+
await this.getExistingProjectScopedNames(
|
|
689
|
+
ScheduledMaintenanceStateService,
|
|
690
|
+
projectId,
|
|
691
|
+
);
|
|
692
|
+
|
|
693
|
+
if (!existingNames.has("Scheduled")) {
|
|
694
|
+
const createdScheduledMaintenanceState: ScheduledMaintenanceState =
|
|
695
|
+
new ScheduledMaintenanceState();
|
|
696
|
+
createdScheduledMaintenanceState.name = "Scheduled";
|
|
697
|
+
createdScheduledMaintenanceState.description =
|
|
698
|
+
"When an event is scheduled, it belongs to this state";
|
|
699
|
+
createdScheduledMaintenanceState.color = Black;
|
|
700
|
+
createdScheduledMaintenanceState.isScheduledState = true;
|
|
701
|
+
createdScheduledMaintenanceState.projectId = projectId;
|
|
702
|
+
createdScheduledMaintenanceState.order = 1;
|
|
703
|
+
|
|
655
704
|
await ScheduledMaintenanceStateService.create({
|
|
656
705
|
data: createdScheduledMaintenanceState,
|
|
657
706
|
props: {
|
|
658
707
|
isRoot: true,
|
|
659
708
|
},
|
|
660
709
|
});
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
if (!existingNames.has("Ongoing")) {
|
|
713
|
+
const ongoingScheduledMaintenanceState: ScheduledMaintenanceState =
|
|
714
|
+
new ScheduledMaintenanceState();
|
|
715
|
+
ongoingScheduledMaintenanceState.name = "Ongoing";
|
|
716
|
+
ongoingScheduledMaintenanceState.description =
|
|
717
|
+
"When an event is ongoing, it belongs to this state.";
|
|
718
|
+
ongoingScheduledMaintenanceState.color = Yellow;
|
|
719
|
+
ongoingScheduledMaintenanceState.isOngoingState = true;
|
|
720
|
+
ongoingScheduledMaintenanceState.projectId = projectId;
|
|
721
|
+
ongoingScheduledMaintenanceState.order = 2;
|
|
661
722
|
|
|
662
|
-
let ongoingScheduledMaintenanceState: ScheduledMaintenanceState =
|
|
663
|
-
new ScheduledMaintenanceState();
|
|
664
|
-
ongoingScheduledMaintenanceState.name = "Ongoing";
|
|
665
|
-
ongoingScheduledMaintenanceState.description =
|
|
666
|
-
"When an event is ongoing, it belongs to this state.";
|
|
667
|
-
ongoingScheduledMaintenanceState.color = Yellow;
|
|
668
|
-
ongoingScheduledMaintenanceState.isOngoingState = true;
|
|
669
|
-
ongoingScheduledMaintenanceState.projectId = createdItem.id!;
|
|
670
|
-
ongoingScheduledMaintenanceState.order = 2;
|
|
671
|
-
|
|
672
|
-
ongoingScheduledMaintenanceState =
|
|
673
723
|
await ScheduledMaintenanceStateService.create({
|
|
674
724
|
data: ongoingScheduledMaintenanceState,
|
|
675
725
|
props: {
|
|
676
726
|
isRoot: true,
|
|
677
727
|
},
|
|
678
728
|
});
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
if (!existingNames.has("Ended")) {
|
|
732
|
+
const endedScheduledMaintenanceState: ScheduledMaintenanceState =
|
|
733
|
+
new ScheduledMaintenanceState();
|
|
734
|
+
endedScheduledMaintenanceState.name = "Ended";
|
|
735
|
+
endedScheduledMaintenanceState.description =
|
|
736
|
+
"Scheduled maintenance events switch to this state when they end.";
|
|
737
|
+
endedScheduledMaintenanceState.color = new Color("#4A4A4A");
|
|
738
|
+
endedScheduledMaintenanceState.isEndedState = true;
|
|
739
|
+
endedScheduledMaintenanceState.projectId = projectId;
|
|
740
|
+
endedScheduledMaintenanceState.order = 3;
|
|
679
741
|
|
|
680
|
-
let endedScheduledMaintenanceState: ScheduledMaintenanceState =
|
|
681
|
-
new ScheduledMaintenanceState();
|
|
682
|
-
endedScheduledMaintenanceState.name = "Ended";
|
|
683
|
-
endedScheduledMaintenanceState.description =
|
|
684
|
-
"Scheduled maintenance events switch to this state when they end.";
|
|
685
|
-
endedScheduledMaintenanceState.color = new Color("#4A4A4A");
|
|
686
|
-
endedScheduledMaintenanceState.isEndedState = true;
|
|
687
|
-
endedScheduledMaintenanceState.projectId = createdItem.id!;
|
|
688
|
-
endedScheduledMaintenanceState.order = 3;
|
|
689
|
-
|
|
690
|
-
endedScheduledMaintenanceState =
|
|
691
742
|
await ScheduledMaintenanceStateService.create({
|
|
692
743
|
data: endedScheduledMaintenanceState,
|
|
693
744
|
props: {
|
|
694
745
|
isRoot: true,
|
|
695
746
|
},
|
|
696
747
|
});
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
if (!existingNames.has("Completed")) {
|
|
751
|
+
const completedScheduledMaintenanceState: ScheduledMaintenanceState =
|
|
752
|
+
new ScheduledMaintenanceState();
|
|
753
|
+
completedScheduledMaintenanceState.name = "Completed";
|
|
754
|
+
completedScheduledMaintenanceState.description =
|
|
755
|
+
"When an event is completed, it belongs to this state.";
|
|
756
|
+
completedScheduledMaintenanceState.color = Green;
|
|
757
|
+
completedScheduledMaintenanceState.isResolvedState = true;
|
|
758
|
+
completedScheduledMaintenanceState.projectId = projectId;
|
|
759
|
+
completedScheduledMaintenanceState.order = 4;
|
|
697
760
|
|
|
698
|
-
let completedScheduledMaintenanceState: ScheduledMaintenanceState =
|
|
699
|
-
new ScheduledMaintenanceState();
|
|
700
|
-
completedScheduledMaintenanceState.name = "Completed";
|
|
701
|
-
completedScheduledMaintenanceState.description =
|
|
702
|
-
"When an event is completed, it belongs to this state.";
|
|
703
|
-
completedScheduledMaintenanceState.color = Green;
|
|
704
|
-
completedScheduledMaintenanceState.isResolvedState = true;
|
|
705
|
-
completedScheduledMaintenanceState.projectId = createdItem.id!;
|
|
706
|
-
completedScheduledMaintenanceState.order = 4;
|
|
707
|
-
|
|
708
|
-
completedScheduledMaintenanceState =
|
|
709
761
|
await ScheduledMaintenanceStateService.create({
|
|
710
762
|
data: completedScheduledMaintenanceState,
|
|
711
763
|
props: {
|
|
712
764
|
isRoot: true,
|
|
713
765
|
},
|
|
714
766
|
});
|
|
767
|
+
}
|
|
715
768
|
|
|
716
769
|
return createdItem;
|
|
717
770
|
}
|
|
@@ -858,304 +911,380 @@ export class ProjectService extends DatabaseService<Model> {
|
|
|
858
911
|
}
|
|
859
912
|
|
|
860
913
|
private async addDefaultIncidentState(createdItem: Model): Promise<Model> {
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
914
|
+
const projectId: ObjectID = createdItem.id!;
|
|
915
|
+
|
|
916
|
+
// Idempotent — see getExistingProjectScopedNames.
|
|
917
|
+
const existingNames: Set<string | undefined> =
|
|
918
|
+
await this.getExistingProjectScopedNames(IncidentStateService, projectId);
|
|
919
|
+
|
|
920
|
+
if (!existingNames.has("Identified")) {
|
|
921
|
+
const createdIncidentState: IncidentState = new IncidentState();
|
|
922
|
+
createdIncidentState.name = "Identified";
|
|
923
|
+
createdIncidentState.description =
|
|
924
|
+
"When an incident is created, it belongs to this state";
|
|
925
|
+
createdIncidentState.color = Red;
|
|
926
|
+
createdIncidentState.isCreatedState = true;
|
|
927
|
+
createdIncidentState.projectId = projectId;
|
|
928
|
+
createdIncidentState.order = 1;
|
|
929
|
+
|
|
930
|
+
await IncidentStateService.create({
|
|
931
|
+
data: createdIncidentState,
|
|
932
|
+
props: {
|
|
933
|
+
isRoot: true,
|
|
934
|
+
},
|
|
935
|
+
});
|
|
936
|
+
}
|
|
876
937
|
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
938
|
+
if (!existingNames.has("Acknowledged")) {
|
|
939
|
+
const acknowledgedIncidentState: IncidentState = new IncidentState();
|
|
940
|
+
acknowledgedIncidentState.name = "Acknowledged";
|
|
941
|
+
acknowledgedIncidentState.description =
|
|
942
|
+
"When an incident is acknowledged, it belongs to this state.";
|
|
943
|
+
acknowledgedIncidentState.color = Yellow;
|
|
944
|
+
acknowledgedIncidentState.isAcknowledgedState = true;
|
|
945
|
+
acknowledgedIncidentState.projectId = projectId;
|
|
946
|
+
acknowledgedIncidentState.order = 2;
|
|
947
|
+
|
|
948
|
+
await IncidentStateService.create({
|
|
949
|
+
data: acknowledgedIncidentState,
|
|
950
|
+
props: {
|
|
951
|
+
isRoot: true,
|
|
952
|
+
},
|
|
953
|
+
});
|
|
954
|
+
}
|
|
892
955
|
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
956
|
+
if (!existingNames.has("Resolved")) {
|
|
957
|
+
const resolvedIncidentState: IncidentState = new IncidentState();
|
|
958
|
+
resolvedIncidentState.name = "Resolved";
|
|
959
|
+
resolvedIncidentState.description =
|
|
960
|
+
"When an incident is resolved, it belongs to this state.";
|
|
961
|
+
resolvedIncidentState.color = Green;
|
|
962
|
+
resolvedIncidentState.isResolvedState = true;
|
|
963
|
+
resolvedIncidentState.projectId = projectId;
|
|
964
|
+
resolvedIncidentState.order = 3;
|
|
965
|
+
|
|
966
|
+
await IncidentStateService.create({
|
|
967
|
+
data: resolvedIncidentState,
|
|
968
|
+
props: {
|
|
969
|
+
isRoot: true,
|
|
970
|
+
},
|
|
971
|
+
});
|
|
972
|
+
}
|
|
908
973
|
|
|
909
974
|
return createdItem;
|
|
910
975
|
}
|
|
911
976
|
|
|
912
977
|
@CaptureSpan()
|
|
913
978
|
public async addDefaultAlertState(createdItem: Model): Promise<Model> {
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
979
|
+
const projectId: ObjectID = createdItem.id!;
|
|
980
|
+
|
|
981
|
+
// Idempotent — see getExistingProjectScopedNames.
|
|
982
|
+
const existingNames: Set<string | undefined> =
|
|
983
|
+
await this.getExistingProjectScopedNames(AlertStateService, projectId);
|
|
984
|
+
|
|
985
|
+
if (!existingNames.has("Identified")) {
|
|
986
|
+
const createdAlertState: AlertState = new AlertState();
|
|
987
|
+
createdAlertState.name = "Identified";
|
|
988
|
+
createdAlertState.description =
|
|
989
|
+
"When an alert is created, it belongs to this state";
|
|
990
|
+
createdAlertState.color = Red;
|
|
991
|
+
createdAlertState.isCreatedState = true;
|
|
992
|
+
createdAlertState.projectId = projectId;
|
|
993
|
+
createdAlertState.order = 1;
|
|
994
|
+
|
|
995
|
+
await AlertStateService.create({
|
|
996
|
+
data: createdAlertState,
|
|
997
|
+
props: {
|
|
998
|
+
isRoot: true,
|
|
999
|
+
},
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
929
1002
|
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
1003
|
+
if (!existingNames.has("Acknowledged")) {
|
|
1004
|
+
const acknowledgedAlertState: AlertState = new AlertState();
|
|
1005
|
+
acknowledgedAlertState.name = "Acknowledged";
|
|
1006
|
+
acknowledgedAlertState.description =
|
|
1007
|
+
"When an alert is acknowledged, it belongs to this state.";
|
|
1008
|
+
acknowledgedAlertState.color = Yellow;
|
|
1009
|
+
acknowledgedAlertState.isAcknowledgedState = true;
|
|
1010
|
+
acknowledgedAlertState.projectId = projectId;
|
|
1011
|
+
acknowledgedAlertState.order = 2;
|
|
1012
|
+
|
|
1013
|
+
await AlertStateService.create({
|
|
1014
|
+
data: acknowledgedAlertState,
|
|
1015
|
+
props: {
|
|
1016
|
+
isRoot: true,
|
|
1017
|
+
},
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
945
1020
|
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1021
|
+
if (!existingNames.has("Resolved")) {
|
|
1022
|
+
const resolvedAlertState: AlertState = new AlertState();
|
|
1023
|
+
resolvedAlertState.name = "Resolved";
|
|
1024
|
+
resolvedAlertState.description =
|
|
1025
|
+
"When an incident is resolved, it belongs to this state.";
|
|
1026
|
+
resolvedAlertState.color = Green;
|
|
1027
|
+
resolvedAlertState.isResolvedState = true;
|
|
1028
|
+
resolvedAlertState.projectId = projectId;
|
|
1029
|
+
resolvedAlertState.order = 3;
|
|
1030
|
+
|
|
1031
|
+
await AlertStateService.create({
|
|
1032
|
+
data: resolvedAlertState,
|
|
1033
|
+
props: {
|
|
1034
|
+
isRoot: true,
|
|
1035
|
+
},
|
|
1036
|
+
});
|
|
1037
|
+
}
|
|
961
1038
|
|
|
962
1039
|
return createdItem;
|
|
963
1040
|
}
|
|
964
1041
|
|
|
965
1042
|
@CaptureSpan()
|
|
966
1043
|
public async addDefaultAlertSeverity(createdItem: Model): Promise<Model> {
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1044
|
+
const projectId: ObjectID = createdItem.id!;
|
|
1045
|
+
|
|
1046
|
+
// Idempotent — see getExistingProjectScopedNames.
|
|
1047
|
+
const existingNames: Set<string | undefined> =
|
|
1048
|
+
await this.getExistingProjectScopedNames(AlertSeverityService, projectId);
|
|
1049
|
+
|
|
1050
|
+
if (!existingNames.has("High")) {
|
|
1051
|
+
const highSeverity: AlertSeverity = new AlertSeverity();
|
|
1052
|
+
highSeverity.name = "High";
|
|
1053
|
+
highSeverity.description =
|
|
1054
|
+
"Issues causing very high impact to customers. Immediate attention is required.";
|
|
1055
|
+
highSeverity.color = Moroon500;
|
|
1056
|
+
highSeverity.projectId = projectId;
|
|
1057
|
+
highSeverity.order = 1;
|
|
1058
|
+
|
|
1059
|
+
await AlertSeverityService.create({
|
|
1060
|
+
data: highSeverity,
|
|
1061
|
+
props: {
|
|
1062
|
+
isRoot: true,
|
|
1063
|
+
},
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
981
1066
|
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1067
|
+
if (!existingNames.has("Low")) {
|
|
1068
|
+
const lowSeverity: AlertSeverity = new AlertSeverity();
|
|
1069
|
+
lowSeverity.name = "Low";
|
|
1070
|
+
lowSeverity.description = "Issues causing low impact to customers.";
|
|
1071
|
+
lowSeverity.color = Yellow500;
|
|
1072
|
+
lowSeverity.projectId = projectId;
|
|
1073
|
+
lowSeverity.order = 2;
|
|
988
1074
|
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1075
|
+
await AlertSeverityService.create({
|
|
1076
|
+
data: lowSeverity,
|
|
1077
|
+
props: {
|
|
1078
|
+
isRoot: true,
|
|
1079
|
+
},
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
995
1082
|
|
|
996
1083
|
return createdItem;
|
|
997
1084
|
}
|
|
998
1085
|
|
|
999
1086
|
private async addDefaultIncidentSeverity(createdItem: Model): Promise<Model> {
|
|
1000
|
-
|
|
1001
|
-
criticalIncident.name = "Critical Incident";
|
|
1002
|
-
criticalIncident.description =
|
|
1003
|
-
"Issues causing very high impact to customers. Immediate response is required. Examples include a full outage, or a data breach.";
|
|
1004
|
-
criticalIncident.color = Moroon500;
|
|
1005
|
-
criticalIncident.projectId = createdItem.id!;
|
|
1006
|
-
criticalIncident.order = 1;
|
|
1007
|
-
|
|
1008
|
-
criticalIncident = await IncidentSeverityService.create({
|
|
1009
|
-
data: criticalIncident,
|
|
1010
|
-
props: {
|
|
1011
|
-
isRoot: true,
|
|
1012
|
-
},
|
|
1013
|
-
});
|
|
1087
|
+
const projectId: ObjectID = createdItem.id!;
|
|
1014
1088
|
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
majorIncident.order = 2;
|
|
1089
|
+
// Idempotent — see getExistingProjectScopedNames.
|
|
1090
|
+
const existingNames: Set<string | undefined> =
|
|
1091
|
+
await this.getExistingProjectScopedNames(
|
|
1092
|
+
IncidentSeverityService,
|
|
1093
|
+
projectId,
|
|
1094
|
+
);
|
|
1022
1095
|
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1096
|
+
if (!existingNames.has("Critical Incident")) {
|
|
1097
|
+
const criticalIncident: IncidentSeverity = new IncidentSeverity();
|
|
1098
|
+
criticalIncident.name = "Critical Incident";
|
|
1099
|
+
criticalIncident.description =
|
|
1100
|
+
"Issues causing very high impact to customers. Immediate response is required. Examples include a full outage, or a data breach.";
|
|
1101
|
+
criticalIncident.color = Moroon500;
|
|
1102
|
+
criticalIncident.projectId = projectId;
|
|
1103
|
+
criticalIncident.order = 1;
|
|
1104
|
+
|
|
1105
|
+
await IncidentSeverityService.create({
|
|
1106
|
+
data: criticalIncident,
|
|
1107
|
+
props: {
|
|
1108
|
+
isRoot: true,
|
|
1109
|
+
},
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1029
1112
|
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1113
|
+
if (!existingNames.has("Major Incident")) {
|
|
1114
|
+
const majorIncident: IncidentSeverity = new IncidentSeverity();
|
|
1115
|
+
majorIncident.name = "Major Incident";
|
|
1116
|
+
majorIncident.description =
|
|
1117
|
+
"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.";
|
|
1118
|
+
majorIncident.color = Red;
|
|
1119
|
+
majorIncident.projectId = projectId;
|
|
1120
|
+
majorIncident.order = 2;
|
|
1121
|
+
|
|
1122
|
+
await IncidentSeverityService.create({
|
|
1123
|
+
data: majorIncident,
|
|
1124
|
+
props: {
|
|
1125
|
+
isRoot: true,
|
|
1126
|
+
},
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1037
1129
|
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1130
|
+
if (!existingNames.has("Minor Incident")) {
|
|
1131
|
+
const minorIncident: IncidentSeverity = new IncidentSeverity();
|
|
1132
|
+
minorIncident.name = "Minor Incident";
|
|
1133
|
+
minorIncident.description =
|
|
1134
|
+
"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.";
|
|
1135
|
+
minorIncident.color = Yellow;
|
|
1136
|
+
minorIncident.projectId = projectId;
|
|
1137
|
+
minorIncident.order = 3;
|
|
1138
|
+
|
|
1139
|
+
await IncidentSeverityService.create({
|
|
1140
|
+
data: minorIncident,
|
|
1141
|
+
props: {
|
|
1142
|
+
isRoot: true,
|
|
1143
|
+
},
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1044
1146
|
|
|
1045
1147
|
return createdItem;
|
|
1046
1148
|
}
|
|
1047
1149
|
|
|
1048
1150
|
public async addDefaultIncidentRoles(createdItem: Model): Promise<Model> {
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
responder = await IncidentRoleService.create({
|
|
1075
|
-
data: responder,
|
|
1076
|
-
props: {
|
|
1077
|
-
isRoot: true,
|
|
1078
|
-
},
|
|
1079
|
-
});
|
|
1151
|
+
const projectId: ObjectID = createdItem.id!;
|
|
1152
|
+
|
|
1153
|
+
// Idempotent — see getExistingProjectScopedNames.
|
|
1154
|
+
const existingNames: Set<string | undefined> =
|
|
1155
|
+
await this.getExistingProjectScopedNames(IncidentRoleService, projectId);
|
|
1156
|
+
|
|
1157
|
+
if (!existingNames.has("Incident Commander")) {
|
|
1158
|
+
const incidentCommander: IncidentRole = new IncidentRole();
|
|
1159
|
+
incidentCommander.name = "Incident Commander";
|
|
1160
|
+
incidentCommander.description =
|
|
1161
|
+
"Primary decision maker during an incident. Responsible for coordinating the response and making final decisions.";
|
|
1162
|
+
incidentCommander.color = Purple500;
|
|
1163
|
+
incidentCommander.roleIcon = IconProp.ShieldCheck;
|
|
1164
|
+
incidentCommander.projectId = projectId;
|
|
1165
|
+
incidentCommander.isPrimaryRole = true;
|
|
1166
|
+
incidentCommander.isDeleteable = false;
|
|
1167
|
+
|
|
1168
|
+
await IncidentRoleService.create({
|
|
1169
|
+
data: incidentCommander,
|
|
1170
|
+
props: {
|
|
1171
|
+
isRoot: true,
|
|
1172
|
+
},
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1080
1175
|
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1176
|
+
if (!existingNames.has("Responder")) {
|
|
1177
|
+
const responder: IncidentRole = new IncidentRole();
|
|
1178
|
+
responder.name = "Responder";
|
|
1179
|
+
responder.description =
|
|
1180
|
+
"Active participant in incident resolution. Performs hands-on work to resolve the incident.";
|
|
1181
|
+
responder.color = Blue500;
|
|
1182
|
+
responder.roleIcon = IconProp.Wrench;
|
|
1183
|
+
responder.projectId = projectId;
|
|
1184
|
+
|
|
1185
|
+
await IncidentRoleService.create({
|
|
1186
|
+
data: responder,
|
|
1187
|
+
props: {
|
|
1188
|
+
isRoot: true,
|
|
1189
|
+
},
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1088
1192
|
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1193
|
+
if (!existingNames.has("Communications Lead")) {
|
|
1194
|
+
const communicationsLead: IncidentRole = new IncidentRole();
|
|
1195
|
+
communicationsLead.name = "Communications Lead";
|
|
1196
|
+
communicationsLead.description =
|
|
1197
|
+
"Handles stakeholder communication and status updates during an incident.";
|
|
1198
|
+
communicationsLead.color = Teal500;
|
|
1199
|
+
communicationsLead.roleIcon = IconProp.Announcement;
|
|
1200
|
+
communicationsLead.projectId = projectId;
|
|
1201
|
+
|
|
1202
|
+
await IncidentRoleService.create({
|
|
1203
|
+
data: communicationsLead,
|
|
1204
|
+
props: {
|
|
1205
|
+
isRoot: true,
|
|
1206
|
+
},
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1095
1209
|
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1210
|
+
if (!existingNames.has("Observer")) {
|
|
1211
|
+
const observer: IncidentRole = new IncidentRole();
|
|
1212
|
+
observer.name = "Observer";
|
|
1213
|
+
observer.description =
|
|
1214
|
+
"Read-only participant who monitors the incident without active involvement.";
|
|
1215
|
+
observer.color = Gray500;
|
|
1216
|
+
observer.roleIcon = IconProp.Activity;
|
|
1217
|
+
observer.projectId = projectId;
|
|
1218
|
+
observer.canAssignMultipleUsers = true;
|
|
1219
|
+
|
|
1220
|
+
await IncidentRoleService.create({
|
|
1221
|
+
data: observer,
|
|
1222
|
+
props: {
|
|
1223
|
+
isRoot: true,
|
|
1224
|
+
},
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1111
1227
|
|
|
1112
1228
|
return createdItem;
|
|
1113
1229
|
}
|
|
1114
1230
|
|
|
1115
1231
|
private async addDefaultMonitorStatus(createdItem: Model): Promise<Model> {
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
degradedStatus = await MonitorStatusService.create({
|
|
1139
|
-
data: degradedStatus,
|
|
1140
|
-
props: {
|
|
1141
|
-
isRoot: true,
|
|
1142
|
-
},
|
|
1143
|
-
});
|
|
1232
|
+
const projectId: ObjectID = createdItem.id!;
|
|
1233
|
+
|
|
1234
|
+
// Idempotent — see getExistingProjectScopedNames.
|
|
1235
|
+
const existingNames: Set<string | undefined> =
|
|
1236
|
+
await this.getExistingProjectScopedNames(MonitorStatusService, projectId);
|
|
1237
|
+
|
|
1238
|
+
if (!existingNames.has("Operational")) {
|
|
1239
|
+
const operationalStatus: MonitorStatus = new MonitorStatus();
|
|
1240
|
+
operationalStatus.name = "Operational";
|
|
1241
|
+
operationalStatus.description = "Monitor operating normally";
|
|
1242
|
+
operationalStatus.projectId = projectId;
|
|
1243
|
+
operationalStatus.priority = 1;
|
|
1244
|
+
operationalStatus.isOperationalState = true;
|
|
1245
|
+
operationalStatus.color = Green;
|
|
1246
|
+
|
|
1247
|
+
await MonitorStatusService.create({
|
|
1248
|
+
data: operationalStatus,
|
|
1249
|
+
props: {
|
|
1250
|
+
isRoot: true,
|
|
1251
|
+
},
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1144
1254
|
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1255
|
+
if (!existingNames.has("Degraded")) {
|
|
1256
|
+
const degradedStatus: MonitorStatus = new MonitorStatus();
|
|
1257
|
+
degradedStatus.name = "Degraded";
|
|
1258
|
+
degradedStatus.description =
|
|
1259
|
+
"Monitor is operating at reduced performance.";
|
|
1260
|
+
degradedStatus.priority = 2;
|
|
1261
|
+
degradedStatus.projectId = projectId;
|
|
1262
|
+
degradedStatus.color = Yellow;
|
|
1263
|
+
|
|
1264
|
+
await MonitorStatusService.create({
|
|
1265
|
+
data: degradedStatus,
|
|
1266
|
+
props: {
|
|
1267
|
+
isRoot: true,
|
|
1268
|
+
},
|
|
1269
|
+
});
|
|
1270
|
+
}
|
|
1152
1271
|
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1272
|
+
if (!existingNames.has("Offline")) {
|
|
1273
|
+
const downStatus: MonitorStatus = new MonitorStatus();
|
|
1274
|
+
downStatus.name = "Offline";
|
|
1275
|
+
downStatus.description = "Monitor is offline.";
|
|
1276
|
+
downStatus.isOfflineState = true;
|
|
1277
|
+
downStatus.projectId = projectId;
|
|
1278
|
+
downStatus.priority = 3;
|
|
1279
|
+
downStatus.color = Red;
|
|
1280
|
+
|
|
1281
|
+
await MonitorStatusService.create({
|
|
1282
|
+
data: downStatus,
|
|
1283
|
+
props: {
|
|
1284
|
+
isRoot: true,
|
|
1285
|
+
},
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1159
1288
|
|
|
1160
1289
|
return createdItem;
|
|
1161
1290
|
}
|