fix(protocol): align quality ack schema
This commit is contained in:
@@ -14,7 +14,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const SchemaSHA256 = "8c1ed430127cf5774a919f14ff4770b2509d322c8518bf30c412e1e89eeffced"
|
||||
const SchemaSHA256 = "b9e72838b2ae02b3a47e7e2dfa03620b8580f2c98668e6eaf551ea8fb642d297"
|
||||
const ProtocolVersion = "1.0.0"
|
||||
const CurrentWireVersion = "2"
|
||||
const NMinus1WireVersion = "1"
|
||||
|
||||
+1
-1
@@ -14,5 +14,5 @@
|
||||
},
|
||||
"generator_sha256": "c5756780f59a82d8f56762e52f58e22a4d6aca6648157f45e5bad6559b9deeec",
|
||||
"protocol_version": "1.0.0",
|
||||
"schema_sha256": "8c1ed430127cf5774a919f14ff4770b2509d322c8518bf30c412e1e89eeffced"
|
||||
"schema_sha256": "b9e72838b2ae02b3a47e7e2dfa03620b8580f2c98668e6eaf551ea8fb642d297"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by tools/generate.py; DO NOT EDIT.
|
||||
#![allow(non_snake_case)]
|
||||
pub const SCHEMA_SHA256: &str = "8c1ed430127cf5774a919f14ff4770b2509d322c8518bf30c412e1e89eeffced";
|
||||
pub const SCHEMA_SHA256: &str = "b9e72838b2ae02b3a47e7e2dfa03620b8580f2c98668e6eaf551ea8fb642d297";
|
||||
pub const CURRENT_WIRE_VERSION: &str = "2";
|
||||
pub const N_MINUS_1_WIRE_VERSION: &str = "1";
|
||||
pub const N_MINUS_2_WIRE_VERSION: &str = "0";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by tools/generate.py; DO NOT EDIT.
|
||||
import Foundation
|
||||
public typealias JSONObject = [String: String]
|
||||
public let schemaSHA256 = "8c1ed430127cf5774a919f14ff4770b2509d322c8518bf30c412e1e89eeffced"
|
||||
public let schemaSHA256 = "b9e72838b2ae02b3a47e7e2dfa03620b8580f2c98668e6eaf551ea8fb642d297"
|
||||
public let currentWireVersion = "2"
|
||||
public let nMinus1WireVersion = "1"
|
||||
public let nMinus2WireVersion = "0"
|
||||
|
||||
@@ -440,7 +440,7 @@ paths:
|
||||
post:
|
||||
tags: [gateway]
|
||||
operationId: acknowledgeGatewayQualityWork
|
||||
description: The authenticated gateway mTLS certificate identity MUST match `gateway_id`; Server authority MUST exactly match the session, gateway, reconnect sequence, operation, revision, and monotonic lease generation tuple. Stale lease generations MUST be rejected. Maximum JSON body: 16384 bytes.
|
||||
description: The authenticated gateway mTLS certificate identity MUST match `gateway_id`; Server authority MUST exactly match the session, gateway, reconnect sequence, operation, revision, and monotonic lease generation tuple. Stale lease generations MUST be rejected. `applied` requires `current_applied_revision == revision`; `proven_prior` requires `current_applied_revision < revision`; `unknown` forbids `current_applied_revision` and makes no applied-revision assertion. Maximum JSON body: 16384 bytes.
|
||||
security: [{gatewayMutualTLS: []}]
|
||||
requestBody: {required: true, description: 'Maximum JSON body: 16384 bytes.', content: {application/json: {schema: {$ref: ../schemas/control-v1.schema.json#/$defs/GatewayQualityAck}}}}
|
||||
responses:
|
||||
|
||||
@@ -740,7 +740,13 @@
|
||||
"GatewayQualityAck": {
|
||||
"type": "object", "additionalProperties": false,
|
||||
"x-max-bytes": 16384,
|
||||
"description": "Outcome invariants: applied requires current_applied_revision equal to revision; proven_prior requires current_applied_revision strictly less than revision; unknown forbids current_applied_revision and makes no applied-revision assertion.",
|
||||
"required": ["version", "session_id", "gateway_id", "reconnect_sequence", "operation_id", "revision", "lease_generation", "outcome"],
|
||||
"oneOf": [
|
||||
{"properties": {"outcome": {"const": "applied"}}, "required": ["current_applied_revision"]},
|
||||
{"properties": {"outcome": {"const": "proven_prior"}}, "required": ["current_applied_revision"]},
|
||||
{"properties": {"outcome": {"const": "unknown"}}, "not": {"required": ["current_applied_revision"]}}
|
||||
],
|
||||
"properties": {
|
||||
"version": {"type": "string", "const": "1"}, "session_id": {"type": "string", "minLength": 1, "maxLength": 128},
|
||||
"gateway_id": {"type": "string", "minLength": 1, "maxLength": 128}, "reconnect_sequence": {"type": "integer", "minimum": 0},
|
||||
|
||||
+40
-2
@@ -12,6 +12,17 @@ import sys
|
||||
ROOT = pathlib.Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def matches_outcome_branch(instance: dict[str, object], branch: dict[str, object]) -> bool:
|
||||
required = branch.get("required", [])
|
||||
if not all(field in instance for field in required):
|
||||
return False
|
||||
outcome = branch.get("properties", {}).get("outcome", {}).get("const")
|
||||
if instance.get("outcome") != outcome:
|
||||
return False
|
||||
forbidden = branch.get("not", {}).get("required", [])
|
||||
return not all(field in instance for field in forbidden) if forbidden else True
|
||||
|
||||
|
||||
def main() -> int:
|
||||
schema_path = ROOT / "schemas/control-v1.schema.json"
|
||||
schema = json.loads(schema_path.read_text(encoding="utf-8"))
|
||||
@@ -59,8 +70,32 @@ def main() -> int:
|
||||
assert quality_request["properties"]["lease_generation"]["minimum"] == 1
|
||||
assert defs["GatewayStopWorkRequest"]["required"] == ["version", "session_id", "gateway_id", "reconnect_sequence", "acquisition"]
|
||||
assert defs["GatewayQualityWork"]["required"][-3:] == ["lease_generation", "lease_expires_at", "selected_descriptor"]
|
||||
assert defs["GatewayQualityAck"]["required"][-3:] == ["revision", "lease_generation", "outcome"]
|
||||
assert defs["GatewayQualityAck"]["properties"]["outcome"]["enum"] == ["applied", "proven_prior", "unknown"]
|
||||
quality_ack = defs["GatewayQualityAck"]
|
||||
assert quality_ack["required"][-3:] == ["revision", "lease_generation", "outcome"]
|
||||
assert quality_ack["properties"]["outcome"]["enum"] == ["applied", "proven_prior", "unknown"]
|
||||
assert quality_ack["description"] == (
|
||||
"Outcome invariants: applied requires current_applied_revision equal to revision; "
|
||||
"proven_prior requires current_applied_revision strictly less than revision; "
|
||||
"unknown forbids current_applied_revision and makes no applied-revision assertion."
|
||||
)
|
||||
assert quality_ack["oneOf"] == [
|
||||
{"properties": {"outcome": {"const": "applied"}}, "required": ["current_applied_revision"]},
|
||||
{"properties": {"outcome": {"const": "proven_prior"}}, "required": ["current_applied_revision"]},
|
||||
{"properties": {"outcome": {"const": "unknown"}}, "not": {"required": ["current_applied_revision"]}},
|
||||
]
|
||||
ack_branches = quality_ack["oneOf"]
|
||||
for valid_ack in (
|
||||
{"outcome": "applied", "current_applied_revision": 7},
|
||||
{"outcome": "proven_prior", "current_applied_revision": 6},
|
||||
{"outcome": "unknown"},
|
||||
):
|
||||
assert sum(matches_outcome_branch(valid_ack, branch) for branch in ack_branches) == 1, valid_ack
|
||||
for invalid_ack in (
|
||||
{"outcome": "applied"},
|
||||
{"outcome": "proven_prior"},
|
||||
{"outcome": "unknown", "current_applied_revision": 6},
|
||||
):
|
||||
assert not any(matches_outcome_branch(invalid_ack, branch) for branch in ack_branches), invalid_ack
|
||||
|
||||
operation_id_pattern = r"^(?!00000000-0000-0000-0000-000000000000$)[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
|
||||
canonical_time_pattern = r"^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(?:\.[0-9]{0,8}[1-9])?Z$"
|
||||
@@ -235,6 +270,9 @@ def main() -> int:
|
||||
assert "coordinates MUST match exactly" in quality_acquisition
|
||||
quality_acknowledgement = openapi.split(" operationId: acknowledgeGatewayQualityWork\n", 1)[1].split(" responses:\n", 1)[0]
|
||||
assert "Stale lease generations MUST be rejected" in quality_acknowledgement
|
||||
assert "`applied` requires `current_applied_revision == revision`" in quality_acknowledgement
|
||||
assert "`proven_prior` requires `current_applied_revision < revision`" in quality_acknowledgement
|
||||
assert "`unknown` forbids `current_applied_revision`" in quality_acknowledgement
|
||||
for operation_id in ("createSessionQualityChange", "getSessionQualityChange", "createSessionStopOperation", "getSessionStopOperation"):
|
||||
operation = openapi.split(f" operationId: {operation_id}\n", 1)[1].split(" responses:\n", 1)[0]
|
||||
assert "owning principal and active device/key" in operation, operation_id
|
||||
|
||||
Reference in New Issue
Block a user