show all checks
This commit is contained in:
1 parent
88e28b9819
commit
477c30286d
9 files changed
+123
-39
No files matched your search
@@ -28,8 +28,6 @@ export const HeaderGeoTabs = ({
|
||||
onLocationChange(newValue);
|
||||
};
|
||||
|
||||
console.log(locations);
|
||||
|
||||
return (
|
||||
<Stack
|
||||
spacing={{ xs: theme.spacing(8), md: 0 }}
|
||||
|
||||
@@ -8,14 +8,48 @@ import type { RootState } from "@/Types/state";
|
||||
import { useSelector } from "react-redux";
|
||||
import prettyMilliseconds from "pretty-ms";
|
||||
|
||||
// Flatten geo check results - each location becomes a separate row
|
||||
interface FlattenedGeoCheckResult {
|
||||
id: string;
|
||||
monitorId: string;
|
||||
createdAt: string;
|
||||
location: {
|
||||
continent: string;
|
||||
country: string;
|
||||
city: string;
|
||||
};
|
||||
status: boolean;
|
||||
statusCode: number;
|
||||
timings: {
|
||||
total: number;
|
||||
};
|
||||
}
|
||||
|
||||
const flattenGeoChecks = (geoChecks: GeoCheck[]): FlattenedGeoCheckResult[] => {
|
||||
const flattened: FlattenedGeoCheckResult[] = [];
|
||||
for (const check of geoChecks) {
|
||||
for (const result of check.results) {
|
||||
flattened.push({
|
||||
id: `${check.id}-${result.location.continent}-${result.location.city}`,
|
||||
monitorId: check.metadata.monitorId,
|
||||
createdAt: check.createdAt,
|
||||
location: result.location,
|
||||
status: result.status,
|
||||
statusCode: result.statusCode,
|
||||
timings: result.timings,
|
||||
});
|
||||
}
|
||||
}
|
||||
return flattened;
|
||||
};
|
||||
|
||||
const getHeaders = (t: Function, uiTimezone: string) => {
|
||||
const headers: Header<GeoCheck>[] = [
|
||||
const headers: Header<FlattenedGeoCheckResult>[] = [
|
||||
{
|
||||
id: "status",
|
||||
content: t("common.table.headers.status"),
|
||||
render: (row) => {
|
||||
const firstResult = row.results[0];
|
||||
const status = firstResult?.status ? "up" : "down";
|
||||
const status = row.status ? "up" : "down";
|
||||
return <StatusLabel status={status} />;
|
||||
},
|
||||
},
|
||||
@@ -30,17 +64,14 @@ const getHeaders = (t: Function, uiTimezone: string) => {
|
||||
id: "statusCode",
|
||||
content: t("pages.checks.table.headers.statusCode"),
|
||||
render: (row) => {
|
||||
const firstResult = row.results[0];
|
||||
return firstResult?.statusCode || "N/A";
|
||||
return row.statusCode || "N/A";
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "location",
|
||||
content: t("pages.checks.table.headers.location"),
|
||||
render: (row) => {
|
||||
const firstResult = row.results[0];
|
||||
if (!firstResult) return "N/A";
|
||||
const { continent, country, city } = firstResult.location;
|
||||
const { continent, country, city } = row.location;
|
||||
return `${continent} - ${country}, ${city}`;
|
||||
},
|
||||
},
|
||||
@@ -48,9 +79,8 @@ const getHeaders = (t: Function, uiTimezone: string) => {
|
||||
id: "responseTime",
|
||||
content: t("common.table.headers.responseTime"),
|
||||
render: (row) => {
|
||||
const firstResult = row.results[0];
|
||||
if (!firstResult?.timings?.total) return "N/A";
|
||||
return prettyMilliseconds(firstResult.timings.total, { compact: true });
|
||||
if (!row.timings?.total) return "N/A";
|
||||
return prettyMilliseconds(row.timings.total, { compact: true });
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -75,6 +105,7 @@ export const GeoChecksTable = ({
|
||||
const { t } = useTranslation();
|
||||
const uiTimezone = useSelector((state: RootState) => state.ui.timezone);
|
||||
const headers = getHeaders(t, uiTimezone);
|
||||
const flattenedData = flattenGeoChecks(geoChecks);
|
||||
|
||||
const handlePageChange = (
|
||||
_e: React.MouseEvent<HTMLButtonElement> | null,
|
||||
@@ -95,7 +126,7 @@ export const GeoChecksTable = ({
|
||||
<Box>
|
||||
<Table
|
||||
headers={headers}
|
||||
data={geoChecks}
|
||||
data={flattenedData}
|
||||
/>
|
||||
<Pagination
|
||||
component="div"
|
||||
|
||||
@@ -144,9 +144,6 @@ const UptimeDetailsPage = () => {
|
||||
params.append("dateRange", dateRange);
|
||||
params.append("page", String(geoPage));
|
||||
params.append("rowsPerPage", String(geoRowsPerPage));
|
||||
if (selectedLocation) {
|
||||
params.append("continent", selectedLocation);
|
||||
}
|
||||
return `/geo-checks/${monitorId}?${params.toString()}`;
|
||||
}, [
|
||||
monitorId,
|
||||
@@ -155,7 +152,6 @@ const UptimeDetailsPage = () => {
|
||||
dateRange,
|
||||
geoPage,
|
||||
geoRowsPerPage,
|
||||
selectedLocation,
|
||||
]);
|
||||
|
||||
const { data: geoChecksTableData } = useGet<GeoChecksResponse>(
|
||||
|
||||
@@ -142,14 +142,19 @@ class MonitorController {
|
||||
|
||||
const monitorId = requireString(req?.params?.monitorId, "Monitor ID");
|
||||
const dateRange = requireString(req?.query?.dateRange, "dateRange");
|
||||
const continent = optionalString(req?.query?.continent, "continent") as GeoContinent | undefined;
|
||||
const continentParam = req?.query?.continent;
|
||||
const continents = continentParam
|
||||
? Array.isArray(continentParam)
|
||||
? (continentParam as GeoContinent[])
|
||||
: [continentParam as GeoContinent]
|
||||
: undefined;
|
||||
const teamId = requireTeamId(req?.user?.teamId);
|
||||
|
||||
const data = await this.monitorService.getGeoChecksByMonitorId({
|
||||
teamId,
|
||||
monitorId,
|
||||
dateRange,
|
||||
continent,
|
||||
continents,
|
||||
});
|
||||
|
||||
return res.status(200).json({
|
||||
|
||||
@@ -14,7 +14,7 @@ export interface IGeoChecksRepository {
|
||||
dateRange: string,
|
||||
page: number,
|
||||
rowsPerPage: number,
|
||||
continent?: GeoContinent
|
||||
continents?: GeoContinent[]
|
||||
): Promise<GeoChecksQueryResult>;
|
||||
findByMonitorIdAndDateRange(monitorId: string, startDate: Date, endDate: Date): Promise<GeoCheck[]>;
|
||||
findGroupedByMonitorIdAndDateRange(
|
||||
@@ -22,7 +22,7 @@ export interface IGeoChecksRepository {
|
||||
startDate: Date,
|
||||
endDate: Date,
|
||||
dateFormat: string,
|
||||
continent?: GeoContinent
|
||||
continents?: GeoContinent[]
|
||||
): Promise<GroupedGeoCheck[]>;
|
||||
deleteByMonitorId(monitorId: string): Promise<number>;
|
||||
deleteByTeamId(teamId: string): Promise<number>;
|
||||
|
||||
@@ -111,9 +111,16 @@ class MongoGeoChecksRepository implements IGeoChecksRepository {
|
||||
dateRange: string,
|
||||
page: number,
|
||||
rowsPerPage: number,
|
||||
continent?: GeoContinent
|
||||
continents?: GeoContinent[]
|
||||
): Promise<GeoChecksQueryResult> => {
|
||||
try {
|
||||
this.logger.debug({
|
||||
message: "findByMonitorId called",
|
||||
service: SERVICE_NAME,
|
||||
method: "findByMonitorId",
|
||||
details: { monitorId, continents, continentsLength: continents?.length },
|
||||
});
|
||||
|
||||
const matchStage: Record<string, any> = {
|
||||
"metadata.monitorId": new mongoose.Types.ObjectId(monitorId),
|
||||
...(dateRangeLookup[dateRange] && {
|
||||
@@ -130,13 +137,19 @@ class MongoGeoChecksRepository implements IGeoChecksRepository {
|
||||
skip = page * rowsPerPage;
|
||||
}
|
||||
|
||||
if (continent) {
|
||||
if (continents && continents.length > 0) {
|
||||
this.logger.debug({
|
||||
message: "Filtering by continents",
|
||||
service: SERVICE_NAME,
|
||||
method: "findByMonitorId",
|
||||
details: { continents },
|
||||
});
|
||||
const pipeline: any[] = [
|
||||
{ $match: matchStage },
|
||||
{ $unwind: "$results" },
|
||||
{
|
||||
$match: {
|
||||
"results.location.continent": continent,
|
||||
"results.location.continent": { $in: continents },
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -162,7 +175,7 @@ class MongoGeoChecksRepository implements IGeoChecksRepository {
|
||||
GeoCheckModel.aggregate([
|
||||
{ $match: matchStage },
|
||||
{ $unwind: "$results" },
|
||||
{ $match: { "results.location.continent": continent } },
|
||||
{ $match: { "results.location.continent": { $in: continents } } },
|
||||
{ $group: { _id: "$_id" } },
|
||||
{ $count: "count" },
|
||||
]),
|
||||
@@ -174,12 +187,44 @@ class MongoGeoChecksRepository implements IGeoChecksRepository {
|
||||
|
||||
return { geoChecksCount, geoChecks };
|
||||
} else {
|
||||
const [geoChecksCount, docs] = await Promise.all([
|
||||
GeoCheckModel.countDocuments(matchStage),
|
||||
GeoCheckModel.find(matchStage).sort({ createdAt: convertedSortOrder }).skip(skip).limit(rowsPerPage).lean() as Promise<GeoCheckDocument[]>,
|
||||
this.logger.debug({
|
||||
message: "No continent filtering - returning all checks",
|
||||
service: SERVICE_NAME,
|
||||
method: "findByMonitorId",
|
||||
});
|
||||
|
||||
// Still need to unwind to show each location as a separate row
|
||||
const pipeline: any[] = [
|
||||
{ $match: matchStage },
|
||||
{ $unwind: "$results" },
|
||||
{
|
||||
$group: {
|
||||
_id: "$_id",
|
||||
doc: { $first: "$$ROOT" },
|
||||
results: { $push: "$results" },
|
||||
},
|
||||
},
|
||||
{
|
||||
$replaceRoot: {
|
||||
newRoot: {
|
||||
$mergeObjects: ["$doc", { results: "$results" }],
|
||||
},
|
||||
},
|
||||
},
|
||||
{ $sort: { createdAt: convertedSortOrder } },
|
||||
{ $skip: skip },
|
||||
{ $limit: rowsPerPage },
|
||||
];
|
||||
|
||||
const [countPipeline, dataResults] = await Promise.all([
|
||||
GeoCheckModel.aggregate([{ $match: matchStage }, { $unwind: "$results" }, { $group: { _id: "$_id" } }, { $count: "count" }]),
|
||||
GeoCheckModel.aggregate(pipeline),
|
||||
]);
|
||||
|
||||
return { geoChecksCount, geoChecks: docs.map(this.toEntity) };
|
||||
const geoChecksCount = countPipeline[0]?.count || 0;
|
||||
const geoChecks = dataResults.map(this.toEntity);
|
||||
|
||||
return { geoChecksCount, geoChecks };
|
||||
}
|
||||
} catch (error: any) {
|
||||
this.logger.error({
|
||||
@@ -217,7 +262,7 @@ class MongoGeoChecksRepository implements IGeoChecksRepository {
|
||||
startDate: Date,
|
||||
endDate: Date,
|
||||
dateFormat: string,
|
||||
continent?: string
|
||||
continents?: GeoContinent[]
|
||||
): Promise<GroupedGeoCheck[]> => {
|
||||
try {
|
||||
const pipeline: any[] = [
|
||||
@@ -236,11 +281,11 @@ class MongoGeoChecksRepository implements IGeoChecksRepository {
|
||||
$unwind: "$results",
|
||||
},
|
||||
// Filter by continent if specified
|
||||
...(continent
|
||||
...(continents && continents.length > 0
|
||||
? [
|
||||
{
|
||||
$match: {
|
||||
"results.location.continent": continent,
|
||||
"results.location.continent": { $in: continents },
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
@@ -177,10 +177,19 @@ class GeoChecksService implements IGeoChecksService {
|
||||
}
|
||||
|
||||
let { sortOrder, dateRange, page, rowsPerPage, continent } = query;
|
||||
const continents = continent ? (Array.isArray(continent) ? continent : [continent]) : undefined;
|
||||
|
||||
this.logger.debug({
|
||||
message: "getGeoChecksByMonitor query params",
|
||||
service: SERVICE_NAME,
|
||||
method: "getGeoChecksByMonitor",
|
||||
details: { continent, continents, query },
|
||||
});
|
||||
|
||||
const parsedPage = page ? parseInt(page) : page;
|
||||
const parsedRowsPerPage = rowsPerPage ? parseInt(rowsPerPage) : rowsPerPage;
|
||||
|
||||
const result = await this.geoChecksRepository.findByMonitorId(monitorId, sortOrder, dateRange, parsedPage, parsedRowsPerPage, continent);
|
||||
const result = await this.geoChecksRepository.findByMonitorId(monitorId, sortOrder, dateRange, parsedPage, parsedRowsPerPage, continents);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -38,7 +38,7 @@ export interface IMonitorService {
|
||||
getUptimeDetailsById(args: { teamId: string; monitorId: string; dateRange: string; normalize?: boolean }): Promise<UptimeDetailsResult>;
|
||||
getHardwareDetailsById(args: { teamId: string; monitorId: string; dateRange: string }): Promise<HardwareDetailsResult>;
|
||||
getPageSpeedDetailsById(args: { teamId: string; monitorId: string; dateRange: string }): Promise<PageSpeedDetailsResult>;
|
||||
getGeoChecksByMonitorId(args: { teamId: string; monitorId: string; dateRange: string; continent?: GeoContinent }): Promise<any>;
|
||||
getGeoChecksByMonitorId(args: { teamId: string; monitorId: string; dateRange: string; continents?: GeoContinent[] }): Promise<any>;
|
||||
getMonitorById(args: { teamId: string; monitorId: string }): Promise<Monitor>;
|
||||
getMonitorsByTeamId(args: {
|
||||
teamId: string;
|
||||
@@ -327,12 +327,12 @@ export class MonitorService implements IMonitorService {
|
||||
teamId,
|
||||
monitorId,
|
||||
dateRange,
|
||||
continent,
|
||||
continents,
|
||||
}: {
|
||||
teamId: string;
|
||||
monitorId: string;
|
||||
dateRange: string;
|
||||
continent?: GeoContinent;
|
||||
continents?: GeoContinent[];
|
||||
}): Promise<any> => {
|
||||
const monitor = await this.monitorsRepository.findById(monitorId, teamId);
|
||||
if (!monitor) {
|
||||
@@ -350,7 +350,7 @@ export class MonitorService implements IMonitorService {
|
||||
start,
|
||||
end,
|
||||
this.getDateFormat(rangeKey),
|
||||
continent
|
||||
continents
|
||||
);
|
||||
|
||||
return { groupedGeoChecks };
|
||||
|
||||
@@ -324,7 +324,7 @@ const getChecksQueryValidation = joi.object({
|
||||
page: joi.number(),
|
||||
rowsPerPage: joi.number(),
|
||||
status: joi.boolean(),
|
||||
continent: joi.string().valid(...GeoContinents),
|
||||
continent: joi.alternatives().try(joi.string().valid(...GeoContinents), joi.array().items(joi.string().valid(...GeoContinents))),
|
||||
});
|
||||
|
||||
const getTeamChecksQueryValidation = joi.object({
|
||||
|
||||
Reference in new issue
Block a user