Refactor MQTT course and bound backend message processing
Move lesson source into the teaching structure, align the course path with the learner's Go and DevOps experience, and add the interactive lesson guides. Add advanced DevOps lifecycle study as a separate section. Bound MQTT message work and drain accepted jobs during shutdown; align the backend implementation with the revised course through Section 6.
This commit is contained in:
@@ -2,27 +2,13 @@ package contracts
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const SchemaVersion = 1
|
||||
const SchemaVersionOne = 1
|
||||
|
||||
// Kind values match the final topic segment used to route each message.
|
||||
type Kind string
|
||||
|
||||
const (
|
||||
KindTelemetry Kind = "telemetry"
|
||||
KindCommands Kind = "commands"
|
||||
KindResponses Kind = "responses"
|
||||
KindSales Kind = "sales"
|
||||
)
|
||||
|
||||
// Topic is the parsed device scope and message kind from an MQTT topic.
|
||||
type Topic struct {
|
||||
DeviceID string
|
||||
Kind Kind
|
||||
}
|
||||
// SchemaVersion keeps the teaching examples' original package-level name.
|
||||
const SchemaVersion = SchemaVersionOne
|
||||
|
||||
// MQTTMessage is implemented by each versioned message contract.
|
||||
// Validation uses the parsed route and caller-supplied time for deterministic checks.
|
||||
@@ -72,22 +58,9 @@ type SalesReport struct {
|
||||
Items []SalesLine `json:"items"`
|
||||
}
|
||||
|
||||
// validID limits identifiers to safe, single-segment topic values.
|
||||
func validID(value string) bool {
|
||||
if value == "" || len(value) > 128 {
|
||||
return false
|
||||
}
|
||||
for _, r := range value {
|
||||
if !(r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' || strings.ContainsRune("._-", r)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// validateEnvelope checks fields shared by every versioned message.
|
||||
func validateEnvelope(topic Topic, version int, messageID, deviceID, occurredAt string, now time.Time) error {
|
||||
if version != SchemaVersion {
|
||||
if version != SchemaVersionOne {
|
||||
return fmt.Errorf("unsupported schema_version %d", version)
|
||||
}
|
||||
if !validID(messageID) || !validID(deviceID) {
|
||||
@@ -163,18 +136,3 @@ func (m SalesReport) Validate(topic Topic, now time.Time) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseTopic turns broker routing metadata into values handlers can compare.
|
||||
func ParseTopic(value string) (Topic, error) {
|
||||
parts := strings.Split(value, "/")
|
||||
if len(parts) != 5 || parts[0] != "lab" || parts[1] != "v1" || parts[2] != "devices" || !validID(parts[3]) {
|
||||
return Topic{}, fmt.Errorf("topic must be lab/v1/devices/{deviceID}/{kind}")
|
||||
}
|
||||
kind := Kind(parts[4])
|
||||
switch kind {
|
||||
case KindTelemetry, KindCommands, KindResponses, KindSales:
|
||||
return Topic{DeviceID: parts[3], Kind: kind}, nil
|
||||
default:
|
||||
return Topic{}, fmt.Errorf("unsupported topic kind %q", kind)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user