remove $consider, replace with $not

This commit is contained in:
nate 2026-04-09 23:43:10 +04:00
parent 9b2f1de4e7
commit ed59e9c8dc
3 changed files with 26 additions and 55 deletions

View File

@ -14,17 +14,10 @@ pub struct Response {
pub fn evaluate(query: &Value, response: &Response) -> Result<bool> {
match query {
Value::Array(clauses) => {
return Ok(clauses.iter().all(|c| evaluate(c, response).unwrap_or(false)));
}
Value::Object(map) => {
if let Some(consider) = map.get("$consider") {
let is_down = consider.as_str() == Some("down");
let rest: serde_json::Map<String, Value> = map.iter()
.filter(|(k, _)| k.as_str() != "$consider")
.map(|(k, v)| (k.clone(), v.clone()))
.collect();
let matches = evaluate(&Value::Object(rest), response).unwrap_or(false);
return Ok(if is_down { !matches } else { matches });
}
if let Some(and) = map.get("$and") {
let Value::Array(clauses) = and else { bail!("$and expects array") };
return Ok(clauses.iter().all(|c| evaluate(c, response).unwrap_or(false)));

View File

@ -20,7 +20,7 @@ class QueryBuilder {
this.container = container;
this.onChange = onChange;
this.logic = '$and';
this.consider = 'up'; // 'up' | 'down'
this.invert = false; // true = wrap in $not (match = down)
this.rules = this._defaultRules();
this.render();
// Notify the form that the default query is already populated, so submitting
@ -48,7 +48,7 @@ class QueryBuilder {
.filter(Boolean);
if (conditions.length === 0) return null;
const base = conditions.length === 1 ? conditions[0] : { [this.logic]: conditions };
if (this.consider === 'down') return { $consider: 'down', ...base };
if (this.invert) return { $not: base };
return base;
}
@ -92,13 +92,19 @@ class QueryBuilder {
if (!query || typeof query !== 'object') {
this.rules = this._defaultRules();
this.logic = '$and';
this.consider = 'up';
this.invert = false;
this.render();
return;
}
this.consider = query.$consider === 'down' ? 'down' : 'up';
const q = Object.fromEntries(Object.entries(query).filter(([k]) => k !== '$consider'));
// Detect $not wrapper (inverted query = match means down)
let q = query;
if ('$not' in query && Object.keys(query).length === 1) {
this.invert = true;
q = query.$not;
} else {
this.invert = false;
}
if ('$and' in q || '$or' in q) {
this.logic = '$and' in q ? '$and' : '$or';
@ -175,10 +181,10 @@ class QueryBuilder {
this.container.innerHTML = `
<div class="space-y-3">
<div class="flex items-center gap-2 mb-4 flex-wrap">
<span class="text-sm text-gray-400">Consider</span>
<select id="qb-consider" class="bg-gray-800 border border-gray-700 text-sm rounded px-2 py-1 focus:border-blue-500 focus:outline-none font-medium ${this.consider === 'down' ? 'text-red-400' : 'text-green-400'}">
<option value="up" ${this.consider === 'up' ? 'selected' : ''}>UP</option>
<option value="down" ${this.consider === 'down' ? 'selected' : ''}>DOWN</option>
<span class="text-sm text-gray-400">Mark</span>
<select id="qb-consider" class="bg-gray-800 border border-gray-700 text-sm rounded px-2 py-1 focus:border-blue-500 focus:outline-none font-medium ${this.invert ? 'text-red-400' : 'text-green-400'}">
<option value="up" ${!this.invert ? 'selected' : ''}>UP</option>
<option value="down" ${this.invert ? 'selected' : ''}>DOWN</option>
</select>
<span class="text-sm text-gray-400">when</span>
<select id="qb-logic" class="bg-gray-800 border border-gray-700 text-gray-200 text-sm rounded px-2 py-1 focus:border-blue-500 focus:outline-none">
@ -205,7 +211,7 @@ class QueryBuilder {
`;
this.container.querySelector('#qb-consider').addEventListener('change', (e) => {
this.consider = e.target.value;
this.invert = e.target.value === 'down';
this.render();
this.onChange?.(this.getQuery());
});

View File

@ -67,7 +67,6 @@
<a href="#ql-json" class="nav-link">$json</a>
<a href="#ql-select" class="nav-link">$html</a>
<a href="#ql-logical" class="nav-link">Logical</a>
<a href="#ql-consider" class="nav-link">$consider</a>
<div class="nav-section">Guides</div>
<a href="#ql-examples" class="nav-link">Query examples</a>
@ -518,29 +517,6 @@ Content-Type: application/json
</div>
</div>
<!-- $consider -->
<div id="ql-consider" class="section">
<h2>$consider</h2>
<p>By default, matching conditions mean the monitor is <strong style="color:#4ade80">up</strong>. Set <code style="color:#93c5fd;background:#0f172a;padding:0.1em 0.35em;border-radius:0.2rem;font-size:0.78rem">"$consider": "down"</code> to flip this. If the conditions match, the monitor is <strong style="color:#f87171">down</strong>.</p>
<div class="cb">
<div class="cb-header"><span class="cb-lang">json</span></div>
<pre><span class="c">// down if response time exceeds 2 seconds</span>
{ <span class="o">"$consider"</span>: <span class="s">"down"</span>, <span class="k">"$time"</span>: { <span class="o">"$gt"</span>: <span class="n">2000</span> } }
<span class="c">// down if cert expires in less than 7 days</span>
{ <span class="o">"$consider"</span>: <span class="s">"down"</span>, <span class="k">"$certExpiry"</span>: { <span class="o">"$lt"</span>: <span class="n">7</span> } }
<span class="c">// down if any of these match</span>
{
<span class="o">"$consider"</span>: <span class="s">"down"</span>,
<span class="o">"$or"</span>: [
{ <span class="k">"status"</span>: { <span class="o">"$ge"</span>: <span class="n">500</span> } },
{ <span class="k">"$time"</span>: { <span class="o">"$gt"</span>: <span class="n">5000</span> } }
]
}</pre>
</div>
</div>
<!-- Query examples -->
<div id="ql-examples" class="section">
<h2>Query examples</h2>
@ -558,13 +534,13 @@ Content-Type: application/json
]
}</pre></div>
<h3>Performance monitor (mark down if slow)</h3>
<h3>Performance monitor (down if slow)</h3>
<div class="cb"><div class="cb-header"><span class="cb-lang">json</span></div>
<pre>{ <span class="o">"$consider"</span>: <span class="s">"down"</span>, <span class="k">"$time"</span>: { <span class="o">"$gt"</span>: <span class="n">1000</span> } }</pre></div>
<pre>{ <span class="k">"$time"</span>: { <span class="o">"$lt"</span>: <span class="n">1000</span> } }</pre></div>
<h3>Cert expiry alert</h3>
<div class="cb"><div class="cb-header"><span class="cb-lang">json</span></div>
<pre>{ <span class="o">"$consider"</span>: <span class="s">"down"</span>, <span class="k">"$certExpiry"</span>: { <span class="o">"$lt"</span>: <span class="n">14</span> } }</pre></div>
<pre>{ <span class="k">"$certExpiry"</span>: { <span class="o">"$gt"</span>: <span class="n">14</span> } }</pre></div>
<h3>Status page (HTML)</h3>
<div class="cb"><div class="cb-header"><span class="cb-lang">json</span></div>
@ -573,23 +549,19 @@ Content-Type: application/json
<h3>Page down if a JSON queue is backed up</h3>
<p>Alert when a JSON field crosses a threshold. Most monitoring tools can't compose this without a script.</p>
<div class="cb"><div class="cb-header"><span class="cb-lang">json</span></div>
<pre>{
<span class="o">"$consider"</span>: <span class="s">"down"</span>,
<span class="o">"$json"</span>: { <span class="s">"$.queue.depth"</span>: { <span class="o">"$gt"</span>: <span class="n">1000</span> } }
}</pre></div>
<pre>{ <span class="o">"$json"</span>: { <span class="s">"$.queue.depth"</span>: { <span class="o">"$le"</span>: <span class="n">1000</span> } } }</pre></div>
<h3>Down if any signal looks bad</h3>
<p>Compose multiple conditions with <code>$or</code> and flip the result with <code>$consider</code>.</p>
<p>Wrap <code>$or</code> in <code>$not</code> to mark down when any condition matches.</p>
<div class="cb"><div class="cb-header"><span class="cb-lang">json</span></div>
<pre>{
<span class="o">"$consider"</span>: <span class="s">"down"</span>,
<pre>{ <span class="o">"$not"</span>: {
<span class="o">"$or"</span>: [
{ <span class="k">"status"</span>: { <span class="o">"$ge"</span>: <span class="n">500</span> } },
{ <span class="k">"$time"</span>: { <span class="o">"$gt"</span>: <span class="n">3000</span> } },
{ <span class="o">"$json"</span>: { <span class="s">"$.healthy"</span>: { <span class="o">"$eq"</span>: <span class="n">false</span> } } },
{ <span class="o">"$html"</span>: { <span class="s">".error-banner"</span>: { <span class="o">"$exists"</span>: <span class="n">true</span> } } }
]
}</pre></div>
} }</pre></div>
<h3>Up only when everything matches</h3>
<p>Combine <code>$and</code> with header, body, and JSON checks for a strict definition of healthy.</p>