File Coverage

blib/lib/Kelp/Module/Whelk.pm
Criterion Covered Total %
statement 66 66 100.0
branch 17 24 70.8
condition 14 20 70.0
subroutine 12 12 100.0
pod 2 2 100.0
total 111 124 89.5


line stmt bran cond sub pod time code
1             package Kelp::Module::Whelk;
2             $Kelp::Module::Whelk::VERSION = '1.04';
3 20     20   4609772 use Kelp::Base 'Kelp::Module';
  20         52  
  20         178  
4 20     20   6284 use Kelp::Util;
  20         68  
  20         709  
5 20     20   156 use Carp;
  20         88  
  20         2128  
6 20     20   12204 use Whelk::Schema;
  20         81  
  20         745  
7 20     20   10875 use Whelk::ResourceMeta;
  20         70  
  20         146  
8              
9             attr config => undef;
10             attr inhale_response => !!1;
11             attr openapi_generator => undef;
12             attr resources => sub { {} };
13             attr endpoints => sub { [] };
14              
15             sub build
16             {
17 21     21 1 5510 my ($self, %args) = @_;
18 21         368 $self->_load_config(\%args);
19              
20             # register before loading, so that controllers have acces to whelk
21 21         312 $self->register(whelk => $self);
22             }
23              
24             sub finalize
25             {
26 21     21 1 463 my ($self) = @_;
27              
28 21         92 $self->_initialize_resources();
29 19         93 $self->_install_openapi();
30             }
31              
32             sub _load_package
33             {
34 58     58   1633 my ($self, $package, $base) = @_;
35              
36 58         235 my $class = Kelp::Util::camelize($package, $base, 1);
37 58         1410 return Kelp::Util::load_package($class);
38             }
39              
40             sub _load_config
41             {
42 21     21   56 my ($self, $args) = @_;
43 21         84 my $app = $self->app;
44              
45             # if this is Whelk or based on Whelk, use the main config
46 21 100       346 if ($app->isa('Whelk')) {
47             $args->{$_} //= $app->config($_)
48 19   66     167 for qw(
49             resources
50             openapi
51             wrapper
52             formatter
53             inhale_response
54             );
55             }
56              
57 21   100     3214 $args->{wrapper} //= 'Simple';
58 21   100     157 $args->{formatter} //= 'JSON';
59              
60             $self->inhale_response($args->{inhale_response})
61 21 50       181 if defined $args->{inhale_response};
62              
63 21         210 $self->config($args);
64             }
65              
66             sub _initialize_resources
67             {
68 21     21   79 my ($self) = @_;
69 21         77 my $app = $self->app;
70 21         161 my $args = $self->config;
71              
72 21   50     114 my %resources = %{$args->{resources} // {}};
  21         160  
73 21 50       90 carp 'No resources for Whelk, you should define some in config'
74             unless keys %resources;
75              
76             # sort to have deterministic order of endpoints
77 21         123 foreach my $resource (sort keys %resources) {
78 24         171 my $controller = $app->context->controller($resource);
79 24         28380 my $config = $resources{$resource};
80              
81 24 100       161 $config = {
82             path => $config
83             } unless ref $config eq 'HASH';
84              
85 24 50       145 croak "$resource does not extend " . $app->routes->base
86             unless $controller->isa($app->routes->base);
87              
88 24 50       475 croak "$resource does not implement Whelk::Role::Resource"
89             unless $controller->DOES('Whelk::Role::Resource');
90              
91             croak "Wrong path for $resource"
92 24 50       580 unless $config->{path} =~ m{^/};
93              
94             $self->resources->{ref $controller} = {
95             base_route => $config->{path},
96             wrapper => $self
97             ->_load_package(
98             $config->{wrapper} // $args->{wrapper},
99             'Whelk::Wrapper',
100             )
101             ->new,
102              
103             formatter => $self
104             ->_load_package(
105             $config->{formatter} // $args->{formatter},
106 24   66     277 'Whelk::Formatter',
      66        
107             )
108             ->new(app => $self->app),
109              
110             resource => Whelk::ResourceMeta
111             ->new(
112             class => $resource,
113             config => $config,
114             ),
115             };
116              
117 24 100       298 $controller->schemas
118             if $controller->can('schemas');
119 24         125 $controller->api;
120             }
121             }
122              
123             sub _install_openapi
124             {
125 19     19   70 my ($self) = @_;
126 19         93 my $app = $self->app;
127 19         156 my $args = $self->config;
128              
129 19         149 my $config = $args->{openapi};
130 19 100       176 return unless $config;
131              
132 5 100       27 $config = {
133             path => $config
134             } unless ref $config eq 'HASH';
135              
136 5   50     62 my $openapi_class = $self->_load_package($config->{class} // 'Whelk::OpenAPI');
137 5         84 $self->openapi_generator($openapi_class->new);
138              
139             $self->openapi_generator->parse(
140             app => $app,
141             info => $config->{info},
142             extra => $config->{extra},
143 5         75 endpoints => $self->endpoints,
144             schemas => Whelk::Schema->all_schemas,
145             );
146              
147 5 50       68 if (defined $config->{path}) {
148             croak "Wrong path for openapi"
149 5 50       27 unless $config->{path} =~ m{^/};
150              
151 5   66     57 my $formatter_class = $self->_load_package($config->{formatter} // $args->{formatter}, 'Whelk::Formatter');
152 5         76 my $formatter = $formatter_class->new(app => $self->app);
153              
154             $app->add_route(
155             [GET => $config->{path}] => {
156             to => sub {
157 4     4   30863 my ($controller) = @_;
158 4         14 my $app = $controller->context->app;
159              
160 4         126 my $generated = $self->openapi_generator->generate();
161              
162 4         39 return $formatter->format_response($app, $generated, 'openapi');
163             },
164 5         118 name => 'whelk_openapi',
165             }
166             );
167             }
168             }
169              
170             1;
171              
172             __END__
173              
174             =pod
175              
176             =head1 NAME
177              
178             Kelp::Module::Whelk - Whelk as a Kelp module
179              
180             =head1 SYNOPSIS
181              
182             # in Kelp configuration
183             {
184             modules => [qw(Whelk)],
185             modules_init => {
186             Whelk => {
187             resources => {
188             MyResource => '/',
189             },
190              
191             # rest of the normal Whelk configuration
192             ...
193             },
194             },
195             }
196              
197             =head1 DESCRIPTION
198              
199             Whelk module is a Kelp module which implements Whelk. If is used by default by
200             the standalone L<Whelk> module and can be used explicitly in a Kelp application
201             to add Whelk API to it.
202              
203             See L<Whelk::Manual::Kelp> for details on how to use Whelk in existing Kelp
204             applications. There is no need to interact with this module in standalone
205             applications.
206              
207             =head1 METHODS ADDED TO KELP
208              
209             This module just adds a single method to the Kelp application, C<whelk>, which
210             returns instance of this module.
211              
212             =head1 ATTRIBUTES
213              
214             These attributes are generated by the module as a result of looking at the
215             config. Most of those will not be available before L</finalize> is called.
216              
217             =head2 config
218              
219             Whelk-specific configuration as the module sees it.
220              
221             =head2 inhale_response
222              
223             Whether to inhale response before exhaling it.
224              
225             =head2 openapi_generator
226              
227             Instance of L<Whelk::OpenAPI> or other class used to generate the OpenAPI document.
228              
229             =head2 resources
230              
231             Metadata for Whelk resources in form of a hashref.
232              
233             =head2 endpoints
234              
235             An array reference of all endpoint instances created by C<add_endpoint> calls.
236              
237             =head1 METHODS
238              
239             =head2 finalize
240              
241             $self->whelk->finalize;
242              
243             Finalizes the API by calling all resources, installing their endpoints and
244             installing the OpenAPI endpoint. Must be called explicitly in application's
245             C<build> method.
246