Revert "feat: grouped query builder with $upIf/$downIf condition groups"
This reverts commit 99b59070a2.
This commit is contained in:
parent
99b59070a2
commit
5328471229
|
|
@ -50,17 +50,6 @@ pub struct Response {
|
||||||
pub fn evaluate(query: &Value, response: &Response) -> Result<bool> {
|
pub fn evaluate(query: &Value, response: &Response) -> Result<bool> {
|
||||||
match query {
|
match query {
|
||||||
Value::Object(map) => {
|
Value::Object(map) => {
|
||||||
// $upIf / $downIf — named groups, $downIf takes precedence
|
|
||||||
if map.contains_key("$upIf") || map.contains_key("$downIf") {
|
|
||||||
if let Some(down) = map.get("$downIf") {
|
|
||||||
if evaluate(down, response).unwrap_or(false) { return Ok(false); }
|
|
||||||
}
|
|
||||||
if let Some(up) = map.get("$upIf") {
|
|
||||||
return evaluate(up, response);
|
|
||||||
}
|
|
||||||
return Ok(true); // only $downIf set and didn't match → up
|
|
||||||
}
|
|
||||||
|
|
||||||
// $and / $or / $not
|
// $and / $or / $not
|
||||||
if let Some(and) = map.get("$and") {
|
if let Some(and) = map.get("$and") {
|
||||||
let Value::Array(clauses) = and else { bail!("$and expects array") };
|
let Value::Array(clauses) = and else { bail!("$and expects array") };
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm text-gray-400 mb-1.5">Conditions <span class="text-gray-600">(optional)</span></label>
|
<label class="block text-sm text-gray-400 mb-1.5">Query Conditions <span class="text-gray-600">(optional)</span></label>
|
||||||
<p class="text-xs text-gray-600 mb-3">Define up/down conditions. <span class="text-red-400/70">Down if</span> takes priority over <span class="text-green-400/70">Up if</span>. Leave empty to use default (status < 400).</p>
|
<p class="text-xs text-gray-600 mb-3">Define when this monitor should be considered "up". Defaults to status < 400.</p>
|
||||||
<div id="query-builder"></div>
|
<div id="query-builder"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -71,7 +71,7 @@
|
||||||
if (!requireAuth()) throw 'auth';
|
if (!requireAuth()) throw 'auth';
|
||||||
|
|
||||||
let currentQuery = null;
|
let currentQuery = null;
|
||||||
const qb = new GroupedQueryBuilder(document.getElementById('query-builder'), (q) => {
|
const qb = new QueryBuilder(document.getElementById('query-builder'), (q) => {
|
||||||
currentQuery = q;
|
currentQuery = q;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -304,150 +304,3 @@ class QueryBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Grouped Query Builder ─────────────────────────────────────────────────────
|
|
||||||
// Two named condition groups: "Up if..." and "Down if..."
|
|
||||||
// $downIf takes precedence over $upIf. Both optional.
|
|
||||||
|
|
||||||
class GroupedQueryBuilder {
|
|
||||||
constructor(container, onChange) {
|
|
||||||
this.container = container;
|
|
||||||
this.onChange = onChange;
|
|
||||||
this.groups = {
|
|
||||||
upIf: new QueryBuilder(null, () => this._emit()),
|
|
||||||
downIf: new QueryBuilder(null, () => this._emit()),
|
|
||||||
};
|
|
||||||
this.groups.upIf.rules = [];
|
|
||||||
this.groups.downIf.rules = [];
|
|
||||||
this._render();
|
|
||||||
}
|
|
||||||
|
|
||||||
_emit() {
|
|
||||||
this.onChange?.(this.getQuery());
|
|
||||||
}
|
|
||||||
|
|
||||||
getQuery() {
|
|
||||||
const up = this.groups.upIf.rules.length ? this.groups.upIf.getQuery() : null;
|
|
||||||
const down = this.groups.downIf.rules.length ? this.groups.downIf.getQuery() : null;
|
|
||||||
if (!up && !down) return null;
|
|
||||||
const q = {};
|
|
||||||
if (up) q.$upIf = up;
|
|
||||||
if (down) q.$downIf = down;
|
|
||||||
return q;
|
|
||||||
}
|
|
||||||
|
|
||||||
setQuery(query) {
|
|
||||||
if (!query || typeof query !== 'object') return;
|
|
||||||
if (query.$upIf) { this.groups.upIf.setQuery(query.$upIf); }
|
|
||||||
if (query.$downIf) { this.groups.downIf.setQuery(query.$downIf); }
|
|
||||||
this._render();
|
|
||||||
}
|
|
||||||
|
|
||||||
_render() {
|
|
||||||
this.container.innerHTML = `
|
|
||||||
<div class="space-y-4">
|
|
||||||
<div id="gqb-upif"></div>
|
|
||||||
<div id="gqb-downif"></div>
|
|
||||||
<div class="mt-3 p-3 bg-gray-950 rounded border border-gray-800">
|
|
||||||
<div class="flex items-center justify-between mb-1">
|
|
||||||
<span class="text-xs text-gray-500 font-mono">Query JSON</span>
|
|
||||||
<button id="gqb-copy" class="text-xs text-blue-400 hover:text-blue-300">Copy</button>
|
|
||||||
</div>
|
|
||||||
<pre id="gqb-preview" class="text-xs text-gray-300 font-mono whitespace-pre-wrap overflow-x-auto"></pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
this._renderGroup('upIf', this.container.querySelector('#gqb-upif'), '🟢 Up if', 'text-green-400', 'border-green-900/50');
|
|
||||||
this._renderGroup('downIf', this.container.querySelector('#gqb-downif'), '🔴 Down if', 'text-red-400', 'border-red-900/50');
|
|
||||||
this._updatePreview();
|
|
||||||
|
|
||||||
this.container.querySelector('#gqb-copy').addEventListener('click', () => {
|
|
||||||
const q = this.getQuery();
|
|
||||||
navigator.clipboard.writeText(JSON.stringify(q ?? {}, null, 2));
|
|
||||||
const btn = this.container.querySelector('#gqb-copy');
|
|
||||||
btn.textContent = 'Copied!';
|
|
||||||
setTimeout(() => btn.textContent = 'Copy', 1500);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_renderGroup(key, el, label, labelClass, borderClass) {
|
|
||||||
const group = this.groups[key];
|
|
||||||
const hasRules = group.rules.length > 0;
|
|
||||||
|
|
||||||
el.innerHTML = `
|
|
||||||
<div class="rounded border ${borderClass} bg-gray-900/50 p-3">
|
|
||||||
<div class="flex items-center justify-between mb-3">
|
|
||||||
<span class="text-sm font-medium ${labelClass}">${label}</span>
|
|
||||||
<div class="flex items-center gap-3">
|
|
||||||
${hasRules ? `
|
|
||||||
<div class="flex items-center gap-1.5">
|
|
||||||
<span class="text-xs text-gray-500">Match</span>
|
|
||||||
<select id="gqb-logic-${key}" class="bg-gray-800 border border-gray-700 text-gray-200 text-xs rounded px-2 py-1 focus:border-blue-500 focus:outline-none">
|
|
||||||
<option value="$and" ${group.logic === '$and' ? 'selected' : ''}>ALL</option>
|
|
||||||
<option value="$or" ${group.logic === '$or' ? 'selected' : ''}>ANY</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
` : ''}
|
|
||||||
<button id="gqb-add-${key}" class="text-xs text-blue-400 hover:text-blue-300">+ Add condition</button>
|
|
||||||
${hasRules ? `<button id="gqb-clear-${key}" class="text-xs text-gray-600 hover:text-gray-400">Clear</button>` : ''}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
${hasRules ? `
|
|
||||||
<div id="gqb-rules-${key}" class="space-y-2">
|
|
||||||
${group.rules.map((rule, i) => group._renderRule(rule, i)).join('')}
|
|
||||||
</div>
|
|
||||||
` : `<p class="text-xs text-gray-600 italic">No conditions — defaults to status < 400</p>`}
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
// Bind logic toggle
|
|
||||||
const logicSel = el.querySelector(`#gqb-logic-${key}`);
|
|
||||||
if (logicSel) {
|
|
||||||
logicSel.addEventListener('change', (e) => {
|
|
||||||
group.logic = e.target.value;
|
|
||||||
this._renderGroup(key, el, label, labelClass, borderClass);
|
|
||||||
this._updatePreview();
|
|
||||||
this._emit();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind add
|
|
||||||
el.querySelector(`#gqb-add-${key}`).addEventListener('click', () => {
|
|
||||||
group.rules.push(group._emptyRule());
|
|
||||||
this._renderGroup(key, el, label, labelClass, borderClass);
|
|
||||||
this._updatePreview();
|
|
||||||
this._emit();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Bind clear
|
|
||||||
const clearBtn = el.querySelector(`#gqb-clear-${key}`);
|
|
||||||
if (clearBtn) {
|
|
||||||
clearBtn.addEventListener('click', () => {
|
|
||||||
group.rules = [];
|
|
||||||
this._renderGroup(key, el, label, labelClass, borderClass);
|
|
||||||
this._updatePreview();
|
|
||||||
this._emit();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind rule events — wrap onChange to re-render preview
|
|
||||||
if (hasRules) {
|
|
||||||
group.container = el.querySelector(`#gqb-rules-${key}`);
|
|
||||||
group.onChange = () => { this._updatePreview(); this._emit(); };
|
|
||||||
el.querySelectorAll('.qb-rule').forEach((ruleEl, i) => {
|
|
||||||
group._bindRuleEvents(ruleEl, i);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_updatePreview() {
|
|
||||||
const q = this.getQuery();
|
|
||||||
const pre = this.container.querySelector('#gqb-preview');
|
|
||||||
if (pre) {
|
|
||||||
pre.innerHTML = q
|
|
||||||
? escapeHtml(JSON.stringify(q, null, 2))
|
|
||||||
: '<span class="text-gray-600">No conditions — defaults to status < 400</span>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -86,13 +86,6 @@ export function evaluate(query: unknown, ctx: EvalContext): boolean {
|
||||||
|
|
||||||
const q = query as Record<string, unknown>;
|
const q = query as Record<string, unknown>;
|
||||||
|
|
||||||
// $upIf / $downIf — named condition groups, $downIf takes precedence
|
|
||||||
if ("$upIf" in q || "$downIf" in q) {
|
|
||||||
if ("$downIf" in q && evaluate(q.$downIf, ctx)) return false;
|
|
||||||
if ("$upIf" in q) return evaluate(q.$upIf, ctx);
|
|
||||||
return true; // only $downIf set and it didn't match → up
|
|
||||||
}
|
|
||||||
|
|
||||||
// $and
|
// $and
|
||||||
if ("$and" in q) {
|
if ("$and" in q) {
|
||||||
const clauses = q.$and;
|
const clauses = q.$and;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue