File Coverage

blib/lib/Device/WallyHome/Role/REST.pm
Criterion Covered Total %
statement 61 68 89.7
branch 9 16 56.2
condition 6 14 42.8
subroutine 14 15 93.3
pod 0 4 0.0
total 90 117 76.9


line stmt bran cond sub pod time code
1             package Device::WallyHome::Role::REST;
2 2     2   1181 use Moose::Role;
  2         2  
  2         14  
3 2     2   8288 use MooseX::AttributeShortcuts;
  2         2  
  2         14  
4              
5 2     2   4158 use Data::Dumper;
  2         3  
  2         122  
6 2     2   1223 use HTTP::Headers;
  2         13241  
  2         72  
7 2     2   950 use HTTP::Request;
  2         21053  
  2         59  
8 2     2   1006 use JSON::MaybeXS qw(decode_json);
  2         9189  
  2         133  
9 2     2   1321 use LWP::UserAgent;
  2         35345  
  2         60  
10              
11 2     2   870 use Device::WallyHome::Test::Data;
  2         15  
  2         1301  
12              
13              
14             #== ATTRIBUTES =================================================================
15              
16             has 'apiHostname' => (
17             is => 'rw',
18             isa => 'Str',
19             default => 'api.snsr.net',
20             );
21              
22             has 'apiUseHttps' => (
23             is => 'rw',
24             isa => 'Int',
25             default => 1,
26             );
27              
28             has 'apiVersion' => (
29             is => 'rw',
30             isa => 'Str',
31             default => 'v2',
32             );
33              
34             has 'lastApiError' => (
35             is => 'ro',
36             isa => 'Maybe[Str]',
37             writer => '_lastApiError',
38             );
39              
40             has 'token' => (
41             is => 'rw',
42             isa => 'Str',
43             required => 1,
44             );
45              
46             has 'userAgentName' => (
47             is => 'lazy',
48             );
49              
50             has 'timeout' => (
51             is => 'rw',
52             isa => 'Int',
53             default => '180',
54             );
55              
56             has '_userAgent' => (
57             is => 'lazy',
58             );
59              
60             has '_testModeIdentifier' => (
61             is => 'rw',
62             isa => 'Maybe[Str]',
63             );
64              
65              
66             #== ATTRIBUTE BUILDERS =========================================================
67              
68             sub _build__userAgent {
69 0     0   0 my ($self) = @_;
70              
71 0         0 return LWP::UserAgent->new(
72             agent => $self->userAgentName(),
73             timeout => $self->timeout(),
74             );
75             }
76              
77             sub _build_userAgentName {
78 1     1   14 my $VERSION = Device::WallyHome->VERSION;
79              
80 1         34 return "Device::WallyHome v$VERSION";
81             }
82              
83              
84             #== PUBLIC METHODS =============================================================
85              
86             sub baseUrl {
87 2     2 0 3 my ($self) = @_;
88              
89 2 50       72 my $s = $self->apiUseHttps() ? 's' : '';
90              
91 2         65 return "http$s://" . $self->apiHostname() . '/' . $self->apiVersion() . '/';
92             }
93              
94             sub headers {
95 2     2 0 2 my ($self) = @_;
96              
97 2         19 my $headers = HTTP::Headers->new();
98              
99 2         102 $headers->header('Authorization' => 'Bearer ' . $self->token());
100              
101 2         105 return $headers;
102             }
103              
104             sub request {
105 2     2 0 3 my ($self, $params) = @_;
106              
107 2   50     6 $params //= {};
108              
109 2 50 33     13 die 'invalid params: ' . Dumper($params) unless ref($params) && ref($params) eq 'HASH';
110              
111 2   50     8 my $uri = $params->{uri} // die 'uri required';
112              
113 2   50     10 my $content = $params->{content} // undef;
114 2   33     9 my $headers = $params->{headers} // $self->headers();
115 2   50     10 my $method = $params->{method} // 'GET';
116              
117 2         9 my $request = HTTP::Request->new($method, $self->wallyUrl($uri), $headers, $content);
118              
119 2         7181 my $responseContent = undef;
120              
121 2 50       85 if (defined $self->_testModeIdentifier()) {
122             # Avoid actual API calls for automated testing
123 2         7 $responseContent = $self->_simulateTestResponse($uri);
124             } else {
125 0         0 my $response = $self->_userAgent()->request($request);
126              
127 0         0 $responseContent = $response->content();
128             }
129              
130 2         3 my $decodedResponse = {};
131              
132 2         3 eval {
133 2         104 $decodedResponse = decode_json($responseContent);
134             };
135              
136 2 50       7 if ($@) {
137 0         0 $self->_lastApiError($@);
138              
139 0         0 return undef;
140             }
141              
142 2         27 return $decodedResponse;
143             }
144              
145             sub _simulateTestResponse {
146 2     2   4 my ($self, $uri) = @_;
147              
148 2 50       65 die 'testModeIdentifier required' unless defined $self->_testModeIdentifier();
149              
150 2         77 my $testUtil = Device::WallyHome::Test::Data->new();
151              
152 2         3 my $testResponseFunc = 'sampleResponse_';
153              
154 2 100       12 if ($uri =~ /^places$/) {
    50          
155 1         3 $testResponseFunc .= 'places';
156             } elsif ($uri =~ /^places\/[^\/]+\/sensors/) {
157 1         2 $testResponseFunc .= 'sensors';
158             } else {
159 0         0 die "invalid/unexpected uri for testing: $uri";
160             }
161              
162 2         64 $testResponseFunc .= '_' . $self->_testModeIdentifier();
163              
164 2 50       16 die "invalid testResponseFunc: $testResponseFunc" unless $testUtil->can($testResponseFunc);
165              
166 2         11 return $testUtil->$testResponseFunc();
167             }
168              
169             sub wallyUrl {
170 2     2 0 4 my ($self, $path) = @_;
171              
172 2         7 return $self->baseUrl() . $path;
173             }
174              
175             1;