mirror of
https://github.com/Such-Software/adaptive-multi-protocol-gateway.git
synced 2026-10-17 07:31:32 +02:00
Gate route fixtures by protocol tier
This commit is contained in:
+29
-1
@@ -9,6 +9,22 @@ from .config import GatewayConfig, ProtocolConfig, RoutePolicyConfig, SiteConfig
|
||||
|
||||
|
||||
SCHEMA = "ampg.fixture-manifest.v1"
|
||||
TIER_ORDER = {
|
||||
"static": 0,
|
||||
"interactive-lite": 1,
|
||||
"identity": 2,
|
||||
"transactional": 3,
|
||||
"realtime": 4,
|
||||
"internal": 5,
|
||||
}
|
||||
DEFAULT_MAX_TIERS = {
|
||||
"clearnet": "realtime",
|
||||
"tor": "transactional",
|
||||
"i2p": "transactional",
|
||||
"gemini": "interactive-lite",
|
||||
"ipfs": "static",
|
||||
"reticulum": "interactive-lite",
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -27,7 +43,7 @@ def fixture_manifest(site: SiteConfig) -> dict[str, Any]:
|
||||
fixtures.extend(
|
||||
_fixture_entry(site, protocol, route_policy)
|
||||
for route_policy in site.interactions.routes
|
||||
if _route_is_public(route_policy)
|
||||
if _route_is_public(route_policy) and _route_supported(protocol, route_policy)
|
||||
)
|
||||
return {
|
||||
"schema": SCHEMA,
|
||||
@@ -101,6 +117,18 @@ def _route_is_public(route_policy: RoutePolicyConfig) -> bool:
|
||||
return route_policy.public_allowed and route_policy.tier != "internal"
|
||||
|
||||
|
||||
def _route_supported(protocol: ProtocolConfig, route_policy: RoutePolicyConfig) -> bool:
|
||||
max_tier = str(protocol.options.get("max_tier", DEFAULT_MAX_TIERS.get(protocol.name, "static")))
|
||||
return _tier_rank(route_policy.tier) <= _tier_rank(max_tier)
|
||||
|
||||
|
||||
def _tier_rank(tier: str) -> int:
|
||||
try:
|
||||
return TIER_ORDER[tier]
|
||||
except KeyError as exc:
|
||||
raise ValueError(f"unsupported interaction tier {tier!r}") from exc
|
||||
|
||||
|
||||
def _interaction_policy(
|
||||
protocol: ProtocolConfig,
|
||||
route_policy: RoutePolicyConfig | None,
|
||||
|
||||
@@ -195,6 +195,14 @@ CONFIG_FIELDS = (
|
||||
"strip",
|
||||
"Script handling policy for privacy-html render targets.",
|
||||
),
|
||||
ConfigField(
|
||||
"site.protocols.<name>",
|
||||
"max_tier",
|
||||
"enum",
|
||||
False,
|
||||
"transport default",
|
||||
"Maximum interaction tier this protocol target may expose.",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -65,3 +65,7 @@ not emitted.
|
||||
|
||||
Routes imported from `site.interactions.route_manifest` are treated the same way as inline
|
||||
TOML routes.
|
||||
|
||||
AMPG also omits route fixtures whose interaction tier exceeds the protocol target's
|
||||
`max_tier`. When `max_tier` is not configured, AMPG uses the transport capability defaults
|
||||
from the generated interaction capability matrix.
|
||||
|
||||
@@ -28,3 +28,4 @@ This file is generated from code. Do not edit it by hand.
|
||||
| site.protocols.<name> | `daemon_policy` | enum | no | protocol default | Whether AMPG adopts, manages, or only renders config. |
|
||||
| site.protocols.<name> | `max_asset_bytes` | integer | no | 1048576 | Maximum asset size copied by privacy-html render targets. |
|
||||
| site.protocols.<name> | `script_policy` | enum | no | strip | Script handling policy for privacy-html render targets. |
|
||||
| site.protocols.<name> | `max_tier` | enum | no | transport default | Maximum interaction tier this protocol target may expose. |
|
||||
|
||||
@@ -303,6 +303,70 @@ renderer = "privacy-html"
|
||||
with self.assertRaisesRegex(ValueError, "route manifest schema"):
|
||||
load_config(config_path)
|
||||
|
||||
def test_manifest_omits_routes_above_protocol_max_tier(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root = Path(tmp)
|
||||
source = root / "site"
|
||||
source.mkdir()
|
||||
(source / "index.html").write_text("<h1>App</h1>", encoding="utf-8")
|
||||
config_path = root / "gateway.toml"
|
||||
config_path.write_text(
|
||||
"""
|
||||
[[site]]
|
||||
id = "app"
|
||||
domain = "app.test"
|
||||
|
||||
[site.source]
|
||||
kind = "static-html"
|
||||
path = "./site"
|
||||
|
||||
[site.outputs]
|
||||
root = "./out"
|
||||
plan_root = "./plan"
|
||||
|
||||
[[site.interactions.route]]
|
||||
match = "/play/*"
|
||||
tier = "interactive-lite"
|
||||
|
||||
[[site.interactions.route]]
|
||||
match = "/checkout/*"
|
||||
tier = "transactional"
|
||||
|
||||
[[site.interactions.route]]
|
||||
match = "/live/*"
|
||||
tier = "realtime"
|
||||
realtime = true
|
||||
|
||||
[site.protocols.tor]
|
||||
enabled = true
|
||||
renderer = "privacy-html"
|
||||
max_tier = "interactive-lite"
|
||||
|
||||
[site.protocols.gemini]
|
||||
enabled = true
|
||||
renderer = "gemtext"
|
||||
|
||||
[site.protocols.ipfs]
|
||||
enabled = true
|
||||
renderer = "clearnet"
|
||||
cid = "bafyexample"
|
||||
""",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
config = load_config(config_path)
|
||||
manifest = fixture_manifest(config.sites[0])
|
||||
|
||||
by_protocol = {}
|
||||
for fixture in manifest["fixtures"]:
|
||||
by_protocol.setdefault(fixture["protocol"], []).append(
|
||||
fixture.get("route", {}).get("fixture_path", "/")
|
||||
)
|
||||
|
||||
self.assertEqual(["/", "/play/"], by_protocol["tor"])
|
||||
self.assertEqual(["/", "/play/"], by_protocol["gemini"])
|
||||
self.assertEqual(["/"], by_protocol["ipfs"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user