File Coverage

blib/lib/Net/API/Stripe/List.pm
Criterion Covered Total %
statement 7 128 5.4
branch 0 68 0.0
condition 0 50 0.0
subroutine 3 24 12.5
pod 10 15 66.6
total 20 285 7.0


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## Stripe API - ~/lib/Net/API/Stripe/List.pm
3             ## Version v0.200.2
4             ## Copyright(c) 2020 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest <@sitael.tokyo.deguest.jp>
6             ## Created 2019/11/02
7             ## Modified 2020/05/15
8             ##
9             ##----------------------------------------------------------------------------
10             package Net::API::Stripe::List;
11             ## To be inherited
12             BEGIN
13             {
14 1     1   400 use strict;
  1         1  
  1         29  
15 1     1   4 use parent qw( Net::API::Stripe::Generic );
  1         1  
  1         6  
16             # use B;
17             # use B::Deparse;
18 1     1   1183 our( $VERSION ) = 'v0.200.2';
19             };
20              
21             ## Provide our own version of as_hash to avoid our helper methods from being called by Module::Generic::as_hash
22             sub as_hash
23             {
24 0     0 1   my $self = shift( @_ );
25 0           my $p = {};
26 0 0 0       $p = shift( @_ ) if( scalar( @_ ) == 1 && ref( $_[0] ) eq 'HASH' );
27 0           my $data = $self->_data;
28 0           my $res = [];
29 0           foreach my $this ( @$data )
30             {
31 0 0 0       if( $self->_is_object( $this ) && $this->can( 'as_hash' ) )
32             {
33 0           my $v = $this->as_hash( $p );
34 0           push( @$res, $v );
35             }
36             }
37 0           return( { data => $res } );
38             }
39              
40 0     0 1   sub object { CORE::shift->_set_get_scalar( 'object', @_ ); }
41              
42 0     0 0   sub count { return( CORE::shift->_set_get_scalar( 'total_count', @_ ) ); }
43              
44 0     0 1   sub data { return( shift->_data( @_ ) ); }
45              
46 0     0 1   sub has_more { CORE::shift->_set_get_boolean( 'has_more', @_ ); }
47              
48 0     0 1   sub url { CORE::shift->_set_get_uri( 'url', @_ ); }
49              
50             sub total_count
51             {
52 0     0 1   my $self = shift( @_ );
53 0 0         if( @_ )
54             {
55 0           $self->_set_get_scalar( 'total_count', @_ );
56             }
57 0           my $total = $self->_set_get_scalar( 'total_count' );
58 0 0         if( !CORE::length( $total ) )
59             {
60 0           return( $self->_data->size );
61             }
62             else
63             {
64 0           return( $total );
65             }
66             }
67              
68             ## Additional methods for navigation to be used like $list->next or $list->prev
69             sub get
70             {
71 0     0 1   my $self = CORE::shift( @_ );
72 0 0 0       my $pos = @_ ? int( CORE::shift( @_ ) ) : ( $self->{ '_pos' } || 0 );
73 0           my $data = $self->_data;
74 0           return( $data->[ $pos ] );
75             }
76              
77             sub length
78             {
79 0     0 1   my $self = CORE::shift( @_ );
80 0           my $data = $self->_data;
81 0           return( scalar( @$data ) );
82             }
83              
84             sub next
85             {
86 0     0 1   my $self = CORE::shift( @_ );
87 0 0         $self->{ '_pos' } = -1 if( !exists( $self->{ '_pos' } ) );
88 0           my $data = $self->_data;
89 0 0         $self->message( 3, "Is there more data? ", $self->has_more ? 'yes' : 'no' );
90 0 0 0       if( $self->{ '_pos' } + 1 < scalar( @$data ) )
    0 0        
91             {
92 0           return( $data->[ ++$self->{ '_pos' } ] );
93             }
94             elsif( $self->has_more && scalar( @$data ) && $self->_is_object( $data->[-1] ) )
95             {
96 0           my $last_id = $data->[-1]->id;
97 0 0         $self->{_limit} = scalar( @$data ) if( !$self->{_limit} );
98 0           $self->messagef( 3, "Fetching more starting from last id $last_id with _limit value %d", $self->{_limit} );
99             my $opts =
100             {
101             starting_after => $last_id,
102 0   0       limit => ( $self->{_limit} || 10 ),
103             };
104 0   0       my $hash = $self->parent->get( $self->url, $opts ) || return;
105 0 0   0     return( $self->error( "Cannot find property 'object' in this hash reference: ", sub{ $self->dumper( $hash ) } ) ) if( !CORE::exists( $hash->{object} ) );
  0            
106 0   0       my $class = $self->_object_type_to_class( $hash->{object} ) || return;
107 0   0       my $list = $self->parent->_response_to_object( $class, $hash ) || return;
108 0           $data = $list->_data;
109 0           $self->{data} = $data;
110 0           $self->{_pos} = 0;
111 0 0         return( '' ) if( !scalar( @$data ) );
112 0           return( $data->[ $self->{_pos} ] );
113             }
114             else
115             {
116             ## We do not return undef(), which we use to signal errors
117 0           return( '' );
118             }
119             }
120              
121             sub prev
122             {
123 0     0 1   my $self = CORE::shift( @_ );
124 0 0         $self->{ '_pos' } = -1 if( !exists( $self->{ '_pos' } ) );
125 0           my $data = $self->_data;
126 0           my $ret = $data->[ $pos ];
127 0 0 0       if( $self->{ '_pos' } - 1 >= 0 )
    0          
128             {
129 0           return( $data->[ --$self->{ '_pos' } ] );
130             }
131             elsif( scalar( @$data ) && $self->_is_object( $data->[0] ) )
132             {
133 0           my $first_id = $data->[0]->id;
134 0 0         $self->{_limit} = scalar( @$data ) if( !$self->{_limit} );
135 0           $self->messagef( 3, "Fetching previous set of data starting from first id $first_id with _limit value %d", $self->{_limit} );
136             my $opts =
137             {
138             ending_before => $first_id,
139 0   0       limit => ( $self->{_limit} || 10 ),
140             };
141 0   0       my $hash = $self->parent->get( $self->url, $opts ) || return;
142 0 0   0     return( $self->error( "Cannot find property 'object' in this hash reference: ", sub{ $self->dumper( $hash ) } ) ) if( !CORE::exists( $hash->{object} ) );
  0            
143 0   0       my $class = $self->_object_type_to_class( $hash->{object} ) || return;
144 0   0       my $list = $self->parent->_response_to_object( $class, $hash ) || return;
145 0           $data = $list->_data;
146 0           $self->{data} = $data;
147             ## $self->_data( $data );
148 0           $self->{_pos} = $#$data;
149 0 0         return( '' ) if( !scalar( @$data ) );
150 0           return( $data->[ $self->{_pos} ] );
151             }
152             else
153             {
154             ## We do not return undef(), which we use to signal errors
155 0           return( '' );
156             }
157             }
158              
159             sub pop
160             {
161 0     0 0   my $self = CORE::shift( @_ );
162 0           return( $self->_data->pop );
163             }
164              
165             sub push
166             {
167 0     0 0   my $self = CORE::shift( @_ );
168 0   0       my $this = CORE::shift( @_ ) || return( $self->error( "Nothing was provided to add to the list of object." ) );
169 0 0         $self->_check( $this ) || return;
170 0           $self->_data->push( $this );
171 0           return( $self );
172             }
173              
174             sub shift
175             {
176 0     0 0   my $self = CORE::shift( @_ );
177 0           return( $self->_data->shift );
178             }
179              
180             sub unshift
181             {
182 0     0 0   my $self = CORE::shift( @_ );
183 0   0       my $this = CORE::shift( @_ ) || return( $self->error( "Nothing was provided to add to the list of object." ) );
184 0 0         $self->_check( $this ) || return;
185 0           $self->_data->unshift( $this );
186 0           return( $self );
187             }
188              
189             sub _check
190             {
191 0     0     my $self = CORE::shift( @_ );
192 0   0       my $this = CORE::shift( @_ ) || return( $self->error( "No data was provided to check." ) );
193 0 0         return( $self->error( "Data provided is not an object." ) ) if( !$self->_is_object( $this ) );
194             ## Check if there is any data and if there is find out what kind of object we are holding so we can maintain consistency
195 0           my $data = $self->_data;
196 0           my $obj_name;
197 0 0 0       if( scalar( @$data ) && $self->_is_object( $data->[0] ) )
198             {
199 0 0         $obj_name = $data->[0]->object if( $data->[0]->can( 'object' ) );
200             }
201 0 0         if( $this->can( 'object' ) )
202             {
203 0 0         return( $self->error( "Object provided ($this) has an object type (", $this->object, ") different from the ones currently in our stack ($obj_name)." ) ) if( $this->object ne $obj_name );
204             }
205 0           return( $this );
206             }
207              
208             sub _data
209             {
210 0     0     my $self = CORE::shift( @_ );
211 0           my $field = 'data';
212 0 0         if( @_ )
213             {
214 0           my $ref = CORE::shift( @_ );
215 0 0         return( $self->error( "I was expecting an array ref, but instead got '$ref'. _is_array returned: '", $self->_is_array( $ref ), "'" ) ) if( !$self->_is_array( $ref ) );
216 0           my $arr = [];
217             ## Are we provided with an array of existing objects? No need to do anything then
218 0 0         if( $self->_is_object( $ref->[0] ) )
219             {
220 0           $arr = $ref;
221             }
222             else
223             {
224 0 0         if( scalar( @$ref ) )
225             {
226 0           my $type;
227 0 0         if( $self->_is_object( $ref->[0] ) )
228             {
229 0 0         return( $self->error( "I found an array of objects, but they do not have the method \"object\"." ) ) if( !$ref->[0]->can( 'object' ) );
230 0   0       $type = $ref->[0]->object || return( $self->error( "Somehow, the object property for this object (", $ref->[0], ") is empty." ) );
231             }
232             else
233             {
234 0 0         return( $self->error( "I was expecting an array of hash reference, but instead of hash I found $ref->[0]" ) ) if( ref( $ref->[0] ) ne 'HASH' );
235 0 0   0     return( $self->error( "Found an hash reference in this array, but it is empty: ", sub{ $self->dumper( $ref ) } ) ) if( !scalar( keys( %{$ref->[0]} ) ) );
  0            
  0            
236 0   0 0     $type = $ref->[0]->{object} || return( $self->error( "I was expecting a string in property 'object', but found nothing: ", sub{ $self->dumper( $ref ) } ) );
  0            
237             }
238 0   0       my $class = $self->_object_type_to_class( $type ) || return( $self->error( "Could not find corresponding class for ojbect type \"$type\"." ) );
239 0           $arr = $self->_set_get_object_array_object( $field, $class, $ref );
240             ## Store this value used by next() and prev() to replicate the query with the right limit
241             ## If initial query made by the user was 10 this array would be 10 or less if there is no more data
242             }
243             }
244 0           $self->{ $field } = $arr;
245             }
246 0 0 0       if( !$self->{ $field } || !$self->_is_object( $self->{ $field } ) )
247             {
248 0           my $o = Module::Generic::Array->new( $self->{ $field } );
249 0           $self->{ $field } = $o;
250             }
251 0           return( $self->{ $field } );
252             }
253              
254             1;
255              
256             __END__
257              
258             =encoding utf8
259              
260             =head1 NAME
261              
262             Net::API::Stripe::List - Stripe List Object
263              
264             =head1 SYNOPSIS
265              
266             my $stripe = Net::API::Stripe->new( conf_file => 'settings.json' ) || die( Net::API::Stripe->error );
267             my $list = $stripe->customers( 'list' ) || die( $stripe->error );
268             printf( "%d total customer(s) found\n", $list->count );
269             while( my $cust = $list->next )
270             {
271             printf( "Customer %s with e-mail has a balance of %s\n", $cust->name, $cust->email, $cust->balance->format_money( 0, 'Â¥' ) );
272             }
273              
274             =head1 VERSION
275              
276             v0.200.2
277              
278             =head1 DESCRIPTION
279              
280             This is a package with a set of useful methods to be inherited by various Stripe package, such as bellow packages. It can also be used directly in a generic way and this will find out which list of objects this is. This is the case for example when getting the list of customer tax ids in B<Net::API::Stripe::tax_id_list>().
281              
282             =over 4
283              
284             =item L<Net::API::Stripe::Billing::Invoice::Lines>
285              
286             =item L<Net::API::Stripe::Billing::Subscription::Items>
287              
288             =item L<Net::API::Stripe::Charge::Refunds>
289              
290             =item L<Net::API::Stripe::Connect::Account::ExternalAccounts>
291              
292             =item L<Net::API::Stripe::Connect::ApplicationFee::Refunds>
293              
294             =item L<Net::API::Stripe::Connect::Transfer::Reversals>
295              
296             =item L<Net::API::Stripe::Customer::Sources>
297              
298             =item L<Net::API::Stripe::Customer::Subscription>
299              
300             =item L<Net::API::Stripe::Customer::TaxIds>
301              
302             =item L<Net::API::Stripe::File::Links>
303              
304             =item L<Net::API::Stripe::Order::Return>
305              
306             =item L<Net::API::Stripe::Payment::Intent::Charges>
307              
308             =item L<Net::API::Stripe::Sigma::ScheduledQueryRun::File::Links>
309              
310             =back
311              
312             =head1 CONSTRUCTOR
313              
314             =over 4
315              
316             =item B<new>( %ARG )
317              
318             Creates a new L<Net::API::Stripe::List> object.
319             It may also take an hash like arguments, that also are method of the same name.
320              
321             =back
322              
323             =head1 METHODS
324              
325             =over 4
326              
327             =item B<object> string
328              
329             This is the string identifier of the type of data. Usually it is "list"
330              
331             =item B<data> array
332              
333             This is an array of data, usually objects, but it could vary, which is why this method should be overriden by package inheriting from this one.
334              
335             =item B<has_more> boolean
336              
337             This is a boolean value to indicate whether the data is buffered
338              
339             =item B<url> URI
340              
341             This is uri to be used to access the next or previous set of data
342              
343             =item B<total_count> integer
344              
345             Total size of the array i.e. number of elements
346              
347             =item B<get> offset
348              
349             Retrieves the data at the offset specified
350              
351             =item B<length> integer
352              
353             The size of the array
354              
355             =item B<next>
356              
357             Moves to the next entry in the array
358              
359             =item B<prev>
360              
361             Moves to the previous entry in the array
362              
363             =back
364              
365             =head1 API SAMPLE
366              
367             {
368             "object": "list",
369             "url": "/v1/refunds",
370             "has_more": false,
371             "data": [
372             {
373             "id": "re_fake123456789",
374             "object": "refund",
375             "amount": 30200,
376             "balance_transaction": "txn_fake123456789",
377             "charge": "ch_fake123456789",
378             "created": 1540736617,
379             "currency": "jpy",
380             "metadata": {},
381             "reason": null,
382             "receipt_number": null,
383             "source_transfer_reversal": null,
384             "status": "succeeded",
385             "transfer_reversal": null
386             },
387             {...},
388             {...}
389             ]
390             }
391              
392             =head1 HISTORY
393              
394             =head2 v0.1
395              
396             Initial version
397              
398             =head2 v0.200
399              
400             Change in version numbering
401              
402             =head1 AUTHOR
403              
404             Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
405              
406             =head1 SEE ALSO
407              
408             Stripe API documentation:
409              
410             L<https://stripe.com/docs/api>
411              
412             =head1 COPYRIGHT & LICENSE
413              
414             Copyright (c) 2020-2020 DEGUEST Pte. Ltd.
415              
416             You can use, copy, modify and redistribute this package and associated
417             files under the same terms as Perl itself.
418              
419             =cut