From 808d402feb7a9512e7186010ca75effdf7366d36 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Tue, 15 Sep 2026 11:48:35 +0200 Subject: [PATCH] fix(gates): gate 116 accepts a switch and a disabled message hydra#677 adds switch {configKey, jsonPath?, offValues?} and disabledMessage. The vendored schema refused both as additional properties, so the first app to declare a switch would get a false warning. The copy now follows integriq 64b437f. --- .../lib/check_connections_declaration.js | 2 +- .../lib/test_check_connections_declaration.js | 16 +++++++++ .../scripts/schemas/connections.schema.json | 35 +++++++++++++++++-- 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/hydra-gates/scripts/lib/check_connections_declaration.js b/hydra-gates/scripts/lib/check_connections_declaration.js index be362a76..5b38e644 100644 --- a/hydra-gates/scripts/lib/check_connections_declaration.js +++ b/hydra-gates/scripts/lib/check_connections_declaration.js @@ -45,7 +45,7 @@ const { spawnSync } = require('child_process') const DECLARATION = 'lib/Settings/connections.json' const SCHEMA_PATH = path.resolve(__dirname, '..', 'schemas', 'connections.schema.json') -const SOURCE_COMMIT = 'a93665880f7f552d8280b84a1f5ce402507c4466' +const SOURCE_COMMIT = '64b437fc2df24827985ce6e919fe5e47c5205617' const ANCHOR_DIRS = ['src', 'templates'] const SKIP_DIRS = new Set(['node_modules', 'vendor', 'dist', 'build', 'custom_apps', '.git']) const TEXT_FILE = /\.(vue|js|mjs|cjs|ts|tsx|jsx|php|html|json|md|twig)$/ diff --git a/hydra-gates/scripts/lib/test_check_connections_declaration.js b/hydra-gates/scripts/lib/test_check_connections_declaration.js index 2c41658f..a08694fb 100644 --- a/hydra-gates/scripts/lib/test_check_connections_declaration.js +++ b/hydra-gates/scripts/lib/test_check_connections_declaration.js @@ -163,6 +163,22 @@ const findings = (out) => out.split('\n').filter((l) => l.startsWith('FAIL ')) assert(bad.status === 1 && findings(bad.stdout).some((l) => l.includes('/connections/5/requiredConfig/0')), `required object: an entry without jsonPath is refused (got ${bad.status}: ${bad.stdout.trim()})`) } +// --- ARM 1d: hydra#677 adds a switch and a disabled message ---------------------- +{ + const withSwitch = JSON.parse(JSON.stringify(CLEAN)) + withSwitch.connections.push( + { key: 'hibp', title: 'Breach check', switch: { configKey: 'breach_check_enabled' }, disabledMessage: 'An admin switched the breach check off.' }, + { key: 'geo-db', title: 'Visitor geography', switch: { configKey: 'traffic', jsonPath: 'geo.provider', offValues: ['none'] } }, + ) + const r = run(makeApp('switch', { declaration: withSwitch })) + assert(r.status === 0 && findings(r.stdout).length === 0, `switch: switch and disabledMessage validate (got ${r.status}: ${r.stdout.trim()})`) + + const noKey = JSON.parse(JSON.stringify(CLEAN)) + noKey.connections.push({ key: 'hibp', title: 'Breach check', switch: { offValues: ['off'] } }) + const bad = run(makeApp('switch-bad', { declaration: noKey })) + assert(bad.status === 1 && findings(bad.stdout).some((l) => l.includes('/connections/5/switch')), `switch: a switch without configKey is refused (got ${bad.status}: ${bad.stdout.trim()})`) +} + // --- ARM 2: rule 1, the schema -------------------------------------------------- { const bad = JSON.parse(JSON.stringify(CLEAN)) diff --git a/hydra-gates/scripts/schemas/connections.schema.json b/hydra-gates/scripts/schemas/connections.schema.json index b5e2c80e..d83bed3f 100644 --- a/hydra-gates/scripts/schemas/connections.schema.json +++ b/hydra-gates/scripts/schemas/connections.schema.json @@ -1,7 +1,7 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://conduction.nl/schemas/integriq/connections.schema.json", - "$comment": "Vendored for gate-116 (connections-declaration) from ConductionNL/integriq lib/Settings/connections.schema.json at commit a93665880f7f552d8280b84a1f5ce402507c4466 (integriq#1996 amended by #2010 and #2022, development). Integriq owns this file. To update it, copy the file again and change the commit here and in SOURCE_COMMIT in scripts/lib/check_connections_declaration.js.", + "$comment": "Vendored for gate-116 (connections-declaration) from ConductionNL/integriq lib/Settings/connections.schema.json at commit 64b437fc2df24827985ce6e919fe5e47c5205617 (integriq#1996 amended by #2010, #2022 and #2024, development). Integriq owns this file. To update it, copy the file again and change the commit here and in SOURCE_COMMIT in scripts/lib/check_connections_declaration.js.", "title": "Connection declarations", "description": "The shape of lib/Settings/connections.json. An app lists its outside connections here, and integriq turns each entry into one connection row. Contract: hydra openspec/changes/connection-registry/design.md, section D2. Integriq's runtime check is lib/Service/ConnectionDeclarationValidator.php, which mirrors this file rule for rule.", "type": "object", @@ -62,7 +62,7 @@ }, "requiredConfig": { "type": "array", - "description": "Settings of the declaring app that must all be filled. Each entry is an app-config key, always read as the whole key even when it contains dots, or {configKey, jsonPath} to read one value inside a JSON setting. An empty string, false, 0, null or a missing path reads as not filled.", + "description": "Settings of the declaring app that must all be filled. Each entry is an app-config key, always read as the whole key even when it contains dots, or {configKey, jsonPath} to read one value inside a JSON setting. An empty string, false, 0, null, an empty JSON list or object, or a missing path reads as not filled.", "items": { "oneOf": [ { @@ -130,6 +130,37 @@ "type": "string", "description": "Why the connection is not usable." }, + "switch": { + "type": "object", + "additionalProperties": false, + "required": [ + "configKey" + ], + "description": "The setting an admin uses to turn the connection off. Without offValues the connection is off when the value is empty. With offValues it is off only when the value is one of them.", + "properties": { + "configKey": { + "type": "string", + "minLength": 1, + "description": "App-config key that holds the switch." + }, + "jsonPath": { + "type": "string", + "pattern": "^[^.]+(\\.[^.]+)*$", + "description": "Dot path to the switch when configKey holds a JSON object, such as breach.enabled. A missing path reads as empty." + }, + "offValues": { + "type": "array", + "description": "The values that mean the connection is off. Compared case-insensitively after trimming. An unset key is off only when an empty string is listed.", + "items": { + "type": "string" + } + } + } + }, + "disabledMessage": { + "type": "string", + "description": "Shown while the connection is switched off. Default: Switched off in the app's settings." + }, "unconfiguredMessage": { "type": "string", "description": "Shown while nothing is known about the connection yet. Default: Not checked yet."