@@ -, +, @@ --- C4/Context.pm | 64 ++++++++++++++++++++++++++++++++ C4/Installer/PerlDependencies.pm | 5 +++ debian/scripts/koha-create | 4 ++ debian/templates/koha-conf-site.xml.in | 3 ++ debian/templates/triplestore.yaml.in | 30 +++++++++++++++ etc/koha-conf.xml | 3 ++ etc/triplestore.yaml | 30 +++++++++++++++ t/db_dependent/triplestore.t | 67 ++++++++++++++++++++++++++++++++++ 8 files changed, 206 insertions(+) create mode 100644 debian/templates/triplestore.yaml.in create mode 100644 etc/triplestore.yaml create mode 100644 t/db_dependent/triplestore.t --- a/C4/Context.pm +++ a/C4/Context.pm @@ -97,6 +97,7 @@ use File::Spec; use Module::Load::Conditional qw(can_load); use POSIX (); use ZOOM; +use YAML; use C4::Boolean; use C4::Debug; @@ -762,6 +763,69 @@ sub restore_dbh # return something, then this function should, too. } +=head2 triplestore + + my $triplestore = C4::Context->triplestore($model_name); + +Returns a handle to an initialised RDF::Trine::Model object + +=cut + +sub triplestore { + my ($self,$name) = @_; + die "Must provide a model name as an argument for the triplestore() method" unless $name; + my $triplestore = ( $context->{triplestore} && $context->{triplestore}->{$name} ) ? $context->{triplestore}->{$name} : undef; + if ( ! $triplestore ){ + my $config_file = $context->config('triplestore_config'); + if ( -f $config_file ){ + my $config = YAML::LoadFile($config_file); + if ($config && $name){ + my $new_triplestore = $self->_new_triplestore($config,$name); + if ( $new_triplestore && $new_triplestore->isa("RDF::Trine::Model") ){ + $triplestore = $new_triplestore; + $context->{triplestore}->{$name} = $new_triplestore; + } + } + } + } + return $triplestore; +} + +=head2 _new_triplestore + +=cut + +sub _new_triplestore { + my ($self, $config,$name) = @_; + if ($config && $name){ + my $models = $config->{models}; + if ($models){ + my $model = $models->{$name}; + if ($model){ + my $module = $model->{module}; + if ($module && $module eq 'RDF::Trine::Store::SPARQL'){ + if ($module && can_load( 'modules' => { $module => undef } ) ){ + my $url = URI->new($model->{url}); + if ($url){ + my $ua = RDF::Trine->default_useragent; + if ($ua){ + my $realm = $model->{realm}; + my $username = $model->{username}; + my $password = $model->{password}; + $ua->credentials($url->host.":".$url->port, $realm, $username, $password); + } + my $store = RDF::Trine::Store::SPARQL->new($url); + my $model = RDF::Trine::Model->new($store); + return $model; + } + } + } + } + } + } + return; +} + =head2 queryparser $queryparser = C4::Context->queryparser --- a/C4/Installer/PerlDependencies.pm +++ a/C4/Installer/PerlDependencies.pm @@ -893,6 +893,11 @@ our $PERL_DEPS = { required => '1', min_ver => '0.37', }, + 'RDF::Trine' => { + 'usage' => 'Managing RDF and triplestores', + 'required' => '1', + 'min_ver' => '1.017', + }, }; 1; --- a/debian/scripts/koha-create +++ a/debian/scripts/koha-create @@ -656,6 +656,10 @@ eof generate_config_file zebra.passwd.in \ "/etc/koha/sites/$name/zebra.passwd" + # Generate and install triplestore config file + generate_config_file triplestore.yaml.in \ + "/etc/koha/sites/$name/triplestore.yaml" + # Create a GPG-encrypted file for requesting a DB to be set up. if [ "$op" = request ] then --- a/debian/templates/koha-conf-site.xml.in +++ a/debian/templates/koha-conf-site.xml.in @@ -390,5 +390,8 @@ __END_SRU_PUBLICSERVER__ server. e.g. Antarctica/South_Pole --> __TIMEZONE__ + + /etc/koha/sites/__KOHASITE__/triplestore.yaml + --- a/debian/templates/triplestore.yaml.in +++ a/debian/templates/triplestore.yaml.in @@ -0,0 +1,30 @@ +--- +models: + query: + #module must be a valid RDF::Trine::Store::* module + module: RDF::Trine::Store::SPARQL + + #When using RDF::Trine::Store::SPARQL: + url: + realm: + username: + password: + update: + #module must be a valid RDF::Trine::Store::* module + module: RDF::Trine::Store::SPARQL + + #When using RDF::Trine::Store::SPARQL: + url: + realm: + username: + password: + #NOTE: Only the above models are likely to be used in Koha + read/write: + #module must be a valid RDF::Trine::Store::* module + module: RDF::Trine::Store::SPARQL + + #When using RDF::Trine::Store::SPARQL: + url: + realm: + username: + password: --- a/etc/koha-conf.xml +++ a/etc/koha-conf.xml @@ -206,5 +206,8 @@ __PAZPAR2_TOGGLE_XML_POST__ server. e.g. Antarctica/South_Pole --> + + __KOHA_CONF_DIR__/triplestore.yaml + --- a/etc/triplestore.yaml +++ a/etc/triplestore.yaml @@ -0,0 +1,30 @@ +--- +models: + query: + #module must be a valid RDF::Trine::Store::* module + module: RDF::Trine::Store::SPARQL + + #When using RDF::Trine::Store::SPARQL: + url: + realm: + username: + password: + update: + #module must be a valid RDF::Trine::Store::* module + module: RDF::Trine::Store::SPARQL + + #When using RDF::Trine::Store::SPARQL: + url: + realm: + username: + password: + #NOTE: Only the above models are likely to be used in Koha + read/write: + #module must be a valid RDF::Trine::Store::* module + module: RDF::Trine::Store::SPARQL + + #When using RDF::Trine::Store::SPARQL: + url: + realm: + username: + password: --- a/t/db_dependent/triplestore.t +++ a/t/db_dependent/triplestore.t @@ -0,0 +1,67 @@ +#!/usr/bin/perl + +use Modern::Perl; +use Test::More tests => 4; +use Test::Exception; +use YAML; +use File::Temp qw/ tempfile /; + +use C4::Context; + +#Make a temporary configuration file for the triplestore +my ($fh, $filename) = tempfile(undef, UNLINK => 1); +my $temp_config = { + models => { + test => { + module => 'RDF::Trine::Store::SPARQL', + url => 'http://localhost/example/rdf', + realm => undef, + username => undef, + password => undef, + } + } +}; + +#Tell C4::Context to use the temporary configuration file as the triplestore_config +my $context = $C4::Context::context; +$context->{config}->{triplestore_config} = $filename; + +subtest 'success' => sub { + YAML::DumpFile($filename, $temp_config); + + my $context_object = C4::Context->new(); + my $triplestore = $context_object->triplestore("test"); + is(ref $triplestore, 'RDF::Trine::Model', 'C4::Context->new()->triplestore returns RDF::Trine::Model if module equals RDF::Trine::Store::SPARQL'); +}; + +subtest 'missing url' => sub { + #Reset triplestore context + delete $context->{triplestore}->{test}; + + my $url = delete $temp_config->{models}->{test}->{url}; + YAML::DumpFile($filename, $temp_config); + + my $context_object = C4::Context->new(); + my $triplestore = $context_object->triplestore("test"); + is($triplestore, undef, 'C4::Context->new()->triplestore returns undef if missing url for SPARQL store'); + + #Restore url for other tests + $temp_config->{url} = $url; +}; + +subtest 'bad module' => sub { + #Reset triplestore context + delete $context->{triplestore}->{test}; + + $temp_config->{module} = 'Local::RDF::Bad::Module'; + YAML::DumpFile($filename, $temp_config); + + my $context_object = C4::Context->new(); + my $triplestore = $context_object->triplestore("test"); + is($triplestore, undef, 'C4::Context->new()->triplestore returns undef if module equals Local::RDF::Bad::Module'); +}; + +subtest 'missing model name' => sub { + my $context_object = C4::Context->new(); + dies_ok { $context_object->triplestore } 'C4::Context::triplestore() method dies if no model name provided'; +}; --