feat(go): enrich realtime snapshot context
This commit is contained in:
@@ -3,6 +3,7 @@ package realtime
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -102,6 +103,11 @@ func (w *SnapshotWriter) EnsureSchema(ctx context.Context) error {
|
||||
if _, err := w.exec.ExecContext(ctx, realtimeSnapshotTableSQL); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, statement := range realtimeSnapshotCompatibilitySQL {
|
||||
if _, err := w.exec.ExecContext(ctx, statement); err != nil && !isDuplicateColumnError(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := w.exec.ExecContext(ctx, realtimeLocationTableSQL); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -122,10 +128,15 @@ func (w *SnapshotWriter) Update(ctx context.Context, env envelope.FrameEnvelope)
|
||||
}
|
||||
eventTime := nullableTime(env.EventTimeMS)
|
||||
receivedAt := nullableTime(env.ReceivedAtMS)
|
||||
platformName := platformNameFromEnvelope(env)
|
||||
parsedJSON := parsedJSONFromEnvelope(env)
|
||||
if _, err = w.exec.ExecContext(ctx, upsertRealtimeSnapshotSQL,
|
||||
string(env.Protocol),
|
||||
vin,
|
||||
plate,
|
||||
platformName,
|
||||
env.SourceEndpoint,
|
||||
parsedJSON,
|
||||
eventTime,
|
||||
receivedAt,
|
||||
env.StableEventID(),
|
||||
@@ -156,6 +167,43 @@ func (w *SnapshotWriter) Update(ctx context.Context, env envelope.FrameEnvelope)
|
||||
return err
|
||||
}
|
||||
|
||||
func platformNameFromEnvelope(env envelope.FrameEnvelope) string {
|
||||
if env.Fields != nil {
|
||||
if value := strings.TrimSpace(stringValue(env.Fields["platform_account"])); value != "" {
|
||||
return value
|
||||
}
|
||||
}
|
||||
if env.Parsed != nil {
|
||||
if value := strings.TrimSpace(stringValue(env.Parsed["platform_name"])); value != "" {
|
||||
return value
|
||||
}
|
||||
if login, ok := env.Parsed["platform_login"].(map[string]any); ok {
|
||||
return strings.TrimSpace(stringValue(login["username"]))
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func stringValue(value any) string {
|
||||
switch typed := value.(type) {
|
||||
case string:
|
||||
return typed
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func parsedJSONFromEnvelope(env envelope.FrameEnvelope) any {
|
||||
if len(env.Parsed) == 0 {
|
||||
return nil
|
||||
}
|
||||
data, err := json.Marshal(env.Parsed)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func (w *SnapshotWriter) plateForEnvelope(ctx context.Context, env envelope.FrameEnvelope) (string, error) {
|
||||
if plate := strings.TrimSpace(env.Plate); plate != "" {
|
||||
return plate, nil
|
||||
@@ -331,6 +379,9 @@ const realtimeSnapshotTableSQL = `CREATE TABLE IF NOT EXISTS vehicle_realtime_sn
|
||||
protocol VARCHAR(32) NOT NULL,
|
||||
vin VARCHAR(32) NOT NULL DEFAULT '',
|
||||
plate VARCHAR(32) NOT NULL DEFAULT '',
|
||||
platform_name VARCHAR(64) NOT NULL DEFAULT '',
|
||||
peer VARCHAR(128) NOT NULL DEFAULT '',
|
||||
parsed_json LONGTEXT NULL,
|
||||
event_time DATETIME(3) NULL,
|
||||
received_at DATETIME(3) NULL,
|
||||
event_id VARCHAR(64) NOT NULL DEFAULT '',
|
||||
@@ -340,12 +391,26 @@ const realtimeSnapshotTableSQL = `CREATE TABLE IF NOT EXISTS vehicle_realtime_sn
|
||||
KEY idx_protocol_updated (protocol, updated_at)
|
||||
)`
|
||||
|
||||
var realtimeSnapshotCompatibilitySQL = []string{
|
||||
"ALTER TABLE vehicle_realtime_snapshot ADD COLUMN platform_name VARCHAR(64) NOT NULL DEFAULT '' AFTER plate",
|
||||
"ALTER TABLE vehicle_realtime_snapshot ADD COLUMN peer VARCHAR(128) NOT NULL DEFAULT '' AFTER platform_name",
|
||||
"ALTER TABLE vehicle_realtime_snapshot ADD COLUMN parsed_json LONGTEXT NULL AFTER peer",
|
||||
}
|
||||
|
||||
func isDuplicateColumnError(err error) bool {
|
||||
text := strings.ToLower(err.Error())
|
||||
return strings.Contains(text, "duplicate column") || strings.Contains(text, "1060")
|
||||
}
|
||||
|
||||
const upsertRealtimeSnapshotSQL = `
|
||||
INSERT INTO vehicle_realtime_snapshot
|
||||
(protocol, vin, plate, event_time, received_at, event_id)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
(protocol, vin, plate, platform_name, peer, parsed_json, event_time, received_at, event_id)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
plate = IF(VALUES(plate) <> '', VALUES(plate), plate),
|
||||
platform_name = IF(VALUES(event_time) IS NOT NULL AND (vehicle_realtime_snapshot.event_time IS NULL OR VALUES(event_time) >= vehicle_realtime_snapshot.event_time), VALUES(platform_name), platform_name),
|
||||
peer = IF(VALUES(event_time) IS NOT NULL AND (vehicle_realtime_snapshot.event_time IS NULL OR VALUES(event_time) >= vehicle_realtime_snapshot.event_time), VALUES(peer), peer),
|
||||
parsed_json = IF(VALUES(event_time) IS NOT NULL AND (vehicle_realtime_snapshot.event_time IS NULL OR VALUES(event_time) >= vehicle_realtime_snapshot.event_time), VALUES(parsed_json), parsed_json),
|
||||
event_time = IF(VALUES(event_time) IS NOT NULL AND (vehicle_realtime_snapshot.event_time IS NULL OR VALUES(event_time) >= vehicle_realtime_snapshot.event_time), VALUES(event_time), event_time),
|
||||
received_at = IF(VALUES(event_time) IS NOT NULL AND (vehicle_realtime_snapshot.event_time IS NULL OR VALUES(event_time) >= vehicle_realtime_snapshot.event_time), VALUES(received_at), received_at),
|
||||
event_id = IF(VALUES(event_time) IS NOT NULL AND (vehicle_realtime_snapshot.event_time IS NULL OR VALUES(event_time) >= vehicle_realtime_snapshot.event_time), VALUES(event_id), event_id),
|
||||
|
||||
Reference in New Issue
Block a user