Group
Extension

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.



Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.