File Coverage

blib/lib/VMware/vCloudDirector2/Object.pm
Criterion Covered Total %
statement 59 195 30.2
branch 3 102 2.9
condition 0 24 0.0
subroutine 34 55 61.8
pod n/a
total 96 376 25.5


line stmt bran cond sub pod time code
1             package VMware::vCloudDirector2::Object;
2              
3             # ABSTRACT: Module to contain an object!
4              
5 4     4   27 use strict;
  4         8  
  4         122  
6 4     4   20 use warnings;
  4         7  
  4         252  
7              
8             our $VERSION = '0.106'; # VERSION
9             our $AUTHORITY = 'cpan:NIGELM'; # AUTHORITY
10              
11 4     4   24 use Moose;
  4         17  
  4         123  
12 4     4   28388 use Method::Signatures;
  4         10  
  4         40  
13 4     4   1703 use MooseX::Types::URI qw(Uri);
  4         9  
  4         85  
14 4     4   8776 use Const::Fast;
  4         3946  
  4         23  
15 4     4   257 use Ref::Util qw(is_plain_hashref is_plain_arrayref);
  4         10  
  4         182  
16 4     4   1507 use VMware::vCloudDirector2::Link;
  4         13  
  4         152  
17 4     4   42 use VMware::vCloudDirector2::Error;
  4         8  
  4         1546  
18              
19             # ------------------------------------------------------------------------
20              
21              
22             has api => (
23             is => 'ro',
24             isa => 'VMware::vCloudDirector2::API',
25             required => 1,
26             weak_ref => 1,
27             documentation => 'API we use'
28             );
29              
30             has mime_type => ( is => 'ro', isa => 'Str', required => 1 );
31             has href => ( is => 'ro', isa => Uri, required => 1, coerce => 1 );
32             has type => ( is => 'ro', isa => 'Str', required => 1 );
33             has uuid => ( is => 'ro', isa => 'Str', builder => '_build_uuid', lazy => 1 );
34             has name =>
35             ( is => 'ro', isa => 'Str', predicate => 'has_name', lazy => 1, builder => '_build_name' );
36             has id => ( is => 'ro', isa => 'Str', predicate => 'has_id', lazy => 1, builder => '_build_id' );
37              
38             has _partial_object => ( is => 'rw', isa => 'Bool', default => 0 );
39             has is_json => ( is => 'rw', isa => 'Bool', default => 0 );
40              
41             # ------------------------------------------------------------------------
42             around BUILDARGS => sub {
43             my ( $orig, $class, $first, @rest ) = @_;
44              
45             my $params = is_plain_hashref($first) ? $first : { $first, @rest };
46             if ( $params->{hash} ) {
47             my $hash = $params->{hash};
48              
49             # copy elements into object attributes
50             foreach (qw[href name id]) {
51             $params->{$_} = $hash->{$_} if ( exists( $hash->{$_} ) and defined( $hash->{$_} ) );
52             }
53              
54             # set the object type and mime_type
55             if ( exists( $hash->{type} ) ) {
56             $params->{mime_type} = $hash->{type};
57             $params->{type} = $1
58             if ( $hash->{type} =~ m!^application/vnd\..*\.(\w+)\+(json|xml)$! );
59             $params->{is_json} = ( $2 eq 'json' ) ? 1 : 0;
60             }
61              
62             # if this has a links section it is a complete object, otherwise its partial
63             if ( exists( $hash->{link} ) ) {
64             $params->{_partial_object} = 0;
65             const $params->{hash} => $hash; # force hash read-only to stop people playing
66             }
67             else {
68             $params->{_partial_object} = 1;
69             delete( $params->{hash} ); # do not populate the hash in the partial object
70             }
71             }
72             else {
73             # no hash so this must be a partial object
74             $params->{_partial_object} = 1;
75             }
76             return $class->$orig($params);
77             };
78              
79             # ------------------------------------------------------------------------
80             has hash => (
81             is => 'ro',
82             traits => ['Hash'],
83             isa => 'HashRef',
84             builder => '_build_hash',
85             clearer => '_clear_hash',
86             lazy => 1,
87             handles => { get_hash_item => 'get', exists_hash_item => 'exists', }
88             );
89              
90 4 50   4   11357 method _build_hash () {
  1     1   3  
  1         4  
91              
92             # fetch object content
93 1         28 const my $hash => $self->api->GET_hash( $self->href );
94 1 0       1868 $self->api->_debug(
    50          
95             sprintf(
96             'Object: %s a [%s]',
97             ( $self->_partial_object ? 'Inflated' : 'Refetched' ),
98             $self->type
99             )
100             ) if ( $self->api->debug );
101              
102             # mark as being a whole object
103 1         30 $self->_partial_object(0);
104              
105 1         31 return $hash;
106             }
107              
108 4 0   4   2896 method _build_name () { return $self->get_hash_item('name'); }
  0     0   0  
  0         0  
  0         0  
109 4 50   4   2575 method _build_id () { return $self->get_hash_item('id'); }
  1     1   3  
  1         4  
  1         34  
110              
111 4 0   4   2672 method _build_uuid () {
  0     0      
  0            
112              
113             # The UUID is in the href - return the first match
114 0           my $path = lc( $self->href->path() );
115 0 0         return $1
116             if ( $path =~ m|\b([0-9a-f]{8}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{12})\b| );
117 0           return;
118             }
119              
120             # ------------------------------------------------------------------------
121              
122             has _links => (
123             is => 'ro',
124             traits => ['Array'],
125             isa => 'ArrayRef[VMware::vCloudDirector2::Link]',
126             lazy => 1,
127             builder => '_build_links',
128             clearer => '_clear_links',
129             handles => { links => 'elements', },
130             );
131             has _all_links => (
132             is => 'ro',
133             traits => ['Array'],
134             isa => 'ArrayRef[VMware::vCloudDirector2::Link]',
135             lazy => 1,
136             builder => '_build_all_links',
137             clearer => '_clear_all_links',
138             handles => { all_links => 'elements', },
139             );
140              
141 4 0   4   2874 method _build_links () {
  0     0      
  0            
142 0           my @links = grep { $_->is_json } $self->all_links;
  0            
143 0           return \@links;
144             }
145              
146 4 0   4   2580 method _build_all_links () {
  0     0      
  0            
147 0           my @links;
148 0 0         if ( exists( $self->hash->{link} ) ) {
149             push( @links, VMware::vCloudDirector2::Link->new( hash => $_, object => $self ) )
150 0           foreach ( $self->_listify( $self->hash->{link} ) );
151             }
152 0           return \@links;
153             }
154              
155             # ------------------------------------------------------------------------
156              
157              
158             has is_admin_object => (
159             is => 'ro',
160             isa => 'Bool',
161             lazy => 1,
162             builder => '_build_is_admin_object',
163             documentation => 'Is this an admin level object?',
164             );
165 4 0   4   2855 method _build_is_admin_object () { return ( $self->href->path() =~ m|/api/admin/| ) ? 1 : 0; }
  0 0   0      
  0            
  0            
166              
167             # ------------------------------------------------------------------------
168              
169              
170 4 0   4   2655 method inflate () {
  0     0      
  0            
171 0 0         $self->refetch if ( $self->_partial_object );
172 0           return $self;
173             }
174              
175             # ------------------------------------------------------------------------
176 4 0   4   2454 method refetch () {
  0     0      
  0            
177              
178             # simplest way to force the object to be refetched is to clear the hash
179             # and then request it which forces a lazy eval
180 0           $self->_clear_hash;
181 0           $self->_clear_links;
182 0           $self->_clear_all_links;
183 0           $self->hash;
184              
185 0           return $self;
186             }
187              
188             # ------------------------------------------------------------------------
189              
190              
191 4 0   4   19234 method find_links (:$name, :$type, :$rel) {
  0 0   0      
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
192 0           my @matched_links;
193 0           foreach my $link ( $self->links ) {
194 0 0 0       if ( not( defined($rel) ) or ( $rel eq ( $link->rel || '' ) ) ) {
      0        
195 0 0 0       if ( not( defined($type) ) or ( $type eq ( $link->type || '' ) ) ) {
      0        
196 0 0 0       if ( not( defined($name) ) or ( $name eq ( $link->name || '' ) ) ) {
      0        
197 0           push( @matched_links, $link );
198             }
199             }
200             }
201             }
202 0           return @matched_links;
203             }
204              
205             # ------------------------------------------------------------------------
206              
207              
208 4     4   9264 method find_link (@criteria) {
  0     0      
  0            
209 0           my @matched_links = $self->find_links(@criteria);
210 0 0         unless ( scalar(@matched_links) ) {
211 0           VMware::vCloudDirector2::Error->throw(
212             { message => sprintf( "No links matching criteria: %s", join( ', ', @criteria ) ),
213             object => $self
214             }
215             );
216             }
217 0           return $matched_links[0];
218             }
219              
220             # ------------------------------------------------------------------------
221              
222              
223 4     4   7947 method fetch_links (@search_items) {
  0     0      
  0            
224 0           my @matched_objects;
225 0           foreach my $link ( $self->find_links(@search_items) ) {
226 0           push( @matched_objects, $link->GET() );
227             }
228 0           return @matched_objects;
229             }
230              
231             # ------------------------------------------------------------------------
232 4 0   4   18013 method _create_object ($hash, $type='Thing') {
  0 0   0      
  0 0          
  0            
  0            
  0            
233              
234             # if thing has Link content within it then it is a full object, otherwise it
235             # is just a stub
236             my $object = VMware::vCloudDirector2::Object->new(
237             hash => $hash,
238             api => $self->api,
239 0 0         _partial_object => ( exists( $hash->{link} ) ) ? 0 : 1,
240             );
241 0 0         $self->api->_debug(
    0          
242             sprintf(
243             'Object: [%s] instantiated %s for [%s]',
244             $self->type, ( $object->_partial_object ? 'a stub' : 'an object' ),
245             $object->type
246             )
247             ) if ( $self->api->debug );
248 0           return $object;
249             }
250              
251             # ------------------------------------------------------------------------
252              
253              
254 4 0   4   8379 method build_sub_objects ($type) {
  0 0   0      
  0            
  0            
  0            
255 0           my @objects;
256              
257 0 0         return unless ( exists( $self->hash->{$type} ) );
258 0           foreach my $thing ( $self->_listify( $self->hash->{$type} ) ) {
259 0           push( @objects, $self->_create_object( $thing, $type ) );
260             }
261 0           return @objects;
262             }
263              
264 4 0   4   10758 method build_sub_sub_objects ($type, $subtype) {
  0 0   0      
  0 0          
  0            
  0            
  0            
  0            
265 0           my @objects;
266              
267 0 0 0       return unless ( exists( $self->hash->{$type} ) and is_plain_hashref( $self->hash->{$type} ) );
268 0 0         return unless ( exists( $self->hash->{$type}{$subtype} ) );
269 0           foreach my $thing ( $self->_listify( $self->hash->{$type}{$subtype} ) ) {
270 0           push( @objects, $self->_create_object( $thing, $subtype ) );
271             }
272 0           return @objects;
273             }
274              
275 4 0   4   3387 method build_children_objects () {
  0     0      
  0            
276 0           my $hash = $self->hash;
277 0 0 0       return unless ( exists( $hash->{children} ) and is_plain_hashref( $hash->{children} ) );
278 0           my @objects;
279 0           foreach my $key ( keys %{ $hash->{children} } ) {
  0            
280 0           foreach my $thing ( $self->_listify( $self->hash->{children}{$key} ) ) {
281 0           push( @objects, $self->_create_object( $thing, $key ) );
282             }
283             }
284 0           return @objects;
285             }
286              
287             # ------------------------------------------------------------------------
288              
289              
290 4 0   4   2974 method DELETE () { return $self->api->DELETE( $self->href ); }
  0     0      
  0            
  0            
291              
292              
293 4 0   4   2476 method GET () { return $self->api->GET( $self->href ); }
  0     0      
  0            
  0            
294 4 0   4   6607 method GET_hash () { return $self->api->GET_hash( $self->href ); }
  0     0      
  0            
  0            
295              
296              
297 4 0   4   9041 method POST ($hash) { return $self->api->POST( $self->href, $hash, $self->mime_type ); }
  0 0   0      
  0            
  0            
  0            
  0            
298              
299              
300 4 0   4   8034 method PUT ($hash) { return $self->api->PUT( $self->href, $hash, $self->mime_type ); }
  0 0   0      
  0            
  0            
  0            
  0            
301              
302             # ------------------------------------------------------------------------
303              
304              
305 4 0   4   9685 method fetch_admin_object ($subpath?) {
  0     0      
  0            
  0            
306 0 0 0       if ( $self->is_admin_object and not( defined($subpath) ) ) {
307 0           return $self;
308             }
309             else {
310 0           my $uri = $self->href;
311 0           my $path = $uri->path;
312 0           $path =~ s|^/api/|api/admin/|;
313 0 0         $path .= '/' . $subpath if ( defined($subpath) );
314 0           return $self->api->GET($path);
315             }
316             }
317              
318             # ------------------------------------------------------------------------
319 4 0   4   7962 method _listify ($thing) { !defined $thing ? () : ( ( ref $thing eq 'ARRAY' ) ? @{$thing} : $thing ) }
  0 0   0      
  0 0          
  0 0          
  0            
  0            
  0            
320              
321             # ------------------------------------------------------------------------
322              
323             __PACKAGE__->meta->make_immutable;
324              
325             1;
326              
327             __END__
328              
329             =pod
330              
331             =encoding UTF-8
332              
333             =head1 NAME
334              
335             VMware::vCloudDirector2::Object - Module to contain an object!
336              
337             =head1 VERSION
338              
339             version 0.106
340              
341             =head2 Attributes
342              
343             =head3 api
344              
345             A weak link to the API object to be used.
346              
347             =head3 content
348              
349             The object content. This is in a separate container so that partial objects
350             passed can be inflated at a later stage without having to replace the object
351             itself.
352              
353             =head3 hash
354              
355             A reference to the hash returned from the vCloud API. Forces object inflation.
356              
357             =head3 links
358              
359             Returns L<VMware::vCloudDirector2::Link> objects for each of the JSON targetted
360             links contained in this object. Forces object inflation.
361              
362             =head3 all_links
363              
364             Returns L<VMware::vCloudDirector2::Link> objects for each of the links
365             contained in this object. Will typically return two links per thing - one to
366             the XML version, one to the JSON version. Forces object inflation.
367              
368             =head3 id
369              
370             The id attribute from the returned vCloud JSON. Forces object inflation.
371              
372             =head3 is_admin_object
373              
374             This determines, based on the href path, whether or not this is an admin
375             object.
376              
377             =head2 Methods
378              
379             =head3 inflate
380              
381             If this object is a partial object (ie taken from a link or partial chunk
382             within a containing object), then this forces a refetch of the content from
383             vCloud creating a fully populated object.
384              
385             =head3 refetch
386              
387             Forces a refetch of this object's content unconditionally.
388              
389             =head3 find_links
390              
391             Returns any links found that match the search criteria. The possible criteria
392             are:-
393              
394             =over 4
395              
396             =item name
397              
398             The name of the link
399              
400             =item type
401              
402             The type of the link (short type, not full MIME type)
403              
404             =item rel
405              
406             The rel of the link
407              
408             =back
409              
410             The return value is a list of link objects.
411              
412             =head3 find_link
413              
414             Finds and returns one link that matches the search criteria, exactly as
415             L<find_links>, except that if no links are found an exception is thrown. If
416             multiple links match then the first one returned (normally the first one back
417             from the API) would be returned.
418              
419             The return value is a single link object.
420              
421             =head3 fetch_links
422              
423             As per L</find_links> except that each link found is fetched and expanded up as
424             an object.
425              
426             =head3 build_sub_objects
427              
428             Given a type (specifically a key used within the current object hash), grabs
429             the descendants of that key and instantiates them as partial objects (they can
430             then be inflated into full objects).
431              
432             =head3 build_sub_sub_objects
433              
434             Similar to L<build_sub_objects>, but builds objects from two levels down.
435              
436             =head3 build_children_objects
437              
438             Similar to L<build_sub_objects>, but builds objects from within a children hash
439              
440             =head3 DELETE
441              
442             Make a delete request to the URL in this link. Returns Objects. Failure will
443             generate an exception. See L<VMware::vCloudDirector2::API/DELETE>.
444              
445             =head3 GET
446              
447             Make a get request to the URL in this link. Returns Objects. Failure will
448             generate an exception. See L<VMware::vCloudDirector2::API/GET>.
449              
450             =head3 GET_hash
451              
452             Make a get request to the URL in this link. Returns a decoded hash. Failure
453             will generate an exception. See L<VMware::vCloudDirector2::API/GET_hash>.
454              
455             =head3 POST
456              
457             Make a post request with the specified payload to the URL in this link. Returns
458             Objects. Failure will generate an exception. See
459             L<VMware::vCloudDirector2::API/POST>.
460              
461             =head3 PUT
462              
463             Make a put request with the specified payload to the URL in this link. Returns
464             Objects. Failure will generate an exception. See
465             L<VMware::vCloudDirector2::API/PUT>.
466              
467             =head3 fetch_admin_object
468              
469             If this is already an admin object (ie C<is_admin_object> is true), then this
470             object is returned.
471              
472             Otherwise, the path is modified to point to the admin API object and the object
473             is fetched. Since this only exists for a subset of objects there is a
474             reasonable chance that just attempting this will lead to an exception being
475             thrown due to a non-existant object being requested.
476              
477             =head1 AUTHOR
478              
479             Nigel Metheringham <nigelm@cpan.org>
480              
481             =head1 COPYRIGHT AND LICENSE
482              
483             This software is copyright (c) 2019 by Nigel Metheringham.
484              
485             This is free software; you can redistribute it and/or modify it under
486             the same terms as the Perl 5 programming language system itself.
487              
488             =cut