File Coverage

lib/WWW/Mixpanel.pm
Criterion Covered Total %
statement 16 106 15.0
branch 0 56 0.0
condition 0 12 0.0
subroutine 6 16 37.5
pod 6 7 85.7
total 28 197 14.2


line stmt bran cond sub pod time code
1             package WWW::Mixpanel;
2              
3 4     4   122375 use strict;
  4         11  
  4         137  
4 4     4   141 use warnings;
  4         8  
  4         118  
5 4     4   7194 use LWP::UserAgent;
  4         262205  
  4         155  
6 4     4   3949 use MIME::Base64;
  4         9395  
  4         320  
7 4     4   5001 use JSON;
  4         62586  
  4         39  
8              
9             BEGIN {
10 4     4   6617 $WWW::Mixpanel::VERSION = '0.07';
11             }
12              
13             sub new {
14 0     0 1   my ( $class, $token, $use_ssl, $api_key, $api_secret ) = @_;
15 0 0         die "You must provide your API token." unless $token;
16              
17 0           my $ua = LWP::UserAgent->new;
18 0           $ua->timeout(180);
19 0           $ua->env_proxy;
20              
21 0           my $json = JSON->new->allow_blessed(1)->convert_blessed(1);
22              
23 0           bless { token => $token,
24             use_ssl => $use_ssl,
25             api_key => $api_key,
26             api_secret => $api_secret,
27             data_api_default_expire_seconds => 180,
28             track_api => 'api.mixpanel.com/track/', # trailing slash required
29             data_api => 'mixpanel.com/api/2.0/',
30             people_api => 'api.mixpanel.com/engage/',
31             json => $json,
32             ua => $ua, }, $class;
33             }
34              
35             sub people_set{
36 0     0 1   my ( $self, $distinct_id, %params ) = @_;
37              
38 0 0         die "Distinct User Id required" unless $distinct_id;
39              
40 0           my $data = { '$set' => \%params,
41             '$distinct_id' => $distinct_id,
42             '$ip' => 0,
43             '$token' => $self->{token}
44             };
45              
46 0 0         my $res =
47             $self->{ua}->post( $self->{use_ssl}
48             ? "https://$self->{people_api}"
49             : "http://$self->{people_api}",
50             { 'data' => encode_base64( $self->{json}->encode($data), '' ) } );
51              
52 0 0         if ( $res->is_success ) {
53 0 0         if ( $res->content == 1 ) {
54 0           return 1;
55             }
56             else {
57 0           die "Failure from api: " . $res->content;
58             }
59             }
60             else {
61 0           die "Failed sending event: " . $self->_res($res);
62             }
63             }
64              
65             sub people_increment{
66 0     0 1   my ( $self, $distinct_id, %params ) = @_;
67              
68 0 0         die "Distinct User Id required" unless $distinct_id;
69              
70 0           my $data = { '$add' => \%params,
71             '$distinct_id' => $distinct_id,
72             '$ip' => 0,
73             '$token' => $self->{token}
74             };
75              
76 0 0         my $res =
77             $self->{ua}->post( $self->{use_ssl}
78             ? "https://$self->{people_api}"
79             : "http://$self->{people_api}",
80             { 'data' => encode_base64( $self->{json}->encode($data), '' ) } );
81              
82 0 0         if ( $res->is_success ) {
83 0 0         if ( $res->content == 1 ) {
84 0           return 1;
85             }
86             else {
87 0           die "Failure from api: " . $res->content;
88             }
89             }
90             else {
91 0           die "Failed sending event: " . $self->_res($res);
92             }
93             }
94              
95             sub people_append_transactions{
96 0     0 0   my ( $self, $distinct_id, %params ) = @_;
97              
98 0 0         die "Distinct User Id required" unless $distinct_id;
99              
100 0           my $data = { '$append' => {'$transactions' => \%params},
101             '$distinct_id' => $distinct_id,
102             '$ip' => 0,
103             '$token' => $self->{token}
104             };
105              
106 0 0         my $res =
107             $self->{ua}->post( $self->{use_ssl}
108             ? "https://$self->{people_api}"
109             : "http://$self->{people_api}",
110             { 'data' => encode_base64( $self->{json}->encode($data), '' ) } );
111              
112              
113             }
114              
115             sub people_track_charge{
116 0     0 1   my ( $self, $distinct_id, $amount ) = @_;
117              
118 0 0         die "Distinct User Id required" unless $distinct_id;
119              
120 0           return $self->people_append_transactions( $distinct_id, '$time' => time(), '$amount' => $amount);
121             }
122              
123              
124             sub track {
125 0     0 1   my ( $self, $event, %params ) = @_;
126              
127 0 0         die "You must provide an event name" unless $event;
128              
129 0   0       $params{time} ||= time();
130 0           $params{token} = $self->{token};
131              
132 0           my $data = { event => $event,
133             properties => \%params, };
134              
135 0 0         my $res =
136             $self->{ua}->post( $self->{use_ssl}
137             ? "https://$self->{track_api}"
138             : "http://$self->{track_api}",
139             { 'data' => encode_base64( $self->{json}->encode($data), '' ) } );
140              
141 0 0         if ( $res->is_success ) {
142 0 0         if ( $res->content == 1 ) {
143 0           return 1;
144             }
145             else {
146 0           die "Failure from api: " . $res->content;
147             }
148             }
149             else {
150 0           die "Failed sending event: " . $self->_res($res);
151             }
152             } # end track
153              
154             sub data {
155 0     0 1   my $self = shift;
156 0           my $methods = shift;
157 0           my %params = @_;
158              
159 0 0         $methods = [$methods] if !ref($methods);
160 0           my $api_methods = join( '/', @$methods );
161              
162 0           $self->_data_params_to_json( $api_methods, \%params );
163              
164 0   0       $params{format} ||= 'json';
165 0 0         $params{expire} = time() + $self->{data_api_default_expire_seconds}
166             if !defined( $params{expire} );
167 0   0       $params{api_key} = $self->{api_key} || die 'API Key must be specified for data requests';
168 0   0       my $api_secret = $self->{api_secret} || die 'API Secret must be specified for data requests';
169              
170 0           my $sig = $self->_create_sig( $api_secret, \%params );
171 0           $params{sig} = $sig;
172              
173 0 0         my $url =
174             $self->{use_ssl}
175             ? "https://$self->{data_api}"
176             : "http://$self->{data_api}";
177 0           $url .= $api_methods;
178              
179             # We have to hand-build the url because HTTP::REQUEST/HEADER was
180             # changing underscores and capitalization, and Mixpanel is sensitive
181             # about such things.
182 0           my $ps = join( '&', map {"$_=$params{$_}"} sort keys %params );
  0            
183 0           my $res = $self->{ua}->get( $url . '/?' . $ps );
184              
185 0 0         if ( $res->is_success ) {
186 0           my $reso = $res->content;
187 0 0         $reso = $self->{json}->decode($reso) if $params{format} eq 'json';
188 0           return $reso;
189             }
190             else {
191 0           die "Failed sending event: " . $self->_res($res);
192             }
193             } # end data
194              
195             # Calculate data request signature according to spec.
196             sub _create_sig {
197 0     0     my $self = shift;
198 0           my $api_secret = shift;
199 0           my $params = shift;
200              
201 0           require Digest::MD5;
202 0           my $pstr = join( '', map { $_ . '=' . $params->{$_} } sort keys %$params ) . $api_secret;
  0            
203 0           return Digest::MD5::md5_hex($pstr);
204             }
205              
206             sub _data_params_to_json {
207 0     0     my $self = shift;
208 0           my $api = shift;
209 0           my $params = shift;
210              
211             # A few API calls require json encoded arrays, so transform those here.
212 0           my $toj;
213 0 0         if ( $api eq 'events' ) {
214 0           $toj = 'event';
215             }
216 0 0         if ( $api eq 'events/properties' ) {
217 0           $toj = 'values';
218             }
219 0 0         if ( $api eq 'arb_funnels' ) {
220 0           $toj = 'events';
221             }
222              
223 0 0 0       if ( $toj && defined( $params->{$toj} ) ) {
224 0 0         $params->{$toj} = [ $params->{$toj} ] if !ref( $params->{$toj} );
225 0           $params->{$toj} = $self->{json}->encode( $params->{$toj} );
226             }
227              
228             } # end _data_params_to_json
229              
230             sub _res {
231 0     0     my ( $self, $res ) = @_;
232              
233 0 0         if ( $res->code == 500 ) {
    0          
234 0           return "Mixpanel service error. The service might be down.";
235             }
236             elsif ( $res->code == 400 ) {
237 0           return "Bad Request Elements: " . $res->content;
238             }
239             else {
240 0           return "Unknown error. " . $res->message;
241             }
242             }
243              
244             1;
245              
246             __END__