mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-03-29 14:19:53 +00:00
fix: resolve 24 proto definition mismatches against Node fixtures
fixes #164 Mismatches fixed: - analytics-channels: ChannelAnalyticsSummary.hash string -> int32 - analytics-rf: PayloadTypeEntry.type -> optional int32 (can be null) - bulk-health: flatten BulkHealthEntry (remove .node nesting) - node-analytics: TimeBucket field label -> bucket (keep both as optional) - observer-analytics: recentPackets Transmission -> Observation - packet-detail: ByteRange add string color field - websocket-message: DecodedResult add transportCodes, raw, routeTypeName; flatten payload to DecodedFlatPayload; packet -> Observation - validate-protos: bare-array wrapping note downgraded to WARNING Validator now reports 0 errors across all 33 fixtures. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -42,8 +42,8 @@ message ScatterPoint {
|
||||
|
||||
// Payload type name + count entry.
|
||||
message PayloadTypeEntry {
|
||||
// Payload type number.
|
||||
int32 type = 1;
|
||||
// Payload type number (null when unknown).
|
||||
optional int32 type = 1;
|
||||
// Human-readable type name.
|
||||
string name = 2;
|
||||
// Observation count.
|
||||
@@ -250,8 +250,8 @@ message TopologyResponse {
|
||||
|
||||
// Channel summary in analytics context.
|
||||
message ChannelAnalyticsSummary {
|
||||
// Channel identifier.
|
||||
string hash = 1;
|
||||
// Channel identifier (numeric hash).
|
||||
int32 hash = 1;
|
||||
// Channel display name.
|
||||
string name = 2;
|
||||
// Total messages.
|
||||
|
||||
@@ -77,9 +77,12 @@ message SignalStats {
|
||||
|
||||
// Generic label + count pair for time-series and distribution charts.
|
||||
// Used in activity timelines, observer analytics timelines, etc.
|
||||
// Node analytics uses `bucket`, observer analytics uses `label`.
|
||||
message TimeBucket {
|
||||
// Time label (ISO timestamp, hour string, or category label).
|
||||
string label = 1;
|
||||
// Time label used by observer analytics (e.g. "Sat 12 AM").
|
||||
optional string label = 1;
|
||||
// Count in this bucket.
|
||||
int32 count = 2;
|
||||
// ISO timestamp used by node analytics (e.g. "2026-03-21T21:00:00Z").
|
||||
optional string bucket = 3;
|
||||
}
|
||||
|
||||
@@ -11,17 +11,26 @@ option go_package = "github.com/meshcore-analyzer/proto/v1";
|
||||
// Full decoded result: header + path + payload.
|
||||
message DecodedResult {
|
||||
DecodedHeader header = 1;
|
||||
// Transport code bytes (null if not a TRANSPORT route).
|
||||
optional DecodedTransportCodes transport_codes = 6 [json_name = "transportCodes"];
|
||||
DecodedPath path = 2;
|
||||
// Type-specific decoded payload.
|
||||
// NOTE: Proto3 JSON serializes oneof with a wrapper key (e.g. {"advert":{...}}).
|
||||
// Go serialization layer must flatten this to match the spec's flat payload shape.
|
||||
DecodedPayload payload = 3;
|
||||
// Flat decoded payload (type-discriminated by payload.type).
|
||||
DecodedFlatPayload payload = 3;
|
||||
// Raw hex string of the entire packet.
|
||||
optional string raw = 4;
|
||||
}
|
||||
|
||||
// Transport code pair for TRANSPORT-routed packets.
|
||||
message DecodedTransportCodes {
|
||||
repeated int32 codes = 1;
|
||||
}
|
||||
|
||||
// Parsed packet header (first byte).
|
||||
message DecodedHeader {
|
||||
// Route type: 0=DIRECT, 1=FLOOD, 2=reserved, 3=TRANSPORT.
|
||||
int32 route_type = 1 [json_name = "routeType"];
|
||||
// Human-readable route type name: "DIRECT", "FLOOD", "TRANSPORT".
|
||||
optional string route_type_name = 5 [json_name = "routeTypeName"];
|
||||
// Payload type: 0=REQ .. 11=CONTROL (see Payload Type Reference).
|
||||
int32 payload_type = 2 [json_name = "payloadType"];
|
||||
// Payload format version.
|
||||
@@ -40,7 +49,51 @@ message DecodedPath {
|
||||
int32 hash_count = 3 [json_name = "hashCount"];
|
||||
}
|
||||
|
||||
// ─── Payload Oneof ─────────────────────────────────────────────────────────────
|
||||
// ─── Flat Payload (used in WS broadcast / decoded result) ──────────────────────
|
||||
// Node.js returns a flat payload object with a `type` discriminator string.
|
||||
// All type-specific fields are optional — only the relevant ones are populated.
|
||||
|
||||
// Decoded advert flags (from AdvertDataHelpers.h).
|
||||
message AdvertFlags {
|
||||
// Raw flags bitmask value.
|
||||
int32 raw = 1;
|
||||
// Advert type code.
|
||||
int32 type = 2;
|
||||
// Supports chat.
|
||||
bool chat = 3;
|
||||
// Is a repeater.
|
||||
bool repeater = 4;
|
||||
// Is a room server.
|
||||
bool room = 5;
|
||||
// Is a sensor.
|
||||
bool sensor = 6;
|
||||
// Includes GPS coordinates.
|
||||
bool has_location = 7 [json_name = "hasLocation"];
|
||||
// Includes node name.
|
||||
bool has_name = 8 [json_name = "hasName"];
|
||||
}
|
||||
|
||||
// Flat decoded payload — all payload type fields merged, discriminated by `type`.
|
||||
message DecodedFlatPayload {
|
||||
// Payload type name: "ADVERT", "TXT_MSG", "GRP_TXT", etc.
|
||||
optional string type = 1;
|
||||
// --- ADVERT fields ---
|
||||
optional string pub_key = 2 [json_name = "pubKey"];
|
||||
optional int64 timestamp = 3;
|
||||
optional string timestamp_iso = 4 [json_name = "timestampISO"];
|
||||
optional string signature = 5;
|
||||
optional AdvertFlags flags = 6;
|
||||
optional double lat = 7;
|
||||
optional double lon = 8;
|
||||
optional string name = 9;
|
||||
// --- TXT_MSG / GRP_TXT fields ---
|
||||
optional string text = 10;
|
||||
optional string sender = 11;
|
||||
optional string channel = 12;
|
||||
optional int64 sender_timestamp = 13 [json_name = "sender_timestamp"];
|
||||
}
|
||||
|
||||
// ─── Payload Oneof (legacy / typed-discriminated) ──────────────────────────────────
|
||||
|
||||
// Type-discriminated decoded payload.
|
||||
message DecodedPayload {
|
||||
|
||||
@@ -101,15 +101,22 @@ message NodeSearchResponse {
|
||||
}
|
||||
|
||||
// GET /api/nodes/bulk-health — bulk health summary for analytics dashboard.
|
||||
// NOTE: The API currently returns a bare JSON array with flat node fields.
|
||||
// Go serialization should flatten node fields to top level for backward compat.
|
||||
// NOTE: The API returns a bare JSON array with flat node fields at top level.
|
||||
message BulkHealthEntry {
|
||||
// Node identification and location (reuses Node — DO NOT duplicate fields).
|
||||
Node node = 1;
|
||||
// Node public key (flat, not nested in a sub-message).
|
||||
string public_key = 1 [json_name = "public_key"];
|
||||
// Node display name.
|
||||
optional string name = 2;
|
||||
// Node role: "repeater", "room", "companion", "sensor".
|
||||
optional string role = 3;
|
||||
// GPS latitude (null if unknown).
|
||||
optional double lat = 4;
|
||||
// GPS longitude (null if unknown).
|
||||
optional double lon = 5;
|
||||
// Aggregate packet stats.
|
||||
NodeStats stats = 2;
|
||||
NodeStats stats = 6;
|
||||
// Per-observer signal quality.
|
||||
repeated NodeObserverStats observers = 3;
|
||||
repeated NodeObserverStats observers = 7;
|
||||
}
|
||||
|
||||
// Wrapper for the bulk-health array response.
|
||||
|
||||
@@ -99,6 +99,6 @@ message ObserverAnalyticsResponse {
|
||||
repeated TimeBucket nodes_timeline = 3 [json_name = "nodesTimeline"];
|
||||
// SNR distribution in labeled ranges.
|
||||
repeated SnrDistributionEntry snr_distribution = 4 [json_name = "snrDistribution"];
|
||||
// Last 20 enriched observations.
|
||||
repeated Transmission recent_packets = 5 [json_name = "recentPackets"];
|
||||
// Last 20 enriched observations (Observation-shaped, includes transmission_id).
|
||||
repeated Observation recent_packets = 5 [json_name = "recentPackets"];
|
||||
}
|
||||
|
||||
@@ -137,6 +137,8 @@ message ByteRange {
|
||||
string hex = 4;
|
||||
// Interpreted value (may be string, number, or absent).
|
||||
optional string value = 5;
|
||||
// CSS color for visual highlighting.
|
||||
string color = 6;
|
||||
}
|
||||
|
||||
// Byte-level packet structure breakdown.
|
||||
|
||||
@@ -42,8 +42,8 @@ message WSPacketData {
|
||||
optional string observer_name = 8 [json_name = "observer_name"];
|
||||
// JSON-stringified hops array (redundant with decoded.path.hops).
|
||||
optional string path_json = 9 [json_name = "path_json"];
|
||||
// Full packet object (present for MQTT/raw ingestion, may be absent for bridge messages).
|
||||
optional Transmission packet = 10;
|
||||
// Full packet object (Observation-shaped, includes transmission_id).
|
||||
optional Observation packet = 10;
|
||||
// Observation count (present when packet is included).
|
||||
optional int32 observation_count = 11 [json_name = "observation_count"];
|
||||
}
|
||||
|
||||
@@ -316,6 +316,9 @@ FIELD_TYPE_TO_MESSAGE = {
|
||||
'DecodedHeader': 'DecodedHeader',
|
||||
'DecodedPath': 'DecodedPath',
|
||||
'DecodedPayload': 'DecodedPayload',
|
||||
'DecodedFlatPayload': 'DecodedFlatPayload',
|
||||
'DecodedTransportCodes': 'DecodedTransportCodes',
|
||||
'AdvertFlags': 'AdvertFlags',
|
||||
'AdvertPayload': 'AdvertPayload',
|
||||
# channel.proto
|
||||
'Channel': 'Channel',
|
||||
@@ -558,9 +561,9 @@ def main():
|
||||
validate_object(fixture_file, data[0], message_name,
|
||||
all_messages, path=f'{message_name}[0]',
|
||||
mismatches=mismatches)
|
||||
# Also flag the structural mismatch
|
||||
# Flag structural note (serialization concern, not a field mismatch)
|
||||
mismatches.append(Mismatch(
|
||||
fixture_file, message_name, 'ERROR',
|
||||
fixture_file, message_name, 'WARNING',
|
||||
f'API returns a bare JSON array, but proto wraps it in a '
|
||||
f'response message. Serialization layer must handle unwrapping.'
|
||||
))
|
||||
|
||||
Reference in New Issue
Block a user