mirror of
https://github.com/foomo/sesamy-cli.git
synced 2025-10-16 12:35:36 +00:00
commit
ec29972261
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@ -8,7 +8,7 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
|
||||
@ -74,9 +74,11 @@ linters-settings:
|
||||
- name: struct-tag
|
||||
arguments:
|
||||
- "json,inline"
|
||||
- "yaml,squash"
|
||||
- name: unhandled-error
|
||||
arguments:
|
||||
- "fmt.Println"
|
||||
- "strings.Builder.WriteString"
|
||||
# TODO remove
|
||||
- name: deep-exit
|
||||
disabled: true
|
||||
|
||||
11
Makefile
11
Makefile
@ -27,12 +27,7 @@ doc:
|
||||
.PHONY: test
|
||||
## Run tests
|
||||
test:
|
||||
@go test -coverprofile=coverage.out -race -json ./... | gotestfmt
|
||||
|
||||
.PHONY: test.update
|
||||
## Run tests and update snapshots
|
||||
test.update:
|
||||
@go test -update -coverprofile=coverage.out -race -json ./... | gotestfmt
|
||||
@GO_TEST_TAGS=-skip go test -coverprofile=coverage.out -race -json ./... | gotestfmt
|
||||
|
||||
.PHONY: lint
|
||||
## Run linter
|
||||
@ -58,6 +53,10 @@ outdated:
|
||||
install:
|
||||
@go build -o ${GOPATH}/bin/sesamy main.go
|
||||
|
||||
## Install debug binary
|
||||
install.debug:
|
||||
@go build -gcflags "all=-N -l" -o ${GOPATH}/bin/sesamy main.go
|
||||
|
||||
## Build binary
|
||||
build:
|
||||
@mkdir -p bin
|
||||
|
||||
98
README.md
98
README.md
@ -31,23 +31,99 @@ google:
|
||||
|
||||
gtm:
|
||||
account_id: 6099238525
|
||||
server:
|
||||
container_id: 175348980
|
||||
workspace_id: 10
|
||||
measurement_id: GTM-5NWPR4QW
|
||||
|
||||
web:
|
||||
container_id: 175355532
|
||||
workspace_id: 23
|
||||
measurement_id: GTM-57BHX34G
|
||||
workspace_id: 23
|
||||
server:
|
||||
container_id: 175348980
|
||||
measurement_id: GTM-5NWPR4QW
|
||||
workspace_id: 10
|
||||
|
||||
credentials_file: ./tmp/google_service_account_creds.json
|
||||
request_quota: 15
|
||||
credentials_file: ./google_service_account_creds.json
|
||||
|
||||
events:
|
||||
|
||||
typescript:
|
||||
packages:
|
||||
- path: "github.com/foomo/sesamy-cli/_example/server"
|
||||
output_path: "./_example/client/types.d.ts"
|
||||
indent: "\t"
|
||||
- path: 'github.com/username/repository/event'
|
||||
types:
|
||||
- Custom
|
||||
- path: 'github.com/foomo/sesamy-go/event'
|
||||
types:
|
||||
- PageView
|
||||
- SelectItem
|
||||
output_path: '/path/to/src'
|
||||
|
||||
tagmanager:
|
||||
tags:
|
||||
ga4_enabled: true
|
||||
packages:
|
||||
- path: 'github.com/username/repository/event'
|
||||
types:
|
||||
- Custom
|
||||
- path: 'github.com/foomo/sesamy-go/event'
|
||||
types:
|
||||
- AddPaymentInfo
|
||||
- AddShippingInfo
|
||||
- AddToCart
|
||||
- AddToWishlist
|
||||
- AdImpression
|
||||
- BeginCheckout
|
||||
- CampaignDetails
|
||||
- Click
|
||||
- EarnVirtualMoney
|
||||
- FileDownload
|
||||
- FormStart
|
||||
- FormSubmit
|
||||
- GenerateLead
|
||||
- JoinGroup
|
||||
- LevelEnd
|
||||
- LevelStart
|
||||
- LevelUp
|
||||
- Login
|
||||
- PageView
|
||||
- PostScore
|
||||
- Purchase
|
||||
- Refund
|
||||
- RemoveFromCart
|
||||
- ScreenView
|
||||
- Scroll
|
||||
- Search
|
||||
- SelectContent
|
||||
- SelectItem
|
||||
- SelectPromotion
|
||||
- SessionStart
|
||||
- Share
|
||||
- SignUp
|
||||
- SpendVirtualCurrency
|
||||
- TutorialBegin
|
||||
- TutorialComplete
|
||||
- UnlockAchievement
|
||||
- UserEngagement
|
||||
- VideoComplete
|
||||
- VideoProgress
|
||||
- VideoStart
|
||||
- ViewCart
|
||||
- ViewItem
|
||||
- ViewItemList
|
||||
- ViewPromotion
|
||||
- ViewSearchResults
|
||||
prefixes:
|
||||
client: ''
|
||||
folder: ''
|
||||
tags:
|
||||
ga4_event: 'GA4 - '
|
||||
google_tag: ''
|
||||
server_ga4_event: 'GA4 - '
|
||||
triggers:
|
||||
client: ''
|
||||
custom_event: 'Event - '
|
||||
variables:
|
||||
constant: ''
|
||||
event_model: 'dlv.eventModel.'
|
||||
gt_event_settings: 'Event Settings - '
|
||||
gt_settings: 'Settings - '
|
||||
```
|
||||
|
||||
## Caveats
|
||||
|
||||
55
_example/client/types.d.ts
vendored
55
_example/client/types.d.ts
vendored
@ -1,55 +0,0 @@
|
||||
// Code generated by tygo. DO NOT EDIT.
|
||||
|
||||
//////////
|
||||
// source: addtocart.go
|
||||
|
||||
export interface AddToCart {
|
||||
currency?: string;
|
||||
value?: number /* float64 */;
|
||||
items?: (Item | undefined)[];
|
||||
}
|
||||
|
||||
//////////
|
||||
// source: item.go
|
||||
|
||||
export interface Item {
|
||||
affiliation?: string;
|
||||
coupon?: string;
|
||||
discount?: number /* float64 */;
|
||||
index?: number /* int */;
|
||||
item_brand?: string;
|
||||
item_category?: string;
|
||||
item_category2?: string;
|
||||
item_category3?: string;
|
||||
item_category4?: string;
|
||||
item_category5?: string;
|
||||
item_id?: string;
|
||||
item_list_name?: string;
|
||||
item_name?: string;
|
||||
item_variant?: string;
|
||||
item_list_id?: string;
|
||||
location_id?: string;
|
||||
price?: string;
|
||||
quantity?: number /* float64 */;
|
||||
}
|
||||
|
||||
//////////
|
||||
// source: login.go
|
||||
|
||||
export interface Login {
|
||||
method?: string;
|
||||
}
|
||||
|
||||
//////////
|
||||
// source: search.go
|
||||
|
||||
export interface Search {
|
||||
search_term?: string;
|
||||
}
|
||||
|
||||
//////////
|
||||
// source: signup.go
|
||||
|
||||
export interface SignUp {
|
||||
method?: string;
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
package server
|
||||
|
||||
type AddToCart struct {
|
||||
Currency string `json:"currency,omitempty"`
|
||||
Value float64 `json:"value,omitempty"`
|
||||
Items []*Item `json:"items,omitempty"`
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
package server
|
||||
|
||||
type Item struct {
|
||||
Affiliation string `json:"affiliation,omitempty"`
|
||||
Coupon string `json:"coupon,omitempty"`
|
||||
Discount float64 `json:"discount,omitempty"`
|
||||
Index int `json:"index,omitempty"`
|
||||
ItemBrand string `json:"item_brand,omitempty"`
|
||||
ItemCategory string `json:"item_category,omitempty"`
|
||||
ItemCategory2 string `json:"item_category2,omitempty"`
|
||||
ItemCategory3 string `json:"item_category3,omitempty"`
|
||||
ItemCategory4 string `json:"item_category4,omitempty"`
|
||||
ItemCategory5 string `json:"item_category5,omitempty"`
|
||||
ItemID string `json:"item_id,omitempty"`
|
||||
ItemListName string `json:"item_list_name,omitempty"`
|
||||
ItemName string `json:"item_name,omitempty"`
|
||||
ItemVariant string `json:"item_variant,omitempty"`
|
||||
ItemListID string `json:"item_list_id,omitempty"`
|
||||
LocationID string `json:"location_id,omitempty"`
|
||||
Price string `json:"price,omitempty"`
|
||||
Quantity float64 `json:"quantity,omitempty"`
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
package server
|
||||
|
||||
type Login struct {
|
||||
Method string `json:"method,omitempty"`
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
package server
|
||||
|
||||
type Search struct {
|
||||
SearchTerm string `json:"search_term,omitempty"`
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
package server
|
||||
|
||||
type SignUp struct {
|
||||
Method string `json:"method,omitempty"`
|
||||
}
|
||||
@ -4,22 +4,20 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// tagmanagerCmd represents the tagmanager command
|
||||
var tagmanagerCmd = &cobra.Command{
|
||||
Use: "tagmanager",
|
||||
Short: "Provision Google Tag Manager containers",
|
||||
// NewTagmanagerCmd represents the tagmanager command
|
||||
func NewTagmanagerCmd(root *cobra.Command) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "tagmanager",
|
||||
Short: "Provision Google Tag Manager containers",
|
||||
}
|
||||
|
||||
root.AddCommand(cmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(tagmanagerCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// tagmanagerCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
// tagmanagerCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
cmd := NewTagmanagerCmd(rootCmd)
|
||||
NewTagManagerServerCmd(cmd)
|
||||
NewTagmanagerWebCmd(cmd)
|
||||
}
|
||||
|
||||
@ -11,110 +11,116 @@ import (
|
||||
tagmanager2 "google.golang.org/api/tagmanager/v2"
|
||||
)
|
||||
|
||||
// tagmanagerServerCmd represents the server command
|
||||
// NewTagManagerServerCmd represents the server command
|
||||
// TODO flags workspace, dry, diff
|
||||
// TODO google user auth
|
||||
var tagmanagerServerCmd = &cobra.Command{
|
||||
Use: "server",
|
||||
Short: "Provision Google Tag Manager Server Container",
|
||||
PersistentPreRunE: preRunReadConfig,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var clientCredentialsOption option.ClientOption
|
||||
if cfg.Google.CredentialsFile != "" {
|
||||
clientCredentialsOption = option.WithCredentialsFile(cfg.Google.CredentialsFile)
|
||||
} else {
|
||||
clientCredentialsOption = option.WithCredentialsJSON([]byte(cfg.Google.CredentialsJSON))
|
||||
}
|
||||
func NewTagManagerServerCmd(root *cobra.Command) {
|
||||
var cmd = &cobra.Command{
|
||||
Use: "server",
|
||||
Short: "Provision Google Tag Manager Server Container",
|
||||
PersistentPreRunE: preRunReadConfig,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var clientCredentialsOption option.ClientOption
|
||||
if cfg.Google.CredentialsFile != "" {
|
||||
clientCredentialsOption = option.WithCredentialsFile(cfg.Google.CredentialsFile)
|
||||
} else {
|
||||
clientCredentialsOption = option.WithCredentialsJSON([]byte(cfg.Google.CredentialsJSON))
|
||||
}
|
||||
|
||||
c, err := tagmanager.NewClient(
|
||||
cmd.Context(),
|
||||
logger,
|
||||
cfg.Google.GTM.AccountID,
|
||||
cfg.Google.GTM.Server.ContainerID,
|
||||
cfg.Google.GTM.Server.WorkspaceID,
|
||||
cfg.Google.GA4.MeasurementID,
|
||||
tagmanager.ClientWithRequestQuota(cfg.Google.RequestQuota),
|
||||
tagmanager.ClientWithClientOptions(clientCredentialsOption),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p := cfg.Tagmanager.Prefixes
|
||||
|
||||
{
|
||||
name := p.FolderName(c.FolderName())
|
||||
if _, err := c.UpsertFolder(name); err != nil {
|
||||
c, err := tagmanager.NewClient(
|
||||
cmd.Context(),
|
||||
logger,
|
||||
cfg.Google.GTM.AccountID,
|
||||
cfg.Google.GTM.Server.ContainerID,
|
||||
cfg.Google.GTM.Server.WorkspaceID,
|
||||
cfg.Google.GA4.MeasurementID,
|
||||
tagmanager.ClientWithRequestQuota(cfg.Google.RequestQuota),
|
||||
tagmanager.ClientWithClientOptions(clientCredentialsOption),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if _, err := c.EnableBuiltInVariable("clientName"); err != nil {
|
||||
return err
|
||||
p := cfg.Tagmanager.Prefixes
|
||||
|
||||
{
|
||||
name := p.FolderName(c.FolderName())
|
||||
if _, err := c.UpsertFolder(name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var ga4MeasurementID *tagmanager2.Variable
|
||||
{
|
||||
name := p.Variables.ConstantName("Google Analytics GA4 ID")
|
||||
if ga4MeasurementID, err = c.UpsertVariable(variable.NewConstant(name, c.MeasurementID())); err != nil {
|
||||
return err
|
||||
{
|
||||
if _, err := c.EnableBuiltInVariable("clientName"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var webContainerMeasurementID *tagmanager2.Variable
|
||||
{
|
||||
name := p.Variables.ConstantName("Google Tag Mangager Web Container ID")
|
||||
if webContainerMeasurementID, err = c.UpsertVariable(variable.NewConstant(name, cfg.Google.GTM.Web.MeasurementID)); err != nil {
|
||||
return err
|
||||
var ga4MeasurementID *tagmanager2.Variable
|
||||
{
|
||||
name := p.Variables.ConstantName("Google Analytics GA4 ID")
|
||||
if ga4MeasurementID, err = c.UpsertVariable(variable.NewConstant(name, c.MeasurementID())); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
name := p.ClientName("Google Tag Manager Web Container")
|
||||
if _, err := c.UpsertClient(client.NewGTM(name, webContainerMeasurementID)); err != nil {
|
||||
return err
|
||||
var webContainerMeasurementID *tagmanager2.Variable
|
||||
{
|
||||
name := p.Variables.ConstantName("Google Tag Mangager Web Container ID")
|
||||
if webContainerMeasurementID, err = c.UpsertVariable(variable.NewConstant(name, cfg.Google.GTM.Web.MeasurementID)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var ga4Client *tagmanager2.Client
|
||||
{
|
||||
name := p.ClientName("Google Analytics GA4")
|
||||
if ga4Client, err = c.UpsertClient(client.NewGA4(name)); err != nil {
|
||||
return err
|
||||
{
|
||||
name := p.ClientName("Google Tag Manager Web Container")
|
||||
if _, err := c.UpsertClient(client.NewGTM(name, webContainerMeasurementID)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var ga4ClientTrigger *tagmanager2.Trigger
|
||||
{
|
||||
name := p.Triggers.ClientName("Google Analytics GA4 Client")
|
||||
if ga4ClientTrigger, err = c.UpsertTrigger(trigger.NewClient(name, ga4Client)); err != nil {
|
||||
return err
|
||||
var mpv2Client *tagmanager2.Client
|
||||
{
|
||||
name := p.ClientName("Measurement Protocol GA4")
|
||||
if mpv2Client, err = c.UpsertClient(client.NewMPv2(name)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
name := p.Tags.ServerGA4EventName("Google Analytics GA4")
|
||||
if _, err := c.UpsertTag(client2.NewServerGA4Event(name, ga4MeasurementID, ga4ClientTrigger)); err != nil {
|
||||
return err
|
||||
var mpv2ClientTrigger *tagmanager2.Trigger
|
||||
{
|
||||
name := p.Triggers.ClientName("Measurement Protocol GA4 Client")
|
||||
if mpv2ClientTrigger, err = c.UpsertTrigger(trigger.NewClient(name, mpv2Client)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
tagmanagerCmd.AddCommand(tagmanagerServerCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// tagmanagerServerCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
// tagmanagerServerCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
var ga4Client *tagmanager2.Client
|
||||
{
|
||||
name := p.ClientName("Google Analytics GA4")
|
||||
if ga4Client, err = c.UpsertClient(client.NewGA4(name)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var ga4ClientTrigger *tagmanager2.Trigger
|
||||
{
|
||||
name := p.Triggers.ClientName("Google Analytics GA4 Client")
|
||||
if ga4ClientTrigger, err = c.UpsertTrigger(trigger.NewClient(name, ga4Client)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
name := p.Tags.ServerGA4EventName("Google Analytics GA4")
|
||||
if _, err := c.UpsertTag(client2.NewServerGA4Event(name, ga4MeasurementID, ga4ClientTrigger, mpv2ClientTrigger)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
root.AddCommand(cmd)
|
||||
}
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"strconv"
|
||||
|
||||
"github.com/foomo/sesamy-cli/internal"
|
||||
"github.com/foomo/gocontemplate/pkg/assume"
|
||||
"github.com/foomo/gocontemplate/pkg/contemplate"
|
||||
"github.com/foomo/sesamy-cli/pkg/tagmanager"
|
||||
client "github.com/foomo/sesamy-cli/pkg/tagmanager/tag"
|
||||
trigger2 "github.com/foomo/sesamy-cli/pkg/tagmanager/trigger"
|
||||
@ -13,130 +15,143 @@ import (
|
||||
tagmanager2 "google.golang.org/api/tagmanager/v2"
|
||||
)
|
||||
|
||||
// tagmanagerWebCmd represents the web command
|
||||
var tagmanagerWebCmd = &cobra.Command{
|
||||
Use: "web",
|
||||
Short: "Provision Google Tag Manager Web Container",
|
||||
PersistentPreRunE: preRunReadConfig,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var clientCredentialsOption option.ClientOption
|
||||
if cfg.Google.CredentialsFile != "" {
|
||||
clientCredentialsOption = option.WithCredentialsFile(cfg.Google.CredentialsFile)
|
||||
} else {
|
||||
clientCredentialsOption = option.WithCredentialsJSON([]byte(cfg.Google.CredentialsJSON))
|
||||
}
|
||||
|
||||
eventParameters, err := internal.GetEventParameters(cfg.Tagmanager)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c, err := tagmanager.NewClient(
|
||||
cmd.Context(),
|
||||
logger,
|
||||
cfg.Google.GTM.AccountID,
|
||||
cfg.Google.GTM.Web.ContainerID,
|
||||
cfg.Google.GTM.Web.WorkspaceID,
|
||||
cfg.Google.GA4.MeasurementID,
|
||||
tagmanager.ClientWithRequestQuota(cfg.Google.RequestQuota),
|
||||
tagmanager.ClientWithClientOptions(clientCredentialsOption),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p := cfg.Tagmanager.Prefixes
|
||||
|
||||
if _, err := c.UpsertFolder(p.FolderName(c.FolderName())); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var ga4MeasurementID *tagmanager2.Variable
|
||||
{
|
||||
name := p.Variables.ConstantName("Google Analytics GA4 ID")
|
||||
if ga4MeasurementID, err = c.UpsertVariable(variable.NewConstant(name, c.MeasurementID())); err != nil {
|
||||
return err
|
||||
// NewTagmanagerWebCmd represents the web command
|
||||
func NewTagmanagerWebCmd(root *cobra.Command) {
|
||||
getEventParams := func(obj types.Object) []string {
|
||||
var ret []string
|
||||
if eventStruct := assume.T[*types.Struct](obj.Type().Underlying()); eventStruct != nil {
|
||||
for i := range eventStruct.NumFields() {
|
||||
if eventField := eventStruct.Field(i); eventField.Name() == "Params" {
|
||||
if paramsStruct := assume.T[*types.Struct](eventField.Type().Underlying()); paramsStruct != nil {
|
||||
for j := range paramsStruct.NumFields() {
|
||||
ret = append(ret, paramsStruct.Field(j).Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
var serverContainerURL *tagmanager2.Variable
|
||||
{
|
||||
name := p.Variables.ConstantName("Server Container URL")
|
||||
if serverContainerURL, err = c.UpsertVariable(variable.NewConstant(name, cfg.Google.GT.ServerContainerURL)); err != nil {
|
||||
cmd := &cobra.Command{
|
||||
Use: "web",
|
||||
Short: "Provision Google Tag Manager Web Container",
|
||||
PersistentPreRunE: preRunReadConfig,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var clientCredentialsOption option.ClientOption
|
||||
if cfg.Google.CredentialsFile != "" {
|
||||
clientCredentialsOption = option.WithCredentialsFile(cfg.Google.CredentialsFile)
|
||||
} else {
|
||||
clientCredentialsOption = option.WithCredentialsJSON([]byte(cfg.Google.CredentialsJSON))
|
||||
}
|
||||
|
||||
parser, err := contemplate.Load(&cfg.Tagmanager.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var googleTagSettings *tagmanager2.Variable
|
||||
{
|
||||
name := p.Variables.GTSettingsName("Google Tag")
|
||||
if googleTagSettings, err = c.UpsertVariable(variable.NewGTSettings(name, map[string]*tagmanager2.Variable{
|
||||
"server_container_url": serverContainerURL,
|
||||
})); err != nil {
|
||||
eventParameters := map[string][]string{}
|
||||
for _, pkgCfg := range cfg.Tagmanager.Packages {
|
||||
pkg := parser.Package(pkgCfg.Path)
|
||||
for _, event := range pkgCfg.Types {
|
||||
eventParameters[event] = getEventParams(pkg.LookupScopeType(event))
|
||||
}
|
||||
}
|
||||
|
||||
c, err := tagmanager.NewClient(
|
||||
cmd.Context(),
|
||||
logger,
|
||||
cfg.Google.GTM.AccountID,
|
||||
cfg.Google.GTM.Web.ContainerID,
|
||||
cfg.Google.GTM.Web.WorkspaceID,
|
||||
cfg.Google.GA4.MeasurementID,
|
||||
tagmanager.ClientWithRequestQuota(cfg.Google.RequestQuota),
|
||||
tagmanager.ClientWithClientOptions(clientCredentialsOption),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
name := p.Tags.GoogleTagName("Google Tag")
|
||||
if _, err = c.UpsertTag(client.NewGoogleTag(name, ga4MeasurementID, googleTagSettings, map[string]string{
|
||||
"enable_page_views": strconv.FormatBool(cfg.Google.GT.EnablePageViews),
|
||||
})); err != nil {
|
||||
p := cfg.Tagmanager.Prefixes
|
||||
|
||||
if _, err := c.UpsertFolder(p.FolderName(c.FolderName())); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for event, parameters := range eventParameters {
|
||||
var trigger *tagmanager2.Trigger
|
||||
var ga4MeasurementID *tagmanager2.Variable
|
||||
{
|
||||
name := p.Triggers.CustomEventName(event)
|
||||
if trigger, err = c.UpsertTrigger(trigger2.NewCustomEvent(name, event)); err != nil {
|
||||
name := p.Variables.ConstantName("Google Analytics GA4 ID")
|
||||
if ga4MeasurementID, err = c.UpsertVariable(variable.NewConstant(name, c.MeasurementID())); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
eventSettingsVariables := make(map[string]*tagmanager2.Variable, len(parameters))
|
||||
for _, parameter := range parameters {
|
||||
name := p.Variables.EventModelName(parameter)
|
||||
eventSettingsVariables[parameter], err = c.UpsertVariable(variable.NewEventModel(name, parameter))
|
||||
if err != nil {
|
||||
var serverContainerURL *tagmanager2.Variable
|
||||
{
|
||||
name := p.Variables.ConstantName("Server Container URL")
|
||||
if serverContainerURL, err = c.UpsertVariable(variable.NewConstant(name, cfg.Google.GT.ServerContainerURL)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var eventSettings *tagmanager2.Variable
|
||||
var googleTagSettings *tagmanager2.Variable
|
||||
{
|
||||
name := p.Variables.GTEventSettingsName(event)
|
||||
if eventSettings, err = c.UpsertVariable(variable.NewGTEventSettings(name, eventSettingsVariables)); err != nil {
|
||||
name := p.Variables.GTSettingsName("Google Tag")
|
||||
if googleTagSettings, err = c.UpsertVariable(variable.NewGTSettings(name, map[string]*tagmanager2.Variable{
|
||||
"server_container_url": serverContainerURL,
|
||||
})); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
name := p.Tags.GA4EventName(event)
|
||||
if _, err := c.UpsertTag(client.NewGA4Event(name, event, eventSettings, ga4MeasurementID, trigger)); err != nil {
|
||||
name := p.Tags.GoogleTagName("Google Tag")
|
||||
if _, err = c.UpsertTag(client.NewGoogleTag(name, ga4MeasurementID, googleTagSettings, map[string]string{
|
||||
"enable_page_views": strconv.FormatBool(cfg.Google.GT.EnablePageViews),
|
||||
})); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var filter string
|
||||
|
||||
func init() {
|
||||
tagmanagerCmd.AddCommand(tagmanagerWebCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// tagmanagerWebCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
// tagmanagerWebCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
tagmanagerWebCmd.Flags().StringVar(&filter, "filter", "", "Filter tag manager")
|
||||
for event, parameters := range eventParameters {
|
||||
var trigger *tagmanager2.Trigger
|
||||
{
|
||||
name := p.Triggers.CustomEventName(event)
|
||||
if trigger, err = c.UpsertTrigger(trigger2.NewCustomEvent(name, event)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Tagmanager.Tags.GA4Enabled {
|
||||
eventSettingsVariables := make(map[string]*tagmanager2.Variable, len(parameters))
|
||||
for _, parameter := range parameters {
|
||||
name := p.Variables.EventModelName(parameter)
|
||||
eventSettingsVariables[parameter], err = c.UpsertVariable(variable.NewEventModel(name, parameter))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var eventSettings *tagmanager2.Variable
|
||||
{
|
||||
name := p.Variables.GTEventSettingsName(event)
|
||||
if eventSettings, err = c.UpsertVariable(variable.NewGTEventSettings(name, eventSettingsVariables)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
name := p.Tags.GA4EventName(event)
|
||||
if _, err := c.UpsertTag(client.NewGA4Event(name, event, eventSettings, ga4MeasurementID, trigger)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
root.AddCommand(cmd)
|
||||
}
|
||||
|
||||
@ -1,8 +1,15 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/gzuidhof/tygo/tygo"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/foomo/gocontemplate/pkg/contemplate"
|
||||
"github.com/foomo/sesamy-cli/pkg/typescript/generator"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
// typescriptCmd represents the typescript command
|
||||
@ -11,27 +18,36 @@ var typescriptCmd = &cobra.Command{
|
||||
Short: "Generate typescript events",
|
||||
PersistentPreRunE: preRunReadConfig,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
gen := tygo.New(&tygo.Config{
|
||||
Packages: cfg.Typescript.Packages,
|
||||
})
|
||||
for k, v := range cfg.Typescript.TypeMappings {
|
||||
gen.SetTypeMapping(k, v)
|
||||
ctpl, err := contemplate.Load(&cfg.Typescript.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return gen.Generate()
|
||||
files, err := generator.Generate(logger, ctpl)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get build typescript")
|
||||
}
|
||||
|
||||
outPath, err := filepath.Abs(cfg.Typescript.OutputPath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get output path")
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(outPath, os.ModePerm); err != nil {
|
||||
return errors.Wrap(err, "failed to create typescript output directory")
|
||||
}
|
||||
|
||||
logger.InfoContext(cmd.Context(), "generated typescript code", "dir", outPath, "files", maps.Keys(files))
|
||||
for filename, file := range files {
|
||||
if err = os.WriteFile(path.Join(outPath, filename), []byte(file.String()), 0600); err != nil {
|
||||
return errors.Wrap(err, "failed to write typescript code")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(typescriptCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// typescriptCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
// typescriptCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
}
|
||||
|
||||
@ -8,14 +8,18 @@ import (
|
||||
|
||||
var version = "latest"
|
||||
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "show version information",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println(version)
|
||||
},
|
||||
func NewVersionCmd(root *cobra.Command) {
|
||||
cmd := &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "show version information",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println(version)
|
||||
},
|
||||
}
|
||||
|
||||
root.AddCommand(cmd)
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(versionCmd)
|
||||
NewVersionCmd(rootCmd)
|
||||
}
|
||||
|
||||
23
go.mod
23
go.mod
@ -1,10 +1,13 @@
|
||||
module github.com/foomo/sesamy-cli
|
||||
|
||||
go 1.22.2
|
||||
go 1.22.3
|
||||
|
||||
require (
|
||||
github.com/fatih/structtag v1.2.0
|
||||
github.com/gzuidhof/tygo v0.2.14
|
||||
github.com/foomo/go v0.0.3
|
||||
github.com/foomo/gocontemplate v0.1.4
|
||||
github.com/foomo/sesamy-go v0.2.0
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pterm/pterm v0.12.79
|
||||
@ -12,20 +15,22 @@ require (
|
||||
github.com/spf13/viper v1.18.2
|
||||
github.com/stoewer/go-strcase v1.3.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
golang.org/x/tools v0.21.0
|
||||
google.golang.org/api v0.178.0
|
||||
github.com/wissance/stringFormatter v1.2.0
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
|
||||
google.golang.org/api v0.181.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.3.0 // indirect
|
||||
cloud.google.com/go/auth v0.4.1 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.3.0 // indirect
|
||||
github.com/containerd/console v1.0.3 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/foomo/gostandards v0.1.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
@ -57,10 +62,8 @@ require (
|
||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.23.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/oauth2 v0.20.0 // indirect
|
||||
@ -68,7 +71,9 @@ require (
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/term v0.20.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6 // indirect
|
||||
golang.org/x/tools v0.21.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240506185236-b8a5c65736ae // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 // indirect
|
||||
google.golang.org/grpc v1.63.2 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
|
||||
46
go.sum
46
go.sum
@ -7,8 +7,8 @@ atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtE
|
||||
atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs=
|
||||
atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go/auth v0.3.0 h1:PRyzEpGfx/Z9e8+lHsbkoUVXD0gnu4MNmm7Gp8TQNIs=
|
||||
cloud.google.com/go/auth v0.3.0/go.mod h1:lBv6NKTWp8E3LPzmO1TbiiRKc4drLOfHsgmlH9ogv5w=
|
||||
cloud.google.com/go/auth v0.4.1 h1:Z7YNIhlWRtrnKlZke7z3GMqzvuYzdc2z98F9D1NV5Hg=
|
||||
cloud.google.com/go/auth v0.4.1/go.mod h1:QVBuVEKpCn4Zp58hzRGvL0tjRGU0YqdRTdCHM1IHnro=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q=
|
||||
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
|
||||
@ -42,6 +42,14 @@ 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/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/foomo/go v0.0.3 h1:5pGzcPC78dImuBTT7nsZZnH+GIQUylbCtMkFEH26uZk=
|
||||
github.com/foomo/go v0.0.3/go.mod h1:x6g64wiQusqaFElnh5rlk9unCgLKmfUWy0YFLejJxio=
|
||||
github.com/foomo/gocontemplate v0.1.4 h1:MYSsoltno9pNMU5NwALd7PNSQ1XjN1tpqMGWAhCUETc=
|
||||
github.com/foomo/gocontemplate v0.1.4/go.mod h1:BH8eODDwlqWhasSl86avbtMN3Zfgt4pWwr8/PBPM5v0=
|
||||
github.com/foomo/gostandards v0.1.0 h1:dN6Yoj5un74W8hooC+boYcdbkTzF9jqU39q5kQCkzn4=
|
||||
github.com/foomo/gostandards v0.1.0/go.mod h1:eyoFzndWb1kuDfupR/qf567mHeHZRi5//m64khreVac=
|
||||
github.com/foomo/sesamy-go v0.2.0 h1:xGMYkxXda2P6GlMh6hBAXQgZZRw+ewfKPRPLyxv8Dfo=
|
||||
github.com/foomo/sesamy-go v0.2.0/go.mod h1:Z3QdFwcADg+4epWxhfM+dEAV+Z0gxgb05ug/G6yNlZg=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
@ -88,8 +96,6 @@ github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQ
|
||||
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/gzuidhof/tygo v0.2.14 h1:QRBD6eby2SMyYzv8KzXA+yopPbEO6w2Qzuuqqp9z+vU=
|
||||
github.com/gzuidhof/tygo v0.2.14/go.mod h1:s3lpnppkDixQQhMWD78yPtAmugMHENsPWpQYziUIpw0=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
@ -113,6 +119,8 @@ github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3v
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
||||
@ -135,8 +143,8 @@ github.com/pterm/pterm v0.12.79/go.mod h1:1v/gzOF1N0FsjbgTHZ1wVycRkKiatFvJSJC4IG
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||
@ -161,7 +169,6 @@ github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8w
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@ -173,6 +180,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
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.2.0 h1:lB0zcJkTA1O4Eb2qSTJmyapla/LihQt6NpJLghwWSb0=
|
||||
github.com/wissance/stringFormatter v1.2.0/go.mod h1:H7Mz15+5i8ypmv6bLknM/uD+U1teUW99PlW0DNCNscA=
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
@ -187,18 +196,16 @@ go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGX
|
||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
@ -269,18 +276,17 @@ golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
|
||||
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
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=
|
||||
google.golang.org/api v0.178.0 h1:yoW/QMI4bRVCHF+NWOTa4cL8MoWL3Jnuc7FlcFF91Ok=
|
||||
google.golang.org/api v0.178.0/go.mod h1:84/k2v8DFpDRebpGcooklv/lais3MEfqpaBLA12gl2U=
|
||||
google.golang.org/api v0.181.0 h1:rPdjwnWgiPPOJx3IcSAQ2III5aX5tCer6wMpa/xmZi4=
|
||||
google.golang.org/api v0.181.0/go.mod h1:MnQ+M0CFsfUwA5beZ+g/vCBCPXvtmZwRz2qzZk8ih1k=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240415180920-8c6c420018be h1:Zz7rLWqp0ApfsR/l7+zSHhY3PMiH2xqgxlfYfAfNpoU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240415180920-8c6c420018be/go.mod h1:dvdCTIoAGbkWbcIKBniID56/7XHTt6WfxXNMxuziJ+w=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6 h1:DujSIu+2tC9Ht0aPNA7jgj23Iq8Ewi5sgkQ++wdvonE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240506185236-b8a5c65736ae h1:AH34z6WAGVNkllnKs5raNq3yRq93VnjBG6rpfub/jYk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240506185236-b8a5c65736ae/go.mod h1:FfiGhwUm6CJviekPrc0oJ+7h29e+DmWU6UtjX0ZvI7Y=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 h1:mxSlqyb8ZAHsYDCfiXN1EDdNTdvjUJSLY+OnAUtYNYA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8/go.mod h1:I7Y+G38R2bu5j1aLzfFmQfTcU/WnFuqDwLZAbvKTKpM=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
|
||||
@ -1,82 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/structtag"
|
||||
"github.com/foomo/sesamy-cli/pkg/config"
|
||||
"github.com/pterm/pterm"
|
||||
"github.com/stoewer/go-strcase"
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
func GetEventParameters(source config.Tagmanager) (map[string][]string, error) {
|
||||
ret := map[string][]string{}
|
||||
|
||||
pkgs, err := packages.Load(&packages.Config{
|
||||
Mode: packages.NeedSyntax | packages.NeedFiles,
|
||||
}, source.PackageNames()...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, pkg := range pkgs {
|
||||
if len(pkg.Errors) > 0 {
|
||||
return nil, fmt.Errorf("%+v", pkg.Errors)
|
||||
}
|
||||
|
||||
if len(pkg.GoFiles) == 0 {
|
||||
return nil, fmt.Errorf("no input go files for package index %d", i)
|
||||
}
|
||||
|
||||
conf := source.PackageConfig(pkg.ID)
|
||||
|
||||
for i, file := range pkg.Syntax {
|
||||
if conf.IsFileIgnored(pkg.GoFiles[i]) {
|
||||
continue
|
||||
}
|
||||
|
||||
ast.Inspect(file, func(n ast.Node) bool {
|
||||
// GenDecl can be an import, type, var, or const expression
|
||||
if x, ok := n.(*ast.GenDecl); ok {
|
||||
if x.Tok == token.IMPORT {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, spec := range x.Specs {
|
||||
// e.g. "type Foo struct {}" or "type Bar = string"
|
||||
if elem, ok := spec.(*ast.TypeSpec); ok && elem.Name.IsExported() {
|
||||
if strct, ok := elem.Type.(*ast.StructType); ok {
|
||||
name := strcase.SnakeCase(elem.Name.String())
|
||||
var fields []string
|
||||
for _, field := range strct.Fields.List {
|
||||
tags, err := structtag.Parse(strings.Trim(field.Tag.Value, "`"))
|
||||
if err != nil {
|
||||
pterm.Warning.Println(err.Error(), field.Tag.Value)
|
||||
return false
|
||||
}
|
||||
tag, err := tags.Get("json")
|
||||
if err != nil {
|
||||
pterm.Warning.Println(err.Error())
|
||||
return false
|
||||
}
|
||||
if tag.Value() != "" && tag.Value() != "-" {
|
||||
fields = append(fields, strings.Split(tag.Value(), ",")[0])
|
||||
}
|
||||
}
|
||||
ret[name] = fields
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
1
pkg/code/doc.go
Normal file
1
pkg/code/doc.go
Normal file
@ -0,0 +1 @@
|
||||
package code
|
||||
78
pkg/code/file.go
Normal file
78
pkg/code/file.go
Normal file
@ -0,0 +1,78 @@
|
||||
package code
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
const (
|
||||
SectionAnnotations = 0
|
||||
SectionCopyright = 100
|
||||
SectionHead = 200
|
||||
SectionBody = 300
|
||||
SectionFoot = 400
|
||||
)
|
||||
|
||||
type File struct {
|
||||
Sections map[int]*Section
|
||||
}
|
||||
|
||||
func NewFile() *File {
|
||||
return &File{
|
||||
Sections: map[int]*Section{
|
||||
SectionAnnotations: {},
|
||||
SectionCopyright: {},
|
||||
SectionHead: {},
|
||||
SectionBody: {},
|
||||
SectionFoot: {},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (f *File) Annotations() *Section {
|
||||
return f.Section(SectionAnnotations)
|
||||
}
|
||||
|
||||
func (f *File) Copyright() *Section {
|
||||
return f.Section(SectionCopyright)
|
||||
}
|
||||
|
||||
func (f *File) Head() *Section {
|
||||
return f.Section(SectionHead)
|
||||
}
|
||||
|
||||
func (f *File) Body() *Section {
|
||||
return f.Section(SectionBody)
|
||||
}
|
||||
|
||||
func (f *File) Foot() *Section {
|
||||
return f.Section(SectionFoot)
|
||||
}
|
||||
|
||||
func (f *File) Section(id int) *Section {
|
||||
return f.Sections[id]
|
||||
}
|
||||
|
||||
func (f *File) AddSection(id int) {
|
||||
f.Sections[id] = &Section{}
|
||||
}
|
||||
|
||||
func (f *File) String() string {
|
||||
b := &strings.Builder{}
|
||||
|
||||
sections := maps.Keys(f.Sections)
|
||||
slices.Sort(sections)
|
||||
|
||||
for _, id := range sections {
|
||||
section := f.Sections[id]
|
||||
sectionParts := section.Parts
|
||||
slices.Sort(sectionParts)
|
||||
for _, part := range sectionParts {
|
||||
b.WriteString(part + "\n")
|
||||
}
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
35
pkg/code/section.go
Normal file
35
pkg/code/section.go
Normal file
@ -0,0 +1,35 @@
|
||||
package code
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"github.com/wissance/stringFormatter"
|
||||
)
|
||||
|
||||
type Section struct {
|
||||
Parts []string
|
||||
}
|
||||
|
||||
func (s *Section) Sprintf(format string, a ...any) {
|
||||
value := fmt.Sprintf(format, a...)
|
||||
if !slices.Contains(s.Parts, value) {
|
||||
s.Parts = append(s.Parts, value)
|
||||
}
|
||||
}
|
||||
|
||||
// Tprintn {n} , n here is a number to notes order of argument list to use i.e. {0}, {1}
|
||||
func (s *Section) Tprintn(template string, a ...any) {
|
||||
value := stringFormatter.Format(template, a...)
|
||||
if !slices.Contains(s.Parts, value) {
|
||||
s.Parts = append(s.Parts, value)
|
||||
}
|
||||
}
|
||||
|
||||
// Tprintm {name} to notes arguments by name i.e. {name}, {last_name}, {address} and so on ...
|
||||
func (s *Section) Tprintm(template string, a map[string]any) {
|
||||
value := stringFormatter.FormatComplex(template, a)
|
||||
if !slices.Contains(s.Parts, value) {
|
||||
s.Parts = append(s.Parts, value)
|
||||
}
|
||||
}
|
||||
15
pkg/config/package.go
Normal file
15
pkg/config/package.go
Normal file
@ -0,0 +1,15 @@
|
||||
package config
|
||||
|
||||
type Package struct {
|
||||
Path string `yaml:"path"`
|
||||
Events []string `yaml:"events"`
|
||||
}
|
||||
|
||||
func (c Package) ExportEvent(event string) bool {
|
||||
for _, name := range c.Events {
|
||||
if name == event {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
24
pkg/config/packages.go
Normal file
24
pkg/config/packages.go
Normal file
@ -0,0 +1,24 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type Packages []Package
|
||||
|
||||
func (c Packages) PackageNames() []string {
|
||||
ret := make([]string, len(c))
|
||||
for i, value := range c {
|
||||
ret[i] = value.Path
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c Packages) PackageConfig(path string) (Package, error) {
|
||||
for _, value := range c {
|
||||
if value.Path == path {
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
return Package{}, errors.Errorf("package for path '%s' not found", path)
|
||||
}
|
||||
@ -1,28 +1,15 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/gzuidhof/tygo/tygo"
|
||||
"github.com/foomo/gocontemplate/pkg/contemplate"
|
||||
)
|
||||
|
||||
type Tagmanager struct {
|
||||
Packages []*tygo.PackageConfig `yaml:"packages"`
|
||||
TypeMappings map[string]string `yaml:"type_mappings"`
|
||||
Prefixes TagmanagerPrefixes `yaml:"prefixes"`
|
||||
contemplate.Config `yaml:",squash"`
|
||||
Tags TagmanagerTags `yaml:"tags"`
|
||||
Prefixes TagmanagerPrefixes `yaml:"prefixes"`
|
||||
}
|
||||
|
||||
func (e Tagmanager) PackageNames() []string {
|
||||
ret := make([]string, len(e.Packages))
|
||||
for i, value := range e.Packages {
|
||||
ret[i] = value.Path
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (e Tagmanager) PackageConfig(path string) *tygo.PackageConfig {
|
||||
for _, value := range e.Packages {
|
||||
if value.Path == path {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
type TagmanagerTags struct {
|
||||
GA4Enabled bool `yaml:"gA4_enabled"`
|
||||
}
|
||||
|
||||
@ -1,27 +1,10 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/gzuidhof/tygo/tygo"
|
||||
"github.com/foomo/gocontemplate/pkg/contemplate"
|
||||
)
|
||||
|
||||
type Typescript struct {
|
||||
Packages []*tygo.PackageConfig `yaml:"packages"`
|
||||
TypeMappings map[string]string `yaml:"type_mappings"`
|
||||
}
|
||||
|
||||
func (e Typescript) PackageNames() []string {
|
||||
ret := make([]string, len(e.Packages))
|
||||
for i, value := range e.Packages {
|
||||
ret[i] = value.Path
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (e Typescript) PackageConfig(path string) *tygo.PackageConfig {
|
||||
for _, value := range e.Packages {
|
||||
if value.Path == path {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
contemplate.Config `yaml:",squash"`
|
||||
OutputPath string `yaml:"output_path"`
|
||||
}
|
||||
|
||||
@ -2,9 +2,11 @@ package tagmanager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/hashstructure/v2"
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/api/option"
|
||||
"google.golang.org/api/tagmanager/v2"
|
||||
@ -115,10 +117,6 @@ func (c *Client) WorkspaceID() string {
|
||||
return c.workspaceID
|
||||
}
|
||||
|
||||
func (c *Client) Notes() string {
|
||||
return c.notes
|
||||
}
|
||||
|
||||
func (c *Client) MeasurementID() string {
|
||||
return c.measurementID
|
||||
}
|
||||
@ -146,323 +144,402 @@ func (c *Client) WorkspacePath() string {
|
||||
return c.ContainerPath() + "/workspaces/" + c.workspaceID
|
||||
}
|
||||
|
||||
func (c *Client) Notes(v any) string {
|
||||
var hash string
|
||||
if v != nil {
|
||||
if value, err := hashstructure.Hash(v, hashstructure.FormatV2, nil); err != nil {
|
||||
c.l.Warn("failed to hash struct:", "error", err)
|
||||
} else {
|
||||
hash = fmt.Sprintf(" [%d]", value)
|
||||
}
|
||||
}
|
||||
return c.notes + hash
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ~ Public methods
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
func (c *Client) Client(name string) (*tagmanager.Client, error) {
|
||||
if c.clients == nil {
|
||||
c.clients = map[string]*tagmanager.Client{}
|
||||
func (c *Client) GetClient(name string) (*tagmanager.Client, error) {
|
||||
elems, err := c.LoadClients()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.l.Debug("listing", "type", "Client")
|
||||
if _, ok := elems[name]; !ok {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
return elems[name], nil
|
||||
}
|
||||
|
||||
func (c *Client) LoadClients() (map[string]*tagmanager.Client, error) {
|
||||
if c.clients == nil {
|
||||
c.l.Debug("loading", "type", "Client")
|
||||
r, err := c.Service().Accounts.Containers.Workspaces.Clients.List(c.WorkspacePath()).Do()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := map[string]*tagmanager.Client{}
|
||||
for _, value := range r.Client {
|
||||
c.clients[value.Name] = value
|
||||
res[value.Name] = value
|
||||
}
|
||||
c.clients = res
|
||||
}
|
||||
|
||||
if _, ok := c.clients[name]; !ok {
|
||||
return c.clients, nil
|
||||
}
|
||||
|
||||
func (c *Client) GetFolder(name string) (*tagmanager.Folder, error) {
|
||||
elems, err := c.LoadFolders()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, ok := elems[name]; !ok {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
return c.clients[name], nil
|
||||
return elems[name], nil
|
||||
}
|
||||
|
||||
func (c *Client) Folder(name string) (*tagmanager.Folder, error) {
|
||||
func (c *Client) LoadFolders() (map[string]*tagmanager.Folder, error) {
|
||||
if c.folders == nil {
|
||||
c.folders = map[string]*tagmanager.Folder{}
|
||||
|
||||
c.l.Debug("listing", "type", "Folder")
|
||||
c.l.Debug("loading", "type", "Folder")
|
||||
r, err := c.Service().Accounts.Containers.Workspaces.Folders.List(c.WorkspacePath()).Do()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := map[string]*tagmanager.Folder{}
|
||||
for _, value := range r.Folder {
|
||||
c.folders[value.Name] = value
|
||||
res[value.Name] = value
|
||||
}
|
||||
c.folders = res
|
||||
}
|
||||
return c.folders, nil
|
||||
}
|
||||
|
||||
func (c *Client) GetVariable(name string) (*tagmanager.Variable, error) {
|
||||
elems, err := c.LoadVariables()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, ok := c.folders[name]; !ok {
|
||||
if _, ok := elems[name]; !ok {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
return c.folders[name], nil
|
||||
return elems[name], nil
|
||||
}
|
||||
|
||||
func (c *Client) Variable(name string) (*tagmanager.Variable, error) {
|
||||
func (c *Client) LoadVariables() (map[string]*tagmanager.Variable, error) {
|
||||
if c.variables == nil {
|
||||
c.variables = map[string]*tagmanager.Variable{}
|
||||
|
||||
c.l.Debug("listing", "type", "Variable")
|
||||
c.l.Debug("loading", "type", "Variable")
|
||||
r, err := c.Service().Accounts.Containers.Workspaces.Variables.List(c.WorkspacePath()).Do()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := map[string]*tagmanager.Variable{}
|
||||
for _, value := range r.Variable {
|
||||
c.variables[value.Name] = value
|
||||
res[value.Name] = value
|
||||
}
|
||||
c.variables = res
|
||||
}
|
||||
|
||||
if _, ok := c.variables[name]; !ok {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
return c.variables[name], nil
|
||||
return c.variables, nil
|
||||
}
|
||||
|
||||
func (c *Client) BuiltInVariable(name string) (*tagmanager.BuiltInVariable, error) {
|
||||
if c.builtInVariables == nil {
|
||||
c.builtInVariables = map[string]*tagmanager.BuiltInVariable{}
|
||||
func (c *Client) GetBuiltInVariable(typeName string) (*tagmanager.BuiltInVariable, error) {
|
||||
elems, err := c.LoadBuiltInVariables()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.l.Debug("listing", "type", "Built-In Variable")
|
||||
if _, ok := elems[typeName]; !ok {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
return elems[typeName], nil
|
||||
}
|
||||
|
||||
func (c *Client) LoadBuiltInVariables() (map[string]*tagmanager.BuiltInVariable, error) {
|
||||
if c.builtInVariables == nil {
|
||||
c.l.Debug("loading", "type", "BuiltInVariable")
|
||||
r, err := c.Service().Accounts.Containers.Workspaces.BuiltInVariables.List(c.WorkspacePath()).Do()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := map[string]*tagmanager.BuiltInVariable{}
|
||||
for _, value := range r.BuiltInVariable {
|
||||
c.builtInVariables[value.Type] = value
|
||||
res[value.Type] = value
|
||||
}
|
||||
c.builtInVariables = res
|
||||
}
|
||||
|
||||
if _, ok := c.builtInVariables[name]; !ok {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
return c.builtInVariables[name], nil
|
||||
return c.builtInVariables, nil
|
||||
}
|
||||
|
||||
func (c *Client) Trigger(name string) (*tagmanager.Trigger, error) {
|
||||
if c.triggers == nil {
|
||||
c.triggers = map[string]*tagmanager.Trigger{}
|
||||
elems, err := c.LoadTriggers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.l.Debug("listing", "type", "Trigger")
|
||||
if _, ok := elems[name]; !ok {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
return elems[name], nil
|
||||
}
|
||||
|
||||
func (c *Client) LoadTriggers() (map[string]*tagmanager.Trigger, error) {
|
||||
if c.triggers == nil {
|
||||
c.l.Debug("loading", "type", "Trigger")
|
||||
r, err := c.Service().Accounts.Containers.Workspaces.Triggers.List(c.WorkspacePath()).Do()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := map[string]*tagmanager.Trigger{}
|
||||
for _, value := range r.Trigger {
|
||||
c.triggers[value.Name] = value
|
||||
res[value.Name] = value
|
||||
}
|
||||
c.triggers = res
|
||||
}
|
||||
|
||||
if _, ok := c.triggers[name]; !ok {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
return c.triggers[name], nil
|
||||
return c.triggers, nil
|
||||
}
|
||||
|
||||
func (c *Client) Tag(name string) (*tagmanager.Tag, error) {
|
||||
if c.tags == nil {
|
||||
c.tags = map[string]*tagmanager.Tag{}
|
||||
elems, err := c.LoadTags()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.l.Debug("listing", "type", "Tag")
|
||||
if _, ok := elems[name]; !ok {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
return elems[name], nil
|
||||
}
|
||||
|
||||
func (c *Client) LoadTags() (map[string]*tagmanager.Tag, error) {
|
||||
if c.tags == nil {
|
||||
c.l.Debug("loading", "type", "Tag")
|
||||
r, err := c.Service().Accounts.Containers.Workspaces.Tags.List(c.WorkspacePath()).Do()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := map[string]*tagmanager.Tag{}
|
||||
for _, value := range r.Tag {
|
||||
c.tags[value.Name] = value
|
||||
res[value.Name] = value
|
||||
}
|
||||
c.tags = res
|
||||
}
|
||||
|
||||
if _, ok := c.tags[name]; !ok {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
return c.tags[name], nil
|
||||
return c.tags, nil
|
||||
}
|
||||
|
||||
func (c *Client) UpsertClient(client *tagmanager.Client) (*tagmanager.Client, error) {
|
||||
l := c.l.With("type", "Client", "name", client.Name)
|
||||
func (c *Client) UpsertClient(item *tagmanager.Client) (*tagmanager.Client, error) {
|
||||
l := c.l.With("type", "Client", "name", item.Name)
|
||||
|
||||
client.AccountId = c.AccountID()
|
||||
client.ContainerId = c.ContainerID()
|
||||
client.WorkspaceId = c.WorkspaceID()
|
||||
client.Notes = c.Notes()
|
||||
item.Notes = c.Notes(item)
|
||||
item.AccountId = c.AccountID()
|
||||
item.ContainerId = c.ContainerID()
|
||||
item.WorkspaceId = c.WorkspaceID()
|
||||
|
||||
cache, err := c.Client(client.Name)
|
||||
cache, err := c.GetClient(item.Name)
|
||||
if err != nil && !errors.Is(err, ErrNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
folder, err := c.Folder(c.folderName)
|
||||
folder, err := c.GetFolder(c.folderName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "failed to retrieve folder")
|
||||
}
|
||||
client.ParentFolderId = folder.FolderId
|
||||
item.ParentFolderId = folder.FolderId
|
||||
|
||||
if cache == nil {
|
||||
l.Info("creating")
|
||||
c.clients[client.Name], err = c.Service().Accounts.Containers.Workspaces.Clients.Create(c.WorkspacePath(), client).Do()
|
||||
l.Info("⚠️ creating")
|
||||
c.clients[item.Name], err = c.Service().Accounts.Containers.Workspaces.Clients.Create(c.WorkspacePath(), item).Do()
|
||||
} else if item.Notes == cache.Notes {
|
||||
l.Info("✅ unchanged", "id", cache.ClientId)
|
||||
} else {
|
||||
l.Info("updating", "id", cache.ClientId)
|
||||
c.clients[client.Name], err = c.Service().Accounts.Containers.Workspaces.Clients.Update(c.WorkspacePath()+"/clients/"+cache.ClientId, client).Do()
|
||||
l.Info("⚠️ updating", "id", cache.ClientId)
|
||||
c.clients[item.Name], err = c.Service().Accounts.Containers.Workspaces.Clients.Update(c.WorkspacePath()+"/clients/"+cache.ClientId, item).Do()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.Client(client.Name)
|
||||
return c.GetClient(item.Name)
|
||||
}
|
||||
|
||||
func (c *Client) UpsertFolder(name string) (*tagmanager.Folder, error) {
|
||||
l := c.l.With("type", "Folder", "name", name)
|
||||
|
||||
cache, err := c.Folder(name)
|
||||
item := &tagmanager.Folder{
|
||||
Name: name,
|
||||
}
|
||||
|
||||
item.Notes = c.Notes(item)
|
||||
item.AccountId = c.AccountID()
|
||||
item.ContainerId = c.ContainerID()
|
||||
item.WorkspaceId = c.WorkspaceID()
|
||||
|
||||
cache, err := c.GetFolder(name)
|
||||
if err != nil && !errors.Is(err, ErrNotFound) && !errors.Is(err, ErrNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
folder := &tagmanager.Folder{
|
||||
AccountId: c.accountID,
|
||||
ContainerId: c.containerID,
|
||||
WorkspaceId: c.workspaceID,
|
||||
Name: name,
|
||||
Notes: c.notes,
|
||||
}
|
||||
|
||||
if cache == nil {
|
||||
l.Info("creating")
|
||||
c.folders[name], err = c.Service().Accounts.Containers.Workspaces.Folders.Create(c.WorkspacePath(), folder).Do()
|
||||
l.Info("⚠️ creating")
|
||||
c.folders[name], err = c.Service().Accounts.Containers.Workspaces.Folders.Create(c.WorkspacePath(), item).Do()
|
||||
} else if item.Notes == cache.Notes {
|
||||
l.Info("✅ unchanged", "id", item.FolderId)
|
||||
} else {
|
||||
l.Info("updating", "id", cache.FolderId)
|
||||
c.folders[name], err = c.Service().Accounts.Containers.Workspaces.Folders.Update(c.WorkspacePath()+"/folders/"+cache.FolderId, folder).Do()
|
||||
l.Info("⚠️ updating", "id", cache.FolderId)
|
||||
c.folders[name], err = c.Service().Accounts.Containers.Workspaces.Folders.Update(c.WorkspacePath()+"/folders/"+cache.FolderId, item).Do()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.Folder(name)
|
||||
return c.GetFolder(name)
|
||||
}
|
||||
|
||||
func (c *Client) UpsertVariable(variable *tagmanager.Variable) (*tagmanager.Variable, error) {
|
||||
l := c.l.With("type", "Variable", "name", variable.Name)
|
||||
func (c *Client) UpsertVariable(item *tagmanager.Variable) (*tagmanager.Variable, error) {
|
||||
l := c.l.With("type", "Variable", "name", item.Name)
|
||||
|
||||
variable.AccountId = c.AccountID()
|
||||
variable.ContainerId = c.ContainerID()
|
||||
variable.WorkspaceId = c.WorkspaceID()
|
||||
variable.Notes = c.Notes()
|
||||
item.Notes = c.Notes(item)
|
||||
item.AccountId = c.AccountID()
|
||||
item.ContainerId = c.ContainerID()
|
||||
item.WorkspaceId = c.WorkspaceID()
|
||||
|
||||
cache, err := c.Variable(variable.Name)
|
||||
cache, err := c.GetVariable(item.Name)
|
||||
if err != nil && !errors.Is(err, ErrNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
folder, err := c.Folder(c.folderName)
|
||||
folder, err := c.GetFolder(c.folderName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "failed to retrieve folder")
|
||||
}
|
||||
variable.ParentFolderId = folder.FolderId
|
||||
item.ParentFolderId = folder.FolderId
|
||||
|
||||
if cache == nil {
|
||||
l.Info("creating")
|
||||
c.variables[variable.Name], err = c.Service().Accounts.Containers.Workspaces.Variables.Create(c.WorkspacePath(), variable).Do()
|
||||
l.Info("⚠️ creating")
|
||||
c.variables[item.Name], err = c.Service().Accounts.Containers.Workspaces.Variables.Create(c.WorkspacePath(), item).Do()
|
||||
} else if item.Notes == cache.Notes {
|
||||
l.Info("✅ unchanged", "id", cache.VariableId)
|
||||
} else {
|
||||
l.Info("updating", "id", cache.VariableId)
|
||||
c.variables[variable.Name], err = c.Service().Accounts.Containers.Workspaces.Variables.Update(c.WorkspacePath()+"/variables/"+cache.VariableId, variable).Do()
|
||||
l.Info("⚠️ updating", "id", cache.VariableId)
|
||||
c.variables[item.Name], err = c.Service().Accounts.Containers.Workspaces.Variables.Update(c.WorkspacePath()+"/variables/"+cache.VariableId, item).Do()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.Variable(variable.Name)
|
||||
return c.GetVariable(item.Name)
|
||||
}
|
||||
|
||||
func (c *Client) EnableBuiltInVariable(name string) (*tagmanager.BuiltInVariable, error) {
|
||||
l := c.l.With("type", "Built-In Variable", "name", name)
|
||||
func (c *Client) EnableBuiltInVariable(typeName string) (*tagmanager.BuiltInVariable, error) {
|
||||
l := c.l.With("type", "Built-In Variable", "typeName", typeName)
|
||||
|
||||
cache, err := c.BuiltInVariable(name)
|
||||
cache, err := c.GetBuiltInVariable(typeName)
|
||||
if err != nil && !errors.Is(err, ErrNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cache != nil {
|
||||
l.Info("✅ unchanged")
|
||||
return cache, nil
|
||||
}
|
||||
|
||||
l.Info("creating")
|
||||
resp, err := c.Service().Accounts.Containers.Workspaces.BuiltInVariables.Create(c.WorkspacePath()).Type(name).Do()
|
||||
l.Info("⚠️ creating")
|
||||
resp, err := c.Service().Accounts.Containers.Workspaces.BuiltInVariables.Create(c.WorkspacePath()).Type(typeName).Do()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create built-in variable")
|
||||
}
|
||||
|
||||
for _, builtInVariable := range resp.BuiltInVariable {
|
||||
l.Debug(builtInVariable.Type)
|
||||
c.builtInVariables[builtInVariable.Type] = builtInVariable
|
||||
}
|
||||
|
||||
return c.BuiltInVariable(name)
|
||||
return c.GetBuiltInVariable(typeName)
|
||||
}
|
||||
|
||||
func (c *Client) UpsertTrigger(trigger *tagmanager.Trigger) (*tagmanager.Trigger, error) {
|
||||
l := c.l.With("type", "Trigger", "name", trigger.Name)
|
||||
func (c *Client) UpsertTrigger(item *tagmanager.Trigger) (*tagmanager.Trigger, error) {
|
||||
l := c.l.With("type", "Trigger", "name", item.Name)
|
||||
|
||||
trigger.AccountId = c.AccountID()
|
||||
trigger.ContainerId = c.ContainerID()
|
||||
trigger.WorkspaceId = c.WorkspaceID()
|
||||
trigger.Notes = c.Notes()
|
||||
item.Notes = c.Notes(item)
|
||||
item.AccountId = c.AccountID()
|
||||
item.ContainerId = c.ContainerID()
|
||||
item.WorkspaceId = c.WorkspaceID()
|
||||
|
||||
cache, err := c.Trigger(trigger.Name)
|
||||
cache, err := c.Trigger(item.Name)
|
||||
if err != nil && !errors.Is(err, ErrNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
folder, err := c.Folder(c.folderName)
|
||||
folder, err := c.GetFolder(c.folderName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "failed to retrieve folder")
|
||||
}
|
||||
trigger.ParentFolderId = folder.FolderId
|
||||
item.ParentFolderId = folder.FolderId
|
||||
|
||||
if cache == nil {
|
||||
l.Info("creating")
|
||||
c.triggers[trigger.Name], err = c.Service().Accounts.Containers.Workspaces.Triggers.Create(c.WorkspacePath(), trigger).Do()
|
||||
l.Info("⚠️ creating")
|
||||
c.triggers[item.Name], err = c.Service().Accounts.Containers.Workspaces.Triggers.Create(c.WorkspacePath(), item).Do()
|
||||
} else if item.Notes == cache.Notes {
|
||||
l.Info("✅ unchanged", "id", cache.TriggerId)
|
||||
} else {
|
||||
l.Info("updating", "id", cache.TriggerId)
|
||||
c.triggers[trigger.Name], err = c.Service().Accounts.Containers.Workspaces.Triggers.Update(c.WorkspacePath()+"/triggers/"+cache.TriggerId, trigger).Do()
|
||||
l.Info("⚠️ updating", "id", cache.TriggerId)
|
||||
c.triggers[item.Name], err = c.Service().Accounts.Containers.Workspaces.Triggers.Update(c.WorkspacePath()+"/triggers/"+cache.TriggerId, item).Do()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.Trigger(trigger.Name)
|
||||
return c.Trigger(item.Name)
|
||||
}
|
||||
|
||||
func (c *Client) UpsertTag(tag *tagmanager.Tag) (*tagmanager.Tag, error) {
|
||||
l := c.l.With("type", "Tag", "name", tag.Name)
|
||||
func (c *Client) UpsertTag(item *tagmanager.Tag) (*tagmanager.Tag, error) {
|
||||
l := c.l.With("type", "Tag", "name", item.Name)
|
||||
|
||||
tag.AccountId = c.AccountID()
|
||||
tag.ContainerId = c.ContainerID()
|
||||
tag.WorkspaceId = c.WorkspaceID()
|
||||
tag.Notes = c.Notes()
|
||||
item.Notes = c.Notes(item)
|
||||
item.AccountId = c.AccountID()
|
||||
item.ContainerId = c.ContainerID()
|
||||
item.WorkspaceId = c.WorkspaceID()
|
||||
|
||||
cache, err := c.Tag(tag.Name)
|
||||
cache, err := c.Tag(item.Name)
|
||||
if err != nil && !errors.Is(err, ErrNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
folder, err := c.Folder(c.folderName)
|
||||
folder, err := c.GetFolder(c.folderName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "failed to retrieve folder")
|
||||
}
|
||||
tag.ParentFolderId = folder.FolderId
|
||||
item.ParentFolderId = folder.FolderId
|
||||
|
||||
if cache == nil {
|
||||
l.Info("creating")
|
||||
c.tags[tag.Name], err = c.Service().Accounts.Containers.Workspaces.Tags.Create(c.WorkspacePath(), tag).Do()
|
||||
l.Info("⚠️ creating")
|
||||
c.tags[item.Name], err = c.Service().Accounts.Containers.Workspaces.Tags.Create(c.WorkspacePath(), item).Do()
|
||||
} else if item.Notes == cache.Notes {
|
||||
l.Info("✅ unchanged", "id", cache.TagId)
|
||||
} else {
|
||||
l.Info("updating", "id", cache.TagId)
|
||||
c.tags[tag.Name], err = c.Service().Accounts.Containers.Workspaces.Tags.Update(c.WorkspacePath()+"/tags/"+cache.TagId, tag).Do()
|
||||
l.Info("⚠️ updating", "id", cache.TagId)
|
||||
c.tags[item.Name], err = c.Service().Accounts.Containers.Workspaces.Tags.Update(c.WorkspacePath()+"/tags/"+cache.TagId, item).Do()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.Tag(tag.Name)
|
||||
return c.Tag(item.Name)
|
||||
}
|
||||
|
||||
19
pkg/tagmanager/client/mpv2.go
Normal file
19
pkg/tagmanager/client/mpv2.go
Normal file
@ -0,0 +1,19 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"google.golang.org/api/tagmanager/v2"
|
||||
)
|
||||
|
||||
func NewMPv2(name string) *tagmanager.Client {
|
||||
return &tagmanager.Client{
|
||||
Name: name,
|
||||
Parameter: []*tagmanager.Parameter{
|
||||
{
|
||||
Type: "template",
|
||||
Key: "activationPath",
|
||||
Value: "/mp/collect",
|
||||
},
|
||||
},
|
||||
Type: "mpaw_client",
|
||||
}
|
||||
}
|
||||
@ -4,9 +4,14 @@ import (
|
||||
"google.golang.org/api/tagmanager/v2"
|
||||
)
|
||||
|
||||
func NewServerGA4Event(name string, measurementID *tagmanager.Variable, trigger *tagmanager.Trigger) *tagmanager.Tag {
|
||||
func NewServerGA4Event(name string, measurementID *tagmanager.Variable, triggers ...*tagmanager.Trigger) *tagmanager.Tag {
|
||||
triggerIDs := make([]string, len(triggers))
|
||||
for i, trigger := range triggers {
|
||||
triggerIDs[i] = trigger.TriggerId
|
||||
}
|
||||
|
||||
return &tagmanager.Tag{
|
||||
FiringTriggerId: []string{trigger.TriggerId},
|
||||
FiringTriggerId: triggerIDs,
|
||||
Name: name,
|
||||
Parameter: []*tagmanager.Parameter{
|
||||
{
|
||||
|
||||
42
pkg/typescript/generator/generate.go
Normal file
42
pkg/typescript/generator/generate.go
Normal file
@ -0,0 +1,42 @@
|
||||
package generator
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/foomo/gocontemplate/pkg/contemplate"
|
||||
"github.com/foomo/sesamy-cli/pkg/code"
|
||||
)
|
||||
|
||||
type (
|
||||
Options struct {
|
||||
PackageNameReplacer *strings.Replacer
|
||||
}
|
||||
Option func(*Options)
|
||||
)
|
||||
|
||||
func Generate(l *slog.Logger, ctpl *contemplate.Contemplate, opts ...Option) (map[string]*code.File, error) {
|
||||
l.Info("👷 generating typescript code")
|
||||
|
||||
o := Options{
|
||||
PackageNameReplacer: strings.NewReplacer(".", "_", "/", "_", "-", "_"),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
if opt != nil {
|
||||
opt(&o)
|
||||
}
|
||||
}
|
||||
|
||||
ret := make(map[string]*code.File, len(ctpl.Packages))
|
||||
for path, pkg := range ctpl.Packages {
|
||||
l.Debug("👷 adding package", "name", pkg.Name(), "path", pkg.Path())
|
||||
inst := NewPackage(l, ctpl, pkg, o.PackageNameReplacer)
|
||||
if err := inst.Generate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(inst.Code().Body().Parts) > 0 {
|
||||
ret[o.PackageNameReplacer.Replace(path)+".ts"] = inst.Code()
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
365
pkg/typescript/generator/generate_test.go
Normal file
365
pkg/typescript/generator/generate_test.go
Normal file
@ -0,0 +1,365 @@
|
||||
package generator_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log/slog"
|
||||
"testing"
|
||||
|
||||
"github.com/foomo/gocontemplate/pkg/contemplate"
|
||||
"github.com/foomo/sesamy-cli/pkg/typescript/generator"
|
||||
_ "github.com/foomo/sesamy-go/pkg/event/params" // force inclusion
|
||||
_ "github.com/foomo/sesamy-go/pkg/sesamy" // force inclusion
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
expectedEvent = `
|
||||
// Code generated by sesamy. DO NOT EDIT.
|
||||
import type * as github_com_foomo_sesamy_go_pkg_event_params from './github_com_foomo_sesamy_go_pkg_event_params';
|
||||
import { EventName } from './github_com_foomo_sesamy_go_pkg_sesamy';
|
||||
import { collect } from '@foomo/sesamy';
|
||||
|
||||
export const addToCart = (
|
||||
params: github_com_foomo_sesamy_go_pkg_event_params.AddToCart<github_com_foomo_sesamy_go_pkg_event_params.Item>,
|
||||
) => {
|
||||
collect({
|
||||
name: EventName.AddToCart,
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
export const pageView = (
|
||||
params: github_com_foomo_sesamy_go_pkg_event_params.PageView,
|
||||
) => {
|
||||
collect({
|
||||
name: EventName.PageView,
|
||||
params,
|
||||
});
|
||||
}
|
||||
`
|
||||
expectedParams = `
|
||||
// Code generated by sesamy. DO NOT EDIT.
|
||||
import type * as github_com_foomo_gostandards_iso4217 from './github_com_foomo_gostandards_iso4217';
|
||||
|
||||
export interface AddToCart<I> {
|
||||
currency?: github_com_foomo_gostandards_iso4217.Currency;
|
||||
value?: number;
|
||||
items?: Array<I>;
|
||||
}
|
||||
|
||||
export interface Item {
|
||||
affiliation?: string;
|
||||
coupon?: string;
|
||||
creative_name?: string;
|
||||
creative_slot?: string;
|
||||
discount?: number;
|
||||
index?: number;
|
||||
item_brand?: string;
|
||||
item_category?: string;
|
||||
item_category2?: string;
|
||||
item_category3?: string;
|
||||
item_category4?: string;
|
||||
item_category5?: string;
|
||||
item_id?: string;
|
||||
item_list_id?: string;
|
||||
item_list_name?: string;
|
||||
item_name?: string;
|
||||
item_variant?: string;
|
||||
location_id?: string;
|
||||
price?: number;
|
||||
promotion_id?: string;
|
||||
promotion_name?: string;
|
||||
quantity?: number;
|
||||
}
|
||||
|
||||
export interface PageView {
|
||||
page_title?: string;
|
||||
page_location?: string;
|
||||
}
|
||||
`
|
||||
expectedSesamy = `
|
||||
// Code generated by sesamy. DO NOT EDIT.
|
||||
|
||||
export enum EventName {
|
||||
AdImpression = "ad_impression",
|
||||
AddPaymentInfo = "add_payment_info",
|
||||
AddShippingInfo = "add_shipping_info",
|
||||
AddToCart = "add_to_cart",
|
||||
AddToWishlist = "add_to_wishlist",
|
||||
BeginCheckout = "begin_checkout",
|
||||
CampaignDetails = "campaign_details",
|
||||
Click = "click",
|
||||
EarnVirtualMoney = "earn_virtual_money",
|
||||
FileDownload = "file_download",
|
||||
FormStart = "form_start",
|
||||
FormSubmit = "form_submit",
|
||||
GenerateLead = "generate_lead",
|
||||
JoinGroup = "join_group",
|
||||
LevelEnd = "level_end",
|
||||
LevelStart = "level_start",
|
||||
LevelUp = "level_up",
|
||||
Login = "login",
|
||||
PageView = "page_view",
|
||||
PostScore = "post_score",
|
||||
Purchase = "purchase",
|
||||
Refund = "refund",
|
||||
RemoveFromCart = "remove_from_cart",
|
||||
ScreenView = "screen_view",
|
||||
Scroll = "scroll",
|
||||
Search = "search",
|
||||
SelectContent = "select_content",
|
||||
SelectItem = "select_item",
|
||||
SelectPromotion = "select_promotion",
|
||||
SessionStart = "session_start",
|
||||
Share = "share",
|
||||
SignUp = "sign_up",
|
||||
SpendVirtualCurrency = "spend_virtual_currency",
|
||||
TutorialBegin = "tutorial_begin",
|
||||
TutorialComplete = "tutorial_complete",
|
||||
UnlockAchievement = "unlock_achievement",
|
||||
UserEngagement = "user_engagement",
|
||||
VideoComplete = "video_complete",
|
||||
VideoProgress = "video_progress",
|
||||
VideoStart = "video_start",
|
||||
ViewCart = "view_cart",
|
||||
ViewItem = "view_item",
|
||||
ViewItemList = "view_item_list",
|
||||
ViewPromotion = "view_promotion",
|
||||
ViewSearchResults = "view_search_results",
|
||||
}
|
||||
|
||||
export interface Event<P> {
|
||||
name: EventName;
|
||||
params?: P;
|
||||
}
|
||||
`
|
||||
expectedISO4217 = `
|
||||
// Code generated by sesamy. DO NOT EDIT.
|
||||
|
||||
export enum Currency {
|
||||
AED = "AED",
|
||||
AFN = "AFN",
|
||||
ALL = "ALL",
|
||||
AMD = "AMD",
|
||||
ANG = "ANG",
|
||||
AOA = "AOA",
|
||||
ARS = "ARS",
|
||||
AUD = "AUD",
|
||||
AWG = "AWG",
|
||||
AZN = "AZN",
|
||||
BAM = "BAM",
|
||||
BBD = "BBD",
|
||||
BDT = "BDT",
|
||||
BGN = "BGN",
|
||||
BHD = "BHD",
|
||||
BIF = "BIF",
|
||||
BMD = "BMD",
|
||||
BND = "BND",
|
||||
BOB = "BOB",
|
||||
BOV = "BOV",
|
||||
BRL = "BRL",
|
||||
BSD = "BSD",
|
||||
BTN = "BTN",
|
||||
BWP = "BWP",
|
||||
BYN = "BYN",
|
||||
BZD = "BZD",
|
||||
CAD = "CAD",
|
||||
CDF = "CDF",
|
||||
CHE = "CHE",
|
||||
CHF = "CHF",
|
||||
CHW = "CHW",
|
||||
CLF = "CLF",
|
||||
CLP = "CLP",
|
||||
CNY = "CNY",
|
||||
COP = "COP",
|
||||
COU = "COU",
|
||||
CRC = "CRC",
|
||||
CUP = "CUP",
|
||||
CVE = "CVE",
|
||||
CZK = "CZK",
|
||||
DJF = "DJF",
|
||||
DKK = "DKK",
|
||||
DOP = "DOP",
|
||||
DZD = "DZD",
|
||||
EGP = "EGP",
|
||||
ERN = "ERN",
|
||||
ETB = "ETB",
|
||||
EUR = "EUR",
|
||||
FJD = "FJD",
|
||||
FKP = "FKP",
|
||||
GBP = "GBP",
|
||||
GEL = "GEL",
|
||||
GHS = "GHS",
|
||||
GIP = "GIP",
|
||||
GMD = "GMD",
|
||||
GNF = "GNF",
|
||||
GTQ = "GTQ",
|
||||
GYD = "GYD",
|
||||
HKD = "HKD",
|
||||
HNL = "HNL",
|
||||
HTG = "HTG",
|
||||
HUF = "HUF",
|
||||
IDR = "IDR",
|
||||
ILS = "ILS",
|
||||
INR = "INR",
|
||||
IQD = "IQD",
|
||||
IRR = "IRR",
|
||||
ISK = "ISK",
|
||||
JMD = "JMD",
|
||||
JOD = "JOD",
|
||||
JPY = "JPY",
|
||||
KES = "KES",
|
||||
KGS = "KGS",
|
||||
KHR = "KHR",
|
||||
KMF = "KMF",
|
||||
KPW = "KPW",
|
||||
KRW = "KRW",
|
||||
KWD = "KWD",
|
||||
KYD = "KYD",
|
||||
KZT = "KZT",
|
||||
LAK = "LAK",
|
||||
LBP = "LBP",
|
||||
LKR = "LKR",
|
||||
LRD = "LRD",
|
||||
LSL = "LSL",
|
||||
LYD = "LYD",
|
||||
MAD = "MAD",
|
||||
MDL = "MDL",
|
||||
MGA = "MGA",
|
||||
MKD = "MKD",
|
||||
MMK = "MMK",
|
||||
MNT = "MNT",
|
||||
MOP = "MOP",
|
||||
MRU = "MRU",
|
||||
MUR = "MUR",
|
||||
MVR = "MVR",
|
||||
MWK = "MWK",
|
||||
MXN = "MXN",
|
||||
MXV = "MXV",
|
||||
MYR = "MYR",
|
||||
MZN = "MZN",
|
||||
NAD = "NAD",
|
||||
NGN = "NGN",
|
||||
NIO = "NIO",
|
||||
NOK = "NOK",
|
||||
NPR = "NPR",
|
||||
NZD = "NZD",
|
||||
OMR = "OMR",
|
||||
PAB = "PAB",
|
||||
PEN = "PEN",
|
||||
PGK = "PGK",
|
||||
PHP = "PHP",
|
||||
PKR = "PKR",
|
||||
PLN = "PLN",
|
||||
PYG = "PYG",
|
||||
QAR = "QAR",
|
||||
RON = "RON",
|
||||
RSD = "RSD",
|
||||
RUB = "RUB",
|
||||
RWF = "RWF",
|
||||
SAR = "SAR",
|
||||
SBD = "SBD",
|
||||
SCR = "SCR",
|
||||
SDG = "SDG",
|
||||
SEK = "SEK",
|
||||
SGD = "SGD",
|
||||
SHP = "SHP",
|
||||
SLE = "SLE",
|
||||
SLL = "SLL",
|
||||
SOS = "SOS",
|
||||
SRD = "SRD",
|
||||
SSP = "SSP",
|
||||
STN = "STN",
|
||||
SVC = "SVC",
|
||||
SYP = "SYP",
|
||||
SZL = "SZL",
|
||||
THB = "THB",
|
||||
TJS = "TJS",
|
||||
TMT = "TMT",
|
||||
TND = "TND",
|
||||
TOP = "TOP",
|
||||
TRY = "TRY",
|
||||
TTD = "TTD",
|
||||
TWD = "TWD",
|
||||
TZS = "TZS",
|
||||
UAH = "UAH",
|
||||
UGX = "UGX",
|
||||
USD = "USD",
|
||||
USN = "USN",
|
||||
UYI = "UYI",
|
||||
UYU = "UYU",
|
||||
UYW = "UYW",
|
||||
UZS = "UZS",
|
||||
VED = "VED",
|
||||
VES = "VES",
|
||||
VND = "VND",
|
||||
VUV = "VUV",
|
||||
WST = "WST",
|
||||
XAF = "XAF",
|
||||
XAG = "XAG",
|
||||
XAU = "XAU",
|
||||
XBA = "XBA",
|
||||
XBB = "XBB",
|
||||
XBC = "XBC",
|
||||
XBD = "XBD",
|
||||
XCD = "XCD",
|
||||
XDR = "XDR",
|
||||
XOF = "XOF",
|
||||
XPD = "XPD",
|
||||
XPF = "XPF",
|
||||
XPT = "XPT",
|
||||
XSU = "XSU",
|
||||
XTS = "XTS",
|
||||
XUA = "XUA",
|
||||
XXX = "XXX",
|
||||
YER = "YER",
|
||||
ZAR = "ZAR",
|
||||
ZMW = "ZMW",
|
||||
ZWL = "ZWL",
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
func TestGenerate(t *testing.T) {
|
||||
cfg := &contemplate.Config{
|
||||
Packages: []*contemplate.PackageConfig{
|
||||
{
|
||||
Path: "github.com/foomo/sesamy-go/pkg/event",
|
||||
Types: []string{"AddToCart", "PageView"},
|
||||
},
|
||||
},
|
||||
}
|
||||
ctpl, err := contemplate.Load(cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
l := slog.New(slog.NewTextHandler(io.Discard, nil))
|
||||
actual, err := generator.Generate(l, ctpl)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, actual, 4)
|
||||
|
||||
t.Run("event.ts", func(t *testing.T) {
|
||||
if !assert.Equal(t, expectedEvent, "\n"+actual["github_com_foomo_sesamy_go_pkg_event.ts"].String()) {
|
||||
t.Log("\n" + actual["github_com_foomo_sesamy_go_pkg_event.ts"].String())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("sesamy.ts", func(t *testing.T) {
|
||||
if !assert.Equal(t, expectedSesamy, "\n"+actual["github_com_foomo_sesamy_go_pkg_sesamy.ts"].String()) {
|
||||
t.Log("\n" + actual["github_com_foomo_sesamy_go_pkg_sesamy.ts"].String())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("params.ts", func(t *testing.T) {
|
||||
if !assert.Equal(t, expectedParams, "\n"+actual["github_com_foomo_sesamy_go_pkg_event_params.ts"].String()) {
|
||||
t.Log("\n" + actual["github_com_foomo_sesamy_go_pkg_event_params.ts"].String())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("iso4217.ts", func(t *testing.T) {
|
||||
if !assert.Equal(t, expectedISO4217, "\n"+actual["github_com_foomo_gostandards_iso4217.ts"].String()) {
|
||||
t.Log("\n" + actual["github_com_foomo_gostandards_iso4217.ts"].String())
|
||||
}
|
||||
})
|
||||
}
|
||||
348
pkg/typescript/generator/package.go
Normal file
348
pkg/typescript/generator/package.go
Normal file
@ -0,0 +1,348 @@
|
||||
package generator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/types"
|
||||
"log/slog"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/structtag"
|
||||
"github.com/foomo/gocontemplate/pkg/assume"
|
||||
"github.com/foomo/gocontemplate/pkg/contemplate"
|
||||
"github.com/foomo/sesamy-cli/pkg/code"
|
||||
"github.com/foomo/sesamy-cli/pkg/typescript"
|
||||
"github.com/stoewer/go-strcase"
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
const FallbackType = "any"
|
||||
|
||||
type Package struct {
|
||||
l *slog.Logger
|
||||
Filename string
|
||||
goctpl *contemplate.Contemplate
|
||||
pkg *contemplate.Package
|
||||
code *code.File
|
||||
imports *typescript.Imports
|
||||
pkgNameReplacer *strings.Replacer
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ~ Constructor
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
func NewPackage(l *slog.Logger, goctpl *contemplate.Contemplate, pkg *contemplate.Package, pkgNameReplacer *strings.Replacer) *Package {
|
||||
inst := &Package{
|
||||
l: l,
|
||||
goctpl: goctpl,
|
||||
pkg: pkg,
|
||||
Filename: pkgNameReplacer.Replace(pkg.Path()) + ".ts",
|
||||
pkgNameReplacer: pkgNameReplacer,
|
||||
code: code.NewFile(),
|
||||
imports: typescript.NewImports(),
|
||||
}
|
||||
return inst
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ~ Getter
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
func (p *Package) Code() *code.File {
|
||||
return p.code
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ~ Public methods
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
func (p *Package) IsSesamyEvent(expr ast.Expr) bool {
|
||||
if ident := assume.T[*ast.Ident](expr); ident != nil {
|
||||
if typeSpec := assume.T[*ast.TypeSpec](ident.Obj.Decl); typeSpec != nil {
|
||||
if indexExpr := assume.T[*ast.IndexExpr](typeSpec.Type); indexExpr != nil {
|
||||
if selectorExpr := assume.T[*ast.SelectorExpr](indexExpr.X); selectorExpr != nil {
|
||||
if xIdent := assume.T[*ast.Ident](selectorExpr.X); xIdent != nil {
|
||||
return xIdent.Name == "sesamy" && selectorExpr.Sel.Name == "Event"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Package) Generate() error {
|
||||
// add annotations
|
||||
p.code.Annotations().Sprintf(`// Code generated by sesamy. DO NOT EDIT.`)
|
||||
|
||||
var err error
|
||||
for scopeTypeName, scopeType := range p.pkg.ScopeTypes() {
|
||||
switch t := scopeType.Type().Underlying().(type) {
|
||||
case *types.Struct:
|
||||
expr := p.pkg.LookupExpr(scopeTypeName)
|
||||
if p.IsSesamyEvent(expr) {
|
||||
err = p.renderSesamyEvent(expr)
|
||||
} else {
|
||||
err = p.renderStruct(scopeType, scopeTypeName, t)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case *types.Basic:
|
||||
p.renderTypeBasic(scopeTypeName, scopeType)
|
||||
default:
|
||||
p.l.Debug("👷 unhandled type", "type", t)
|
||||
}
|
||||
}
|
||||
|
||||
p.imports.Write(p.code.Head())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ~ Private methods
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
func (p *Package) renderTypeBasic(typeName string, typeObject types.Object) {
|
||||
res := map[string]string{}
|
||||
|
||||
for _, object := range p.goctpl.LookupTypesByType(typeObject) {
|
||||
if objectType := assume.T[*types.Const](object); objectType != nil {
|
||||
if objectTypeNamed := assume.T[*types.Named](objectType.Type()); objectTypeNamed != nil {
|
||||
if objectTypeNamed.Obj() == typeObject {
|
||||
res[strings.TrimPrefix(object.Name(), typeName)] = objectType.Val().String()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
names := maps.Keys(res)
|
||||
slices.Sort(names)
|
||||
|
||||
typeValues := &strings.Builder{}
|
||||
for _, name := range names {
|
||||
typeValues.WriteString(fmt.Sprintf("\t%s = %s,\n", name, res[name]))
|
||||
}
|
||||
|
||||
p.code.Body().Tprintn(`
|
||||
export enum {0} {
|
||||
{1}
|
||||
}`,
|
||||
typeName,
|
||||
strings.TrimSuffix(typeValues.String(), "\n"),
|
||||
)
|
||||
}
|
||||
|
||||
func (p *Package) tsASTExpr(input ast.Expr) string {
|
||||
switch t := input.(type) {
|
||||
case *ast.SelectorExpr:
|
||||
return p.tsAstType(p.pkg.Raw().TypesInfo.TypeOf(t.Sel))
|
||||
case *ast.IndexExpr:
|
||||
return fmt.Sprintf("%s<%s>", p.tsASTExpr(t.X), p.tsASTExpr(t.Index))
|
||||
case *ast.Ident:
|
||||
return t.Name
|
||||
default:
|
||||
p.l.Debug("👷 unhandled type", "type", t)
|
||||
return FallbackType
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Package) tsAstType(input types.Type) string {
|
||||
switch t := input.(type) {
|
||||
case *types.Named:
|
||||
return p.tsTypeFromNamed(t)
|
||||
default:
|
||||
p.l.Debug("👷 unhandled type", "type", t)
|
||||
return FallbackType
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Package) renderSesamyEvent(v ast.Expr) error {
|
||||
p.imports.Import("@foomo/sesamy").AddModule("collect")
|
||||
p.imports.Import("./github_com_foomo_sesamy_go_pkg_sesamy").AddModule("EventName")
|
||||
|
||||
var typeName string
|
||||
var typeType string
|
||||
if inputIdent := assume.T[*ast.Ident](v); inputIdent != nil {
|
||||
typeName = inputIdent.Name
|
||||
if inputTypeSpec := assume.T[*ast.TypeSpec](inputIdent.Obj.Decl); inputTypeSpec != nil {
|
||||
if inputIndexExpr := assume.T[*ast.IndexExpr](inputTypeSpec.Type); inputIndexExpr != nil {
|
||||
typeType = p.tsASTExpr(inputIndexExpr.Index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p.code.Body().Tprintn(`
|
||||
export const {0} = (
|
||||
params: {1},
|
||||
) => {
|
||||
collect({
|
||||
name: EventName.{2},
|
||||
params,
|
||||
});
|
||||
}`,
|
||||
strcase.LowerCamelCase(typeName),
|
||||
typeType,
|
||||
typeName,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Package) renderStruct(obj types.Object, typeName string, t *types.Struct) error {
|
||||
typeFields := &strings.Builder{}
|
||||
|
||||
for i := range t.NumFields() {
|
||||
tag := t.Tag(i)
|
||||
field := t.Field(i)
|
||||
name := field.Name()
|
||||
|
||||
tags, err := structtag.Parse(strings.Trim(tag, "`"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tagVal, err := tags.Get("json")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if value := tagVal.Value(); value != "" && value != "-" {
|
||||
name = tagVal.Name
|
||||
}
|
||||
|
||||
if tagVal.HasOption("omitempty") {
|
||||
name += "?"
|
||||
}
|
||||
|
||||
typeFields.WriteString(
|
||||
fmt.Sprintf("\t%s: %s;\n", name, p.tsType(t.Field(i).Type())),
|
||||
)
|
||||
}
|
||||
|
||||
if typeFields.Len() == 0 {
|
||||
p.code.Body().Tprintn(`
|
||||
export type {0} = {1}`,
|
||||
p.tsDecl(obj.Type()),
|
||||
"{}",
|
||||
)
|
||||
} else {
|
||||
p.code.Body().Tprintn(`
|
||||
export interface {0} {
|
||||
{1}
|
||||
}`,
|
||||
p.tsDecl(obj.Type()),
|
||||
strings.TrimSuffix(typeFields.String(), "\n"),
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Package) tsDecl(v types.Type) string {
|
||||
switch t := v.(type) {
|
||||
case *types.Named:
|
||||
return p.tsDeclFromNamed(t)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Package) tsDeclFromNamed(v *types.Named) string {
|
||||
return p.tsDeclFromTypeName(v.Obj()) + p.tsDeclFromTypeParamList(v.TypeParams())
|
||||
}
|
||||
|
||||
func (p *Package) tsDeclFromTypeName(v *types.TypeName) string {
|
||||
return v.Name()
|
||||
}
|
||||
|
||||
func (p *Package) tsDeclFromTypeParamList(v *types.TypeParamList) string {
|
||||
if v == nil {
|
||||
return ""
|
||||
}
|
||||
params := make([]string, v.Len())
|
||||
for i := range v.Len() {
|
||||
params[i] = p.tsDeclFromTypeParam(v.At(i))
|
||||
}
|
||||
|
||||
if len(params) > 0 {
|
||||
return "<" + strings.Join(params, ", ") + ">"
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *Package) tsDeclFromTypeParam(v *types.TypeParam) string {
|
||||
return v.Obj().Name()
|
||||
}
|
||||
|
||||
func (p *Package) tsType(v types.Type) string {
|
||||
switch t := v.(type) {
|
||||
case *types.Pointer:
|
||||
return p.tsType(t.Elem()) + " | undefined"
|
||||
case *types.Basic:
|
||||
return p.tsTypeFromBasicInfoType(t.Info())
|
||||
case *types.Named:
|
||||
return p.tsTypeFromNamed(t)
|
||||
case *types.TypeParam:
|
||||
return p.tsTypeFromTypeName(t.Obj())
|
||||
case *types.Slice:
|
||||
return "Array<" + p.tsType(t.Elem()) + ">"
|
||||
default:
|
||||
p.l.Debug("👷 unhandled type", "type", t)
|
||||
return FallbackType
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Package) tsTypeFromBasicInfoType(c types.BasicInfo) string {
|
||||
switch {
|
||||
case c&types.IsNumeric != 0:
|
||||
return "number"
|
||||
case c&types.IsString != 0:
|
||||
return "string"
|
||||
case c&types.IsBoolean != 0:
|
||||
return "boolean"
|
||||
default:
|
||||
p.l.Debug("👷 unhandled type", "type", c)
|
||||
return FallbackType
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Package) tsTypeFromTypeName(t *types.TypeName) string {
|
||||
name := t.Name()
|
||||
// add package prefix is needed
|
||||
if t.Pkg().Path() != p.pkg.Path() {
|
||||
// retrieve ts package name
|
||||
tsPkg := p.pkgNameReplacer.Replace(t.Pkg().Path())
|
||||
// ensure import exists
|
||||
p.imports.Import("./" + tsPkg).SetDefault("type * as " + tsPkg)
|
||||
name = tsPkg + "." + name
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func (p *Package) tsTypeFromNamed(t *types.Named) string {
|
||||
return p.tsTypeFromTypeName(t.Obj()) // + b.tsTypeFromTypeParamList(t.TypeParams())
|
||||
}
|
||||
|
||||
// func (p *Package) tsTypeFromTypeParamList(t *types.TypeParamList) string {
|
||||
// if t == nil {
|
||||
// return ""
|
||||
// }
|
||||
// params := make([]string, t.Len())
|
||||
// for i := range t.Len() {
|
||||
// params[i] = p.tsTypeFromTypeParam(t.At(i))
|
||||
// }
|
||||
//
|
||||
// if len(params) > 0 {
|
||||
// return "<" + strings.Join(params, ", ") + ">"
|
||||
// }
|
||||
//
|
||||
// return ""
|
||||
// }
|
||||
|
||||
// func (p *Package) tsTypeFromTypeParam(t *types.TypeParam) string {
|
||||
// return p.tsType(t)
|
||||
// }
|
||||
110
pkg/typescript/import.go
Normal file
110
pkg/typescript/import.go
Normal file
@ -0,0 +1,110 @@
|
||||
package typescript
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
type Import struct {
|
||||
def string
|
||||
location string
|
||||
// types indicates if the given module is a type
|
||||
types map[string]bool
|
||||
// modules map of map[name]alias
|
||||
modules map[string]string
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ~ Constructor
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
func NewImport(location string) *Import {
|
||||
return &Import{
|
||||
location: location,
|
||||
types: make(map[string]bool),
|
||||
modules: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ~ Getter
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
func (i *Import) Default() string {
|
||||
return i.def
|
||||
}
|
||||
|
||||
func (i *Import) Location() string {
|
||||
return i.def
|
||||
}
|
||||
|
||||
func (i *Import) SetDefault(v string) {
|
||||
i.def = v
|
||||
}
|
||||
|
||||
func (i *Import) Modules() []string {
|
||||
ret := maps.Keys(i.modules)
|
||||
slices.Sort(ret)
|
||||
return ret
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ~ Public methods
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
func (i *Import) IsType(name string) bool {
|
||||
v, ok := i.types[name]
|
||||
return ok && v
|
||||
}
|
||||
|
||||
func (i *Import) Alias(name string) string {
|
||||
if v, ok := i.modules[name]; ok {
|
||||
return v
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (i *Import) AddModule(name string) {
|
||||
i.modules[name] = ""
|
||||
}
|
||||
|
||||
func (i *Import) AddModuleWithAlias(name, alias string) {
|
||||
i.modules[name] = alias
|
||||
}
|
||||
|
||||
func (i *Import) AddTypeModule(name string) {
|
||||
i.types[name] = true
|
||||
i.modules[name] = ""
|
||||
}
|
||||
|
||||
func (i *Import) AddTypeModuleWithAlias(name, alias string) {
|
||||
i.types[name] = true
|
||||
i.modules[name] = alias
|
||||
}
|
||||
|
||||
func (i *Import) String() string {
|
||||
var defModules []string
|
||||
|
||||
if i.def != "" {
|
||||
defModules = append(defModules, i.def)
|
||||
}
|
||||
|
||||
if len(i.modules) > 0 {
|
||||
var modules []string
|
||||
for name, alias := range i.modules {
|
||||
if alias != "" {
|
||||
name += " as " + alias
|
||||
}
|
||||
if i.IsType(name) {
|
||||
name = "type " + name
|
||||
}
|
||||
modules = append(modules, name)
|
||||
}
|
||||
defModules = append(defModules, "{ "+strings.Join(modules, ", ")+" }")
|
||||
}
|
||||
|
||||
return fmt.Sprintf("import %s from '%s';", strings.Join(defModules, ", "), i.location)
|
||||
}
|
||||
36
pkg/typescript/imports.go
Normal file
36
pkg/typescript/imports.go
Normal file
@ -0,0 +1,36 @@
|
||||
package typescript
|
||||
|
||||
import (
|
||||
"github.com/foomo/sesamy-cli/pkg/code"
|
||||
)
|
||||
|
||||
type Imports struct {
|
||||
imports map[string]*Import
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ~ Constructor
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
func NewImports() *Imports {
|
||||
return &Imports{
|
||||
imports: map[string]*Import{},
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ~ Public methods
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
func (i *Imports) Import(location string) *Import {
|
||||
if _, ok := i.imports[location]; !ok {
|
||||
i.imports[location] = NewImport(location)
|
||||
}
|
||||
return i.imports[location]
|
||||
}
|
||||
|
||||
func (i *Imports) Write(f *code.Section) {
|
||||
for key := range i.imports {
|
||||
f.Sprintf(i.imports[key].String())
|
||||
}
|
||||
}
|
||||
43
pkg/typescript/tag.go
Normal file
43
pkg/typescript/tag.go
Normal file
@ -0,0 +1,43 @@
|
||||
package typescript
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/structtag"
|
||||
)
|
||||
|
||||
func ParseTagName(value string) (string, error) {
|
||||
tags, err := structtag.Parse(strings.Trim(value, "`"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
tag, err := tags.Get("json")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if tag.Value() != "" && tag.Value() != "-" {
|
||||
return strings.Split(tag.Value(), ",")[0], nil
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func ParseTagOmitempty(value string) (string, error) {
|
||||
tags, err := structtag.Parse(strings.Trim(value, "`"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
tag, err := tags.Get("json")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if tag.Value() != "" && tag.Value() != "-" {
|
||||
return strings.Split(tag.Value(), ",")[0], nil
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
@ -30,9 +30,9 @@ typescript:
|
||||
tagmanager:
|
||||
packages:
|
||||
- path: 'github.com/foomo/sesamy-cli/_example/server'
|
||||
output_path: './_example/client/types.d.ts'
|
||||
exclude_files:
|
||||
- item.go
|
||||
events:
|
||||
- PageView
|
||||
- SelectItem
|
||||
prefixes:
|
||||
client: ''
|
||||
folder: ''
|
||||
@ -8,6 +8,8 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
testingx "github.com/foomo/go/testing"
|
||||
tagx "github.com/foomo/go/testing/tag"
|
||||
"github.com/foomo/sesamy-cli/pkg/tagmanager"
|
||||
"github.com/foomo/sesamy-cli/pkg/tagmanager/trigger"
|
||||
"github.com/foomo/sesamy-cli/pkg/tagmanager/variable"
|
||||
@ -18,7 +20,7 @@ import (
|
||||
)
|
||||
|
||||
func TestNewClient_Server(t *testing.T) {
|
||||
t.Skip()
|
||||
testingx.Tags(t, tagx.Skip)
|
||||
|
||||
c, err := tagmanager.NewClient(
|
||||
context.TODO(),
|
||||
@ -101,7 +103,7 @@ func TestNewClient_Server(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewClient_Web(t *testing.T) {
|
||||
t.Skip()
|
||||
testingx.Tags(t, tagx.Skip)
|
||||
|
||||
c, err := tagmanager.NewClient(
|
||||
context.TODO(),
|
||||
@ -150,7 +152,7 @@ func TestNewClient_Web(t *testing.T) {
|
||||
ContainerId: c.ContainerID(),
|
||||
WorkspaceId: c.WorkspaceID(),
|
||||
Name: name,
|
||||
Notes: c.Notes(),
|
||||
Notes: c.Notes(nil),
|
||||
})
|
||||
if r, err := cmd.Do(); assert.NoError(t, err) {
|
||||
dump(t, r)
|
||||
@ -211,7 +213,7 @@ func TestNewClient_Web(t *testing.T) {
|
||||
WorkspaceId: c.WorkspaceID(),
|
||||
ParentFolderId: folderID,
|
||||
Name: "Constant." + name,
|
||||
Notes: c.Notes(),
|
||||
Notes: c.Notes(nil),
|
||||
Parameter: []*gtagmanager.Parameter{
|
||||
{
|
||||
Key: "value",
|
||||
@ -263,7 +265,7 @@ func TestNewClient_Web(t *testing.T) {
|
||||
ParentFolderId: folderID,
|
||||
Type: "customEvent",
|
||||
Name: "Event." + name,
|
||||
Notes: c.Notes(),
|
||||
Notes: c.Notes(nil),
|
||||
CustomEventFilter: []*gtagmanager.Condition{
|
||||
{
|
||||
Parameter: []*gtagmanager.Parameter{
|
||||
@ -323,7 +325,7 @@ func TestNewClient_Web(t *testing.T) {
|
||||
WorkspaceId: c.WorkspaceID(),
|
||||
ParentFolderId: folderID,
|
||||
Name: "GA4." + name,
|
||||
Notes: c.Notes(),
|
||||
Notes: c.Notes(nil),
|
||||
Parameter: []*gtagmanager.Parameter{
|
||||
{
|
||||
Type: "boolean",
|
||||
@ -395,25 +397,6 @@ func TestNewClient_Web(t *testing.T) {
|
||||
// ~ Private methods
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
// func eventParameters(event any) []string {
|
||||
// if event == nil {
|
||||
// return nil
|
||||
// }
|
||||
// var res []string
|
||||
// v := reflect.TypeOf(event)
|
||||
//
|
||||
// if v.Kind() == reflect.Ptr {
|
||||
// v = v.Elem()
|
||||
// }
|
||||
// for i := range v.NumField() {
|
||||
// tag := v.Field(i).Tag.Get("json")
|
||||
// if tag != "" && tag != "-" {
|
||||
// res = append(res, strings.Split(tag, ",")[0])
|
||||
// }
|
||||
// }
|
||||
// return res
|
||||
// }
|
||||
|
||||
func dump(t *testing.T, i interface{ MarshalJSON() ([]byte, error) }) {
|
||||
t.Helper()
|
||||
var ret bytes.Buffer
|
||||
Loading…
Reference in New Issue
Block a user