File Coverage

blib/lib/Stancer/Config.pm
Criterion Covered Total %
statement 109 109 100.0
branch 44 44 100.0
condition 3 3 100.0
subroutine 24 24 100.0
pod 11 11 100.0
total 191 191 100.0


line stmt bran cond sub pod time code
1             package Stancer::Config;
2              
3 41     41   2260625 use 5.020;
  41         163  
4 41     41   252 use strict;
  41         93  
  41         1262  
5 41     41   224 use warnings;
  41         100  
  41         3942  
6              
7             # ABSTRACT: API configuration
8             our $VERSION = '1.0.3'; # VERSION
9              
10 41     41   6947 use Stancer::Core::Types qw(:api ArrayRef Bool Enum InstanceOf Int Port Str);
  41         140  
  41         11086  
11              
12 41     41   495 use Config qw(%Config);
  41         156  
  41         2455  
13 41     41   290 use DateTime::TimeZone;
  41         84  
  41         1620  
14 41     41   19713 use English qw(-no_match_vars);
  41         68371  
  41         337  
15 41     41   37626 use Stancer::Exceptions::MissingApiKey;
  41         177  
  41         1979  
16 41     41   35670 use LWP::UserAgent;
  41         2543662  
  41         2304  
17 41     41   508 use Scalar::Util qw(blessed);
  41         130  
  41         3024  
18              
19 41     41   293 use Moo;
  41         2786  
  41         446  
20 41     41   29909 use namespace::clean;
  41         101  
  41         439  
21              
22             use constant {
23 41         74531 LIVE => 'live',
24             TEST => 'test',
25 41     41   22575 };
  41         122  
26              
27              
28             around BUILDARGS => sub {
29             my ($orig, $class, @args) = @_;
30             my $params = $class->$orig(@args);
31              
32             if (exists $params->{keychain}) {
33             my @keys = $params->{keychain};
34              
35             @keys = @{$params->{keychain}} if ref $params->{keychain} eq 'ARRAY';
36              
37             foreach my $key (@keys) {
38             my $prefix = substr $key, 0, 5;
39              
40             $params->{pprod} = $key if $prefix eq 'pprod';
41             $params->{ptest} = $key if $prefix eq 'ptest';
42             $params->{sprod} = $key if $prefix eq 'sprod';
43             $params->{stest} = $key if $prefix eq 'stest';
44             }
45             }
46              
47             return $params;
48             };
49              
50              
51             has calls => (
52             is => 'ro',
53             isa => ArrayRef[InstanceOf['Stancer::Core::Request::Call']],
54             default => sub { [] },
55             );
56              
57              
58             has debug => (
59             is => 'rw',
60             isa => Bool,
61             default => sub { not(defined $_[0]->mode) or $_[0]->mode ne Stancer::Config::LIVE },
62             lazy => 1,
63             );
64              
65              
66             has default_timezone => (
67             is => 'rw',
68             isa => InstanceOf['DateTime::TimeZone'],
69             coerce => sub {
70             return $_[0] if not defined $_[0];
71             return $_[0] if blessed($_[0]) and $_[0]->isa('DateTime::TimeZone');
72             return DateTime::TimeZone->new(name => $_[0]);
73             },
74             );
75              
76              
77             has host => (
78             is => 'rw',
79             isa => Str,
80             default => 'api.stancer.com',
81             predicate => 1,
82             );
83              
84              
85             sub keychain {
86 23     23 1 16183 my ($this, @args) = @_;
87 23         40 my @data = @args;
88 23         35 my @keychain = ();
89              
90 23 100       78 if (scalar @args == 1) {
91 8 100       21 if (ref $args[0] eq 'ARRAY') {
92 1         2 @data = @{$args[0]};
  1         4  
93             } else {
94 7         15 @data = ($args[0]);
95             }
96             }
97              
98 23 100       39 if (@data) {
99 11         23 foreach my $key (@data) {
100 19         60 ApiKey->($key); # Automaticaly throw an error if $key has a bad format
101              
102 16         45 my $prefix = substr $key, 0, 5;
103              
104 16 100       136 $this->pprod($key) if $prefix eq 'pprod';
105 16 100       229 $this->ptest($key) if $prefix eq 'ptest';
106 16 100       212 $this->sprod($key) if $prefix eq 'sprod';
107 16 100       218 $this->stest($key) if $prefix eq 'stest';
108             }
109             }
110              
111 20 100       414 push @keychain, $this->pprod if defined $this->pprod;
112 20 100       526 push @keychain, $this->ptest if defined $this->ptest;
113 20 100       451 push @keychain, $this->sprod if defined $this->sprod;
114 20 100       435 push @keychain, $this->stest if defined $this->stest;
115              
116 20         237 return \@keychain;
117             }
118              
119              
120             has lwp => (
121             is => 'rw',
122             isa => sub { ref $_[0] eq 'LWP::UserAgent' },
123             default => sub { LWP::UserAgent->new() },
124             lazy => 1,
125             predicate => 1,
126             );
127              
128              
129             has mode => (
130             is => 'rw',
131             isa => Enum[TEST, LIVE],
132             default => TEST,
133             predicate => 1,
134             );
135              
136              
137             has port => (
138             is => 'rw',
139             isa => Port,
140             predicate => 1,
141             );
142              
143              
144             has pprod => (
145             is => 'rw',
146             isa => PublicLiveApiKey,
147             );
148              
149              
150             sub public_key {
151 9     9 1 184 my $this = shift;
152 9         280 my $key = $this->ptest;
153 9         76 my $message = 'You did not provide valid public API key for %s.';
154 9         22 my $environment = 'development';
155              
156 9 100       36 if ($this->is_live_mode) {
157 2         61 $key = $this->pprod;
158 2         14 $environment = 'production';
159             }
160              
161 9 100       111 Stancer::Exceptions::MissingApiKey->throw(message => sprintf $message, $environment) unless defined $key;
162              
163 6         60 return $key;
164             }
165              
166              
167             has ptest => (
168             is => 'rw',
169             isa => PublicTestApiKey,
170             );
171              
172              
173             sub secret_key {
174 120     120 1 454 my $this = shift;
175 120         4670 my $key = $this->stest;
176 120         1423 my $message = 'You did not provide valid secret API key for %s.';
177 120         305 my $environment = 'development';
178              
179 120 100       603 if ($this->is_live_mode) {
180 2         57 $key = $this->sprod;
181 2         43 $environment = 'production';
182             }
183              
184 120 100       436 Stancer::Exceptions::MissingApiKey->throw(message => sprintf $message, $environment) unless defined $key;
185              
186 118         675 return $key;
187             }
188              
189              
190             has sprod => (
191             is => 'rw',
192             isa => SecretLiveApiKey,
193             );
194              
195              
196             has stest => (
197             is => 'rw',
198             isa => SecretTestApiKey,
199             );
200              
201              
202             has timeout => (
203             is => 'rw',
204             isa => Int,
205             predicate => 1,
206             );
207              
208              
209             sub uri {
210 147     147 1 567 my $this = shift;
211             ## no critic (ValuesAndExpressions::RequireInterpolationOfMetachars)
212 147         448 my $pattern = 'https://%1$s/v%3$s';
213              
214 147 100       7236 if ($this->port) {
215 2         17 $pattern = 'https://%1$s:%2$d/v%3$s';
216             }
217             ## use critic
218              
219 147         5521 return sprintf $pattern, $this->host, $this->port, $this->version;
220             }
221              
222              
223             sub user_agent {
224             return sprintf 'libwww-perl/%s libstancer-perl/%s (%s %s; perl %vd)', (
225             $LWP::VERSION,
226             $Stancer::Config::VERSION,
227             $Config{osname},
228             $Config{archname},
229 116     116 1 6767 $PERL_VERSION,
230             );
231             }
232              
233              
234             has version => (
235             is => 'rw',
236             isa => Int,
237             default => 1,
238             predicate => 1,
239             );
240              
241              
242             my $instance;
243              
244             sub init {
245 487     487 1 1034070 my ($self, @args) = @_;
246              
247 487 100 100     4243 if ($self && $self ne __PACKAGE__) {
248 13         47 unshift @args, $self;
249             }
250              
251 487         1121 my $params = {};
252              
253 487 100       1715 if (scalar @args == 0) {
254 468         2694 return $instance;
255             }
256              
257 19 100       86 if (scalar @args == 1) {
258 16 100       69 if (ref $args[0] eq 'HASH') {
259 3         9 $params = $args[0];
260             } else {
261 13         55 $params->{keychain} = $args[0];
262             }
263             } else {
264 3         10 $params = {@args};
265             }
266              
267 19         517 $instance = __PACKAGE__->new($params);
268              
269 19         819 return $instance;
270             }
271              
272              
273             sub is_live_mode {
274 135     135 1 307 my $this = shift;
275              
276 135 100       3612 return 1 if $this->mode eq LIVE;
277 129         1862 return q//;
278             }
279              
280             sub is_test_mode {
281 6     6 1 53 my $this = shift;
282              
283 6 100       183 return 1 if $this->mode eq TEST;
284 2         27 return q//;
285             }
286              
287             sub is_not_live_mode {
288 3     3 1 9 my $this = shift;
289              
290 3         11 return not $this->is_live_mode;
291             }
292              
293             sub is_not_test_mode {
294 3     3 1 9 my $this = shift;
295              
296 3         11 return not $this->is_test_mode;
297             }
298              
299              
300             sub last_call {
301 11     11 1 46038 my $this = shift;
302              
303 11         35 return ${ $this->calls }[-1];
  11         115  
304             }
305              
306             1;
307              
308             __END__
309              
310             =pod
311              
312             =encoding UTF-8
313              
314             =head1 NAME
315              
316             Stancer::Config - API configuration
317              
318             =head1 VERSION
319              
320             version 1.0.3
321              
322             =head1 SYNOPSIS
323              
324             Handle configuration, connection and credential to API.
325              
326             use Stancer::Config;
327              
328             Stancer::Config->init($secret_key);
329              
330             my $payment = Stancer::Payment->new();
331             $payment->send();
332              
333             =head1 ATTRIBUTES
334              
335             =head2 C<calls>
336              
337             Read-only list of C<Stancer::Core::Request::Call>.
338              
339             This list is only available in debug mode.
340              
341             =head2 C<debug>
342              
343             Read/Write boolean.
344              
345             Indicate if we are in debug mode.
346              
347             In debug mode we will register every request made to the API.
348              
349             Debug mode is enabled by default in test mode and disabled by default in live mode.
350              
351             =head2 C<default_timezone>
352              
353             Read/Write instance of C<DateTime::TimeZone>.
354              
355             Will be used as default time zone for every C<DateTime> object created by the API.
356              
357             If none provided, we will let
358             L<DateTime default mechanism|https://metacpan.org/pod/DateTime#Globally-Setting-a-Default-Time-Zone>
359             do the work.
360              
361             You may pass a string, it will be used to create a new C<DateTime::TimeZone> instance.
362              
363             =head2 C<host>
364              
365             Read/Write string, default to "api.stancer.com".
366              
367             API host
368              
369             =head2 C<keychain>
370              
371             Read/Write array reference of API keys.
372              
373             API keychain.
374              
375             =head2 C<lwp>
376              
377             Read/Write instance of C<LWP::UserAgent>.
378              
379             If none provided, it will instanciate a new instance.
380             This allow you to provide your own configured L<LWP::UserAgent>.
381              
382             =head2 C<mode>
383              
384             Read/Write, must be 'test' or 'live', default depends on key.
385              
386             You better use `Stancer::Config::TEST` or `Stancer::Config::LIVE` constants.
387              
388             API mode
389              
390             =head2 C<port>
391              
392             Read/Write integer.
393              
394             API HTTP port
395              
396             =head2 C<pprod>
397              
398             Read/Write 30 characters string.
399              
400             Public production authentication key.
401              
402             =head2 C<public_key>
403              
404             Read-only 30 characters string.
405              
406             Public authentication key.
407             Will return C<pprod> or C<ptest> key depending on configured C<mode>.
408              
409             =head2 C<ptest>
410              
411             Read/Write 30 characters string.
412              
413             Public development authentication key.
414              
415             =head2 C<secret_key>
416              
417             Read-only 30 characters string.
418              
419             Secret authentication key.
420             Will return C<sprod> or C<stest> key depending on configured C<mode>.
421              
422             =head2 C<sprod>
423              
424             Read/Write 30 characters string.
425              
426             Secret production authentication key.
427              
428             =head2 C<stest>
429              
430             Read/Write 30 characters string.
431              
432             Secret development authentication key.
433              
434             =head2 C<timeout>
435              
436             Read/Write integer.
437              
438             Timeout for every API call
439              
440             =head2 C<uri>
441              
442             Read-only string.
443              
444             Complete location for the API.
445              
446             =head2 C<user_agent>
447              
448             Read-only default user agent.
449              
450             =head2 C<version>
451              
452             Read/Write integer.
453              
454             API version
455              
456             =head1 METHODS
457              
458             =head2 C<< Stancer::Config->new(I<%args>) : I<self> >>
459              
460             This method create a new configuration object.
461             C<%args> can have any attribute listed in current documentation.
462              
463             We may also see L</init> method.
464              
465             =head2 C<< Config::init(I<$token>) : I<self> >>
466              
467             =head2 C<< Config::init(I<%args>) : I<self> >>
468              
469             =head2 C<< Config::init() : I<self> >>
470              
471             Get an instance with only a token. It may also accept the same hash used in `new` method.
472              
473             Will act as a singleton if called without argument.
474              
475             =head2 C<< $config->is_live_mode() : I<bool> >>
476              
477             =head2 C<< $config->is_test_mode() : I<bool> >>
478              
479             =head2 C<< $config->is_not_live_mode() : I<bool> >>
480              
481             =head2 C<< $config->is_not_test_mode() : I<bool> >>
482              
483             Indicate if we are running or not is live mode or test mode.
484              
485             =head2 C<< $config->last_call() : I<Stancer::Core::Request::Call> >>
486              
487             Return the last call to the API.
488              
489             =head1 USAGE
490              
491             =head2 Logging
492              
493              
494              
495             We use the L<Log::Any> framework for logging events.
496             You may tell where it should log using any available L<Log::Any::Adapter> module.
497              
498             For example, to log everything to a file you just have to add a line to your script, like this:
499             #! /usr/bin/env perl
500             use Log::Any::Adapter (File => '/var/log/payment.log');
501             use Stancer::Config;
502              
503             You must import C<Log::Any::Adapter> before our libraries, to initialize the logger instance before use.
504              
505             You can choose your log level on import directly:
506             use Log::Any::Adapter (File => '/var/log/payment.log', log_level => 'info');
507              
508             Read the L<Log::Any> documentation to know what other options you have.
509              
510             =cut
511              
512             =head1 SECURITY
513              
514             =over
515              
516             =item *
517              
518             Never, never, NEVER register a card or a bank account number in your database.
519              
520             =item *
521              
522             Always uses HTTPS in card/SEPA in communication.
523              
524             =item *
525              
526             Our API will never give you a complete card/SEPA number, only the last four digits.
527             If you need to keep track, use these last four digit.
528              
529             =back
530              
531             =cut
532              
533             =head1 BUGS
534              
535             Please report any bugs or feature requests on the bugtracker website
536             L<https://gitlab.com/wearestancer/library/lib-perl/-/issues> or by email to
537             L<bug-stancer@rt.cpan.org|mailto:bug-stancer@rt.cpan.org>.
538              
539             When submitting a bug or request, please include a test-file or a
540             patch to an existing test-file that illustrates the bug or desired
541             feature.
542              
543             =head1 AUTHOR
544              
545             Joel Da Silva <jdasilva@cpan.org>
546              
547             =head1 COPYRIGHT AND LICENSE
548              
549             This software is Copyright (c) 2018-2024 by Stancer / Iliad78.
550              
551             This is free software, licensed under:
552              
553             The Artistic License 2.0 (GPL Compatible)
554              
555             =cut