diff --git a/dtos/token.go b/dtos/token.go index 314d2d2b..5bd5a2ed 100644 --- a/dtos/token.go +++ b/dtos/token.go @@ -17,6 +17,48 @@ const occupancy = "[?occupancy=metrics.publishers]" type Token struct { Token string `json:"token"` PushEnabled bool `json:"pushEnabled"` + ConnDelay int64 `json:"connDelay"` +} + +// UnmarshalJSON parses the legacy `pushEnabled` boolean and `connDelay` seconds when present, and +// otherwise falls back to `config.streaming.enabled` and `config.streaming.delay` respectively - +// some auth backends (e.g. the Configs auth service) have moved streaming capability there instead +// of the top-level fields. When neither is present, PushEnabled defaults to false and ConnDelay +// defaults to 0, matching the pre-existing zero-value behavior. +func (t *Token) UnmarshalJSON(raw []byte) error { + var shadow struct { + Token string `json:"token"` + PushEnabled *bool `json:"pushEnabled"` + ConnDelay *int64 `json:"connDelay"` + Config *struct { + Streaming *struct { + Enabled *bool `json:"enabled"` + Delay *int64 `json:"delay"` + } `json:"streaming"` + } `json:"config"` + } + if err := json.Unmarshal(raw, &shadow); err != nil { + return err + } + + t.Token = shadow.Token + switch { + case shadow.PushEnabled != nil: + t.PushEnabled = *shadow.PushEnabled + case shadow.Config != nil && shadow.Config.Streaming != nil && shadow.Config.Streaming.Enabled != nil: + t.PushEnabled = *shadow.Config.Streaming.Enabled + default: + t.PushEnabled = false + } + switch { + case shadow.ConnDelay != nil: + t.ConnDelay = *shadow.ConnDelay + case shadow.Config != nil && shadow.Config.Streaming != nil && shadow.Config.Streaming.Delay != nil: + t.ConnDelay = *shadow.Config.Streaming.Delay + default: + t.ConnDelay = 0 + } + return nil } // TokenPayload payload dto diff --git a/dtos/token_test.go b/dtos/token_test.go index 2441f757..4a49dbcd 100644 --- a/dtos/token_test.go +++ b/dtos/token_test.go @@ -1,12 +1,82 @@ package dtos import ( + "encoding/json" "testing" "time" "github.com/splitio/go-toolkit/v5/datastructures/set" ) +func TestTokenUnmarshalJSON(t *testing.T) { + // Legacy shape: top-level pushEnabled wins, config.streaming.enabled is ignored if present. + var legacy Token + if err := json.Unmarshal([]byte(`{"token":"abc","pushEnabled":true,"config":{"streaming":{"enabled":false}}}`), &legacy); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !legacy.PushEnabled { + t.Error("expected top-level pushEnabled to take precedence") + } + + var legacyFalse Token + if err := json.Unmarshal([]byte(`{"token":"abc","pushEnabled":false}`), &legacyFalse); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if legacyFalse.PushEnabled { + t.Error("expected pushEnabled=false to be honored") + } + + // New shape: no top-level pushEnabled, fall back to config.streaming.enabled. + var viaConfig Token + if err := json.Unmarshal([]byte(`{"token":"abc","config":{"streaming":{"enabled":true}}}`), &viaConfig); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !viaConfig.PushEnabled { + t.Error("expected config.streaming.enabled fallback to enable push") + } + if viaConfig.Token != "abc" { + t.Error("expected token field to still be parsed") + } + + // Neither field present: defaults to false, same as the zero value. + var neither Token + if err := json.Unmarshal([]byte(`{"token":"abc"}`), &neither); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if neither.PushEnabled { + t.Error("expected PushEnabled to default to false") + } +} + +func TestTokenUnmarshalJSONConnDelay(t *testing.T) { + // Legacy shape: top-level connDelay wins, config.streaming.delay is ignored if present. + var legacy Token + if err := json.Unmarshal([]byte(`{"token":"abc","connDelay":30,"config":{"streaming":{"delay":90}}}`), &legacy); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if legacy.ConnDelay != 30 { + t.Errorf("expected top-level connDelay to take precedence, got %d", legacy.ConnDelay) + } + + // New shape: no top-level connDelay, fall back to config.streaming.delay. + var viaConfig Token + if err := json.Unmarshal([]byte(`{"token":"abc","config":{"streaming":{"delay":90}}}`), &viaConfig); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if viaConfig.ConnDelay != 90 { + t.Errorf("expected config.streaming.delay fallback, got %d", viaConfig.ConnDelay) + } + + // Neither field present: defaults to 0, same as the zero value. + var neither Token + if err := json.Unmarshal([]byte(`{"token":"abc"}`), &neither); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if neither.ConnDelay != 0 { + t.Errorf("expected ConnDelay to default to 0, got %d", neither.ConnDelay) + } +} + func TestTokenChannels(t *testing.T) { token := Token{ PushEnabled: false,