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"
|
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)
|
@dataclass(frozen=True)
|
||||||
@@ -27,7 +43,7 @@ def fixture_manifest(site: SiteConfig) -> dict[str, Any]:
|
|||||||
fixtures.extend(
|
fixtures.extend(
|
||||||
_fixture_entry(site, protocol, route_policy)
|
_fixture_entry(site, protocol, route_policy)
|
||||||
for route_policy in site.interactions.routes
|
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 {
|
return {
|
||||||
"schema": SCHEMA,
|
"schema": SCHEMA,
|
||||||
@@ -101,6 +117,18 @@ def _route_is_public(route_policy: RoutePolicyConfig) -> bool:
|
|||||||
return route_policy.public_allowed and route_policy.tier != "internal"
|
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(
|
def _interaction_policy(
|
||||||
protocol: ProtocolConfig,
|
protocol: ProtocolConfig,
|
||||||
route_policy: RoutePolicyConfig | None,
|
route_policy: RoutePolicyConfig | None,
|
||||||
|
|||||||
@@ -195,6 +195,14 @@ CONFIG_FIELDS = (
|
|||||||
"strip",
|
"strip",
|
||||||
"Script handling policy for privacy-html render targets.",
|
"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
|
Routes imported from `site.interactions.route_manifest` are treated the same way as inline
|
||||||
TOML routes.
|
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> | `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> | `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> | `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"):
|
with self.assertRaisesRegex(ValueError, "route manifest schema"):
|
||||||
load_config(config_path)
|
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__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user