mirror of
https://github.com/foomo/sesamy-cli.git
synced 2025-10-16 12:35:36 +00:00
wip: need added
This commit is contained in:
parent
d2c61b5a25
commit
ab48d6326a
@ -77,6 +77,7 @@ linters-settings:
|
|||||||
- name: unhandled-error
|
- name: unhandled-error
|
||||||
arguments:
|
arguments:
|
||||||
- "fmt.Println"
|
- "fmt.Println"
|
||||||
|
- "strings.Builder.WriteString"
|
||||||
# TODO remove
|
# TODO remove
|
||||||
- name: deep-exit
|
- name: deep-exit
|
||||||
disabled: true
|
disabled: true
|
||||||
|
|||||||
96
README.md
96
README.md
@ -31,23 +31,97 @@ google:
|
|||||||
|
|
||||||
gtm:
|
gtm:
|
||||||
account_id: 6099238525
|
account_id: 6099238525
|
||||||
server:
|
|
||||||
container_id: 175348980
|
|
||||||
workspace_id: 10
|
|
||||||
measurement_id: GTM-5NWPR4QW
|
|
||||||
|
|
||||||
web:
|
web:
|
||||||
container_id: 175355532
|
container_id: 175355532
|
||||||
workspace_id: 23
|
|
||||||
measurement_id: GTM-57BHX34G
|
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:
|
packages:
|
||||||
- path: "github.com/foomo/sesamy-cli/_example/server"
|
- path: 'github.com/username/repository/event'
|
||||||
output_path: "./_example/client/types.d.ts"
|
events:
|
||||||
indent: "\t"
|
- Custom
|
||||||
|
- path: 'github.com/foomo/sesamy-go/event'
|
||||||
|
events:
|
||||||
|
- PageView
|
||||||
|
- SelectItem
|
||||||
|
output_path: '/path/to/index.ts'
|
||||||
|
|
||||||
|
tagmanager:
|
||||||
|
packages:
|
||||||
|
- path: 'github.com/username/repository/event'
|
||||||
|
events:
|
||||||
|
- Custom
|
||||||
|
- path: 'github.com/foomo/sesamy-go/event'
|
||||||
|
events:
|
||||||
|
- 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
|
## 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"`
|
|
||||||
}
|
|
||||||
@ -78,6 +78,22 @@ var tagmanagerServerCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mpv2Client *tagmanager2.Client
|
||||||
|
{
|
||||||
|
name := p.ClientName("Measurement Protocol GA4")
|
||||||
|
if mpv2Client, err = c.UpsertClient(client.NewMPv2(name)); 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var ga4Client *tagmanager2.Client
|
var ga4Client *tagmanager2.Client
|
||||||
{
|
{
|
||||||
name := p.ClientName("Google Analytics GA4")
|
name := p.ClientName("Google Analytics GA4")
|
||||||
@ -96,7 +112,7 @@ var tagmanagerServerCmd = &cobra.Command{
|
|||||||
|
|
||||||
{
|
{
|
||||||
name := p.Tags.ServerGA4EventName("Google Analytics GA4")
|
name := p.Tags.ServerGA4EventName("Google Analytics GA4")
|
||||||
if _, err := c.UpsertTag(client2.NewServerGA4Event(name, ga4MeasurementID, ga4ClientTrigger)); err != nil {
|
if _, err := c.UpsertTag(client2.NewServerGA4Event(name, ga4MeasurementID, ga4ClientTrigger, mpv2ClientTrigger)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ var tagmanagerWebCmd = &cobra.Command{
|
|||||||
clientCredentialsOption = option.WithCredentialsJSON([]byte(cfg.Google.CredentialsJSON))
|
clientCredentialsOption = option.WithCredentialsJSON([]byte(cfg.Google.CredentialsJSON))
|
||||||
}
|
}
|
||||||
|
|
||||||
eventParameters, err := internal.GetEventParameters(cfg.Tagmanager)
|
eventParameters, err := internal.GetStructTypeParameters(cmd.Context(), cfg.Tagmanager.Packages)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,13 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gzuidhof/tygo/tygo"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/foomo/sesamy-cli/internal"
|
||||||
|
"github.com/foomo/sesamy-cli/pkg/typescript"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/pterm/pterm"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -11,14 +17,32 @@ var typescriptCmd = &cobra.Command{
|
|||||||
Short: "Generate typescript events",
|
Short: "Generate typescript events",
|
||||||
PersistentPreRunE: preRunReadConfig,
|
PersistentPreRunE: preRunReadConfig,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
gen := tygo.New(&tygo.Config{
|
|
||||||
Packages: cfg.Typescript.Packages,
|
eventTypes, err := internal.GetStructTypes(cmd.Context(), cfg.Typescript.Packages)
|
||||||
})
|
if err != nil {
|
||||||
for k, v := range cfg.Typescript.TypeMappings {
|
return err
|
||||||
gen.SetTypeMapping(k, v)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return gen.Generate()
|
code, err := typescript.Generate(eventTypes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
outPath, err := filepath.Abs(cfg.Typescript.OutputPath)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to get output path")
|
||||||
|
}
|
||||||
|
pterm.Info.Printfln("Generated typescript code to: %s", outPath)
|
||||||
|
|
||||||
|
if err = os.MkdirAll(filepath.Dir(outPath), os.ModePerm); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to create typescript output directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = os.WriteFile(outPath, []byte(code), 0600); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to write typescript code")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
15
go.mod
15
go.mod
@ -2,9 +2,12 @@ module github.com/foomo/sesamy-cli
|
|||||||
|
|
||||||
go 1.22.2
|
go 1.22.2
|
||||||
|
|
||||||
|
//replace github.com/foomo/sesamy-go => ../sesamy-go
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fatih/structtag v1.2.0
|
github.com/fatih/structtag v1.2.0
|
||||||
github.com/gzuidhof/tygo v0.2.14
|
github.com/foomo/go v0.0.3
|
||||||
|
github.com/foomo/sesamy-go v0.1.34-0.20240515111745-453e5159a1e7
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/pterm/pterm v0.12.79
|
github.com/pterm/pterm v0.12.79
|
||||||
@ -12,20 +15,22 @@ require (
|
|||||||
github.com/spf13/viper v1.18.2
|
github.com/spf13/viper v1.18.2
|
||||||
github.com/stoewer/go-strcase v1.3.0
|
github.com/stoewer/go-strcase v1.3.0
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
|
||||||
golang.org/x/tools v0.21.0
|
golang.org/x/tools v0.21.0
|
||||||
google.golang.org/api v0.178.0
|
google.golang.org/api v0.180.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
atomicgo.dev/cursor v0.2.0 // indirect
|
atomicgo.dev/cursor v0.2.0 // indirect
|
||||||
atomicgo.dev/keyboard v0.2.9 // indirect
|
atomicgo.dev/keyboard v0.2.9 // indirect
|
||||||
atomicgo.dev/schedule v0.1.0 // 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/auth/oauth2adapt v0.2.2 // indirect
|
||||||
cloud.google.com/go/compute/metadata v0.3.0 // indirect
|
cloud.google.com/go/compute/metadata v0.3.0 // indirect
|
||||||
github.com/containerd/console v1.0.3 // indirect
|
github.com/containerd/console v1.0.3 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // 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/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/go-logr/logr v1.4.1 // indirect
|
github.com/go-logr/logr v1.4.1 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // 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 v1.24.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace 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.11.0 // indirect
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
|
||||||
golang.org/x/crypto v0.23.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/mod v0.17.0 // indirect
|
||||||
golang.org/x/net v0.25.0 // indirect
|
golang.org/x/net v0.25.0 // indirect
|
||||||
golang.org/x/oauth2 v0.20.0 // indirect
|
golang.org/x/oauth2 v0.20.0 // indirect
|
||||||
|
|||||||
23
go.sum
23
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 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs=
|
||||||
atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU=
|
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 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.4.1 h1:Z7YNIhlWRtrnKlZke7z3GMqzvuYzdc2z98F9D1NV5Hg=
|
||||||
cloud.google.com/go/auth v0.3.0/go.mod h1:lBv6NKTWp8E3LPzmO1TbiiRKc4drLOfHsgmlH9ogv5w=
|
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 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4=
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q=
|
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=
|
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
|
||||||
@ -42,6 +42,12 @@ github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4
|
|||||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
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/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.1.34-0.20240515111745-453e5159a1e7 h1:SBK4F4iTYuNNYx7ZJa46xGafifZtYNa4F9ZzhgCiJzw=
|
||||||
|
github.com/foomo/sesamy-go v0.1.34-0.20240515111745-453e5159a1e7/go.mod h1:zeYfOTHDzH9cQF8UjWmOUrMoPUM6LlvmY7IrliA9roQ=
|
||||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
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/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
@ -88,8 +94,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.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
|
||||||
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
|
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
|
||||||
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
|
github.com/gookit/color v1.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 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
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=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
@ -161,7 +165,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.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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
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.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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
@ -187,10 +190,8 @@ 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/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 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
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/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
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=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
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-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.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
@ -269,8 +270,8 @@ golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
|
|||||||
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
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-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/api v0.178.0 h1:yoW/QMI4bRVCHF+NWOTa4cL8MoWL3Jnuc7FlcFF91Ok=
|
google.golang.org/api v0.180.0 h1:M2D87Yo0rGBPWpo1orwfCLehUUL6E7/TYe5gvMQWDh4=
|
||||||
google.golang.org/api v0.178.0/go.mod h1:84/k2v8DFpDRebpGcooklv/lais3MEfqpaBLA12gl2U=
|
google.golang.org/api v0.180.0/go.mod h1:51AiyoEg1MJPSZ9zvklA8VnRILPXxn1iVen9v25XHAE=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
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/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-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
|||||||
@ -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
|
|
||||||
}
|
|
||||||
433
internal/reflect.go
Normal file
433
internal/reflect.go
Normal file
@ -0,0 +1,433 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
"go/types"
|
||||||
|
"maps"
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
"github.com/foomo/sesamy-cli/pkg/config"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/pterm/pterm"
|
||||||
|
"github.com/stoewer/go-strcase"
|
||||||
|
"golang.org/x/tools/go/packages"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetStructTypes(ctx context.Context, cfg config.Packages) (map[string]*types.Struct, error) {
|
||||||
|
ret := map[string]*types.Struct{}
|
||||||
|
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
pkgs, err := packages.Load(&packages.Config{
|
||||||
|
Mode: packages.NeedName | packages.NeedTypesInfo |
|
||||||
|
packages.NeedFiles | packages.NeedImports | packages.NeedDeps |
|
||||||
|
packages.NeedModule | packages.NeedTypes | packages.NeedSyntax,
|
||||||
|
Context: ctx,
|
||||||
|
Logf: func(format string, args ...any) {
|
||||||
|
pterm.Debug.Printfln(format, args...)
|
||||||
|
},
|
||||||
|
Fset: fset,
|
||||||
|
}, cfg.PackageNames()...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pkg := range pkgs {
|
||||||
|
if len(pkg.Errors) > 0 {
|
||||||
|
return nil, errors.Wrap(pkg.Errors[0], "packages contain errors")
|
||||||
|
}
|
||||||
|
packageStructs, err := getPackageStructs(cfg, pkg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
maps.Copy(ret, packageStructs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetStructTypeParameters(ctx context.Context, cfg config.Packages) (map[string][]string, error) {
|
||||||
|
typs, err := GetStructTypes(ctx, cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := map[string][]string{}
|
||||||
|
for name, t := range typs {
|
||||||
|
var fields []string
|
||||||
|
for i := range t.NumFields() {
|
||||||
|
tag, err := ParseTag(t.Tag(i))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if tag != "" {
|
||||||
|
fields = append(fields, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret[strcase.SnakeCase(name)] = fields
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Struct struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Attributes []Attribute `json:"attributes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Attribute struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Tags map[string]string `json:"tags,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
Scanner struct {
|
||||||
|
cfg *Config
|
||||||
|
pkgs []*packages.Package
|
||||||
|
Packages map[string]*ScannerPackage
|
||||||
|
}
|
||||||
|
ScannerPackage struct {
|
||||||
|
pkg *packages.Package
|
||||||
|
Name string
|
||||||
|
PkgPath string
|
||||||
|
Imports map[*types.Package][]string
|
||||||
|
Scope ScannerScope
|
||||||
|
Values ScannerValues
|
||||||
|
}
|
||||||
|
ScannerScope map[string]types.Type
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s ScannerScope) LookupUnderlyingTypeName(name string) map[string]types.Type {
|
||||||
|
ret := map[string]types.Type{}
|
||||||
|
for i, i2 := range s {
|
||||||
|
if x, ok := i2.(*types.Named); ok && i != name && x.Obj().Name() == name {
|
||||||
|
ret[i] = i2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
Config struct {
|
||||||
|
Packages []*ConfigPackage
|
||||||
|
}
|
||||||
|
ConfigPackage struct {
|
||||||
|
Path string
|
||||||
|
Names []string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Config) PackageNames(path string) []string {
|
||||||
|
for _, configPackage := range c.Packages {
|
||||||
|
if configPackage.Path == path {
|
||||||
|
return configPackage.Names
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) PackagePaths() []string {
|
||||||
|
ret := make([]string, len(c.Packages))
|
||||||
|
for i, p := range c.Packages {
|
||||||
|
ret[i] = p.Path
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewScanner(cfg *Config) *Scanner {
|
||||||
|
return &Scanner{
|
||||||
|
cfg: cfg,
|
||||||
|
Packages: map[string]*ScannerPackage{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) Scan(ctx context.Context) error {
|
||||||
|
var err error
|
||||||
|
// load packages
|
||||||
|
s.pkgs, err = packages.Load(&packages.Config{
|
||||||
|
Mode: packages.NeedName | packages.NeedTypesInfo |
|
||||||
|
packages.NeedFiles | packages.NeedImports | packages.NeedDeps |
|
||||||
|
packages.NeedModule | packages.NeedTypes | packages.NeedSyntax,
|
||||||
|
Context: ctx,
|
||||||
|
// Fset: token.NewFileSet(),
|
||||||
|
}, s.cfg.PackagePaths()...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate packages
|
||||||
|
for _, pkg := range s.pkgs {
|
||||||
|
// retrieve requested packages names
|
||||||
|
if names := s.cfg.PackageNames(pkg.PkgPath); len(names) > 0 {
|
||||||
|
s.pkgPackage(pkg, names...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// ~ Private methods
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func (s *Scanner) pkgPackage(pkg *packages.Package, names ...string) {
|
||||||
|
if _, ok := s.Packages[pkg.PkgPath]; !ok {
|
||||||
|
s.Packages[pkg.PkgPath] = NewScannerPackage(pkg)
|
||||||
|
}
|
||||||
|
// add request scopes
|
||||||
|
added := s.Packages[pkg.PkgPath].typesScope(names)
|
||||||
|
|
||||||
|
// check underlying added scopes
|
||||||
|
for _, name := range added {
|
||||||
|
s.typesType(pkg, s.Packages[pkg.PkgPath].Scope[name].Underlying())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) typesType(pkg *packages.Package, v types.Type) {
|
||||||
|
switch t := v.(type) {
|
||||||
|
case *types.Struct:
|
||||||
|
// iterate fields
|
||||||
|
for i := range t.NumFields() {
|
||||||
|
s.typesVar(pkg, t.Field(i))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
fmt.Println(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) typesVar(pkg *packages.Package, v *types.Var) {
|
||||||
|
if !v.Exported() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch t := v.Type().(type) {
|
||||||
|
case *types.Named:
|
||||||
|
if p, ok := pkg.Imports[v.Pkg().Path()]; ok {
|
||||||
|
s.pkgPackage(p, t.Obj().Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScannerValues map[string]*ast.ValueSpec
|
||||||
|
|
||||||
|
func (s ScannerValues) Lookup(name string) *ast.ValueSpec {
|
||||||
|
return s[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewScannerPackage(pkg *packages.Package) *ScannerPackage {
|
||||||
|
values := ScannerValues{}
|
||||||
|
for _, file := range pkg.Syntax {
|
||||||
|
for _, decl := range file.Decls {
|
||||||
|
if genDecl, ok := decl.(*ast.GenDecl); ok {
|
||||||
|
for _, spec := range genDecl.Specs {
|
||||||
|
if valueSpec, ok := spec.(*ast.ValueSpec); ok && len(valueSpec.Names) > 0 {
|
||||||
|
values[valueSpec.Names[0].Name] = valueSpec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ScannerPackage{
|
||||||
|
pkg: pkg,
|
||||||
|
Name: pkg.Name,
|
||||||
|
PkgPath: pkg.PkgPath,
|
||||||
|
Imports: map[*types.Package][]string{},
|
||||||
|
Scope: ScannerScope{},
|
||||||
|
Values: values,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ScannerPackage) typesScope(names []string) []string {
|
||||||
|
var added []string
|
||||||
|
scope := s.pkg.Types.Scope()
|
||||||
|
|
||||||
|
// iterate requested names
|
||||||
|
for _, name := range names {
|
||||||
|
// check if already within the local scope
|
||||||
|
if _, ok := s.Scope[name]; !ok {
|
||||||
|
// lookup scope object
|
||||||
|
if obj := scope.Lookup(name); obj != nil {
|
||||||
|
// add type to local scope
|
||||||
|
s.Scope[name] = obj.Type()
|
||||||
|
// scan the underlying type
|
||||||
|
s.typesType(obj.Type().Underlying())
|
||||||
|
// append to added scopes
|
||||||
|
added = append(added, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME find underlying type usages e.g. `const Foo <name>`
|
||||||
|
for _, i := range scope.Names() {
|
||||||
|
child := scope.Lookup(i)
|
||||||
|
if x, ok := child.Type().(*types.Named); ok && x.Obj().Name() == name {
|
||||||
|
s.Scope[i] = x
|
||||||
|
added = append(added, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return added
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ScannerPackage) typesType(v types.Type) {
|
||||||
|
switch t := v.(type) {
|
||||||
|
case *types.Struct:
|
||||||
|
for i := range t.NumFields() {
|
||||||
|
s.typesVar(t.Field(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ScannerPackage) typesVar(v *types.Var) {
|
||||||
|
if v.Exported() {
|
||||||
|
if p, ok := v.Type().(*types.Named); ok {
|
||||||
|
pkg := p.Obj().Pkg().Path()
|
||||||
|
if s.pkg.PkgPath != pkg {
|
||||||
|
if tv, ok := v.Type().(*types.Named); ok {
|
||||||
|
typeName := tv.Obj().Name()
|
||||||
|
if !slices.Contains(s.Imports[v.Pkg()], typeName) {
|
||||||
|
s.Imports[v.Pkg()] = append(s.Imports[v.Pkg()], typeName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pterm.Debug.Println("unhandled typeVar")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
type Package struct {
|
||||||
|
pkg *packages.Package
|
||||||
|
Types []*Type `json:"s,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Type struct {
|
||||||
|
Spec *ast.TypeSpec
|
||||||
|
TypeInfo types.TypeAndValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPackage(pkg *packages.Package) *Package {
|
||||||
|
inst := &Package{
|
||||||
|
pkg: pkg,
|
||||||
|
}
|
||||||
|
for _, file := range pkg.Syntax {
|
||||||
|
ast.Inspect(file, inst.astNode)
|
||||||
|
}
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Package) Name() string {
|
||||||
|
return s.pkg.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Package) Path() string {
|
||||||
|
return s.pkg.PkgPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// ~ Private methods
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func (s *Package) astNode(n ast.Node) bool {
|
||||||
|
switch t := n.(type) {
|
||||||
|
case *ast.File:
|
||||||
|
for _, d := range t.Decls {
|
||||||
|
s.astDecl(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Package) astDecl(v ast.Decl) {
|
||||||
|
switch t := v.(type) {
|
||||||
|
case *ast.GenDecl:
|
||||||
|
s.astGenDecl(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Package) astGenDecl(v *ast.GenDecl) {
|
||||||
|
switch v.Tok {
|
||||||
|
case token.IMPORT:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
for _, spec := range v.Specs {
|
||||||
|
s.astSpec(spec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Package) astSpec(v ast.Spec) {
|
||||||
|
switch t := v.(type) {
|
||||||
|
case *ast.TypeSpec:
|
||||||
|
if t.Name.IsExported() {
|
||||||
|
s.astTypeSpec(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Package) astTypeSpec(v *ast.TypeSpec) {
|
||||||
|
r := &Type{Spec: v}
|
||||||
|
ast.Inspect(v, s.astNode)
|
||||||
|
switch t := v.Type.(type) {
|
||||||
|
case *ast.IndexExpr:
|
||||||
|
if value, ok := s.typeInfo(t.Index); ok {
|
||||||
|
r.TypeInfo = value
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.Types = append(s.Types, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Package) typeInfo(n ast.Expr) (types.TypeAndValue, bool) {
|
||||||
|
v, ok := s.pkg.TypesInfo.Types[n]
|
||||||
|
return v, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPackageStructs(cfg config.Packages, pkg *packages.Package) (map[string]*types.Struct, error) {
|
||||||
|
ret := map[string]*types.Struct{}
|
||||||
|
if len(pkg.GoFiles) == 0 {
|
||||||
|
return nil, fmt.Errorf("no input go files for package index")
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgCfg, err := cfg.PackageConfig(pkg.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range pkg.Syntax {
|
||||||
|
ast.Inspect(file, func(n ast.Node) bool {
|
||||||
|
// GenDecl can be an import, type, var, or const expression
|
||||||
|
if genDecl, ok := n.(*ast.GenDecl); ok {
|
||||||
|
if genDecl.Tok == token.IMPORT {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, spec := range genDecl.Specs {
|
||||||
|
// e.g. "type Foo struct {}" or "type Bar = string"
|
||||||
|
if typeSpec, ok := spec.(*ast.TypeSpec); ok && typeSpec.Name.IsExported() {
|
||||||
|
if t, ok := typeSpec.Type.(*ast.IndexExpr); ok {
|
||||||
|
x := pkg.TypesInfo.Types[t]
|
||||||
|
fmt.Println(x)
|
||||||
|
}
|
||||||
|
if !pkgCfg.ExportEvent(typeSpec.Name.String()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if indexExpr, ok := typeSpec.Type.(*ast.IndexExpr); ok {
|
||||||
|
if indexType, ok := pkg.TypesInfo.Types[indexExpr.Index]; ok {
|
||||||
|
if s, ok := indexType.Type.Underlying().(*types.Struct); ok {
|
||||||
|
ret[typeSpec.Name.String()] = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
57
internal/reflect_test.go
Normal file
57
internal/reflect_test.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package internal_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/foomo/sesamy-cli/internal"
|
||||||
|
"github.com/foomo/sesamy-cli/pkg/config"
|
||||||
|
_ "github.com/foomo/sesamy-go" // force inclusion
|
||||||
|
_ "github.com/foomo/sesamy-go/event/params" // force inclusion
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetEventParameters(t *testing.T) {
|
||||||
|
params, err := internal.GetStructTypeParameters(context.TODO(), config.Packages{
|
||||||
|
{
|
||||||
|
Path: "github.com/foomo/sesamy-go/event",
|
||||||
|
Events: []string{
|
||||||
|
"PageView",
|
||||||
|
"SelectItem",
|
||||||
|
"AddToCart",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
actual, err := json.Marshal(params)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := `{"add_to_cart":["currency","value","items"],"page_view":["page_title","page_location"],"select_item":["item_list_id","item_list_name","items"]}`
|
||||||
|
if !assert.JSONEq(t, expected, string(actual)) {
|
||||||
|
t.Log(string(actual))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScanner(t *testing.T) {
|
||||||
|
scanner := internal.NewScanner(&internal.Config{
|
||||||
|
Packages: []*internal.ConfigPackage{
|
||||||
|
{
|
||||||
|
Path: "github.com/foomo/sesamy-go/event",
|
||||||
|
Names: []string{"PageView"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
err := scanner.Scan(context.TODO())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// actual, err := json.Marshal(scanner)
|
||||||
|
// require.NoError(t, err)
|
||||||
|
//
|
||||||
|
// expected := `{"add_to_cart":["currency","value","items"],"page_view":["page_title","page_location"],"select_item":["item_list_id","item_list_name","items"]}`
|
||||||
|
// if !assert.JSONEq(t, expected, string(actual)) {
|
||||||
|
// t.Log(string(actual))
|
||||||
|
// }
|
||||||
|
}
|
||||||
25
internal/utils.go
Normal file
25
internal/utils.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/fatih/structtag"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ParseTag(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
|
||||||
|
}
|
||||||
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,6 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gzuidhof/tygo/tygo"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Tagmanager struct {
|
type Tagmanager struct {
|
||||||
Packages []*tygo.PackageConfig `yaml:"packages"`
|
Packages Packages `yaml:"packages"`
|
||||||
TypeMappings map[string]string `yaml:"type_mappings"`
|
Prefixes TagmanagerPrefixes `yaml:"prefixes"`
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,27 +1,6 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gzuidhof/tygo/tygo"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Typescript struct {
|
type Typescript struct {
|
||||||
Packages []*tygo.PackageConfig `yaml:"packages"`
|
Packages Packages `yaml:"packages"`
|
||||||
TypeMappings map[string]string `yaml:"type_mappings"`
|
OutputPath string `yaml:"output_path"`
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|||||||
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",
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,6 +8,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"testing"
|
"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"
|
||||||
"github.com/foomo/sesamy-cli/pkg/tagmanager/trigger"
|
"github.com/foomo/sesamy-cli/pkg/tagmanager/trigger"
|
||||||
"github.com/foomo/sesamy-cli/pkg/tagmanager/variable"
|
"github.com/foomo/sesamy-cli/pkg/tagmanager/variable"
|
||||||
@ -18,7 +20,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestNewClient_Server(t *testing.T) {
|
func TestNewClient_Server(t *testing.T) {
|
||||||
t.Skip()
|
testingx.Tags(t, tagx.Skip)
|
||||||
|
|
||||||
c, err := tagmanager.NewClient(
|
c, err := tagmanager.NewClient(
|
||||||
context.TODO(),
|
context.TODO(),
|
||||||
@ -101,7 +103,7 @@ func TestNewClient_Server(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNewClient_Web(t *testing.T) {
|
func TestNewClient_Web(t *testing.T) {
|
||||||
t.Skip()
|
testingx.Tags(t, tagx.Skip)
|
||||||
|
|
||||||
c, err := tagmanager.NewClient(
|
c, err := tagmanager.NewClient(
|
||||||
context.TODO(),
|
context.TODO(),
|
||||||
@ -395,25 +397,6 @@ func TestNewClient_Web(t *testing.T) {
|
|||||||
// ~ Private methods
|
// ~ 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) }) {
|
func dump(t *testing.T, i interface{ MarshalJSON() ([]byte, error) }) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
var ret bytes.Buffer
|
var ret bytes.Buffer
|
||||||
|
|||||||
@ -4,9 +4,14 @@ import (
|
|||||||
"google.golang.org/api/tagmanager/v2"
|
"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{
|
return &tagmanager.Tag{
|
||||||
FiringTriggerId: []string{trigger.TriggerId},
|
FiringTriggerId: triggerIDs,
|
||||||
Name: name,
|
Name: name,
|
||||||
Parameter: []*tagmanager.Parameter{
|
Parameter: []*tagmanager.Parameter{
|
||||||
{
|
{
|
||||||
|
|||||||
245
pkg/typescript/builder.go
Normal file
245
pkg/typescript/builder.go
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
package typescript
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/types"
|
||||||
|
"slices"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/foomo/sesamy-cli/internal"
|
||||||
|
"github.com/stoewer/go-strcase"
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Builder struct {
|
||||||
|
scanner *internal.Scanner
|
||||||
|
packageNameReplacer *strings.Replacer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBuilder(scanner *internal.Scanner) *Builder {
|
||||||
|
return &Builder{
|
||||||
|
scanner: scanner,
|
||||||
|
packageNameReplacer: strings.NewReplacer(".", "_", "/", "_", "-", "_"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (b *Builder) AddStruct(name string, s *types.Struct) {
|
||||||
|
// b.structs[name] = s
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (b *Builder) AddStructs(v map[string]*types.Struct) {
|
||||||
|
// maps.Copy(b.structs, v)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (b *Builder) AddImport(pkg string, names ...string) {
|
||||||
|
// b.imports[pkg] = append(b.imports[pkg], names...)
|
||||||
|
// }
|
||||||
|
|
||||||
|
type Imports map[string][]string
|
||||||
|
|
||||||
|
func (i Imports) String() string {
|
||||||
|
var ret string
|
||||||
|
keys := maps.Keys(i)
|
||||||
|
sort.Strings(keys)
|
||||||
|
for _, name := range keys {
|
||||||
|
vals := i[name]
|
||||||
|
slices.Sort(vals)
|
||||||
|
ret += fmt.Sprintf("import { %s } from '%s';\n", strings.Join(vals, ", "), name)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
type Package struct {
|
||||||
|
pkg *internal.ScannerPackage
|
||||||
|
packageNameReplacer *strings.Replacer
|
||||||
|
Filename string
|
||||||
|
headBuilder *SBuilder
|
||||||
|
importsBuilder *SBuilder
|
||||||
|
strucstBuilder *SBuilder
|
||||||
|
}
|
||||||
|
|
||||||
|
type SBuilder struct {
|
||||||
|
*strings.Builder
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSBuilder() *SBuilder {
|
||||||
|
return &SBuilder{
|
||||||
|
Builder: &strings.Builder{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SBuilder) NL() {
|
||||||
|
s.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SBuilder) Commentf(indent int, format string, args ...interface{}) {
|
||||||
|
prefix := strings.Repeat("\t", indent)
|
||||||
|
s.WriteString(fmt.Sprintf(prefix+"// "+format+"\n", args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SBuilder) Codef(indent int, format string, args ...interface{}) {
|
||||||
|
prefix := strings.Repeat("\t", indent)
|
||||||
|
s.WriteString(fmt.Sprintf(prefix+format+"\n", args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPackage(pkg *internal.ScannerPackage, packageNameReplacer *strings.Replacer) *Package {
|
||||||
|
return &Package{
|
||||||
|
pkg: pkg,
|
||||||
|
Filename: packageNameReplacer.Replace(pkg.PkgPath) + ".ts",
|
||||||
|
packageNameReplacer: packageNameReplacer,
|
||||||
|
headBuilder: NewSBuilder(),
|
||||||
|
importsBuilder: NewSBuilder(),
|
||||||
|
strucstBuilder: NewSBuilder(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Package) EventPackage(ctx context.Context) (string, error) {
|
||||||
|
// render header
|
||||||
|
b.headBuilder.Commentf(0, `Code generated by sesamy. DO NOT EDIT.`)
|
||||||
|
|
||||||
|
// render imports
|
||||||
|
for name := range b.pkg.Imports {
|
||||||
|
name = b.packageNameReplacer.Replace(name)
|
||||||
|
b.importsBuilder.Codef(0, `import * as %s from './%s.ts';`, name, name)
|
||||||
|
}
|
||||||
|
b.importsBuilder.Codef(0, `import { collect } from '@foomo/sesamy';`)
|
||||||
|
|
||||||
|
// iterate scope types
|
||||||
|
names := maps.Keys(b.pkg.Scope)
|
||||||
|
slices.Sort(names)
|
||||||
|
for _, name := range names {
|
||||||
|
scope := b.pkg.Scope[name]
|
||||||
|
switch t := scope.Underlying().(type) {
|
||||||
|
case *types.Struct:
|
||||||
|
b.strucstBuilder.Codef(0, "export interface %s {", name)
|
||||||
|
for i := range t.NumFields() {
|
||||||
|
tag, err := internal.ParseTag(t.Tag(i))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if tag != "" {
|
||||||
|
b.strucstBuilder.Codef(1, "%s: %s;", tag, b.typeType(t.Field(i).Type()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.strucstBuilder.Codef(0, "};")
|
||||||
|
b.strucstBuilder.NL()
|
||||||
|
|
||||||
|
b.strucstBuilder.Codef(0, "export const %s = (params: %s) => {", strcase.LowerCamelCase(name), name)
|
||||||
|
b.strucstBuilder.Codef(1, "collect({ name:'%s', params });", strcase.SnakeCase(name))
|
||||||
|
b.strucstBuilder.Codef(0, "};")
|
||||||
|
b.strucstBuilder.NL()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s := &strings.Builder{}
|
||||||
|
s.WriteString(b.headBuilder.String())
|
||||||
|
s.WriteString(b.importsBuilder.String())
|
||||||
|
s.WriteString("\n")
|
||||||
|
s.WriteString(b.strucstBuilder.String())
|
||||||
|
return s.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Package) DependencyPackage(ctx context.Context) (string, error) {
|
||||||
|
// render header
|
||||||
|
b.headBuilder.Commentf(0, `Code generated by sesamy. DO NOT EDIT.`)
|
||||||
|
|
||||||
|
// render imports
|
||||||
|
for name := range b.pkg.Imports {
|
||||||
|
name = b.packageNameReplacer.Replace(name)
|
||||||
|
b.importsBuilder.Codef(0, `import * as %s from './%s.ts';`, name, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate scope types
|
||||||
|
names := maps.Keys(b.pkg.Scope)
|
||||||
|
slices.Sort(names)
|
||||||
|
for _, name := range names {
|
||||||
|
scope := b.pkg.Scope[name]
|
||||||
|
|
||||||
|
switch scope.Underlying().(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
// lookup scope type with underlying types e.g. const ScopeTypeCustom ScopeType = "custom"
|
||||||
|
if u := b.pkg.Scope.LookupUnderlyingTypeName(name); len(u) > 0 {
|
||||||
|
uNames := maps.Keys(u)
|
||||||
|
sort.Strings(uNames)
|
||||||
|
b.strucstBuilder.Codef(0, "export enum %s {", name)
|
||||||
|
for _, uName := range uNames {
|
||||||
|
if valueSpec, ok := b.pkg.Values.Lookup(uName).Values[0].(*ast.BasicLit); ok {
|
||||||
|
b.strucstBuilder.Codef(1, "%s = %s,", strings.TrimPrefix(uName, name), valueSpec.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.strucstBuilder.Codef(0, "};")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s := &strings.Builder{}
|
||||||
|
s.WriteString(b.headBuilder.String())
|
||||||
|
s.WriteString(b.importsBuilder.String())
|
||||||
|
s.WriteString("\n")
|
||||||
|
s.WriteString(b.strucstBuilder.String())
|
||||||
|
return s.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) Package(ctx context.Context, paths []string) (map[string]string, error) {
|
||||||
|
var err error
|
||||||
|
ret := make(map[string]string)
|
||||||
|
for name, scannerPackage := range b.scanner.Packages {
|
||||||
|
var s string
|
||||||
|
pkg := NewPackage(scannerPackage, b.packageNameReplacer)
|
||||||
|
if slices.Contains(paths, scannerPackage.PkgPath) {
|
||||||
|
s, err = pkg.EventPackage(ctx)
|
||||||
|
} else {
|
||||||
|
s, err = pkg.DependencyPackage(ctx)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ret[name] = s
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (b *Builder) String(pkgs []*internal.Package) (map[string]string, error) {
|
||||||
|
// ret := make(map[string]string)
|
||||||
|
// for _, pkg := range pkgs {
|
||||||
|
// s, err := b.Package(pkg)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// ret[b.packageNameReplacer.Replace(pkg.Path)] = s
|
||||||
|
// }
|
||||||
|
// return ret, nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (b *Package) typeType(value types.Type) string {
|
||||||
|
switch t := value.(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
return b.basicInfoType(t.Info())
|
||||||
|
case *types.Named:
|
||||||
|
return b.namedType(t) + "." + t.Obj().Name()
|
||||||
|
case *types.Slice:
|
||||||
|
return "Array<" + b.typeType(t.Elem()) + ">"
|
||||||
|
default:
|
||||||
|
return "any"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Package) namedType(t *types.Named) string {
|
||||||
|
return b.packageNameReplacer.Replace(t.Obj().Pkg().Path())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Package) basicInfoType(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:
|
||||||
|
return "any"
|
||||||
|
}
|
||||||
|
}
|
||||||
130
pkg/typescript/builder_test.go
Normal file
130
pkg/typescript/builder_test.go
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
package typescript_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/foomo/sesamy-cli/internal"
|
||||||
|
"github.com/foomo/sesamy-cli/pkg/typescript"
|
||||||
|
_ "github.com/foomo/sesamy-go" // force inclusion
|
||||||
|
_ "github.com/foomo/sesamy-go/event/params" // force inclusion
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBuiler(t *testing.T) {
|
||||||
|
cfg := &internal.Config{
|
||||||
|
Packages: []*internal.ConfigPackage{
|
||||||
|
{
|
||||||
|
Path: "github.com/foomo/sesamy-go/event",
|
||||||
|
Names: []string{"PageView", "AddToCart"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
scanner := internal.NewScanner(cfg)
|
||||||
|
err := scanner.Scan(context.TODO())
|
||||||
|
|
||||||
|
// structTypes, err := internal.GetStructTypes(context.TODO(), config.Packages{
|
||||||
|
// {
|
||||||
|
// Path: "github.com/foomo/sesamy-go/event",
|
||||||
|
// Events: []string{
|
||||||
|
// "PageView",
|
||||||
|
// "SelectItem",
|
||||||
|
// "AddToCart",
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// })
|
||||||
|
// require.NoError(t, err)
|
||||||
|
|
||||||
|
b := typescript.NewBuilder(scanner)
|
||||||
|
// b.AddStructs(structTypes)
|
||||||
|
actual, err := b.Package(context.Background(), cfg.PackagePaths())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
{
|
||||||
|
expected := `
|
||||||
|
// Code generated by sesamy. DO NOT EDIT.
|
||||||
|
import * as github_com_foomo_sesamy_go from './github_com_foomo_sesamy_go.ts';
|
||||||
|
import { collect } from '@foomo/sesamy';
|
||||||
|
|
||||||
|
export interface AddToCart {
|
||||||
|
name: github_com_foomo_sesamy_go.EventName;
|
||||||
|
params: github_com_foomo_sesamy_go_event_params.AddToCart;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addToCart = (params: AddToCart) => {
|
||||||
|
collect({ name:'add_to_cart', params });
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface PageView {
|
||||||
|
name: github_com_foomo_sesamy_go.EventName;
|
||||||
|
params: github_com_foomo_sesamy_go_event_params.PageView;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const pageView = (params: PageView) => {
|
||||||
|
collect({ name:'page_view', params });
|
||||||
|
};
|
||||||
|
|
||||||
|
`
|
||||||
|
if !assert.Equal(t, expected, "\n"+actual["github.com/foomo/sesamy-go/event"]) {
|
||||||
|
t.Log("\n" + actual["github.com/foomo/sesamy-go/event"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
expected := `
|
||||||
|
// 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_wishlit",
|
||||||
|
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",
|
||||||
|
UnlockArchievement = "unlock_archievement",
|
||||||
|
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",
|
||||||
|
};
|
||||||
|
`
|
||||||
|
if !assert.Equal(t, expected, "\n"+actual["github.com/foomo/sesamy-go"]) {
|
||||||
|
t.Log("\n" + actual["github.com/foomo/sesamy-go"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
pkg/typescript/utils.go
Normal file
31
pkg/typescript/utils.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package typescript
|
||||||
|
|
||||||
|
// func TSType(value types.Type) string {
|
||||||
|
// switch t := value.(type) {
|
||||||
|
// case *types.Basic:
|
||||||
|
// return tsType(t.Info())
|
||||||
|
// case *types.Named:
|
||||||
|
// return TSType(t.Obj().Type().Underlying())
|
||||||
|
// case *types.Slice:
|
||||||
|
// var t string
|
||||||
|
// if s, ok := t.Elem().(*types.Named); ok {
|
||||||
|
// s.Obj()
|
||||||
|
// }
|
||||||
|
// return "Array<" + TSType(t.Elem().Underlying()) + ">"
|
||||||
|
// default:
|
||||||
|
// return "any"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func tsType(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:
|
||||||
|
// return "any"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
@ -30,9 +30,9 @@ typescript:
|
|||||||
tagmanager:
|
tagmanager:
|
||||||
packages:
|
packages:
|
||||||
- path: 'github.com/foomo/sesamy-cli/_example/server'
|
- path: 'github.com/foomo/sesamy-cli/_example/server'
|
||||||
output_path: './_example/client/types.d.ts'
|
events:
|
||||||
exclude_files:
|
- PageView
|
||||||
- item.go
|
- SelectItem
|
||||||
prefixes:
|
prefixes:
|
||||||
client: ''
|
client: ''
|
||||||
folder: ''
|
folder: ''
|
||||||
Loading…
Reference in New Issue
Block a user