File Coverage

blib/lib/Net/OpenStack/Client/API/Convert.pm
Criterion Covered Total %
statement 86 91 94.5
branch 38 44 86.3
condition 4 6 66.6
subroutine 10 10 100.0
pod 3 3 100.0
total 141 154 91.5


line stmt bran cond sub pod time code
1             package Net::OpenStack::Client::API::Convert;
2             $Net::OpenStack::Client::API::Convert::VERSION = '0.1.4';
3 10     10   66668 use strict;
  10         25  
  10         291  
4 10     10   49 use warnings qw(FATAL numeric);
  10         15  
  10         354  
5              
6 10     10   3489 use Net::OpenStack::Client::Request;
  10         24  
  10         504  
7              
8             # cannot use 'use Types::Serialiser'; it is incompatible with JSON::XS 2.X (eg on EL6)
9 10     10   4719 use JSON::XS;
  10         33566  
  10         462  
10 10     10   66 use Readonly;
  10         17  
  10         368  
11              
12 10     10   51 use base qw(Exporter);
  10         26  
  10         10339  
13              
14             our @EXPORT_OK = qw(convert process_args);
15              
16             # Convert dispatch table
17             Readonly::Hash my %CONVERT_DISPATCH => {
18             string => sub {my $val = shift; return "$val";}, # stringify
19             long => sub {my $val = shift; return 0 + $val;}, # Force internal conversion to int/long
20             double => sub {my $val = shift; return 1.0 * $val;}, # Force internal conversion to float/double
21             boolean => sub {my $val = shift; return $val ? JSON::XS::true : JSON::XS::false;},
22             };
23              
24             # Aliases for each dispatch
25             Readonly::Hash my %CONVERT_ALIAS => {
26             };
27              
28             Readonly my $API_REST_OPTION_PATTERN => '^__';
29              
30              
31             =head1 NAME
32              
33             Net::OpenStack::Client::API::Convert provides type conversion for Net::OpenStack
34              
35             =head2 Public functions
36              
37             =over
38              
39             =item convert
40              
41             Convert/cast value to type.
42              
43             If a type is not found in the dispatch table, log a warning and return the value as-is.
44              
45             Always returns value, dies when dealing with non-convertable type (using 'FATAL numeric').
46              
47             =cut
48              
49             # Do not use intermediate variables for the result
50              
51             sub convert
52             {
53 103     103 1 1689 my ($value, $type) = @_;
54              
55 103         380 my $funcref = $CONVERT_DISPATCH{$type};
56              
57 103 100       708 if (!defined($funcref)) {
58             # is it an alias?
59 46         130 foreach my $tmpref (sort keys %CONVERT_ALIAS) {
60 0 0       0 $funcref = $CONVERT_DISPATCH{$tmpref} if (grep {$_ eq $type} @{$CONVERT_ALIAS{$tmpref}});
  0         0  
  0         0  
61             }
62             };
63              
64 103 100       417 if (defined($funcref)) {
65 57         92 my $vref = ref($value);
66 57 100       126 if ($vref eq 'ARRAY') {
    100          
67 4         10 return [map {$funcref->($_)} @$value];
  6         16  
68             } elsif ($vref eq 'HASH') {
69 1         4 return {map {$_ => $funcref->($value->{$_})} sort keys %$value};
  3         11  
70             } else {
71 52         96 return $funcref->($value);
72             };
73             } else {
74 46         128 return $value;
75             }
76             }
77              
78             =item check_option
79              
80             Given the (single) option hashref C
81             verify the value, convert it and add it to C.
82              
83             (Adding to where is required to avoid using intermediadate variables
84             which can cause problems for the internal types).
85              
86             Returns errormessage (which is undef on success).
87              
88             =cut
89              
90             sub check_option
91             {
92 120     120 1 4551 my ($opt, $value, $where, $attr) = @_;
93              
94 120         157 my $errmsg;
95              
96 120         166 my $ref = ref($value);
97 120         167 my $name = $opt->{name};
98              
99 120 50       212 if ($attr) {
100             # insert value attribute if needed. Reset where to this attribute
101             };
102              
103             # Check mandatory / undef
104 120 100       225 my $mandatory = $opt->{required} ? 1 : 0;
105              
106 120 100 66     291 if (! defined($value)) {
    50          
107 38 100       99 if ($mandatory) {
108 5         12 $errmsg = "name $name mandatory with undefined value";
109             };
110             } elsif (!$ref || $ref eq 'ARRAY') {
111             # Convert and add to where
112 82         134 my $wref = ref($where);
113 82         111 local $@;
114 82         133 eval {
115 82 100       221 if ($wref eq 'ARRAY') {
    100          
116 2         6 push(@$where, convert($value, $opt->{type}));
117             } elsif ($wref eq 'HASH') {
118 79         143 $where->{$name} = convert($value, $opt->{type});
119             } else {
120 1         3 $errmsg = "name $name unknown where ref $wref";
121             };
122             };
123 82 100       203 $errmsg = "name $name where ref $wref died $@" if ($@);
124             } else {
125 0         0 $errmsg = "name $name wrong multivalue (ref $ref)";
126             };
127              
128 120         221 return $errmsg;
129             }
130              
131             =item process_args
132              
133             Given the command hashref C and the arguments passed, return
134             Request instance.
135              
136             Command hashref
137              
138             =over
139              
140             =item endpoint
141              
142             =item method
143              
144             =item templates (optional)
145              
146             =item parameters (optional)
147              
148             =item options (optional)
149              
150             (All options starting with C<__> are passed as options to
151             C, with C<__> prefix removed).
152              
153             =back
154              
155             Request instance:
156              
157             =over
158              
159             =item error: an error message in case of failure
160              
161             =item tpls: hashref with templates for endpoint
162              
163             =item params: hashref with parameters for endpoint
164              
165             =item opts: hashref with options
166              
167             =item rest: hashref with options for the REST call
168              
169             =back
170              
171             Values are converted using C function.
172              
173             =cut
174              
175             sub process_args
176             {
177 38     38 1 808 my ($cmdhs, @args) = @_;
178              
179             # template name and value
180 38         68 my $templates = {};
181             # parameters name and value
182 38         59 my $parameters = {};
183             # option name and value
184 38         58 my $options = {};
185             # option name and path (separate from option values)
186 38         59 my $paths = {};
187             # rest options
188 38         59 my $rest = {};
189              
190 38         58 my $errmsg;
191              
192 38         69 my $endpoint = $cmdhs->{endpoint};
193 38         57 my $method = $cmdhs->{method};
194              
195             my $err_req = sub {
196 6     6   23 $errmsg = join(" ", "$endpoint $method:", shift, $errmsg);
197 6         17 return mkrequest($endpoint, $method, error => $errmsg);
198 38         183 };
199              
200 38         123 my %origopts = @args;
201              
202 38         111 my $raw = delete $origopts{raw};
203 38 50 66     126 if ($raw && ref($raw) ne 'HASH') {
204 0         0 return &$err_req("raw option must be a hashref, got ".ref($raw));
205             }
206              
207             # Check endpoint template values; sort of mandatory special named options
208             # The processed options are removed from %origopts
209             # TODO: naming conflict between JSON key, parameter and template name? (handled in gen.pl)
210 38 100       61 foreach my $name (@{$cmdhs->{templates} || []}) {
  38         153  
211             # all strings, used for templating
212 40         166 $errmsg = check_option({name => $name, required => 1, type => 'str'}, delete $origopts{$name}, $templates);
213 40 100       111 return &$err_req("endpoint template $name") if $errmsg;
214             }
215              
216             # Check parameters
217 36 100       90 foreach my $name (@{$cmdhs->{parameters} || []}) {
  36         141  
218             # all strings, used for url buildup
219 30         80 $errmsg = check_option({name => $name, type => 'str'}, delete $origopts{$name}, $parameters);
220 30 50       85 return &$err_req("endpoint parameter $name") if $errmsg;
221             }
222              
223             # Check options
224             # Process all options (for JSON data)
225             # The processed options are removed from %origopts
226 36 100       61 foreach my $name (sort keys %{$cmdhs->{options} || {}}) {
  36         146  
227 44         76 my $opt = $cmdhs->{options}->{$name};
228 44 100       96 $opt->{name} = $name if ! exists($opt->{$name});
229              
230             # Need both value (added via check_option) and path
231 44         74 $paths->{$name} = $opt->{path};
232 44         97 $errmsg = check_option($opt, delete $origopts{$name}, $options);
233 44 100       120 return &$err_req("option $name") if $errmsg;
234             }
235              
236             # Filter out any REST options
237             # Any remaining key is invalid
238 33         86 foreach my $name (sort keys %origopts) {
239 2 100       8 if ($name =~ m/$API_REST_OPTION_PATTERN/) {
240 1         7 my $val = $origopts{$name};
241 1         3 $name =~ s/$API_REST_OPTION_PATTERN//;
242 1         16 $rest->{$name} = $val;
243             } else {
244 1         18 return &$err_req("option invalid name $name");
245             };
246             }
247              
248             # No error
249             return mkrequest(
250             $endpoint,
251             $method,
252             tpls => $templates,
253             params => $parameters,
254             opts => $options,
255             paths => $paths,
256             rest => $rest,
257             raw => $raw,
258             service => $cmdhs->{service},
259             version => $cmdhs->{version},
260             result => $cmdhs->{result},
261 32         148 );
262             }
263              
264             =pod
265              
266             =back
267              
268             =cut
269              
270             1;