File Coverage

blib/lib/App/MonM/Store.pm
Criterion Covered Total %
statement 141 169 83.4
branch 27 58 46.5
condition 11 31 35.4
subroutine 27 30 90.0
pod 11 11 100.0
total 217 299 72.5


line stmt bran cond sub pod time code
1             package App::MonM::Store; # $Id: Store.pm 108 2022-08-24 14:30:32Z abalama $
2 2     2   51424 use strict;
  2         9  
  2         47  
3 2     2   476 use utf8;
  2         13  
  2         8  
4              
5             =encoding utf8
6              
7             =head1 NAME
8              
9             App::MonM::Store - DBI interface for checkit's data storing
10              
11             =head1 VERSION
12              
13             Version 1.01
14              
15             =head1 SYNOPSIS
16              
17             use App::MonM::Store;
18              
19             my $store = App::MonM::Store->new(
20             dsn => "DBI:mysql:database=monm;host=mysql.example.com",
21             user => "username",
22             password => "password",
23             set => [
24             "RaiseError 0",
25             "PrintError 0",
26             "mysql_enable_utf8 1",
27             ],
28             );
29             die($store->error) if $store->error;
30              
31             =head1 DESCRIPTION
32              
33             DBI interface for checkit's data storing. This module provides store methods
34              
35             =head2 new
36              
37             my $store = App::MonM::Store->new(
38             dsn => "DBI:mysql:database=monm;host=mysql.example.com",
39             user => "username",
40             password => "password",
41             set => [
42             "RaiseError 0",
43             "PrintError 0",
44             "mysql_enable_utf8 1",
45             ],
46             );
47              
48             Creates DBI object
49              
50             =head2 add
51              
52             $store->add(
53             name => "foo",
54             type => "http",
55             source => "http://example.com",
56             status => 1,
57             message => "Ok"
58             ) or die $store->error;
59              
60             Add new record on database
61              
62             =head2 clean
63              
64             $store->clean(
65             period => 600
66             ) or die $store->error;
67              
68             Delete too old records from database
69              
70             =head2 del
71              
72             $store->del(
73             id => 1
74             ) or die $store->error;
75              
76             Delete record from database
77              
78             =head2 dsn
79              
80             my $dsn = $store->dsn;
81              
82             Returns DSN string of current database connection
83              
84             =head2 error
85              
86             my $error = $store->error;
87              
88             Returns error message
89              
90             $store->error("Error message");
91              
92             Sets error message if argument is provided.
93              
94             =head2 get
95              
96             my %info = $store->get(
97             name => "foo"
98             );
99              
100             Gets information about file from database
101              
102             Format:
103              
104             {
105             id => 1,
106             time => 123456789,
107             name => "foo"
108             type => "http",
109             source => "http://example.com"
110             status => 1,
111             message => "Ok"
112             }
113              
114             =over 4
115              
116             =item B
117              
118             The Record ID. Autoincremented value!
119              
120             =item B
121              
122             The checking message
123              
124             =item B
125              
126             Name of checkit section
127              
128             =item B
129              
130             Source Name: URL, DSN or command
131              
132             =item B
133              
134             Status of checking: 000-111 as binary notation
135              
136             Default: 0
137              
138             =item B
139              
140             Time of record insert
141              
142             Default: time()
143              
144             =item B
145              
146             Type of checkit source: http, dbi, command
147              
148             Default: http
149              
150             =back
151              
152             =head2 is_sqlite
153              
154             print $store->is_sqlite ? "Is SQLite" : "Is not SQLite"
155              
156             Returns true if type of current database is SQLite
157              
158             =head2 getall
159              
160             my @files = $store->getall();
161              
162             Returns list of all checkit values
163              
164             Record format of return result: see L
165              
166             =head2 ping
167              
168             $store->ping ? 'OK' : 'Database session is expired';
169              
170             Checks the connection to database
171              
172             =head2 set
173              
174             $store->set(
175             id => 1,
176             name => "foo",
177             type => "http",
178             source => "http://example.com",
179             status => 1,
180             message => "Ok"
181             ) or die $store->error;
182              
183             Update existing record on database
184              
185             =head1 SEE ALSO
186              
187             L, L
188              
189             =head1 AUTHOR
190              
191             Serż Minus (Sergey Lepenkov) L Eabalama@cpan.orgE
192              
193             =head1 COPYRIGHT
194              
195             Copyright (C) 1998-2022 D&D Corporation. All Rights Reserved
196              
197             =head1 LICENSE
198              
199             This program is free software; you can redistribute it and/or
200             modify it under the same terms as Perl itself.
201              
202             See C file and L
203              
204             =cut
205              
206 2     2   87 use vars qw/ $VERSION /;
  2         2  
  2         80  
207             $VERSION = '1.01';
208              
209 2     2   9 use Carp;
  2         2  
  2         83  
210 2     2   724 use CTK::DBI;
  2         163068  
  2         61  
211 2     2   11 use CTK::Util qw/ touch /;
  2         3  
  2         77  
212 2     2   357 use CTK::ConfGenUtil;
  2         930  
  2         119  
213 2     2   346 use CTK::TFVals qw/ :ALL /;
  2         1542  
  2         305  
214 2     2   11 use File::Spec;
  2         3  
  2         44  
215              
216 2     2   349 use App::MonM::Const;
  2         2  
  2         125  
217 2     2   349 use App::MonM::Util qw/ set2attr /;
  2         5  
  2         129  
218              
219             use constant {
220 2         147 DB_FILENAME => 'monm.db',
221             DEFAULT_DSN_MASK => 'dbi:SQLite:dbname=%s',
222             DEFAULT_DBI_ATTR => {
223             dsn => '', # See DEFAULT_DSN_MASK
224             user => '',
225             password => '',
226             set => [
227             'RaiseError 0',
228             'PrintError 0',
229             'sqlite_unicode 1',
230             ],
231             },
232 2     2   11 };
  2         2  
233              
234 2     2   10 use constant CHECKIT_DDL => <<'DDL';
  2         2  
  2         71  
235             CREATE TABLE IF NOT EXISTS monm (
236             `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
237             `time` NUMERIC DEFAULT 0, -- time()
238             `name` CHAR(255) DEFAULT NULL, -- name of checkit section
239             `type` CHAR(32) DEFAULT NULL, -- http/dbi/command
240             `source` CHAR(255) DEFAULT NULL, -- URL/DSN/Command
241             `status` INTEGER DEFAULT 0, -- status value
242             `message` TEXT DEFAULT NULL -- message
243             )
244             DDL
245              
246 2     2   31 use constant CHECKIT_INSERT => <<'DML';
  2         4  
  2         74  
247             INSERT INTO monm
248             (`time`, `name`, `type`, `source`, `status`, `message`)
249             VALUES
250             (?, ?, ?, ?, ?, ?)
251             DML
252              
253 2     2   8 use constant CHECKIT_UPDATE => <<'DML';
  2         3  
  2         75  
254             UPDATE monm SET
255             `time` = ?, `name` = ?, `type` = ?, `source` = ?, `status` = ?, `message` = ?
256             WHERE `id` = ?
257             DML
258              
259 2     2   9 use constant CHECKIT_DELETE => <<'DML';
  2         3  
  2         82  
260             DELETE FROM monm WHERE `id` = ?
261             DML
262              
263 2     2   9 use constant CHECKIT_CLEAN => <<'DML';
  2         18  
  2         72  
264             DELETE FROM monm WHERE `time` < ?
265             DML
266              
267 2     2   8 use constant CHECKIT_SELECT => <<'DML';
  2         3  
  2         94  
268             SELECT `id`, `time`, `name`, `type`, `source`, `status`, `message`
269             FROM monm
270             WHERE `name` = ?
271             DML
272              
273 2     2   10 use constant CHECKIT_SELECT_ALL => <<'DML';
  2         2  
  2         2167  
274             SELECT `id`, `time`, `name`, `type`, `source`, `status`, `message`
275             FROM monm
276             DML
277              
278             sub new {
279 1     1 1 75 my $class = shift;
280 1         23 my %args = @_;
281 1 50       3 unless ($args{dsn}) {
282 1         2 my $dda = DEFAULT_DBI_ATTR;
283 1         5 foreach (%$dda) {
284 8   66     23 $args{$_} //= $dda->{$_}
285             }
286             }
287 1   50     4 my $file = $args{file} || DB_FILENAME;
288 1   33     7 my $dsn = $args{dsn} || sprintf(DEFAULT_DSN_MASK, $file);
289              
290             # DB
291             my $db = CTK::DBI->new(
292             -dsn => $dsn,
293             -debug => 0,
294             -username => $args{'user'},
295             -password => $args{'password'},
296             -attr => set2attr($args{'set'}),
297             $args{timeout} ? (
298             -timeout_connect => $args{timeout},
299             -timeout_request => $args{timeout},
300 1 50       5 ) : (),
301             );
302 1 50       21760 my $dbh = $db->connect if $db;
303              
304             # SQLite
305 1         8 my $fnew = 0;
306 1         2 my $issqlite = 0;
307 1 50 33     17 if ($dbh && $dsn =~ /SQLite/i) {
308 1         8 $file = $dbh->sqlite_db_filename();
309 1 50 33     24 unless ($file && (-e $file) && !(-z $file)) {
      33        
310 1         6 touch($file);
311 1         102 chmod(0666, $file);
312 1         2 $fnew = 1;
313             }
314 1         2 $issqlite = 1;
315             }
316              
317             # Errors
318 1         2 my $error = "";
319 1 50       7 if (!$db) {
    50          
    50          
320 0         0 $error = sprintf("Can't init database \"%s\"", $dsn);
321             } elsif (!$dbh) {
322 0   0     0 $error = sprintf("Can't connect to database \"%s\": %s", $dsn, $DBI::errstr || "unknown error");
323             } elsif ($fnew) {
324 1         6 $db->execute(CHECKIT_DDL);
325 1 50       18263 $error = $dbh->errstr() if $dbh->err;
326             }
327 1 50       5 unless ($error) {
328 1 50 0     6 $error = sprintf("Can't init database \"%s\". Ping failed: %s",
329             $dsn, $dbh->errstr() || "unknown error") unless $dbh->ping;
330             }
331              
332 1   50     37 my $self = bless {
333             file => $file,
334             issqlite=> $issqlite,
335             dsn => $dsn,
336             error => $error // "",
337             dbi => $db,
338             }, $class;
339              
340 1         7 return $self;
341             }
342             sub error {
343 10     10 1 28 my $self = shift;
344 10         10 my $err = shift;
345 10 100       30 return $self->{error} unless defined $err;
346 6         22 $self->{error} = $err;
347 6         6 return $self->{error};
348             }
349             sub ping {
350 6     6 1 6 my $self = shift;
351 6 50       14 return 0 unless $self->{dsn};
352 6         7 my $dbi = $self->{dbi};
353 6 50       8 return 0 unless $dbi;
354 6         9 my $dbh = $dbi->{dbh};
355 6 50       9 return 0 unless $dbh;
356 6 50       28 return 0 unless $dbh->can('ping');
357 6         19 return $dbh->ping();
358             }
359             sub dsn {
360 0     0 1 0 my $self = shift;
361 0         0 return $self->{dsn};
362             }
363             sub is_sqlite {
364 0     0 1 0 my $self = shift;
365 0 0       0 return $self->{issqlite} ? 1 : 0;
366             }
367              
368             # CRUD Methods
369              
370             sub add {
371 1     1 1 2 my $self = shift;
372 1         6 my %params = @_;
373 1 50       4 return unless $self->ping;
374 1         39 $self->error("");
375 1         2 my $dbi = $self->{dbi};
376              
377             # Add
378             $dbi->execute(CHECKIT_INSERT,
379             time(),
380             $params{name},
381             $params{type},
382             $params{source},
383             $params{status} || 0,
384             $params{message},
385 1   50     10 )->finish;
386 1 50       5954 if ($dbi->connect->err) {
387 0         0 $self->error(sprintf("Can't insert new record: %s", uv2null($dbi->connect->errstr)));
388 0         0 return;
389             }
390              
391 1         14 return 1;
392             }
393             sub set {
394 1     1 1 2 my $self = shift;
395 1         4 my %params = @_;
396 1 50       3 return unless $self->ping;
397 1         28 $self->error("");
398 1         2 my $dbi = $self->{dbi};
399              
400             # Update
401             $dbi->execute(CHECKIT_UPDATE,
402             time(),
403             $params{name},
404             $params{type},
405             $params{source},
406             $params{status} || 0,
407             $params{message},
408             $params{id},
409 1   50     7 )->finish;
410 1 50       6608 if ($dbi->connect->err) {
411 0         0 $self->error(sprintf("Can't update record: %s", uv2null($dbi->connect->errstr)));
412 0         0 return;
413             }
414              
415 1         14 return 1;
416             }
417             sub del {
418 1     1 1 1 my $self = shift;
419 1         4 my %params = @_;
420 1 50       2 return unless $self->ping;
421 1         28 $self->error("");
422 1         2 my $dbi = $self->{dbi};
423              
424             # Del
425 1         3 $dbi->execute(CHECKIT_DELETE, $params{id})->finish;
426 1 50       6997 if ($dbi->connect->err) {
427 0         0 $self->error(sprintf("Can't delete record: %s", uv2null($dbi->connect->errstr)));
428 0         0 return;
429             }
430              
431 1         12 return 1;
432             }
433             sub get {
434 2     2 1 4 my $self = shift;
435 2         5 my %params = @_;
436 2 50       4 return () unless $self->ping;
437 2         53 $self->error("");
438 2         3 my $dbi = $self->{dbi};
439              
440             # Get
441 2   50     5 my $name = $params{name} || "";
442 2         15 my %info = $dbi->recordh(CHECKIT_SELECT, $name);
443 2 50       465 if ($dbi->connect->err) {
444 0         0 $self->error(sprintf("Can't get record: %s", uv2null($dbi->connect->errstr)));
445 0         0 return ();
446             }
447              
448 2         28 return %info;
449             }
450             sub getall {
451 1     1 1 302 my $self = shift;
452 1         2 my %params = @_;
453 1 50       2 return () unless $self->ping;
454 1         29 $self->error("");
455 1         1 my $dbi = $self->{dbi};
456              
457             # Get table
458 1         6 my %table = $dbi->tableh("id", CHECKIT_SELECT_ALL);
459 1 50       305 if ($dbi->connect->err) {
460 0         0 $self->error(sprintf("Can't get records: %s", uv2null($dbi->connect->errstr)));
461 0         0 return ();
462             }
463              
464             # Out
465 1         10 my @out = ();
466 1         6 foreach my $k (sort {$a <=> $b} keys %table) {
  0         0  
467 1         3 push @out, $table{$k};
468             }
469 1         4 return @out;
470             }
471             sub clean {
472 0     0 1   my $self = shift;
473 0           my %params = @_;
474 0 0         return unless $self->ping;
475 0           $self->error("");
476 0           my $dbi = $self->{dbi};
477              
478             # Clean
479 0   0       my $period = $params{period} || 0;
480 0           $dbi->execute(CHECKIT_CLEAN, time() - $period)->finish;
481 0 0         if ($dbi->connect->err) {
482 0           $self->error(sprintf("Can't clean (truncate) table: %s", uv2null($dbi->connect->errstr)));
483 0           return;
484             }
485              
486 0           return 1;
487             }
488              
489             1;
490              
491             __END__