File Coverage

blib/lib/Paymill/REST/Base.pm
Criterion Covered Total %
statement 18 90 20.0
branch 0 36 0.0
condition 0 14 0.0
subroutine 6 11 54.5
pod n/a
total 24 151 15.8


line stmt bran cond sub pod time code
1             package Paymill::REST::Base;
2              
3 9     9   98331 use Moose::Role;
  9         4299827  
  9         45  
4 9     9   59055 use MooseX::Types::URI qw(Uri);
  9         1981442  
  9         64  
5              
6             with 'Paymill::REST::Operations::Find';
7             with 'Paymill::REST::Operations::List';
8             with 'Paymill::REST::Operations::Create';
9              
10 9     9   26195 use LWP::UserAgent;
  9         357454  
  9         365  
11 9     9   99 use HTTP::Request;
  9         30  
  9         262  
12 9     9   6743 use JSON::XS;
  9         47374  
  9         10131  
13              
14             has type => (is => 'ro', required => 1, isa => 'Str');
15             has debug => (is => 'rw', required => 0, isa => 'Bool', default => 0);
16             has proxy => (is => 'rw', required => 0, isa => Uri, coerce => 1);
17             has api_key => (is => 'ro', required => 1, isa => 'Str', default => sub {$Paymill::REST::PRIVATE_KEY});
18             has auth_netloc => (is => 'rw', required => 0, isa => 'Str', default => 'api.paymill.com:443');
19             has auth_realm => (is => 'rw', required => 0, isa => 'Str', default => 'Api Access');
20             has verify_hostname => (is => 'rw', required => 0, isa => 'Bool', default => 1);
21              
22             has useragent => (
23             is => 'ro',
24             required => 0,
25             isa => 'LWP::UserAgent',
26             lazy => 1,
27             builder => '_build_useragent',
28             clearer => '_reset_useragent',
29             );
30              
31             has agent_name =>
32             (is => 'rw', required => 0, isa => 'Str', default => sub { "Paymill::REST/" . $Paymill::REST::VERSION });
33             has base_url =>
34             (is => 'rw', required => 0, isa => Uri, coerce => 1, default => sub { to_Uri('https://api.paymill.com/v2/') });
35              
36             sub _build_item {
37 0     0     my $self = shift;
38 0           my $item_attrs = shift;
39              
40             # Find type. Coercing will not have ->type but _type as item attribute.
41 0           my $item_type;
42 0 0         $item_type = $self->type if $self->can('type');
43 0 0         $item_type = delete $item_attrs->{_type} if exists $item_attrs->{_type};
44              
45             # Remove "data" root if it exists
46 0 0 0       if (ref $item_attrs eq 'HASH' && exists $item_attrs->{data}) {
47 0           $item_attrs = $item_attrs->{data};
48             }
49              
50             # Deleting objects may result in no returned data (eg. deleting offers)
51 0 0 0       return if ref $item_attrs eq 'ARRAY' && scalar @$item_attrs < 1;
52              
53             # Passing the factory to the item so it can call the API directly (eg. for delete)
54             # For coercing, $self is not a blessed object so we need to create one. This is
55             # ugly because it's losing all custom settings.
56 0 0         if (ref $self) {
57 0           $item_attrs->{_factory} = $self;
58             } else {
59 0           my $factory = 'Paymill::REST::' . ucfirst($item_type) . 's';
60 0           $item_attrs->{_factory} = $factory->new;
61             }
62              
63             # Remove empty attributes because of validation
64 0           foreach (keys %$item_attrs) {
65 0 0         delete $item_attrs->{$_} unless defined $item_attrs->{$_};
66             }
67              
68             # Create new instance
69 0           my $module = 'Paymill::REST::Item::' . ucfirst($item_type);
70 0           my $item_instance = $module->new($item_attrs);
71              
72 0           return $item_instance;
73             }
74              
75             sub _build_items {
76 0     0     my $self = shift;
77 0           my $hashed_items = shift;
78 0           my $type = shift; # optional, for coercing
79              
80 0 0         $type = $self->type if $self->can('type');
81              
82             # Remove "data" root if it exists
83 0 0 0       if (ref $hashed_items eq 'HASH' && exists $hashed_items->{data}) {
84 0           $hashed_items = $hashed_items->{data};
85             }
86              
87 0           my @items;
88 0           foreach my $item_attrs (@$hashed_items) {
89              
90             # Some objects are empty and only return the identifier for the object
91 0 0         unless (ref $item_attrs) {
92 0           $item_attrs = { id => $item_attrs };
93             }
94              
95             # Build single item
96 0           push @items, $self->_build_item({ %$item_attrs, _type => $type });
97             }
98              
99             # Return how the caller want it
100 0 0         return wantarray ? @items : \@items;
101             }
102              
103             sub _build_useragent {
104 0     0     my $self = shift;
105 0           my $ua = LWP::UserAgent->new;
106              
107 0           $self->_debug("New user agent " . $self->agent_name);
108 0           $ua->agent($self->agent_name);
109              
110 0 0         if ($self->proxy) {
111 0           $self->_debug("Using https proxy " . $self->proxy);
112 0           $ua->proxy('https', $self->proxy);
113             }
114              
115 0           $self->_debug("Authenticate with " . $self->api_key);
116 0           $ua->credentials($self->auth_netloc, $self->auth_realm, $self->api_key, '');
117              
118 0           $ua->ssl_opts(verify_hostname => $self->verify_hostname);
119              
120 0           return $ua;
121             }
122              
123             sub _get_response {
124 0     0     my $self = shift;
125 0           my $params = shift;
126              
127 0   0       my $uri = $params->{uri} || die "No URI given!";
128 0           my $method = $params->{method};
129 0 0         my $query = $params->{query} ? $params->{query} : undef;
130              
131 0 0         $self->useragent->requests_redirectable([]) if $params->{noredirect};
132              
133 0           my $res;
134              
135 0           $self->_debug("New request, URI is $uri");
136 0           my $req = HTTP::Request->new;
137 0           $req->header(Accept => 'application/json');
138 0           $req->content_type('application/json');
139 0           $req->uri($uri);
140              
141 0 0         if ($query) {
142 0           $self->_debug("Adding params: " . encode_json($query));
143 0           $req->uri->query_form($query);
144             }
145              
146 0 0 0       if (defined $method && $method) {
147 0           $self->_debug("Explicit method $method");
148 0           $req->method($method);
149             } else {
150 0           $req->method('GET');
151             }
152              
153 0           $res = $self->useragent->request($req);
154 0           $self->_reset_useragent;
155              
156 0 0         unless ($res->is_success) {
157 0           $self->_debug("Request unsuccessful: " . $res->code);
158 0           $self->_debug("Content: '" . $res->content . "'");
159 0           die "Request error: " . $res->status_line . "\n";
160             } else {
161 0           $self->_debug("Request successful: " . $res->code);
162 0           $self->_debug("Content: '" . $res->content . "'");
163 0 0         if ($res->content !~ /^\s*$/) {
164 0           return decode_json($res->content);
165             } else {
166 0           return undef;
167             }
168             }
169             }
170              
171             =head2 _debug
172              
173             Parameters:
174              
175             =over
176              
177             =item C<@msgs>
178              
179             =back
180              
181             Small debug message handler that C<warn>s C<@msgs> joined with a line break. Only prints if C<debug> set to C<true>.
182              
183             =cut
184              
185             sub _debug {
186 0     0     my $self = shift;
187 0 0         warn "[" . localtime . "] " . join("\n", @_) . "\n" if $self->debug;
188             }
189              
190 9     9   98 no Moose::Role;
  9         26  
  9         136  
191             1;
192             __END__
193              
194             =encoding utf-8
195              
196             =head1 NAME
197              
198             Paymill::REST - Base class for item factories
199              
200             =head1 SEE ALSO
201              
202             L<Paymill::REST> for documentation.
203              
204             =head1 AUTHOR
205              
206             Matthias Dietrich E<lt>perl@rainboxx.deE<gt>
207              
208             =head1 COPYRIGHT
209              
210             Copyright 2013 - Matthias Dietrich
211              
212             =head1 LICENSE
213              
214             This library is free software; you can redistribute it and/or modify
215             it under the same terms as Perl itself.