AtteanX-Endpoint
view release on metacpan or search on metacpan
lib/AtteanX/Endpoint.pm view on Meta::CPAN
The L<Attean::API::IRI> of the graph in the model that represents the default graph.
=back
=head1 METHODS
=over 4
=cut
package AtteanX::Endpoint {
use Moo;
use Attean;
use Attean::RDF qw(iri);
use TryCatch;
use JSON;
use Encode;
use Plack::Request;
use Plack::Response;
use Scalar::Util qw(blessed refaddr);
use List::MoreUtils qw(any);
use File::ShareDir qw(dist_dir);
use HTTP::Negotiate qw(choose);
use IO::Compress::Gzip qw(gzip);
use HTML::HTML5::Writer qw(DOCTYPE_XHTML_RDFA);
use Carp qw(croak);
use Types::Standard qw(ConsumerOf CodeRef HashRef ArrayRef Str Int);
# use IO::Handle;
# use Digest::MD5 qw(md5_base64);
use XML::LibXML 1.70;
# use RDF::RDFa::Generator 0.102;
# use Hash::Merge::Simple qw/ merge /;
# use Fcntl qw(:flock SEEK_END);
use namespace::clean;
with 'MooX::Log::Any';
has 'planner' => (
is => 'ro',
isa => ConsumerOf['Attean::API::QueryPlanner'],
required => 1,
default => sub {
Attean::IDPQueryPlanner->new();
}
);
has 'model' => (is => 'ro', isa => ConsumerOf['Attean::API::Model'], required => 1);
has 'conf' => (is => 'ro', isa => HashRef, required => 1);
has 'graph' => (is => 'ro', isa => ConsumerOf['Attean::API::IRI'], required => 1);
sub BUILDARGS {
my $class = shift;
my @params = @_;
my %args;
if (blessed($params[0]) and $params[0]->does('Attean::API::Model')) {
# ->new( $model, \%conf )
$args{ model } = shift @params;
$args{ conf } = shift @params;
$args{ graph } = Attean::IRI->new('http://example.org/graph');
} elsif (any { blessed($_) && $_->does('Attean::API::Model') } @params) {
# Assume the buildargs can be taken directly
return $class->SUPER::BUILDARGS(@params);
} else {
# ->new( \%conf )
my $conf = shift @params;
my $store_conf = $conf->{store};
my ($name_, $file) = split(';', $store_conf, 2);
my ($name, @args) = split('=', $name_);
my $sclass = Attean->get_store($name);
my $store = $sclass->new(@args);
my $model = Attean::MutableQuadModel->new( store => $store );
my $graph = Attean::IRI->new('http://example.org/graph');
if (defined($file) and length($file)) {
$graph = Attean::IRI->new('file://' . File::Spec->rel2abs($file));
open(my $fh, '<:encoding(UTF-8)', $file) or die $!;
#$self->log->debug("Parsing data from $file...");
my $pclass = Attean->get_parser( filename => $file ) // 'AtteanX::Parser::Turtle';
my $parser = $pclass->new(base => $graph);
my $iter = $parser->parse_iter_from_io($fh);
my $quads = $iter->as_quads($graph);
$model->add_iter($quads);
}
$args{ model } = $model;
$args{ conf } = $conf;
$args{ graph } = $graph;
}
return $class->SUPER::BUILDARGS(%args);
}
=item C<< run ( $request ) >>
Run the SPARQL request contained in the given C<< $request >> object and return
a response object.
=cut
sub run {
my $self = shift;
my $req = shift;
try {
return $self->_run($req, @_);
}
catch (AtteanX::Endpoint::Error $e) {
my $resp = Plack::Response->new;
my $code = $e->code;
my $status = $e->message;
my $error = {
title => $status,
describedby => $e->uri,
};
if (my $d = $e->details) {
$error->{details} = $d;
}
my @variants = (
['text/plain', 0.98, 'text/plain'],
['application/json-problem', 0.99, 'application/json-problem'],
);
my $headers = $req->headers;
my $stype = choose( \@variants, $headers ) || 'text/plain';
if ($stype eq 'application/json-problem') {
$resp->headers->content_type( 'application/json-problem' );
$resp->status($code);
my $content = encode_json($error);
$resp->body($content);
} else {
$resp->headers->content_type( 'text/plain' );
$resp->status($code);
my @messages = grep { defined($_) } @{ $error }{ qw(title detail) };
my $content = join("\n\n", $status, @messages);
$resp->body($content);
}
return $resp;
}
}
sub _run {
my $self = shift;
my $req = shift;
my $config = $self->{conf};
my $endpoint_path = $config->{endpoint}{endpoint_path} || '/sparql';
my $gsp_path = $config->{endpoint}{gsp_path} || '/gsp';
my $response = Plack::Response->new;
our $VERSION;
my $server = "AtteanX::Endpoint/$VERSION";
( run in 2.570 seconds using v1.01-cache-2.11-cpan-97f6503c9c8 )