File Coverage

blib/lib/WWW/GoDaddy/REST/Shell/GraphCommand.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package WWW::GoDaddy::REST::Shell::GraphCommand;
2              
3 1     1   4 use strict;
  1         1  
  1         33  
4 1     1   4 use warnings;
  1         1  
  1         20  
5              
6 1     1   4 use Carp;
  1         32  
  1         72  
7 1     1   223 use GraphViz;
  0            
  0            
8             use GraphViz::Data::Grapher;
9             use Sub::Exporter -setup => {
10             exports => [qw(run_graph smry_graph help_graph comp_graph)],
11             groups => { default => [qw(run_graph smry_graph help_graph comp_graph)] }
12             };
13              
14             sub run_graph {
15             my ( $self, @args ) = @_;
16              
17             my @opts = grep {/=/} @args;
18             my @schemas = grep {/^=/} @args;
19              
20             my $client = $self->client;
21              
22             my @plot_schemas;
23              
24             if (@schemas) {
25             foreach (@schemas) {
26             if ( !$client->schema($_) ) {
27             warn("'$_' is not a recognized schema");
28             return 0;
29             }
30             push @plot_schemas, $client->schema($_);
31             }
32             }
33             else {
34             @plot_schemas = @{ $client->schemas() };
35             }
36              
37             do_graphviz( $self, @plot_schemas );
38              
39             }
40              
41             sub outgoing_edges {
42             my ( $self, @plot_schemas ) = @_;
43              
44             my @outgoing;
45              
46             my %dupe_edge_detect;
47              
48             foreach my $s (@plot_schemas) {
49              
50             # first do the resource fields
51             foreach my $field ( $s->resource_field_names ) {
52             my ( $container, $type )
53             = $s->resource_field_type( $field,
54             { auto_upconvert_reference => 1, qualify_schema_types => 1 } );
55              
56             if ( $type =~ /^http/ ) {
57             my $complex_type = $self->client->schema($type);
58             my $complex_name = $complex_type->id();
59             my $arrowhead = ( !$container or $container eq 'reference' ) ? "normal" : "inv";
60             my $arrowtail = "none";
61              
62             my $from = $s->id;
63             my $to = $complex_name;
64              
65             my $edge_key = join '', sort ( $from, $to );
66              
67             my $edge = {
68             'from' => $from,
69             'to' => $to,
70             'arrowhead' => $arrowhead,
71             'arrowtail' => $arrowtail,
72             'via' => 'field'
73             };
74              
75             $dupe_edge_detect{$edge_key} ||= [];
76             push @{ $dupe_edge_detect{$edge_key} }, $edge;
77             }
78             }
79             my %resource_actions = %{ $s->f('resourceActions') };
80             while ( my ( $action, $action_data ) = each(%resource_actions) ) {
81             my $input_schema = $self->client->schema( $action_data->{input} || '' );
82             if ($input_schema) {
83             my $from = $input_schema->id;
84             my $to = $s->id;
85             my $arrowhead = 'dot';
86             my $arrowtail = 'none';
87              
88             my $edge_key = join '', sort ( $from, $to );
89             my $edge = {
90             'from' => $from,
91             'to' => $to,
92             'arrowhead' => $arrowhead,
93             'arrowtail' => $arrowtail,
94             'via' => 'action',
95             'style' => 'dotted',
96             'label' => $action
97             };
98             $dupe_edge_detect{$edge_key} ||= [];
99             push @{ $dupe_edge_detect{$edge_key} }, $edge;
100             }
101             my $output_schema = $self->client->schema( $action_data->{output} || '' );
102             if ($output_schema) {
103             my $from = $s->id;
104             my $to = $output_schema->id;
105             my $arrowhead = 'dot';
106             my $arrowtail = 'none';
107              
108             my $edge_key = join '', sort ( $from, $to );
109             my $edge = {
110             'from' => $from,
111             'to' => $to,
112             'arrowhead' => $arrowhead,
113             'arrowtail' => $arrowtail,
114             'via' => 'action',
115             'style' => 'dotted',
116             'label' => $action
117             };
118             $dupe_edge_detect{$edge_key} ||= [];
119             push @{ $dupe_edge_detect{$edge_key} }, $edge;
120             }
121             }
122             }
123              
124             while ( my ( $edge_key, $edge_dupe ) = each %dupe_edge_detect ) {
125             my $size = @$edge_dupe;
126             if ( $size == 1 ) {
127             push @outgoing, @$edge_dupe;
128             }
129             elsif ( $size == 2 ) {
130              
131             # make the edge be double arrowed to prevent too
132             # many edges from making a mess on the screen
133             my ( $a, $b ) = @$edge_dupe;
134             my $new_edge = {
135             'from' => $a->{from},
136             'to' => $a->{to},
137             'arrowhead' => $a->{arrowhead},
138             'arrowtail' => $b->{arrowhead}, # this ones head is the other ones tail
139             'via' => $a->{via},
140             };
141             push @outgoing, $new_edge;
142             }
143             else {
144             warn("unexpect edge duplication count: $size");
145             push @outgoing, @$edge_dupe;
146             }
147             }
148              
149             return @outgoing;
150             }
151              
152             sub do_graphviz {
153             my ( $self, @plot_schemas ) = @_;
154              
155             my $graph = GraphViz->new();
156             $graph->add_node( $_->id ) foreach @plot_schemas;
157             foreach my $edge ( outgoing_edges( $self, @plot_schemas ) ) {
158             $graph->add_edge(
159             $edge->{from} => $edge->{to},
160             arrowhead => $edge->{arrowhead},
161             arrowtail => $edge->{arrowtail},
162             style => $edge->{style} || 'solid',
163             fontsize => $edge->{via} eq 'action' ? 8 : 12,
164             label => $edge->{label} || ''
165             );
166             }
167             $self->page( $graph->as_dot );
168              
169             return 1;
170             }
171              
172             sub smry_graph {
173             return "generate schema relationship graph for Graphviz or OmniGraffle"
174             }
175              
176             sub help_graph {
177             return <<HELP
178             Output a relationship graph of all of the schemas so that you can visualize
179             their relationships to one another.
180              
181             This can be done for a single schema, non recursive; or for all schemas.
182              
183             Usage:
184             gdapi-shell --config=yourconfig.yml graph > graph.dot
185             graph
186             graph [schema]
187             HELP
188             }
189              
190             sub comp_graph {
191             my $self = shift;
192             return $self->schema_completion(@_);
193             }
194              
195             1;
196              
197             =head1 AUTHOR
198              
199             David Bartle, C<< <davidb@mediatemple.net> >>
200              
201             =head1 COPYRIGHT & LICENSE
202              
203             Copyright (c) 2014 Go Daddy Operating Company, LLC
204              
205             Permission is hereby granted, free of charge, to any person obtaining a
206             copy of this software and associated documentation files (the "Software"),
207             to deal in the Software without restriction, including without limitation
208             the rights to use, copy, modify, merge, publish, distribute, sublicense,
209             and/or sell copies of the Software, and to permit persons to whom the
210             Software is furnished to do so, subject to the following conditions:
211              
212             The above copyright notice and this permission notice shall be included in
213             all copies or substantial portions of the Software.
214              
215             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
216             IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
217             FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
218             THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
219             LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
220             FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
221             DEALINGS IN THE SOFTWARE.
222              
223             u=cut