AtteanX-Endpoint

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

AtteanX::Endpoint
=================

Created:      2016-03-25
Home page:    <http://search.cpan.org/dist/AtteanX::Endpoint/>
Bug tracker:  <https://github.com/kasei/atteanx-endpoint/issues>
Maintainer:   Gregory Todd Williams <mailto:gwilliams@cpan.org>

0.003  2026-05-21

 - (Update) Add implementation of SPARQL Graph Store Protocol.
 - (Update) Miscellaneous bug-fixes.

0.001  2016-03-25

 - (Addition) Initial release.

LICENSE  view on Meta::CPAN

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.



The CodeMirror sources included in share/endpoint/www/ are
Copyright (C) 2012 by Marijn Haverbeke <marijnh@gmail.com>
and are also licensed under the MIT License. More information may be found at
<https://github.com/codemirror/CodeMirror>.

MANIFEST  view on Meta::CPAN

inc/Module/Install/Share.pm
inc/Module/Install/Win32.pm
inc/Module/Install/WriteAll.pm
lib/AtteanX/Endpoint.pm
LICENSE
Makefile.PL
MANIFEST			This list of files
META.yml
meta/changes.ttl
README.md
scripts/endpoint.psgi
share/endpoint/index.html
share/endpoint/results.html
share/endpoint/www/css/docs.css
share/endpoint/www/css/sparqlcolors.css
share/endpoint/www/favicon.ico
share/endpoint/www/js/codemirror.js
share/endpoint/www/js/editor.js
share/endpoint/www/js/highlight.js
share/endpoint/www/js/mirrorframe.js
share/endpoint/www/js/parsecss.js
share/endpoint/www/js/parsedummy.js
share/endpoint/www/js/parsehtmlmixed.js
share/endpoint/www/js/parsejavascript.js
share/endpoint/www/js/parsesparql.js
share/endpoint/www/js/parsexml.js
share/endpoint/www/js/select.js
share/endpoint/www/js/sparql_form.js
share/endpoint/www/js/stringstream.js
share/endpoint/www/js/tokenize.js
share/endpoint/www/js/tokenizejavascript.js
share/endpoint/www/js/undo.js
share/endpoint/www/js/unittests.js
share/endpoint/www/js/util.js
t/00.load.t
t/gsp-custom-endpoint.t
t/gsp-read-only.t
t/gsp.t
t/simple.t
xt/pod-coverage.t
xt/pod.t
SIGNATURE                                Public-key signature (added by MakeMaker)

META.yml  view on Meta::CPAN

  Moo: 1.006
  MooX::Log::Any: 0
  Plack: 0
  TryCatch: 0
  Types::Standard: 0
  XML::LibXML: 1.7
  namespace::clean: 0
  perl: 5.14.0
resources:
  IRC: irc://irc.perl.org/#perlrdf
  bugtracker: https://github.com/kasei/atteanx-endpoint/issues
  homepage: https://metacpan.org/release/AtteanX-Endpoint/
  license: http://dev.perl.org/licenses/
  repository: https://github.com/kasei/atteanx-endpoint/
version: '0.003'

Makefile.PL  view on Meta::CPAN

requires		'Moo'							=> 1.006000;
requires		'MooX::Log::Any'				=> 0;
requires		'namespace::clean'				=> 0;
requires		'Plack'							=> 0;
requires		'TryCatch'						=> 0;
requires		'Types::Standard'				=> 0;
requires		'Config::JFDI'					=> 0;

resources(
	'homepage'   => "https://metacpan.org/release/AtteanX-Endpoint/",
	'repository' => "https://github.com/kasei/atteanx-endpoint/",
	'bugtracker' => "https://github.com/kasei/atteanx-endpoint/issues",
	'IRC'        => "irc://irc.perl.org/#perlrdf",
);

author_tests('xt');

install_share;
install_script 'scripts/endpoint.psgi';

write_doap_changes "meta/changes.ttl", "Changes", "turtle";

# On MacOS, prefer homebrew-installed GNU tar so that we don't include Mac-specific
# extended header data (which results in "Ignoring unknown extended header keyword"
# messages on non-Mac systems when extracting data.
if (-x '/opt/homebrew/bin/gtar') {
	makemaker_args(
		dist => {
			TAR => '/opt/homebrew/bin/gtar'

SIGNATURE  view on Meta::CPAN

SHA256 668306ae2fad17b3049f885251b8679497c4eb8d5c4b0d13f5c95bda331d1f00 inc/Module/Install/DOAPChangeSets.pm
SHA256 65d7a6098bf3f829e8c1c2865476d3537aa6f0ad0ffc9149e10812c856529043 inc/Module/Install/Fetch.pm
SHA256 70c4b77acab3ff51dfb318110369607cb109e1c319459249623b787cf3859750 inc/Module/Install/Makefile.pm
SHA256 14556386168007ce913e669fc08a332ccdb6140246fd55a90c879b5190c1b57a inc/Module/Install/Metadata.pm
SHA256 63ec8405523ae67c33823dcf4b136a46f0711fb17a8b58b49ddd922ed6b69611 inc/Module/Install/Scripts.pm
SHA256 b986592700675e11c2c62cb41fa24d9f4d15e077a0d317f372efcaa78d68a1d9 inc/Module/Install/Share.pm
SHA256 4c746c02c5cc19bed4c352e76205b4adff4c45ce8310d71294e1b83c059659c2 inc/Module/Install/Win32.pm
SHA256 d3d9b4583243c470ae895defa4c44564485b53693cba1c50ab0320768f443e97 inc/Module/Install/WriteAll.pm
SHA256 ffee183c74ae6862b141b7eb608453253e8cb40c62dbcb27e983f1af72cf675f lib/AtteanX/Endpoint.pm
SHA256 36d8bfb97bffbc267ca3cd5eacdc2e0d40b6a9a46b4b843c875b26e8e5c809e8 meta/changes.ttl
SHA256 de96338e5357d3c5df86e363565d68816bbd6ba0d1aee138e7dc16b149aa084c scripts/endpoint.psgi
SHA256 abfb9b04bcdef9a10cf6543471fdbad1fdef193942b67b8e2d5780e64d344cb6 share/endpoint/index.html
SHA256 e69caef172ec6314b1b4b69816263d699e6a05e25295afb1b0f02441425341ce share/endpoint/results.html
SHA256 148eabf663f91b87a7ebc86009ba599dbef41b5bbc01f1ad545942ecae8ef61f share/endpoint/www/css/docs.css
SHA256 91d7fc5249933db30dd0657c9a93d875488b9767ec18af8e0b03c7a361dea926 share/endpoint/www/css/sparqlcolors.css
SHA256 dfba29d03ee7b7a2a51ebb5500fb034ddfb35b3fe44a3619e1a96a6389de19fc share/endpoint/www/favicon.ico
SHA256 e5bb9e9ece76ff87ee59273ae4349042bea3f937a7441d8fda7fc5d3a3d197b9 share/endpoint/www/js/codemirror.js
SHA256 7d4f32f5ff170e4727093c549c5c483a8b9e058f0387053865f80b181ea730ad share/endpoint/www/js/editor.js
SHA256 ad0655918fc9b59693a4374930a0fc5752fcaf46bc06157e2a303cff5f20edad share/endpoint/www/js/highlight.js
SHA256 f2a94d9ec5b53ad58f58dc02e78717b9ce27e9e23fb4d01b169bb64a26f70d1b share/endpoint/www/js/mirrorframe.js
SHA256 90b5d652ec5ef41e1fd693cd7e0d1c56f6134b44159b1cf3d4353ed46bb2860d share/endpoint/www/js/parsecss.js
SHA256 085ed3937d56e8d7721d026277fb3a97da4873aecd2e6b719a287496775b06a9 share/endpoint/www/js/parsedummy.js
SHA256 23e3804668969fb98b989a7982b9fe00a9446102a2b5a12e95f3d3e5ce1d92bc share/endpoint/www/js/parsehtmlmixed.js
SHA256 212506b040c885b825031e0e994fcbb9f2a5ac268ccfc4aa144647dbbccc5ed5 share/endpoint/www/js/parsejavascript.js
SHA256 8f1008011c217d799b225c0d906a740b5034d87679dadf46212576de3199f6c7 share/endpoint/www/js/parsesparql.js
SHA256 1f1b0bfab402f57c19cecb652ca68b05e0895a1bd6830afab8b277a9a2e0281e share/endpoint/www/js/parsexml.js
SHA256 165cb522044a7d5bffaa3dc489f841e5e5504f8ac01dfe87160f74c29e9e010a share/endpoint/www/js/select.js
SHA256 fadbc674c5af65f5725e0854d95eee6753295bb51c2225d6361f00a5593ae638 share/endpoint/www/js/sparql_form.js
SHA256 7c9286894772caedca1af04c44eadb290492ccbc1fcf6cea92cbfae189ab0571 share/endpoint/www/js/stringstream.js
SHA256 5860fb4ad9f72176bd206e64434b82c4c16eb7fe8c420896d0c8412bcc2a6669 share/endpoint/www/js/tokenize.js
SHA256 c45f8ffc9182d1ed35ebb748c23d0fb9e792c7857b464f9c53ddcc04eb67890f share/endpoint/www/js/tokenizejavascript.js
SHA256 89e8b157fc38f9f8a0915b427ea76c4c5ffb1e0a5ed89bf0cf72c46f039d304d share/endpoint/www/js/undo.js
SHA256 0fe5b25dfe5f752c073a4c85c068188dfa9cbfc8c3a28f47257a644c6616c07b share/endpoint/www/js/unittests.js
SHA256 228bfbcab8da6ae5f2509c4070c38cd046fb567452318f4987fdfe7a1171d01b share/endpoint/www/js/util.js
SHA256 cc74b5c5b2528279562fad5e9b9202f4b98c97f34ce46ea550c0a7a4fbd49261 t/00.load.t
SHA256 7b79c513cfc7ca7a9f235aaadc3c4b2804406195be7a3224d6d464f9e3355467 t/gsp-custom-endpoint.t
SHA256 d3abdca00ff72c165180929878b8bd396103652b101f15a3e97edbaf50d8d526 t/gsp-read-only.t
SHA256 958d138fceaf60d42ce1457dac56536ccb8571ba69e28b72135534e630ba1e27 t/gsp.t
SHA256 239a7ac72217994e513340d67f96ed6e457d438a42ae7dd85cfa5783cec7002b t/simple.t
SHA256 81dae5e652e694c2acc7d105e32ba5f69edc343b22b2e1d47f95fdef8b441cd3 xt/pod-coverage.t
SHA256 2b04b20ff767801fde2fb6435361dc8ca0a9b60dda6cb0bbd18dd52962e1c1a3 xt/pod.t
-----BEGIN PGP SIGNATURE-----

iF0EAREDAB0WIQSC+8GhU5AgVmDLxA+E8rpUyqjILQUCag8a+QAKCRCE8rpUyqjI
Lde8AKCn5LE7QbZyJ1C5S5XNK/I/JqBBvACdFaCBz9V0XlkwMkNtQxCihT07Ego=
=e1eh

lib/AtteanX/Endpoint.pm  view on Meta::CPAN

	
	sub configure {
		my $self	= shift;
		$self->{config}	= shift;
		return $self;
	}
	
	sub prepare_app {
		my $self = shift;
		my $config = $self->{config};
		$self->{endpoint} = eval { AtteanX::Endpoint->new( $config ) };
		if ($@) {
			warn $@;
		}
	}

	sub call {
		my($self, $env) = @_;
		my $req	= Plack::Request->new($env);
		unless ($req->method =~ /^(GET|HEAD|POST)$/) {
			return [ 405, [ 'Content-type', 'text/plain' ], [ 'Method not allowed' ] ];
		}

		my $ep		= $self->{endpoint};
		my $resp	= $ep->run( $req );
		return $resp->finalize;
	}
}

=head1 NAME

AtteanX::Endpoint - SPARQL 1.1 Protocol Endpoint

=head1 VERSION

This document describes AtteanX::Endpoint version 0.003

=head1 SYNOPSIS

  plackup -p 9091 scripts/endpoint.psgi

=head1 DESCRIPTION

The AtteanX::Endpoint class implements a PSGI SPARQL Protocol endpoint.

=head1 ATTRIBUTES

=over 4

=item C<< planner >>

=item C<< model >>

=item C<< conf >>

A hash reference containing configuration data for the endpoint. For example:

  {
    endpoint  => {
      service_description => {
        named_graphs => 1,
        default => 1,
      },
      load_data => 0,
      update => 0,
    }
  }

=item C<< graph >>

lib/AtteanX/Endpoint.pm  view on Meta::CPAN

			}
			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";
		$server .= " " . $response->headers->header('Server') if defined($response->headers->header('Server'));
		$response->headers->header('Server' => $server);

		if ($req->path eq $endpoint_path or $req->path eq $gsp_path) {
			# no-op
		} else {
			my $content;
			my $path	= $req->path_info;
			$path		=~ s#^/##;
			my $dir		= $ENV{ATTEAN_ENDPOINT_SHAREDIR} || File::Spec->catdir((eval { dist_dir('AtteanX-Endpoint') } || 'share'), 'endpoint');
			my $abs		= File::Spec->rel2abs($dir);
			my $file	= File::Spec->catfile($abs, 'www', $path);
			if (-r $file) {
				open( my $fh, '<', $file ) or croak $!;
				$response->status(200);
				$content	= $fh;
			} else {
				my $path	= $req->path;
				$response->status(404);
				$content	= <<"END";
	<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">\n<html><head>\n<title>404 Not Found</title>\n</head><body>\n
	<h1>Not Found</h1>\n<p>The requested URL $path was not found on this server.</p>\n</body></html>
END
			}
			$response->body($content);
			return $response;
		}
	
		if ($req->path eq $endpoint_path) {
			$self->_run_protocol($req, $response);
		} else {
			$self->_run_gsp($req, $response);
		}
	}

	sub get_gsp_graph {
		my $self	= shift;
		my $req		= shift;
		my $query     = $req->parameters;

lib/AtteanX/Endpoint.pm  view on Meta::CPAN

		if (my $t = $req->param('media-type')) {
			$type	= $t;
			$headers->header('Accept' => $type);
		}

		my $ae		= $req->headers->header('Accept-Encoding') || '';
		my $graph	= $self->get_gsp_graph($req); # TODO
		unless ($graph) {
			die AtteanX::Endpoint::ClientError->new(
				code => 400,
				message => 'No graph specified', uri => 'http://id.kasei.us/rdf-endpoint/error/gsp_no_graph'
			);
		}
		my $base	= $req->base;
		my $parser	= Attean->get_parser('SPARQL')->new(base => $base);
		my $algebra;
		my $content;
		if ($req->method eq 'GET' or $req->method eq 'HEAD') {
			my $sparql		= 'CONSTRUCT WHERE { ?s ?p ?o }';
			($algebra)	= eval { $parser->parse($sparql, base => $base) };
			$self->log_gsp_request( $req );
			if ($@ or not($algebra)) {
				my $error	= $@ || 'Internal error';
				$self->log_error( $req, $error );
				my $eclass	= ($error =~ /Syntax/) ? 'AtteanX::Endpoint::ClientError' : 'AtteanX::Endpoint::ServerError';
				die $eclass->new(message => 'SPARQL GSP parse error', uri => 'http://id.kasei.us/rdf-endpoint/error/parse_error', details => { error => $error });
			}
		} else {
			unless ($config->{endpoint}{update}) {
				die AtteanX::Endpoint::ClientError->new(code => 405, message => 'Method not allowed', uri => 'http://id.kasei.us/rdf-endpoint/error/bad_http_method');
			}
			if ($req->method eq 'PUT' or $req->method eq 'POST') {
				my $ct		= $req->headers->header('Content-Type');
				unless ($ct) {
					die AtteanX::Endpoint::ClientError->new(
						code => 400,
						message => 'No Content-Type specified for PUT request', uri => 'http://id.kasei.us/rdf-endpoint/error/gsp_no_media_type'
					);
				}
				my $pclass	= Attean->get_parser(media_type => $ct);
				unless ($pclass) {
					die AtteanX::Endpoint::ClientError->new(
						code => 400,
						message => "No parser available for media type $ct", uri => 'http://id.kasei.us/rdf-endpoint/error/gsp_unrecognized_media_type'
					);
				}
				my $parser	= $pclass->new();
				my $iter = $parser->parse_iter_from_io($req->body);
				
				my @ops;
				if ($req->method eq 'PUT') {
					my $drop	= Attean::Algebra::Clear->new(drop => 1, silent => 1, target => 'GRAPH', graph => $graph);
					push(@ops, $drop);
				}
				my $insert	= Attean::Algebra::Modify->new(insert => [$iter->as_quads($graph)->elements]);
				push(@ops, $insert);
				my $seq		= Attean::Algebra::Sequence->new(children => \@ops);
	# 			warn $seq->as_string;
				$algebra	= $seq;
			} elsif ($req->method eq 'DELETE') {
				$algebra	= Attean::Algebra::Clear->new(drop => 1, silent => 1, target => 'GRAPH', graph => $graph);
			} else {
				die AtteanX::Endpoint::ClientError->new(
					code => 400,
					message => "Unsupported HTTP method: " . $req->method, uri => 'http://id.kasei.us/rdf-endpoint/error/gsp_unrecognized_http_method'
				);
			}
		}

		if ($self->log->is_trace) {
			$self->log->trace("Algebra:\n" . $algebra->as_string);
		}
		my $default_graphs	= [$graph];
		my $planner	= $self->planner;
		if ($self->log->is_trace) {

lib/AtteanX/Endpoint.pm  view on Meta::CPAN

				}
				$response->headers->content_type($stype);
			} else {
				$response->headers->content_type('text/plain');
				$content	= "OK\n";
			}
		};
		if ($@) {
			my $error	= $@;
			$self->log->fatal($error);
			die AtteanX::Endpoint::ServerError->new(code => 500, message => 'SPARQL GSP execution error', uri => 'http://id.kasei.us/rdf-endpoint/error/execution_error', details => { error => $@ });
		}
	
		$self->wrap_response_with_encoding($req, $response, $ae, $content);
		return $response;
	}
	
	sub _run_protocol {
		my $self	= shift;
		my $req		= shift;
		my $response	= shift;

lib/AtteanX/Endpoint.pm  view on Meta::CPAN

		my $ae		= $req->headers->header('Accept-Encoding') || '';
	
		my $sparql;
		my $content;
		my $ct	= $req->header('Content-type');
		if ($req->method !~ /^(GET|POST)$/i) {
			my $method	= uc($req->method);
			$content	= "Unexpected method $method (expecting GET or POST)";
			$self->log_error( $req, $content );
			$response->header('Allow' => 'GET, POST');
			die AtteanX::Endpoint::ClientError->new(code => 405, message => 'Method not allowed', uri => 'http://id.kasei.us/rdf-endpoint/error/bad_http_method');
		} elsif (defined($ct) and $ct eq 'application/sparql-query') {
			$sparql	= $req->content;
		} elsif (defined($ct) and $ct eq 'application/sparql-update') {
			if ($config->{endpoint}{update} and $req->method eq 'POST') {
				$sparql	= $req->content;
			}
		} elsif ($req->param('query')) {
			my @sparql	= $req->param('query');
			if (scalar(@sparql) > 1) {
				$content	= "More than one query string submitted";
				$self->log_error( $req, $content );
				die AtteanX::Endpoint::ClientError->new(code => 400, message => 'Multiple query strings not allowed', uri => 'http://id.kasei.us/rdf-endpoint/error/multiple_queries');
			} else {
				$sparql = $sparql[0];
			}
		} elsif ($req->param('update')) {
			my @sparql	= $req->param('update');
			if (scalar(@sparql) > 1) {
				$content	= "More than one update string submitted";
				$self->log_error( $req, $content );
				die AtteanX::Endpoint::ClientError->new(code => 400, message => 'Multiple update strings not allowed', uri => 'http://id.kasei.us/rdf-endpoint/error/multiple_updates');
			}
		
			if ($config->{endpoint}{update} and $req->method eq 'POST') {
				$sparql = $sparql[0];
			} elsif ($req->method ne 'POST') {
				my $method	= $req->method;
				$content	= "Update operations must use POST";
				$self->log_error( $req, $content );
				$response->header('Allow' => 'POST');
				die AtteanX::Endpoint::ClientError->new(code => 405, message => "$method Not Allowed for Update Operation", uri => 'http://id.kasei.us/rdf-endpoint/error/bad_http_method_update');
			}
		}
	
		if ($sparql) {
			my %args;
			$args{ update }		= 1 if ($config->{endpoint}{update} and $req->method eq 'POST');
			$args{ load_data }	= 1 if ($config->{endpoint}{load_data});
		
			my $protocol_specifies_update_dataset	= 0;
			{
				my @default	= $req->param('default-graph-uri');
				my @named	= $req->param('named-graph-uri');
				if (scalar(@default) or scalar(@named)) {
					delete $args{ load_data };
					# TODO: handle custom-dataset
					$self->log->warn('custom query datasets not supported yet');
# 					$model	= Attean::MutableQuadModel->new( store => Attean->get_store('Memory')->new() );

lib/AtteanX/Endpoint.pm  view on Meta::CPAN

			my $base	= $req->base;
			my $parser	= Attean->get_parser('SPARQL')->new(base => $base);
			$parser->update(1) if ($args{update});
			my ($algebra)	= eval { $args{update} ? $parser->parse_update($sparql, base => $base) : $parser->parse($sparql, base => $base) };
			if ($@ or not($algebra)) {
				my $error	= $@ || 'Internal error';
				$self->log_error( $req, $error );
				my $eclass	= ($error =~ /Syntax/) ? 'AtteanX::Endpoint::ClientError' : 'AtteanX::Endpoint::ServerError';
				if ($req->method ne 'POST' and $error =~ /read-only queries/sm) {
					$error	= 'Updates must use a HTTP POST request.';
					die $eclass->new(message => 'Updates must use a HTTP POST request', uri => 'http://id.kasei.us/rdf-endpoint/error/bad_http_method_update');
				} else {
					die $eclass->new(message => 'SPARQL query/update parse error', uri => 'http://id.kasei.us/rdf-endpoint/error/parse_error', details => { error => $error, sparql => $sparql });
				}
			} else {
				$self->log_query( $req, $sparql );
				# TODO: handle case where query specifies update dataset
# 				if ($protocol_specifies_update_dataset and $query->specifies_update_dataset) {
# 					my $method	= $req->method;
# 					$content	= "Update operations cannot specify a dataset in both the query and with protocol parameters";
# 					$self->log_error( $req, $content );
# 					die AtteanX::Endpoint::ClientError->new(code => 400, message => 'Multiple datasets specified for update', uri => 'http://id.kasei.us/rdf-endpoint/error/update_specifies_multiple_datasets');
# 				}
				if ($self->log->is_trace) {
					$self->log->trace("Algebra:\n" . $algebra->as_string);
				}
				my $graph	= $self->graph;
				my $default_graphs	= [$graph];
				my $planner	= $self->planner;
				if ($self->log->is_trace) {
					$self->log->debug('Planning with default graphs:');
					foreach my $g (@$default_graphs) {

lib/AtteanX/Endpoint.pm  view on Meta::CPAN

					}
					$self->log->debug("Serializer class: $sclass");
					my $s		= $sclass->new(@args);
					$content	= $s->serialize_iter_to_bytes($iter);
					my $stype	= $s->canonical_media_type;
					$response->headers->content_type($stype);
				};
				if ($@) {
					my $error	= $@;
					$self->log->fatal($error);
					die AtteanX::Endpoint::ServerError->new(code => 500, message => 'SPARQL query/update execution error', uri => 'http://id.kasei.us/rdf-endpoint/error/execution_error', details => { error => $@, sparql => $sparql });
				}
			}
		} elsif ($req->method eq 'POST') {
			$content	= "POST without recognized query or update";
			$self->log_error( $req, $content );
			die AtteanX::Endpoint::ClientError->new(message => 'Missing SPARQL Query/Update String', uri => 'http://id.kasei.us/rdf-endpoint/error/missing_sparql_string');
		} else {
			my $stype		= 'text/html';
			my $dir			= $ENV{ATTEAN_ENDPOINT_SHAREDIR} || File::Spec->catdir((eval { dist_dir('AtteanX-Endpoint') } || 'share'), 'endpoint');
			my $template	= File::Spec->catfile($dir, 'index.html');
			my $parser		= XML::LibXML->new(validation => 0, suppress_errors => 1, no_network => 1, recover => 2);
			my $doc			= $parser->parse_file( $template );
# 			my $gen			= RDF::RDFa::Generator->new( style => 'HTML::Head');
# 			$gen->inject_document($doc, $sdmodel);
		
			my $writer	= HTML::HTML5::Writer->new( markup => 'xhtml', doctype => DOCTYPE_XHTML_RDFA );
			$content	= encode_utf8( $writer->document($doc) );
			$response->status(200);
			$response->headers->content_type('text/html');

lib/AtteanX/Endpoint.pm  view on Meta::CPAN


1;

__END__

=back

=head1 BUGS

Please report any bugs or feature requests to through the GitHub web interface
at L<https://github.com/kasei/atteanx-endpoint/issues>.

=head1 SEE ALSO

L<http://www.perlrdf.org/>

=head1 AUTHOR

Gregory Todd Williams  C<< <gwilliams@cpan.org> >>

=head1 COPYRIGHT

meta/changes.ttl  view on Meta::CPAN

	.

my:project
	a 						:Project ;
	:name					"AtteanX::Endpoint" ;
	:shortdesc				"An Attean SPARQL 1.1 Endpoint implementation." ;
	:programming-language	"Perl" ;
	:created				"2016-03-25"^^xsd:date ;
	:maintainer				my:developer ;
	:homepage				<http://search.cpan.org/dist/AtteanX::Endpoint/> ;
	:bug-database			<https://github.com/kasei/atteanx-endpoint/issues> ;
	doap:download-page		<http://search.cpan.org/dist/AtteanX-Endpoint/> ;
	doap:download-mirror	<http://kasei.us/code/files/> ;
	.

my:project :release my:v_0-003 .

my:v_0-003
	a					:Version ;
	dc:issued			"2026-05-21"^^xsd:date ;
	:revision			"0.003" ;

scripts/endpoint.psgi  view on Meta::CPAN

add_type( 'text/plain' => qw(nt) );
add_type( 'text/x-nquads' => qw(nq) );
add_type( 'text/json' => qw(json) );
add_type( 'text/html' => qw(html xhtml htm) );

my $config;
if (my $file = $ENV{RDF_ENDPOINT_FILE}) {
	my $abs	= File::Spec->rel2abs( $file );
	$config	= {
		store	=> "Memory;$abs",
		endpoint	=> {
			service_description => {
				named_graphs	=> 1,
				default			=> 1,
			},
			html				=> {
				embed_images	=> 1,
				image_width		=> 200,
				resource_links	=> 1,
			},
			load_data	=> 0,
			update		=> 1,
        }
    };
} elsif ($config = eval { Config::JFDI->open( name => "AtteanX::Endpoint") }) {
} else {
	$config	= {
		store	=> "Memory",
		endpoint	=> {
			service_description => {
				named_graphs	=> 1,
				default			=> 1,
			},
			html				=> {
				embed_images	=> 1,
				image_width		=> 200,
				resource_links	=> 1,
			},
			load_data	=> 0,

share/endpoint/www/js/select.js  view on Meta::CPAN

        start: innerNode(range.startContainer, range.startOffset),
        end: innerNode(range.endContainer, range.endOffset),
        changed: false
      };
    };

    select.selectMarked = function () {
      var cs = currentSelection;
      // on webkit-based browsers, it is apparently possible that the
      // selection gets reset even when a node that is not one of the
      // endpoints get messed with. the most common situation where
      // this occurs is when a selection is deleted or overwitten. we
      // check for that here.
      function focusIssue() {
        if (cs.start.node == cs.end.node && cs.start.offset == cs.end.offset) {
          var selection = window.getSelection();
          if (!selection || selection.rangeCount == 0) return true;
          var range = selection.getRangeAt(0), point = innerNode(range.startContainer, range.startOffset);
          return cs.start.node != point.node || cs.start.offset != point.offset;
        }
      }

t/gsp-custom-endpoint.t  view on Meta::CPAN

no warnings 'redefine';

use Attean::RDF;
use AtteanX::Endpoint;
use Test::WWW::Mechanize::PSGI;

sub mech_for_nquads {
	my $allow_update	= shift;
	my $nquads			= shift;
	my $config			= {
		endpoint	=> {
			service_description => {},
			html				=> {},
			load_data			=> 0,
			gsp_path			=> '/custom/gsp_path',
			update				=> $allow_update,
		}
	};

	my $store	= Attean->get_store('Memory')->new();
	my $model	= Attean::MutableQuadModel->new( store => $store );

t/gsp-custom-endpoint.t  view on Meta::CPAN

}


my $mech	= mech_for_nquads(1, <<"END");
_:b <num> "000" .
_:b <num> "123" <http://example.org/graph1> .
_:b <num> "787" <http://example.org/graph2> .
<doc> <name> "My Document" <http://example.org/graph3> .
END

subtest 'HEAD default graph (default endpoint)' => sub {
	$mech->head('/gsp?default', Accept => 'text/turtle');
	cmp_ok($mech->status, '>=', 400, 'response is error');
	cmp_ok($mech->status, '<', 500, 'response is client error');
};

subtest 'HEAD default graph (custom endpoint)' => sub {
	$mech->head('/custom/gsp_path?default', Accept => 'text/turtle');
	ok( $mech->success );
	is( $mech->ct, 'text/turtle', 'Is text/turtle' );
	is(length($mech->content(raw => 1)), 0, 'empty body');
};

subtest 'GET default graph (custom endpoint)' => sub {
	$mech->get('/custom/gsp_path?default', Accept => 'text/turtle');
	ok( $mech->success );
	is( $mech->ct, 'text/turtle', 'Is text/turtle' );
	$mech->content_like(qr#"000"#);
	$mech->content_unlike(qr#"123"#);
	$mech->content_unlike(qr#"My Document"#);
};

done_testing();

t/gsp-read-only.t  view on Meta::CPAN

no warnings 'redefine';

use Attean::RDF;
use AtteanX::Endpoint;
use Test::WWW::Mechanize::PSGI;

sub mech_for_nquads {
	my $allow_update	= shift;
	my $nquads			= shift;
	my $config			= {
		endpoint	=> {
			service_description => {},
			html				=> {},
			load_data			=> 0,
			update				=> $allow_update,
		}
	};

	my $store	= Attean->get_store('Memory')->new();
	my $model	= Attean::MutableQuadModel->new( store => $store );
	my $graph	= iri('http://example.org/graph');

t/gsp.t  view on Meta::CPAN

no warnings 'redefine';

use Attean::RDF;
use AtteanX::Endpoint;
use Test::WWW::Mechanize::PSGI;

sub mech_for_nquads {
	my $allow_update	= shift;
	my $nquads			= shift;
	my $config			= {
		endpoint	=> {
			service_description => {},
			html				=> {},
			load_data			=> 0,
			update				=> $allow_update,
		}
	};

	my $store	= Attean->get_store('Memory')->new();
	my $model	= Attean::MutableQuadModel->new( store => $store );
	my $graph	= iri('http://example.org/graph');

t/simple.t  view on Meta::CPAN

use warnings;
no warnings 'redefine';

use Attean::RDF;
use AtteanX::Endpoint;
use Test::WWW::Mechanize::PSGI;

sub mech_for_nquads {
	my $nquads	= shift;
	my $config	= {
		endpoint	=> {
			service_description => {
				named_graphs	=> 1,
				default			=> 1,
			},
			html				=> {
				embed_images	=> 1,
				image_width		=> 200,
				resource_links	=> 1,
			},
			load_data	=> 0,



( run in 1.712 second using v1.01-cache-2.11-cpan-98e64b0badf )