mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-08-26 17:31:34 +00:00
fix(routing): deliver meshcore_broadcast via meshcore_channel through CompositeTransport
The hinted _broadcast path passed the channel NAME on the `channel` kwarg, which MeshCoreTransport ignores (it reads meshcore_channel), so meshcore_broadcast silently no-op'd on transport=both configs. Route the meshcore child via meshcore_channel and the meshtastic child via channel. Fix the test that asserted the broken kwarg layout. Add the new delivery types to the remaining enumeration/validation sites (channel-test endpoint, scheduler digest chunking, danger-zone valid set). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3b2813cf06
commit
8e6acb9c52
5 changed files with 24 additions and 8 deletions
|
|
@ -550,7 +550,7 @@ class NotificationRuleConfig:
|
||||||
custom_message: str = ""
|
custom_message: str = ""
|
||||||
|
|
||||||
# Delivery type
|
# Delivery type
|
||||||
delivery_type: str = "" # mesh_broadcast, mesh_dm, email, webhook
|
delivery_type: str = "" # mesh_broadcast, mesh_dm, meshcore_broadcast, meshcore_dm, email, webhook
|
||||||
|
|
||||||
# Mesh broadcast fields
|
# Mesh broadcast fields
|
||||||
broadcast_channel: int = 0
|
broadcast_channel: int = 0
|
||||||
|
|
@ -697,7 +697,8 @@ _DZ_VALID_ROLES = frozenset({
|
||||||
})
|
})
|
||||||
|
|
||||||
_DZ_VALID_DELIVERY = frozenset({
|
_DZ_VALID_DELIVERY = frozenset({
|
||||||
"mesh_broadcast", "mesh_dm", "email", "webhook", "none",
|
"mesh_broadcast", "mesh_dm", "meshcore_broadcast", "meshcore_dm",
|
||||||
|
"email", "webhook", "none",
|
||||||
})
|
})
|
||||||
# Hazard families that map onto categories.VALID_TOGGLES. snow is a sub-gate of
|
# Hazard families that map onto categories.VALID_TOGGLES. snow is a sub-gate of
|
||||||
# weather and flood a sub-gate of seismic (resolved in the correlator), so they
|
# weather and flood a sub-gate of seismic (resolved in the correlator), so they
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,15 @@ class TestRequest(BaseModel):
|
||||||
|
|
||||||
class ChannelTestRequest(BaseModel):
|
class ChannelTestRequest(BaseModel):
|
||||||
"""Request body for channel connectivity test."""
|
"""Request body for channel connectivity test."""
|
||||||
type: str # mesh_broadcast, mesh_dm, email, webhook
|
type: str # mesh_broadcast, mesh_dm, meshcore_broadcast, meshcore_dm, email, webhook
|
||||||
# Mesh broadcast
|
# Mesh broadcast
|
||||||
channel_index: Optional[int] = 0
|
channel_index: Optional[int] = 0
|
||||||
# Mesh DM
|
# Mesh DM
|
||||||
node_ids: Optional[List[str]] = []
|
node_ids: Optional[List[str]] = []
|
||||||
|
# MeshCore broadcast
|
||||||
|
meshcore_channel: Optional[str] = None
|
||||||
|
# MeshCore DM
|
||||||
|
meshcore_dm_contacts: Optional[List[str]] = []
|
||||||
# Email
|
# Email
|
||||||
smtp_host: Optional[str] = ""
|
smtp_host: Optional[str] = ""
|
||||||
smtp_port: Optional[int] = 587
|
smtp_port: Optional[int] = 587
|
||||||
|
|
@ -117,6 +121,10 @@ async def test_channel(request: Request, body: ChannelTestRequest):
|
||||||
channel_config["channel_index"] = body.channel_index or 0
|
channel_config["channel_index"] = body.channel_index or 0
|
||||||
elif body.type == "mesh_dm":
|
elif body.type == "mesh_dm":
|
||||||
channel_config["node_ids"] = body.node_ids or []
|
channel_config["node_ids"] = body.node_ids or []
|
||||||
|
elif body.type == "meshcore_broadcast":
|
||||||
|
channel_config["meshcore_channel"] = body.meshcore_channel or ""
|
||||||
|
elif body.type == "meshcore_dm":
|
||||||
|
channel_config["meshcore_dm_contacts"] = body.meshcore_dm_contacts or []
|
||||||
elif body.type == "email":
|
elif body.type == "email":
|
||||||
channel_config.update({
|
channel_config.update({
|
||||||
"smtp_host": body.smtp_host or "",
|
"smtp_host": body.smtp_host or "",
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ class DigestScheduler:
|
||||||
channel = self._channel_factory(rule, self._connector)
|
channel = self._channel_factory(rule, self._connector)
|
||||||
delivery_type = rule.delivery_type
|
delivery_type = rule.delivery_type
|
||||||
|
|
||||||
if delivery_type in ("mesh_broadcast", "mesh_dm"):
|
if delivery_type in ("mesh_broadcast", "mesh_dm", "meshcore_broadcast", "meshcore_dm"):
|
||||||
# One deliver call per chunk
|
# One deliver call per chunk
|
||||||
chunks = digest.mesh_chunks
|
chunks = digest.mesh_chunks
|
||||||
total = len(chunks)
|
total = len(chunks)
|
||||||
|
|
|
||||||
|
|
@ -266,9 +266,13 @@ class CompositeTransport(MeshTransport):
|
||||||
"CompositeTransport: hinted broadcast child %r not connected", name
|
"CompositeTransport: hinted broadcast child %r not connected", name
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
child_channel = meshcore_channel if name == "meshcore" else channel
|
|
||||||
try:
|
try:
|
||||||
return child.send_message(text, destination=None, channel=child_channel)
|
if name == "meshcore":
|
||||||
|
return child.send_message(
|
||||||
|
text, destination=None, meshcore_channel=meshcore_channel
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return child.send_message(text, destination=None, channel=channel)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.error(
|
logger.error(
|
||||||
"CompositeTransport: hinted broadcast via %r raised: %s", name, exc
|
"CompositeTransport: hinted broadcast via %r raised: %s", name, exc
|
||||||
|
|
|
||||||
|
|
@ -229,11 +229,14 @@ def test_meshcore_broadcast_routes_to_meshcore_child_only():
|
||||||
assert rules[0]["delivery_type"] == "meshcore_broadcast"
|
assert rules[0]["delivery_type"] == "meshcore_broadcast"
|
||||||
assert rules[0]["meshcore_channel"] == "AIDA"
|
assert rules[0]["meshcore_channel"] == "AIDA"
|
||||||
|
|
||||||
# MeshCore child received the call with correct transport+channel.
|
# MeshCore child received the call with the channel NAME on the correct kwarg.
|
||||||
assert meshcore_child.send_message.called
|
assert meshcore_child.send_message.called
|
||||||
mc_kwargs = meshcore_child.send_message.call_args.kwargs
|
mc_kwargs = meshcore_child.send_message.call_args.kwargs
|
||||||
assert mc_kwargs.get("destination") is None
|
assert mc_kwargs.get("destination") is None
|
||||||
assert mc_kwargs.get("channel") == "AIDA" # child receives channel=meshcore_channel
|
# Regression guard for DEFECT 1: channel NAME must be routed via meshcore_channel=.
|
||||||
|
assert mc_kwargs.get("meshcore_channel") == "AIDA"
|
||||||
|
# The old broken code passed AIDA via channel=; that must NOT be the routing mechanism.
|
||||||
|
assert mc_kwargs.get("channel") != "AIDA"
|
||||||
|
|
||||||
# Meshtastic child must NOT have been called.
|
# Meshtastic child must NOT have been called.
|
||||||
meshtastic_child.send_message.assert_not_called()
|
meshtastic_child.send_message.assert_not_called()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue