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 { events := pkg.Events() if len(events) == 0 { return fmt.Errorf("reports[%d].packages[%d].events is required", ri, pi) } for ei, event := range events { if event == nil { return fmt.Errorf("reports[%d].packages[%d].events[%d] is required", ri, pi, ei) } if err := event.Err(); err != nil { return fmt.Errorf("reports[%d].packages[%d].events[%d] is invalid: %w", ri, pi, ei, err) } payload := event.Payload() code, ok := payload["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 := payload["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 } }