wip: need added

This commit is contained in:
Kevin Franklin Kim 2024-05-17 17:36:32 +02:00
parent d2c61b5a25
commit ab48d6326a
No known key found for this signature in database
28 changed files with 1153 additions and 291 deletions

View File

@ -77,6 +77,7 @@ linters-settings:
- name: unhandled-error
arguments:
- "fmt.Println"
- "strings.Builder.WriteString"
# TODO remove
- name: deep-exit
disabled: true

View File

@ -31,23 +31,97 @@ 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'
events:
- 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

View File

@ -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;
}

View File

@ -1,7 +0,0 @@
package server
type AddToCart struct {
Currency string `json:"currency,omitempty"`
Value float64 `json:"value,omitempty"`
Items []*Item `json:"items,omitempty"`
}

View File

@ -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"`
}

View File

@ -1,5 +0,0 @@
package server
type Login struct {
Method string `json:"method,omitempty"`
}

View File

@ -1,5 +0,0 @@
package server
type Search struct {
SearchTerm string `json:"search_term,omitempty"`
}

View File

@ -1,5 +0,0 @@
package server
type SignUp struct {
Method string `json:"method,omitempty"`
}

View File

@ -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
{
name := p.ClientName("Google Analytics GA4")
@ -96,7 +112,7 @@ var tagmanagerServerCmd = &cobra.Command{
{
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
}
}

View File

@ -26,7 +26,7 @@ var tagmanagerWebCmd = &cobra.Command{
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 {
return err
}

View File

@ -1,7 +1,13 @@
package cmd
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"
)
@ -11,14 +17,32 @@ 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)
eventTypes, err := internal.GetStructTypes(cmd.Context(), cfg.Typescript.Packages)
if err != nil {
return err
}
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
View File

@ -2,9 +2,12 @@ module github.com/foomo/sesamy-cli
go 1.22.2
//replace github.com/foomo/sesamy-go => ../sesamy-go
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/sesamy-go v0.1.34-0.20240515111745-453e5159a1e7
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/exp v0.0.0-20230905200255-921286631fa9
golang.org/x/tools v0.21.0
google.golang.org/api v0.178.0
google.golang.org/api v0.180.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

23
go.sum
View File

@ -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,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/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/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/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
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.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=
@ -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.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=
@ -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/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=
@ -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/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.180.0 h1:M2D87Yo0rGBPWpo1orwfCLehUUL6E7/TYe5gvMQWDh4=
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.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=

View File

@ -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
View 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
View 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
View 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
View 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
View 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)
}

View File

@ -1,28 +1,6 @@
package config
import (
"github.com/gzuidhof/tygo/tygo"
)
type Tagmanager struct {
Packages []*tygo.PackageConfig `yaml:"packages"`
TypeMappings map[string]string `yaml:"type_mappings"`
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
Packages Packages `yaml:"packages"`
Prefixes TagmanagerPrefixes `yaml:"prefixes"`
}

View File

@ -1,27 +1,6 @@
package config
import (
"github.com/gzuidhof/tygo/tygo"
)
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
Packages Packages `yaml:"packages"`
OutputPath string `yaml:"output_path"`
}

View 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",
}
}

View File

@ -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(),
@ -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

View File

@ -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{
{

245
pkg/typescript/builder.go Normal file
View 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"
}
}

View 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
View 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"
// }
// }

View File

@ -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: ''