package devtodev import "fmt" // ValidatePayload checks for required fields and common schema errors. func ValidatePayload(payload Payload) error { if len(payload.Reports) == 0 { return fmt.Errorf("reports is required") } for ri, report := range payload.Reports { if report.DeviceID == "" { return fmt.Errorf("reports[%d].deviceId is required", ri) } if len(report.Packages) == 0 { return fmt.Errorf("reports[%d].packages is required", ri) } for pi, pkg := range report.Packages { if len(pkg.Events) == 0 { return fmt.Errorf("reports[%d].packages[%d].events is required", ri, pi) } for ei, event := range pkg.Events { code, ok := event["code"] if !ok { return fmt.Errorf("reports[%d].packages[%d].events[%d].code is required", ri, pi, ei) } if codeStr, ok := code.(string); !ok || codeStr == "" { return fmt.Errorf("reports[%d].packages[%d].events[%d].code must be a non-empty string", ri, pi, ei) } ts, ok := event["timestamp"] if !ok { return fmt.Errorf("reports[%d].packages[%d].events[%d].timestamp is required", ri, pi, ei) } if !isNumber(ts) { return fmt.Errorf("reports[%d].packages[%d].events[%d].timestamp must be a number", ri, pi, ei) } } } } return nil } func isNumber(v interface{}) bool { switch v.(type) { case int, int8, int16, int32, int64: return true case uint, uint8, uint16, uint32, uint64: return true case float32, float64: return true default: return false } }