@@ -25,29 +25,33 @@ func (err *MixpanelError) Error() string {
2525}
2626
2727type ErrTrackFailed struct {
28- Body string
29- Resp * http.Response
28+ Message string
3029}
3130
3231func (err * ErrTrackFailed ) Error () string {
33- return fmt .Sprintf ("Mixpanel did not return 1 when tracking: %s" , err .Body )
32+ return fmt .Sprintf ("mixpanel did not return 1 when tracking: %s" , err .Message )
3433}
3534
3635// The Mixapanel struct store the mixpanel endpoint and the project token
3736type Mixpanel interface {
38- // Create a mixpanel event
37+ // Create a mixpanel event using the track api
3938 Track (distinctId , eventName string , e * Event ) error
4039
40+ // Create a mixpanel event using the import api
41+ Import (distinctId , eventName string , e * Event ) error
42+
4143 // Set properties for a mixpanel user.
4244 Update (distinctId string , u * Update ) error
4345
46+ // Create an alias for an existing distinct id
4447 Alias (distinctId , newId string ) error
4548}
4649
4750// The Mixapanel struct store the mixpanel endpoint and the project token
4851type mixpanel struct {
4952 Client * http.Client
5053 Token string
54+ Secret string
5155 ApiURL string
5256}
5357
@@ -81,7 +85,7 @@ type Update struct {
8185 Properties map [string ]interface {}
8286}
8387
84- // Track create a events to current distinct id
88+ // Alias create an alias for an existing distinct id
8589func (m * mixpanel ) Alias (distinctId , newId string ) error {
8690 props := map [string ]interface {}{
8791 "token" : m .Token ,
@@ -97,7 +101,7 @@ func (m *mixpanel) Alias(distinctId, newId string) error {
97101 return m .send ("track" , params , false )
98102}
99103
100- // Track create a events to current distinct id
104+ // Track create an event for an existing distinct id
101105func (m * mixpanel ) Track (distinctId , eventName string , e * Event ) error {
102106 props := map [string ]interface {}{
103107 "token" : m .Token ,
@@ -124,7 +128,35 @@ func (m *mixpanel) Track(distinctId, eventName string, e *Event) error {
124128 return m .send ("track" , params , autoGeolocate )
125129}
126130
127- // Updates a user in mixpanel. See
131+ // Import create an event for an existing distinct id
132+ // See https://developer.mixpanel.com/docs/importing-old-events
133+ func (m * mixpanel ) Import (distinctId , eventName string , e * Event ) error {
134+ props := map [string ]interface {}{
135+ "token" : m .Token ,
136+ "distinct_id" : distinctId ,
137+ }
138+ if e .IP != "" {
139+ props ["ip" ] = e .IP
140+ }
141+ if e .Timestamp != nil {
142+ props ["time" ] = e .Timestamp .Unix ()
143+ }
144+
145+ for key , value := range e .Properties {
146+ props [key ] = value
147+ }
148+
149+ params := map [string ]interface {}{
150+ "event" : eventName ,
151+ "properties" : props ,
152+ }
153+
154+ autoGeolocate := e .IP == ""
155+
156+ return m .send ("import" , params , autoGeolocate )
157+ }
158+
159+ // Update updates a user in mixpanel. See
128160// https://mixpanel.com/help/reference/http#people-analytics-updates
129161func (m * mixpanel ) Update (distinctId string , u * Update ) error {
130162 params := map [string ]interface {}{
@@ -159,7 +191,7 @@ func (m *mixpanel) send(eventType string, params interface{}, autoGeolocate bool
159191 return err
160192 }
161193
162- url := m .ApiURL + "/" + eventType + "?data=" + m .to64 (data )
194+ url := m .ApiURL + "/" + eventType + "?data=" + m .to64 (data ) + "&verbose=1"
163195
164196 if autoGeolocate {
165197 url += "&ip=1"
@@ -169,7 +201,11 @@ func (m *mixpanel) send(eventType string, params interface{}, autoGeolocate bool
169201 return & MixpanelError {URL : url , Err : err }
170202 }
171203
172- resp , err := m .Client .Get (url )
204+ req , _ := http .NewRequest ("GET" , url , nil )
205+ if m .Secret != "" {
206+ req .SetBasicAuth (m .Secret , "" )
207+ }
208+ resp , err := m .Client .Do (req )
173209
174210 if err != nil {
175211 return wrapErr (err )
@@ -183,8 +219,17 @@ func (m *mixpanel) send(eventType string, params interface{}, autoGeolocate bool
183219 return wrapErr (bodyErr )
184220 }
185221
186- if strBody := string (body ); strBody != "1" && strBody != "1\n " {
187- return wrapErr (& ErrTrackFailed {Body : strBody , Resp : resp })
222+ type verboseResponse struct {
223+ Error string `json:"error"`
224+ Status int `json:"status"`
225+ }
226+
227+ var jsonBody verboseResponse
228+ json .Unmarshal (body , & jsonBody )
229+
230+ if jsonBody .Status != 1 {
231+ errMsg := fmt .Sprintf ("error=%s; status=%d; httpCode=%d" , jsonBody .Error , jsonBody .Status , resp .StatusCode )
232+ return wrapErr (& ErrTrackFailed {Message : errMsg })
188233 }
189234
190235 return nil
@@ -196,16 +241,28 @@ func New(token, apiURL string) Mixpanel {
196241 return NewFromClient (http .DefaultClient , token , apiURL )
197242}
198243
199- // Creates a client instance using the specified client instance. This is useful
244+ // NewWithSecret returns the client instance using a secret.If apiURL is blank,
245+ // the default will be used ("https://api.mixpanel.com").
246+ func NewWithSecret (token , secret , apiURL string ) Mixpanel {
247+ return NewFromClientWithSecret (http .DefaultClient , token , secret , apiURL )
248+ }
249+
250+ // NewFromClient creates a client instance using the specified client instance. This is useful
200251// when using a proxy.
201252func NewFromClient (c * http.Client , token , apiURL string ) Mixpanel {
253+ return NewFromClientWithSecret (c , token , "" , apiURL )
254+ }
255+
256+ // NewFromClientWithSecret creates a client instance using the specified client instance and secret.
257+ func NewFromClientWithSecret (c * http.Client , token , secret , apiURL string ) Mixpanel {
202258 if apiURL == "" {
203259 apiURL = "https://api.mixpanel.com"
204260 }
205261
206262 return & mixpanel {
207263 Client : c ,
208264 Token : token ,
265+ Secret : secret ,
209266 ApiURL : apiURL ,
210267 }
211268}
0 commit comments