From 38715a58b6d8765c9bd840569a03b84ed312f4b8 Mon Sep 17 00:00:00 2001 From: Kevin Franklin Kim Date: Mon, 30 Sep 2024 18:01:20 +0200 Subject: [PATCH] feat: add inline mpv2 --- go.mod | 7 +- go.sum | 6 ++ integration/watermill/mpv2/publisher.go | 3 - integration/watermill/mpv2/publisher_test.go | 70 ++++++++++++++++++++ pkg/encoding/mpv2/payload.go | 22 +++--- pkg/encoding/mpv2/payload_test.go | 43 ++++++++++++ pkg/event/params/addtocart.go | 1 + pkg/event/params/pageview.go | 7 ++ 8 files changed, 143 insertions(+), 16 deletions(-) create mode 100644 integration/watermill/mpv2/publisher_test.go create mode 100644 pkg/encoding/mpv2/payload_test.go diff --git a/go.mod b/go.mod index 6f45eeb..c938036 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ toolchain go1.23.0 require ( github.com/ThreeDotsLabs/watermill v1.3.5 + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/foomo/go v0.0.3 github.com/foomo/gostandards v0.1.0 github.com/gogo/protobuf v1.3.2 @@ -17,8 +18,10 @@ require ( github.com/mitchellh/mapstructure v1.5.0 github.com/pkg/errors v0.9.1 github.com/prometheus/common v0.55.0 + github.com/qntfy/kazaam/v4 v4.0.1 github.com/stretchr/testify v1.9.0 go.uber.org/zap v1.27.0 + gopkg.in/yaml.v2 v2.4.0 ) require ( @@ -40,7 +43,6 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dennwc/varint v1.0.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dustin/go-humanize v1.0.1 // indirect @@ -54,6 +56,7 @@ require ( github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-redis/redis/v8 v8.11.5 // indirect + github.com/gofrs/uuid v3.2.0+incompatible // indirect github.com/gogo/googleapis v1.4.0 // indirect github.com/gogo/status v1.1.1 // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect @@ -108,6 +111,7 @@ require ( github.com/prometheus/exporter-toolkit v0.11.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/prometheus/prometheus v0.51.0 // indirect + github.com/qntfy/jsonparser v1.0.2 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/sercand/kuberesolver/v5 v5.1.1 // indirect github.com/shopspring/decimal v1.2.0 // indirect @@ -140,7 +144,6 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect google.golang.org/grpc v1.62.1 // indirect google.golang.org/protobuf v1.34.2 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apimachinery v0.29.2 // indirect k8s.io/client-go v0.29.2 // indirect diff --git a/go.sum b/go.sum index f1465cf..9b08861 100644 --- a/go.sum +++ b/go.sum @@ -201,6 +201,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.4.0 h1:zgVt4UpGxcqVOw97aRGxT4svlcmdK35fynLNctY32zI= github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= @@ -529,6 +531,10 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/prometheus v0.51.0 h1:aRdjTnmHLved29ILtdzZN2GNvOjWATtA/z+3fYuexOc= github.com/prometheus/prometheus v0.51.0/go.mod h1:yv4MwOn3yHMQ6MZGHPg/U7Fcyqf+rxqiZfSur6myVtc= +github.com/qntfy/jsonparser v1.0.2 h1:hko+J4L7HSaYoB2yuzinWc9MkO93zWKUmzPHJwB53OM= +github.com/qntfy/jsonparser v1.0.2/go.mod h1:F+LCdwPnFBsubQ+ugnBczIP9RWv5wSCqnUmLHPUx4ZU= +github.com/qntfy/kazaam/v4 v4.0.1 h1:fMOC+w4o6ZYcWeKiscvPJDYPIDa1UwertGeOjTP/yII= +github.com/qntfy/kazaam/v4 v4.0.1/go.mod h1:wGZi4dkLdXkZJnAh3s9k27TAwov5z4DqdbuQJ/tqLdE= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= diff --git a/integration/watermill/mpv2/publisher.go b/integration/watermill/mpv2/publisher.go index 425d74f..7140f85 100644 --- a/integration/watermill/mpv2/publisher.go +++ b/integration/watermill/mpv2/publisher.go @@ -149,12 +149,9 @@ func (p *Publisher) handle(l *zap.Logger, msg *message.Message) error { if body, err := io.ReadAll(resp.Body); err == nil { l = l.With(zap.String("http_response", string(body))) } - l.Warn("server responded with error") return errors.Wrap(ErrErrorResponse, resp.Status) } - l.Debug("message published") - return nil }(); err != nil { return err diff --git a/integration/watermill/mpv2/publisher_test.go b/integration/watermill/mpv2/publisher_test.go new file mode 100644 index 0000000..fc912da --- /dev/null +++ b/integration/watermill/mpv2/publisher_test.go @@ -0,0 +1,70 @@ +package mpv2_test + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "net/http/httptest" + "sync/atomic" + "testing" + "time" + + "github.com/ThreeDotsLabs/watermill" + "github.com/ThreeDotsLabs/watermill/message" + "github.com/foomo/sesamy-go/integration/watermill/mpv2" + encoding "github.com/foomo/sesamy-go/pkg/encoding/mpv2" + "github.com/foomo/sesamy-go/pkg/event" + "github.com/foomo/sesamy-go/pkg/event/params" + "github.com/foomo/sesamy-go/pkg/sesamy" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zaptest" +) + +func TestPublisher(t *testing.T) { + l := zaptest.NewLogger(t) + + var done atomic.Bool + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + out, err := io.ReadAll(r.Body) + assert.NoError(t, err) + + expected := `{"client_id":"C123456","user_id":"U123456","timestamp_micros":1727701064057701,"events":[{"name":"page_view","params":{"debug_mode":true,"session_id":"S123456","engagement_time_msec":100,"page_title":"Home","page_location":"https://foomo.org"}}]}` + if !assert.JSONEq(t, expected, string(out)) { + fmt.Println(string(out)) + } + done.Store(true) + })) + + p := mpv2.NewPublisher(l, s.URL) + + payload := encoding.Payload[params.PageView]{ + ClientID: "C123456", + UserID: "U123456", + TimestampMicros: 1727701064057701, + UserProperties: nil, + Consent: nil, + Events: []sesamy.Event[params.PageView]{ + event.NewPageView(params.PageView{ + MPv2: ¶ms.MPv2{ + DebugMode: true, + SessionID: "S123456", + EngagementTimeMSec: 100, + }, + PageTitle: "Home", + PageLocation: "https://foomo.org", + }), + }, + UserData: nil, + } + jsonPayload, err := json.Marshal(payload) + require.NoError(t, err) + + msg := message.NewMessage(watermill.NewUUID(), jsonPayload) + + err = p.Publish("foo", msg) + require.NoError(t, err) + + assert.Eventually(t, done.Load, time.Second, time.Millisecond) +} diff --git a/pkg/encoding/mpv2/payload.go b/pkg/encoding/mpv2/payload.go index bcf952d..4abcf25 100644 --- a/pkg/encoding/mpv2/payload.go +++ b/pkg/encoding/mpv2/payload.go @@ -4,16 +4,16 @@ import ( "github.com/foomo/sesamy-go/pkg/sesamy" ) +// https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference?client_type=gtag#payload_post_body type Payload[P any] struct { - ClientID string `json:"client_id,omitempty"` - UserID string `json:"user_id,omitempty"` - SessionID string `json:"session_id,omitempty"` - TimestampMicros int64 `json:"timestamp_micros,omitempty"` - EngagementTimeMSec int64 `json:"engagement_time_msec,omitempty"` - // Reserved user property names: https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference?client_type=gtag#reserved_user_property_names - UserProperties map[string]any `json:"user_properties,omitempty"` - Consent *ConsentData `json:"consent,omitempty"` - Events []sesamy.Event[P] `json:"events,omitempty"` - UserData *UserData `json:"user_data,omitempty"` - DebugMode bool `json:"debug_mode,omitempty"` + ClientID string `json:"client_id,omitempty"` + UserID string `json:"user_id,omitempty"` + TimestampMicros int64 `json:"timestamp_micros,omitempty"` + UserProperties map[string]any `json:"user_properties,omitempty"` + Consent *ConsentData `json:"consent,omitempty"` + Events []sesamy.Event[P] `json:"events,omitempty"` + UserData *UserData `json:"user_data,omitempty"` + DebugMode bool `json:"debug_mode,omitempty"` + SessionID string `json:"session_id,omitempty"` + EngagementTimeMSec int64 `json:"engagement_time_msec,omitempty"` } diff --git a/pkg/encoding/mpv2/payload_test.go b/pkg/encoding/mpv2/payload_test.go new file mode 100644 index 0000000..6abf9e9 --- /dev/null +++ b/pkg/encoding/mpv2/payload_test.go @@ -0,0 +1,43 @@ +package mpv2_test + +import ( + "encoding/json" + "testing" + + "github.com/foomo/sesamy-go/pkg/encoding/mpv2" + "github.com/foomo/sesamy-go/pkg/event" + "github.com/foomo/sesamy-go/pkg/event/params" + "github.com/foomo/sesamy-go/pkg/sesamy" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPayload(t *testing.T) { + v := mpv2.Payload[params.PageView]{ + ClientID: "C123456", + UserID: "U123456", + TimestampMicros: 1727701064057701, + UserProperties: nil, + Consent: nil, + Events: []sesamy.Event[params.PageView]{ + event.NewPageView(params.PageView{ + PageTitle: "Home", + PageLocation: "https://foomo.org", + }), + }, + UserData: nil, + DebugMode: true, + SessionID: "S123456", + EngagementTimeMSec: 100, + } + + out, err := json.Marshal(v) + require.NoError(t, err) + expected := `{"debug_mode":true,"session_id":"S123456","engagement_time_msec":100,"client_id":"C123456","user_id":"U123456","timestamp_micros":1727701064057701,"events":[{"name":"page_view","params":{"page_title":"Home","page_location":"https://foomo.org"}}]}` + assert.JSONEq(t, expected, string(out)) + + var in mpv2.Payload[params.PageView] + err = json.Unmarshal(out, &in) + require.NoError(t, err) + assert.Equal(t, v, in) +} diff --git a/pkg/event/params/addtocart.go b/pkg/event/params/addtocart.go index 4945e6f..74205bd 100644 --- a/pkg/event/params/addtocart.go +++ b/pkg/event/params/addtocart.go @@ -6,6 +6,7 @@ import ( // AddToCart https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#add_to_cart type AddToCart[I any] struct { + *MPv2 `json:",inline"` Currency iso4217.Currency `json:"currency,omitempty"` Value float64 `json:"value,omitempty"` Items []I `json:"items,omitempty"` diff --git a/pkg/event/params/pageview.go b/pkg/event/params/pageview.go index 89c71a5..ed1af65 100644 --- a/pkg/event/params/pageview.go +++ b/pkg/event/params/pageview.go @@ -2,6 +2,13 @@ package params // PageView https://developers.google.com/analytics/devguides/collection/ga4/views?client_type=gtag#manually_send_page_view_events type PageView struct { + *MPv2 `json:",inline"` PageTitle string `json:"page_title,omitempty"` PageLocation string `json:"page_location,omitempty"` } + +type MPv2 struct { + DebugMode bool `json:"debug_mode,omitempty"` + SessionID string `json:"session_id,omitempty"` + EngagementTimeMSec int64 `json:"engagement_time_msec,omitempty"` +}