From 09a32f6f7ee9f7665d0dd7f1425651a8f7b576a0 Mon Sep 17 00:00:00 2001
From: David Cook <dcook@prosentient.com.au>
Date: Thu, 1 Jun 2017 16:17:30 +1000
Subject: [PATCH] Bug 18713: Add support for named graphs and rfds:seeAlso to
 Koha::RDF

This patch adds methods to Koha::RDF that creates or deletes a named
graph by removing/adding RDF quads from/to a triplestore.

This patch also can create a rdfs:seeAlso relationship between
two RDF entities.

This patch includes unit tests for both of these changes.

_TEST PLAN_
1) Run "prove t/Koha/RDF.t"
2) Note that all tests have run successfully.

Signed-off-by: Josef Moravec <josef.moravec@gmail.com>
---
 Koha/RDF.pm  | 54 +++++++++++++++++++++++++++++++++++++++++++++++
 t/Koha/RDF.t | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 121 insertions(+), 1 deletion(-)

diff --git a/Koha/RDF.pm b/Koha/RDF.pm
index def6dfddcb..d14d74d1d9 100644
--- a/Koha/RDF.pm
+++ b/Koha/RDF.pm
@@ -47,4 +47,58 @@ sub mint_uri {
     return $new_uri;
 }
 
+sub DelNamedGraph {
+    my ($self, $args) = @_;
+    my $model = $args->{model};
+    my $context = $args->{context};
+    if ($model && $context){
+        #Create variables to match all nodes with a given context (ie within a named graph),
+        # so that we can delete all triples in the named graph and thus delete the graph.
+        my $s = RDF::Trine::Node::Variable->new("s");
+        my $p = RDF::Trine::Node::Variable->new("p");
+        my $o = RDF::Trine::Node::Variable->new("o");
+
+        #Perform operations
+        #FIXME: Workaround for differences in RDF::Trine::Store::* API interfaces...
+        #See https://github.com/kasei/perlrdf/issues/149 and https://github.com/kasei/perlrdf/pull/150
+        my $store = $model->_store;
+        if ($store){
+            if ( $store->isa("RDF::Trine::Store::SPARQL") ){
+                my $quad = RDF::Trine::Statement::Quad->new($s, $p, $o, $context);
+                $model->remove_statements($quad);
+            }
+            else {
+                $model->remove_statements($s,$p,$o,$context);
+            }
+        }
+    }
+}
+
+sub AddNamedGraph {
+    my ($self, $args) = @_;
+    my $model = $args->{model};
+    my $context = $args->{context};
+    my $iterator = $args->{iterator};
+
+    while (my $st = $iterator->next){
+        #Set the context (in order to populate the named graph)
+        $st->context($context);
+
+        $model->add_statement($st);
+        #NOTE: This method returns undef on success.
+    }
+}
+
+sub AddSeeAlso {
+    my ($self, $args) = @_;
+    my $model = $args->{model};
+    my $subject = $args->{subject};
+    my $predicate = RDF::Trine::Node::Resource->new("http://www.w3.org/2000/01/rdf-schema#seeAlso");
+    my $object = $args->{object};
+    if ($model && $subject && $predicate && $object){
+        my $statement = RDF::Trine::Statement->new($subject,$predicate,$object);
+        $model->add_statement($statement);
+    }
+}
+
 1;
diff --git a/t/Koha/RDF.t b/t/Koha/RDF.t
index 9c164fc0a4..c31590d38c 100644
--- a/t/Koha/RDF.t
+++ b/t/Koha/RDF.t
@@ -16,7 +16,9 @@
 # along with Koha; if not, see <http://www.gnu.org/licenses>.
 
 use Modern::Perl;
-use Test::More tests => 3;
+use Test::More tests => 5;
+use RDF::Trine;
+use JSON;
 
 use t::lib::Mocks;
 
@@ -31,3 +33,67 @@ is($well_formed_uri,'http://koha-community.org/bib/1','Successfully minted a RDF
 t::lib::Mocks::mock_preference('OPACBaseURL', 'koha-community.org');
 my $malformed_uri = $rdf->mint_uri('biblio',2);
 is($malformed_uri,undef,"Didn't mint URI due to missing URI scheme");
+
+
+subtest 'Create and delete named graphs' => sub {
+    plan tests => 3;
+    my $store = RDF::Trine::Store::Memory->new();
+    my $model = RDF::Trine::Model->new($store);
+
+    my $context = RDF::Trine::Node::Resource->new("http://koha-community/graph/1");
+    my $subject = RDF::Trine::Node::Resource->new("http://koha-community/resource/1");
+    my $predicate = RDF::Trine::Node::Resource->new("http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
+    my $object = RDF::Trine::Node::Resource->new("http://koha-community/vocab/record");
+    my $triple = RDF::Trine::Statement->new($subject,$predicate,$object);
+    my $quad = RDF::Trine::Statement::Quad->new($subject,$predicate,$object,undef);
+    my $stream = [
+        $quad,
+    ];
+    my $iterator = RDF::Trine::Iterator::Graph->new($stream);
+
+    #NOTE: Add a triple to the default graph
+    $model->add_statement($triple);
+
+    #NOTE: Add a triple to a named graph (ie add a quad to the triplestore) separate from the
+    #named graph we're going to make with the Koha::RDF method
+    if (my $clone = $quad->clone){
+        my $separate_context = RDF::Trine::Node::Resource->new("http://koha-community/graph/keep");
+        $clone->context($separate_context);
+        $model->add_statement($clone);
+    }
+
+    is($model->size,2,"2 statements in model (in default graph and a named graph)");
+    $rdf->AddNamedGraph({
+        model => $model,
+        context => $context,
+        iterator => $iterator,
+    });
+    is($model->size,3,"3 statements in model (1 in default graph and 2 in separate named graphs");
+
+    $rdf->DelNamedGraph({
+        model => $model,
+        context => $context,
+    });
+    is($model->size,2,"2 statements in model (1 in default graph and 1 in a named graph) after 1 of the named graphs was deleted");
+};
+
+subtest 'Add triple using Koha::RDF::AddSeeAlso' => sub {
+    plan tests => 2;
+    my $store = RDF::Trine::Store::Memory->new();
+    my $model = RDF::Trine::Model->new($store);
+
+    my $resource = RDF::Trine::Node::Resource->new("http://koha-community/resource/1");
+    my $linked_resource = RDF::Trine::Node::Resource->new("http://upstream/resource/1000");
+
+    $rdf->AddSeeAlso({
+        model => $model,
+        subject => $resource,
+        object => $linked_resource,
+    });
+    my $iterator = $model->as_stream;
+    my @statements = $iterator->get_all;
+    is(scalar @statements,1,"Added one statement");
+    my $statement = shift @statements;
+    my $string = $statement->as_string;
+    is($string,'(quad <http://koha-community/resource/1> <http://www.w3.org/2000/01/rdf-schema#seeAlso> <http://upstream/resource/1000> (nil))',"Statement correctly added");
+};
-- 
2.11.0