mirror of
https://github.com/foomo/sesamy-cli.git
synced 2025-10-16 12:35:36 +00:00
commit
7a58d31d2d
@ -108,6 +108,7 @@ linters:
|
||||
- asciicheck # checks that all code identifiers does not have non-ASCII symbols in the name [fast: true, auto-fix: false]
|
||||
- bidichk # Checks for dangerous unicode character sequences [fast: true, auto-fix: false]
|
||||
- bodyclose # checks whether HTTP response body is closed successfully [fast: false, auto-fix: false]
|
||||
- canonicalheader # Checks whether net/http.Header uses canonical header [fast: false, auto-fix: false]
|
||||
- containedctx # containedctx is a linter that detects struct contained context.Context field [fast: false, auto-fix: false]
|
||||
- contextcheck # check whether the function uses a non-inherited context [fast: false, auto-fix: false]
|
||||
- copyloopvar # (go >= 1.22) copyloopvar is a linter detects places where loop variables are copied [fast: true, auto-fix: false]
|
||||
@ -117,6 +118,7 @@ linters:
|
||||
- errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. [fast: false, auto-fix: false]
|
||||
- errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. [fast: false, auto-fix: false]
|
||||
- exhaustive # check exhaustiveness of enum switch statements [fast: false, auto-fix: false]
|
||||
- fatcontext #Detects nested contexts in loops [fast: false, auto-fix: false]
|
||||
#- forbidigo # Forbids identifiers [fast: false, auto-fix: false]
|
||||
- forcetypeassert # finds forced type assertions [fast: true, auto-fix: false]
|
||||
- gocheckcompilerdirectives # Checks that go compiler directive comments (//go:) are valid. [fast: true, auto-fix: false]
|
||||
@ -137,11 +139,12 @@ linters:
|
||||
- intrange # (go >= 1.22) intrange is a linter to find places where for loops could make use of an integer range. [fast: true, auto-fix: false]
|
||||
- loggercheck # (logrlint) Checks key value pairs for common logger libraries (kitlog,klog,logr,zap). [fast: false, auto-fix: false]
|
||||
- makezero # Finds slice declarations with non-zero initial length [fast: false, auto-fix: false]
|
||||
- misspell # Finds commonly misspelled English words [fast: true, auto-fix: true]
|
||||
- mirror # reports wrong mirror patterns of bytes/strings usage [fast: false, auto-fix: true]
|
||||
- misspell # Finds commonly misspelled English words [fast: true, auto-fix: true]
|
||||
- musttag # enforce field tags in (un)marshaled structs [fast: false, auto-fix: false]
|
||||
- nakedret # Checks that functions with naked returns are not longer than a maximum size (can be zero). [fast: true, auto-fix: false]
|
||||
- nilerr # Finds the code that returns nil even if it checks that the error is not nil. [fast: false, auto-fix: false]
|
||||
- nilnesserr # Reports constructs that checks for err != nil, but returns a different nil value error.
|
||||
- nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. [fast: false, auto-fix: false]
|
||||
- noctx # Finds sending http request without context.Context [fast: false, auto-fix: false]
|
||||
- nolintlint # Reports ill-formed or insufficient nolint directives [fast: true, auto-fix: true]
|
||||
@ -156,7 +159,6 @@ linters:
|
||||
- spancheck # Checks for mistakes with OpenTelemetry/Census spans. [fast: false, auto-fix: false]
|
||||
- sqlclosecheck # Checks that sql.Rows, sql.Stmt, sqlx.NamedStmt, pgx.Query are closed. [fast: false, auto-fix: false]
|
||||
- stylecheck # Stylecheck is a replacement for golint [fast: false, auto-fix: false]
|
||||
- tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 [fast: false, auto-fix: false]
|
||||
- testableexamples # linter checks if examples are testable (have an expected output) [fast: true, auto-fix: false]
|
||||
- testifylint # Checks usage of github.com/stretchr/testify. [fast: false, auto-fix: false]
|
||||
- testpackage # linter that makes you use a separate _test package [fast: true, auto-fix: false]
|
||||
@ -166,8 +168,6 @@ linters:
|
||||
- usestdlibvars # A linter that detect the possibility to use variables/constants from the Go standard library. [fast: true, auto-fix: false]
|
||||
- wastedassign # Finds wasted assignment statements [fast: false, auto-fix: false]
|
||||
- whitespace # Whitespace is a linter that checks for unnecessary newlines at the start and end of functions, if, for, etc. [fast: true, auto-fix: true]
|
||||
- canonicalheader # Checks whether net/http.Header uses canonical header [fast: false, auto-fix: false]
|
||||
- fatcontext #Detects nested contexts in loops [fast: false, auto-fix: false]
|
||||
|
||||
## Don't enable
|
||||
#- cyclop # checks function and package cyclomatic complexity [fast: false, auto-fix: false]
|
||||
@ -177,6 +177,7 @@ linters:
|
||||
#- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) [fast: true, auto-fix: false]
|
||||
#- err113 # Go linter to check the errors handling expressions [fast: false, auto-fix: false]
|
||||
#- exhaustruct # Checks if all structure fields are initialized [fast: false, auto-fix: false]
|
||||
#- exptostd # Detects functions from golang.org/x/exp/ that can be replaced by std functions. [auto-fix]
|
||||
#- gci # Gci controls Go package import order and makes it always deterministic. [fast: true, auto-fix: true]
|
||||
#- gochecknoglobals # Check that no global variables exist. [fast: false, auto-fix: false]
|
||||
#- gochecknoinits # Checks that no init functions are present in Go code [fast: true, auto-fix: false]
|
||||
|
||||
4
go.mod
4
go.mod
@ -6,11 +6,11 @@ require (
|
||||
github.com/fatih/structtag v1.2.0
|
||||
github.com/foomo/go v0.0.3
|
||||
github.com/foomo/gocontemplate v0.1.4
|
||||
github.com/foomo/sesamy-go v0.8.0
|
||||
github.com/foomo/sesamy-go v0.8.1
|
||||
github.com/invopop/jsonschema v0.13.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pterm/pterm v0.12.80
|
||||
github.com/spf13/cobra v1.9.0
|
||||
|
||||
12
go.sum
12
go.sum
@ -44,8 +44,8 @@ github.com/foomo/gocontemplate v0.1.4 h1:MYSsoltno9pNMU5NwALd7PNSQ1XjN1tpqMGWAhC
|
||||
github.com/foomo/gocontemplate v0.1.4/go.mod h1:BH8eODDwlqWhasSl86avbtMN3Zfgt4pWwr8/PBPM5v0=
|
||||
github.com/foomo/gostandards v0.2.0 h1:Ryd7TI9yV3Xk5B84DcUDB7KcL3LzQ8NS+TVOrFxTYfA=
|
||||
github.com/foomo/gostandards v0.2.0/go.mod h1:XQx7Ur6vyvxaIe2cQvAthuhPYDe+d2soibqVcXDXOh4=
|
||||
github.com/foomo/sesamy-go v0.8.0 h1:o3zfJ6/FDpBpy/+fGihiQrePLUWwe34zK4iTa5OlHQU=
|
||||
github.com/foomo/sesamy-go v0.8.0/go.mod h1:P1EKsMhG8kAPmxeGCACorY4lfDzIaAgCWw9FVi6r3+Y=
|
||||
github.com/foomo/sesamy-go v0.8.1 h1:5m1ySZB5gW9Dymz8MFggU8DXs7F1AAcV6WW2ieX9DPI=
|
||||
github.com/foomo/sesamy-go v0.8.1/go.mod h1:9TlGLPABYmjt/louonKy4Na4nmx9RHq7N9KyZ6Sy7Xw=
|
||||
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.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||
@ -102,8 +102,8 @@ github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6T
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 h1:BpfhmLKZf+SjVanKKhCgf3bg+511DmU9eDQTen7LLbY=
|
||||
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@ -243,8 +243,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
google.golang.org/api v0.221.0 h1:qzaJfLhDsbMeFee8zBRdt/Nc+xmOuafD/dbdgGfutOU=
|
||||
google.golang.org/api v0.221.0/go.mod h1:7sOU2+TL4TxUTdbi0gWgAIg7tH5qBXxoyhtL+9x3biQ=
|
||||
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk=
|
||||
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
|
||||
|
||||
@ -9,6 +9,10 @@ type Emarsys struct {
|
||||
Enabled bool `json:"enabled" yaml:"enabled"`
|
||||
// Emarsys merchant id
|
||||
MerchantID string `json:"merchantId" yaml:"merchantId"`
|
||||
// Enable test mode
|
||||
TestMode bool `json:"testMode" yaml:"testMode"`
|
||||
// Enable debug mode
|
||||
DebugMode bool `json:"debugMode" yaml:"debugMode"`
|
||||
// Google Consent settings
|
||||
GoogleConsent GoogleConsent `json:"googleConsent" yaml:"googleConsent"`
|
||||
// Google Tag Manager web container settings
|
||||
|
||||
@ -5,6 +5,8 @@ type GoogleTag struct {
|
||||
TagID string `json:"tagId" yaml:"tagId"`
|
||||
// Whether a page_view should be sent on initial load
|
||||
SendPageView bool `json:"sendPageView" yaml:"sendPageView"`
|
||||
// Data layer variables to be added to the event settings
|
||||
DataLayerVariables map[string]string `json:"dataLayerVariables" yaml:"dataLayerVariables"`
|
||||
// TypeScript settings
|
||||
TypeScript TypeScript `json:"typeScript" yaml:"typeScript"`
|
||||
}
|
||||
|
||||
127
pkg/provider/emarsys/README.md
Normal file
127
pkg/provider/emarsys/README.md
Normal file
@ -0,0 +1,127 @@
|
||||
# Emarsys Web Extend
|
||||
|
||||
The emarsys server side web extend provider makes use of the [Web Extend Command](https://dev.emarsys.com/docs/web-extend-reference/a1a185e5fbb6b-web-extend-command-implementation).
|
||||
|
||||
## Initialization
|
||||
|
||||
Since we need to trigger multiple commands, we need to ensure we're always sending the same `sessionId`, `visitorId` & `pageViewId` for all calls.
|
||||
The initialization sets the `emarsys.page_view_id` variable into the `dataLayer` to be sent with each following request.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Data Layer
|
||||
participant Web Container
|
||||
participant Server Container
|
||||
participant Emarsys
|
||||
Web Container->>+Server Container: initialization (/gtag/js/emarsys)
|
||||
Server Container->>Emarsys: pageViewId (sesssionId, visitorId)
|
||||
Server Container->>-Web Container: Cookies (emarsys_cdv, emarsys_s)
|
||||
Web Container->>Data Layer: emarsys.page_view_id
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
### Cart
|
||||
|
||||
NOTE: The default `page_view` event does not contain the `items` so we need to enrich them in the `collect` service.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Web Container
|
||||
participant Collect
|
||||
participant Server Container
|
||||
participant Emarsys
|
||||
|
||||
Web Container->>Collect: page_view
|
||||
Collect->>+Server Container: enrich: items
|
||||
Server Container->>Emarsys: cart<br/>Cookies (s, cdv, xp, fc)
|
||||
Server Container->>-Web Container: Cookies (s, cdv, xp, fc)
|
||||
```
|
||||
|
||||
Standard implementation
|
||||
|
||||
```javascript
|
||||
// The usual commands to identify visitors and report cart contents.
|
||||
ScarabQueue.push(['cart', [
|
||||
{item: 'item_1', price: 19.9, quantity: 1},
|
||||
{item: 'item_2', price: 29.7, quantity: 3}
|
||||
]]);
|
||||
// Firing the ScarabQueue. Should be the last call on the page, called only once.
|
||||
ScarabQueue.push(['go']);
|
||||
```
|
||||
|
||||
### View
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Web Container
|
||||
participant Server Container
|
||||
participant Emarsys
|
||||
|
||||
Web Container->>+Server Container: view_item
|
||||
Server Container->>Emarsys: view<br/>Cookies (s, cdv, xp, fc)
|
||||
Server Container->>-Web Container: Cookies (s, cdv, xp, fc)
|
||||
```
|
||||
|
||||
Standard implementation
|
||||
|
||||
```javascript
|
||||
// Passing on item ID to report product view. Item ID should match the value listed in the Product Catalog
|
||||
ScarabQueue.push(['view', 'item_3']);
|
||||
// Firing the ScarabQueue. Should be the last call on the page, called only once.
|
||||
ScarabQueue.push(['go']);
|
||||
```
|
||||
|
||||
## Category
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Web Container
|
||||
participant Server Container
|
||||
participant Emarsys
|
||||
|
||||
Web Container->>+Server Container: view_item_list
|
||||
Server Container->>Emarsys: category<br/>Cookies (s, cdv, xp, fc)
|
||||
Server Container->>-Web Container: Cookies (s, cdv, xp, fc)
|
||||
```
|
||||
|
||||
Standard implementation
|
||||
|
||||
```javascript
|
||||
// Passing on the category path being visited. Must match the 'category' values listed in the Product Catalog
|
||||
ScarabQueue.push(['category', 'Bikes > Road Bikes']);
|
||||
// Firing the ScarabQueue. Should be the last call on the page, called only once.
|
||||
ScarabQueue.push(['go']);
|
||||
```
|
||||
|
||||
## Purchase
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Web Container
|
||||
participant Server Container
|
||||
participant Emarsys
|
||||
|
||||
Web Container->>+Server Container: purchase
|
||||
Server Container->>Emarsys: category<br/>Cookies (s, cdv, xp, fc)
|
||||
Server Container->>-Web Container: Cookies (s, cdv, xp, fc)
|
||||
```
|
||||
|
||||
Standard implementation
|
||||
|
||||
```javascript
|
||||
// Passing on order details. The price values passed on here serve as the basis of our revenue and revenue contribution reports.
|
||||
ScarabQueue.push(['purchase', {
|
||||
orderId: '231213',
|
||||
items: [
|
||||
{item: 'item_1', price: 19.9, quantity: 1},
|
||||
{item: 'item_2', price: 29.7, quantity: 3}
|
||||
]
|
||||
}]);
|
||||
// Firing the ScarabQueue. Should be the last call on the page, called only once.
|
||||
ScarabQueue.push(['go']);
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [Web Extend Command](https://dev.emarsys.com/docs/web-extend-reference/a1a185e5fbb6b-web-extend-command-implementationhttps://dev.emarsys.com/docs/web-extend-reference/a1a185e5fbb6b-web-extend-command-implementation)
|
||||
@ -70,7 +70,7 @@ func Server(l *slog.Logger, tm *tagmanager.TagManager, cfg config.Emarsys) error
|
||||
return errors.Wrap(err, "failed to upsert event trigger: "+event)
|
||||
}
|
||||
|
||||
if _, err := tm.UpsertTag(servertagx.NewEmarsys(event, merchantID, tagTemplate, eventTrigger)); err != nil {
|
||||
if _, err := tm.UpsertTag(servertagx.NewEmarsys(event, merchantID, cfg.TestMode, cfg.DebugMode, tagTemplate, eventTrigger)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
package tag
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/foomo/sesamy-cli/pkg/utils"
|
||||
"google.golang.org/api/tagmanager/v2"
|
||||
)
|
||||
@ -9,7 +11,7 @@ func EmarsysName(v string) string {
|
||||
return "Emarsys - " + v
|
||||
}
|
||||
|
||||
func NewEmarsys(name string, merchantID *tagmanager.Variable, template *tagmanager.CustomTemplate, triggers ...*tagmanager.Trigger) *tagmanager.Tag {
|
||||
func NewEmarsys(name string, merchantID *tagmanager.Variable, testMode, debugMode bool, template *tagmanager.CustomTemplate, triggers ...*tagmanager.Trigger) *tagmanager.Tag {
|
||||
return &tagmanager.Tag{
|
||||
FiringTriggerId: utils.TriggerIDs(triggers),
|
||||
Name: EmarsysName(name),
|
||||
@ -28,12 +30,12 @@ func NewEmarsys(name string, merchantID *tagmanager.Variable, template *tagmanag
|
||||
{
|
||||
Key: "isTestMode",
|
||||
Type: "boolean",
|
||||
Value: "false",
|
||||
Value: strconv.FormatBool(testMode),
|
||||
},
|
||||
{
|
||||
Key: "isDebugMode",
|
||||
Type: "boolean",
|
||||
Value: "false",
|
||||
Value: strconv.FormatBool(debugMode),
|
||||
},
|
||||
},
|
||||
Type: utils.TemplateType(template),
|
||||
|
||||
@ -81,9 +81,10 @@ ___SANDBOXED_JS_FOR_SERVER___
|
||||
|
||||
const Math = require('Math');
|
||||
const JSON = require('JSON');
|
||||
const setCookie = require('setCookie');
|
||||
const sendHttpGet = require('sendHttpGet');
|
||||
const setResponseBody = require('setResponseBody');
|
||||
const setResponseStatus = require('setResponseStatus');
|
||||
const getRemoteAddress = require('getRemoteAddress');
|
||||
const encodeUriComponent = require('encodeUriComponent');
|
||||
const getAllEventData = require('getAllEventData');
|
||||
const getRequestHeader = require('getRequestHeader');
|
||||
@ -99,22 +100,32 @@ const merchantUrl = 'https://recommender.scarabresearch.com/merchants/'+data.mer
|
||||
// --- Consent ---
|
||||
|
||||
if (!isConsentGivenOrNotRequired()) {
|
||||
return data.gtmOnSuccess();
|
||||
return data.gtmOnSuccess();
|
||||
}
|
||||
|
||||
// --- Main ---
|
||||
|
||||
const mappedData = mapEventData();
|
||||
const cookieList = ["s", "cdv", "xp", "fc"];
|
||||
const headerList = ["referer", "user-agent"];
|
||||
const requestUrl = merchantUrl+'?'+serializeData(mappedData);
|
||||
const requestOptions = {
|
||||
headers: generateRequestHeaders(headerList, cookieList),
|
||||
timeout: 500,
|
||||
};
|
||||
|
||||
return sendHttpGet(merchantUrl+'?'+serializeData(mappedData)).then((result) => {
|
||||
return sendHttpGet(requestUrl, requestOptions).then((result) => {
|
||||
if (result.statusCode >= 200 && result.statusCode < 300) {
|
||||
data.gtmOnSuccess();
|
||||
if (result.headers['set-cookie']) {
|
||||
setResponseCookies(result.headers['set-cookie']);
|
||||
}
|
||||
data.gtmOnSuccess();
|
||||
} else {
|
||||
logToConsole('[FAILURE]', {
|
||||
request: mappedData,
|
||||
eventData: eventData,
|
||||
});
|
||||
data.gtmOnFailure();
|
||||
data.gtmOnFailure();
|
||||
}
|
||||
});
|
||||
|
||||
@ -122,10 +133,11 @@ return sendHttpGet(merchantUrl+'?'+serializeData(mappedData)).then((result) => {
|
||||
|
||||
function mapEventData() {
|
||||
const mappedData = {
|
||||
email: eventData.emarsys_email || null,
|
||||
customerId: eventData.user_id || null,
|
||||
sessionId: getCookieValues('emarsys_s')[0] || eventData.ga_session_id,
|
||||
pageViewId: eventData.page_view_id || generatePageViewId(),
|
||||
isNewPageView: !eventData.page_view_id,
|
||||
pageViewId: eventData.emarsys_page_view_id || generatePageViewId(),
|
||||
isNewPageView: !eventData.emarsys_page_view_id,
|
||||
visitorId: getCookieValues('emarsys_cdv')[0] || eventData.client_id,
|
||||
referrer: eventData.page_referrer || null,
|
||||
orderId: null,
|
||||
@ -136,27 +148,12 @@ function mapEventData() {
|
||||
};
|
||||
|
||||
switch (eventData.event_name) {
|
||||
// custom events
|
||||
case 'emarsys_cart': {
|
||||
case 'page_view': {
|
||||
mappedData.cart = serializeItems(eventData.items || []);
|
||||
break;
|
||||
}
|
||||
case 'emarsys_category': {
|
||||
mappedData.category = eventData.item_list_id;
|
||||
break;
|
||||
}
|
||||
case 'emarsys_purchase': {
|
||||
mappedData.orderId = eventData.transaction_id;
|
||||
mappedData.order = serializeItems(eventData.items || []);
|
||||
break;
|
||||
}
|
||||
case 'emarsys_view': {
|
||||
mappedData.view = serializeItem(eventData.items[0] || {});
|
||||
break;
|
||||
}
|
||||
// standard ecommerce evens
|
||||
case 'view_cart': {
|
||||
mappedData.cart = serializeItems(eventData.items || []);
|
||||
case 'view_item': {
|
||||
mappedData.view = serializeItem(eventData.items[0] || {}, false);
|
||||
break;
|
||||
}
|
||||
case 'view_item_list': {
|
||||
@ -166,10 +163,12 @@ function mapEventData() {
|
||||
case 'purchase': {
|
||||
mappedData.orderId = eventData.transaction_id;
|
||||
mappedData.order = serializeItems(eventData.items || []);
|
||||
break;
|
||||
}
|
||||
case 'view_item': {
|
||||
mappedData.view = serializeItem(eventData.items[0] || {});
|
||||
if (eventData.tax) {
|
||||
mappedData.order[0].price += eventData.tax;
|
||||
}
|
||||
if (eventData.shipping) {
|
||||
mappedData.order[0].price += eventData.shipping;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -179,20 +178,20 @@ function mapEventData() {
|
||||
function serializeItems(items) {
|
||||
const ret = [];
|
||||
items.forEach((item) => {
|
||||
ret.push(serializeItem(item));
|
||||
ret.push(serializeItem(item, true));
|
||||
});
|
||||
return ret.join('|');
|
||||
}
|
||||
|
||||
function serializeItem(item) {
|
||||
function serializeItem(item, full) {
|
||||
const ret = [];
|
||||
if (item.item_id) {
|
||||
ret.push('i:'+item.item_id);
|
||||
}
|
||||
if (item.price) {
|
||||
if (full && item.price) {
|
||||
ret.push('p:'+item.price);
|
||||
}
|
||||
if (item.quantity) {
|
||||
if (full && item.quantity) {
|
||||
ret.push('q:'+item.quantity);
|
||||
}
|
||||
return ret.join(',');
|
||||
@ -207,6 +206,9 @@ function serializeData(mappedData) {
|
||||
if (mappedData.isNewPageView) {
|
||||
slist.push("xp=1");
|
||||
}
|
||||
if (mappedData.email) {
|
||||
slist.push("eh=" + encodeUriComponent(mappedData.email));
|
||||
}
|
||||
if (mappedData.customerId) {
|
||||
slist.push("ci=" + encodeUriComponent(mappedData.customerId));
|
||||
}
|
||||
@ -250,14 +252,59 @@ function generatePageViewId() {
|
||||
}
|
||||
|
||||
function isConsentGivenOrNotRequired() {
|
||||
if (data.adStorageConsent !== 'required') {
|
||||
return true;
|
||||
if (data.adStorageConsent !== 'required') {
|
||||
return true;
|
||||
}
|
||||
if (eventData.consent_state) {
|
||||
return !!eventData.consent_state.ad_storage;
|
||||
}
|
||||
const xGaGcs = eventData['x-ga-gcs'] || ''; // x-ga-gcs is a string like "G110"
|
||||
return xGaGcs[2] === '1';
|
||||
}
|
||||
|
||||
function setResponseCookies(cookieList) {
|
||||
for (let i = 0; i < cookieList.length; i++) {
|
||||
let cookieArray = cookieList[i].split("; ").map((pair) => pair.split("="));
|
||||
let cookieJSON = "";
|
||||
|
||||
for (let j = 1; j < cookieArray.length; j++) {
|
||||
if (j === 1) cookieJSON += "{";
|
||||
if (cookieArray[j].length > 1) cookieJSON += '"' + cookieArray[j][0] + '": "' + cookieArray[j][1] + '"';
|
||||
else cookieJSON += '"' + cookieArray[j][0] + '": ' + true;
|
||||
if (j + 1 < cookieArray.length) cookieJSON += ",";
|
||||
else cookieJSON += "}";
|
||||
}
|
||||
if (eventData.consent_state) {
|
||||
return !!eventData.consent_state.ad_storage;
|
||||
|
||||
setCookie(cookieArray[0][0], cookieArray[0][1], JSON.parse(cookieJSON));
|
||||
}
|
||||
}
|
||||
|
||||
function generateRequestHeaders(headerList, cookieList) {
|
||||
let headers = {};
|
||||
let cookies = [];
|
||||
|
||||
for (let i = 0; i < headerList.length; i++) {
|
||||
let headerName = headerList[i];
|
||||
let headerValue = getRequestHeader(headerName);
|
||||
if (headerValue) {
|
||||
headers[headerName] = getRequestHeader(headerName);
|
||||
}
|
||||
const xGaGcs = eventData['x-ga-gcs'] || ''; // x-ga-gcs is a string like "G110"
|
||||
return xGaGcs[2] === '1';
|
||||
}
|
||||
|
||||
headers.cookie = "";
|
||||
|
||||
for (let i = 0; i < cookieList.length; i++) {
|
||||
let cookieName = cookieList[i];
|
||||
let cookieValue = getCookieValues(cookieName);
|
||||
if (cookieValue && cookieValue.length) {
|
||||
cookies.push(cookieName + "=" + cookieValue[0]);
|
||||
}
|
||||
}
|
||||
|
||||
headers.cookie = cookies.join("; ");
|
||||
headers["X-Forwarded-For"] = getRemoteAddress();
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
|
||||
@ -392,6 +439,75 @@ ___SERVER_PERMISSIONS___
|
||||
]
|
||||
},
|
||||
"isRequired": true
|
||||
},
|
||||
{
|
||||
"instance": {
|
||||
"key": {
|
||||
"publicId": "set_cookies",
|
||||
"versionId": "1"
|
||||
},
|
||||
"param": [
|
||||
{
|
||||
"key": "allowedCookies",
|
||||
"value": {
|
||||
"type": 2,
|
||||
"listItem": [
|
||||
{
|
||||
"type": 3,
|
||||
"mapKey": [
|
||||
{
|
||||
"type": 1,
|
||||
"string": "name"
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"string": "domain"
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"string": "path"
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"string": "secure"
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"string": "session"
|
||||
}
|
||||
],
|
||||
"mapValue": [
|
||||
{
|
||||
"type": 1,
|
||||
"string": "*"
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"string": "*"
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"string": "*"
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"string": "any"
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"string": "any"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"clientAnnotations": {
|
||||
"isEditedByUser": true
|
||||
},
|
||||
"isRequired": true
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@ -32,6 +32,12 @@ func Web(tm *tagmanager.TagManager, cfg config.Emarsys) error {
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := googletag.CreateWebDatalayerVariables(tm, map[string]string{
|
||||
"emarsys_page_view_id": "emarsys.page_view_id",
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
{ // create event tags
|
||||
tagID, err := tm.LookupVariable(googletag.NameGoogleTagID)
|
||||
if err != nil {
|
||||
|
||||
@ -42,7 +42,6 @@ const logToConsole = require('logToConsole');
|
||||
const generateRandom = require('generateRandom');
|
||||
const getCookieValues = require('getCookieValues');
|
||||
const encodeUriComponent = require('encodeUriComponent');
|
||||
const createArgumentsQueue = require('createArgumentsQueue');
|
||||
|
||||
// --- Config
|
||||
|
||||
@ -56,16 +55,14 @@ logToConsole(JSON.stringify({
|
||||
pageViewId: pageViewId,
|
||||
}));
|
||||
|
||||
const gtag = createArgumentsQueue('gtag', 'dataLayer');
|
||||
|
||||
// set page view id
|
||||
gtag('set', 'emarsys', { page_view_id: pageViewId });
|
||||
let query = [];
|
||||
if (sessionId) query.push('s='+encodeUriComponent(sessionId));
|
||||
if (sessionId) query.push('s='+encodeUriComponent(sessionId));
|
||||
if (pageViewId) query.push('pv='+encodeUriComponent(pageViewId));
|
||||
gtagSet({emarsys: {page_view_id: pageViewId}});
|
||||
|
||||
// call emarsys client
|
||||
let query = [];
|
||||
if (sessionId) query.push('s='+encodeUriComponent(sessionId));
|
||||
if (visitorId) query.push('vi='+encodeUriComponent(visitorId));
|
||||
if (pageViewId) query.push('pv='+encodeUriComponent(pageViewId));
|
||||
sendPixel("/gtag/js/emarsys?"+query.join('&'));
|
||||
|
||||
// Call data.gtmOnSuccess when the tag is finished.
|
||||
@ -151,103 +148,6 @@ ___WEB_PERMISSIONS___
|
||||
]
|
||||
},
|
||||
"isRequired": true
|
||||
},
|
||||
{
|
||||
"instance": {
|
||||
"key": {
|
||||
"publicId": "access_globals",
|
||||
"versionId": "1"
|
||||
},
|
||||
"param": [
|
||||
{
|
||||
"key": "keys",
|
||||
"value": {
|
||||
"type": 2,
|
||||
"listItem": [
|
||||
{
|
||||
"type": 3,
|
||||
"mapKey": [
|
||||
{
|
||||
"type": 1,
|
||||
"string": "key"
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"string": "read"
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"string": "write"
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"string": "execute"
|
||||
}
|
||||
],
|
||||
"mapValue": [
|
||||
{
|
||||
"type": 1,
|
||||
"string": "gtag"
|
||||
},
|
||||
{
|
||||
"type": 8,
|
||||
"boolean": true
|
||||
},
|
||||
{
|
||||
"type": 8,
|
||||
"boolean": true
|
||||
},
|
||||
{
|
||||
"type": 8,
|
||||
"boolean": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": 3,
|
||||
"mapKey": [
|
||||
{
|
||||
"type": 1,
|
||||
"string": "key"
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"string": "read"
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"string": "write"
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"string": "execute"
|
||||
}
|
||||
],
|
||||
"mapValue": [
|
||||
{
|
||||
"type": 1,
|
||||
"string": "dataLayer"
|
||||
},
|
||||
{
|
||||
"type": 8,
|
||||
"boolean": true
|
||||
},
|
||||
{
|
||||
"type": 8,
|
||||
"boolean": true
|
||||
},
|
||||
{
|
||||
"type": 8,
|
||||
"boolean": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"isRequired": true
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@ -23,11 +23,20 @@ func Web(tm *tagmanager.TagManager, cfg config.GoogleTag) error {
|
||||
}
|
||||
|
||||
{ // setup google tag
|
||||
settings := map[string]string{
|
||||
configSettings := map[string]string{
|
||||
"server_container_url": "https://{{Page Hostname}}",
|
||||
}
|
||||
if !cfg.SendPageView {
|
||||
settings["send_page_view"] = "false"
|
||||
configSettings["send_page_view"] = "false"
|
||||
}
|
||||
|
||||
eventSettings := map[string]*api.Variable{}
|
||||
for k, v := range cfg.DataLayerVariables {
|
||||
dlv, err := tm.UpsertVariable(variable.NewDataLayerVariable(v))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
eventSettings[k] = dlv
|
||||
}
|
||||
|
||||
tagID, err := tm.UpsertVariable(commonvariable.NewConstant(NameGoogleTagID, cfg.TagID))
|
||||
@ -35,11 +44,11 @@ func Web(tm *tagmanager.TagManager, cfg config.GoogleTag) error {
|
||||
return err
|
||||
}
|
||||
|
||||
settingsVariable, err := tm.UpsertVariable(containervariable.NewGoogleTagConfigurationSettings(NameGoogleTagSettings, settings))
|
||||
settingsVariable, err := tm.UpsertVariable(containervariable.NewGoogleTagConfigurationSettings(NameGoogleTagSettings, configSettings))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = tm.UpsertTag(webtag.NewGoogleTag(NameGoogleTag, tagID, settingsVariable)); err != nil {
|
||||
if _, err = tm.UpsertTag(webtag.NewGoogleTag(NameGoogleTag, tagID, settingsVariable, eventSettings)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -62,11 +71,9 @@ func CreateWebEventTriggers(tm *tagmanager.TagManager, cfg contemplate.Config) (
|
||||
return nil, err
|
||||
}
|
||||
|
||||
variables := make(map[string]*api.Variable, len(parameters))
|
||||
for parameterName, parameterValue := range parameters {
|
||||
if variables[parameterName], err = tm.UpsertVariable(variable.NewDataLayerVariable(parameterValue)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
variables, err := CreateWebDatalayerVariables(tm, parameters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := tm.UpsertVariable(containervariable.NewGoogleTagEventSettings(event, variables)); err != nil {
|
||||
@ -76,3 +83,17 @@ func CreateWebEventTriggers(tm *tagmanager.TagManager, cfg contemplate.Config) (
|
||||
|
||||
return eventParameters, nil
|
||||
}
|
||||
|
||||
func CreateWebDatalayerVariables(tm *tagmanager.TagManager, parameters map[string]string) (map[string]*api.Variable, error) {
|
||||
previousFolderName := tm.FolderName()
|
||||
tm.SetFolderName("Sesamy - " + Name)
|
||||
defer tm.SetFolderName(previousFolderName)
|
||||
var err error
|
||||
variables := make(map[string]*api.Variable, len(parameters))
|
||||
for parameterName, parameterValue := range parameters {
|
||||
if variables[parameterName], err = tm.UpsertVariable(variable.NewDataLayerVariable(parameterValue)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return variables, nil
|
||||
}
|
||||
|
||||
@ -5,7 +5,26 @@ import (
|
||||
"google.golang.org/api/tagmanager/v2"
|
||||
)
|
||||
|
||||
func NewGoogleTag(name string, tagID *tagmanager.Variable, settings *tagmanager.Variable) *tagmanager.Tag {
|
||||
func NewGoogleTag(name string, tagID *tagmanager.Variable, configSettings *tagmanager.Variable, eventSettings map[string]*tagmanager.Variable) *tagmanager.Tag {
|
||||
var eventSettingsList []*tagmanager.Parameter
|
||||
for k, v := range eventSettings {
|
||||
eventSettingsList = append(eventSettingsList, &tagmanager.Parameter{
|
||||
Type: "map",
|
||||
Map: []*tagmanager.Parameter{
|
||||
{
|
||||
Key: "parameter",
|
||||
Type: "template",
|
||||
Value: k,
|
||||
},
|
||||
{
|
||||
Key: "parameterValue",
|
||||
Type: "template",
|
||||
Value: "{{" + v.Name + "}}",
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
ret := &tagmanager.Tag{
|
||||
FiringTriggerId: []string{trigger.IDInitialization},
|
||||
Name: name,
|
||||
@ -15,10 +34,15 @@ func NewGoogleTag(name string, tagID *tagmanager.Variable, settings *tagmanager.
|
||||
Type: "template",
|
||||
Value: "{{" + tagID.Name + "}}",
|
||||
},
|
||||
{
|
||||
Key: "eventSettingsTable",
|
||||
Type: "list",
|
||||
List: eventSettingsList,
|
||||
},
|
||||
{
|
||||
Key: "configSettingsVariable",
|
||||
Type: "template",
|
||||
Value: "{{" + settings.Name + "}}",
|
||||
Value: "{{" + configSettings.Name + "}}",
|
||||
},
|
||||
},
|
||||
Type: "googtag",
|
||||
|
||||
@ -188,6 +188,14 @@
|
||||
"type": "string",
|
||||
"description": "Emarsys merchant id"
|
||||
},
|
||||
"testMode": {
|
||||
"type": "boolean",
|
||||
"description": "Enable test mode"
|
||||
},
|
||||
"debugMode": {
|
||||
"type": "boolean",
|
||||
"description": "Enable debug mode"
|
||||
},
|
||||
"googleConsent": {
|
||||
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.GoogleConsent",
|
||||
"description": "Google Consent settings"
|
||||
@ -413,6 +421,10 @@
|
||||
"type": "boolean",
|
||||
"description": "Whether a page_view should be sent on initial load"
|
||||
},
|
||||
"dataLayerVariables": {
|
||||
"$ref": "#/$defs/map[string]string",
|
||||
"description": "Data layer variables to be added to the event settings"
|
||||
},
|
||||
"typeScript": {
|
||||
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.TypeScript",
|
||||
"description": "TypeScript settings"
|
||||
@ -611,6 +623,12 @@
|
||||
"$ref": "#/$defs/github.com.foomo.sesamy-cli.pkg.config.MicrosoftAdsConversionTag"
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"map[string]string": {
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -43,6 +43,9 @@ googleTag:
|
||||
tagId: G-PZ5ELRCR31
|
||||
# Whether a page_view should be sent on initial load
|
||||
sendPageView: true
|
||||
# Data layer variables to be added to the event settings
|
||||
dataLayerVariables:
|
||||
emarsys_page_view_id: emarsys.page_view_id
|
||||
# TypeScript settings
|
||||
typeScript:
|
||||
# Target directory for generate files
|
||||
@ -255,6 +258,10 @@ emarsys:
|
||||
enabled: true
|
||||
# Emarsys merchant id
|
||||
merchantId: ''
|
||||
# Enable test mode
|
||||
testMode: false
|
||||
# Enable debug mode
|
||||
debugMode: false
|
||||
# Google Consent settings
|
||||
googleConsent:
|
||||
# Enable consent mode
|
||||
|
||||
@ -122,7 +122,7 @@ func TestNewClient_Server(t *testing.T) {
|
||||
cmd := c.Service().Accounts.Containers.Workspaces.Templates.List(c.WorkspacePath())
|
||||
if r, err := cmd.Do(); assert.NoError(t, err) {
|
||||
dump(t, r)
|
||||
fmt.Println(r.Template[8].TemplateData)
|
||||
fmt.Println(r.Template[5].TemplateData)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -274,8 +274,8 @@ func TestNewClient_Web(t *testing.T) {
|
||||
t.Run("list templates", func(t *testing.T) {
|
||||
cmd := c.Service().Accounts.Containers.Workspaces.Templates.List(c.WorkspacePath())
|
||||
if r, err := cmd.Do(); assert.NoError(t, err) {
|
||||
// dump(t, r)
|
||||
fmt.Println(r.Template[0].TemplateData)
|
||||
dump(t, r)
|
||||
fmt.Println(r.Template[2].TemplateData)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user