Whelk/lib/Kelp/Module/Whelk.pm
package Kelp::Module::Whelk;
$Kelp::Module::Whelk::VERSION = '1.04';
use Kelp::Base 'Kelp::Module';
use Kelp::Util;
use Carp;
use Whelk::Schema;
use Whelk::ResourceMeta;
attr config => undef;
attr inhale_response => !!1;
attr openapi_generator => undef;
attr resources => sub { {} };
attr endpoints => sub { [] };
sub build
{
my ($self, %args) = @_;
$self->_load_config(\%args);
# register before loading, so that controllers have acces to whelk
$self->register(whelk => $self);
}
sub finalize
{
my ($self) = @_;
$self->_initialize_resources();
$self->_install_openapi();
}
sub _load_package
{
my ($self, $package, $base) = @_;
my $class = Kelp::Util::camelize($package, $base, 1);
return Kelp::Util::load_package($class);
}
sub _load_config
{
my ($self, $args) = @_;
my $app = $self->app;
# if this is Whelk or based on Whelk, use the main config
if ($app->isa('Whelk')) {
$args->{$_} //= $app->config($_)
for qw(
resources
openapi
wrapper
formatter
inhale_response
);
}
$args->{wrapper} //= 'Simple';
$args->{formatter} //= 'JSON';
$self->inhale_response($args->{inhale_response})
if defined $args->{inhale_response};
$self->config($args);
}
sub _initialize_resources
{
my ($self) = @_;
my $app = $self->app;
my $args = $self->config;
my %resources = %{$args->{resources} // {}};
carp 'No resources for Whelk, you should define some in config'
unless keys %resources;
# sort to have deterministic order of endpoints
foreach my $resource (sort keys %resources) {
my $controller = $app->context->controller($resource);
my $config = $resources{$resource};
$config = {
path => $config
} unless ref $config eq 'HASH';
croak "$resource does not extend " . $app->routes->base
unless $controller->isa($app->routes->base);
croak "$resource does not implement Whelk::Role::Resource"
unless $controller->DOES('Whelk::Role::Resource');
croak "Wrong path for $resource"
unless $config->{path} =~ m{^/};
$self->resources->{ref $controller} = {
base_route => $config->{path},
wrapper => $self
->_load_package(
$config->{wrapper} // $args->{wrapper},
'Whelk::Wrapper',
)
->new,
formatter => $self
->_load_package(
$config->{formatter} // $args->{formatter},
'Whelk::Formatter',
)
->new(app => $self->app),
resource => Whelk::ResourceMeta
->new(
class => $resource,
config => $config,
),
};
$controller->schemas
if $controller->can('schemas');
$controller->api;
}
}
sub _install_openapi
{
my ($self) = @_;
my $app = $self->app;
my $args = $self->config;
my $config = $args->{openapi};
return unless $config;
$config = {
path => $config
} unless ref $config eq 'HASH';
my $openapi_class = $self->_load_package($config->{class} // 'Whelk::OpenAPI');
$self->openapi_generator($openapi_class->new);
$self->openapi_generator->parse(
app => $app,
info => $config->{info},
extra => $config->{extra},
endpoints => $self->endpoints,
schemas => Whelk::Schema->all_schemas,
);
if (defined $config->{path}) {
croak "Wrong path for openapi"
unless $config->{path} =~ m{^/};
my $formatter_class = $self->_load_package($config->{formatter} // $args->{formatter}, 'Whelk::Formatter');
my $formatter = $formatter_class->new(app => $self->app);
$app->add_route(
[GET => $config->{path}] => {
to => sub {
my ($controller) = @_;
my $app = $controller->context->app;
my $generated = $self->openapi_generator->generate();
return $formatter->format_response($app, $generated, 'openapi');
},
name => 'whelk_openapi',
}
);
}
}
1;
__END__
=pod
=head1 NAME
Kelp::Module::Whelk - Whelk as a Kelp module
=head1 SYNOPSIS
# in Kelp configuration
{
modules => [qw(Whelk)],
modules_init => {
Whelk => {
resources => {
MyResource => '/',
},
# rest of the normal Whelk configuration
...
},
},
}
=head1 DESCRIPTION
Whelk module is a Kelp module which implements Whelk. If is used by default by
the standalone L<Whelk> module and can be used explicitly in a Kelp application
to add Whelk API to it.
See L<Whelk::Manual::Kelp> for details on how to use Whelk in existing Kelp
applications. There is no need to interact with this module in standalone
applications.
=head1 METHODS ADDED TO KELP
This module just adds a single method to the Kelp application, C<whelk>, which
returns instance of this module.
=head1 ATTRIBUTES
These attributes are generated by the module as a result of looking at the
config. Most of those will not be available before L</finalize> is called.
=head2 config
Whelk-specific configuration as the module sees it.
=head2 inhale_response
Whether to inhale response before exhaling it.
=head2 openapi_generator
Instance of L<Whelk::OpenAPI> or other class used to generate the OpenAPI document.
=head2 resources
Metadata for Whelk resources in form of a hashref.
=head2 endpoints
An array reference of all endpoint instances created by C<add_endpoint> calls.
=head1 METHODS
=head2 finalize
$self->whelk->finalize;
Finalizes the API by calling all resources, installing their endpoints and
installing the OpenAPI endpoint. Must be called explicitly in application's
C<build> method.