diff --git a/internal/events/event-relay.go b/internal/events/event-relay.go index e4e11ce4..3ecdb201 100644 --- a/internal/events/event-relay.go +++ b/internal/events/event-relay.go @@ -83,12 +83,17 @@ func (r *analyticsEventEndpointDispatcher) dispatch(w http.ResponseWriter, req * metadata := GetEventPayloadMetadata(req) r.loggers.Debugf("Received %d events (v%d) to be proxied to %s", len(evts), metadata.SchemaVersion, r.remotePath) - if metadata.SchemaVersion >= SummaryEventsSchemaVersion { - // New-style events that have already gone through summarization - deliver them as-is - r.getVerbatimRelay().enqueue(metadata, evts) - } else { + if metadata.SchemaVersion < SummaryEventsSchemaVersion { r.getSummarizingRelay().enqueue(metadata, evts) + return + } + + if _, ok := req.Header[http.CanonicalHeaderKey(EventUnsummarizedHeader)]; ok { + r.getSummarizingRelay().enqueue(metadata, evts) + return } + + r.getVerbatimRelay().enqueue(metadata, evts) }) } diff --git a/internal/events/event_constants.go b/internal/events/event_constants.go index d04f2f70..5d6f0bec 100644 --- a/internal/events/event_constants.go +++ b/internal/events/event_constants.go @@ -10,6 +10,10 @@ const ( // EventSchemaHeader is an HTTP header that describes the schema version for event requests. EventSchemaHeader = "X-LaunchDarkly-Event-Schema" + // EventUnsummarizedHeader is an HTTP header that denotes events being received have not gone + // through the standard event summarization process. + EventUnsummarizedHeader = "X-LaunchDarkly-Unsummarized" + // TagsHeader is an HTTP header that may be sent by SDKs that support application metadata. // We copy the value of this header when proxying events. TagsHeader = "X-LaunchDarkly-Tags" diff --git a/internal/events/summarizing-relay-testdata_test.go b/internal/events/summarizing-relay-testdata_test.go index c422b8a8..3fb8b57d 100644 --- a/internal/events/summarizing-relay-testdata_test.go +++ b/internal/events/summarizing-relay-testdata_test.go @@ -90,6 +90,28 @@ func makeAllSummarizeEventsParams() []summarizeEventsParams { } ]`, }, + { + name: "php feature event using schema 4 will be summarized", + schemaVersion: 4, + inputEventsJSON: ` + [ + { "kind": "feature", "creationDate": 1000, "key": "flagkey", "user": {"key": "userkey"}, + "value": "b", "default": "c", "version": 11, "variation": 1, "trackEvents": false, "samplingRatio": 1, "excludeFromSummaries": false } + ]`, + expectedEventsJSON: ` + [ + { "kind": "index", "creationDate": 1000, "context": {"key": "userkey"} }, + { + "kind": "summary", "startDate": 1000, "endDate": 1000, + "features": { + "flagkey": { + "default": "c", "contextKinds": ["user"], + "counters": [{"variation": 1, "version": 11, "value": "b", "count": 1}] + } + } + } + ]`, + }, { name: "feature event has non-standard defaults for ratio and exclusion fields", inputEventsJSON: ` @@ -106,13 +128,13 @@ func makeAllSummarizeEventsParams() []summarizeEventsParams { name: "feature event includes the provided sampling ratio", inputEventsJSON: ` [ - { "kind": "feature", "creationDate": 1000, "key": "flagkey", "user": {"key": "userkey"}, + { "kind": "feature", "creationDate": 1000, "key": "flagkey", "context": {"key": "userkey"}, "value": "b", "default": "c", "version": 11, "variation": 1, "trackEvents": true, "samplingRatio": 0 } ]`, expectedEventsJSON: ` [ {"kind":"index","creationDate":1000,"context":{"key": "userkey"}}, - {"kind":"feature","creationDate":1000,"key":"flagkey","version":11,"contextKeys":{"user":"userkey"},"variation":1,"value":"b","default":"c","samplingRatio":0}, + {"kind":"feature","creationDate":1000,"key":"flagkey","version":11,"context":{"key":"userkey"},"variation":1,"value":"b","default":"c","samplingRatio":0}, {"kind":"summary","startDate":1000,"endDate":1000,"features":{"flagkey":{"default":"c","counters":[{"variation":1,"version":11,"value":"b","count":1}],"contextKinds":["user"]}}} ]`, }, @@ -126,7 +148,7 @@ func makeAllSummarizeEventsParams() []summarizeEventsParams { expectedEventsJSON: ` [ {"kind":"index","creationDate":1000,"context":{"key": "userkey"}}, - {"kind":"feature","creationDate":1000,"key":"flagkey","version":11,"contextKeys":{"user":"userkey"},"variation":1,"value":"b","default":"c"} + {"kind":"feature","creationDate":1000,"key":"flagkey","version":11,"context":{"key":"userkey"},"variation":1,"value":"b","default":"c"} ]`, }, { diff --git a/internal/events/summarizing-relay_test.go b/internal/events/summarizing-relay_test.go index fb110f74..3e2357dd 100644 --- a/internal/events/summarizing-relay_test.go +++ b/internal/events/summarizing-relay_test.go @@ -48,7 +48,10 @@ func TestSummarizeEvents(t *testing.T) { _, _ = st.UpsertFlag(p.dataStore, ep.storedFlag) } - req := st.BuildRequest("POST", "/", []byte(ep.inputEventsJSON), headersWithEventSchema(ep.schemaVersion)) + headers := headersWithEventSchema(ep.schemaVersion) + headers.Set(EventUnsummarizedHeader, "true") + + req := st.BuildRequest("POST", "/", []byte(ep.inputEventsJSON), headers) p.dispatcher.GetHandler(basictypes.ServerSDK, ldevents.AnalyticsEventDataKind)(httptest.NewRecorder(), req) p.dispatcher.flush()