From 07adfb08c78863223f43bd5f0923f3d1efffb348 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Mar 2026 07:07:02 +0000 Subject: [PATCH] feat: expand ping CSV to flat health columns (charging_flags, battery_percent, internet_connected TRUE/FALSE, etc.) Co-authored-by: coolaj86 <122831+coolaj86@users.noreply.github.com> --- http/androidsmsgateway/webhooks.go | 74 +++++++++++++++++++----------- 1 file changed, 48 insertions(+), 26 deletions(-) diff --git a/http/androidsmsgateway/webhooks.go b/http/androidsmsgateway/webhooks.go index aec61c5..d2fa9dd 100644 --- a/http/androidsmsgateway/webhooks.go +++ b/http/androidsmsgateway/webhooks.go @@ -3,6 +3,7 @@ package androidsmsgateway import ( "encoding/json" "errors" + "strconv" "time" ) @@ -99,7 +100,7 @@ type WebhookPing struct { DeviceID string `json:"deviceId" csv:"deviceId"` Event string `json:"event" csv:"event"` ID string `json:"id" csv:"id"` - Payload WebhookPingPayload `json:"payload" csv:"payload"` + Payload WebhookPingPayload `json:"payload" csv:",inline"` WebhookID string `json:"webhookId" csv:"webhookId"` PingedAt time.Time `json:"pingedAt,omitempty" csv:"pingedAt"` XSignature string `json:"x-signature" csv:"-"` @@ -112,45 +113,34 @@ func (w *WebhookPing) GetEvent() string { } // WebhookPingPayload contains the health data reported by a system:ping event. -// MarshalText serialises the payload as a compact JSON string so that csvutil -// stores it as a single "payload" column rather than expanding every health check. type WebhookPingPayload struct { - Health DeviceHealth `json:"health"` -} - -// MarshalText implements encoding.TextMarshaler for CSV serialisation. -func (p WebhookPingPayload) MarshalText() ([]byte, error) { - return json.Marshal(p) -} - -// MarshalJSON prevents MarshalText from being used during JSON serialisation, -// ensuring WebhookPingPayload is always encoded as a full JSON object. -func (p WebhookPingPayload) MarshalJSON() ([]byte, error) { - type alias WebhookPingPayload - return json.Marshal(alias(p)) + Health DeviceHealth `json:"health" csv:",inline"` } // DeviceHealth is the top-level health object inside a system:ping payload. // Named fields use colon json tags matching the API key names inside "checks". type DeviceHealth struct { - Checks DeviceChecks `json:"checks"` - ReleaseID int `json:"releaseId"` - Status string `json:"status"` - Version string `json:"version"` + Checks DeviceChecks `json:"checks" csv:",inline"` + ReleaseID int `json:"releaseId" csv:"releaseId"` + Status string `json:"status" csv:"status"` + Version string `json:"version" csv:"version"` } // DeviceChecks holds the individual health checks reported by the device. // Go field names are camelCase; json tags carry the colon-delimited API key names. +// csv tags use descriptive column names that reflect the unit of each observation. type DeviceChecks struct { - BatteryCharging HealthCheck `json:"battery:charging"` - BatteryLevel HealthCheck `json:"battery:level"` - ConnectionCellular HealthCheck `json:"connection:cellular"` - ConnectionStatus HealthCheck `json:"connection:status"` - ConnectionTransport HealthCheck `json:"connection:transport"` - MessagesFailed HealthCheck `json:"messages:failed"` + BatteryCharging HealthCheck `json:"battery:charging" csv:"charging_flags"` + BatteryLevel HealthCheck `json:"battery:level" csv:"battery_percent"` + ConnectionCellular HealthCheck `json:"connection:cellular" csv:"cellular_index"` + ConnectionStatus BoolHealthCheck `json:"connection:status" csv:"internet_connected"` + ConnectionTransport HealthCheck `json:"connection:transport" csv:"transport_flags"` + MessagesFailed HealthCheck `json:"messages:failed" csv:"failed_messages"` } // HealthCheck represents a single named health check result. +// MarshalText returns the observed value as a number for CSV encoding. +// MarshalJSON overrides MarshalText so JSON output is always the full object. type HealthCheck struct { Description string `json:"description"` ObservedUnit string `json:"observedUnit"` @@ -158,6 +148,38 @@ type HealthCheck struct { Status string `json:"status"` } +// MarshalText implements encoding.TextMarshaler for CSV serialisation. +// Returns the observed numeric value so each check becomes a single CSV column. +func (c HealthCheck) MarshalText() ([]byte, error) { + return []byte(strconv.FormatFloat(c.ObservedValue, 'f', -1, 64)), nil +} + +// MarshalJSON prevents MarshalText from being used during JSON serialisation, +// ensuring HealthCheck is always encoded as a full JSON object. +func (c HealthCheck) MarshalJSON() ([]byte, error) { + type alias HealthCheck + return json.Marshal(alias(c)) +} + +// BoolHealthCheck is a HealthCheck whose CSV representation is TRUE or FALSE +// based on whether ObservedValue is non-zero. Used for connection:status. +type BoolHealthCheck HealthCheck + +// MarshalText implements encoding.TextMarshaler for CSV serialisation. +func (c BoolHealthCheck) MarshalText() ([]byte, error) { + if c.ObservedValue != 0 { + return []byte("TRUE"), nil + } + return []byte("FALSE"), nil +} + +// MarshalJSON prevents MarshalText from being used during JSON serialisation, +// ensuring BoolHealthCheck is always encoded as a full JSON object. +func (c BoolHealthCheck) MarshalJSON() ([]byte, error) { + type alias BoolHealthCheck + return json.Marshal(alias(c)) +} + // Decode decodes the raw Payload based on the Event field and returns the appropriate WebhookEvent. func Decode(webhook *Webhook) (WebhookEvent, error) { switch webhook.Event {