@oneuptime/common 7.0.3338 → 7.0.3341

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.
@@ -22,6 +22,8 @@ export enum MetricPointType {
22
22
  export enum ServiceType {
23
23
  OpenTelemetry = "OpenTelemetry",
24
24
  Monitor = "Monitor",
25
+ Alert = "Alert",
26
+ Incident = "Incident",
25
27
  }
26
28
 
27
29
  export default class Metric extends AnalyticsBaseModel {
@@ -28,6 +28,16 @@ import AlertState from "Common/Models/DatabaseModels/AlertState";
28
28
  import AlertStateTimeline from "Common/Models/DatabaseModels/AlertStateTimeline";
29
29
  import User from "Common/Models/DatabaseModels/User";
30
30
  import { IsBillingEnabled } from "../EnvironmentConfig";
31
+ import TelemetryType from "../../Types/Telemetry/TelemetryType";
32
+ import logger from "../Utils/Logger";
33
+ import TelemetryUtil from "../Utils/Telemetry/Telemetry";
34
+ import MetricService from "./MetricService";
35
+ import OneUptimeDate from "../../Types/Date";
36
+ import Metric, {
37
+ MetricPointType,
38
+ ServiceType,
39
+ } from "../../Models/AnalyticsModels/Metric";
40
+ import AlertMetricType from "../../Types/Alerts/AlertMetricType";
31
41
 
32
42
  export class Service extends DatabaseService<Model> {
33
43
  public constructor() {
@@ -564,5 +574,265 @@ export class Service extends DatabaseService<Model> {
564
574
  props: props || {},
565
575
  });
566
576
  }
577
+
578
+ public async refreshAlertMetrics(data: { alertId: ObjectID }): Promise<void> {
579
+ const alert: Model | null = await this.findOneById({
580
+ id: data.alertId,
581
+ select: {
582
+ projectId: true,
583
+ monitor: {
584
+ _id: true,
585
+ name: true,
586
+ },
587
+ alertSeverity: {
588
+ name: true,
589
+ _id: true,
590
+ },
591
+ },
592
+ props: {
593
+ isRoot: true,
594
+ },
595
+ });
596
+
597
+ if (!alert) {
598
+ throw new BadDataException("Alert not found");
599
+ }
600
+
601
+ if (!alert.projectId) {
602
+ throw new BadDataException("Incient Project ID not found");
603
+ }
604
+
605
+ // get alert state timeline
606
+
607
+ const alertStateTimelines: Array<AlertStateTimeline> =
608
+ await AlertStateTimelineService.findBy({
609
+ query: {
610
+ alertId: data.alertId,
611
+ },
612
+ select: {
613
+ projectId: true,
614
+ alertStateId: true,
615
+ alertState: {
616
+ isAcknowledgedState: true,
617
+ isResolvedState: true,
618
+ },
619
+ startsAt: true,
620
+ endsAt: true,
621
+ },
622
+ sort: {
623
+ startsAt: SortOrder.Ascending,
624
+ },
625
+ skip: 0,
626
+ limit: LIMIT_PER_PROJECT,
627
+ props: {
628
+ isRoot: true,
629
+ },
630
+ });
631
+
632
+ const firstAlertStateTimeline: AlertStateTimeline | undefined =
633
+ alertStateTimelines[0];
634
+
635
+ // delete all the alert metrics with this alert id because its a refresh.
636
+
637
+ await MetricService.deleteBy({
638
+ query: {
639
+ serviceId: data.alertId,
640
+ },
641
+ props: {
642
+ isRoot: true,
643
+ },
644
+ });
645
+
646
+ const itemsToSave: Array<Metric> = [];
647
+
648
+ // now we need to create new metrics for this alert - TimeToAcknowledge, TimeToResolve, AlertCount, AlertDuration
649
+
650
+ const alertStartsAt: Date =
651
+ firstAlertStateTimeline?.startsAt ||
652
+ alert.createdAt ||
653
+ OneUptimeDate.getCurrentDate();
654
+
655
+ const alertCountMetric: Metric = new Metric();
656
+
657
+ alertCountMetric.projectId = alert.projectId;
658
+ alertCountMetric.serviceId = alert.id!;
659
+ alertCountMetric.serviceType = ServiceType.Alert;
660
+ alertCountMetric.name = AlertMetricType.AlertCount;
661
+ alertCountMetric.description = "Number of alerts created";
662
+ alertCountMetric.value = 1;
663
+ alertCountMetric.unit = "";
664
+ alertCountMetric.attributes = {
665
+ alertId: data.alertId.toString(),
666
+ projectId: alert.projectId.toString(),
667
+ monitorId: alert.monitor?.id!.toString() || "",
668
+ monitorName: alert.monitor?.name!.toString() || "",
669
+ alertSeverityId: alert.alertSeverity?.id!.toString() || "",
670
+ alertSeverityName: alert.alertSeverity?.name!.toString() || "",
671
+ };
672
+
673
+ alertCountMetric.time = alertStartsAt;
674
+ alertCountMetric.timeUnixNano = OneUptimeDate.toUnixNano(
675
+ alertCountMetric.time,
676
+ );
677
+ alertCountMetric.metricPointType = MetricPointType.Sum;
678
+
679
+ itemsToSave.push(alertCountMetric);
680
+
681
+ // is the alert acknowledged?
682
+ const isAlertAcknowledged: boolean = alertStateTimelines.some(
683
+ (timeline: AlertStateTimeline) => {
684
+ return timeline.alertState?.isAcknowledgedState;
685
+ },
686
+ );
687
+
688
+ if (isAlertAcknowledged) {
689
+ const ackAlertStateTimeline: AlertStateTimeline | undefined =
690
+ alertStateTimelines.find((timeline: AlertStateTimeline) => {
691
+ return timeline.alertState?.isAcknowledgedState;
692
+ });
693
+
694
+ if (ackAlertStateTimeline) {
695
+ const timeToAcknowledgeMetric: Metric = new Metric();
696
+
697
+ timeToAcknowledgeMetric.projectId = alert.projectId;
698
+ timeToAcknowledgeMetric.serviceId = alert.id!;
699
+ timeToAcknowledgeMetric.serviceType = ServiceType.Alert;
700
+ timeToAcknowledgeMetric.name = AlertMetricType.TimeToAcknowledge;
701
+ timeToAcknowledgeMetric.description =
702
+ "Time taken to acknowledge the alert";
703
+ timeToAcknowledgeMetric.value = OneUptimeDate.getDifferenceInSeconds(
704
+ ackAlertStateTimeline?.startsAt || OneUptimeDate.getCurrentDate(),
705
+ alertStartsAt,
706
+ );
707
+ timeToAcknowledgeMetric.unit = "seconds";
708
+ timeToAcknowledgeMetric.attributes = {
709
+ alertId: data.alertId.toString(),
710
+ projectId: alert.projectId.toString(),
711
+ monitorId: alert.monitor?.id!.toString() || "",
712
+ monitorName: alert.monitor?.name!.toString() || "",
713
+ alertSeverityId: alert.alertSeverity?.id!.toString() || "",
714
+ alertSeverityName: alert.alertSeverity?.name!.toString() || "",
715
+ };
716
+
717
+ timeToAcknowledgeMetric.time =
718
+ ackAlertStateTimeline?.startsAt ||
719
+ alert.createdAt ||
720
+ OneUptimeDate.getCurrentDate();
721
+ timeToAcknowledgeMetric.timeUnixNano = OneUptimeDate.toUnixNano(
722
+ timeToAcknowledgeMetric.time,
723
+ );
724
+ timeToAcknowledgeMetric.metricPointType = MetricPointType.Sum;
725
+
726
+ itemsToSave.push(timeToAcknowledgeMetric);
727
+ }
728
+ }
729
+
730
+ // time to resolve
731
+ const isAlertResolved: boolean = alertStateTimelines.some(
732
+ (timeline: AlertStateTimeline) => {
733
+ return timeline.alertState?.isResolvedState;
734
+ },
735
+ );
736
+
737
+ if (isAlertResolved) {
738
+ const resolvedAlertStateTimeline: AlertStateTimeline | undefined =
739
+ alertStateTimelines.find((timeline: AlertStateTimeline) => {
740
+ return timeline.alertState?.isResolvedState;
741
+ });
742
+
743
+ if (resolvedAlertStateTimeline) {
744
+ const timeToResolveMetric: Metric = new Metric();
745
+
746
+ timeToResolveMetric.projectId = alert.projectId;
747
+ timeToResolveMetric.serviceId = alert.id!;
748
+ timeToResolveMetric.serviceType = ServiceType.Alert;
749
+ timeToResolveMetric.name = AlertMetricType.TimeToResolve;
750
+ timeToResolveMetric.description = "Time taken to resolve the alert";
751
+ timeToResolveMetric.value = OneUptimeDate.getDifferenceInSeconds(
752
+ resolvedAlertStateTimeline?.startsAt ||
753
+ OneUptimeDate.getCurrentDate(),
754
+ alertStartsAt,
755
+ );
756
+ timeToResolveMetric.unit = "seconds";
757
+ timeToResolveMetric.attributes = {
758
+ alertId: data.alertId.toString(),
759
+ projectId: alert.projectId.toString(),
760
+ monitorId: alert.monitor?.id!.toString() || "",
761
+ monitorName: alert.monitor?.name!.toString() || "",
762
+ alertSeverityId: alert.alertSeverity?.id!.toString() || "",
763
+ alertSeverityName: alert.alertSeverity?.name!.toString() || "",
764
+ };
765
+
766
+ timeToResolveMetric.time =
767
+ resolvedAlertStateTimeline?.startsAt ||
768
+ alert.createdAt ||
769
+ OneUptimeDate.getCurrentDate();
770
+ timeToResolveMetric.timeUnixNano = OneUptimeDate.toUnixNano(
771
+ timeToResolveMetric.time,
772
+ );
773
+ timeToResolveMetric.metricPointType = MetricPointType.Sum;
774
+
775
+ itemsToSave.push(timeToResolveMetric);
776
+ }
777
+ }
778
+
779
+ // alert duration
780
+
781
+ const alertDurationMetric: Metric = new Metric();
782
+
783
+ const lastAlertStateTimeline: AlertStateTimeline | undefined =
784
+ alertStateTimelines[alertStateTimelines.length - 1];
785
+
786
+ if (lastAlertStateTimeline) {
787
+ const alertEndsAt: Date =
788
+ lastAlertStateTimeline.startsAt || OneUptimeDate.getCurrentDate();
789
+
790
+ // save metric.
791
+
792
+ alertDurationMetric.projectId = alert.projectId;
793
+ alertDurationMetric.serviceId = alert.id!;
794
+ alertDurationMetric.serviceType = ServiceType.Alert;
795
+ alertDurationMetric.name = AlertMetricType.AlertDuration;
796
+ alertDurationMetric.description = "Duration of the alert";
797
+ alertDurationMetric.value = OneUptimeDate.getDifferenceInSeconds(
798
+ alertEndsAt,
799
+ alertStartsAt,
800
+ );
801
+ alertDurationMetric.unit = "seconds";
802
+ alertDurationMetric.attributes = {
803
+ alertId: data.alertId.toString(),
804
+ projectId: alert.projectId.toString(),
805
+ monitorId: alert.monitor?.id!.toString() || "",
806
+ monitorName: alert.monitor?.name!.toString() || "",
807
+ alertSeverityId: alert.alertSeverity?.id!.toString() || "",
808
+ alertSeverityName: alert.alertSeverity?.name!.toString() || "",
809
+ };
810
+
811
+ alertDurationMetric.time =
812
+ lastAlertStateTimeline?.startsAt ||
813
+ alert.createdAt ||
814
+ OneUptimeDate.getCurrentDate();
815
+ alertDurationMetric.timeUnixNano = OneUptimeDate.toUnixNano(
816
+ alertDurationMetric.time,
817
+ );
818
+ alertDurationMetric.metricPointType = MetricPointType.Sum;
819
+ }
820
+
821
+ await MetricService.createMany({
822
+ items: itemsToSave,
823
+ props: {
824
+ isRoot: true,
825
+ },
826
+ });
827
+
828
+ // index attributes.
829
+ TelemetryUtil.indexAttributes({
830
+ attributes: ["monitorId", "projectId", "alertId", "monitorNames"],
831
+ projectId: alert.projectId,
832
+ telemetryType: TelemetryType.Metric,
833
+ }).catch((err: Error) => {
834
+ logger.error(err);
835
+ });
836
+ }
567
837
  }
568
838
  export default new Service();
@@ -18,6 +18,7 @@ import { IsBillingEnabled } from "../EnvironmentConfig";
18
18
  import { JSONObject } from "../../Types/JSON";
19
19
  import AlertInternalNote from "../../Models/DatabaseModels/AlertInternalNote";
20
20
  import AlertInternalNoteService from "./AlertInternalNoteService";
21
+ import logger from "../Utils/Logger";
21
22
 
22
23
  export class Service extends DatabaseService<AlertStateTimeline> {
23
24
  public constructor() {
@@ -173,6 +174,15 @@ export class Service extends DatabaseService<AlertStateTimeline> {
173
174
  props: onCreate.createBy.props,
174
175
  });
175
176
 
177
+ AlertService.refreshAlertMetrics({
178
+ alertId: createdItem.alertId,
179
+ }).catch((error: Error) => {
180
+ logger.error(
181
+ "Error while refreshing alert metrics after alert state timeline creation",
182
+ );
183
+ logger.error(error);
184
+ });
185
+
176
186
  return createdItem;
177
187
  }
178
188
 
@@ -34,6 +34,16 @@ import MonitorStatus from "Common/Models/DatabaseModels/MonitorStatus";
34
34
  import MonitorStatusTimeline from "Common/Models/DatabaseModels/MonitorStatusTimeline";
35
35
  import User from "Common/Models/DatabaseModels/User";
36
36
  import { IsBillingEnabled } from "../EnvironmentConfig";
37
+ import MetricService from "./MetricService";
38
+ import IncidentMetricType from "../../Types/Incident/IncidentMetricType";
39
+ import Metric, {
40
+ MetricPointType,
41
+ ServiceType,
42
+ } from "../../Models/AnalyticsModels/Metric";
43
+ import OneUptimeDate from "../../Types/Date";
44
+ import TelemetryUtil from "../Utils/Telemetry/Telemetry";
45
+ import TelemetryType from "../../Types/Telemetry/TelemetryType";
46
+ import logger from "../Utils/Logger";
37
47
 
38
48
  export class Service extends DatabaseService<Model> {
39
49
  public constructor() {
@@ -139,6 +149,8 @@ export class Service extends DatabaseService<Model> {
139
149
  isRoot: true,
140
150
  },
141
151
  });
152
+
153
+ // store incident metric
142
154
  }
143
155
 
144
156
  protected override async onBeforeCreate(
@@ -737,5 +749,291 @@ export class Service extends DatabaseService<Model> {
737
749
  props: props || {},
738
750
  });
739
751
  }
752
+
753
+ public async refreshIncidentMetrics(data: {
754
+ incidentId: ObjectID;
755
+ }): Promise<void> {
756
+ const incident: Model | null = await this.findOneById({
757
+ id: data.incidentId,
758
+ select: {
759
+ projectId: true,
760
+ monitors: {
761
+ _id: true,
762
+ name: true,
763
+ },
764
+ incidentSeverity: {
765
+ _id: true,
766
+ name: true,
767
+ },
768
+ },
769
+ props: {
770
+ isRoot: true,
771
+ },
772
+ });
773
+
774
+ if (!incident) {
775
+ throw new BadDataException("Incident not found");
776
+ }
777
+
778
+ if (!incident.projectId) {
779
+ throw new BadDataException("Incient Project ID not found");
780
+ }
781
+
782
+ // get incident state timeline
783
+
784
+ const incidentStateTimelines: Array<IncidentStateTimeline> =
785
+ await IncidentStateTimelineService.findBy({
786
+ query: {
787
+ incidentId: data.incidentId,
788
+ },
789
+ select: {
790
+ projectId: true,
791
+ incidentStateId: true,
792
+ incidentState: {
793
+ isAcknowledgedState: true,
794
+ isResolvedState: true,
795
+ },
796
+ startsAt: true,
797
+ endsAt: true,
798
+ },
799
+ sort: {
800
+ startsAt: SortOrder.Ascending,
801
+ },
802
+ skip: 0,
803
+ limit: LIMIT_PER_PROJECT,
804
+ props: {
805
+ isRoot: true,
806
+ },
807
+ });
808
+
809
+ const firstIncidentStateTimeline: IncidentStateTimeline | undefined =
810
+ incidentStateTimelines[0];
811
+
812
+ // delete all the incident metrics with this incident id because its a refresh.
813
+
814
+ await MetricService.deleteBy({
815
+ query: {
816
+ serviceId: data.incidentId,
817
+ },
818
+ props: {
819
+ isRoot: true,
820
+ },
821
+ });
822
+
823
+ const itemsToSave: Array<Metric> = [];
824
+
825
+ // now we need to create new metrics for this incident - TimeToAcknowledge, TimeToResolve, IncidentCount, IncidentDuration
826
+
827
+ const incidentStartsAt: Date =
828
+ firstIncidentStateTimeline?.startsAt ||
829
+ incident.createdAt ||
830
+ OneUptimeDate.getCurrentDate();
831
+
832
+ const incidentCountMetric: Metric = new Metric();
833
+
834
+ incidentCountMetric.projectId = incident.projectId;
835
+ incidentCountMetric.serviceId = incident.id!;
836
+ incidentCountMetric.serviceType = ServiceType.Incident;
837
+ incidentCountMetric.name = IncidentMetricType.IncidentCount;
838
+ incidentCountMetric.description = "Number of incidents created";
839
+ incidentCountMetric.value = 1;
840
+ incidentCountMetric.unit = "";
841
+ incidentCountMetric.attributes = {
842
+ incidentId: data.incidentId.toString(),
843
+ projectId: incident.projectId.toString(),
844
+ monitorIds:
845
+ incident.monitors?.map((monitor: Monitor) => {
846
+ return monitor._id?.toString();
847
+ }) || [],
848
+ monitorNames:
849
+ incident.monitors?.map((monitor: Monitor) => {
850
+ return monitor.name?.toString();
851
+ }) || [],
852
+ incidentSeverityId: incident.incidentSeverity?._id?.toString(),
853
+ incidentSeverityName: incident.incidentSeverity?.name?.toString(),
854
+ };
855
+
856
+ incidentCountMetric.time = incidentStartsAt;
857
+ incidentCountMetric.timeUnixNano = OneUptimeDate.toUnixNano(
858
+ incidentCountMetric.time,
859
+ );
860
+ incidentCountMetric.metricPointType = MetricPointType.Sum;
861
+
862
+ itemsToSave.push(incidentCountMetric);
863
+
864
+ // is the incident acknowledged?
865
+ const isIncidentAcknowledged: boolean = incidentStateTimelines.some(
866
+ (timeline: IncidentStateTimeline) => {
867
+ return timeline.incidentState?.isAcknowledgedState;
868
+ },
869
+ );
870
+
871
+ if (isIncidentAcknowledged) {
872
+ const ackIncidentStateTimeline: IncidentStateTimeline | undefined =
873
+ incidentStateTimelines.find((timeline: IncidentStateTimeline) => {
874
+ return timeline.incidentState?.isAcknowledgedState;
875
+ });
876
+
877
+ if (ackIncidentStateTimeline) {
878
+ const timeToAcknowledgeMetric: Metric = new Metric();
879
+
880
+ timeToAcknowledgeMetric.projectId = incident.projectId;
881
+ timeToAcknowledgeMetric.serviceId = incident.id!;
882
+ timeToAcknowledgeMetric.serviceType = ServiceType.Incident;
883
+ timeToAcknowledgeMetric.name = IncidentMetricType.TimeToAcknowledge;
884
+ timeToAcknowledgeMetric.description =
885
+ "Time taken to acknowledge the incident";
886
+ timeToAcknowledgeMetric.value = OneUptimeDate.getDifferenceInSeconds(
887
+ ackIncidentStateTimeline?.startsAt || OneUptimeDate.getCurrentDate(),
888
+ incidentStartsAt,
889
+ );
890
+ timeToAcknowledgeMetric.unit = "seconds";
891
+ timeToAcknowledgeMetric.attributes = {
892
+ incidentId: data.incidentId.toString(),
893
+ projectId: incident.projectId.toString(),
894
+ monitorIds:
895
+ incident.monitors?.map((monitor: Monitor) => {
896
+ return monitor._id?.toString();
897
+ }) || [],
898
+ monitorNames:
899
+ incident.monitors?.map((monitor: Monitor) => {
900
+ return monitor.name?.toString();
901
+ }) || [],
902
+ incidentSeverityId: incident.incidentSeverity?._id?.toString(),
903
+ incidentSeverityName: incident.incidentSeverity?.name?.toString(),
904
+ };
905
+
906
+ timeToAcknowledgeMetric.time =
907
+ ackIncidentStateTimeline?.startsAt ||
908
+ incident.createdAt ||
909
+ OneUptimeDate.getCurrentDate();
910
+ timeToAcknowledgeMetric.timeUnixNano = OneUptimeDate.toUnixNano(
911
+ timeToAcknowledgeMetric.time,
912
+ );
913
+ timeToAcknowledgeMetric.metricPointType = MetricPointType.Sum;
914
+
915
+ itemsToSave.push(timeToAcknowledgeMetric);
916
+ }
917
+ }
918
+
919
+ // time to resolve
920
+ const isIncidentResolved: boolean = incidentStateTimelines.some(
921
+ (timeline: IncidentStateTimeline) => {
922
+ return timeline.incidentState?.isResolvedState;
923
+ },
924
+ );
925
+
926
+ if (isIncidentResolved) {
927
+ const resolvedIncidentStateTimeline: IncidentStateTimeline | undefined =
928
+ incidentStateTimelines.find((timeline: IncidentStateTimeline) => {
929
+ return timeline.incidentState?.isResolvedState;
930
+ });
931
+
932
+ if (resolvedIncidentStateTimeline) {
933
+ const timeToResolveMetric: Metric = new Metric();
934
+
935
+ timeToResolveMetric.projectId = incident.projectId;
936
+ timeToResolveMetric.serviceId = incident.id!;
937
+ timeToResolveMetric.serviceType = ServiceType.Incident;
938
+ timeToResolveMetric.name = IncidentMetricType.TimeToResolve;
939
+ timeToResolveMetric.description = "Time taken to resolve the incident";
940
+ timeToResolveMetric.value = OneUptimeDate.getDifferenceInSeconds(
941
+ resolvedIncidentStateTimeline?.startsAt ||
942
+ OneUptimeDate.getCurrentDate(),
943
+ incidentStartsAt,
944
+ );
945
+ timeToResolveMetric.unit = "seconds";
946
+ timeToResolveMetric.attributes = {
947
+ incidentId: data.incidentId.toString(),
948
+ projectId: incident.projectId.toString(),
949
+ monitorIds:
950
+ incident.monitors?.map((monitor: Monitor) => {
951
+ return monitor._id?.toString();
952
+ }) || [],
953
+ monitorNames:
954
+ incident.monitors?.map((monitor: Monitor) => {
955
+ return monitor.name?.toString();
956
+ }) || [],
957
+ incidentSeverityId: incident.incidentSeverity?._id?.toString(),
958
+ incidentSeverityName: incident.incidentSeverity?.name?.toString(),
959
+ };
960
+
961
+ timeToResolveMetric.time =
962
+ resolvedIncidentStateTimeline?.startsAt ||
963
+ incident.createdAt ||
964
+ OneUptimeDate.getCurrentDate();
965
+ timeToResolveMetric.timeUnixNano = OneUptimeDate.toUnixNano(
966
+ timeToResolveMetric.time,
967
+ );
968
+ timeToResolveMetric.metricPointType = MetricPointType.Sum;
969
+
970
+ itemsToSave.push(timeToResolveMetric);
971
+ }
972
+ }
973
+
974
+ // incident duration
975
+
976
+ const incidentDurationMetric: Metric = new Metric();
977
+
978
+ const lastIncidentStateTimeline: IncidentStateTimeline | undefined =
979
+ incidentStateTimelines[incidentStateTimelines.length - 1];
980
+
981
+ if (lastIncidentStateTimeline) {
982
+ const incidentEndsAt: Date =
983
+ lastIncidentStateTimeline.startsAt || OneUptimeDate.getCurrentDate();
984
+
985
+ // save metric.
986
+
987
+ incidentDurationMetric.projectId = incident.projectId;
988
+ incidentDurationMetric.serviceId = incident.id!;
989
+ incidentDurationMetric.serviceType = ServiceType.Incident;
990
+ incidentDurationMetric.name = IncidentMetricType.IncidentDuration;
991
+ incidentDurationMetric.description = "Duration of the incident";
992
+ incidentDurationMetric.value = OneUptimeDate.getDifferenceInSeconds(
993
+ incidentEndsAt,
994
+ incidentStartsAt,
995
+ );
996
+ incidentDurationMetric.unit = "seconds";
997
+ incidentDurationMetric.attributes = {
998
+ incidentId: data.incidentId.toString(),
999
+ projectId: incident.projectId.toString(),
1000
+ monitorIds:
1001
+ incident.monitors?.map((monitor: Monitor) => {
1002
+ return monitor._id?.toString();
1003
+ }) || [],
1004
+ monitorNames:
1005
+ incident.monitors?.map((monitor: Monitor) => {
1006
+ return monitor.name?.toString();
1007
+ }) || [],
1008
+ incidentSeverityId: incident.incidentSeverity?._id?.toString(),
1009
+ incidentSeverityName: incident.incidentSeverity?.name?.toString(),
1010
+ };
1011
+
1012
+ incidentDurationMetric.time =
1013
+ lastIncidentStateTimeline?.startsAt ||
1014
+ incident.createdAt ||
1015
+ OneUptimeDate.getCurrentDate();
1016
+ incidentDurationMetric.timeUnixNano = OneUptimeDate.toUnixNano(
1017
+ incidentDurationMetric.time,
1018
+ );
1019
+ incidentDurationMetric.metricPointType = MetricPointType.Sum;
1020
+ }
1021
+
1022
+ await MetricService.createMany({
1023
+ items: itemsToSave,
1024
+ props: {
1025
+ isRoot: true,
1026
+ },
1027
+ });
1028
+
1029
+ // index attributes.
1030
+ TelemetryUtil.indexAttributes({
1031
+ attributes: ["monitorIds", "projectId", "incidentId", "monitorNames"],
1032
+ projectId: incident.projectId,
1033
+ telemetryType: TelemetryType.Metric,
1034
+ }).catch((err: Error) => {
1035
+ logger.error(err);
1036
+ });
1037
+ }
740
1038
  }
741
1039
  export default new Service();
@@ -19,6 +19,7 @@ import IncidentState from "Common/Models/DatabaseModels/IncidentState";
19
19
  import IncidentStateTimeline from "Common/Models/DatabaseModels/IncidentStateTimeline";
20
20
  import User from "Common/Models/DatabaseModels/User";
21
21
  import { IsBillingEnabled } from "../EnvironmentConfig";
22
+ import logger from "../Utils/Logger";
22
23
 
23
24
  export class Service extends DatabaseService<IncidentStateTimeline> {
24
25
  public constructor() {
@@ -227,6 +228,13 @@ export class Service extends DatabaseService<IncidentStateTimeline> {
227
228
  }
228
229
  }
229
230
 
231
+ IncidentService.refreshIncidentMetrics({
232
+ incidentId: createdItem.incidentId,
233
+ }).catch((error: Error) => {
234
+ logger.error(`Error while refreshing incident metrics:`);
235
+ logger.error(error);
236
+ });
237
+
230
238
  return createdItem;
231
239
  }
232
240
 
@@ -0,0 +1,8 @@
1
+ enum AlertMetricType {
2
+ TimeToAcknowledge = "oneuptime.alert.time-to-acknowledge",
3
+ TimeToResolve = "oneuptime.alert.time-to-resolve",
4
+ AlertCount = "oneuptime.alert.count",
5
+ AlertDuration = "oneuptime.alert.duration",
6
+ }
7
+
8
+ export default AlertMetricType;
package/Types/Date.ts CHANGED
@@ -845,6 +845,18 @@ export default class OneUptimeDate {
845
845
  return minutes;
846
846
  }
847
847
 
848
+ public static getDifferenceInSeconds(date: Date, date2: Date): number {
849
+ date = this.fromString(date);
850
+ date2 = this.fromString(date2);
851
+ const seconds: number = moment(date).diff(moment(date2), "seconds");
852
+
853
+ if (seconds < 0) {
854
+ return seconds * -1;
855
+ }
856
+
857
+ return seconds;
858
+ }
859
+
848
860
  public static getDifferenceInMonths(date: Date, date2: Date): number {
849
861
  date = this.fromString(date);
850
862
  date2 = this.fromString(date2);
@@ -0,0 +1,8 @@
1
+ enum IncidentMetricType {
2
+ TimeToAcknowledge = "oneuptime.incident.time-to-acknowledge",
3
+ TimeToResolve = "oneuptime.incident.time-to-resolve",
4
+ IncidentCount = "oneuptime.incident.count",
5
+ IncidentDuration = "oneuptime.incident.duration",
6
+ }
7
+
8
+ export default IncidentMetricType;