Merge pull request #90 from foomo/v0.13.0

V0.13.0
This commit is contained in:
Kevin Franklin Kim 2025-02-24 15:08:28 +01:00 committed by GitHub
commit c0248b188b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 762 additions and 221 deletions

View File

@ -18,8 +18,9 @@ updates:
interval: 'weekly' interval: 'weekly'
groups: groups:
gomod-security: gomod-security:
applies-to: security-updates
patterns: ['*'] patterns: ['*']
applies-to: security-updates
gomod-update: gomod-update:
applies-to: version-updates applies-to: version-updates
update-types: ['minor', 'patch']
patterns: ['*'] patterns: ['*']

View File

@ -1,5 +1,10 @@
# yaml-language-server: $schema=https://golangci-lint.run/jsonschema/golangci.jsonschema.json
# https://golangci-lint.run/usage/configuration/
run: run:
timeout: 5m go: 1.24.0
build-tags: [safe]
modules-download-mode: readonly
issues: issues:
exclude-dirs: exclude-dirs:
@ -95,7 +100,7 @@ linters-settings:
linters: linters:
disable-all: true disable-all: true
enable: enable:
## Enabled by default linters: ## 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] - 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] - 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] - 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]
@ -103,12 +108,12 @@ linters:
- 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] - 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] - unused # (megacheck) Checks Go code for unused constants, variables, functions and types [fast: false, auto-fix: false]
## Disabled by your configuration linters: ## Recommended linters
- asasalint # check for pass []any as any in variadic func(...any) [fast: false, auto-fix: false] - 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] - 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] - bidichk # Checks for dangerous unicode character sequences [fast: true, auto-fix: false]
- bodyclose # checks whether HTTP response body is closed successfully [fast: false, auto-fix: false] - bodyclose # checks whether HTTP response body is closed successfully [fast: false, auto-fix: false]
- canonicalheader # Checks whether net/http.Header uses canonical header [fast: false, auto-fix: false] - canonicalheader # checks whether net/http.Header uses canonical header [fast: false, auto-fix: false]
- containedctx # containedctx is a linter that detects struct contained context.Context field [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] - 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] - copyloopvar # (go >= 1.22) copyloopvar is a linter detects places where loop variables are copied [fast: true, auto-fix: false]
@ -118,7 +123,8 @@ linters:
- errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. [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] - 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] - exhaustive # check exhaustiveness of enum switch statements [fast: false, auto-fix: false]
- fatcontext #Detects nested contexts in loops [fast: false, auto-fix: false] - exptostd # Detects functions from golang.org/x/exp/ that can be replaced by std functions. [auto-fix]
- fatcontext # detects nested contexts in loops and function literals [fast: false, auto-fix: false]
#- forbidigo # Forbids identifiers [fast: false, auto-fix: false] #- forbidigo # Forbids identifiers [fast: false, auto-fix: false]
- forcetypeassert # finds forced type assertions [fast: true, auto-fix: false] - forcetypeassert # finds forced type assertions [fast: true, auto-fix: false]
- gocheckcompilerdirectives # Checks that go compiler directive comments (//go:) are valid. [fast: true, auto-fix: false] - gocheckcompilerdirectives # Checks that go compiler directive comments (//go:) are valid. [fast: true, auto-fix: false]
@ -134,6 +140,7 @@ linters:
- gosec # (gas) Inspects source code for security problems [fast: false, 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] - 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] - grouper # Analyze expression groups. [fast: true, auto-fix: false]
- iface # Detect the incorrect use of interfaces, helping developers avoid interface pollution. [fast: false, auto-fix: false]
- importas # Enforces consistent import aliases [fast: false, auto-fix: false] - importas # Enforces consistent import aliases [fast: false, auto-fix: false]
- inamedparam # reports interfaces with unnamed method parameters [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] - 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]
@ -154,6 +161,7 @@ linters:
- predeclared # find code that shadows one of Go's predeclared identifiers [fast: true, 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] - 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] - reassign # Checks that package variables are not reassigned [fast: false, auto-fix: false]
- recvcheck # checks for receiver type consistency [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] - revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint. [fast: false, auto-fix: false]
- rowserrcheck # checks whether Rows.Err of rows is checked successfully [fast: false, 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] - spancheck # Checks for mistakes with OpenTelemetry/Census spans. [fast: false, auto-fix: false]
@ -166,34 +174,37 @@ linters:
- tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes. [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] - unconvert # Remove unnecessary type conversions [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] - usestdlibvars # A linter that detect the possibility to use variables/constants from the Go standard library. [fast: true, auto-fix: false]
- usetesting # Reports uses of functions with replacement inside the testing package. [auto-fix]
- wastedassign # Finds wasted assignment statements [fast: false, auto-fix: false] - 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] - 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 ## Discouraged linters
#- cyclop # checks function and package cyclomatic complexity [fast: false, auto-fix: false] #- 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] #- depguard # Go linter that checks if package imports are in a list of acceptable packages [fast: true, auto-fix: false]
#- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) [fast: true, auto-fix: false]
#- dupl # Tool for code clone detection [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] #- 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] #- 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] #- 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] #- exhaustruct # Checks if all structure fields are initialized [fast: false, auto-fix: false]
#- exptostd # Detects functions from golang.org/x/exp/ that can be replaced by std functions. [auto-fix] #- funlen # Tool for detection of long functions [fast: true, auto-fix: false]
#- gci # Gci controls Go package import order and makes it always deterministic. [fast: true, auto-fix: true] #- gci # Gci controls Go package import order and makes it always deterministic. [fast: true, auto-fix: true]
#- ginkgolinter # enforces standards of using ginkgo and gomega [fast: false, auto-fix: false]
#- gochecknoglobals # Check that no global variables exist. [fast: false, auto-fix: false] #- 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] #- gochecknoinits # Checks that no init functions are present in Go code [fast: true, auto-fix: false]
#- gocognit # Computes and checks the cognitive complexity of functions [fast: true, auto-fix: false]
#- gocyclo # Computes and checks the cyclomatic complexity of functions [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] #- 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] #- godox # Tool for detection of comment keywords [fast: true, auto-fix: false]
#- gofumpt # Gofumpt checks whether code was gofumpt-ed. [fast: true, auto-fix: true] #- 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] #- 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] #- 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]
#- ireturn # Accept Interfaces, Return Concrete Types [fast: false, auto-fix: false] #- ireturn # Accept Interfaces, Return Concrete Types [fast: false, auto-fix: false]
#- lll # Reports long lines [fast: true, auto-fix: false]
#- maintidx # maintidx measures the maintainability index of each function. [fast: true, 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] #- 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] #- nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity [fast: true, auto-fix: false]
#- mnd # An analyzer to detect magic numbers. [fast: true, auto-fix: false]
#- perfsprint # Checks that fmt.Sprintf can be replaced with a faster alternative. [fast: false, 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] #- 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] #- protogetter # Reports direct reads from proto message fields when getters should be used [fast: false, auto-fix: true]

20
cmd/list.go Normal file
View File

@ -0,0 +1,20 @@
package cmd
import (
"github.com/foomo/sesamy-cli/cmd/list"
"github.com/spf13/cobra"
)
// NewList represents the list command
func NewList(root *cobra.Command) *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "List Google Tag Manager containers",
}
list.NewServer(cmd)
list.NewWeb(cmd)
root.AddCommand(cmd)
return cmd
}

70
cmd/list/list.go Normal file
View File

@ -0,0 +1,70 @@
package list
import (
"bytes"
"fmt"
"log/slog"
"os"
"strings"
"github.com/alecthomas/chroma/quick"
"github.com/foomo/sesamy-cli/pkg/tagmanager"
"github.com/itchyny/json2yaml"
)
func dump(i interface{ MarshalJSON() ([]byte, error) }, err error) error {
if err != nil {
return err
}
out, err := i.MarshalJSON()
if err != nil {
return err
}
// if err := json.Indent(ret, out, "", " "); err != nil {
// return err
// }
// fmt.Println(ret.String())
var output strings.Builder
if err := json2yaml.Convert(&output, bytes.NewBuffer(out)); err != nil {
return err
}
// fmt.Print(output.String())
return quick.Highlight(os.Stdout, output.String(), "yaml", "terminal", "monokai")
}
func list(l *slog.Logger, tm *tagmanager.TagManager, resource string) error {
switch resource {
case "clients":
return dump(tm.Service().Accounts.Containers.Workspaces.Clients.List(tm.WorkspacePath()).Do())
case "tags":
return dump(tm.Service().Accounts.Containers.Workspaces.Tags.List(tm.WorkspacePath()).Do())
case "built-in-variables":
return dump(tm.Service().Accounts.Containers.Workspaces.BuiltInVariables.List(tm.WorkspacePath()).Do())
case "folders":
return dump(tm.Service().Accounts.Containers.Workspaces.Folders.List(tm.WorkspacePath()).Do())
case "variables":
return dump(tm.Service().Accounts.Containers.Workspaces.Variables.List(tm.WorkspacePath()).Do())
case "templates":
return dump(tm.Service().Accounts.Containers.Workspaces.Templates.List(tm.WorkspacePath()).Do())
case "templates-data":
r, err := tm.Service().Accounts.Containers.Workspaces.Templates.List(tm.WorkspacePath()).Do()
if err != nil {
return err
}
for _, template := range r.Template {
l.Info("---- Template data: " + template.Name + " ----------------------")
fmt.Println(template.TemplateData)
}
return nil
case "gtag-config":
return dump(tm.Service().Accounts.Containers.Workspaces.GtagConfig.List(tm.WorkspacePath()).Do())
case "triggers":
return dump(tm.Service().Accounts.Containers.Workspaces.Triggers.List(tm.WorkspacePath()).Do())
case "transformations":
return dump(tm.Service().Accounts.Containers.Workspaces.Transformations.List(tm.WorkspacePath()).Do())
case "zones":
return dump(tm.Service().Accounts.Containers.Workspaces.Zones.List(tm.WorkspacePath()).Do())
default:
return fmt.Errorf("unknown resource %s", resource)
}
}

55
cmd/list/server.go Normal file
View File

@ -0,0 +1,55 @@
package list
import (
pkgcmd "github.com/foomo/sesamy-cli/pkg/cmd"
"github.com/foomo/sesamy-cli/pkg/tagmanager"
"github.com/spf13/cobra"
)
// NewServer represents the server command
func NewServer(root *cobra.Command) {
cmd := &cobra.Command{
Use: "server",
Short: "List Google Tag Manager Server Container",
Args: cobra.OnlyValidArgs,
ValidArgs: []cobra.Completion{
"built-in-variables",
"clients",
"folders",
"gtag-config",
"tags",
"templates",
"templates-data",
"transformations",
"triggers",
"variables",
"zones",
},
RunE: func(cmd *cobra.Command, args []string) error {
resource := args[0]
l := pkgcmd.Logger()
l.Info("☕ Listing Server Container resources: " + resource)
cfg, err := pkgcmd.ReadConfig(l, cmd)
if err != nil {
return err
}
tm, err := tagmanager.New(
cmd.Context(),
l,
cfg.GoogleTagManager.AccountID,
cfg.GoogleTagManager.ServerContainer,
tagmanager.WithRequestQuota(cfg.GoogleAPI.RequestQuota),
tagmanager.WithClientOptions(cfg.GoogleAPI.GetClientOption()),
)
if err != nil {
return err
}
return list(l, tm, resource)
},
}
root.AddCommand(cmd)
}

54
cmd/list/web.go Normal file
View File

@ -0,0 +1,54 @@
package list
import (
pkgcmd "github.com/foomo/sesamy-cli/pkg/cmd"
"github.com/foomo/sesamy-cli/pkg/tagmanager"
"github.com/spf13/cobra"
)
// NewWeb represents the web command
func NewWeb(root *cobra.Command) {
cmd := &cobra.Command{
Use: "web",
Short: "List Google Tag Manager Web Container",
Args: cobra.OnlyValidArgs,
ValidArgs: []cobra.Completion{
"built-in-variables",
"folders",
"gtag-config",
"tags",
"templates",
"templates-data",
"transformations",
"triggers",
"variables",
"zones",
},
RunE: func(cmd *cobra.Command, args []string) error {
resource := args[0]
l := pkgcmd.Logger()
l.Info("☕ Listing Web Container resources: " + resource)
cfg, err := pkgcmd.ReadConfig(l, cmd)
if err != nil {
return err
}
tm, err := tagmanager.New(
cmd.Context(),
l,
cfg.GoogleTagManager.AccountID,
cfg.GoogleTagManager.WebContainer,
tagmanager.WithRequestQuota(cfg.GoogleAPI.RequestQuota),
tagmanager.WithClientOptions(cfg.GoogleAPI.GetClientOption()),
)
if err != nil {
return err
}
return list(l, tm, resource)
},
}
root.AddCommand(cmd)
}

20
cmd/provision.go Normal file
View File

@ -0,0 +1,20 @@
package cmd
import (
"github.com/foomo/sesamy-cli/cmd/provision"
"github.com/spf13/cobra"
)
// NewProvision represents the provision command
func NewProvision(root *cobra.Command) *cobra.Command {
cmd := &cobra.Command{
Use: "provision",
Short: "Provision Google Tag Manager containers",
}
provision.NewServer(cmd)
provision.NewWeb(cmd)
root.AddCommand(cmd)
return cmd
}

View File

@ -1,4 +1,4 @@
package tagmanager package provision
import ( import (
pkgcmd "github.com/foomo/sesamy-cli/pkg/cmd" pkgcmd "github.com/foomo/sesamy-cli/pkg/cmd"
@ -15,6 +15,7 @@ import (
umamiprovider "github.com/foomo/sesamy-cli/pkg/provider/umami" umamiprovider "github.com/foomo/sesamy-cli/pkg/provider/umami"
"github.com/foomo/sesamy-cli/pkg/tagmanager" "github.com/foomo/sesamy-cli/pkg/tagmanager"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/pterm/pterm"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
@ -26,6 +27,7 @@ func NewServer(root *cobra.Command) {
Short: "Provision Google Tag Manager Server Container", Short: "Provision Google Tag Manager Server Container",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
l := pkgcmd.Logger() l := pkgcmd.Logger()
l.Info("☕ Provisioning Server Container")
tags, err := cmd.Flags().GetStringSlice("tags") tags, err := cmd.Flags().GetStringSlice("tags")
if err != nil { if err != nil {
@ -124,6 +126,24 @@ func NewServer(root *cobra.Command) {
} }
} }
if missed := tm.Missed(); len(tags) == 0 && len(missed) > 0 {
tree := pterm.TreeNode{
Text: "♻️ Missed resources (potentially garbage)",
}
for k, i := range missed {
child := pterm.TreeNode{
Text: k,
}
for _, s := range i {
child.Children = append(child.Children, pterm.TreeNode{Text: s})
}
tree.Children = append(tree.Children, child)
}
if err := pterm.DefaultTree.WithRoot(tree).Render(); err != nil {
l.Warn("failed to render missed resources", "error", err)
}
}
return nil return nil
}, },
} }

View File

@ -1,4 +1,4 @@
package tagmanager package provision
import ( import (
pkgcmd "github.com/foomo/sesamy-cli/pkg/cmd" pkgcmd "github.com/foomo/sesamy-cli/pkg/cmd"
@ -10,6 +10,7 @@ import (
hotjarprovider "github.com/foomo/sesamy-cli/pkg/provider/hotjar" hotjarprovider "github.com/foomo/sesamy-cli/pkg/provider/hotjar"
"github.com/foomo/sesamy-cli/pkg/tagmanager" "github.com/foomo/sesamy-cli/pkg/tagmanager"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/pterm/pterm"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
@ -21,6 +22,7 @@ func NewWeb(root *cobra.Command) {
Short: "Provision Google Tag Manager Web Container", Short: "Provision Google Tag Manager Web Container",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
l := pkgcmd.Logger() l := pkgcmd.Logger()
l.Info("☕ Provisioning Web Container")
tags, err := cmd.Flags().GetStringSlice("tags") tags, err := cmd.Flags().GetStringSlice("tags")
if err != nil { if err != nil {
@ -45,41 +47,65 @@ func NewWeb(root *cobra.Command) {
} }
if pkgcmd.Tag(googletagprovider.Tag, tags) { if pkgcmd.Tag(googletagprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", googletagprovider.Name, "tag", googletagprovider.Tag)
if err := googletagprovider.Web(tm, cfg.GoogleTag); err != nil { if err := googletagprovider.Web(tm, cfg.GoogleTag); err != nil {
return errors.Wrap(err, "failed to provision google tag provider") return errors.Wrap(err, "failed to provision google tag provider")
} }
} }
if cfg.GoogleAnalytics.Enabled && pkgcmd.Tag(googleanaylticsprovider.Tag, tags) { if cfg.GoogleAnalytics.Enabled && pkgcmd.Tag(googleanaylticsprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", googleanaylticsprovider.Name, "tag", googleanaylticsprovider.Tag)
if err := googleanaylticsprovider.Web(tm, cfg.GoogleAnalytics); err != nil { if err := googleanaylticsprovider.Web(tm, cfg.GoogleAnalytics); err != nil {
return errors.Wrap(err, "failed to provision google analytics provider") return errors.Wrap(err, "failed to provision google analytics provider")
} }
} }
if cfg.Emarsys.Enabled && pkgcmd.Tag(emarsysprovider.Tag, tags) { if cfg.Emarsys.Enabled && pkgcmd.Tag(emarsysprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", emarsysprovider.Name, "tag", emarsysprovider.Tag)
if err := emarsysprovider.Web(tm, cfg.Emarsys); err != nil { if err := emarsysprovider.Web(tm, cfg.Emarsys); err != nil {
return errors.Wrap(err, "failed to provision emarsys provider") return errors.Wrap(err, "failed to provision emarsys provider")
} }
} }
if cfg.Hotjar.Enabled && pkgcmd.Tag(hotjarprovider.Tag, tags) { if cfg.Hotjar.Enabled && pkgcmd.Tag(hotjarprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", hotjarprovider.Name, "tag", hotjarprovider.Tag)
if err := hotjarprovider.Web(tm, cfg.Hotjar); err != nil { if err := hotjarprovider.Web(tm, cfg.Hotjar); err != nil {
return errors.Wrap(err, "failed to provision hotjar provider") return errors.Wrap(err, "failed to provision hotjar provider")
} }
} }
if cfg.Criteo.Enabled && pkgcmd.Tag(criteoprovider.Tag, tags) { if cfg.Criteo.Enabled && pkgcmd.Tag(criteoprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", criteoprovider.Name, "tag", criteoprovider.Tag)
if err := criteoprovider.Web(l, tm, cfg.Criteo); err != nil { if err := criteoprovider.Web(l, tm, cfg.Criteo); err != nil {
return errors.Wrap(err, "failed to provision criteo provider") return errors.Wrap(err, "failed to provision criteo provider")
} }
} }
if cfg.Cookiebot.Enabled && pkgcmd.Tag(cookiebotprovider.Tag, tags) { if cfg.Cookiebot.Enabled && pkgcmd.Tag(cookiebotprovider.Tag, tags) {
l.Info("🅿️ Running provider", "name", cookiebotprovider.Name, "tag", cookiebotprovider.Tag)
if err := cookiebotprovider.Web(tm, cfg.Cookiebot); err != nil { if err := cookiebotprovider.Web(tm, cfg.Cookiebot); err != nil {
return errors.Wrap(err, "failed to provision cookiebot provider") return errors.Wrap(err, "failed to provision cookiebot provider")
} }
} }
if missed := tm.Missed(); len(tags) == 0 && len(missed) > 0 {
tree := pterm.TreeNode{
Text: "♻️ Missed resources (potentially garbage)",
}
for k, i := range missed {
child := pterm.TreeNode{
Text: k,
}
for _, s := range i {
child.Children = append(child.Children, pterm.TreeNode{Text: s})
}
tree.Children = append(tree.Children, child)
}
if err := pterm.DefaultTree.WithRoot(tree).Render(); err != nil {
l.Warn("failed to render missed resources", "error", err)
}
}
return nil return nil
}, },
} }

View File

@ -14,7 +14,9 @@ func init() {
root = NewRoot() root = NewRoot()
NewConfig(root) NewConfig(root)
NewVersion(root) NewVersion(root)
NewTagmanager(root) NewTags(root)
NewList(root)
NewProvision(root)
NewTypeScript(root) NewTypeScript(root)
cobra.OnInitialize(pkgcmd.InitConfig) cobra.OnInitialize(pkgcmd.InitConfig)
} }

View File

@ -1,21 +0,0 @@
package cmd
import (
"github.com/foomo/sesamy-cli/cmd/tagmanager"
"github.com/spf13/cobra"
)
// NewTagmanager represents the tagmanager command
func NewTagmanager(root *cobra.Command) *cobra.Command {
cmd := &cobra.Command{
Use: "tagmanager",
Short: "Provision Google Tag Manager containers",
}
tagmanager.NewTags(cmd)
tagmanager.NewServer(cmd)
tagmanager.NewWeb(cmd)
root.AddCommand(cmd)
return cmd
}

View File

@ -1,46 +0,0 @@
package tagmanager
import (
conversionlinkerprovider "github.com/foomo/sesamy-cli/pkg/provider/conversionlinker"
criteoprovider "github.com/foomo/sesamy-cli/pkg/provider/criteo"
emarsysprovider "github.com/foomo/sesamy-cli/pkg/provider/emarsys"
facebookprovider "github.com/foomo/sesamy-cli/pkg/provider/facebook"
googleadsprovider "github.com/foomo/sesamy-cli/pkg/provider/googleads"
googleanalyticsprovider "github.com/foomo/sesamy-cli/pkg/provider/googleanalytics"
googletagmanagerprovider "github.com/foomo/sesamy-cli/pkg/provider/googletagmanager"
microsoftadsprovider "github.com/foomo/sesamy-cli/pkg/provider/microsoftads"
tracifyprovider "github.com/foomo/sesamy-cli/pkg/provider/tracify"
umamiprovider "github.com/foomo/sesamy-cli/pkg/provider/umami"
"github.com/pterm/pterm"
"github.com/spf13/cobra"
)
// NewTags represents the tags command
func NewTags(root *cobra.Command) *cobra.Command {
cmd := &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"},
{conversionlinkerprovider.Name, conversionlinkerprovider.Tag},
{criteoprovider.Name, criteoprovider.Tag},
{emarsysprovider.Name, emarsysprovider.Tag},
{facebookprovider.Name, facebookprovider.Tag},
{googleadsprovider.Name, googleadsprovider.Tag},
{googleanalyticsprovider.Name, googleanalyticsprovider.Tag},
{googletagmanagerprovider.Name, googletagmanagerprovider.Tag},
{microsoftadsprovider.Name, microsoftadsprovider.Tag},
{tracifyprovider.Name, tracifyprovider.Tag},
{umamiprovider.Name, umamiprovider.Tag},
}
return pterm.DefaultTable.WithHasHeader().WithData(data).Render()
},
}
root.AddCommand(cmd)
return cmd
}

54
cmd/tags.go Normal file
View File

@ -0,0 +1,54 @@
package cmd
import (
"github.com/foomo/sesamy-cli/pkg/provider/conversionlinker"
"github.com/foomo/sesamy-cli/pkg/provider/cookiebot"
"github.com/foomo/sesamy-cli/pkg/provider/criteo"
"github.com/foomo/sesamy-cli/pkg/provider/emarsys"
"github.com/foomo/sesamy-cli/pkg/provider/facebook"
"github.com/foomo/sesamy-cli/pkg/provider/googleads"
"github.com/foomo/sesamy-cli/pkg/provider/googleanalytics"
"github.com/foomo/sesamy-cli/pkg/provider/googleconsent"
"github.com/foomo/sesamy-cli/pkg/provider/googletag"
"github.com/foomo/sesamy-cli/pkg/provider/googletagmanager"
"github.com/foomo/sesamy-cli/pkg/provider/hotjar"
"github.com/foomo/sesamy-cli/pkg/provider/microsoftads"
"github.com/foomo/sesamy-cli/pkg/provider/tracify"
"github.com/foomo/sesamy-cli/pkg/provider/umami"
"github.com/pterm/pterm"
"github.com/spf13/cobra"
)
// NewTags represents the tags command
func NewTags(root *cobra.Command) *cobra.Command {
cmd := &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},
{tracify.Name, tracify.Tag},
{umami.Name, umami.Tag},
}
return pterm.DefaultTable.WithHasHeader().WithData(data).Render()
},
}
root.AddCommand(cmd)
return cmd
}

View File

@ -1,16 +1,17 @@
package cmd package cmd
import ( import (
"maps"
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"slices"
"github.com/foomo/gocontemplate/pkg/contemplate" "github.com/foomo/gocontemplate/pkg/contemplate"
pkgcmd "github.com/foomo/sesamy-cli/pkg/cmd" pkgcmd "github.com/foomo/sesamy-cli/pkg/cmd"
"github.com/foomo/sesamy-cli/pkg/typescript/generator" "github.com/foomo/sesamy-cli/pkg/typescript/generator"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"golang.org/x/exp/maps"
) )
// NewTypeScript represents the typescript command // NewTypeScript represents the typescript command
@ -45,7 +46,7 @@ func NewTypeScript(root *cobra.Command) *cobra.Command {
return errors.Wrap(err, "failed to create typescript output directory") return errors.Wrap(err, "failed to create typescript output directory")
} }
l.InfoContext(cmd.Context(), "generated typescript code", "dir", outPath, "files", maps.Keys(files)) l.InfoContext(cmd.Context(), "generated typescript code", "dir", outPath, "files", slices.AppendSeq(make([]string, 0, len(files)), maps.Keys(files)))
for filename, file := range files { for filename, file := range files {
if err = os.WriteFile(path.Join(outPath, filename), []byte(file.String()), 0600); err != nil { if err = os.WriteFile(path.Join(outPath, filename), []byte(file.String()), 0600); err != nil {
return errors.Wrap(err, "failed to write typescript code") return errors.Wrap(err, "failed to write typescript code")

21
go.mod
View File

@ -1,25 +1,27 @@
module github.com/foomo/sesamy-cli module github.com/foomo/sesamy-cli
go 1.23.4 go 1.24.0
require ( require (
github.com/alecthomas/chroma v0.10.0
github.com/fatih/structtag v1.2.0 github.com/fatih/structtag v1.2.0
github.com/foomo/go v0.0.3 github.com/foomo/go v0.0.3
github.com/foomo/gocontemplate v0.1.4 github.com/foomo/gocontemplate v0.1.4
github.com/foomo/sesamy-go v0.8.1 github.com/foomo/sesamy-go v0.8.1
github.com/invopop/jsonschema v0.13.0 github.com/invopop/jsonschema v0.13.0
github.com/itchyny/json2yaml v0.1.4
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/pterm/pterm v0.12.80 github.com/pterm/pterm v0.12.80
github.com/spf13/cobra v1.9.0 github.com/spf13/cobra v1.9.1
github.com/spf13/viper v1.19.0 github.com/spf13/viper v1.19.0
github.com/stoewer/go-strcase v1.3.0 github.com/stoewer/go-strcase v1.3.0
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/wissance/stringFormatter v1.3.0 github.com/wissance/stringFormatter v1.3.0
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa
google.golang.org/api v0.221.0 google.golang.org/api v0.222.0
) )
require ( require (
@ -33,6 +35,7 @@ require (
github.com/buger/jsonparser v1.1.1 // indirect github.com/buger/jsonparser v1.1.1 // indirect
github.com/containerd/console v1.0.4 // indirect github.com/containerd/console v1.0.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/foomo/gostandards v0.2.0 // indirect github.com/foomo/gostandards v0.2.0 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect
@ -52,10 +55,10 @@ require (
github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect
github.com/sagikazarmark/locafero v0.6.0 // indirect github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect github.com/spf13/afero v1.12.0 // indirect
github.com/spf13/cast v1.7.1 // indirect github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect github.com/spf13/pflag v1.0.6 // indirect
github.com/subosito/gotenv v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect
@ -68,15 +71,15 @@ require (
go.opentelemetry.io/otel/trace v1.34.0 // indirect go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.33.0 // indirect golang.org/x/crypto v0.33.0 // indirect
golang.org/x/mod v0.22.0 // indirect golang.org/x/mod v0.23.0 // indirect
golang.org/x/net v0.35.0 // indirect golang.org/x/net v0.35.0 // indirect
golang.org/x/oauth2 v0.26.0 // indirect golang.org/x/oauth2 v0.26.0 // indirect
golang.org/x/sync v0.11.0 // indirect golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect golang.org/x/sys v0.30.0 // indirect
golang.org/x/term v0.29.0 // indirect golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect golang.org/x/text v0.22.0 // indirect
golang.org/x/tools v0.28.0 // indirect golang.org/x/tools v0.30.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250212204824-5a70512c5d8b // indirect
google.golang.org/grpc v1.70.0 // indirect google.golang.org/grpc v1.70.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect

40
go.sum
View File

@ -21,6 +21,8 @@ github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/
github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE= github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE=
github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi+zB4= github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi+zB4=
github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY= github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY=
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk= github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
@ -34,6 +36,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
@ -77,6 +81,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E= github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/itchyny/json2yaml v0.1.4 h1:/pErVOXGG5iTyXHi/QKR4y3uzhLjGTEmmJIy97YT+k8=
github.com/itchyny/json2yaml v0.1.4/go.mod h1:6iudhBZdarpjLFRNj+clWLAkGft+9uCcjAZYXUH9eGI=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
@ -126,20 +132,20 @@ 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 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= 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/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= 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/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 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.9.0 h1:Py5fIuq/lJsRYxcxfOtsJqpmwJWCMOUy2tMJYV8TNHE= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.0/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
@ -187,12 +193,12 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo= golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4=
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 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.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 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-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.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
@ -237,16 +243,16 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 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.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.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.221.0 h1:qzaJfLhDsbMeFee8zBRdt/Nc+xmOuafD/dbdgGfutOU= google.golang.org/api v0.222.0 h1:Aiewy7BKLCuq6cUCeOUrsAlzjXPqBkEeQ/iwGHVQa/4=
google.golang.org/api v0.221.0/go.mod h1:7sOU2+TL4TxUTdbi0gWgAIg7tH5qBXxoyhtL+9x3biQ= google.golang.org/api v0.222.0/go.mod h1:efZia3nXpWELrwMlN5vyQrD4GmJN1Vw0x68Et3r+a9c=
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y= google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk=
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA= google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA=
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o= google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI= google.golang.org/genproto/googleapis/rpc v0.0.0-20250212204824-5a70512c5d8b h1:FQtJ1MxbXoIIrZHZ33M+w5+dAP9o86rgpjoKr/ZmT7k=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= google.golang.org/genproto/googleapis/rpc v0.0.0-20250212204824-5a70512c5d8b/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk=
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=

View File

@ -1,10 +1,9 @@
package code package code
import ( import (
"maps"
"slices" "slices"
"strings" "strings"
"golang.org/x/exp/maps"
) )
const ( const (
@ -62,7 +61,7 @@ func (f *File) AddSection(id int) {
func (f *File) String() string { func (f *File) String() string {
b := &strings.Builder{} b := &strings.Builder{}
sections := maps.Keys(f.Sections) sections := slices.AppendSeq(make([]int, 0, len(f.Sections)), maps.Keys(f.Sections))
slices.Sort(sections) slices.Sort(sections)
for _, id := range sections { for _, id := range sections {

View File

@ -1,8 +1,12 @@
package config package config
type GoogleTagManager struct { type GoogleTagManager struct {
AccountID string `json:"accountId" yaml:"accountId"` // Google Tag Manager account id
AccountID string `json:"accountId" yaml:"accountId"`
// Google Tag Manager web container settings
WebContainer GoogleTagManagerContainer `json:"webContainer" yaml:"webContainer"` WebContainer GoogleTagManagerContainer `json:"webContainer" yaml:"webContainer"`
// Google Tag Manager server container settings // Google Tag Manager server container settings
ServerContainer GoogleTagManagerContainer `json:"serverContainer" yaml:"serverContainer"` ServerContainer GoogleTagManagerContainer `json:"serverContainer" yaml:"serverContainer"`
// Google Tag Manager server container variables
ServerContaienrVariables GoogleTagManagerServerContainerVariables `json:"serverContainerVariables" yaml:"serverContainerVariables"`
} }

View File

@ -1,7 +1,10 @@
package config package config
type GoogleTagManagerContainer struct { type GoogleTagManagerContainer struct {
TagID string `json:"tagId" yaml:"tagId"` // The container tag id
TagID string `json:"tagId" yaml:"tagId"`
// The container id
ContainerID string `json:"containerId" yaml:"containerId"` ContainerID string `json:"containerId" yaml:"containerId"`
// The workspace id that should be used by the api
WorkspaceID string `json:"workspaceId" yaml:"workspaceId"` WorkspaceID string `json:"workspaceId" yaml:"workspaceId"`
} }

View File

@ -0,0 +1,8 @@
package config
type GoogleTagManagerServerContainerVariables struct {
// List of event data variables
EventData []string `json:"eventData" yaml:"eventData"`
// Map of lookup table variables
LookupTables map[string]LookupTable `json:"lookupTables" yaml:"lookupTables"`
}

10
pkg/config/lookuptable.go Normal file
View File

@ -0,0 +1,10 @@
package config
type LookupTable struct {
// Input source
Input string `json:"input" yaml:"input"`
// Key value data map
KeyTable map[string]string `json:"keyTable" yaml:"keyTable"`
// Vaule key data map
ValueTable map[string]string `json:"valueTable" yaml:"valueTable"`
}

View File

@ -5,6 +5,7 @@ import (
"github.com/foomo/sesamy-cli/pkg/provider/googletagmanager/server/client" "github.com/foomo/sesamy-cli/pkg/provider/googletagmanager/server/client"
"github.com/foomo/sesamy-cli/pkg/provider/googletagmanager/server/variable" "github.com/foomo/sesamy-cli/pkg/provider/googletagmanager/server/variable"
"github.com/foomo/sesamy-cli/pkg/tagmanager" "github.com/foomo/sesamy-cli/pkg/tagmanager"
variable2 "github.com/foomo/sesamy-cli/pkg/tagmanager/server/variable"
) )
func Server(tm *tagmanager.TagManager, cfg config.GoogleTagManager, enableGeoResolution bool) error { func Server(tm *tagmanager.TagManager, cfg config.GoogleTagManager, enableGeoResolution bool) error {
@ -33,5 +34,18 @@ func Server(tm *tagmanager.TagManager, cfg config.GoogleTagManager, enableGeoRes
} }
} }
{ // create variables
for _, value := range cfg.ServerContaienrVariables.EventData {
if _, err := tm.UpsertVariable(variable2.NewEventData(value)); err != nil {
return err
}
}
for key, value := range cfg.ServerContaienrVariables.LookupTables {
if _, err := tm.UpsertVariable(variable2.NewLookupTable(key, value)); err != nil {
return err
}
}
}
return nil return nil
} }

View File

@ -1,4 +1,4 @@
package facebook package microsoftads
const ( const (
Tag = "mads" Tag = "mads"

View File

@ -1,4 +1,4 @@
package facebook package microsoftads
import ( import (
"log/slog" "log/slog"

View File

@ -0,0 +1,69 @@
package variable
import (
"github.com/foomo/sesamy-cli/pkg/config"
"google.golang.org/api/tagmanager/v2"
)
func LookupTableName(v string) string {
return "lookup_table." + v
}
func NewLookupTable(name string, data config.LookupTable) *tagmanager.Variable {
var list []*tagmanager.Parameter
for k, v := range data.KeyTable {
list = append(list, &tagmanager.Parameter{
Type: "map",
Map: []*tagmanager.Parameter{
{
Key: "key",
Type: "template",
Value: k,
},
{
Key: "value",
Type: "template",
Value: v,
},
},
})
}
for k, v := range data.ValueTable {
list = append(list, &tagmanager.Parameter{
Type: "map",
Map: []*tagmanager.Parameter{
{
Key: "key",
Type: "template",
Value: v,
},
{
Key: "value",
Type: "template",
Value: k,
},
},
})
}
return &tagmanager.Variable{
Name: LookupTableName(name),
Parameter: []*tagmanager.Parameter{
{
Key: "setDefaultValue",
Type: "boolean",
Value: "false",
},
{
Key: "input",
Type: "template",
Value: data.Input,
},
{
Key: "map",
Type: "list",
List: list,
},
},
Type: "smm",
}
}

View File

@ -25,18 +25,57 @@ type (
requestThrottler *time.Ticker requestThrottler *time.Ticker
// cache // cache
service *tagmanager.Service service *tagmanager.Service
clients map[string]*tagmanager.Client clients *AccessedMap[*tagmanager.Client]
folders map[string]*tagmanager.Folder folders *AccessedMap[*tagmanager.Folder]
variables map[string]*tagmanager.Variable variables *AccessedMap[*tagmanager.Variable]
builtInVariables map[string]*tagmanager.BuiltInVariable builtInVariables *AccessedMap[*tagmanager.BuiltInVariable]
triggers map[string]*tagmanager.Trigger triggers *AccessedMap[*tagmanager.Trigger]
tags map[string]*tagmanager.Tag tags *AccessedMap[*tagmanager.Tag]
customTemplates map[string]*tagmanager.CustomTemplate customTemplates *AccessedMap[*tagmanager.CustomTemplate]
transformations map[string]*tagmanager.Transformation transformations *AccessedMap[*tagmanager.Transformation]
} }
Option func(*TagManager) Option func(*TagManager)
) )
type AccessedMap[T any] struct {
data map[string]T
keys map[string]bool
}
func NewAccessedMap[T any](data map[string]T) *AccessedMap[T] {
return &AccessedMap[T]{
data: data,
keys: make(map[string]bool, len(data)),
}
}
func (l AccessedMap[T]) Has(key string) bool {
_, ok := l.data[key]
return ok
}
func (l AccessedMap[T]) Get(key string) T {
if l.Has(key) {
l.keys[key] = true
}
return l.data[key]
}
func (l AccessedMap[T]) Set(key string, value T) {
l.keys[key] = true
l.data[key] = value
}
func (l AccessedMap[T]) Misssed() map[string]T {
ret := map[string]T{}
for k := range l.data {
if !l.keys[k] {
ret[k] = l.data[k]
}
}
return ret
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// ~ Options // ~ Options
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -102,6 +141,51 @@ func New(ctx context.Context, l *slog.Logger, accountID string, container config
// ~ Getter // ~ Getter
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
func (t *TagManager) Missed() map[string][]string {
ret := map[string][]string{}
if t.clients != nil {
for _, i2 := range t.clients.Misssed() {
ret["Clients"] = append(ret["Clients"], i2.Name)
}
}
if t.folders != nil {
for _, i2 := range t.folders.Misssed() {
ret["Folders"] = append(ret["Folders"], i2.Name)
}
}
if t.variables != nil {
for _, i2 := range t.variables.Misssed() {
ret["Variables"] = append(ret["Variables"], i2.Name)
}
}
if t.builtInVariables != nil {
for _, i2 := range t.builtInVariables.Misssed() {
ret["Built In Variables"] = append(ret["Built In Variables"], i2.Name)
}
}
if t.triggers != nil {
for _, i2 := range t.triggers.Misssed() {
ret["Triggers"] = append(ret["Triggers"], i2.Name)
}
}
if t.tags != nil {
for _, i2 := range t.tags.Misssed() {
ret["Tags"] = append(ret["Tags"], i2.Name)
}
}
if t.customTemplates != nil {
for _, i2 := range t.customTemplates.Misssed() {
ret["Custom Templates"] = append(ret["Custom Templates"], i2.Name)
}
}
if t.transformations != nil {
for _, i2 := range t.transformations.Misssed() {
ret["Transformations"] = append(ret["Transformations"], i2.Name)
}
}
return ret
}
func (t *TagManager) AccountID() string { func (t *TagManager) AccountID() string {
return t.accountID return t.accountID
} }
@ -163,16 +247,16 @@ func (t *TagManager) LookupClient(name string) (*tagmanager.Client, error) {
return nil, err return nil, err
} }
if _, ok := elems[name]; !ok { if !elems.Has(name) {
return nil, ErrNotFound return nil, ErrNotFound
} }
return elems[name], nil return elems.Get(name), nil
} }
func (t *TagManager) LoadClients() (map[string]*tagmanager.Client, error) { func (t *TagManager) LoadClients() (*AccessedMap[*tagmanager.Client], error) {
if t.clients == nil { if t.clients == nil {
t.l.Info("🛄 Loading list", "type", "Client") t.l.Info("└ ⬇︎ Loading list", "type", "Client")
r, err := t.Service().Accounts.Containers.Workspaces.Clients.List(t.WorkspacePath()).Do() r, err := t.Service().Accounts.Containers.Workspaces.Clients.List(t.WorkspacePath()).Do()
if err != nil { if err != nil {
return nil, err return nil, err
@ -182,7 +266,7 @@ func (t *TagManager) LoadClients() (map[string]*tagmanager.Client, error) {
for _, value := range r.Client { for _, value := range r.Client {
res[value.Name] = value res[value.Name] = value
} }
t.clients = res t.clients = NewAccessedMap(res)
} }
return t.clients, nil return t.clients, nil
@ -194,16 +278,16 @@ func (t *TagManager) LookupFolder(name string) (*tagmanager.Folder, error) {
return nil, err return nil, err
} }
if _, ok := elems[name]; !ok { if !elems.Has(name) {
return nil, ErrNotFound return nil, ErrNotFound
} }
return elems[name], nil return elems.Get(name), nil
} }
func (t *TagManager) LoadFolders() (map[string]*tagmanager.Folder, error) { func (t *TagManager) LoadFolders() (*AccessedMap[*tagmanager.Folder], error) {
if t.folders == nil { if t.folders == nil {
t.l.Info("🛄 Loading list", "type", "Folder") t.l.Info("└ ⬇︎ Loading list", "type", "Folder")
r, err := t.Service().Accounts.Containers.Workspaces.Folders.List(t.WorkspacePath()).Do() r, err := t.Service().Accounts.Containers.Workspaces.Folders.List(t.WorkspacePath()).Do()
if err != nil { if err != nil {
return nil, err return nil, err
@ -213,7 +297,7 @@ func (t *TagManager) LoadFolders() (map[string]*tagmanager.Folder, error) {
for _, value := range r.Folder { for _, value := range r.Folder {
res[value.Name] = value res[value.Name] = value
} }
t.folders = res t.folders = NewAccessedMap(res)
} }
return t.folders, nil return t.folders, nil
} }
@ -224,16 +308,16 @@ func (t *TagManager) LookupVariable(name string) (*tagmanager.Variable, error) {
return nil, err return nil, err
} }
if _, ok := elems[name]; !ok { if !elems.Has(name) {
return nil, ErrNotFound return nil, ErrNotFound
} }
return elems[name], nil return elems.Get(name), nil
} }
func (t *TagManager) LoadVariables() (map[string]*tagmanager.Variable, error) { func (t *TagManager) LoadVariables() (*AccessedMap[*tagmanager.Variable], error) {
if t.variables == nil { if t.variables == nil {
t.l.Info("🛄 Loading list", "type", "Variable") t.l.Info("└ ⬇︎ Loading list", "type", "Variable")
r, err := t.Service().Accounts.Containers.Workspaces.Variables.List(t.WorkspacePath()).Do() r, err := t.Service().Accounts.Containers.Workspaces.Variables.List(t.WorkspacePath()).Do()
if err != nil { if err != nil {
return nil, err return nil, err
@ -243,7 +327,7 @@ func (t *TagManager) LoadVariables() (map[string]*tagmanager.Variable, error) {
for _, value := range r.Variable { for _, value := range r.Variable {
res[value.Name] = value res[value.Name] = value
} }
t.variables = res t.variables = NewAccessedMap(res)
} }
return t.variables, nil return t.variables, nil
@ -255,16 +339,16 @@ func (t *TagManager) GetBuiltInVariable(typeName string) (*tagmanager.BuiltInVar
return nil, err return nil, err
} }
if _, ok := elems[typeName]; !ok { if !elems.Has(typeName) {
return nil, ErrNotFound return nil, ErrNotFound
} }
return elems[typeName], nil return elems.Get(typeName), nil
} }
func (t *TagManager) LoadBuiltInVariables() (map[string]*tagmanager.BuiltInVariable, error) { func (t *TagManager) LoadBuiltInVariables() (*AccessedMap[*tagmanager.BuiltInVariable], error) {
if t.builtInVariables == nil { if t.builtInVariables == nil {
t.l.Info("🛄 Loading list", "type", "BuiltInVariable") t.l.Info("└ ⬇︎ Loading list", "type", "BuiltInVariable")
r, err := t.Service().Accounts.Containers.Workspaces.BuiltInVariables.List(t.WorkspacePath()).Do() r, err := t.Service().Accounts.Containers.Workspaces.BuiltInVariables.List(t.WorkspacePath()).Do()
if err != nil { if err != nil {
return nil, err return nil, err
@ -274,7 +358,7 @@ func (t *TagManager) LoadBuiltInVariables() (map[string]*tagmanager.BuiltInVaria
for _, value := range r.BuiltInVariable { for _, value := range r.BuiltInVariable {
res[value.Type] = value res[value.Type] = value
} }
t.builtInVariables = res t.builtInVariables = NewAccessedMap(res)
} }
return t.builtInVariables, nil return t.builtInVariables, nil
@ -286,11 +370,11 @@ func (t *TagManager) LookupTrigger(name string) (*tagmanager.Trigger, error) {
return nil, err return nil, err
} }
if _, ok := elems[name]; !ok { if !elems.Has(name) {
return nil, ErrNotFound return nil, ErrNotFound
} }
return elems[name], nil return elems.Get(name), nil
} }
func (t *TagManager) LookupTemplate(name string) (*tagmanager.CustomTemplate, error) { func (t *TagManager) LookupTemplate(name string) (*tagmanager.CustomTemplate, error) {
@ -299,11 +383,11 @@ func (t *TagManager) LookupTemplate(name string) (*tagmanager.CustomTemplate, er
return nil, err return nil, err
} }
if _, ok := elems[name]; !ok { if !elems.Has(name) {
return nil, ErrNotFound return nil, ErrNotFound
} }
return elems[name], nil return elems.Get(name), nil
} }
func (t *TagManager) LookupTransformation(name string) (*tagmanager.Transformation, error) { func (t *TagManager) LookupTransformation(name string) (*tagmanager.Transformation, error) {
@ -312,16 +396,16 @@ func (t *TagManager) LookupTransformation(name string) (*tagmanager.Transformati
return nil, err return nil, err
} }
if _, ok := elems[name]; !ok { if !elems.Has(name) {
return nil, ErrNotFound return nil, ErrNotFound
} }
return elems[name], nil return elems.Get(name), nil
} }
func (t *TagManager) LoadTriggers() (map[string]*tagmanager.Trigger, error) { func (t *TagManager) LoadTriggers() (*AccessedMap[*tagmanager.Trigger], error) {
if t.triggers == nil { if t.triggers == nil {
t.l.Info("🛄 Loading list", "type", "Trigger") t.l.Info("└ ⬇︎ Loading list", "type", "Trigger")
r, err := t.Service().Accounts.Containers.Workspaces.Triggers.List(t.WorkspacePath()).Do() r, err := t.Service().Accounts.Containers.Workspaces.Triggers.List(t.WorkspacePath()).Do()
if err != nil { if err != nil {
return nil, err return nil, err
@ -331,7 +415,7 @@ func (t *TagManager) LoadTriggers() (map[string]*tagmanager.Trigger, error) {
for _, value := range r.Trigger { for _, value := range r.Trigger {
res[value.Name] = value res[value.Name] = value
} }
t.triggers = res t.triggers = NewAccessedMap(res)
} }
return t.triggers, nil return t.triggers, nil
@ -343,16 +427,16 @@ func (t *TagManager) LookupTag(name string) (*tagmanager.Tag, error) {
return nil, err return nil, err
} }
if _, ok := elems[name]; !ok { if !elems.Has(name) {
return nil, ErrNotFound return nil, ErrNotFound
} }
return elems[name], nil return elems.Get(name), nil
} }
func (t *TagManager) LoadTags() (map[string]*tagmanager.Tag, error) { func (t *TagManager) LoadTags() (*AccessedMap[*tagmanager.Tag], error) {
if t.tags == nil { if t.tags == nil {
t.l.Info("🛄 Loading list", "type", "Tag") t.l.Info("└ ⬇︎ Loading list", "type", "Tag")
r, err := t.Service().Accounts.Containers.Workspaces.Tags.List(t.WorkspacePath()).Do() r, err := t.Service().Accounts.Containers.Workspaces.Tags.List(t.WorkspacePath()).Do()
if err != nil { if err != nil {
return nil, err return nil, err
@ -362,7 +446,7 @@ func (t *TagManager) LoadTags() (map[string]*tagmanager.Tag, error) {
for _, value := range r.Tag { for _, value := range r.Tag {
res[value.Name] = value res[value.Name] = value
} }
t.tags = res t.tags = NewAccessedMap(res)
} }
return t.tags, nil return t.tags, nil
@ -374,16 +458,16 @@ func (t *TagManager) CustomTemplate(name string) (*tagmanager.CustomTemplate, er
return nil, err return nil, err
} }
if _, ok := elems[name]; !ok { if !elems.Has(name) {
return nil, ErrNotFound return nil, ErrNotFound
} }
return elems[name], nil return elems.Get(name), nil
} }
func (t *TagManager) LoadCustomTemplates() (map[string]*tagmanager.CustomTemplate, error) { func (t *TagManager) LoadCustomTemplates() (*AccessedMap[*tagmanager.CustomTemplate], error) {
if t.customTemplates == nil { if t.customTemplates == nil {
t.l.Info("🛄 Loading list", "type", "CustomTemplate") t.l.Info("└ ⬇︎ Loading list", "type", "CustomTemplate")
r, err := t.Service().Accounts.Containers.Workspaces.Templates.List(t.WorkspacePath()).Do() r, err := t.Service().Accounts.Containers.Workspaces.Templates.List(t.WorkspacePath()).Do()
if err != nil { if err != nil {
return nil, err return nil, err
@ -393,15 +477,15 @@ func (t *TagManager) LoadCustomTemplates() (map[string]*tagmanager.CustomTemplat
for _, value := range r.Template { for _, value := range r.Template {
res[value.Name] = value res[value.Name] = value
} }
t.customTemplates = res t.customTemplates = NewAccessedMap(res)
} }
return t.customTemplates, nil return t.customTemplates, nil
} }
func (t *TagManager) LoadTransformations() (map[string]*tagmanager.Transformation, error) { func (t *TagManager) LoadTransformations() (*AccessedMap[*tagmanager.Transformation], error) {
if t.transformations == nil { if t.transformations == nil {
t.l.Info("🛄 Loading list", "type", "Transformation") t.l.Info("└ ⬇︎ Loading list", "type", "Transformation")
r, err := t.Service().Accounts.Containers.Workspaces.Transformations.List(t.WorkspacePath()).Do() r, err := t.Service().Accounts.Containers.Workspaces.Transformations.List(t.WorkspacePath()).Do()
if err != nil { if err != nil {
return nil, err return nil, err
@ -411,7 +495,7 @@ func (t *TagManager) LoadTransformations() (map[string]*tagmanager.Transformatio
for _, value := range r.Transformation { for _, value := range r.Transformation {
res[value.Name] = value res[value.Name] = value
} }
t.transformations = res t.transformations = NewAccessedMap(res)
} }
return t.transformations, nil return t.transformations, nil
@ -436,14 +520,17 @@ func (t *TagManager) UpsertClient(item *tagmanager.Client) (*tagmanager.Client,
return nil, err return nil, err
} }
var value *tagmanager.Client
if cache == nil { if cache == nil {
l.Info("🚀 New") l.Info("└ 🚀 New")
t.clients[item.Name], err = t.Service().Accounts.Containers.Workspaces.Clients.Create(t.WorkspacePath(), item).Do() value, err = t.Service().Accounts.Containers.Workspaces.Clients.Create(t.WorkspacePath(), item).Do()
t.clients.Set(item.Name, value)
} else if item.Notes == cache.Notes { } else if item.Notes == cache.Notes {
l.Info(" OK", "id", cache.ClientId) l.Info("└ ✔︎ OK", "id", cache.ClientId)
} else { } else {
l.Info("🔄 Update", "id", cache.ClientId) l.Info("└ 🔄 Update", "id", cache.ClientId)
t.clients[item.Name], err = t.Service().Accounts.Containers.Workspaces.Clients.Update(t.WorkspacePath()+"/clients/"+cache.ClientId, item).Do() value, err = t.Service().Accounts.Containers.Workspaces.Clients.Update(t.WorkspacePath()+"/clients/"+cache.ClientId, item).Do()
t.clients.Set(item.Name, value)
} }
if err != nil { if err != nil {
if out, err := json.MarshalIndent(item, "", " "); err == nil { if out, err := json.MarshalIndent(item, "", " "); err == nil {
@ -474,14 +561,17 @@ func (t *TagManager) UpsertTransformation(item *tagmanager.Transformation) (*tag
return nil, err return nil, err
} }
var value *tagmanager.Transformation
if cache == nil { if cache == nil {
l.Info("🚀 New") l.Info("└ 🚀 New")
t.transformations[item.Name], err = t.Service().Accounts.Containers.Workspaces.Transformations.Create(t.WorkspacePath(), item).Do() value, err = t.Service().Accounts.Containers.Workspaces.Transformations.Create(t.WorkspacePath(), item).Do()
t.transformations.Set(item.Name, value)
} else if item.Notes == cache.Notes { } else if item.Notes == cache.Notes {
l.Info(" OK", "id", cache.TransformationId) l.Info("└ ✔︎ OK", "id", cache.TransformationId)
} else { } else {
l.Info("🔄 Update", "id", cache.TransformationId) l.Info("└ 🔄 Update", "id", cache.TransformationId)
t.transformations[item.Name], err = t.Service().Accounts.Containers.Workspaces.Transformations.Update(t.WorkspacePath()+"/transformations/"+cache.TransformationId, item).Do() value, err = t.Service().Accounts.Containers.Workspaces.Transformations.Update(t.WorkspacePath()+"/transformations/"+cache.TransformationId, item).Do()
t.transformations.Set(item.Name, value)
} }
if err != nil { if err != nil {
if out, err := json.MarshalIndent(item, "", " "); err == nil { if out, err := json.MarshalIndent(item, "", " "); err == nil {
@ -509,14 +599,17 @@ func (t *TagManager) UpsertFolder(name string) (*tagmanager.Folder, error) {
return nil, err return nil, err
} }
var value *tagmanager.Folder
if cache == nil { if cache == nil {
l.Info("🚀 New") l.Info("└ 🚀 New")
t.folders[name], err = t.Service().Accounts.Containers.Workspaces.Folders.Create(t.WorkspacePath(), item).Do() value, err = t.Service().Accounts.Containers.Workspaces.Folders.Create(t.WorkspacePath(), item).Do()
t.folders.Set(item.Name, value)
} else if item.Notes == cache.Notes { } else if item.Notes == cache.Notes {
l.Info(" OK", "id", item.FolderId) l.Info("└ ✔︎ OK", "id", item.FolderId)
} else { } else {
l.Info("🔄 Update", "id", cache.FolderId) l.Info("└ 🔄 Update", "id", cache.FolderId)
t.folders[name], err = t.Service().Accounts.Containers.Workspaces.Folders.Update(t.WorkspacePath()+"/folders/"+cache.FolderId, item).Do() value, err = t.Service().Accounts.Containers.Workspaces.Folders.Update(t.WorkspacePath()+"/folders/"+cache.FolderId, item).Do()
t.folders.Set(item.Name, value)
} }
if err != nil { if err != nil {
if out, err := json.MarshalIndent(item, "", " "); err == nil { if out, err := json.MarshalIndent(item, "", " "); err == nil {
@ -547,14 +640,17 @@ func (t *TagManager) UpsertVariable(item *tagmanager.Variable) (*tagmanager.Vari
return nil, err return nil, err
} }
var value *tagmanager.Variable
if cache == nil { if cache == nil {
l.Info("🚀 New") l.Info("└ 🚀 New")
t.variables[item.Name], err = t.Service().Accounts.Containers.Workspaces.Variables.Create(t.WorkspacePath(), item).Do() value, err = t.Service().Accounts.Containers.Workspaces.Variables.Create(t.WorkspacePath(), item).Do()
t.variables.Set(item.Name, value)
} else if item.Notes == cache.Notes { } else if item.Notes == cache.Notes {
l.Info(" OK", "id", cache.VariableId) l.Info("└ ✔︎ OK", "id", cache.VariableId)
} else { } else {
l.Info("🔄 Update", "id", cache.VariableId) l.Info("└ 🔄 Update", "id", cache.VariableId)
t.variables[item.Name], err = t.Service().Accounts.Containers.Workspaces.Variables.Update(t.WorkspacePath()+"/variables/"+cache.VariableId, item).Do() value, err = t.Service().Accounts.Containers.Workspaces.Variables.Update(t.WorkspacePath()+"/variables/"+cache.VariableId, item).Do()
t.variables.Set(item.Name, value)
} }
if err != nil { if err != nil {
if out, err := json.MarshalIndent(item, "", " "); err == nil { if out, err := json.MarshalIndent(item, "", " "); err == nil {
@ -575,18 +671,18 @@ func (t *TagManager) EnableBuiltInVariable(typeName string) (*tagmanager.BuiltIn
} }
if cache != nil { if cache != nil {
l.Info(" OK") l.Info("└ ✔︎ OK")
return cache, nil return cache, nil
} }
l.Info("🚀 New") l.Info("🚀 New")
resp, err := t.Service().Accounts.Containers.Workspaces.BuiltInVariables.Create(t.WorkspacePath()).Type(typeName).Do() resp, err := t.Service().Accounts.Containers.Workspaces.BuiltInVariables.Create(t.WorkspacePath()).Type(typeName).Do()
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to create built-in variable") return nil, errors.Wrap(err, "failed to create built-in variable")
} }
for _, builtInVariable := range resp.BuiltInVariable { for _, builtInVariable := range resp.BuiltInVariable {
t.builtInVariables[builtInVariable.Type] = builtInVariable t.builtInVariables.Set(builtInVariable.Type, builtInVariable)
} }
return t.GetBuiltInVariable(typeName) return t.GetBuiltInVariable(typeName)
@ -611,14 +707,17 @@ func (t *TagManager) UpsertTrigger(item *tagmanager.Trigger) (*tagmanager.Trigge
return nil, err return nil, err
} }
var value *tagmanager.Trigger
if cache == nil { if cache == nil {
l.Info("🚀 New") l.Info("└ 🚀 New")
t.triggers[item.Name], err = t.Service().Accounts.Containers.Workspaces.Triggers.Create(t.WorkspacePath(), item).Do() value, err = t.Service().Accounts.Containers.Workspaces.Triggers.Create(t.WorkspacePath(), item).Do()
t.triggers.Set(item.Name, value)
} else if item.Notes == cache.Notes { } else if item.Notes == cache.Notes {
l.Info(" OK", "id", cache.TriggerId) l.Info("└ ✔︎ OK", "id", cache.TriggerId)
} else { } else {
l.Info("🔄 Update", "id", cache.TriggerId) l.Info("└ 🔄 Update", "id", cache.TriggerId)
t.triggers[item.Name], err = t.Service().Accounts.Containers.Workspaces.Triggers.Update(t.WorkspacePath()+"/triggers/"+cache.TriggerId, item).Do() value, err = t.Service().Accounts.Containers.Workspaces.Triggers.Update(t.WorkspacePath()+"/triggers/"+cache.TriggerId, item).Do()
t.triggers.Set(item.Name, value)
} }
if err != nil { if err != nil {
if out, err := json.MarshalIndent(item, "", " "); err == nil { if out, err := json.MarshalIndent(item, "", " "); err == nil {
@ -649,14 +748,17 @@ func (t *TagManager) UpsertTag(item *tagmanager.Tag) (*tagmanager.Tag, error) {
return nil, err return nil, err
} }
var value *tagmanager.Tag
if cache == nil { if cache == nil {
l.Info("🚀 New") l.Info("└ 🚀 New")
t.tags[item.Name], err = t.Service().Accounts.Containers.Workspaces.Tags.Create(t.WorkspacePath(), item).Do() value, err = t.Service().Accounts.Containers.Workspaces.Tags.Create(t.WorkspacePath(), item).Do()
t.tags.Set(item.Name, value)
} else if item.Notes == cache.Notes { } else if item.Notes == cache.Notes {
l.Info(" OK", "id", cache.TagId) l.Info("└ ✔︎ OK", "id", cache.TagId)
} else { } else {
l.Info("🔄 Update", "id", cache.TagId) l.Info("└ 🔄 Update", "id", cache.TagId)
t.tags[item.Name], err = t.Service().Accounts.Containers.Workspaces.Tags.Update(t.WorkspacePath()+"/tags/"+cache.TagId, item).Do() value, err = t.Service().Accounts.Containers.Workspaces.Tags.Update(t.WorkspacePath()+"/tags/"+cache.TagId, item).Do()
t.tags.Set(item.Name, value)
} }
if err != nil { if err != nil {
if out, err := json.MarshalIndent(item, "", " "); err == nil { if out, err := json.MarshalIndent(item, "", " "); err == nil {
@ -679,14 +781,17 @@ func (t *TagManager) UpsertCustomTemplate(item *tagmanager.CustomTemplate) (*tag
return nil, err return nil, err
} }
var value *tagmanager.CustomTemplate
if cache == nil { if cache == nil {
l.Info("🚀 New") l.Info("└ 🚀 New")
t.customTemplates[item.Name], err = t.Service().Accounts.Containers.Workspaces.Templates.Create(t.WorkspacePath(), item).Do() value, err = t.Service().Accounts.Containers.Workspaces.Templates.Create(t.WorkspacePath(), item).Do()
t.customTemplates.Set(item.Name, value)
} else if item.TemplateData == cache.TemplateData { } else if item.TemplateData == cache.TemplateData {
l.Info(" OK", "id", cache.TemplateId) l.Info("└ ✔︎ OK", "id", cache.TemplateId)
} else { } else {
l.Info("🔄 Update", "id", cache.TemplateId) l.Info("└ 🔄 Update", "id", cache.TemplateId)
t.customTemplates[item.Name], err = t.Service().Accounts.Containers.Workspaces.Templates.Update(t.WorkspacePath()+"/templates/"+cache.TemplateId, item).Do() value, err = t.Service().Accounts.Containers.Workspaces.Templates.Update(t.WorkspacePath()+"/templates/"+cache.TemplateId, item).Do()
t.customTemplates.Set(item.Name, value)
} }
if err != nil { if err != nil {
if out, err := json.MarshalIndent(item, "", " "); err == nil { if out, err := json.MarshalIndent(item, "", " "); err == nil {

View File

@ -5,6 +5,7 @@ import (
"go/ast" "go/ast"
"go/types" "go/types"
"log/slog" "log/slog"
"maps"
"slices" "slices"
"strings" "strings"
@ -14,7 +15,6 @@ import (
"github.com/foomo/sesamy-cli/pkg/code" "github.com/foomo/sesamy-cli/pkg/code"
"github.com/foomo/sesamy-cli/pkg/typescript" "github.com/foomo/sesamy-cli/pkg/typescript"
"github.com/stoewer/go-strcase" "github.com/stoewer/go-strcase"
"golang.org/x/exp/maps"
) )
const FallbackType = "any" const FallbackType = "any"
@ -119,7 +119,7 @@ func (p *Package) renderTypeBasic(typeName string, typeObject types.Object) {
} }
} }
names := maps.Keys(res) names := slices.AppendSeq(make([]string, 0, len(res)), maps.Keys(res))
slices.Sort(names) slices.Sort(names)
typeValues := &strings.Builder{} typeValues := &strings.Builder{}

View File

@ -2,10 +2,9 @@ package typescript
import ( import (
"fmt" "fmt"
"maps"
"slices" "slices"
"strings" "strings"
"golang.org/x/exp/maps"
) )
type Import struct { type Import struct {
@ -46,7 +45,7 @@ func (i *Import) SetDefault(v string) {
} }
func (i *Import) Modules() []string { func (i *Import) Modules() []string {
ret := maps.Keys(i.modules) ret := slices.AppendSeq(make([]string, 0, len(i.modules)), maps.Keys(i.modules))
slices.Sort(ret) slices.Sort(ret)
return ret return ret
} }

View File

@ -21,7 +21,7 @@ func LoadEventParams(cfg contemplate.Config) (map[string]map[string]string, erro
for _, typ := range cfgPkg.Types { for _, typ := range cfgPkg.Types {
eventParams, err := getEventParams(pkg.LookupScopeType(typ)) eventParams, err := getEventParams(pkg.LookupScopeType(typ))
if err != nil { if err != nil {
return nil, err return nil, errors.Wrap(err, "failed to load event params: "+typ)
} }
ret[strcase.SnakeCase(typ)] = eventParams ret[strcase.SnakeCase(typ)] = eventParams
} }
@ -32,6 +32,15 @@ func LoadEventParams(cfg contemplate.Config) (map[string]map[string]string, erro
func getEventParams(obj types.Object) (map[string]string, error) { func getEventParams(obj types.Object) (map[string]string, error) {
ret := map[string]string{} ret := map[string]string{}
if obj == nil {
return nil, errors.New("obj is nil")
}
if obj.Type() == nil {
return nil, errors.New("object is not a type: " + obj.String())
}
if obj.Type().Underlying() == nil {
return nil, errors.New("underlying object is not a type: " + obj.Type().String())
}
if eventStruct := assume.T[*types.Struct](obj.Type().Underlying()); eventStruct != nil { if eventStruct := assume.T[*types.Struct](obj.Type().Underlying()); eventStruct != nil {
for i := range eventStruct.NumFields() { for i := range eventStruct.NumFields() {
if eventField := eventStruct.Field(i); eventField.Name() == "Params" { if eventField := eventStruct.Field(i); eventField.Name() == "Params" {

View File

@ -436,14 +436,20 @@
"github.com.foomo.sesamy-cli.pkg.config.GoogleTagManager": { "github.com.foomo.sesamy-cli.pkg.config.GoogleTagManager": {
"properties": { "properties": {
"accountId": { "accountId": {
"type": "string" "type": "string",
"description": "Google Tag Manager account id"
}, },
"webContainer": { "webContainer": {
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.GoogleTagManagerContainer" "$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.GoogleTagManagerContainer",
"description": "Google Tag Manager web container settings"
}, },
"serverContainer": { "serverContainer": {
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.GoogleTagManagerContainer", "$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.GoogleTagManagerContainer",
"description": "Google Tag Manager server container settings" "description": "Google Tag Manager server container settings"
},
"serverContainerVariables": {
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.GoogleTagManagerServerContainerVariables",
"description": "Google Tag Manager server container variables"
} }
}, },
"additionalProperties": false, "additionalProperties": false,
@ -452,13 +458,30 @@
"github.com.foomo.sesamy-cli.pkg.config.GoogleTagManagerContainer": { "github.com.foomo.sesamy-cli.pkg.config.GoogleTagManagerContainer": {
"properties": { "properties": {
"tagId": { "tagId": {
"type": "string" "type": "string",
"description": "The container tag id"
}, },
"containerId": { "containerId": {
"type": "string" "type": "string",
"description": "The container id"
}, },
"workspaceId": { "workspaceId": {
"type": "string" "type": "string",
"description": "The workspace id that should be used by the api"
}
},
"additionalProperties": false,
"type": "object"
},
"github.com.foomo.sesamy-cli.pkg.config.GoogleTagManagerServerContainerVariables": {
"properties": {
"eventData": {
"$ref": "#/$defs/[]string",
"description": "List of event data variables"
},
"lookupTables": {
"$ref": "#/$defs/map[string]config.LookupTable",
"description": "Map of lookup table variables"
} }
}, },
"additionalProperties": false, "additionalProperties": false,
@ -477,6 +500,24 @@
"additionalProperties": false, "additionalProperties": false,
"type": "object" "type": "object"
}, },
"github.com.foomo.sesamy-cli.pkg.config.LookupTable": {
"properties": {
"input": {
"type": "string",
"description": "Input source"
},
"keyTable": {
"$ref": "#/$defs/map[string]string",
"description": "Key value data map"
},
"valueTable": {
"$ref": "#/$defs/map[string]string",
"description": "Vaule key data map"
}
},
"additionalProperties": false,
"type": "object"
},
"github.com.foomo.sesamy-cli.pkg.config.MicrosoftAds": { "github.com.foomo.sesamy-cli.pkg.config.MicrosoftAds": {
"properties": { "properties": {
"enabled": { "enabled": {
@ -618,6 +659,12 @@
}, },
"type": "object" "type": "object"
}, },
"map[string]config.LookupTable": {
"additionalProperties": {
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.LookupTable"
},
"type": "object"
},
"map[string]config.MicrosoftAdsConversionTag": { "map[string]config.MicrosoftAdsConversionTag": {
"additionalProperties": { "additionalProperties": {
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.MicrosoftAdsConversionTag" "$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.MicrosoftAdsConversionTag"

View File

@ -1,7 +1,6 @@
package tagmanager_test package tagmanager_test
import ( import (
"context"
"fmt" "fmt"
"log/slog" "log/slog"
"os" "os"
@ -23,7 +22,7 @@ func TestNewClient_Server(t *testing.T) {
require.NoError(t, godotenv.Load("../../.env")) require.NoError(t, godotenv.Load("../../.env"))
c, err := tagmanager.New( c, err := tagmanager.New(
context.TODO(), t.Context(),
slog.New(slog.NewTextHandler(os.Stdout, nil)), slog.New(slog.NewTextHandler(os.Stdout, nil)),
os.Getenv("TEST_ACCOUNT_ID"), os.Getenv("TEST_ACCOUNT_ID"),
config.GoogleTagManagerContainer{ config.GoogleTagManagerContainer{

View File

@ -1,7 +1,6 @@
package tagmanager_test package tagmanager_test
import ( import (
"context"
"fmt" "fmt"
"log/slog" "log/slog"
"os" "os"
@ -24,7 +23,7 @@ func TestNewClient_Web(t *testing.T) {
require.NoError(t, godotenv.Load("../../.env")) require.NoError(t, godotenv.Load("../../.env"))
c, err := tagmanager.New( c, err := tagmanager.New(
context.TODO(), t.Context(),
slog.New(slog.NewTextHandler(os.Stdout, nil)), slog.New(slog.NewTextHandler(os.Stdout, nil)),
os.Getenv("TEST_ACCOUNT_ID"), os.Getenv("TEST_ACCOUNT_ID"),
config.GoogleTagManagerContainer{ config.GoogleTagManagerContainer{