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>
This commit is contained in:
copilot-swe-agent[bot] 2026-03-02 07:07:02 +00:00
parent a708352634
commit 07adfb08c7

View File

@ -3,6 +3,7 @@ package androidsmsgateway
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"strconv"
"time" "time"
) )
@ -99,7 +100,7 @@ type WebhookPing struct {
DeviceID string `json:"deviceId" csv:"deviceId"` DeviceID string `json:"deviceId" csv:"deviceId"`
Event string `json:"event" csv:"event"` Event string `json:"event" csv:"event"`
ID string `json:"id" csv:"id"` ID string `json:"id" csv:"id"`
Payload WebhookPingPayload `json:"payload" csv:"payload"` Payload WebhookPingPayload `json:"payload" csv:",inline"`
WebhookID string `json:"webhookId" csv:"webhookId"` WebhookID string `json:"webhookId" csv:"webhookId"`
PingedAt time.Time `json:"pingedAt,omitempty" csv:"pingedAt"` PingedAt time.Time `json:"pingedAt,omitempty" csv:"pingedAt"`
XSignature string `json:"x-signature" csv:"-"` 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. // 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 { type WebhookPingPayload struct {
Health DeviceHealth `json:"health"` Health DeviceHealth `json:"health" csv:",inline"`
}
// 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))
} }
// DeviceHealth is the top-level health object inside a system:ping payload. // 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". // Named fields use colon json tags matching the API key names inside "checks".
type DeviceHealth struct { type DeviceHealth struct {
Checks DeviceChecks `json:"checks"` Checks DeviceChecks `json:"checks" csv:",inline"`
ReleaseID int `json:"releaseId"` ReleaseID int `json:"releaseId" csv:"releaseId"`
Status string `json:"status"` Status string `json:"status" csv:"status"`
Version string `json:"version"` Version string `json:"version" csv:"version"`
} }
// DeviceChecks holds the individual health checks reported by the device. // DeviceChecks holds the individual health checks reported by the device.
// Go field names are camelCase; json tags carry the colon-delimited API key names. // 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 { type DeviceChecks struct {
BatteryCharging HealthCheck `json:"battery:charging"` BatteryCharging HealthCheck `json:"battery:charging" csv:"charging_flags"`
BatteryLevel HealthCheck `json:"battery:level"` BatteryLevel HealthCheck `json:"battery:level" csv:"battery_percent"`
ConnectionCellular HealthCheck `json:"connection:cellular"` ConnectionCellular HealthCheck `json:"connection:cellular" csv:"cellular_index"`
ConnectionStatus HealthCheck `json:"connection:status"` ConnectionStatus BoolHealthCheck `json:"connection:status" csv:"internet_connected"`
ConnectionTransport HealthCheck `json:"connection:transport"` ConnectionTransport HealthCheck `json:"connection:transport" csv:"transport_flags"`
MessagesFailed HealthCheck `json:"messages:failed"` MessagesFailed HealthCheck `json:"messages:failed" csv:"failed_messages"`
} }
// HealthCheck represents a single named health check result. // 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 { type HealthCheck struct {
Description string `json:"description"` Description string `json:"description"`
ObservedUnit string `json:"observedUnit"` ObservedUnit string `json:"observedUnit"`
@ -158,6 +148,38 @@ type HealthCheck struct {
Status string `json:"status"` 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. // Decode decodes the raw Payload based on the Event field and returns the appropriate WebhookEvent.
func Decode(webhook *Webhook) (WebhookEvent, error) { func Decode(webhook *Webhook) (WebhookEvent, error) {
switch webhook.Event { switch webhook.Event {