Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin'
Browse files Browse the repository at this point in the history
  • Loading branch information
jvanbaarsen committed Jun 17, 2024
2 parents 7ab793b + 573bbb6 commit 6856030
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 53 deletions.
11 changes: 7 additions & 4 deletions components/UptimeDot/UptimeDot.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ const DEFAULT_THRESHOLD = 5;

const state = (timeserie, threshold) => {
if (timeserie.missingDataPoint) return "missing";
return Object.values(timeserie.values).reduce((a, b) => Math.max(a, b)) >
threshold
? "down"
: "up";
const maxDowntime = Object.values(timeserie.values).reduce((a, b) =>
Math.max(a, b)
);
if (maxDowntime === 1440) return "down";
if (maxDowntime > threshold) return "partial";
return "up";
};

export const downtimeSummary = (timeserie, threshold) => {
Expand Down Expand Up @@ -41,6 +43,7 @@ const UptimeDot = ({ timeserie, threshold = DEFAULT_THRESHOLD }) => {
const stateColor = {
up: "bg-green-500",
down: "bg-red-500",
partial: "bg-yellow-500",
missing: "bg-gray-200",
};

Expand Down
2 changes: 1 addition & 1 deletion components/UptimeDot/UptimeDot.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ describe("UptimeDot", () => {
test("dot is red when down and above threshold", () => {
const { container } = build({ timeserie: down, threshold: 0 });
const dot = container.querySelector("div");
expect(dot.classList).toContain("bg-red-500");
expect(dot.classList).toContain("bg-yellow-500");
});

test("dot is green when down and under threshold", () => {
Expand Down
67 changes: 66 additions & 1 deletion components/UptimeMonitor/UptimeMonitor.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import React from "react";
import PropTypes from "prop-types";

import Tippy from "@tippyjs/react";

import OutagesOverlay from "../OutagesOverlay";
import UptimeDots from "../UptimeDots";

import {
timeseriesByDay as groupTimeseriesByDay,
roundDecimal,
} from "../../utils";

export const LoadingDot = () => {
return (
<div
Expand All @@ -24,6 +31,45 @@ export const UptimeMonitorLoading = () => {
);
};

export const calculateUptime = (timeseries, regions) => {
const timeseriesByDay = groupTimeseriesByDay(timeseries, regions);
const timeSeriesLast30Days = timeseriesByDay
.slice(-30, timeseriesByDay.length - 1)
.filter((item) => item.missingDataPoint === false);
const minutesPerDay = 1440.0;

const downtimePerRegion = [];

regions.map((region) => {
const downtimeInMinutes = timeSeriesLast30Days.reduce((acc, item) => {
return acc + item.values[region];
}, 0);

const uptimePercentage =
100 -
roundDecimal(
(100.0 / (minutesPerDay * timeSeriesLast30Days.length)) *
downtimeInMinutes
);

downtimePerRegion.push({
region: region,
minutes: downtimeInMinutes,
percentage: uptimePercentage,
});
});

return downtimePerRegion;
};

export const averageDowntimeOverRegions = (downtimePerRegion) => {
const average =
Object.values(downtimePerRegion).reduce((acc, item) => {
return (acc += item.percentage);
}, 0) / Object.keys(downtimePerRegion).length;
return roundDecimal(average).toFixed(2);
};

const UptimeMonitor = ({ uptimeMonitor, threshold }) => {
const [overlayOpen, setOverlayOpen] = React.useState(false);
const [loading, setLoading] = React.useState(true);
Expand All @@ -45,6 +91,11 @@ const UptimeMonitor = ({ uptimeMonitor, threshold }) => {
return () => (mounted = false);
}, [uptimeMonitor.id, uptimeMonitor.endpoint]);

const calculatedUptime = calculateUptime(
monitor.timeseries,
uptimeMonitor.regions
);

return (
<>
<div className="px-6 py-5 space-y-3" data-testid="UptimeMonitor">
Expand All @@ -54,7 +105,21 @@ const UptimeMonitor = ({ uptimeMonitor, threshold }) => {
className="c_h-heading focus:outline-none"
onClick={() => setOverlayOpen(true)}
>
{uptimeMonitor.title}
{uptimeMonitor.title}&nbsp;
{!loading && (
<Tippy
animation={false}
content={calculatedUptime.map((region) => (
<div key={region.region}>
{region.percentage}% in {region.region}
</div>
))}
>
<span className="text-xs">
({averageDowntimeOverRegions(calculatedUptime)} % uptime)
</span>
</Tippy>
)}
</button>
</h2>
<p className="mt-1 sm:mt-0 text-gray-700">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ exports[`UptimeMonitor renders without errors 1`] = `
class="c_h-heading focus:outline-none"
>
homepage
 
</button>
</h2>
<p
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ exports[`UptimeMonitors renders without problems 1`] = `
class="c_h-heading focus:outline-none"
>
Always Down
 
</button>
</h2>
<p
Expand Down Expand Up @@ -171,6 +172,7 @@ exports[`UptimeMonitors renders without problems 1`] = `
class="c_h-heading focus:outline-none"
>
blog
 
</button>
</h2>
<p
Expand Down Expand Up @@ -319,6 +321,7 @@ exports[`UptimeMonitors renders without problems 1`] = `
class="c_h-heading focus:outline-none"
>
homepage
 
</button>
</h2>
<p
Expand Down
44 changes: 22 additions & 22 deletions mocks/monitors/always-down.json
Original file line number Diff line number Diff line change
Expand Up @@ -1663,10 +1663,10 @@
{
"timestamp": "2021-08-20T08:00:00.000Z",
"values": {
"europe": 48,
"north-america": 48,
"south-america": 48,
"asia-pacific": 48
"europe": 60,
"north-america": 60,
"south-america": 60,
"asia-pacific": 60
}
},
{
Expand Down Expand Up @@ -2898,7 +2898,7 @@
"values": {
"europe": 60,
"north-america": 60,
"south-america": 59,
"south-america": 60,
"asia-pacific": 60
}
},
Expand Down Expand Up @@ -3391,10 +3391,10 @@
{
"timestamp": "2021-08-05T01:00:00.000Z",
"values": {
"europe": 61,
"north-america": 61,
"south-america": 61,
"asia-pacific": 61
"europe": 60,
"north-america": 60,
"south-america": 60,
"asia-pacific": 60
}
},
{
Expand Down Expand Up @@ -5065,10 +5065,10 @@
{
"timestamp": "2021-08-23T19:00:00.000Z",
"values": {
"europe": 61,
"north-america": 61,
"south-america": 61,
"asia-pacific": 61
"europe": 60,
"north-america": 60,
"south-america": 60,
"asia-pacific": 60
}
},
{
Expand Down Expand Up @@ -5659,10 +5659,10 @@
{
"timestamp": "2021-08-09T20:00:00.000Z",
"values": {
"europe": 68,
"north-america": 68,
"south-america": 68,
"asia-pacific": 68
"europe": 60,
"north-america": 60,
"south-america": 60,
"asia-pacific": 60
}
},
{
Expand Down Expand Up @@ -6010,10 +6010,10 @@
{
"timestamp": "2021-08-23T18:00:00.000Z",
"values": {
"europe": 59,
"north-america": 59,
"south-america": 59,
"asia-pacific": 59
"europe": 60,
"north-america": 60,
"south-america": 60,
"asia-pacific": 60
}
},
{
Expand Down Expand Up @@ -6237,7 +6237,7 @@
"values": {
"europe": 60,
"north-america": 60,
"south-america": 59,
"south-america": 60,
"asia-pacific": 60
}
},
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,23 @@
"@tailwindcss/typography": "^0.5.9",
"@tippyjs/react": "^4.2.5",
"autoprefixer": "^10.4.14",
"dayjs": "^1.10.6",
"dayjs": "^1.11.11",
"mockdate": "^3.0.5",
"msw": "^0.35.0",
"next": "11.1.1",
"postcss": "^8.4.24",
"postcss": "^8.4.38",
"postcss-import": "^15.1.0",
"postcss-url": "^10.1.3",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-test-renderer": "^17.0.2",
"tailwindcss": "^3.3.2",
"tailwindcss": "^3.4.4",
"whatwg-fetch": "^3.6.2"
},
"devDependencies": {
"@fullhuman/postcss-purgecss": "^4.0.3",
"@testing-library/dom": "^7.30.0",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^12.0.0",
"babel-jest": "^27.0.6",
"babel-plugin-directory-named": "^1.2.0",
Expand Down
5 changes: 5 additions & 0 deletions utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const formatRegion = (region) => {
};

export const timeseriesByDay = (timeseries, expectedRegions) => {
if (timeseries === undefined) return [];
return sortedTimeseries(timeseries.slice()).reduce((group, timeserie) => {
const startOfDay = dayjs(timeserie.timestamp).startOf("day").utc().format();

Expand Down Expand Up @@ -67,3 +68,7 @@ export const fillMissingDataPoints = (timeseries, expectedDays) => {
}
return sortedTimeseries(timeseries);
};

export const roundDecimal = (number) => {
return parseInt(number.toFixed(3) * 100) / 100;
};
19 changes: 19 additions & 0 deletions utils/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
timeseriesByDay,
sortedTimeseries,
fillMissingDataPoints,
roundDecimal,
} from "./index";
import homepageMock from "../mocks/monitors/homepage.json";

Expand Down Expand Up @@ -120,3 +121,21 @@ describe("#fillMissingDataPoints", () => {
expect(withFilledMissingPoints[1].missingDataPoint).toBeFalsy();
});
});

describe("#roundDecimal", () => {
it("returns a round number if round number is given", () => {
expect(roundDecimal(100)).toEqual(100);
expect(roundDecimal(50)).toEqual(50);
});

it("rounds a number with two decimals", () => {
expect(roundDecimal(100.12)).toEqual(100.12);
expect(roundDecimal(50.34)).toEqual(50.34);
});

it("rounds number with more decimals", () => {
expect(roundDecimal(100.122342342344234)).toEqual(100.12);
expect(roundDecimal(50.3499999)).toEqual(50.35);
expect(roundDecimal(99.9075)).toEqual(99.9);
});
});
Loading

0 comments on commit 6856030

Please sign in to comment.