-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathconversions.go
232 lines (197 loc) · 6.17 KB
/
conversions.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
package tonicpow
import (
"encoding/json"
"fmt"
"net/http"
)
// ConversionOps allow functional options to be supplied
// that overwrite default conversion options.
type ConversionOps func(c *conversionOptions)
// conversionOptions holds all the configuration for the conversion
type conversionOptions struct {
customDimensions string // (optional) custom dimensions to add to the conversion
delayInMinutes uint64 // (optional) delay the conversion x minutes (before processing, allowing cancellation)
goalID uint64 // Goal by ID
goalName string // Goal by name
purchaseAmount float64 // (optional) purchase amount (total for e-commerce)
shortCode string // (optional) trigger a conversion for a link short_code
tncpwSession string // tncpw session
tonicPowUserID uint64 // (optional) trigger a conversion for a specific user
twitterID string // (optional) trigger a conversion for a specific Twitter user
}
// validate will check the options before processing
func (o *conversionOptions) validate() error {
if o.goalID == 0 && len(o.goalName) == 0 {
return fmt.Errorf("missing required attribute(s): %s or %s", fieldID, fieldName)
} else if o.goalID == 0 && o.tonicPowUserID > 0 {
return fmt.Errorf("missing required attribute: %s", fieldID)
} else if o.tonicPowUserID == 0 && len(o.tncpwSession) == 0 && len(o.shortCode) == 0 && len(o.twitterID) == 0 {
return fmt.Errorf(
"missing required attribute(s): %s or %s or %s or %s",
fieldVisitorSessionGUID, fieldUserID, fieldShortCode, fieldTwitterID,
)
}
return nil
}
// payload will generate the payload given the options
func (o *conversionOptions) payload() map[string]string {
m := map[string]string{}
// Set goal id
if o.goalID > 0 {
m[fieldGoalID] = fmt.Sprintf("%d", o.goalID)
}
// Set goal name
if len(o.goalName) > 0 {
m[fieldName] = o.goalName
}
// Set tonic pow user
if o.tonicPowUserID > 0 {
m[fieldUserID] = fmt.Sprintf("%d", o.tonicPowUserID)
} else if len(o.shortCode) > 0 {
m[fieldShortCode] = o.shortCode
} else if len(o.twitterID) > 0 {
m[fieldTwitterID] = o.twitterID
} else if len(o.tncpwSession) > 0 {
m[fieldVisitorSessionGUID] = o.tncpwSession
}
// Set delay in minutes
if o.delayInMinutes > 0 {
m[fieldDelayInMinutes] = fmt.Sprintf("%d", o.delayInMinutes)
}
// Set purchase amount
if o.purchaseAmount > 0 {
m[fieldAmount] = fmt.Sprintf("%f", o.purchaseAmount)
}
// Set custom dimensions
if len(o.customDimensions) > 0 {
m[fieldCustomDimensions] = o.customDimensions
}
return m
}
// WithGoalID will set a goal ID
func WithGoalID(goalID uint64) ConversionOps {
return func(c *conversionOptions) {
c.goalID = goalID
}
}
// WithGoalName will set a goal name
func WithGoalName(name string) ConversionOps {
return func(c *conversionOptions) {
c.goalName = name
}
}
// WithTncpwSession will set a tncpw_session
func WithTncpwSession(session string) ConversionOps {
return func(c *conversionOptions) {
c.tncpwSession = session
}
}
// WithCustomDimensions will set custom dimensions (string / json)
func WithCustomDimensions(dimensions string) ConversionOps {
return func(c *conversionOptions) {
c.customDimensions = dimensions
}
}
// WithPurchaseAmount will set purchase amount from e-commerce
func WithPurchaseAmount(amount float64) ConversionOps {
return func(c *conversionOptions) {
c.purchaseAmount = amount
}
}
// WithDelay will set a delay in minutes
func WithDelay(minutes uint64) ConversionOps {
return func(c *conversionOptions) {
c.delayInMinutes = minutes
}
}
// WithUserID will set a tonicpow user ID
func WithUserID(userID uint64) ConversionOps {
return func(c *conversionOptions) {
c.tonicPowUserID = userID
}
}
// WithShortCode will set a link short code
func WithShortCode(shortCode string) ConversionOps {
return func(c *conversionOptions) {
c.shortCode = shortCode
}
}
// WithTwitterID will set a Twitter user ID
func WithTwitterID(twitterID string) ConversionOps {
return func(c *conversionOptions) {
c.twitterID = twitterID
}
}
// CreateConversion will fire a conversion for a given goal, if successful it will make a new Conversion
//
// For more information: https://docs.tonicpow.com/#caeffdd5-eaad-4fc8-ac01-8288b50e8e27
func (c *Client) CreateConversion(opts ...ConversionOps) (conversion *Conversion,
response *StandardResponse, err error) {
// Start the options
options := new(conversionOptions)
// Set the conversion options
for _, opt := range opts {
opt(options)
}
// Validate options
if err = options.validate(); err != nil {
return
}
// Fire the Request
if response, err = c.Request(
http.MethodPost,
"/"+modelConversion,
options.payload(), http.StatusCreated,
); err != nil {
return
}
err = json.Unmarshal(response.Body, &conversion)
return
}
// GetConversion will get an existing conversion
// This will return an Error if the goal is not found (404)
//
// For more information: https://docs.tonicpow.com/#fce465a1-d8d5-442d-be22-95169170167e
func (c *Client) GetConversion(conversionID uint64) (conversion *Conversion,
response *StandardResponse, err error) {
// Must have an ID
if conversionID == 0 {
err = fmt.Errorf("missing required attribute: %s", fieldID)
return
}
// Fire the Request
if response, err = c.Request(
http.MethodGet,
fmt.Sprintf("/%s/details/%d", modelConversion, conversionID),
nil, http.StatusOK,
); err != nil {
return
}
err = json.Unmarshal(response.Body, &conversion)
return
}
// CancelConversion will cancel an existing conversion (if delay was set and > 1 minute remaining)
//
// For more information: https://docs.tonicpow.com/#e650b083-bbb4-4ff7-9879-c14b1ab3f753
func (c *Client) CancelConversion(conversionID uint64, cancelReason string) (conversion *Conversion,
response *StandardResponse, err error) {
// Must have an ID
if conversionID == 0 {
err = fmt.Errorf("missing required attribute: %s", fieldID)
return
}
// Fire the Request
if response, err = c.Request(
http.MethodPut,
fmt.Sprintf("/%s/cancel", modelConversion),
map[string]string{
fieldID: fmt.Sprintf("%d", conversionID),
fieldReason: cancelReason,
},
http.StatusOK,
); err != nil {
return
}
err = json.Unmarshal(response.Body, &conversion)
return
}