File Coverage

blib/lib/Mail/TLSRPT/Report.pm
Criterion Covered Total %
statement 110 115 95.6
branch 10 16 62.5
condition n/a
subroutine 22 22 100.0
pod 8 8 100.0
total 150 161 93.1


line stmt bran cond sub pod time code
1             package Mail::TLSRPT::Report;
2             # ABSTRACT: TLSRPT report object
3             our $VERSION = '2.20210112'; # VERSION
4 7     7   4486 use 5.20.0;
  7         25  
5 7     7   38 use Moo;
  7         13  
  7         43  
6 7     7   2185 use Mail::TLSRPT::Pragmas;
  7         15  
  7         44  
7 7     7   4144 use Mail::TLSRPT::Policy;
  7         23  
  7         223  
8 7     7   50 use Mail::TLSRPT::Failure;
  7         14  
  7         126  
9 7     7   6539 use DateTime;
  7         3108320  
  7         380  
10 7     7   3837 use Date::Parse qw{ str2time };
  7         45549  
  7         635  
11 7     7   3944 use IO::Uncompress::Gunzip;
  7         287458  
  7         384  
12 7     7   4979 use Text::CSV;
  7         92327  
  7         9404  
13             has organization_name => (is => 'rw', isa => Str, required => 1);
14             has start_datetime => (is => 'rw', isa => class_type('DateTime'), required => 1, coerce => sub{&_coerce_datetime});
15             has end_datetime => (is => 'rw', isa => class_type('DateTime'), required => 1, coerce => sub{&_coerce_datetime});
16             has contact_info => (is => 'rw', isa => Str, required => 1);
17             has report_id => (is => 'rw', isa => Str, required => 1);
18 1     1   4525 has policies => (is => 'rw', isa => ArrayRef, required => 0, lazy => 1, builder => sub{return []} );
19              
20             sub _coerce_datetime {
21 18     18   57 my $time = shift;
22 18 100       62 $time = DateTime->from_epoch( epoch=>$time ) unless ref $time eq 'DateTime';
23 18         581 return $time;
24             }
25              
26              
27 7     7 1 25541 sub new_from_json($class,$json) {
  7         18  
  7         15  
  7         13  
28 7 50       39 if ( $json =~ /^\037\213/ ) {
29 0         0 return $class->new_from_json_gz($json);
30             }
31 7         115 my $j = JSON->new;
32 7         177 my $data = $j->decode($json);
33 7         31 return $class->new_from_data($data);
34             }
35              
36              
37 1     1 1 3511 sub new_from_json_gz($class,$compressed_json) {
  1         2  
  1         2  
  1         1  
38 1         3 my $json;
39 1         5 IO::Uncompress::Gunzip::gunzip(\$compressed_json,\$json);
40 1         2067 return $class->new_from_json($json);
41             }
42              
43              
44 8     8 1 5163 sub new_from_data($class,$data) {
  8         15  
  8         64  
  8         10  
45 8         14 my @policies;
46 8         29 foreach my $policy ( $data->{policies}->@* ) {
47 6         40 push @policies, Mail::TLSRPT::Policy->new_from_data($policy);
48             }
49             my $self = $class->new(
50             organization_name => $data->{'organization-name'},
51             start_datetime => DateTime->from_epoch(epoch => str2time($data->{'date-range'}->{'start-datetime'})),
52             end_datetime => DateTime->from_epoch(epoch => str2time($data->{'date-range'}->{'end-datetime'})),
53             contact_info => $data->{'contact-info'},
54 8         78 report_id => $data->{'report-id'},
55             policies => \@policies,
56             );
57 8         313 return $self;
58             }
59              
60              
61 3     3 1 2571 sub as_json($self) {
  3         5  
  3         6  
62 3         12 my $j = JSON->new;
63 3         29 $j->canonical;
64 3         8 return $j->encode( $self->as_struct );
65             }
66              
67              
68 5     5 1 1684 sub as_struct($self) {
  5         9  
  5         8  
69 5         115 my @policies = map {$_->as_struct} $self->policies->@*;
  3         29  
70             return {
71 5 100       399 'organization-name' => $self->organization_name,
72             'date-range' => {
73             'start-datetime' => $self->start_datetime->datetime.'Z',
74             'end-datetime' => $self->end_datetime->datetime.'Z',
75             },
76             'contact-info' => $self->contact_info,
77             'report-id' => $self->report_id,
78             scalar $self->policies->@* ? ( policies => \@policies ) : (),
79             };
80             }
81              
82              
83 4     4 1 11432 sub as_string($self) {
  4         9  
  4         7  
84             return join( "\n",
85             'Report-ID: <'.$self->report_id.'>',
86             'From: "'.$self->organization_name.'" <'.$self->contact_info.'>',
87             'Dates: '.$self->start_datetime->datetime.'Z to '.$self->end_datetime->datetime.'Z',
88 4         95 map { $_->as_string } $self->policies->@*,
  1         148  
89             );
90             }
91              
92 1     1   2 sub _register_prometheus($self,$prometheus) {
  1         2  
  1         2  
  1         1  
93 1         3 $prometheus->declare('tlsrpt_reports_processed_total', help=>'TLSRPT reports processed', type=>'counter' );
94             }
95              
96              
97 1     1 1 1452 sub process_prometheus($self,$prometheus) {
  1         2  
  1         2  
  1         1  
98 1         4 $self->_register_prometheus($prometheus);
99 1         240 $prometheus->add('tlsrpt_reports_processed_total',1,{
100             organization_name=>$self->organization_name,
101             policies=>scalar $self->policies->@*,
102             });
103 1 50       91 Mail::TLSRPT::Policy->_register_prometheus($prometheus) if ! scalar $self->policies->@*;
104 1 50       19 Mail::TLSRPT::Failure->_register_prometheus($prometheus) if ! scalar $self->policies->@*;
105 1         18 foreach my $policy ( $self->policies->@* ) {
106 1         8 $policy->process_prometheus($self,$prometheus);
107             }
108             }
109              
110 1     1   2 sub _csv_headers($self) {
  1         2  
  1         2  
111             return (
112 1         7 'report id',
113             'organization name',
114             'start date time',
115             'end date time',
116             'contact info',
117             );
118             }
119              
120 3     3   5 sub _csv_fragment($self) {
  3         5  
  3         5  
121             return (
122 3         51 $self->report_id,
123             $self->organization_name,
124             $self->start_datetime->datetime.'Z',
125             $self->end_datetime->datetime.'Z',
126             $self->contact_info,
127             );
128             }
129              
130              
131 1     1 1 664 sub as_csv($self,$args) {
  1         2  
  1         2  
  1         2  
132 1         2 my @output;
133 1         8 my $csv = Text::CSV->new;
134 1 50       161 if ( $args->{add_header} ) {
135 1         5 $csv->combine($self->_csv_headers,Mail::TLSRPT::Policy->_csv_headers,Mail::TLSRPT::Failure->_csv_headers);
136 1         46 push @output, $csv->string;
137             }
138 1 50       35 if ( scalar $self->policies->@* ) {
139 1         28 foreach my $policy ( $self->policies->@* ) {
140 1 50       26 if ( scalar $policy->failures->@* ) {
141 1         26 foreach my $failure ( $policy->failures->@* ) {
142 3         26 $csv->combine($self->_csv_fragment,$policy->_csv_fragment,$failure->_csv_fragment);
143 3         538 push @output, $csv->string;
144             }
145             }
146             else {
147 0         0 $csv->combine($self->_csv_fragment,$policy->_csv_fragment);
148 0         0 push @output, $csv->string;
149             }
150             }
151             }
152             else {
153 0         0 $csv->combine($self->_csv_fragment);
154 0         0 push @output, $csv->string;
155             }
156 1         45 return join( "\n", @output );
157             }
158              
159             1;
160              
161             __END__
162              
163             =pod
164              
165             =encoding UTF-8
166              
167             =head1 NAME
168              
169             Mail::TLSRPT::Report - TLSRPT report object
170              
171             =head1 VERSION
172              
173             version 2.20210112
174              
175             =head1 SYNOPSIS
176              
177             my $report = Mail::TLSRPT::Report->new(
178             organization_name => 'My Corp',
179             start_datetime => $date,
180             end_datetime => $enddate,
181             contact_info => 'reports@example.com',
182             report_id => '123abc',
183             policies => $policies,
184             };
185              
186             =head1 DESCRIPTION
187              
188             Classes to process tlsrpt report
189              
190             =head1 CONSTRUCTOR
191              
192             =head2 I<new($class)>
193              
194             Create a new object
195              
196             =head2 I<new_from_json($json)>
197              
198             Create a new object using a JSON string, this will create sub-objects as required.
199              
200             Will detect and handle a gzipped string.
201              
202             =head2 I<new_from_json_gz($json)>
203              
204             Create a new object using a gzipped JSON string, this will create sub-objects as required.
205              
206             =head2 I<new_from_data($data)>
207              
208             Create a new object using a data structure, this will create sub-objects as required.
209              
210             =head1 METHODS
211              
212             =head2 I<as_json>
213              
214             Return the current object and sub-objects as a json string
215              
216             =head2 I<as_struct>
217              
218             Return the current object and sub-objects as a data structure
219              
220             =head2 I<as_string>
221              
222             Return a textual human readable representation of the current object and its sub-objects
223              
224             =head2 I<process_prometheus($prometheus)>
225              
226             Generate metrics using the given Prometheus::Tiny object
227              
228             =head2 I<as_csv($args)>
229              
230             Return a csv representation of the current object and its sub-objects
231              
232             If the argument add_header is true then a csv header will be included in the output.
233              
234             =head1 AUTHOR
235              
236             Marc Bradshaw <marc@marcbradshaw.net>
237              
238             =head1 COPYRIGHT AND LICENSE
239              
240             This software is copyright (c) 2020 by Marc Bradshaw.
241              
242             This is free software; you can redistribute it and/or modify it under
243             the same terms as the Perl 5 programming language system itself.
244              
245             =cut