// Package apptracking defines the transport and persistence limits shared by the // public App tracking endpoint and statistics-service. package apptracking import ( "bytes" "encoding/json" "strings" ) const ( // MaxBatchSize keeps one request bounded before gateway fans it into the // statistics write transaction. MaxBatchSize = 50 // MaxPropertiesBytes bounds the raw JSON value after surrounding whitespace // is removed. The value is rejected instead of truncated because truncation // can change JSON meaning or produce invalid JSON. MaxPropertiesBytes = 4096 // Text limits are byte limits, not rune limits. They mirror the durable // app_tracking_events columns and therefore apply identically at both service // boundaries; an oversized client value must never be deferred to MySQL. MaxAppCodeBytes = 32 MaxEventIDBytes = 160 MaxEventNameBytes = 96 MaxEventTypeBytes = 64 MaxScreenBytes = 128 MaxTargetTypeBytes = 64 MaxTargetIDBytes = 128 MaxDeviceIDBytes = 128 MaxSessionIDBytes = 160 MaxPlatformBytes = 32 MaxAppVersionBytes = 64 MaxLanguageBytes = 32 MaxTimezoneBytes = 64 MaxErrorCodeBytes = 64 ) // ValidBatchSize rejects both empty reports and reports large enough to hold a // statistics transaction open for an unbounded period. func ValidBatchSize(size int) bool { return size > 0 && size <= MaxBatchSize } // NormalizeText removes transport-only surrounding whitespace and checks the // encoded byte length. Oversized values return false and are never shortened: // identifiers and error codes are facts whose suffix may be significant. func NormalizeText(value string, maxBytes int) (string, bool) { value = strings.TrimSpace(value) if maxBytes > 0 && len(value) > maxBytes { return "", false } return value, true } // NormalizeProperties returns a detached, trimmed JSON value. Empty and JSON // null both mean no properties; non-empty values must satisfy the same byte and // syntax contract in gateway and statistics-service. func NormalizeProperties(raw json.RawMessage) (json.RawMessage, bool) { trimmed := bytes.TrimSpace(raw) if len(trimmed) == 0 || bytes.Equal(trimmed, []byte("null")) { return nil, true } if len(trimmed) > MaxPropertiesBytes || !json.Valid(trimmed) { return nil, false } return json.RawMessage(bytes.Clone(trimmed)), true }