From 97ef3a7ce6600ff65f26533aff97cb5cf32c0cac Mon Sep 17 00:00:00 2001 From: Kevin Franklin Kim Date: Wed, 10 Sep 2025 16:01:02 +0200 Subject: [PATCH] feat: add pinterest provider --- Makefile | 46 +++--- cmd/provision/server.go | 8 ++ cmd/tags.go | 44 +++--- go.mod | 59 ++++---- go.sum | 136 +++++++++--------- pkg/config/config.go | 2 + pkg/config/pinterest.go | 20 +++ pkg/provider/pinterest/constants.go | 7 + pkg/provider/pinterest/server.go | 62 ++++++++ .../pinterest/server/tag/conversion.go | 59 ++++++++ .../pinterest/server/trigger/conversion.go | 83 +++++++++++ sesamy.yaml | 34 +++++ 12 files changed, 414 insertions(+), 146 deletions(-) create mode 100644 pkg/config/pinterest.go create mode 100644 pkg/provider/pinterest/constants.go create mode 100644 pkg/provider/pinterest/server.go create mode 100644 pkg/provider/pinterest/server/tag/conversion.go create mode 100644 pkg/provider/pinterest/server/trigger/conversion.go diff --git a/Makefile b/Makefile index 2589d56..bc1ca4f 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ PATH:=bin:$(PATH) fi @git config core.hooksPath .husky -## === Tasks === +### Tasks .PHONY: brew ## Install project binaries @@ -73,36 +73,22 @@ install: install.debug: @go build -tags=safe -gclags="all=-N -l" -o ${GOPATH}/bin/sesamy main.go -## === Utils === +### Utils +.PHONY: help ## Show help text help: + @echo "\033[1;36mSesamy CLI\033[0m" @awk '{ \ - if ($$0 ~ /^.PHONY: [a-zA-Z\-\_0-9]+$$/) { \ - helpCommand = substr($$0, index($$0, ":") + 2); \ - if (helpMessage) { \ - printf "\033[36m%-23s\033[0m %s\n", \ - helpCommand, helpMessage; \ - helpMessage = ""; \ - } \ - } else if ($$0 ~ /^[a-zA-Z\-\_0-9.]+:/) { \ - helpCommand = substr($$0, 0, index($$0, ":")); \ - if (helpMessage) { \ - printf "\033[36m%-23s\033[0m %s\n", \ - helpCommand, helpMessage"\n"; \ - helpMessage = ""; \ - } \ - } else if ($$0 ~ /^##/) { \ - if (helpMessage) { \ - helpMessage = helpMessage"\n "substr($$0, 3); \ - } else { \ - helpMessage = substr($$0, 3); \ - } \ - } else { \ - if (helpMessage) { \ - print "\n "helpMessage"\n" \ - } \ - helpMessage = ""; \ - } \ - }' \ - $(MAKEFILE_LIST) + if($$0 ~ /^### /){ \ + if(help) printf "\033[36m%-23s\033[0m %s\n\n", cmd, help; help=""; \ + printf "\n\033[1;36m%s\033[0m\n", substr($$0,5); \ + } else if($$0 ~ /^[a-zA-Z0-9._-]+:/){ \ + cmd = substr($$0, 1, index($$0, ":")-1); \ + if(help) printf " \033[36m%-23s\033[0m %s\n", cmd, help; help=""; \ + } else if($$0 ~ /^##/){ \ + help = help ? help "\n " substr($$0,3) : substr($$0,3); \ + } else if(help){ \ + print "\n " help "\n"; help=""; \ + } \ + }' $(MAKEFILE_LIST) diff --git a/cmd/provision/server.go b/cmd/provision/server.go index d6c6cb5..bb06dd2 100644 --- a/cmd/provision/server.go +++ b/cmd/provision/server.go @@ -14,6 +14,7 @@ import ( googletagmanagerprovider "github.com/foomo/sesamy-cli/pkg/provider/googletagmanager" microsoftadsprovider "github.com/foomo/sesamy-cli/pkg/provider/microsoftads" mixpanelprovider "github.com/foomo/sesamy-cli/pkg/provider/mixpanel" + pinterestprovider "github.com/foomo/sesamy-cli/pkg/provider/pinterest" tracifyprovider "github.com/foomo/sesamy-cli/pkg/provider/tracify" umamiprovider "github.com/foomo/sesamy-cli/pkg/provider/umami" ptermx "github.com/foomo/sesamy-cli/pkg/pterm" @@ -144,6 +145,13 @@ func NewServer(l *slog.Logger) *cobra.Command { } } + if cfg.Pinterest.Enabled && utils.Tag(pinterestprovider.Tag, tags) { + l.Info("🅿️ Running provider", "name", pinterestprovider.Name, "tag", pinterestprovider.Tag) + if err := pinterestprovider.Server(cmd.Context(), l, tm, cfg.Pinterest); err != nil { + return errors.Wrap(err, "failed to provision mixpanel") + } + } + if missed := tm.Missed(); len(tags) == 0 && len(missed) > 0 { tree := pterm.TreeNode{ Text: "♻️ Missed resources (potentially garbage)", diff --git a/cmd/tags.go b/cmd/tags.go index d540743..68569ed 100644 --- a/cmd/tags.go +++ b/cmd/tags.go @@ -2,6 +2,8 @@ package cmd import ( "log/slog" + "maps" + "slices" "github.com/foomo/sesamy-cli/pkg/provider/conversionlinker" "github.com/foomo/sesamy-cli/pkg/provider/cookiebot" @@ -16,6 +18,7 @@ import ( "github.com/foomo/sesamy-cli/pkg/provider/hotjar" "github.com/foomo/sesamy-cli/pkg/provider/microsoftads" "github.com/foomo/sesamy-cli/pkg/provider/mixpanel" + "github.com/foomo/sesamy-cli/pkg/provider/pinterest" "github.com/foomo/sesamy-cli/pkg/provider/tracify" "github.com/foomo/sesamy-cli/pkg/provider/umami" "github.com/pterm/pterm" @@ -28,26 +31,29 @@ func NewTags(l *slog.Logger) *cobra.Command { Use: "tags", Short: "Print out all available tags", RunE: func(cmd *cobra.Command, args []string) error { - // Define the data for the first table - data := pterm.TableData{ - {"Name", "Tag"}, - {conversionlinker.Name, conversionlinker.Tag}, - {cookiebot.Name, cookiebot.Tag}, - {criteo.Name, criteo.Tag}, - {emarsys.Name, emarsys.Tag}, - {facebook.Name, facebook.Tag}, - {googleads.Name, googleads.Tag}, - {googleanalytics.Name, googleanalytics.Tag}, - {googletag.Name, googletag.Tag}, - {googleconsent.Name, googleconsent.Tag}, - {googletagmanager.Name, googletagmanager.Tag}, - {hotjar.Name, hotjar.Tag}, - {microsoftads.Name, microsoftads.Tag}, - {mixpanel.Name, mixpanel.Tag}, - {tracify.Name, tracify.Tag}, - {umami.Name, umami.Tag}, + tags := map[string]string{ + conversionlinker.Name: conversionlinker.Tag, + cookiebot.Name: cookiebot.Tag, + criteo.Name: criteo.Tag, + emarsys.Name: emarsys.Tag, + facebook.Name: facebook.Tag, + googleads.Name: googleads.Tag, + googleanalytics.Name: googleanalytics.Tag, + googletag.Name: googletag.Tag, + googleconsent.Name: googleconsent.Tag, + googletagmanager.Name: googletagmanager.Tag, + hotjar.Name: hotjar.Tag, + microsoftads.Name: microsoftads.Tag, + mixpanel.Name: mixpanel.Tag, + tracify.Name: tracify.Tag, + umami.Name: umami.Tag, + pinterest.Name: pinterest.Tag, + } + // Define the data for the first table + data := pterm.TableData{{"Name", "Tag"}} + for _, name := range slices.Sorted(maps.Keys(tags)) { + data = append(data, []string{name, tags[name]}) } - return pterm.DefaultTable.WithHasHeader().WithData(data).Render() }, } diff --git a/go.mod b/go.mod index 12a4d42..6de9923 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/foomo/sesamy-cli -go 1.24.3 +go 1.25.1 require ( github.com/Code-Hex/Neo-cowsay/v2 v2.0.4 @@ -19,22 +19,22 @@ require ( github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c github.com/pkg/errors v0.9.1 github.com/pterm/pterm v0.12.81 - github.com/spf13/cobra v1.9.1 - github.com/spf13/viper v1.20.1 + github.com/spf13/cobra v1.10.1 + github.com/spf13/viper v1.21.0 github.com/sters/yaml-diff v1.4.1 github.com/stoewer/go-strcase v1.3.1 - github.com/stretchr/testify v1.10.0 + github.com/stretchr/testify v1.11.1 github.com/wissance/stringFormatter v1.5.0 - google.golang.org/api v0.242.0 + google.golang.org/api v0.249.0 ) require ( atomicgo.dev/cursor v0.2.0 // indirect atomicgo.dev/keyboard v0.2.9 // indirect atomicgo.dev/schedule v0.1.0 // indirect - cloud.google.com/go/auth v0.16.2 // indirect + cloud.google.com/go/auth v0.16.5 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect - cloud.google.com/go/compute/metadata v0.7.0 // indirect + cloud.google.com/go/compute/metadata v0.8.0 // indirect github.com/Code-Hex/go-wordwrap v1.0.0 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/buger/jsonparser v1.1.1 // indirect @@ -44,15 +44,15 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/foomo/gostandards v0.2.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect - github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/goccy/go-yaml v1.15.6 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect - github.com/googleapis/gax-go/v2 v2.14.2 // indirect - github.com/gookit/color v1.5.4 // indirect + github.com/googleapis/gax-go/v2 v2.15.0 // indirect + github.com/gookit/color v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/knadh/koanf/maps v0.1.2 // indirect github.com/lithammer/fuzzysearch v1.1.8 // indirect @@ -64,33 +64,32 @@ require ( github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/sagikazarmark/locafero v0.9.0 // indirect - github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.14.0 // indirect - github.com/spf13/cast v1.9.2 // indirect - github.com/spf13/pflag v1.0.7 // indirect + github.com/sagikazarmark/locafero v0.11.0 // indirect + github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect - go.opentelemetry.io/otel v1.36.0 // indirect - go.opentelemetry.io/otel/metric v1.36.0 // indirect - go.opentelemetry.io/otel/trace v1.36.0 // indirect - go.uber.org/multierr v1.11.0 // indirect + go.opentelemetry.io/otel v1.37.0 // indirect + go.opentelemetry.io/otel/metric v1.37.0 // indirect + go.opentelemetry.io/otel/trace v1.37.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.39.0 // indirect + golang.org/x/crypto v0.41.0 // indirect golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect - golang.org/x/mod v0.25.0 // indirect - golang.org/x/net v0.41.0 // indirect + golang.org/x/mod v0.27.0 // indirect + golang.org/x/net v0.43.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.34.0 // indirect - golang.org/x/term v0.33.0 // indirect - golang.org/x/text v0.27.0 // indirect - golang.org/x/tools v0.34.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect - google.golang.org/grpc v1.73.0 // indirect - google.golang.org/protobuf v1.36.6 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/term v0.35.0 // indirect + golang.org/x/text v0.29.0 // indirect + golang.org/x/tools v0.36.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect + google.golang.org/grpc v1.75.0 // indirect + google.golang.org/protobuf v1.36.8 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index c2d165f..a14c8d4 100644 --- a/go.sum +++ b/go.sum @@ -6,12 +6,12 @@ atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8= atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ= atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs= atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU= -cloud.google.com/go/auth v0.16.2 h1:QvBAGFPLrDeoiNjyfVunhQ10HKNYuOwZ5noee0M5df4= -cloud.google.com/go/auth v0.16.2/go.mod h1:sRBas2Y1fB1vZTdurouM0AzuYQBMZinrUYL8EufhtEA= +cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI= +cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= -cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU= -cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo= +cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA= +cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw= github.com/Code-Hex/Neo-cowsay/v2 v2.0.4 h1:y80Hd9hmB+rsEH/p4c5ti5PbO0PhBmxw4NgbpFZvoHg= github.com/Code-Hex/Neo-cowsay/v2 v2.0.4/go.mod h1:6k40Pwrc2FazLf1BUbmAC36E9LvT+DErjZr30isbXhg= github.com/Code-Hex/go-wordwrap v1.0.0 h1:yl5fLyZEz3+hPGbpTRlTQ8mQJ1HXWcTq1FCNR1ch6zM= @@ -60,8 +60,8 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= @@ -79,12 +79,14 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= -github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0= -github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w= +github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= +github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= +github.com/gookit/assert v0.1.1 h1:lh3GcawXe/p+cU7ESTZ5Ui3Sm/x8JWpIis4/1aF0mY0= +github.com/gookit/assert v0.1.1/go.mod h1:jS5bmIVQZTIwk42uXl4lyj4iaaxx32tqH16CFj0VX2E= github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= -github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= -github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= +github.com/gookit/color v1.6.0 h1:JjJXBTk1ETNyqyilJhkTXJYYigHG24TM9Xa2M1xAhRA= +github.com/gookit/color v1.6.0/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E= @@ -153,23 +155,23 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k= -github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk= +github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= +github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= -github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= -github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= -github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE= -github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= -github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/sters/yaml-diff v1.4.1 h1:0W3jnFKCu8/DV7nh2aXSDA2VVfxfHu2+qdh81CuFmZo= github.com/sters/yaml-diff v1.4.1/go.mod h1:K286Xp2z+aGkok7z9k3zXcq0ZsrDaDp7/wyGwFjM9Y8= github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs= @@ -183,8 +185,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/wissance/stringFormatter v1.5.0 h1:hiGpYEJS3B3H/6M03HecqW8nDO0OXgQ75REVTXp6reM= @@ -199,43 +201,41 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= -go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= -go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= -go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= -go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= -go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= -go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= -go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= -go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= -go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= -go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= +go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= +go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= +go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= -golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= -golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= -golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -247,43 +247,45 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= -golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= +golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= +golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= -golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.242.0 h1:7Lnb1nfnpvbkCiZek6IXKdJ0MFuAZNAJKQfA1ws62xg= -google.golang.org/api v0.242.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50= -google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78= -google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk= -google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= -google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= -google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/api v0.249.0 h1:0VrsWAKzIZi058aeq+I86uIXbNhm9GxSHpbmZ92a38w= +google.golang.org/api v0.249.0/go.mod h1:dGk9qyI0UYPwO/cjt2q06LG/EhUpwZGdAbYF14wHHrQ= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= +google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU= +google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c h1:qXWI/sQtv5UKboZ/zUk7h+mrf/lXORyI+n9DKDAusdg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo= +google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= +google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= +google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/pkg/config/config.go b/pkg/config/config.go index c4e55cc..b2dbb11 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -28,6 +28,8 @@ type Config struct { MicrosoftAds MicrosoftAds `json:"microsoftAds" yaml:"microsoftAds"` // Mixpanel provider settings Mixpanel Mixpanel `json:"mixpanel" yaml:"mixpanel"` + // Pinterest provider settings + Pinterest Pinterest `json:"pinterest" yaml:"pinterest"` // Emarsys provider settings Emarsys Emarsys `json:"emarsys" yaml:"emarsys"` // Hotjar provider settings diff --git a/pkg/config/pinterest.go b/pkg/config/pinterest.go new file mode 100644 index 0000000..ef0e648 --- /dev/null +++ b/pkg/config/pinterest.go @@ -0,0 +1,20 @@ +package config + +import ( + "github.com/foomo/gocontemplate/pkg/contemplate" +) + +type Pinterest struct { + // Enable provider + Enabled bool `json:"enabled" yaml:"enabled"` + // Pinterest advertiser id + AdvertiserID string `json:"advertiserId" yaml:"advertiserId"` + // Pinterest API access token + APIAccessToken string `json:"apiAccessToken" yaml:"apiAccessToken"` + // Enable test mode + TestModeEnabled bool `json:"testModeEnabled" yaml:"testModeEnabled"` + // Google Consent settings + GoogleConsent GoogleConsent `json:"googleConsent" yaml:"googleConsent"` + // Google Tag Manager server container settings + ServerContainer contemplate.Config `json:"serverContainer" yaml:"serverContainer"` +} diff --git a/pkg/provider/pinterest/constants.go b/pkg/provider/pinterest/constants.go new file mode 100644 index 0000000..2379a07 --- /dev/null +++ b/pkg/provider/pinterest/constants.go @@ -0,0 +1,7 @@ +package pinterest + +const ( + Tag = "pinterest" + Name = "Pinterest" + NameTagTemplate = "Pinterest API for Conversions Tag" +) diff --git a/pkg/provider/pinterest/server.go b/pkg/provider/pinterest/server.go new file mode 100644 index 0000000..e5b443c --- /dev/null +++ b/pkg/provider/pinterest/server.go @@ -0,0 +1,62 @@ +package pinterest + +import ( + "context" + "log/slog" + + "github.com/foomo/sesamy-cli/pkg/config" + "github.com/foomo/sesamy-cli/pkg/provider/googleconsent" + googleconsentvariable "github.com/foomo/sesamy-cli/pkg/provider/googleconsent/server/variable" + containertag "github.com/foomo/sesamy-cli/pkg/provider/pinterest/server/tag" + "github.com/foomo/sesamy-cli/pkg/provider/pinterest/server/trigger" + "github.com/foomo/sesamy-cli/pkg/tagmanager" + "github.com/foomo/sesamy-cli/pkg/utils" + "github.com/pkg/errors" +) + +func Server(ctx context.Context, l *slog.Logger, tm *tagmanager.TagManager, cfg config.Pinterest) error { + folder, err := tm.UpsertFolder(ctx, "Sesamy - "+Name) + if err != nil { + return err + } + + template, err := tm.LookupTemplate(ctx, NameTagTemplate) + if err != nil { + if errors.Is(err, tagmanager.ErrNotFound) { + l.Warn("Please install the 'Pinterest API for Conversions Ta' Tag Template manually first") + } + return err + } + + { // create tags + eventParameters, err := utils.LoadEventParams(ctx, cfg.ServerContainer) + if err != nil { + return err + } + + for event := range eventParameters { + var eventTriggerOpts []trigger.EventOption + if cfg.GoogleConsent.Enabled { + if err := googleconsent.ServerEnsure(ctx, tm); err != nil { + return err + } + consentVariable, err := tm.LookupVariable(ctx, googleconsentvariable.GoogleConsentModeName(cfg.GoogleConsent.Mode)) + if err != nil { + return err + } + eventTriggerOpts = append(eventTriggerOpts, trigger.ConversionWithConsentMode(consentVariable)) + } + + eventTrigger, err := tm.UpsertTrigger(ctx, folder, trigger.NewConversion(event, eventTriggerOpts...)) + if err != nil { + return errors.Wrap(err, "failed to upsert event trigger: "+event) + } + + if _, err := tm.UpsertTag(ctx, folder, containertag.NewConversion(event, cfg, template, eventTrigger)); err != nil { + return err + } + } + } + + return nil +} diff --git a/pkg/provider/pinterest/server/tag/conversion.go b/pkg/provider/pinterest/server/tag/conversion.go new file mode 100644 index 0000000..d392777 --- /dev/null +++ b/pkg/provider/pinterest/server/tag/conversion.go @@ -0,0 +1,59 @@ +package tag + +import ( + "strconv" + + "github.com/foomo/sesamy-cli/pkg/config" + "github.com/foomo/sesamy-cli/pkg/utils" + "google.golang.org/api/tagmanager/v2" +) + +func ConversionName(v string) string { + return "Pinterest Conversion - " + v +} + +func NewConversion(name string, cfg config.Pinterest, template *tagmanager.CustomTemplate, triggers ...*tagmanager.Trigger) *tagmanager.Tag { + return &tagmanager.Tag{ + FiringTriggerId: utils.TriggerIDs(triggers), + Name: ConversionName(name), + TagFiringOption: "oncePerEvent", + Parameter: []*tagmanager.Parameter{ + { + Key: "apiAccessToken", + Type: "template", + Value: cfg.APIAccessToken, + }, + { + Key: "testMode", + Type: "boolean", + Value: strconv.FormatBool(cfg.TestModeEnabled), + }, + { + Key: "eventName", + Type: "template", + Value: "inherit", + }, + { + Key: "logMode", + Type: "template", + Value: func(testMode bool) string { + if testMode { + return "log" + } + return "donotlog" + }(cfg.TestModeEnabled), + }, + { + Key: "overrideMode", + Type: "boolean", + Value: "false", + }, + { + Key: "advertiserId", + Type: "template", + Value: cfg.AdvertiserID, + }, + }, + Type: utils.TemplateType(template), + } +} diff --git a/pkg/provider/pinterest/server/trigger/conversion.go b/pkg/provider/pinterest/server/trigger/conversion.go new file mode 100644 index 0000000..fcf69f5 --- /dev/null +++ b/pkg/provider/pinterest/server/trigger/conversion.go @@ -0,0 +1,83 @@ +package trigger + +import ( + "google.golang.org/api/tagmanager/v2" +) + +func ConversionName(v string) string { + return "Pinterest Conversion - " + v +} + +type ( + EventOptions struct { + consentMode *tagmanager.Variable + } + EventOption func(*EventOptions) +) + +// ------------------------------------------------------------------------------------------------ +// ~ Options +// ------------------------------------------------------------------------------------------------ + +func ConversionWithConsentMode(mode *tagmanager.Variable) EventOption { + return func(o *EventOptions) { + o.consentMode = mode + } +} + +// ------------------------------------------------------------------------------------------------ +// ~ Constructor +// ------------------------------------------------------------------------------------------------ + +func NewConversion(name string, opts ...EventOption) *tagmanager.Trigger { + o := &EventOptions{} + for _, opt := range opts { + if opt != nil { + opt(o) + } + } + + var filter []*tagmanager.Condition + if o.consentMode != nil { + filter = append(filter, + &tagmanager.Condition{ + Parameter: []*tagmanager.Parameter{ + { + Key: "arg0", + Type: "template", + Value: "{{" + o.consentMode.Name + "}}", + }, + { + Key: "arg1", + Type: "template", + Value: "granted", + }, + }, + Type: "equals", + }, + ) + } + + return &tagmanager.Trigger{ + Type: "customEvent", + Name: ConversionName(name), + CustomEventFilter: []*tagmanager.Condition{ + { + Parameter: []*tagmanager.Parameter{ + { + Key: "arg0", + Type: "template", + Value: "{{_event}}", + }, + { + Key: "arg1", + Type: "template", + Value: name, + }, + }, + Type: "equals", + }, + }, + Filter: filter, + } +} diff --git a/sesamy.yaml b/sesamy.yaml index c377fbc..793375c 100644 --- a/sesamy.yaml +++ b/sesamy.yaml @@ -476,3 +476,37 @@ mixpanel: - ViewCart - ViewItem - ViewItemList + +# --- Pinterest +pinterest: + # Enable provider + enabled: true + # Pinterest advertiser id + advertiserId: '' + # Pinterest API access token + apiAccessToken: '' + # Enable test mode + testModeEnabled: false + # Google Consent settings + googleConsent: + # Enable consent mode + enabled: true + # Consent mode name + mode: analytics_storage + # Google Tag Manager server container settings + serverContainer: + # Track events + track: + # Directory containing the go.mod file + directory: . + # Contemplate package config for generated events + packages: + - path: 'github.com/foomo/sesamy-go/pkg/event' + types: + - AddToCart + - GenerateLead + - PageView + - Purchase + - Search + - SignUp + - ViewItemList