File Coverage

blib/lib/Clustericious/Plugin/CommonRoutes.pm
Criterion Covered Total %
statement 76 95 80.0
branch 20 28 71.4
condition 10 17 58.8
subroutine 16 18 88.8
pod 1 1 100.0
total 123 159 77.3


line stmt bran cond sub pod time code
1             package Clustericious::Plugin::CommonRoutes;
2              
3 26     26   433814 use strict;
  26         81  
  26         774  
4 26     26   144 use warnings;
  26         53  
  26         624  
5 26     26   515 use 5.010;
  26         133  
6 26     26   157 use Mojo::Base 'Mojolicious::Plugin';
  26         65  
  26         179  
7 26     26   5738 use File::Basename ();
  26         55  
  26         362  
8 26     26   116 use Sys::Hostname ();
  26         1879  
  26         564  
9 26     26   134 use List::Util qw/ uniq /;
  26         50  
  26         33091  
10              
11             # ABSTRACT: Routes common to all clustericious applications
12             our $VERSION = '1.27'; # VERSION
13              
14              
15             sub register
16             {
17 32     32 1 1728 my($self, $app) = @_;
18              
19             $app->plugin('Clustericious::Plugin::AutodataHandler')
20 32 50       120 unless $app->renderer->handlers->{autodata};
21              
22              
23 32         595 my $version = do {
24 32         94 my $class = ref $app;
25 32 100 50     124 $class = eval { $app->renderer->classes->[0] } // 'main' if $class eq 'Mojolicious::Lite';
  1         8  
26 32   100     575 $class->VERSION // 'dev';
27             };
28              
29             $app->routes->get('/version')->to(cb => sub {
30 4     4   26243 shift->stash(autodata => [ $version ]);
31 32         158 });
32              
33              
34 32         11159 my($app_name, $hostname) = do {
35 32         91 my $name = ref $app;
36 32 100       163 $name = File::Basename::basename($0) if $name eq 'Mojolicious::Lite';
37 32         174 ($name, Sys::Hostname::hostname());
38             };
39              
40             $app->routes->get('/status')->to(cb => sub {
41 8     8   25738 my($self) = @_;
42 8         56 $self->stash(autodata => {
43             app_name => $app_name,
44             server_version => $version,
45             server_hostname => $hostname,
46             server_url => $self->url_for('/')->to_abs->to_string,
47             });
48 32         256 });
49              
50              
51             $app->routes->get('/api')->to(cb => sub {
52 3     3   15560 shift->render( autodata => [ __PACKAGE__->_dump_api($app) ]);
53 32         6970 });
54              
55              
56             $app->routes->get('/api/:table')->to(cb => sub {
57 1     1   100 my($self) = @_;
58 1         7 my $table = __PACKAGE__->_dump_api_table($self->stash('table'));
59 1 50       23 $table ? $self->render( autodata => $table ) : $self->reply->not_found;
60 32         6165 });
61            
62              
63             $app->routes->get('/log/:lines' => [ lines => qr/\d+/ ] => sub {
64 1     1   60 my($self) = @_;
65 1         5 my $lines = $self->stash("lines");
66 1 50       25 unless ($self->config->export_logs(default => 0))
67             {
68 1         5 return $self->render(text => 'logs not available');
69             }
70 0   0     0 $self->render(text => Clustericious::Log->tail(lines => $lines || 10) || '** empty log **');
71 32 100       7497 }) if $app->isa('Clustericious::App');
72              
73             $app->routes->options('/*opturl' => { opturl => '' } => sub {
74 0     0   0 my($self) = @_;
75 0         0 $self->res->headers->add( 'Access-Control-Allow-Methods' => 'GET, POST, OPTIONS' );
76             # Allow-Origin and Allow-Headers added in after_dispatch hook.
77 0         0 $self->render(text => 'ok');
78 32         6969 });
79             }
80              
81             sub _have_rose
82             {
83 55 100   55   494 Rose::Planter->can('tables') ? 1 : 0;
84             }
85              
86             sub _dump_api {
87 6     6   39 my $class = shift;
88 6         13 my $app = shift;
89 6   66     37 my $routes = shift || $app->routes->children;
90 6         39 my @all;
91              
92 6         20 for my $r (@$routes)
93             {
94 28         208 my $pat = $r->pattern;
95 28         197 $pat->_compile;
96 28         2019 my %placeholders = map { $_ => "<$_>" } @{ $pat->placeholders };
  17         118  
  28         80  
97 28 100       190 my $method = uc join ',', @{ $r->via || ["GET"] };
  28         94  
98 28 100 100     272 if (_have_rose() && $placeholders{table})
    100 66        
    100          
99             {
100 2         12 for my $table (Rose::Planter->tables)
101             {
102 6         16 $placeholders{table} = $table;
103 6         22 my $pat = $pat->unparsed;
104 6         55 $pat =~ s/:table/$table/;
105 6         30 push @all, "$method $pat";
106             }
107             }
108             elsif (_have_rose() && $placeholders{items})
109             {
110 1         5 for my $plural (Rose::Planter->plurals)
111             {
112 3         6 $placeholders{items} = $plural;
113 3         7 my $line = $pat->render(\%placeholders);
114 3         98 push @all, "$method $line";
115             }
116             }
117             elsif (defined($pat->unparsed))
118             {
119 22         181 push @all, join ' ', $method, $pat->unparsed;
120             }
121             else
122             {
123 3         23 push @all, $class->_dump_api($app, $r->children);
124             }
125             }
126 6         109 return uniq sort @all;
127             }
128              
129             sub _dump_api_table_types
130             {
131 0     0   0 my(undef, $rose_type) = @_;
132 0 0       0 return 'datetime' if $rose_type =~ /^datetime/;
133             state $types = {
134 0         0 (map { $_ => 'string' } qw( character text varchar )),
135 0         0 (map { $_ => 'numeric' } 'numeric', 'float', 'double precision','decimal'),
136 0         0 (map { $_ => $_ } qw( blob set time interval enum bytea chkpass bitfield date boolean )),
137 0         0 (map { $_ => 'integer' } qw( bigint integer bigserial serial )),
138 0         0 (map { $_ => 'epoch' } 'epoch', 'epoch hires'),
139 0         0 (map { $_ => 'timestamp' } 'timestamp', 'timestamp with time zone'),
  0         0  
140             };
141 0   0     0 return $types->{$rose_type} // 'unknown';
142             }
143              
144             sub _dump_api_table
145             {
146 1     1   39 my(undef, $table) = @_;
147 1 50       5 return unless _have_rose();
148 0           my $class = Rose::Planter->find_class($table);
149 0 0         return unless defined $class;
150              
151             return {
152             columns => {
153             map {
154 0           $_->name => {
155             rose_db_type => $_->type,
156             not_null => $_->not_null,
157             type => __PACKAGE__->_dump_api_table_types($_->type),
158             } } $class->meta->columns
159             },
160             primary_key => [
161 0           map { $_->name } $class->meta->primary_key_columns
  0            
162             ],
163             };
164             }
165              
166             1;
167              
168             __END__
169              
170             =pod
171              
172             =encoding UTF-8
173              
174             =head1 NAME
175              
176             Clustericious::Plugin::CommonRoutes - Routes common to all clustericious applications
177              
178             =head1 VERSION
179              
180             version 1.27
181              
182             =head1 SYNOPSIS
183              
184             # Mojolicious
185             $self->plugin('Clustericious::Plugin::CommonRoutes');
186            
187             # Clustericious
188             # ... included by default ...
189              
190             =head1 DESCRIPTION
191              
192             This plugin adds routes that are common to all clustericious servers.
193             It is available to Vanilla L<Mojolicious> apps that want to work with
194             L<Clustericious::Client> based clients.
195              
196             =head2 /version
197              
198             Returns the version of the service as a single element list.
199              
200             =head2 /status
201              
202             Returns status information about the service. This comes back
203             as a hash that includes these key/value pairs:
204              
205             =over 4
206              
207             =item app_name
208              
209             The name of the application (example: "MyApp")
210              
211             =item server_hostname
212              
213             The server on which the service is running.
214              
215             =item server_url
216              
217             The URL to use for the service.
218              
219             =item server_version
220              
221             The version of the application.
222              
223             =back
224              
225             =head2 /api
226              
227             Returns a list of API routes for the service. This is similar to the information
228             provided by the L<Mojolicious::Command::routes|routes command>.
229              
230             =head2 /api/:table
231              
232             If you are using L<Module::Build::Database> and L<Route::Planter> for a database
233             back end to your application you can get the columns of each table using this route.
234              
235             =head2 /log/:lines
236              
237             Return the last several lines from the application log (number specified by :lines
238             and defaults to 10 if not specified).
239              
240             Only available if you set export_logs to true in your application's server configuration.
241              
242             example C<~/etc/MyApp.conf>:
243              
244             ---
245             export_logs: 1
246              
247             This route is NOT made available to non L<Clustericious> applications.
248              
249             =head1 CAVEATS
250              
251             This plugin pulls in the L<Clustericious::Plugin::AutodataHandler> plugin if
252             it hasn't already been loaded.
253              
254             =head1 SEE ALSO
255              
256             L<Clustericious>, L<Clustericious::RouteBuilder>
257              
258             =head1 AUTHOR
259              
260             Original author: Brian Duggan
261              
262             Current maintainer: Graham Ollis E<lt>plicease@cpan.orgE<gt>
263              
264             Contributors:
265              
266             Curt Tilmes
267              
268             Yanick Champoux
269              
270             =head1 COPYRIGHT AND LICENSE
271              
272             This software is copyright (c) 2013 by NASA GSFC.
273              
274             This is free software; you can redistribute it and/or modify it under
275             the same terms as the Perl 5 programming language system itself.
276              
277             =cut