refactor: change packages

This commit is contained in:
Kevin Franklin Kim 2024-05-13 22:14:30 +02:00
parent 64c50a6f57
commit 9117669802
No known key found for this signature in database
112 changed files with 942 additions and 413 deletions

View File

@ -1,89 +1,201 @@
run:
skip-dirs:
- tmp
timeout: 5m
issues:
exclude-dirs:
- 'tmp'
exclude-rules:
- path: _test\.go
linters:
- forbidigo
- forcetypeassert
linters-settings:
# https://golangci-lint.run/usage/linters/#revive
revive:
rules:
- name: indent-error-flow
disabled: true
- name: exported
disabled: true
# https://golangci-lint.run/usage/linters/#misspell
misspell:
mode: restricted
# https://golangci-lint.run/usage/linters/#asasalint
asasalint:
ignore-test: true
# https://golangci-lint.run/usage/linters/#exhaustive
exhaustive:
default-signifies-exhaustive: true
# https://golangci-lint.run/usage/linters/#predeclared
predeclared:
ignore: "new,error"
# https://golangci-lint.run/usage/linters/#gocritic
gocritic:
enabled-tags:
- diagnostic
- performance
- style
disabled-tags:
- experimental
- opinionated
disabled-checks:
- ifElseChain
settings:
hugeParam:
sizeThreshold: 512
- commentFormatting
# https://golangci-lint.run/usage/linters/#testifylint
testifylint:
disable:
- float-compare
# https://golangci-lint.run/usage/linters/#gosec
gosec:
config:
G306: "0700"
excludes:
- G101 # Potential hardcoded credentials
- G102 # Bind to all interfaces
- G112 # Potential slowloris attack
- G401 # Detect the usage of DES, RC4, MD5 or SHA1
- G402 # Look for bad TLS connection settings
- G404 # Insecure random number source (rand)
- G501 # Import blocklist: crypto/md5
- G505 # Import blocklist: crypto/sha1
confidence: medium
# https://golangci-lint.run/usage/linters/#importas
importas:
no-unaliased: true
# https://golangci-lint.run/usage/linters/#gomoddirectives
gomoddirectives:
replace-local: true
# https://golangci-lint.run/usage/linters/#revive
revive:
ignore-generated-header: true
enable-all-rules: true
rules:
- name: line-length-limit
disabled: true
- name: cognitive-complexity
disabled: true
- name: unused-parameter
disabled: true
- name: add-constant
disabled: true
- name: cyclomatic
disabled: true
- name: function-length
disabled: true
- name: function-result-limit
disabled: true
- name: flag-parameter
disabled: true
- name: unused-receiver
disabled: true
- name: argument-limit
disabled: true
- name: max-control-nesting
disabled: true
- name: comment-spacings
disabled: true
- name: struct-tag
arguments:
- "json,inline"
- name: unhandled-error
arguments:
- "fmt.Println"
- "io.Writer.Write"
- "strings.Builder.WriteString"
# TODO remove
- name: deep-exit
disabled: true
- name: if-return
disabled: true
- name: empty-block
disabled: true
- name: indent-error-flow
disabled: true
linters:
disable-all: true
enable:
# Enabled by default linters:
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
## Enabled by default linters:
- errcheck # errcheck is a program for checking for unchecked errors in Go code. These unchecked errors can be critical bugs in some cases [fast: false, auto-fix: false]
- gosimple # (megacheck) Linter for Go source code that specializes in simplifying code [fast: false, auto-fix: false]
- govet # (vet, vetshadow) Vet examines Go source code and reports suspicious constructs. It is roughly the same as 'go vet' and uses its passes. [fast: false, auto-fix: false]
- ineffassign # Detects when assignments to existing variables are not used [fast: true, auto-fix: false]
- staticcheck # (megacheck) It's a set of rules from staticcheck. It's not the same thing as the staticcheck binary. The author of staticcheck doesn't support or approve the use of staticcheck as a library inside golangci-lint. [fast: false, auto-fix: false]
- unused # (megacheck) Checks Go code for unused constants, variables, functions and types [fast: false, auto-fix: false]
# Disabled by default linters:
- asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers [fast: true, auto-fix: false]
## Disabled by your configuration linters:
- asasalint # check for pass []any as any in variadic func(...any) [fast: false, auto-fix: false]
- asciicheck # checks that all code identifiers does not have non-ASCII symbols in the name [fast: true, auto-fix: false]
- bidichk # Checks for dangerous unicode character sequences [fast: true, auto-fix: false]
#- dupl # Tool for code clone detection [fast: true, auto-fix: false]
- bodyclose # checks whether HTTP response body is closed successfully [fast: false, auto-fix: false]
- containedctx # containedctx is a linter that detects struct contained context.Context field [fast: false, auto-fix: false]
- contextcheck # check whether the function uses a non-inherited context [fast: false, auto-fix: false]
#- copyloopvar # (go >= 1.22) copyloopvar is a linter detects places where loop variables are copied [fast: true, auto-fix: false]
- decorder # check declaration order and count of types, constants, variables and functions [fast: true, auto-fix: false]
- durationcheck # check for two durations multiplied together [fast: false, auto-fix: false]
- errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and reports occations, where the check for the returned error can be omitted. [fast: false, auto-fix: false]
- errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. [fast: false, auto-fix: false]
- errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. [fast: false, auto-fix: false]
- exhaustive # check exhaustiveness of enum switch statements [fast: false, auto-fix: false]
- exportloopref # checks for pointers to enclosing loop variables [fast: false, auto-fix: false]
#- forbidigo # Forbids identifiers [fast: false, auto-fix: false]
- forcetypeassert # finds forced type assertions [fast: true, auto-fix: false]
#- gochecknoinits # Checks that no init functions are present in Go code [fast: true, auto-fix: false]
- gocheckcompilerdirectives # Checks that go compiler directive comments (//go:) are valid. [fast: true, auto-fix: false]
- gochecksumtype # Run exhaustiveness checks on Go "sum types" [fast: false, auto-fix: false]
- goconst # Finds repeated strings that could be replaced by a constant [fast: true, auto-fix: false]
- gocritic # Provides diagnostics that check for bugs, performance and style issues. [fast: false, auto-fix: false]
- goimports # In addition to fixing imports, goimports also formats your code in the same style as gofmt. [fast: true, auto-fix: true]
#- gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. [fast: true, auto-fix: false]
- gocritic # Provides diagnostics that check for bugs, performance and style issues. [fast: false, auto-fix: true]
- gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification [fast: true, auto-fix: true]
- goheader # Checks is file header matches to pattern [fast: true, auto-fix: true]
- goimports # Check import statements are formatted according to the 'goimport' command. Reformat imports in autofix mode. [fast: true, auto-fix: true]
- gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. [fast: true, auto-fix: false]
- gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. [fast: true, auto-fix: false]
- goprintffuncname # Checks that printf-like functions are named with `f` at the end [fast: true, auto-fix: false]
- gosec # (gas): Inspects source code for security problems [fast: false, auto-fix: false]
- grouper # An analyzer to analyze expression groups. [fast: true, auto-fix: false]
- goprintffuncname # Checks that printf-like functions are named with `f` at the end. [fast: true, auto-fix: false]
- gosec # (gas) Inspects source code for security problems [fast: false, auto-fix: false]
- gosmopolitan # Report certain i18n/l10n anti-patterns in your Go codebase [fast: false, auto-fix: false]
- grouper # Analyze expression groups. [fast: true, auto-fix: false]
- importas # Enforces consistent import aliases [fast: false, auto-fix: false]
#- maintidx # maintidx measures the maintainability index of each function. [fast: true, auto-fix: false]
- inamedparam # reports interfaces with unnamed method parameters [fast: true, auto-fix: false]
#- intrange # (go >= 1.22) intrange is a linter to find places where for loops could make use of an integer range. [fast: true, auto-fix: false]
- loggercheck # (logrlint) Checks key value pairs for common logger libraries (kitlog,klog,logr,zap). [fast: false, auto-fix: false]
- makezero # Finds slice declarations with non-zero initial length [fast: false, auto-fix: false]
- misspell # Finds commonly misspelled English words in comments [fast: true, auto-fix: true]
- nakedret # Finds naked returns in functions greater than a specified function length [fast: true, auto-fix: false]
#- nestif # Reports deeply nested if statements [fast: true, auto-fix: false]
- misspell # Finds commonly misspelled English words [fast: true, auto-fix: true]
- mirror # reports wrong mirror patterns of bytes/strings usage [fast: false, auto-fix: true]
- musttag # enforce field tags in (un)marshaled structs [fast: false, auto-fix: false]
- nakedret # Checks that functions with naked returns are not longer than a maximum size (can be zero). [fast: true, auto-fix: false]
- nilerr # Finds the code that returns nil even if it checks that the error is not nil. [fast: false, auto-fix: false]
- nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. [fast: false, auto-fix: false]
- noctx # noctx finds sending http request without context.Context [fast: false, auto-fix: false]
- nolintlint # Reports ill-formed or insufficient nolint directives [fast: true, auto-fix: false]
- noctx # Finds sending http request without context.Context [fast: false, auto-fix: false]
- nolintlint # Reports ill-formed or insufficient nolint directives [fast: true, auto-fix: true]
- nonamedreturns # Reports all named returns [fast: false, auto-fix: false]
- nosprintfhostport # Checks for misuse of Sprintf to construct a host with port in a URL. [fast: true, auto-fix: false]
- prealloc # Finds slice declarations that could potentially be pre-allocated [fast: true, auto-fix: false]
#- paralleltest # Detects missing usage of t.Parallel() method in your Go test [fast: false, auto-fix: false]
- predeclared # find code that shadows one of Go's predeclared identifiers [fast: true, auto-fix: false]
- promlinter # Check Prometheus metrics naming via promlint [fast: true, auto-fix: false]
- reassign # Checks that package variables are not reassigned [fast: false, auto-fix: false]
- revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint. [fast: false, auto-fix: false]
#- tagliatelle # Checks the struct tags. [fast: true, auto-fix: false]
- rowserrcheck # checks whether Rows.Err of rows is checked successfully [fast: false, auto-fix: false]
- spancheck # Checks for mistakes with OpenTelemetry/Census spans. [fast: false, auto-fix: false]
- sqlclosecheck # Checks that sql.Rows, sql.Stmt, sqlx.NamedStmt, pgx.Query are closed. [fast: false, auto-fix: false]
- stylecheck # Stylecheck is a replacement for golint [fast: false, auto-fix: false]
- tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 [fast: false, auto-fix: false]
- testableexamples # linter checks if examples are testable (have an expected output) [fast: true, auto-fix: false]
- testifylint # Checks usage of github.com/stretchr/testify. [fast: false, auto-fix: false]
- testpackage # linter that makes you use a separate _test package [fast: true, auto-fix: false]
- thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers [fast: false, auto-fix: false]
- thelper # thelper detects tests helpers which is not start with t.Helper() method. [fast: false, auto-fix: false]
- tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes. [fast: false, auto-fix: false]
- unconvert # Remove unnecessary type conversions [fast: false, auto-fix: false]
- unparam # Reports unused function parameters [fast: false, auto-fix: false]
- usestdlibvars # A linter that detect the possibility to use variables/constants from the Go standard library. [fast: true, auto-fix: false]
- wastedassign # wastedassign finds wasted assignment statements. [fast: false, auto-fix: false]
- whitespace # Tool for detection of leading and trailing whitespace [fast: true, auto-fix: true]
disable:
- unused
- wastedassign # Finds wasted assignment statements [fast: false, auto-fix: false]
- whitespace # Whitespace is a linter that checks for unnecessary newlines at the start and end of functions, if, for, etc. [fast: true, auto-fix: true]
## Don't enable
#- cyclop # checks function and package cyclomatic complexity [fast: false, auto-fix: false]
#- depguard # Go linter that checks if package imports are in a list of acceptable packages [fast: true, auto-fix: false]
#- dupl # Tool for code clone detection [fast: true, auto-fix: false]
#- dupword # checks for duplicate words in the source code [fast: true, auto-fix: false]
#- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) [fast: true, auto-fix: false]
#- err113 # Go linter to check the errors handling expressions [fast: false, auto-fix: false]
#- exhaustruct # Checks if all structure fields are initialized [fast: false, auto-fix: false]
#- gci # Gci controls Go package import order and makes it always deterministic. [fast: true, auto-fix: true]
#- gochecknoglobals # Check that no global variables exist. [fast: false, auto-fix: false]
#- gochecknoinits # Checks that no init functions are present in Go code [fast: true, auto-fix: false]
#- gocyclo # Computes and checks the cyclomatic complexity of functions [fast: true, auto-fix: false]
#- godot # Check if comments end in a period [fast: true, auto-fix: true]
#- godox # Tool for detection of FIXME, TODO and other comment keywords [fast: true, auto-fix: false]
#- gofumpt # Gofumpt checks whether code was gofumpt-ed. [fast: true, auto-fix: true]
#- funlen # Tool for detection of long functions [fast: true, auto-fix: false]
#- ginkgolinter # enforces standards of using ginkgo and gomega [fast: false, auto-fix: false]
#- gocognit # Computes and checks the cognitive complexity of functions [fast: true, auto-fix: false]
#- interfacebloat # A linter that checks the number of methods inside an interface. [fast: true, auto-fix: false]
#- lll # Reports long lines [fast: true, auto-fix: false]
#- ireturn # Accept Interfaces, Return Concrete Types [fast: false, auto-fix: false]
#- maintidx # maintidx measures the maintainability index of each function. [fast: true, auto-fix: false]
#- nestif # Reports deeply nested if statements [fast: true, auto-fix: false]
#- nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity [fast: true, auto-fix: false]
#- perfsprint # Checks that fmt.Sprintf can be replaced with a faster alternative. [fast: false, auto-fix: false]
#- prealloc # Finds slice declarations that could potentially be pre-allocated [fast: true, auto-fix: false]
#- protogetter # Reports direct reads from proto message fields when getters should be used [fast: false, auto-fix: true]
#- sloglint # ensure consistent code style when using log/slog [fast: false, auto-fix: false]
#- tagalign # check that struct tags are well aligned [fast: true, auto-fix: true]
#- tagliatelle # Checks the struct tags. [fast: true, auto-fix: false]
#- unparam # Reports unused function parameters [fast: false, auto-fix: false]
#- varnamelen # checks that the length of a variable's name matches its scope [fast: false, auto-fix: false]
#- wrapcheck # Checks that errors returned from external packages are wrapped [fast: false, auto-fix: false]
#- wsl # add or remove empty lines [fast: true, auto-fix: false]
#- zerologlint # Detects the wrong usage of `zerolog` that a user forgets to dispatch with `Send` or `Msg` [fast: false, auto-fix: false]

View File

@ -141,6 +141,7 @@ func (c *Client) SendRawEvent(r *http.Request, event *Payload) error {
if err != nil {
return errors.Wrap(err, "failed to send request")
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
var body string

View File

@ -6,7 +6,7 @@ import (
"reflect"
"testing"
"github.com/foomo/sesamy-go/protocol/gtag"
"github.com/foomo/sesamy-go/encoding/gtag"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -20,17 +20,17 @@ func TestDecode(t *testing.T) {
{
name: "page_view",
args: "v=2&tid=G-123456&gtm=45je42s0v893689383z8894072534za200&_p=1709286636310&gcd=13l3l3l3l1&npa=0&dma_cps=sypham&dma=1&cid=292234677.1707898933&ul=en-us&sr=3840x1600&uaa=arm&uab=64&uafvl=Chromium&uamb=0&uam=&uap=macOS&uapv=14.3.1&uaw=0&are=1&pae=1&pscdl=noapi&_s=1&sid=1709286452&sct=7&seg=1&dl=https%3A%2F%2Fwww.homemade.ch%2Fprodukt%2Fkuhn-rikon-wiegemesser-900047100%3Fid%3D900047100&dr=https%3A%2F%2Fwww.homemade.ch%2Fmesser-besteck&dt=Wiegemesser&en=page_view&tfd=5682",
want: `{"protocol_version":"2","tracking_id":"G-123456","gtmhash_info":"45je42s0v893689383z8894072534za200","client_id":"292234677.1707898933","client_hints":{"screen_resolution":"3840x1600","user_language":"en-us","user_agent_architecture":"arm","user_agent_bitness":"64","user_agent_full_version_list":"Chromium","user_agent_mobile":"0","user_agent_model":"","user_agent_platform":"macOS","user_agent_platform_version":"14.3.1","user_agent_wow_64":"0"},"document_location":"https://www.homemade.ch/produkt/kuhn-rikon-wiegemesser-900047100?id=900047100","document_title":"Wiegemesser","document_referrer":"https://www.homemade.ch/messer-besteck","event_name":"page_view","session_id":"1709286452","ecommerce":{}}`,
want: `{"consent":{"google_consent_default":"13l3l3l3l1"},"campaign":{},"ecommerce":{},"client_hints":{"screen_resolution":"3840x1600","user_language":"en-us","user_agent_architecture":"arm","user_agent_bitness":"64","user_agent_full_version_list":"Chromium","user_agent_mobile":"0","user_agent_model":"","user_agent_platform":"macOS","user_agent_platform_version":"14.3.1","user_agent_wow_64":"0"},"protocol_version":"2","tracking_id":"G-123456","gtmhash_info":"45je42s0v893689383z8894072534za200","client_id":"292234677.1707898933","document_location":"https://www.homemade.ch/produkt/kuhn-rikon-wiegemesser-900047100?id=900047100","document_title":"Wiegemesser","document_referrer":"https://www.homemade.ch/messer-besteck","event_name":"page_view","session_id":"1709286452","non_personalized_ads":"0"}`,
},
{
name: "add_to_cart",
args: "v=2&tid=G-123456&gtm=45je42s0v9175354889z89175348963za200&_p=1709297934217&_dbg=1&gcd=13l3l3l3l1&npa=0&dma_cps=sypham&dma=1&cid=1220643501.1708014725&ul=en-us&sr=3840x1600&_fplc=0&ur=DE-BY&uaa=arm&uab=64&uafvl=Chromium&uamb=0&uam=&uap=macOS&uapv=14.3.1&uaw=0&are=1&pscdl=noapi&_eu=IA&sst.uc=DE&sst.etld=google.de&sst.gcsub=region1&sst.gcd=13l3l3l3l1&sst.tft=1709297934217&_s=8&cu=USD&sid=1709296380&sct=7&seg=1&dl=https%3A%2F%2Fsniffer.cloud.bestbytes.net%2F%3Fgtm_debug%3D1709297933868&dr=https%3A%2F%2Ftagassistant.google.com%2F&dt=Server%20Side%20Tracking%20Prototype%20(codename%3A%20sniffer)&en=add_to_cart&pr1=idSKU_12345~nmStan%20and%20Friends%20Tee~afGoogle%20Store~cpSUMMER_FUN~ds2.22~lp5~brGoogle~caApparel~c2Adult~c3Shirts~c4Crew~c5Short%20sleeve~lirelated_products~lnRelated%20products~vagreen~loChIJIQBpAG2ahYAR_6128GcTUEo~pr10.01~qt3&epn.value=30.03&tfd=15129&richsstsse",
want: `{"protocol_version":"2","tracking_id":"G-123456","gtmhash_info":"45je42s0v9175354889z89175348963za200","client_id":"1220643501.1708014725","richsstsse":"","client_hints":{"screen_resolution":"3840x1600","user_language":"en-us","user_agent_architecture":"arm","user_agent_bitness":"64","user_agent_full_version_list":"Chromium","user_agent_mobile":"0","user_agent_model":"","user_agent_platform":"macOS","user_agent_platform_version":"14.3.1","user_agent_wow_64":"0","user_region":"DE-BY"},"document_location":"https://sniffer.cloud.bestbytes.net/?gtm_debug=1709297933868","document_title":"Server Side Tracking Prototype (codename: sniffer)","document_referrer":"https://tagassistant.google.com/","event_name":"add_to_cart","event_parameter_number":{"value":"30.03"},"session_id":"1709296380","ecommerce":{"currency":"USD","items":[{"item_id":"SKU_12345","item_name":"Stan and Friends Tee","item_brand":"Google","item_category":"Apparel","item_category2":"Adult","item_category3":"Shirts","item_category4":"Crew","item_category5":"Short sleeve","item_variant":"green","price":"10.01","quantity":"3","coupon":"SUMMER_FUN","item_list_name":"Related products","item_list_position":"5","item_list_id":"related_products","discount":"2.22","affiliation":"Google Store","location_id":"ChIJIQBpAG2ahYAR_6128GcTUEo"}]},"sst":{"etld":"google.de","gcsub":"region1","uc":"DE","tft":"1709297934217","gcd":"13l3l3l3l1"}}`,
want: `{"consent":{"google_consent_default":"13l3l3l3l1"},"campaign":{},"ecommerce":{"currency":"USD","items":[{"affiliation":"Google Store","coupon":"SUMMER_FUN","discount":"2.22","item_brand":"Google","item_category":"Apparel","item_category2":"Adult","item_category3":"Shirts","item_category4":"Crew","item_category5":"Short sleeve","item_id":"SKU_12345","item_list_id":"related_products","item_list_name":"Related products","item_name":"Stan and Friends Tee","item_variant":"green","item_list_position":"5","location_id":"ChIJIQBpAG2ahYAR_6128GcTUEo","price":"10.01","quantity":"3"}]},"client_hints":{"screen_resolution":"3840x1600","user_language":"en-us","user_agent_architecture":"arm","user_agent_bitness":"64","user_agent_full_version_list":"Chromium","user_agent_mobile":"0","user_agent_model":"","user_agent_platform":"macOS","user_agent_platform_version":"14.3.1","user_agent_wow_64":"0","user_region":"DE-BY"},"protocol_version":"2","tracking_id":"G-123456","gtmhash_info":"45je42s0v9175354889z89175348963za200","client_id":"1220643501.1708014725","richsstsse":"","document_location":"https://sniffer.cloud.bestbytes.net/?gtm_debug=1709297933868","document_title":"Server Side Tracking Prototype (codename: sniffer)","document_referrer":"https://tagassistant.google.com/","event_name":"add_to_cart","event_parameter_number":{"value":"30.03"},"session_id":"1709296380","non_personalized_ads":"0","sst":{"etld":"google.de","gcsub":"region1","uc":"DE","tft":"1709297934217","gcd":"13l3l3l3l1"}}`,
},
{
name: "select_item",
args: "v=2&tid=G-F9XM71K45T&gtm=45he4580v9184715813z89184708445za200&_p=1715430403224&_dbg=1&gcd=13l3l3l2l1&npa=1&dma_cps=sypham&dma=1&cid=179294588.1715353601&ecid=251283723&ul=en-us&sr=3840x1600&_fplc=0&ur=&uaa=arm&uab=64&uafvl=Chromium%3B124.0.6367.119%7CGoogle%2520Chrome%3B124.0.6367.119%7CNot-A.Brand%3B99.0.0.0&uamb=0&uam=&uap=macOS&uapv=14.4.1&uaw=0&are=1&frm=0&pscdl=noapi&sst.gcd=13l3l3l2l1&sst.tft=1715430403224&sst.ude=0&_s=3&sid=1715428762&sct=2&seg=1&dl=https%3A%2F%2Fsesamy.local.bestbytes.net%2F%3Fgtm_debug%3D1715430402906&dr=https%3A%2F%2Ftagassistant.google.com%2F&dt=Home&en=select_item&pr1=idSKU_12345~nmStan%20and%20Friends%20Tee~afGoogle%20Merchandise%20Store~cpSUMMER_FUN~ds2.22~lp0~brGoogle~caApparel~c2Adult~c3Shirts~c4Crew~c5Short%20sleeve~lirelated_products~lnRelated%20Products~vagreen~loChIJIQBpAG2ahYAR_6128GcTUEo~pr10.01~qt3&ep.enable_page_views=false&ep.item_list_id=related_products&ep.item_list_name=Related%20products&_et=89&tfd=8618&richsstsse",
want: `{"protocol_version":"2","tracking_id":"G-F9XM71K45T","gtmhash_info":"45he4580v9184715813z89184708445za200","client_id":"179294588.1715353601","richsstsse":"","client_hints":{"screen_resolution":"3840x1600","user_language":"en-us","user_agent_architecture":"arm","user_agent_bitness":"64","user_agent_full_version_list":"Chromium;124.0.6367.119|Google Chrome;124.0.6367.119|Not-A.Brand;99.0.0.0","user_agent_mobile":"0","user_agent_model":"","user_agent_platform":"macOS","user_agent_platform_version":"14.4.1","user_agent_wow_64":"0","user_region":""},"document_location":"https://sesamy.local.bestbytes.net/?gtm_debug=1715430402906","document_title":"Home","document_referrer":"https://tagassistant.google.com/","event_name":"select_item","event_parameter":{"enable_page_views":"false","item_list_id":"related_products","item_list_name":"Related products"},"session_id":"1715428762","ecommerce":{"items":[{"item_id":"SKU_12345","item_name":"Stan and Friends Tee","item_brand":"Google","item_category":"Apparel","item_category2":"Adult","item_category3":"Shirts","item_category4":"Crew","item_category5":"Short sleeve","item_variant":"green","price":"10.01","quantity":"3","coupon":"SUMMER_FUN","item_list_name":"Related Products","item_list_position":"0","item_list_id":"related_products","discount":"2.22","affiliation":"Google Merchandise Store","location_id":"ChIJIQBpAG2ahYAR_6128GcTUEo"}]},"sst":{"tft":"1715430403224","gcd":"13l3l3l2l1"}}`,
want: `{"consent":{"google_consent_default":"13l3l3l2l1"},"campaign":{},"ecommerce":{"items":[{"affiliation":"Google Merchandise Store","coupon":"SUMMER_FUN","discount":"2.22","item_brand":"Google","item_category":"Apparel","item_category2":"Adult","item_category3":"Shirts","item_category4":"Crew","item_category5":"Short sleeve","item_id":"SKU_12345","item_list_id":"related_products","item_list_name":"Related Products","item_name":"Stan and Friends Tee","item_variant":"green","item_list_position":"0","location_id":"ChIJIQBpAG2ahYAR_6128GcTUEo","price":"10.01","quantity":"3"}]},"client_hints":{"screen_resolution":"3840x1600","user_language":"en-us","user_agent_architecture":"arm","user_agent_bitness":"64","user_agent_full_version_list":"Chromium;124.0.6367.119|Google Chrome;124.0.6367.119|Not-A.Brand;99.0.0.0","user_agent_mobile":"0","user_agent_model":"","user_agent_platform":"macOS","user_agent_platform_version":"14.4.1","user_agent_wow_64":"0","user_region":""},"protocol_version":"2","tracking_id":"G-F9XM71K45T","gtmhash_info":"45he4580v9184715813z89184708445za200","client_id":"179294588.1715353601","richsstsse":"","document_location":"https://sesamy.local.bestbytes.net/?gtm_debug=1715430402906","document_title":"Home","document_referrer":"https://tagassistant.google.com/","event_name":"select_item","event_parameter":{"enable_page_views":"false","item_list_id":"related_products","item_list_name":"Related products"},"session_id":"1715428762","non_personalized_ads":"1","sst":{"tft":"1715430403224","gcd":"13l3l3l2l1"}}`,
},
}
for _, tt := range tests {
@ -41,7 +41,9 @@ func TestDecode(t *testing.T) {
out, err := json.Marshal(event)
require.NoError(t, err)
assert.Equal(t, tt.want, string(out))
if !assert.JSONEq(t, tt.want, string(out)) {
t.Log(string(out))
}
})
}
}

View File

@ -41,22 +41,13 @@ func Encode(input *Payload) (url.Values, io.Reader, error) {
data[s] = a
}
// a, err := json.Marshal(input)
// if err != nil {
// return nil, nil, errors.Wrap(err, "failed to marshal event")
// }
//
// if err := json.Unmarshal(a, &data); err != nil {
// return nil, nil, errors.Wrap(err, "failed to decode into map")
// }
ret := url.Values{}
for k, v := range data {
switch t := v.(type) {
case []interface{}:
case []any:
for key, value := range t {
switch tt := value.(type) {
case map[string]interface{}:
case map[string]any:
ret[fmt.Sprintf("%s%d", k, key+1)] = []string{EncodeObjectValue(tt)}
default:
panic("unhandled")
@ -66,7 +57,7 @@ func Encode(input *Payload) (url.Values, io.Reader, error) {
for key, value := range t {
ret[fmt.Sprintf("%s.%s", k, key)] = []string{value}
}
case map[string]interface{}:
case map[string]any:
for key, value := range t {
ret[fmt.Sprintf("%s.%s", k, key)] = []string{fmt.Sprintf("%v", value)}
}

View File

@ -4,7 +4,7 @@ import (
"net/url"
"testing"
"github.com/foomo/sesamy-go/protocol/gtag"
"github.com/foomo/sesamy-go/encoding/gtag"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

View File

@ -1,10 +1,18 @@
package gtag
type Item struct {
// Example: 12345
ItemID *string `json:"item_id,omitempty" gtag:"id,omitempty"`
// Example: Stan and Friends Tee
ItemName *string `json:"item_name,omitempty" gtag:"nm,omitempty"`
// Example: Foo Marketplace
Affiliation *string `json:"affiliation,omitempty" gtag:"af,omitempty"`
// Example: 50%OFF
Coupon *string `json:"coupon,omitempty" gtag:"cp,omitempty"`
// The name of the promotional creative.
// Example: summer_banner2
CreativeName *string `json:"creative_name,omitempty" gtag:"cn,omitempty"`
// The name of the promotional creative slot associated with the item.
// Example: featured_app_1
CreativeSlot *string `json:"creative_slot,omitempty" gtag:"cs,omitempty"`
// Example: 10.00
Discount *string `json:"discount,omitempty" gtag:"ds,omitempty"`
// Example: Google
ItemBrand *string `json:"item_brand,omitempty" gtag:"br,omitempty"`
// Example: men
@ -17,36 +25,28 @@ type Item struct {
ItemCategory4 *string `json:"item_category4,omitempty" gtag:"c4,omitempty"`
// Example: men
ItemCategory5 *string `json:"item_category5,omitempty" gtag:"c5,omitempty"`
// Example: Yellow
ItemVariant *string `json:"item_variant,omitempty" gtag:"va,omitempty"`
// Example: 123.45
Price *string `json:"price,omitempty" gtag:"pr,omitempty"`
// Example: 1
Quantity *string `json:"quantity,omitempty" gtag:"qt,omitempty"`
// Example: 50%OFF
Coupon *string `json:"coupon,omitempty" gtag:"cp,omitempty"`
// Example: cross-selling: mens
ItemListName *string `json:"item_list_name,omitempty" gtag:"ln,omitempty"`
// Example: 10
ItemListPosition *string `json:"item_list_position,omitempty" gtag:"lp,omitempty"`
// Example: 12345
ItemID *string `json:"item_id,omitempty" gtag:"id,omitempty"`
// Example: id-mens-123
ItemListID *string `json:"item_list_id,omitempty" gtag:"li,omitempty"`
// Example: 10.00
Discount *string `json:"discount,omitempty" gtag:"ds,omitempty"`
// Example: Foo Marketplace
Affiliation *string `json:"affiliation,omitempty" gtag:"af,omitempty"`
// Example: cross-selling: mens
ItemListName *string `json:"item_list_name,omitempty" gtag:"ln,omitempty"`
// Example: Stan and Friends Tee
ItemName *string `json:"item_name,omitempty" gtag:"nm,omitempty"`
// Example: Yellow
ItemVariant *string `json:"item_variant,omitempty" gtag:"va,omitempty"`
// Example: 10 FIXME
ItemListPosition *string `json:"item_list_position,omitempty" gtag:"lp,omitempty"`
// Example: ChIJIQBpAG2ahYAR_6128GcTUEo
LocationID *string `json:"location_id,omitempty" gtag:"lo,omitempty"`
// The name of the promotional creative.
// Example: summer_banner2
CreativeName *string `json:"creative_name,omitempty" gtag:"cn,omitempty"`
// The name of the promotional creative slot associated with the item.
// Example: featured_app_1
CreativeSlot *string `json:"creative_slot,omitempty" gtag:"cs,omitempty"`
// Example: 123.45
Price *string `json:"price,omitempty" gtag:"pr,omitempty"`
// The ID of the promotion associated with the item.
// Example: P_12345
PromotionID *string `json:"promotion_id,omitempty" gtag:"pi,omitempty"`
// The name of the promotion associated with the item.
// Example: Summer Sale
PromotionName *string `json:"promotion_name,omitempty" gtag:"pn,omitempty"`
// Example: 1
Quantity *string `json:"quantity,omitempty" gtag:"qt,omitempty"`
}

View File

@ -0,0 +1,90 @@
package gtagencode
import (
"maps"
"github.com/foomo/sesamy-go/encoding/gtag"
"github.com/foomo/sesamy-go/encoding/mpv2"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
)
func MPv2[P any](source gtag.Payload, target *mpv2.Payload[P]) error {
var sourceData map[string]any
// encode gtag event to map
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
Result: &sourceData,
TagName: "json",
IgnoreUntaggedFields: true,
})
if err != nil {
return errors.Wrap(err, "failed to create event decoder")
}
if err := dec.Decode(source); err != nil {
return errors.Wrap(err, "failed to decode event")
}
// transorm map to match mpv2 format
targetData := map[string]any{
"client_id": source.ClientID,
"user_id": source.UserID,
"non_personalized_ads": source.NonPersonalizedAds,
"timestamp_micros": source.SST.TFT,
}
if source.SST != nil && source.SST.TFT != nil {
targetData["timestamp_micros"] = gtag.Get(source.SST.TFT) + "000"
}
// combine user properties
targetUserProperties := map[string]any{}
if node, ok := sourceData["user_property"].(map[string]string); ok {
for s, s2 := range node {
targetUserProperties[s] = s2
}
}
if node, ok := sourceData["user_property_number"].(map[string]string); ok {
for s, s2 := range node {
targetUserProperties[s] = s2
}
}
targetData["user_properties"] = targetUserProperties
// transform event
targetEventData := map[string]any{
"name": source.EventName,
}
targetEventDataParams := map[string]any{}
if node, ok := sourceData["ecommerce"].(map[string]any); ok {
maps.Copy(targetEventDataParams, node)
}
if node, ok := sourceData["event_parameter"].(map[string]string); ok {
for s, s2 := range node {
targetEventDataParams[s] = s2
}
}
if node, ok := sourceData["event_parameter_number"].(map[string]string); ok {
for s, s2 := range node {
targetEventDataParams[s] = s2
}
}
targetEventData["params"] = targetEventDataParams
targetData["events"] = []any{targetEventData}
// encode map to target entity
enc, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
Result: target,
TagName: "json",
WeaklyTypedInput: true,
IgnoreUntaggedFields: true,
})
if err != nil {
return errors.Wrap(err, "failed to create event encoder")
}
if err := enc.Decode(targetData); err != nil {
return errors.Wrap(err, "failed to encode event")
}
return nil
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type AddPaymentInfo sesamy.Event[params.AddPaymentInfo[params.Item]]
func NewAddPaymentInfo(p params.AddPaymentInfo[params.Item]) AddPaymentInfo {
return AddPaymentInfo(sesamy.NewEvent(sesamy.EventNameAddPaymentInfo, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type AddShippingInfo sesamy.Event[params.AddShippingInfo[params.Item]]
func NewAddShippingInfo(p params.AddShippingInfo[params.Item]) AddShippingInfo {
return AddShippingInfo(sesamy.NewEvent(sesamy.EventNameAddShippingInfo, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type AddToCart sesamy.Event[params.AddToCart[params.Item]]
func NewAddToCart(p params.AddToCart[params.Item]) AddToCart {
return AddToCart(sesamy.NewEvent(sesamy.EventNameAddToCart, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type AddToWishlist sesamy.Event[params.AddToWishlist[params.Item]]
func NewAddToWishlist(p params.AddToWishlist[params.Item]) AddToWishlist {
return AddToWishlist(sesamy.NewEvent(sesamy.EventNameAddToWishlist, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type AdImpression sesamy.Event[params.AdImpression]
func NewAdImpression(p params.AdImpression) AdImpression {
return AdImpression(sesamy.NewEvent(sesamy.EventNameAdImpression, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type BeginCheckout sesamy.Event[params.BeginCheckout[params.Item]]
func NewBeginCheckout(p params.BeginCheckout[params.Item]) BeginCheckout {
return BeginCheckout(sesamy.NewEvent(sesamy.EventNameBeginCheckout, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type CampaignDetails sesamy.Event[params.CampaignDetails]
func NewCampaignDetails(p params.CampaignDetails) CampaignDetails {
return CampaignDetails(sesamy.NewEvent(sesamy.EventNameCampaignDetails, p))
}

View File

@ -0,0 +1,5 @@
package event
// Google Analytics (GA4) Measurement Protocol events
//
// Reference: https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type EarnVirtualMoney sesamy.Event[params.EarnVirtualMoney]
func NewEarnVirtualMoney(p params.EarnVirtualMoney) EarnVirtualMoney {
return EarnVirtualMoney(sesamy.NewEvent(sesamy.EventNameEarnVirtualMoney, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type GenerateLead sesamy.Event[params.GenerateLead]
func NewGenerateLead(p params.GenerateLead) GenerateLead {
return GenerateLead(sesamy.NewEvent(sesamy.EventNameGenerateLead, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type JoinGroup sesamy.Event[params.JoinGroup]
func NewJoinGroup(p params.JoinGroup) JoinGroup {
return JoinGroup(sesamy.NewEvent(sesamy.EventNameJoinGroup, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type LevelEnd sesamy.Event[params.LevelEnd]
func NewLevelEnd(p params.LevelEnd) LevelEnd {
return LevelEnd(sesamy.NewEvent(sesamy.EventNameLevelEnd, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type LevelStart sesamy.Event[params.LevelStart]
func NewLevelStart(p params.LevelStart) LevelStart {
return LevelStart(sesamy.NewEvent(sesamy.EventNameLevelStart, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type LevelUp sesamy.Event[params.LevelUp]
func NewLevelUp(p params.LevelUp) LevelUp {
return LevelUp(sesamy.NewEvent(sesamy.EventNameLevelUp, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type Login sesamy.Event[params.Login]
func NewLogin(p params.Login) Login {
return Login(sesamy.NewEvent(sesamy.EventNameLogin, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type PageView sesamy.Event[params.PageView]
func NewPageView(p params.PageView) PageView {
return PageView(sesamy.NewEvent(sesamy.EventNamePageView, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type PostScore sesamy.Event[params.PostScore]
func NewPostScore(p params.PostScore) PostScore {
return PostScore(sesamy.NewEvent(sesamy.EventNamePostScore, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type Purchase sesamy.Event[params.Purchase[params.Item]]
func NewPurchase(p params.Purchase[params.Item]) Purchase {
return Purchase(sesamy.NewEvent(sesamy.EventNamePurchase, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type Refund sesamy.Event[params.Refund[params.Item]]
func NewRefund(p params.Refund[params.Item]) Refund {
return Refund(sesamy.NewEvent(sesamy.EventNameRefund, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type RemoveFromCart sesamy.Event[params.RemoveFromCart[params.Item]]
func NewRemoveFromCart(p params.RemoveFromCart[params.Item]) RemoveFromCart {
return RemoveFromCart(sesamy.NewEvent(sesamy.EventNameRemoveFromCart, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type ScreenView sesamy.Event[params.ScreenView]
func NewScreenView(p params.ScreenView) ScreenView {
return ScreenView(sesamy.NewEvent(sesamy.EventNameScreenView, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type Search sesamy.Event[params.Search]
func NewSearch(p params.Search) Search {
return Search(sesamy.NewEvent(sesamy.EventNameSearch, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type SelectContent sesamy.Event[params.SelectContent]
func NewSelectContent(p params.SelectContent) SelectContent {
return SelectContent(sesamy.NewEvent(sesamy.EventNameSelectContent, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type SelectItem sesamy.Event[params.SelectItem[params.Item]]
func NewSelectItem(p params.SelectItem[params.Item]) SelectItem {
return SelectItem(sesamy.NewEvent(sesamy.EventNameSelectItem, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type SelectPromotion sesamy.Event[params.SelectPromotion[params.Item]]
func NewSelectPromotion(p params.SelectPromotion[params.Item]) SelectPromotion {
return SelectPromotion(sesamy.NewEvent(sesamy.EventNameSelectPromotion, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type Share sesamy.Event[params.Share]
func NewShare(p params.Share) Share {
return Share(sesamy.NewEvent(sesamy.EventNameShare, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type SignUp sesamy.Event[params.SignUp]
func NewSignUp(p params.SignUp) SignUp {
return SignUp(sesamy.NewEvent(sesamy.EventNameSignUp, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type SpendVirtualCurrency sesamy.Event[params.SpendVirtualCurrency]
func NewSpendVirtualCurrency(p params.SpendVirtualCurrency) SpendVirtualCurrency {
return SpendVirtualCurrency(sesamy.NewEvent(sesamy.EventNameSpendVirtualCurrency, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type TutorialBegin sesamy.Event[params.TutorialBegin]
func NewTutorialBegin(p params.TutorialBegin) TutorialBegin {
return TutorialBegin(sesamy.NewEvent(sesamy.EventNameTutorialBegin, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type TutorialComplete sesamy.Event[params.TutorialComplete]
func NewTutorialComplete(p params.TutorialComplete) TutorialComplete {
return TutorialComplete(sesamy.NewEvent(sesamy.EventNameTutorialComplete, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type UnlockArchievement sesamy.Event[params.UnlockArchievement]
func NewUnlockArchievement(p params.UnlockArchievement) UnlockArchievement {
return UnlockArchievement(sesamy.NewEvent(sesamy.EventNameUnlockArchievement, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type ViewCart sesamy.Event[params.ViewCart[params.Item]]
func NewViewCart(p params.ViewCart[params.Item]) ViewCart {
return ViewCart(sesamy.NewEvent(sesamy.EventNameViewCart, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type ViewItem sesamy.Event[params.ViewItem[params.Item]]
func NewViewItem(p params.ViewItem[params.Item]) ViewItem {
return ViewItem(sesamy.NewEvent(sesamy.EventNameViewItem, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type ViewItemList sesamy.Event[params.ViewItemList[params.Item]]
func NewViewItemList(p params.ViewItemList[params.Item]) ViewItemList {
return ViewItemList(sesamy.NewEvent(sesamy.EventNameViewItemList, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type ViewPromotion sesamy.Event[params.ViewPromotion[params.Item]]
func NewViewPromotion(p params.ViewPromotion[params.Item]) ViewPromotion {
return ViewPromotion(sesamy.NewEvent(sesamy.EventNameViewPromotion, p))
}

View File

@ -0,0 +1,12 @@
package event
import (
"github.com/foomo/sesamy-go"
"github.com/foomo/sesamy-go/encoding/mpv2/params"
)
type ViewSearchResults sesamy.Event[params.ViewSearchResults]
func NewViewSearchResults(p params.ViewSearchResults) ViewSearchResults {
return ViewSearchResults(sesamy.NewEvent(sesamy.EventNameViewSearchResults, p))
}

View File

@ -0,0 +1,8 @@
package params
// AddToCart https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#add_to_cart
type AddToCart[Item any] struct {
Currency string `json:"currency,omitempty"`
Value float64 `json:"value,omitempty"`
Items []Item `json:"items,omitempty"`
}

View File

@ -1,7 +1,7 @@
package params
// AdImpressionParams https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#ad_impression
type AdImpressionParams struct {
// AdImpression https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#ad_impression
type AdImpression struct {
AdPlatform string `json:"ad_platform,omitempty" mapstructure:"ad_platform"`
AdSource string `json:"ad_source,omitempty" mapstructure:"ad_source"`
AdFormat string `json:"ad_format,omitempty" mapstructure:"ad_format"`

View File

@ -1,6 +1,6 @@
package params
type Promotion struct {
type Item struct {
Affiliation string `json:"affiliation,omitempty"`
Coupon string `json:"coupon,omitempty"`
CreativeName string `json:"creative_name,omitempty"`

View File

@ -0,0 +1,8 @@
package params
// SelectItem https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#select_item
type SelectItem[Item any] struct {
ItemListID string `json:"item_list_id,omitempty" tagging:"item_list_id"`
ItemListName string `json:"item_list_name,omitempty" tagging:"item_list_name"`
Items []Item `json:"items,omitempty" tagging:"pr"`
}

View File

@ -6,7 +6,3 @@ type ViewCart[Item any] struct {
Value float64 `json:"value,omitempty"`
Items []Item `json:"items,omitempty"`
}
func (e ViewCart[Item]) String() string {
return "view_cart"
}

107
encoding/mpv2encode/gtag.go Normal file
View File

@ -0,0 +1,107 @@
package mpv2encode
import (
"encoding/json"
"fmt"
"strconv"
"github.com/foomo/sesamy-go/encoding/gtag"
"github.com/foomo/sesamy-go/encoding/mpv2"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
)
func GTag[P any](source mpv2.Payload[P], target *gtag.Payload) error {
targetData := map[string]any{
"client_id": source.ClientID,
"user_id": source.UserID,
"non_personalized_ads": source.NonPersonalizedAds,
}
{ // user_property
targetUserProperty := map[string]any{}
targetUserPropertyNumber := map[string]any{}
for k, v := range source.UserProperties {
if s, ok := v.(string); ok {
if f, err := strconv.ParseFloat(s, 64); err == nil {
targetUserPropertyNumber[k] = f
} else {
targetUserProperty[k] = v
}
} else {
targetUserProperty[k] = fmt.Sprintf("%s", v)
}
}
targetData["user_property"] = targetUserProperty
targetData["user_property_number"] = targetUserPropertyNumber
}
sourceData := map[string]any{}
out, err := json.Marshal(source.Events[0])
if err != nil {
return errors.Wrap(err, "failed to marshal event")
}
if err = json.Unmarshal(out, &sourceData); err != nil {
return errors.Wrap(err, "failed to unmarshal source events")
}
{ // ecommerce
targetData["event_name"] = sourceData["name"]
if params, ok := sourceData["params"].(map[string]any); ok {
targetEcommerceData := map[string]any{
"currency": params["currency"],
"promotion_id": params["promotion_id"],
"promotion_name": params["promotion_name"],
"location_id": params["location_id"],
"is_conversion": params["is_conversion"],
}
delete(params, "currency")
delete(params, "promotion_id")
delete(params, "promotion_name")
delete(params, "location_id")
delete(params, "is_conversion")
targetData["ecommerce"] = targetEcommerceData
targetData["items"] = params["items"]
delete(params, "items")
{ // user_property
targetEventProperty := map[string]any{}
targetEventPropertyNumber := map[string]any{}
for k, v := range params {
if s, ok := v.(string); ok {
if f, err := strconv.ParseFloat(s, 64); err == nil {
targetEventPropertyNumber[k] = f
} else {
targetEventProperty[k] = v
}
} else {
targetEventProperty[k] = fmt.Sprintf("%s", v)
}
}
targetData["event_parameter"] = targetEventProperty
targetData["event_parameter_number"] = targetEventPropertyNumber
}
}
}
// encode gtag event to map
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
WeaklyTypedInput: true,
Squash: true,
Result: &target,
TagName: "json",
IgnoreUntaggedFields: true,
})
if err != nil {
return errors.Wrap(err, "failed to create event decoder")
}
if err := dec.Decode(targetData); err != nil {
return errors.Wrap(err, "failed to decode event")
}
return nil
}

Some files were not shown because too many files have changed in this diff Show More