useAdvncedMatching

This commit is contained in:
Alex Holliday
2026-01-30 20:21:31 +00:00
parent 0263c17618
commit f685c9cd5d
10 changed files with 66 additions and 18 deletions
+2
View File
@@ -33,6 +33,7 @@ export const useMonitorForm = ({
type: "http",
url: data?.url || "",
ignoreTlsErrors: data?.ignoreTlsErrors || false,
useAdvancedMatching: data?.useAdvancedMatching || false,
matchMethod: data?.matchMethod || "",
expectedValue: data?.expectedValue || "",
jsonPath: data?.jsonPath || "",
@@ -95,6 +96,7 @@ export const useMonitorForm = ({
type: "http",
url: "",
ignoreTlsErrors: false,
useAdvancedMatching: false,
matchMethod: "",
expectedValue: "",
jsonPath: "",
+48 -15
View File
@@ -1,5 +1,5 @@
import { useMemo } from "react";
import { useEffect, useState } from "react";
import { useEffect } from "react";
import { useParams, useLocation, useNavigate } from "react-router";
import { useForm, Controller } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -7,9 +7,10 @@ import { useTheme } from "@mui/material";
import Stack from "@mui/material/Stack";
import RadioGroup from "@mui/material/RadioGroup";
import FormControl from "@mui/material/FormControl";
import { useTranslation } from "react-i18next";
import { Trans, useTranslation } from "react-i18next";
import MenuItem from "@mui/material/MenuItem";
import Typography from "@mui/material/Typography";
import Link from "@mui/material/Link";
import Divider from "@mui/material/Divider";
import IconButton from "@mui/material/IconButton";
import FormControlLabel from "@mui/material/FormControlLabel";
@@ -163,7 +164,7 @@ const CreateMonitorPage = () => {
const watchedType = watch("type") as MonitorType;
const [showAdvancedSettings, setShowAdvancedSettings] = useState(false);
const watchedUseAdvancedMatching = watch("useAdvancedMatching") as boolean;
useEffect(() => {
clearErrors();
@@ -550,16 +551,22 @@ const CreateMonitorPage = () => {
subtitle={t("pages.createMonitor.form.advanced.description")}
rightContent={
<Stack spacing={theme.spacing(8)}>
<FormControlLabel
control={
<Checkbox
checked={showAdvancedSettings}
onChange={(e) => setShowAdvancedSettings(e.target.checked)}
<Controller
name="useAdvancedMatching"
control={control}
render={({ field }) => (
<FormControlLabel
control={
<Checkbox
checked={field.value ?? false}
onChange={(e) => field.onChange(e.target.checked)}
/>
}
label={t("advancedMatching")}
/>
}
label={t("advancedMatching")}
)}
/>
{showAdvancedSettings && (
{watchedUseAdvancedMatching && (
<Stack spacing={theme.spacing(8)}>
<Controller
name="matchMethod"
@@ -568,10 +575,14 @@ const CreateMonitorPage = () => {
<Select
{...field}
value={field.value ?? "equal"}
fieldLabel={t("pages.createMonitor.form.advanced.option.matchMethod.label")}
fieldLabel={t(
"pages.createMonitor.form.advanced.option.matchMethod.label"
)}
>
<MenuItem value="equal">{t("matchMethodOptions.equal")}</MenuItem>
<MenuItem value="include">{t("matchMethodOptions.include")}</MenuItem>
<MenuItem value="include">
{t("matchMethodOptions.include")}
</MenuItem>
<MenuItem value="regex">{t("matchMethodOptions.regex")}</MenuItem>
</Select>
)}
@@ -583,7 +594,9 @@ const CreateMonitorPage = () => {
<TextField
{...field}
value={field.value ?? ""}
fieldLabel={t("pages.createMonitor.form.advanced.option.expectedValue.label")}
fieldLabel={t(
"pages.createMonitor.form.advanced.option.expectedValue.label"
)}
fullWidth
error={!!fieldState.error}
helperText={fieldState.error?.message ?? ""}
@@ -597,13 +610,33 @@ const CreateMonitorPage = () => {
<TextField
{...field}
value={field.value ?? ""}
fieldLabel={t("pages.createMonitor.form.advanced.option.jsonPath.label")}
fieldLabel={t(
"pages.createMonitor.form.advanced.option.jsonPath.label"
)}
fullWidth
error={!!fieldState.error}
helperText={fieldState.error?.message ?? ""}
/>
)}
/>
<Typography
component="span"
color="text.secondary"
sx={{ opacity: 0.8 }}
>
<Trans
i18nKey="pages.createMonitor.form.advanced.option.jsonPath.description"
components={{
jmesLink: (
<Link
href="https://jmespath.org/"
target="_blank"
rel="noopener noreferrer"
/>
),
}}
/>
</Typography>
</Stack>
)}
</Stack>
+1
View File
@@ -35,6 +35,7 @@ export interface Monitor {
statusWindowThreshold: number;
type: MonitorType;
ignoreTlsErrors: boolean;
useAdvancedMatching: boolean;
jsonPath?: string;
expectedValue?: string;
matchMethod?: MonitorMatchMethod;
+1
View File
@@ -27,6 +27,7 @@ const httpSchema = baseSchema.extend({
type: z.literal("http"),
url: urlSchema,
ignoreTlsErrors: z.boolean(),
useAdvancedMatching: z.boolean(),
matchMethod: z.enum(["equal", "include", "regex", ""]).optional(),
expectedValue: z.string().optional(),
jsonPath: z.string().optional(),
+3 -1
View File
@@ -2,6 +2,8 @@
"common": {
"appName": "Checkmate",
"monitoringAgentName": "Capture",
"see": "See",
"forDocumentation": "for query language documentation.",
"breadcrumbs": {
"home": "Home",
"details": "Details"
@@ -322,7 +324,7 @@
},
"jsonPath": {
"label": "JSONPath expression",
"description": "This expression will be evaluated against the response JSON data and the result will be used to match against the expected value."
"description": "This expression will be evaluated against the response JSON data and the result will be used to match against the expected value. See <jmesLink>jmespath.org</jmesLink> for query language documentation."
}
}
}
+4
View File
@@ -92,6 +92,10 @@ const MonitorSchema = new Schema<MonitorDocument>(
type: Boolean,
default: false,
},
useAdvancedMatching: {
type: Boolean,
default: false,
},
jsonPath: {
type: String,
},
@@ -290,6 +290,7 @@ class MongoMonitorsRepository implements IMonitorsRepository {
statusWindowThreshold: doc.statusWindowThreshold,
type: doc.type,
ignoreTlsErrors: doc.ignoreTlsErrors,
useAdvancedMatching: doc.useAdvancedMatching ?? false,
jsonPath: doc.jsonPath ?? undefined,
expectedValue: doc.expectedValue ?? undefined,
matchMethod: doc.matchMethod ?? undefined,
@@ -374,6 +375,7 @@ class MongoMonitorsRepository implements IMonitorsRepository {
statusWindowThreshold: doc.statusWindowThreshold,
type: doc.type,
ignoreTlsErrors: doc.ignoreTlsErrors,
useAdvancedMatching: doc.useAdvancedMatching ?? false,
jsonPath: doc.jsonPath ?? undefined,
expectedValue: doc.expectedValue ?? undefined,
matchMethod: doc.matchMethod ?? undefined,
@@ -255,7 +255,7 @@ class NetworkService implements INetworkService {
}
private async requestHttp(monitor: Monitor): Promise<MonitorStatusResponse> {
const { url, secret, id, teamId, type, ignoreTlsErrors, jsonPath, matchMethod, expectedValue } = monitor;
const { url, secret, id, teamId, type, ignoreTlsErrors, useAdvancedMatching, jsonPath, matchMethod, expectedValue } = monitor;
const httpResponse = this.buildStatusResponse({
monitor,
overrides: {
@@ -305,7 +305,7 @@ class NetworkService implements INetworkService {
timings: response.timings,
});
if (!expectedValue && !jsonPath) {
if (!useAdvancedMatching) {
return httpResponse;
}
+1
View File
@@ -26,6 +26,7 @@ export interface Monitor {
statusWindowThreshold: number;
type: MonitorType;
ignoreTlsErrors: boolean;
useAdvancedMatching: boolean;
jsonPath?: string;
expectedValue?: string;
matchMethod?: MonitorMatchMethod;
+2
View File
@@ -158,6 +158,7 @@ const createMonitorBodyValidation = joi.object({
statusWindowThreshold: joi.number().min(1).max(100).default(60),
url: joi.string().required(),
ignoreTlsErrors: joi.boolean().default(false),
useAdvancedMatching: joi.boolean().default(false),
port: joi.number(),
isActive: joi.boolean(),
interval: joi.number(),
@@ -193,6 +194,7 @@ const editMonitorBodyValidation = joi.object({
notifications: joi.array().items(joi.string()),
secret: joi.string(),
ignoreTlsErrors: joi.boolean(),
useAdvancedMatching: joi.boolean(),
jsonPath: joi.string().allow(""),
expectedValue: joi.string().allow(""),
matchMethod: joi.string().allow(null, ""),