File Coverage

blib/lib/InfluxDB.pm
Criterion Covered Total %
statement 37 194 19.0
branch 1 64 1.5
condition n/a
subroutine 11 40 27.5
pod 25 29 86.2
total 74 327 22.6


line stmt bran cond sub pod time code
1             package InfluxDB;
2              
3 2     2   14168 use strict;
  2         3  
  2         55  
4 2     2   6 use warnings;
  2         1  
  2         41  
5 2     2   37 use 5.010_000;
  2         7  
  2         83  
6              
7             our $VERSION = '1.002';
8              
9             use Class::Accessor::Lite (
10 2         15 new => 0,
11             ro => [qw(host port username database ssl json)],
12 2     2   779 );
  2         1544  
13              
14 2     2   161 use Carp;
  2         2  
  2         129  
15 2     2   970 use Data::Validator;
  2         43212  
  2         52  
16 2     2   16 use Mouse::Util::TypeConstraints;
  2         2  
  2         7  
17 2     2   1138 use Furl;
  2         40659  
  2         49  
18 2     2   779 use URI::Escape;
  2         1718  
  2         99  
19 2     2   486 use JSON 2;
  2         7447  
  2         11  
20              
21             enum 'TimePrecision' => qw(s m u);
22              
23             subtype 'JSONBool' => as 'ScalarRef';
24             coerce 'JSONBool'
25             => from 'Bool' => via { $_ ? \1 : \0 }
26             => from 'Object' => via { JSON::is_bool($_) ? ($_ == 1 ? \1 : \0) : \0 }
27             ;
28              
29             sub new {
30 1     1 1 469 state $rule = Data::Validator->new(
31             host => { isa => 'Str' },
32             port => { isa => 'Int', default => 8086 },
33             username => { isa => 'Str' },
34             password => { isa => 'Str' },
35             database => { isa => 'Str' },
36             ssl => { isa => 'Bool', default => 0 },
37              
38             timeout => { isa => 'Int', default => 120 },
39             debug => { isa => 'Bool', optional => 1 },
40             );
41             # Mouse::Util::apply_all_role leaks memory when takes 2 or more extensions.
42             # so apply for each extension.
43 1         243 $rule->with('NoRestricted');
44 1         3393 $rule->with('Method');
45 1         987 my($class, $args) = $rule->validate(@_);
46              
47 1 50       94 if (delete $args->{debug}) {
48 0         0 $ENV{IX_DEBUG} = 1;
49             }
50              
51 1         9 my $self = bless {
52             ua => Furl->new(
53             agent => join('/', __PACKAGE__, $VERSION),
54             timeout => $args->{timeout},
55             ),
56             json => JSON->new,
57             status => {},
58             %$args
59             }, $class;
60              
61 1         79 return $self;
62             }
63              
64             sub debugf {
65 0 0   0 0   return unless $ENV{IX_DEBUG};
66 0           print STDERR "[DEBUG] ",sprintf(shift @_, @_),"\n";
67             }
68              
69             sub status {
70 0     0 1   my($self, $res) = @_;
71              
72 0 0         if ($res) {
73 0           $self->{status} = {
74             code => $res->code,
75             message => $res->message,
76             status_line => $res->status_line,
77             content => $res->content,
78             };
79 0           debugf("content: %s", $res->content);
80             }
81              
82 0           return $self->{status};
83             }
84              
85             sub errstr {
86 0     0 1   my($self) = @_;
87 0           my $errstr = "";
88              
89 0 0         if (substr($self->{status}{code}, 0, 2) ne "20") {
90 0           $errstr = join("\n",
91             $self->{status}{status_line},
92             $self->{status}{content},
93             );
94             }
95              
96 0           return $errstr;
97             }
98              
99             sub as_hash {
100 0     0 1   my(undef, $result) = @_;
101 0           my $h;
102              
103 0           for my $r (@{ $result }) {
  0            
104 0           my $series = $r->{name};
105 0           my @columns = @{ $r->{columns} };
  0            
106              
107 0           for my $p (@{ $r->{points} }) {
  0            
108 0           my %ph;
109 0           @ph{ @columns } = @$p;
110 0           push @{ $h->{$series} }, \%ph;
  0            
111             }
112             }
113              
114 0           return $h;
115             }
116              
117             sub switch_database {
118 0     0 1   state $rule = Data::Validator->new(
119             database => { isa => 'Str' },
120             )->with('Method');
121 0           my($self, $args) = $rule->validate(@_);
122              
123 0           $self->{database} = $args->{database};
124              
125 0           return 1;
126             }
127              
128             sub switch_user {
129 0     0 1   state $rule = Data::Validator->new(
130             username => { isa => 'Str' },
131             password => { isa => 'Str' },
132             )->with('Method');
133 0           my($self, $args) = $rule->validate(@_);
134              
135 0           $self->{username} = $args->{username};
136 0           $self->{password} = $args->{password};
137              
138 0           return 1;
139             }
140              
141             ### database #############################################################
142             sub create_database {
143 0     0 1   state $rule = Data::Validator->new(
144             database => { isa => 'Str' },
145             )->with('Method');
146 0           my($self, $args) = $rule->validate(@_);
147              
148 0           my $url = $self->_build_url(
149             path => '/db',
150             );
151              
152 0           my $res = $self->{ua}->post($url, [], $self->json->encode({
153             name => $args->{database},
154             }));
155 0           $self->status($res);
156              
157 0 0         return $res->is_success ? 1 : ();
158             }
159              
160             sub list_database {
161 0     0 1   my $self = shift;
162              
163 0           my $url = $self->_build_url(
164             path => '/db',
165             );
166              
167 0           my $res = $self->{ua}->get($url);
168 0           $self->status($res);
169              
170 0 0         return $res->is_success ? $self->json->decode($res->content) : ();
171             }
172              
173             sub delete_database {
174 0     0 1   state $rule = Data::Validator->new(
175             database => { isa => 'Str' },
176             )->with('Method');
177 0           my($self, $args) = $rule->validate(@_);
178              
179 0           my $url = $self->_build_url(
180             path => '/db/'. $args->{database},
181             );
182              
183 0           my $res = $self->{ua}->delete($url);
184 0           $self->status($res);
185              
186 0 0         return $res->is_success ? 1 : ();
187             }
188              
189             ### series ###############################################################
190             ## hmmm v0.5.1 (latest version) returns empty response
191             ## https://github.com/FGRibreau/influxdb-cli/issues/8
192             sub list_series {
193 0     0 1   my $self = shift;
194 0           return $self->query(q => "list series");
195             }
196              
197             ### points ###############################################################
198             sub write_points {
199 0     0 1   state $rule = Data::Validator->new(
200             data => { isa => 'ArrayRef|HashRef' },
201             time_precision => { isa => 'TimePrecision', optional => 1 },
202             )->with('Method');
203 0           my($self, $args) = $rule->validate(@_);
204              
205 0 0         my $data = ref($args->{data}) eq 'ARRAY' ? $args->{data} : [$args->{data}];
206 0           $data = $self->json->encode($data);
207 0           debugf("data: %s", $data);
208              
209 0 0         my $url = $self->_build_url(
210             path => '/db/' . $self->database . '/series',
211             qs => {
212             time_precision => exists $args->{time_precision} ? $args->{time_precision} : undef,
213             },
214             );
215              
216 0           my $res = $self->{ua}->post($url, [], $data);
217 0           $self->status($res);
218              
219 0 0         return $res->is_success ? 1 : ();
220             }
221              
222             sub delete_points {
223 0     0 1   state $rule = Data::Validator->new(
224             name => { isa => 'Str' },
225             )->with('Method');
226 0           my($self, $args) = $rule->validate(@_);
227              
228 0           my $url = $self->_build_url(
229             path => '/db/' . $self->database . '/series/' . $args->{name},
230             qs => {
231             %$args,
232             },
233             );
234              
235 0           my $res = $self->{ua}->delete($url);
236 0           $self->status($res);
237              
238 0 0         return $res->is_success ? 1 : ();
239             }
240              
241             sub create_scheduled_deletes {
242 0     0 0   croak "Not implemented in InfluxDB v0.5.1";
243             # state $rule = Data::Validator->new(
244             # )->with('Method');
245             # my($self, $args) = $rule->validate(@_);
246             }
247              
248             sub list_scheduled_deletes {
249 0     0 0   croak "Not implemented in InfluxDB v0.5.1";
250             # state $rule = Data::Validator->new(
251             # )->with('Method');
252             # my($self, $args) = $rule->validate(@_);
253             }
254              
255             sub delete_scheduled_deletes {
256 0     0 0   croak "Not implemented in InfluxDB v0.5.1";
257             # state $rule = Data::Validator->new(
258             # )->with('Method');
259             # my($self, $args) = $rule->validate(@_);
260             }
261              
262             sub query {
263 0     0 1   state $rule = Data::Validator->new(
264             q => { isa => 'Str' },
265             time_precision => { isa => 'TimePrecision', optional => 1 },
266             chunked => { isa => 'Bool', default => 0 },
267             # order => { isa => 'Str', optional => 1 }, # not implemented?
268             )->with('Method');
269 0           my($self, $args) = $rule->validate(@_);
270              
271 0 0         my $url = $self->_build_url(
    0          
    0          
272             path => '/db/' . $self->database . '/series',
273             qs => {
274             q => $args->{q},
275             time_precision => (exists $args->{time_precision} ? $args->{time_precision} : undef),
276             chunked => ($args->{chunked} ? 'true' : 'false'),
277             order => (exists $args->{order} ? $args->{order} : undef),
278             },
279             );
280              
281 0           my $res = $self->{ua}->get($url);
282 0           $self->status($res);
283              
284 0 0         if ($res->is_success) {
285 0           my $result;
286 0 0         if ($args->{chunked}) {
287 0           $result = [$self->json->incr_parse($res->content)];
288             } else {
289 0           $result = $self->json->decode($res->content);
290             }
291 0           return $result;
292             } else {
293 0           return;
294             }
295             }
296              
297             ### Continuous Queries ###################################################
298             sub create_continuous_query {
299 0     0 1   state $rule = Data::Validator->new(
300             q => { isa => 'Str' },
301             name => { isa => 'Str' },
302             )->with('Method');
303 0           my($self, $args) = $rule->validate(@_);
304              
305 0           return $self->query(q => "$args->{q} into $args->{name}");
306             }
307              
308             sub list_continuous_queries {
309 0     0 1   my $self = shift;
310 0           return $self->query(q => "list continuous queries");
311             }
312              
313             sub drop_continuous_query {
314 0     0 1   state $rule = Data::Validator->new(
315             id => { isa => 'Str' },
316             )->with('Method');
317 0           my($self, $args) = $rule->validate(@_);
318              
319 0           return $self->query(q => "drop continuous query $args->{id}");
320             }
321              
322             ### user #################################################################
323             sub create_database_user {
324 0     0 1   state $rule = Data::Validator->new(
325             name => { isa => 'Str' },
326             password => { isa => 'Str' },
327             )->with('Method');
328 0           my($self, $args) = $rule->validate(@_);
329              
330 0           my $url = $self->_build_url(
331             path => '/db/' . $self->database . '/users',
332             );
333              
334 0           my $res = $self->{ua}->post($url, [], $self->json->encode({
335             name => $args->{name},
336             password => $args->{password},
337             }));
338 0           $self->status($res);
339              
340 0 0         return $res->is_success ? 1 : ();
341             }
342              
343             sub delete_database_user {
344 0     0 1   state $rule = Data::Validator->new(
345             name => { isa => 'Str' },
346             )->with('Method');
347 0           my($self, $args) = $rule->validate(@_);
348              
349 0           my $url = $self->_build_url(
350             path => '/db/' . $self->database . '/users/' . $args->{name},
351             );
352              
353 0           my $res = $self->{ua}->delete($url);
354 0           $self->status($res);
355              
356 0 0         return $res->is_success ? 1 : ();
357             }
358              
359             sub update_database_user {
360 0     0 1   state $rule = Data::Validator->new(
361             name => { isa => 'Str' },
362             password => { isa => 'Str', optional => 1 },
363             admin => { isa => 'JSONBool', optional => 1 },
364             )->with('Method');
365 0           my($self, $args) = $rule->validate(@_);
366              
367 0           my $url = $self->_build_url(
368             path => '/db/' . $self->database . '/users/' . $args->{name},
369             );
370              
371 0 0         my $res = $self->{ua}->post($url, [], $self->json->encode({
    0          
372             exists $args->{password} ? (password => $args->{password}) : (),
373             exists $args->{admin} ? (admin => $args->{admin}) : (),
374             }));
375 0           $self->status($res);
376              
377 0 0         return $res->is_success ? 1 : ();
378             }
379              
380             sub list_database_users {
381 0     0 1   my $self = shift;
382              
383 0           my $url = $self->_build_url(
384             path => '/db/' . $self->database . '/users',
385             );
386              
387 0           my $res = $self->{ua}->get($url);
388 0           $self->status($res);
389              
390 0 0         return $res->is_success ? $self->json->decode($res->content) : ();
391             }
392              
393             sub show_database_user {
394 0     0 1   state $rule = Data::Validator->new(
395             name => { isa => 'Str' },
396             )->with('Method');
397 0           my($self, $args) = $rule->validate(@_);
398              
399 0           my $url = $self->_build_url(
400             path => '/db/' . $self->database . '/users/' . $args->{name},
401             );
402              
403 0           my $res = $self->{ua}->get($url);
404 0           $self->status($res);
405              
406 0 0         return $res->is_success ? $self->json->decode($res->content) : ();
407             }
408              
409             sub create_cluster_admin {
410 0     0 1   state $rule = Data::Validator->new(
411             name => { isa => 'Str' },
412             password => { isa => 'Str' },
413             )->with('Method');
414 0           my($self, $args) = $rule->validate(@_);
415              
416 0           my $url = $self->_build_url(
417             path => '/cluster_admins',
418             );
419              
420 0           my $res = $self->{ua}->post($url, [], $self->json->encode({
421             name => $args->{name},
422             password => $args->{password},
423             }));
424 0           $self->status($res);
425              
426 0 0         return $res->is_success ? 1 : ();
427             }
428              
429             sub delete_cluster_admin {
430 0     0 1   state $rule = Data::Validator->new(
431             name => { isa => 'Str' },
432             )->with('Method');
433 0           my($self, $args) = $rule->validate(@_);
434              
435 0           my $url = $self->_build_url(
436             path => '/cluster_admins/' . $args->{name},
437             );
438              
439 0           my $res = $self->{ua}->delete($url);
440 0           $self->status($res);
441              
442 0 0         return $res->is_success ? 1 : ();
443             }
444              
445             sub update_cluster_admin {
446 0     0 1   state $rule = Data::Validator->new(
447             name => { isa => 'Str' },
448             password => { isa => 'Str' },
449             )->with('Method');
450 0           my($self, $args) = $rule->validate(@_);
451              
452 0           my $url = $self->_build_url(
453             path => '/cluster_admins/' . $args->{name},
454             );
455              
456 0 0         my $res = $self->{ua}->post($url, [], $self->json->encode({
457             exists $args->{password} ? (password => $args->{password}) : (),
458             }));
459 0           $self->status($res);
460              
461 0 0         return $res->is_success ? 1 : ();
462             }
463              
464             sub list_cluster_admins {
465 0     0 1   my $self = shift;
466              
467 0           my $url = $self->_build_url(
468             path => '/cluster_admins',
469             );
470              
471 0           my $res = $self->{ua}->get($url);
472 0           $self->status($res);
473              
474 0 0         return $res->is_success ? $self->json->decode($res->content) : ();
475             }
476              
477             # sub show_cluster_admin {
478             # state $rule = Data::Validator->new(
479             # name => { isa => 'Str' },
480             # )->with('Method');
481             # my($self, $args) = $rule->validate(@_);
482              
483             # my $url = $self->_build_url(
484             # path => '/cluster_admins/' . $args->{name},
485             # );
486              
487             # my $res = $self->{ua}->get($url);
488             # $self->status($res);
489              
490             # return $res->is_success ? $self->json->decode($res->content) : ();
491             # }
492              
493             ### utils ################################################################
494             sub _build_url {
495 0     0     state $rule = Data::Validator->new(
496             path => { isa => 'Str' },
497             qs => { isa => 'HashRef', optional => 1 },
498             )->with('Method');
499 0           my($self, $args) = $rule->validate(@_);
500              
501 0 0         my $url = sprintf("%s://%s:%s@%s:%d%s",
502             ($self->ssl ? 'https' : 'http'),
503             $self->username,
504             $self->{password},
505             $self->host,
506             $self->port,
507             $args->{path},
508             );
509 0 0         if (exists $args->{qs}) {
510 0           my @qs;
511 0           for my $k (keys %{ $args->{qs} }) {
  0            
512 0 0         next unless defined $args->{qs}{$k};
513 0           push @qs, join('=', uri_escape($k), uri_escape($args->{qs}{$k}));
514             }
515 0 0         $url .= '?' . join('&', @qs) if @qs;
516             }
517              
518 0           debugf("url: %s", $url);
519 0           return $url
520             }
521              
522             1;
523              
524             __END__