@oneuptime/common 7.0.3688 → 7.0.3695

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.
@@ -74,6 +74,10 @@ import StatusPageResource from "Common/Models/DatabaseModels/StatusPageResource"
74
74
  import StatusPageSSO from "Common/Models/DatabaseModels/StatusPageSso";
75
75
  import StatusPageSubscriber from "Common/Models/DatabaseModels/StatusPageSubscriber";
76
76
  import StatusPageEventType from "../../Types/StatusPage/StatusPageEventType";
77
+ import StatusPageResourceUptimeUtil from "../../Utils/StatusPage/ResourceUptime";
78
+ import UptimePrecision from "../../Types/StatusPage/UptimePrecision";
79
+ import { Green } from "../../Types/BrandColors";
80
+ import UptimeUtil from "../../Utils/Uptime/UptimeUtil";
77
81
 
78
82
  export default class StatusPageAPI extends BaseAPI<
79
83
  StatusPage,
@@ -520,17 +524,26 @@ export default class StatusPageAPI extends BaseAPI<
520
524
  this.router.post(
521
525
  `${new this.entityType()
522
526
  .getCrudApiPath()
523
- ?.toString()}/overview/:statusPageId`,
527
+ ?.toString()}/uptime/:statusPageId`,
524
528
  UserMiddleware.getUserMiddleware,
525
529
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
526
530
  try {
527
- const objectId: ObjectID = new ObjectID(
531
+ // This reosurce ID can be of a status page resource OR a status page group.
532
+ const statusPageResourceId: ObjectID = new ObjectID(
533
+ req.params["statusPageResourceId"] as string,
534
+ );
535
+
536
+ const statusPageId: ObjectID = new ObjectID(
528
537
  req.params["statusPageId"] as string,
529
538
  );
530
539
 
540
+ if (!statusPageId || !statusPageResourceId) {
541
+ throw new BadDataException("Status Page or Resource not found");
542
+ }
543
+
531
544
  if (
532
545
  !(await this.service.hasReadAccess(
533
- objectId,
546
+ statusPageId,
534
547
  await CommonAPI.getDatabaseCommonInteractionProps(req),
535
548
  req,
536
549
  ))
@@ -540,231 +553,320 @@ export default class StatusPageAPI extends BaseAPI<
540
553
  );
541
554
  }
542
555
 
543
- const statusPage: StatusPage | null =
544
- await StatusPageService.findOneBy({
545
- query: {
546
- _id: objectId.toString(),
547
- },
548
- select: {
549
- _id: true,
550
- projectId: true,
551
- isPublicStatusPage: true,
552
- overviewPageDescription: true,
553
- showIncidentLabelsOnStatusPage: true,
554
- showScheduledEventLabelsOnStatusPage: true,
555
- downtimeMonitorStatuses: {
556
- _id: true,
557
- },
558
- defaultBarColor: true,
559
- showOverallUptimePercentOnStatusPage: true,
560
- overallUptimePercentPrecision: true,
561
- },
562
- props: {
563
- isRoot: true,
564
- },
565
- });
556
+ // get start and end date from request body.
557
+ // if no end date is provided then it will be current date.
558
+ // if no start date is provided then it will be 14 days ago from end date.
566
559
 
567
- if (!statusPage) {
568
- throw new BadDataException("Status Page not found");
569
- }
560
+ let startDate: Date = OneUptimeDate.getSomeDaysAgo(14);
561
+ let endDate: Date = OneUptimeDate.getCurrentDate();
570
562
 
571
- //get monitor statuses
563
+ if (req.body["startDate"]) {
564
+ startDate = OneUptimeDate.fromString(
565
+ req.body["startDate"] as string,
566
+ );
567
+ }
572
568
 
573
- const monitorStatuses: Array<MonitorStatus> =
574
- await MonitorStatusService.findBy({
575
- query: {
576
- projectId: statusPage.projectId!,
577
- },
578
- select: {
579
- name: true,
580
- color: true,
581
- priority: true,
582
- isOperationalState: true,
583
- },
584
- sort: {
585
- priority: SortOrder.Ascending,
586
- },
587
- skip: 0,
588
- limit: LIMIT_PER_PROJECT,
589
- props: {
590
- isRoot: true,
591
- },
592
- });
569
+ if (req.body["endDate"]) {
570
+ endDate = OneUptimeDate.fromString(req.body["endDate"] as string);
571
+ }
593
572
 
594
- // get resource groups.
573
+ if (OneUptimeDate.isAfter(startDate, endDate)) {
574
+ throw new BadDataException("Start date cannot be after end date");
575
+ }
595
576
 
596
- const groups: Array<StatusPageGroup> =
597
- await StatusPageGroupService.findBy({
598
- query: {
599
- statusPageId: objectId,
600
- },
601
- select: {
602
- name: true,
603
- order: true,
604
- description: true,
605
- isExpandedByDefault: true,
606
- showCurrentStatus: true,
607
- showUptimePercent: true,
608
- uptimePercentPrecision: true,
609
- },
610
- sort: {
611
- order: SortOrder.Ascending,
612
- },
613
- skip: 0,
614
- limit: LIMIT_PER_PROJECT,
615
- props: {
616
- isRoot: true,
617
- },
618
- });
577
+ if (
578
+ OneUptimeDate.getDaysBetweenTwoDatesInclusive(startDate, endDate) >
579
+ 90
580
+ ) {
581
+ throw new BadDataException(
582
+ "You can only get uptime for 90 days. Please select a date range within 90 days.",
583
+ );
584
+ }
619
585
 
620
- // get monitors on status page.
621
- const statusPageResources: Array<StatusPageResource> =
622
- await StatusPageResourceService.findBy({
623
- query: {
624
- statusPageId: objectId,
625
- },
626
- select: {
627
- statusPageGroupId: true,
628
- monitorId: true,
629
- displayTooltip: true,
630
- displayDescription: true,
631
- displayName: true,
632
- showStatusHistoryChart: true,
633
- showCurrentStatus: true,
634
- order: true,
635
- monitor: {
636
- _id: true,
637
- currentMonitorStatusId: true,
638
- },
639
- monitorGroupId: true,
640
- showUptimePercent: true,
641
- uptimePercentPrecision: true,
642
- },
643
- sort: {
644
- order: SortOrder.Ascending,
645
- },
646
- skip: 0,
647
- limit: LIMIT_PER_PROJECT,
648
- props: {
649
- isRoot: true,
650
- },
651
- });
586
+ const {
587
+ monitorStatuses,
588
+ monitorGroupCurrentStatuses,
589
+ statusPageResources,
590
+ statusPage,
591
+ monitorStatusTimelines,
592
+ statusPageGroups,
593
+ monitorsInGroup,
594
+ } = await this.getStatusPageResourcesAndTimelines({
595
+ statusPageId: statusPageId,
596
+ startDateForMonitorTimeline: startDate,
597
+ endDateForMonitorTimeline: endDate,
598
+ });
652
599
 
653
- const monitorGroupIds: Array<ObjectID> = statusPageResources
654
- .map((resource: StatusPageResource) => {
655
- return resource.monitorGroupId!;
656
- })
657
- .filter((id: ObjectID) => {
658
- return Boolean(id); // remove nulls
659
- });
600
+ const downtimeMonitorStatuses: Array<MonitorStatus> =
601
+ statusPage.downtimeMonitorStatuses || [];
660
602
 
661
- // get monitors in the group.
662
- const monitorGroupCurrentStatuses: Dictionary<ObjectID> = {};
663
- const monitorsInGroup: Dictionary<Array<ObjectID>> = {};
664
-
665
- // get monitor status charts.
666
- const monitorsOnStatusPage: Array<ObjectID> = statusPageResources
667
- .map((monitor: StatusPageResource) => {
668
- return monitor.monitorId!;
669
- })
670
- .filter((id: ObjectID) => {
671
- return Boolean(id); // remove nulls
672
- });
603
+ type ResourceUptime = {
604
+ statusPageResourceId: ObjectID;
605
+ uptimePercent: number | null;
606
+ statusPageResourceName: string;
607
+ currentStatus: MonitorStatus | null;
608
+ };
673
609
 
674
- const monitorsOnStatusPageForTimeline: Array<ObjectID> =
675
- statusPageResources
676
- .filter((monitor: StatusPageResource) => {
677
- return (
678
- monitor.showStatusHistoryChart || monitor.showUptimePercent
679
- );
680
- })
681
- .map((monitor: StatusPageResource) => {
682
- return monitor.monitorId!;
683
- })
684
- .filter((id: ObjectID) => {
685
- return Boolean(id); // remove nulls
686
- });
610
+ type StatusPageGroupUptime = {
611
+ statusPageGroupId: ObjectID | null;
612
+ uptimePercent: number | null;
613
+ statusPageResourceUptimes: Array<ResourceUptime>;
614
+ statusPageGroupName: string | null;
615
+ currentStatus: MonitorStatus | null;
616
+ };
687
617
 
688
- for (const monitorGroupId of monitorGroupIds) {
689
- // get current status of monitors in the group.
618
+ type GetUptimeByStatusPageGroup = (data: {
619
+ statusPageGroup: StatusPageGroup | null;
620
+ }) => StatusPageGroupUptime;
621
+
622
+ const getUptimeByStatusPageGroup: GetUptimeByStatusPageGroup =
623
+ (data: {
624
+ statusPageGroup: StatusPageGroup | null;
625
+ }): StatusPageGroupUptime => {
626
+ const groupUptime: StatusPageGroupUptime = {
627
+ statusPageGroupId:
628
+ data && data.statusPageGroup
629
+ ? data.statusPageGroup?.id
630
+ : null,
631
+ uptimePercent: null,
632
+ statusPageResourceUptimes: [],
633
+ statusPageGroupName: data.statusPageGroup?.name || null,
634
+ currentStatus: null,
635
+ };
690
636
 
691
- const currentStatus: MonitorStatus =
692
- await MonitorGroupService.getCurrentStatus(monitorGroupId, {
693
- isRoot: true,
694
- });
637
+ const group: StatusPageGroup | null = data.statusPageGroup;
638
+
639
+ for (const resource of statusPageResources) {
640
+ if (
641
+ (resource.statusPageGroupId &&
642
+ resource.statusPageGroupId.toString() &&
643
+ group &&
644
+ group._id?.toString() &&
645
+ group._id?.toString() ===
646
+ resource.statusPageGroupId.toString()) ||
647
+ (!resource.statusPageGroupId && !group)
648
+ ) {
649
+ // if its not a monitor or a monitor group, then continue. This should ideally not happen.
650
+
651
+ if (!resource.monitor && !resource.monitorGroupId) {
652
+ continue;
653
+ }
654
+
655
+ const resourceUptime: ResourceUptime = {
656
+ statusPageResourceId: resource.id!,
657
+ uptimePercent: null,
658
+ statusPageResourceName:
659
+ resource.displayName || resource.monitor?.name || "",
660
+ currentStatus: null,
661
+ };
662
+
663
+ // if its a monitor
664
+
665
+ const precision: UptimePrecision =
666
+ resource.uptimePercentPrecision ||
667
+ UptimePrecision.ONE_DECIMAL;
668
+
669
+ if (resource.monitor) {
670
+ let currentStatus: MonitorStatus | undefined =
671
+ monitorStatuses.find((status: MonitorStatus) => {
672
+ return (
673
+ status._id?.toString() ===
674
+ resource.monitor?.currentMonitorStatusId?.toString()
675
+ );
676
+ });
677
+
678
+ if (!currentStatus) {
679
+ currentStatus = new MonitorStatus();
680
+ currentStatus.name = "Operational";
681
+ currentStatus.color = Green;
682
+
683
+ resourceUptime.currentStatus = currentStatus;
684
+ } else {
685
+ resourceUptime.currentStatus = currentStatus;
686
+ }
687
+
688
+ if (!resource.showCurrentStatus) {
689
+ resourceUptime.currentStatus = null;
690
+ }
691
+
692
+ const resourceStatusTimelines: Array<MonitorStatusTimeline> =
693
+ StatusPageResourceUptimeUtil.getMonitorStatusTimelineForResource(
694
+ {
695
+ statusPageResource: resource,
696
+ monitorStatusTimelines: monitorStatusTimelines,
697
+ monitorsInGroup: monitorsInGroup,
698
+ },
699
+ );
700
+
701
+ if (resource.showUptimePercent) {
702
+ const uptimePercent: number =
703
+ UptimeUtil.calculateUptimePercentage(
704
+ resourceStatusTimelines,
705
+ precision,
706
+ downtimeMonitorStatuses,
707
+ );
708
+
709
+ resourceUptime.uptimePercent = uptimePercent;
710
+ }
711
+
712
+ groupUptime.statusPageResourceUptimes.push(resourceUptime);
713
+ }
714
+
715
+ // if its a monitor group, then...
716
+
717
+ if (resource.monitorGroupId) {
718
+ let currentStatus: MonitorStatus | undefined =
719
+ monitorStatuses.find((status: MonitorStatus) => {
720
+ return (
721
+ status._id?.toString() ===
722
+ monitorGroupCurrentStatuses[
723
+ resource.monitorGroupId?.toString() || ""
724
+ ]?.toString()
725
+ );
726
+ });
727
+
728
+ if (!currentStatus) {
729
+ currentStatus = new MonitorStatus();
730
+ currentStatus.name = "Operational";
731
+ currentStatus.color = Green;
732
+
733
+ resourceUptime.currentStatus = currentStatus;
734
+ } else {
735
+ resourceUptime.currentStatus = currentStatus;
736
+ }
737
+
738
+ if (!resource.showCurrentStatus) {
739
+ resourceUptime.currentStatus = null;
740
+ }
741
+
742
+ if (resource.showUptimePercent) {
743
+ const resourceStatusTimelines: Array<MonitorStatusTimeline> =
744
+ StatusPageResourceUptimeUtil.getMonitorStatusTimelineForResource(
745
+ {
746
+ statusPageResource: resource,
747
+ monitorStatusTimelines: monitorStatusTimelines,
748
+ monitorsInGroup: monitorsInGroup,
749
+ },
750
+ );
751
+
752
+ const uptimePercent: number =
753
+ UptimeUtil.calculateUptimePercentage(
754
+ resourceStatusTimelines,
755
+ precision,
756
+ downtimeMonitorStatuses,
757
+ );
758
+
759
+ resourceUptime.uptimePercent = uptimePercent;
760
+ }
761
+
762
+ groupUptime.statusPageResourceUptimes.push(resourceUptime);
763
+ }
764
+ }
765
+ }
695
766
 
696
- monitorGroupCurrentStatuses[monitorGroupId.toString()] =
697
- currentStatus.id!;
767
+ if (group?.showUptimePercent) {
768
+ // calculate uptime percent for the group.
769
+ const avgUptimePercent: number =
770
+ UptimeUtil.calculateAvgUptimePercentage({
771
+ uptimePercentages: groupUptime.statusPageResourceUptimes
772
+ .filter((resource: ResourceUptime) => {
773
+ return resource.uptimePercent !== null;
774
+ })
775
+ .map((resource: ResourceUptime) => {
776
+ return resource.uptimePercent || 0;
777
+ }),
778
+ precision:
779
+ group.uptimePercentPrecision ||
780
+ UptimePrecision.ONE_DECIMAL,
781
+ });
782
+
783
+ groupUptime.uptimePercent = avgUptimePercent;
784
+ }
698
785
 
699
- // get monitors in the group.
786
+ if (group?.showCurrentStatus) {
787
+ const currentStatuses: Array<MonitorStatus> =
788
+ groupUptime.statusPageResourceUptimes
789
+ .filter((resourceUptime: ResourceUptime) => {
790
+ return resourceUptime.currentStatus !== null;
791
+ })
792
+ .map((resourceUptime: ResourceUptime) => {
793
+ return resourceUptime.currentStatus!;
794
+ });
795
+
796
+ const worstStatus: MonitorStatus | null =
797
+ StatusPageResourceUptimeUtil.getWorstMonitorStatus({
798
+ monitorStatuses: currentStatuses,
799
+ });
800
+
801
+ groupUptime.currentStatus = worstStatus;
802
+ }
700
803
 
701
- const groupResources: Array<MonitorGroupResource> =
702
- await MonitorGroupResourceService.findBy({
703
- query: {
704
- monitorGroupId: monitorGroupId,
705
- },
706
- select: {
707
- monitorId: true,
708
- },
709
- props: {
710
- isRoot: true,
711
- },
712
- limit: LIMIT_PER_PROJECT,
713
- skip: 0,
714
- });
804
+ return groupUptime;
805
+ };
715
806
 
716
- const monitorsInGroupIds: Array<ObjectID> = groupResources
717
- .map((resource: MonitorGroupResource) => {
718
- return resource.monitorId!;
719
- })
720
- .filter((id: ObjectID) => {
721
- return Boolean(id); // remove nulls
722
- });
807
+ const groupUptimes: Array<StatusPageGroupUptime> = [];
723
808
 
724
- const shouldShowTimelineForThisGroup: boolean = Boolean(
725
- statusPageResources.find((resource: StatusPageResource) => {
726
- return (
727
- resource.monitorGroupId?.toString() ===
728
- monitorGroupId.toString() &&
729
- (resource.showStatusHistoryChart ||
730
- resource.showUptimePercent)
731
- );
732
- }),
809
+ for (const group of statusPageGroups) {
810
+ groupUptimes.push(
811
+ getUptimeByStatusPageGroup({ statusPageGroup: group }),
733
812
  );
813
+ }
734
814
 
735
- for (const monitorId of monitorsInGroupIds) {
736
- if (!monitorId) {
737
- continue;
738
- }
739
-
740
- if (
741
- !monitorsOnStatusPage.find((item: ObjectID) => {
742
- return item.toString() === monitorId.toString();
743
- })
744
- ) {
745
- monitorsOnStatusPage.push(monitorId);
746
- }
747
-
748
- // add this to the timeline event for this group.
815
+ return Response.sendJsonObjectResponse(req, res, {
816
+ statusPageResourceUptimes: [
817
+ ...getUptimeByStatusPageGroup({ statusPageGroup: null })
818
+ .statusPageResourceUptimes,
819
+ ],
820
+ groupUptimes: groupUptimes,
821
+ startDate: startDate,
822
+ endDate: endDate,
823
+ });
824
+ } catch (err) {
825
+ next(err);
826
+ }
827
+ },
828
+ );
749
829
 
750
- if (
751
- shouldShowTimelineForThisGroup &&
752
- !monitorsOnStatusPageForTimeline.find((item: ObjectID) => {
753
- return item.toString() === monitorId.toString();
754
- })
755
- ) {
756
- monitorsOnStatusPageForTimeline.push(monitorId);
757
- }
758
- }
830
+ this.router.post(
831
+ `${new this.entityType()
832
+ .getCrudApiPath()
833
+ ?.toString()}/overview/:statusPageId`,
834
+ UserMiddleware.getUserMiddleware,
835
+ async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
836
+ try {
837
+ const objectId: ObjectID = new ObjectID(
838
+ req.params["statusPageId"] as string,
839
+ );
759
840
 
760
- monitorsInGroup[monitorGroupId.toString()] = monitorsInGroupIds;
841
+ if (
842
+ !(await this.service.hasReadAccess(
843
+ objectId,
844
+ await CommonAPI.getDatabaseCommonInteractionProps(req),
845
+ req,
846
+ ))
847
+ ) {
848
+ throw new NotAuthenticatedException(
849
+ "You are not authenticated to access this status page",
850
+ );
761
851
  }
762
852
 
763
- const monitorStatusTimelines: Array<MonitorStatusTimeline> =
764
- await StatusPageService.getMonitorStatusTimelineForStatusPage({
765
- monitorIds: monitorsOnStatusPageForTimeline,
766
- historyDays: 90,
767
- });
853
+ const startDate: Date = OneUptimeDate.getSomeDaysAgo(90);
854
+ const endDate: Date = OneUptimeDate.getCurrentDate();
855
+
856
+ const {
857
+ monitorStatuses,
858
+ monitorGroupCurrentStatuses,
859
+ statusPageResources,
860
+ statusPage,
861
+ monitorsOnStatusPage,
862
+ monitorStatusTimelines,
863
+ statusPageGroups,
864
+ monitorsInGroup,
865
+ } = await this.getStatusPageResourcesAndTimelines({
866
+ statusPageId: objectId,
867
+ startDateForMonitorTimeline: startDate,
868
+ endDateForMonitorTimeline: endDate,
869
+ });
768
870
 
769
871
  // check if status page has active incident.
770
872
  let activeIncidents: Array<Incident> = [];
@@ -1135,7 +1237,10 @@ export default class StatusPageAPI extends BaseAPI<
1135
1237
  monitorStatusTimelines,
1136
1238
  MonitorStatusTimeline,
1137
1239
  ),
1138
- resourceGroups: BaseModel.toJSONArray(groups, StatusPageGroup),
1240
+ resourceGroups: BaseModel.toJSONArray(
1241
+ statusPageGroups,
1242
+ StatusPageGroup,
1243
+ ),
1139
1244
  monitorStatuses: BaseModel.toJSONArray(
1140
1245
  monitorStatuses,
1141
1246
  MonitorStatus,
@@ -2352,4 +2457,254 @@ export default class StatusPageAPI extends BaseAPI<
2352
2457
 
2353
2458
  return currentStatus;
2354
2459
  }
2460
+
2461
+ public async getStatusPageResourcesAndTimelines(data: {
2462
+ statusPageId: ObjectID;
2463
+ startDateForMonitorTimeline: Date;
2464
+ endDateForMonitorTimeline: Date;
2465
+ }): Promise<{
2466
+ statusPageResources: StatusPageResource[];
2467
+ monitorStatuses: MonitorStatus[];
2468
+ monitorStatusTimelines: MonitorStatusTimeline[];
2469
+ monitorGroupCurrentStatuses: Dictionary<ObjectID>;
2470
+ statusPageGroups: StatusPageGroup[];
2471
+ statusPage: StatusPage;
2472
+ monitorsOnStatusPage: ObjectID[];
2473
+ monitorsInGroup: Dictionary<ObjectID[]>;
2474
+ }> {
2475
+ const objectId: ObjectID = data.statusPageId;
2476
+
2477
+ const statusPage: StatusPage | null = await StatusPageService.findOneBy({
2478
+ query: {
2479
+ _id: objectId.toString(),
2480
+ },
2481
+ select: {
2482
+ _id: true,
2483
+ projectId: true,
2484
+ isPublicStatusPage: true,
2485
+ overviewPageDescription: true,
2486
+ showIncidentLabelsOnStatusPage: true,
2487
+ showScheduledEventLabelsOnStatusPage: true,
2488
+ downtimeMonitorStatuses: {
2489
+ _id: true,
2490
+ },
2491
+ defaultBarColor: true,
2492
+ showOverallUptimePercentOnStatusPage: true,
2493
+ overallUptimePercentPrecision: true,
2494
+ },
2495
+ props: {
2496
+ isRoot: true,
2497
+ },
2498
+ });
2499
+
2500
+ if (!statusPage) {
2501
+ throw new BadDataException("Status Page not found");
2502
+ }
2503
+
2504
+ //get monitor statuses
2505
+
2506
+ const monitorStatuses: Array<MonitorStatus> =
2507
+ await MonitorStatusService.findBy({
2508
+ query: {
2509
+ projectId: statusPage.projectId!,
2510
+ },
2511
+ select: {
2512
+ name: true,
2513
+ color: true,
2514
+ priority: true,
2515
+ isOperationalState: true,
2516
+ },
2517
+ sort: {
2518
+ priority: SortOrder.Ascending,
2519
+ },
2520
+ skip: 0,
2521
+ limit: LIMIT_PER_PROJECT,
2522
+ props: {
2523
+ isRoot: true,
2524
+ },
2525
+ });
2526
+
2527
+ // get resource groups.
2528
+
2529
+ const groups: Array<StatusPageGroup> = await StatusPageGroupService.findBy({
2530
+ query: {
2531
+ statusPageId: objectId,
2532
+ },
2533
+ select: {
2534
+ name: true,
2535
+ order: true,
2536
+ description: true,
2537
+ isExpandedByDefault: true,
2538
+ showCurrentStatus: true,
2539
+ showUptimePercent: true,
2540
+ uptimePercentPrecision: true,
2541
+ },
2542
+ sort: {
2543
+ order: SortOrder.Ascending,
2544
+ },
2545
+ skip: 0,
2546
+ limit: LIMIT_PER_PROJECT,
2547
+ props: {
2548
+ isRoot: true,
2549
+ },
2550
+ });
2551
+
2552
+ // get monitors on status page.
2553
+ const statusPageResources: Array<StatusPageResource> =
2554
+ await StatusPageResourceService.findBy({
2555
+ query: {
2556
+ statusPageId: objectId,
2557
+ },
2558
+ select: {
2559
+ statusPageGroupId: true,
2560
+ monitorId: true,
2561
+ displayTooltip: true,
2562
+ displayDescription: true,
2563
+ displayName: true,
2564
+ showStatusHistoryChart: true,
2565
+ showCurrentStatus: true,
2566
+ order: true,
2567
+ monitor: {
2568
+ _id: true,
2569
+ currentMonitorStatusId: true,
2570
+ },
2571
+ monitorGroupId: true,
2572
+ showUptimePercent: true,
2573
+ uptimePercentPrecision: true,
2574
+ },
2575
+ sort: {
2576
+ order: SortOrder.Ascending,
2577
+ },
2578
+ skip: 0,
2579
+ limit: LIMIT_PER_PROJECT,
2580
+ props: {
2581
+ isRoot: true,
2582
+ },
2583
+ });
2584
+
2585
+ const monitorGroupIds: Array<ObjectID> = statusPageResources
2586
+ .map((resource: StatusPageResource) => {
2587
+ return resource.monitorGroupId!;
2588
+ })
2589
+ .filter((id: ObjectID) => {
2590
+ return Boolean(id); // remove nulls
2591
+ });
2592
+
2593
+ // get monitors in the group.
2594
+ const monitorGroupCurrentStatuses: Dictionary<ObjectID> = {};
2595
+ const monitorsInGroup: Dictionary<Array<ObjectID>> = {};
2596
+
2597
+ // get monitor status charts.
2598
+ const monitorsOnStatusPage: Array<ObjectID> = statusPageResources
2599
+ .map((monitor: StatusPageResource) => {
2600
+ return monitor.monitorId!;
2601
+ })
2602
+ .filter((id: ObjectID) => {
2603
+ return Boolean(id); // remove nulls
2604
+ });
2605
+
2606
+ const monitorsOnStatusPageForTimeline: Array<ObjectID> = statusPageResources
2607
+ .filter((monitor: StatusPageResource) => {
2608
+ return monitor.showStatusHistoryChart || monitor.showUptimePercent;
2609
+ })
2610
+ .map((monitor: StatusPageResource) => {
2611
+ return monitor.monitorId!;
2612
+ })
2613
+ .filter((id: ObjectID) => {
2614
+ return Boolean(id); // remove nulls
2615
+ });
2616
+
2617
+ for (const monitorGroupId of monitorGroupIds) {
2618
+ // get current status of monitors in the group.
2619
+
2620
+ const currentStatus: MonitorStatus =
2621
+ await MonitorGroupService.getCurrentStatus(monitorGroupId, {
2622
+ isRoot: true,
2623
+ });
2624
+
2625
+ monitorGroupCurrentStatuses[monitorGroupId.toString()] =
2626
+ currentStatus.id!;
2627
+
2628
+ // get monitors in the group.
2629
+
2630
+ const groupResources: Array<MonitorGroupResource> =
2631
+ await MonitorGroupResourceService.findBy({
2632
+ query: {
2633
+ monitorGroupId: monitorGroupId,
2634
+ },
2635
+ select: {
2636
+ monitorId: true,
2637
+ },
2638
+ props: {
2639
+ isRoot: true,
2640
+ },
2641
+ limit: LIMIT_PER_PROJECT,
2642
+ skip: 0,
2643
+ });
2644
+
2645
+ const monitorsInGroupIds: Array<ObjectID> = groupResources
2646
+ .map((resource: MonitorGroupResource) => {
2647
+ return resource.monitorId!;
2648
+ })
2649
+ .filter((id: ObjectID) => {
2650
+ return Boolean(id); // remove nulls
2651
+ });
2652
+
2653
+ const shouldShowTimelineForThisGroup: boolean = Boolean(
2654
+ statusPageResources.find((resource: StatusPageResource) => {
2655
+ return (
2656
+ resource.monitorGroupId?.toString() === monitorGroupId.toString() &&
2657
+ (resource.showStatusHistoryChart || resource.showUptimePercent)
2658
+ );
2659
+ }),
2660
+ );
2661
+
2662
+ for (const monitorId of monitorsInGroupIds) {
2663
+ if (!monitorId) {
2664
+ continue;
2665
+ }
2666
+
2667
+ if (
2668
+ !monitorsOnStatusPage.find((item: ObjectID) => {
2669
+ return item.toString() === monitorId.toString();
2670
+ })
2671
+ ) {
2672
+ monitorsOnStatusPage.push(monitorId);
2673
+ }
2674
+
2675
+ // add this to the timeline event for this group.
2676
+
2677
+ if (
2678
+ shouldShowTimelineForThisGroup &&
2679
+ !monitorsOnStatusPageForTimeline.find((item: ObjectID) => {
2680
+ return item.toString() === monitorId.toString();
2681
+ })
2682
+ ) {
2683
+ monitorsOnStatusPageForTimeline.push(monitorId);
2684
+ }
2685
+ }
2686
+
2687
+ monitorsInGroup[monitorGroupId.toString()] = monitorsInGroupIds;
2688
+ }
2689
+
2690
+ const monitorStatusTimelines: Array<MonitorStatusTimeline> =
2691
+ await StatusPageService.getMonitorStatusTimelineForStatusPage({
2692
+ monitorIds: monitorsOnStatusPageForTimeline,
2693
+ startDate: data.startDateForMonitorTimeline,
2694
+ endDate: data.endDateForMonitorTimeline,
2695
+ });
2696
+
2697
+ // return everything.
2698
+
2699
+ return {
2700
+ statusPageResources,
2701
+ monitorStatuses,
2702
+ monitorGroupCurrentStatuses,
2703
+ statusPageGroups: groups,
2704
+ monitorStatusTimelines,
2705
+ statusPage,
2706
+ monitorsOnStatusPage,
2707
+ monitorsInGroup,
2708
+ };
2709
+ }
2355
2710
  }