Deploy MQTT test backend with private viewer and Gitea CI/CD
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
// Package events defines safe inspection records shared by logs and the future viewer.
|
||||
package events
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"robot-market/test-mqtt-5/internal/contracts"
|
||||
)
|
||||
|
||||
type Direction string
|
||||
|
||||
const (
|
||||
In Direction = "in"
|
||||
Out Direction = "out"
|
||||
Internal Direction = "internal"
|
||||
)
|
||||
|
||||
type Kind string
|
||||
|
||||
const (
|
||||
PublishAttempt Kind = "publish_attempt"
|
||||
TransportAck Kind = "transport_ack"
|
||||
MessageReceived Kind = "message_received"
|
||||
ValidationFailed Kind = "validation_failed"
|
||||
CommandAccepted Kind = "command_accepted"
|
||||
CommandCompleted Kind = "command_completed"
|
||||
CommandTimeout Kind = "command_timeout"
|
||||
LateAck Kind = "late_ack"
|
||||
DuplicateRequest Kind = "duplicate_request"
|
||||
ConnectionUp Kind = "connection_up"
|
||||
ConnectionDown Kind = "connection_down"
|
||||
EventGap Kind = "event_gap"
|
||||
)
|
||||
|
||||
type Properties struct {
|
||||
ResponseTopic string `json:"response_topic,omitempty"`
|
||||
CorrelationData []byte `json:"correlation_data,omitempty"`
|
||||
MessageExpirySeconds *uint32 `json:"message_expiry_seconds,omitempty"`
|
||||
}
|
||||
|
||||
// Event contains inspection data only. Configuration and connection credentials
|
||||
// are never attached. Raw MQTT payloads require the explicit diagnostics opt-in.
|
||||
type Event struct {
|
||||
Scenario *ScenarioResult `json:"scenario,omitempty"`
|
||||
ID uint64 `json:"id"`
|
||||
ObservedAt time.Time `json:"observed_at"`
|
||||
Kind Kind `json:"kind"`
|
||||
Direction Direction `json:"direction"`
|
||||
ClientID string `json:"client_id,omitempty"`
|
||||
DeviceID string `json:"device_id,omitempty"`
|
||||
Topic string `json:"topic,omitempty"`
|
||||
QoS byte `json:"qos"`
|
||||
Retain bool `json:"retain"`
|
||||
Duplicate *bool `json:"duplicate,omitempty"`
|
||||
Payload Preview `json:"payload"`
|
||||
Properties Properties `json:"properties"`
|
||||
RequestID string `json:"request_id,omitempty"`
|
||||
Outcome string `json:"outcome,omitempty"`
|
||||
LatencyMS *int64 `json:"latency_ms,omitempty"`
|
||||
}
|
||||
|
||||
// ScenarioResult is reported test evidence, never inferred hardware compatibility.
|
||||
type ScenarioResult struct {
|
||||
Feature string `json:"feature"`
|
||||
Environment string `json:"environment"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
Version string `json:"version"`
|
||||
Status string `json:"status"`
|
||||
Observation string `json:"observation"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
type Preview struct {
|
||||
Bytes int `json:"bytes"`
|
||||
Text string `json:"text,omitempty"`
|
||||
Omitted bool `json:"omitted"`
|
||||
Truncated bool `json:"truncated"`
|
||||
}
|
||||
|
||||
// SafePreview only renders validated contract fields. Unknown fields (including
|
||||
// credentials) cause the entire payload to be omitted. Free-form error messages
|
||||
// are always redacted. The original payload is never retained in this value.
|
||||
func SafePreview(kind, device string, payload []byte, limit int) Preview {
|
||||
p := Preview{Bytes: len(payload), Omitted: true}
|
||||
if limit < 1 {
|
||||
return p
|
||||
}
|
||||
if limit > 4096 {
|
||||
limit = 4096
|
||||
}
|
||||
msg, err := contracts.Decode(kind, payload, device)
|
||||
if err != nil {
|
||||
return p
|
||||
}
|
||||
if ack, ok := msg.(*contracts.Ack); ok && ack.Error != nil {
|
||||
ack.Error.Message = "[redacted]"
|
||||
}
|
||||
encoded, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return p
|
||||
}
|
||||
if len(encoded) > limit {
|
||||
encoded = encoded[:limit]
|
||||
for len(encoded) > 0 && !utf8.Valid(encoded) {
|
||||
encoded = encoded[:len(encoded)-1]
|
||||
}
|
||||
p.Truncated = true
|
||||
}
|
||||
p.Text = string(encoded)
|
||||
p.Omitted = false
|
||||
return p
|
||||
}
|
||||
|
||||
// RawPreview is opt-in diagnostics for this isolated test harness. It displays
|
||||
// received bytes without treating them as a valid contract or execution evidence.
|
||||
func RawPreview(payload []byte, limit int) Preview {
|
||||
p := Preview{Bytes: len(payload)}
|
||||
if limit < 1 {
|
||||
p.Omitted = true
|
||||
return p
|
||||
}
|
||||
limit = min(limit, 4096)
|
||||
text := string(payload)
|
||||
if !utf8.Valid(payload) {
|
||||
text = "[base64] " + base64.StdEncoding.EncodeToString(payload)
|
||||
}
|
||||
if len(text) > limit {
|
||||
text = text[:limit]
|
||||
for !utf8.ValidString(text) {
|
||||
text = text[:len(text)-1]
|
||||
}
|
||||
p.Truncated = true
|
||||
}
|
||||
p.Text = text
|
||||
return p
|
||||
}
|
||||
Reference in New Issue
Block a user