File Coverage

blib/lib/WWW/Datafinder.pm
Criterion Covered Total %
statement 35 78 44.8
branch 0 20 0.0
condition 0 10 0.0
subroutine 12 16 75.0
pod 1 1 100.0
total 48 125 38.4


line stmt bran cond sub pod time code
1             package WWW::Datafinder;
2              
3 2     2   34685 use 5.010;
  2         5  
4 2     2   6 use strict;
  2         3  
  2         34  
5 2     2   6 use warnings;
  2         5  
  2         83  
6              
7             =head1 NAME
8              
9             WWW::Datafinder - Perl API for Datafinder L<< http://datafinder.com >> API for marketing data append
10              
11             =head1 VERSION
12              
13             Version 0.01
14              
15             =cut
16              
17             our $VERSION = '0.01';
18              
19 2     2   7 use Carp qw(cluck);
  2         1  
  2         97  
20 2     2   1699 use Data::Dumper;
  2         12585  
  2         92  
21 2     2   833 use REST::Client;
  2         65052  
  2         56  
22 2     2   1184 use JSON::XS;
  2         7690  
  2         103  
23 2     2   8 use URI;
  2         4  
  2         39  
24 2     2   6 use Scalar::Util qw(blessed reftype);
  2         3  
  2         130  
25 2     2   930 use Readonly;
  2         5250  
  2         87  
26 2     2   9 use Exporter 'import';
  2         3  
  2         40  
27              
28 2     2   558 use Mouse;
  2         30402  
  2         8  
29              
30             #** @attr public api_key $api_key API access key
31             #*
32             has api_key => ( isa => 'Str', is => 'rw', required => 1 );
33              
34             #** @attr public Int $retries How many times retry upon timeout
35             #*
36             has retries => ( isa => 'Int', is => 'rw', default => 5 );
37              
38             #** @attr protected String $base_url Base REST URL
39             #*
40             has base_url => (
41             isa => 'Str',
42             is => 'rw',
43             default => 'http://api.datafinder.com/qdf.php'
44             );
45              
46             #** @attr protected CodeRef $ua Reference to the REST UA
47             #*
48             has ua => (
49             isa => 'Object',
50             is => 'rw',
51             lazy => 1,
52             init_arg => undef,
53             default => sub {
54             return REST::Client->new();
55             }
56             );
57              
58             #** @attr public String $error_message Error message regarding the last failed operation
59             #*
60             has error_message =>
61             ( isa => 'Str', is => 'rw', init_arg => undef, default => '' );
62              
63             sub _url {
64 0     0     my ( $self, $query_params ) = @_;
65              
66 0   0       $query_params //= {};
67 0           my $uri = URI->new( $self->base_url, 'http' );
68 0           $uri->query_form($query_params);
69             #print "URL=".$uri->as_string;
70 0           return $uri->as_string;
71             }
72              
73             sub _process_response {
74 0     0     my ( $self, $response ) = @_;
75              
76 0 0         if ($@) {
    0          
77 0           $self->error_message("Error $@");
78 0           return undef;
79             } elsif ( !blessed($response) ) {
80 0           $self->error_message(
81             "Unknown response $response from the REST client instead of object"
82             );
83 0           return undef;
84             }
85             print "Got response:"
86             . Dumper( $response->responseCode() ) . "/"
87             . Dumper( $response->responseContent() ) . "\n"
88 0 0         if $ENV{DEBUG};
89 0           my $code = $response->responseCode();
90 0           my $parsed_content = eval { decode_json( $response->responseContent() ) };
  0            
91 0 0         if ($@) {
92 0           cluck( "Cannot parse response content "
93             . $response->responseContent()
94             . ", error msg: $@. Is this JSON?" );
95 0           $parsed_content = {};
96             }
97 0 0         print "parsed " . Dumper($parsed_content) if $ENV{DEBUG};
98 0 0 0       if ( $code ne '200' && $code ne '201' ) {
99 0           my $err = "Received error code $code from the server instead of "
100             . 'expected 200/201';
101 0 0 0       if ( reftype($parsed_content) eq 'HASH'
102             && $parsed_content->{message} )
103             {
104             $err .=
105             "\nError message from server: "
106             . $parsed_content->{message}
107             . (
108             $parsed_content->{error_code}
109 0 0         ? ' (' . $parsed_content->{error_code} . ')'
110             : q{}
111             );
112              
113 0           $self->error_message($err);
114             }
115 0           return undef;
116             }
117              
118 0           $self->error_message(q{});
119 0           return $parsed_content;
120             }
121              
122             sub _transaction {
123 0     0     my ( $self, $query_params, $data ) = @_;
124              
125 0   0       $data //= {};
126 0 0         $query_params->{k2} = $self->api_key unless $query_params->{k2};
127 0           my $url = $self->_url($query_params);
128 0           my $headers = { 'Content-Type' => 'application/json' };
129 0           my $response;
130             # print "JSON data ".encode_json($data);
131 0           for my $try ( 1 .. $self->retries ) {
132             $response =
133 0           eval { $self->ua->POST( $url, encode_json($data), $headers ); };
  0            
134 0 0         if ($@) {
135 0           cluck($@);
136 0           sleep( int( 1 + rand() * 3 ) * $try );
137             }
138             }
139 0           return $self->_process_response($response);
140             }
141              
142             =head1 SYNOPSIS
143              
144             use WWW::Datafinder;
145             use Text::CSV_XS;
146             use Data::Dumper;
147              
148             my $csv = Text::CSV_XS->new;
149             my $df = WWW::Datafinder->new( {
150             api_key => '456' # place real API key here
151             }) or die 'Cannot create Datafinder object';
152              
153             # process a CSV file with 6 columns:
154             # First Name, Last Name, Address, City, State, ZIP
155             while(<>) {
156             chomp;
157             my $status = $csv->parse($_);
158             unless ($status) {
159             warn qq{Cannot parse '$_':}.$csv->error_diag();
160             next;
161             }
162             my ($name, $surname, $addr, $city, $state, $zip) = $csv->fields();
163             my $data = {
164             d_first => $name,
165             d_last => $surname,
166             d_fulladdr => $addr,
167             d_city => $city,
168             d_state => $state,
169             d_zip => $zip
170             };
171             my $res = $df->append_email($data);
172             if ($res) {
173             if ( $res->{'num-results'} ) {
174             # there is a match!
175             print "Got a match for $name $surname: " . Dumper( $res->{results} );
176             }
177             }
178             }
179            
180             =head1 CONSTRUCTOR
181              
182             =head2 new( hashref )
183              
184             Creates a new object, acceptable parameters are:
185              
186             =over 16
187              
188             =item C - (required) the key to be used for read operations
189              
190             =item C - how many times retry the request upon error (e.g. timeout). Default is 5.
191              
192             =back
193              
194             =head1 METHODS
195              
196             =head2 append_email( $data )
197              
198             Attempts to append customer's email based on his/her name and address (or phone
199             number). Please see L<< https://datafinder.com/api/docs-demo >> for more
200             info regarding the parameter names and format of their values in C<$data>.
201             Returns a reference to a hash, which contains the response
202             received from the server.
203             Returns C on failure, application then may call
204             C method to get the detailed info about the error.
205              
206             my $res = $df->append_email(
207             {
208             d_fulladdr => $cust->{Address},
209             d_city => $cust->{City},
210             d_state => $cust->{State},
211             d_zip => $cust->{ZIP},
212             d_first => $cust->{Name},
213             d_last => $cust->{Surname}
214             }
215             );
216             if ( $res ) {
217             if ( $res->{'num-results'} ) {
218             # there is a match!
219             print "Got a match: " . Dumper( $res->{results} );
220             }
221             } else {
222             warn 'Something went wrong ' . $df->error_message();
223             }
224              
225             =cut
226              
227             sub append_email {
228 0     0 1   my ( $self, $data ) = @_;
229 0           $data->{service} = 'email';
230              
231 0           return $self->_transaction( $data, {} );
232             }
233              
234             =head2 error_message()
235              
236             Returns the detailed explanation of the last error. Empty string if
237             everything went fine.
238              
239             my $res = $df->append_email($cust_data);
240             unless ($res) {
241             warn 'Something went wrong '.$df->error_message();
242             }
243              
244             If you want to troubleshoot the data being sent between the client and the
245             server - set environment variable DEBUG to a positive value.
246              
247             =cut
248              
249             =head1 AUTHOR
250              
251             Andrew Zhilenko, C<< >>
252             (c) Putin Huylo LLC, 2017
253              
254             =head1 BUGS
255              
256             Please report any bugs or feature requests to C, or through
257             the web interface at L.
258             I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
259              
260             =head1 SUPPORT
261              
262             You can find documentation for this module with the perldoc command.
263              
264             perldoc WWW::Datafinder
265              
266              
267             You can also look for information at:
268              
269             =over 4
270              
271             =item * RT: CPAN's request tracker (report bugs here)
272              
273             L
274              
275             =item * AnnoCPAN: Annotated CPAN documentation
276              
277             L
278              
279             =back
280              
281             =head1 LICENSE AND COPYRIGHT
282              
283             Copyright 2017 Putin Huylo LLC
284              
285             This program is free software; you can redistribute it and/or modify it
286             under the terms of the the Artistic License (2.0). You may obtain a
287             copy of the full license at:
288              
289             L
290              
291             Any use, modification, and distribution of the Standard or Modified
292             Versions is governed by this Artistic License. By using, modifying or
293             distributing the Package, you accept this license. Do not use, modify,
294             or distribute the Package, if you do not accept this license.
295              
296             If your Modified Version has been derived from a Modified Version made
297             by someone other than you, you are nevertheless required to ensure that
298             your Modified Version complies with the requirements of this license.
299              
300             This license does not grant you the right to use any trademark, service
301             mark, tradename, or logo of the Copyright Holder.
302              
303             This license includes the non-exclusive, worldwide, free-of-charge
304             patent license to make, have made, use, offer to sell, sell, import and
305             otherwise transfer the Package with respect to any patent claims
306             licensable by the Copyright Holder that are necessarily infringed by the
307             Package. If you institute patent litigation (including a cross-claim or
308             counterclaim) against any party alleging that the Package constitutes
309             direct or contributory patent infringement, then this Artistic License
310             to you shall terminate on the date that such litigation is filed.
311              
312             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
313             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
314             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
315             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
316             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
317             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
318             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
319             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
320              
321              
322             =cut
323              
324             __PACKAGE__->meta->make_immutable;
325              
326             1; # End of WWW::Datafinder