File Coverage

blib/lib/InfluxDB.pm
Criterion Covered Total %
statement 35 192 18.2
branch 1 64 1.5
condition n/a
subroutine 11 40 27.5
pod 25 29 86.2
total 72 325 22.1


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