diff --git a/src/classes/ExtendedGraph.class.php b/src/classes/ExtendedGraph.class.php index f7579ff9..80aaa835 100644 --- a/src/classes/ExtendedGraph.class.php +++ b/src/classes/ExtendedGraph.class.php @@ -141,7 +141,10 @@ public function make_resource_array($resource) { * @return boolean true if the triple was new, false if it already existed in the graph */ public function add_resource_triple($s, $p, $o) { - return $this->_add_triple($s, $p, array('type' => strpos($o, '_:' ) === 0 ? 'bnode' : 'uri', 'value' => $o)); + if($this->isValidResource($o)) { + return $this->_add_triple($s, $p, array('type' => strpos($o, '_:') === 0 ? 'bnode' : 'uri', 'value' => $o)); + } + return false; } /** @@ -154,34 +157,45 @@ public function add_resource_triple($s, $p, $o) { * @return boolean true if the triple was new, false if it already existed in the graph */ public function add_literal_triple($s, $p, $o, $lang = null, $dt = null) { - $o_info = array('type' => 'literal', 'value' => $o); - if ( $lang != null ) { - $o_info['lang'] = $lang; - } - if ( $dt != null ) { - $o_info['datatype'] = $dt; + if($this->isValidLiteral($o)) { + $o_info = array('type' => 'literal', 'value' => $o); + if ($lang != null) { + $o_info['lang'] = $lang; + } + if ($dt != null) { + $o_info['datatype'] = $dt; + } + return $this->_add_triple($s, $p, $o_info); } - return $this->_add_triple($s, $p, $o_info); + return false; } /** * @param string $s * @param string $p * @param array $o_info + * @throws Exceptions\Exception * @return bool */ private function _add_triple($s, $p, Array $o_info) { + // The value $o should already have been validated by this point + // It's validation differs depending on whether it is a literal or resource + // So just check the subject and predicate here... + if(!$this->isValidResource($s)){ + throw new \Tripod\Exceptions\Exception("The subject is invalid"); + } + if(!$this->isValidResource($p)){ + throw new \Tripod\Exceptions\Exception("The predicate is invalid"); + } if (!isset($this->_index[$s])) { $this->_index[$s] = array(); - $this->_index[$s][$p] = array( $o_info ); + $this->_index[$s][$p] = array($o_info); return true; - } - elseif (!isset($this->_index[$s][$p])) { - $this->_index[$s][$p] = array( $o_info); + } elseif (!isset($this->_index[$s][$p])) { + $this->_index[$s][$p] = array($o_info); return true; - } - else { - if ( ! in_array( $o_info, $this->_index[$s][$p] ) ) { + } else { + if (!in_array($o_info, $this->_index[$s][$p])) { $this->_index[$s][$p][] = $o_info; return true; } @@ -189,6 +203,37 @@ private function _add_triple($s, $p, Array $o_info) { return false; } + + /** + * Check if a triple value is valid. + * + * Ideally a valid literal value should be a string + * but accepting scalars so we can handle legacy data + * which was not type-checked. + * + * @param string $value + * @return bool + */ + protected function isValidLiteral($value){ + if(!is_scalar($value)){ + return false; + } + return true; + } + + /** + * Check if a triple value is valid. + * + * @param string $value + * @return bool + */ + protected function isValidResource($value){ + if(!is_string($value) || empty($value)){ + return false; + } + return true; + } + /** * @deprecated this is deprecated */ diff --git a/src/mongo/MongoGraph.class.php b/src/mongo/MongoGraph.class.php index 554db802..436131c9 100644 --- a/src/mongo/MongoGraph.class.php +++ b/src/mongo/MongoGraph.class.php @@ -119,6 +119,7 @@ function to_tripod_view_array($docId,$context) /** * @param array $tarray + * @throws \Tripod\Exceptions\Exception */ private function add_tarray_to_index($tarray) { @@ -129,7 +130,17 @@ private function add_tarray_to_index($tarray) if($key[0] != '_') { $predicate = $this->qname_to_uri($key); - $predObjects[$predicate] = $this->toGraphValueObject($value); + $graphValueObject = $this->toGraphValueObject($value); + // Only add if valid values have been found + if (!empty($graphValueObject)) { + $predObjects[$predicate] = $graphValueObject; + } + } + else if($key == "_id"){ + // If the subject is invalid then throw an exception + if(!isset($value['r']) || !$this->isValidResource($value['r'])){ + throw new \Tripod\Exceptions\Exception("The subject cannot be an empty string"); + } } } $_i[$this->_labeller->qname_to_alias($tarray["_id"][_ID_RESOURCE])] = $predObjects; @@ -138,39 +149,61 @@ private function add_tarray_to_index($tarray) /** * Convert from Tripod value object format (comapct) to ExtendedGraph format (verbose) + * * @param array $mongoValueObject * @return array */ private function toGraphValueObject($mongoValueObject) { - $simpleGraphValueObject = null; + $simpleGraphValueObject = array(); if (array_key_exists(VALUE_LITERAL,$mongoValueObject)) { - // single value literal - $simpleGraphValueObject[] = array( - 'type'=>'literal', - 'value'=>$mongoValueObject[VALUE_LITERAL]); + // only allow valid values + if($this->isValidLiteral($mongoValueObject[VALUE_LITERAL])){ + // single value literal + $simpleGraphValueObject[] = array( + 'type'=>'literal', + 'value'=>$mongoValueObject[VALUE_LITERAL]); + } } else if (array_key_exists(VALUE_URI,$mongoValueObject)) { - // single value literal - $simpleGraphValueObject[] = array( - 'type'=>'uri', - 'value'=>$this->_labeller->qname_to_alias($mongoValueObject[VALUE_URI])); + // only allow valid values + if($this->isValidResource($mongoValueObject[VALUE_URI])) { + // single value uri + $simpleGraphValueObject[] = array( + 'type' => 'uri', + 'value' => $this->_labeller->qname_to_alias($mongoValueObject[VALUE_URI])); + } } else { + // If we have an array of values foreach ($mongoValueObject as $kvp) { foreach ($kvp as $type=>$value) { + // Make sure the value is valid + if($type==VALUE_LITERAL){ + if(!$this->isValidLiteral($value)){ + continue; + } + $valueTypeLabel = 'literal'; + } + else{ + if(!$this->isValidResource($value)){ + continue; + } + $valueTypeLabel = 'uri'; + } $simpleGraphValueObject[] = array( - 'type'=>($type==VALUE_LITERAL) ? 'literal' : 'uri', + 'type'=>$valueTypeLabel, 'value'=>($type==VALUE_URI) ? $this->_labeller->qname_to_alias($value) : $value); } } } + // Otherwise we have found valid values return $simpleGraphValueObject; } diff --git a/test/unit/ExtendedGraphTest.php b/test/unit/ExtendedGraphTest.php index 37347d4e..f85986ed 100644 --- a/test/unit/ExtendedGraphTest.php +++ b/test/unit/ExtendedGraphTest.php @@ -23,6 +23,176 @@ protected function setUp() echo "\nTest: {$className}->{$testName}\n"; } + /** + * @dataProvider addValidValueToLiteralResultsInTriple_Provider + */ + public function testAddValidValueToLiteralResultsInTriple($value) + { + $graph = new ExtendedGraph(); + $addResult = $graph->add_literal_triple('http://some/subject/1', 'http://some/predicate', $value); + $this->assertTrue($addResult, 'The triple should have been added for this value'); + + $hasPropertyResult = $graph->subject_has_property('http://some/subject/1', 'http://some/predicate'); + $this->assertTrue($hasPropertyResult, 'The triple should have been added for this value'); + } + public function addValidValueToLiteralResultsInTriple_Provider(){ + return array( + array('String'), + array(1), + array(1.2), + array(true) + ); + } + + /** + * @dataProvider addInvalidValueToLiteralResultsInNoTriple_Provider + */ + public function testAddInvalidValueToLiteralResultsInNoTriple($value) + { + $graph = new ExtendedGraph(); + $addResult = $graph->add_literal_triple('http://some/subject/1', 'http://some/predicate', $value); + $this->assertFalse($addResult, 'The triple should not have been added for this value'); + + $hasPropertyResult = $graph->subject_has_property('http://some/subject/1', 'http://some/predicate'); + $this->assertFalse($hasPropertyResult, 'The triple should not have been added for this value'); + } + public function addInvalidValueToLiteralResultsInNoTriple_Provider(){ + return array( + array(null), + array(new stdClass()), + array(function(){}) + ); + } + + /** + * @dataProvider addInvalidSubjectToLiteralResultsInNoTriple_Provider + */ + public function testAddInvalidSubjectToLiteralThrowsException($value) + { + $this->setExpectedException('\Tripod\Exceptions\Exception'); + + $graph = new ExtendedGraph(); + $graph->add_resource_triple($value, 'http://some/predicate', 'http://someplace.com'); + } + public function addInvalidSubjectToLiteralResultsInNoTriple_Provider(){ + return array( + array(""), + array(1), + array(1.2), + array(true), + array(array()), + array(null), + array(new stdClass()), + array(function(){}) + ); + } + + /** + * @dataProvider addInvalidSubjectToLiteralResultsInNoTriple_Provider + */ + public function testAddInvalidPredicateToLiteralThrowsException($value) + { + $this->setExpectedException('\Tripod\Exceptions\Exception'); + + $graph = new ExtendedGraph(); + $graph->add_resource_triple('http://some/subject/1', $value, 'http://someplace.com'); + } + public function addInvalidPredicateToLiteralResultsInNoTriple_Provider(){ + return array( + array(""), + array(1), + array(1.2), + array(true), + array(array()), + array(null), + array(new stdClass()), + array(function(){}) + ); + } + + public function testAddValidValueToResourceResultsInTriple() + { + $value = 'A String'; + $graph = new ExtendedGraph(); + $addResult = $graph->add_resource_triple('http://some/subject/1', 'http://some/predicate', $value); + $this->assertTrue($addResult, 'The triple should have been added for this value'); + + $hasPropertyResult = $graph->subject_has_property('http://some/subject/1', 'http://some/predicate'); + $this->assertTrue($hasPropertyResult, 'The triple should have been added for this value'); + } + + + /** + * @dataProvider addInvalidValueToResourceResultsInNoTriple_Provider + */ + public function testAddInvalidValueToResourceResultsInNoTriple($value) + { + $graph = new ExtendedGraph(); + + $addResult = $graph->add_resource_triple('http://some/subject/1', 'http://some/predicate', $value); + $this->assertFalse($addResult, 'The triple should not have been added for this value'); + + $hasPropertyResult = $graph->subject_has_property('http://some/subject/1', 'http://some/predicate'); + $this->assertFalse($hasPropertyResult, 'The triple should not have been added for this value'); + } + public function addInvalidValueToResourceResultsInNoTriple_Provider(){ + return array( + array(1), + array(1.2), + array(true), + array(array()), + array(null), + array(new stdClass()), + array(function(){}) + ); + } + + /** + * @dataProvider addInvalidSubjectToResourceResultsInNoTriple_Provider + */ + public function testAddInvalidSubjectToResourceThrowsException($value) + { + $this->setExpectedException('\Tripod\Exceptions\Exception'); + + $graph = new ExtendedGraph(); + $graph->add_resource_triple($value, 'http://some/predicate', 'http://someplace.com'); + } + public function addInvalidSubjectToResourceResultsInNoTriple_Provider(){ + return array( + array(""), + array(1), + array(1.2), + array(true), + array(array()), + array(null), + array(new stdClass()), + array(function(){}) + ); + } + + /** + * @dataProvider addInvalidSubjectToLiteralResultsInNoTriple_Provider + */ + public function testAddInvalidPredicateToResourceThrowsException($value) + { + $this->setExpectedException('\Tripod\Exceptions\Exception'); + + $graph = new ExtendedGraph(); + $graph->add_resource_triple('http://some/subject/1', $value, 'http://someplace.com'); + } + public function addInvalidPredicateToResourceResultsInNoTriple_Provider(){ + return array( + array(""), + array(1), + array(1.2), + array(true), + array(array()), + array(null), + array(new stdClass()), + array(function(){}) + ); + } + public function testRemoveProperties() { $graph = new ExtendedGraph(); diff --git a/test/unit/mongo/MongoGraphTest.php b/test/unit/mongo/MongoGraphTest.php index eba3556c..98243b98 100644 --- a/test/unit/mongo/MongoGraphTest.php +++ b/test/unit/mongo/MongoGraphTest.php @@ -102,6 +102,223 @@ public function testAddTripodArraySingleDoc() $this->assertEquals($expected, $g); } + /** + * @dataProvider addTripodArrayContainingValidLiteralValues_Provider + */ + public function testAddTripodArrayContainingValidLiteralValues($value) + { + $doc = array( + "_id"=>array("r"=>"http://talisaspire.com/works/4d101f63c10a6-2", "c"=>"http://talisaspire.com/works/4d101f63c10a6-2"), + "_version"=>0, + "rdf:type"=>array( + array("l"=>$value), + array("l"=>"a Value"), + ), + "bibo:isbn13"=>array("l"=>"9211234567890"), + "bibo:isbn10"=>array("l"=>$value) + ); + + $expected = new \Tripod\Mongo\MongoGraph(); + $expected->add_literal_triple("http://talisaspire.com/works/4d101f63c10a6-2", $expected->qname_to_uri("bibo:isbn13"),"9211234567890"); + $expected->add_literal_triple("http://talisaspire.com/works/4d101f63c10a6-2", $expected->qname_to_uri("bibo:isbn10"),$value); + $expected->add_literal_triple("http://talisaspire.com/works/4d101f63c10a6-2", $expected->qname_to_uri("rdf:type"),$value); + $expected->add_literal_triple("http://talisaspire.com/works/4d101f63c10a6-2", $expected->qname_to_uri("rdf:type"),"a Value"); + + $g = new \Tripod\Mongo\MongoGraph(); + $g->add_tripod_array($doc); + + $this->assertEquals($expected, $g); + } + public function addTripodArrayContainingValidLiteralValues_Provider(){ + return array( + array('A String'), + array(1), + array(1.2), + array(true) + ); + } + + /** + * @dataProvider addTripodArrayContainingInvalidLiteralValues_Provider + */ + public function testAddTripodArrayContainingInvalidLiteralValues($value) + { + $doc = array( + "_id"=>array("r"=>"http://talisaspire.com/works/4d101f63c10a6-2", "c"=>"http://talisaspire.com/works/4d101f63c10a6-2"), + "_version"=>0, + "rdf:type"=>array( + array("l"=>$value), + array("l"=>"a Value"), + ), + "bibo:isbn13"=>array("l"=>"9211234567890"), + "bibo:isbn10"=>array("l"=>$value) + ); + + $expected = new \Tripod\Mongo\MongoGraph(); + $expected->add_literal_triple("http://talisaspire.com/works/4d101f63c10a6-2", $expected->qname_to_uri("bibo:isbn13"),"9211234567890"); + $expected->add_literal_triple("http://talisaspire.com/works/4d101f63c10a6-2", $expected->qname_to_uri("rdf:type"),"a Value"); + + $g = new \Tripod\Mongo\MongoGraph(); + $g->add_tripod_array($doc); + + $this->assertEquals($expected, $g); + } + public function addTripodArrayContainingInvalidLiteralValues_Provider(){ + return array( + array(null), + array(new stdClass()), + array(function(){}) + ); + } + + /** + * @dataProvider addTripodArrayContainingInvalidPredicates_Provider + */ + public function testAddTripodArrayContainingInvalidPredicates($value) + { + $this->setExpectedException('\Tripod\Exceptions\LabellerException'); + $doc = array( + "_id"=>array("r"=>"http://talisaspire.com/works/4d101f63c10a6-2", "c"=>"http://talisaspire.com/works/4d101f63c10a6-2"), + "_version"=>0, + "rdf:type"=>array( + array("l"=>"a Value"), + ), + "bibo:isbn13"=>array("l"=>"9211234567890"), + $value=>array("l"=>"9211234567890") + ); + + $expected = new \Tripod\Mongo\MongoGraph(); + $expected->add_literal_triple("http://talisaspire.com/works/4d101f63c10a6-2", $expected->qname_to_uri("bibo:isbn13"),"9211234567890"); + $expected->add_literal_triple("http://talisaspire.com/works/4d101f63c10a6-2", $expected->qname_to_uri("rdf:type"),"a Value"); + + $g = new \Tripod\Mongo\MongoGraph(); + $g->add_tripod_array($doc); + } + public function addTripodArrayContainingInvalidPredicates_Provider(){ + return array( + array(1), + array(1.2), + array(true), + ); + } + + /** + * + * We are expecting the labeller + * + */ + public function testAddTripodArrayContainingEmptyPredicate() + { + // An Uninitialized string offset should occur if an empty predicate is passed. + $this->setExpectedException("PHPUnit_Framework_Error"); + $doc = array( + "_id"=>array("r"=>"http://talisaspire.com/works/4d101f63c10a6-2", "c"=>"http://talisaspire.com/works/4d101f63c10a6-2"), + "_version"=>0, + "rdf:type"=>array( + array("l"=>"a Value"), + ), + "bibo:isbn13"=>array("l"=>"9211234567890"), + ""=>array("l"=>"9211234567890") + ); + + $expected = new \Tripod\Mongo\MongoGraph(); + $expected->add_literal_triple("http://talisaspire.com/works/4d101f63c10a6-2", $expected->qname_to_uri("bibo:isbn13"),"9211234567890"); + $expected->add_literal_triple("http://talisaspire.com/works/4d101f63c10a6-2", $expected->qname_to_uri("rdf:type"),"a Value"); + + $g = new \Tripod\Mongo\MongoGraph(); + $g->add_tripod_array($doc); + } + + /** + * @dataProvider addTripodArrayContainingInvalidSubject_Provider + */ + public function testAddTripodArrayContainingInvalidSubject($value) + { + $this->setExpectedException('\Tripod\Exceptions\Exception'); + $doc = array( + "_id"=>array("r"=>$value, "c"=>"http://talisaspire.com/works/4d101f63c10a6-2"), + "_version"=>0, + "rdf:type"=>array( + array("l"=>"a Value"), + ), + "bibo:isbn13"=>array("l"=>"9211234567890"), + ); + + $g = new \Tripod\Mongo\MongoGraph(); + $g->add_tripod_array($doc); + } + public function addTripodArrayContainingInvalidSubject_Provider(){ + return array( + array(""), + array(1), + array(1.2), + array(true), + ); + } + + public function testAddTripodArrayContainingValidResourceValues() + { + $value = 'A String'; + $doc = array( + "_id"=>array("r"=>"http://talisaspire.com/works/4d101f63c10a6-2", "c"=>"http://talisaspire.com/works/4d101f63c10a6-2"), + "_version"=>0, + "dct:subject"=>array("u"=>"http://talisaspire.com/disciplines/physics"), + "dct:publisher"=>array("u"=>$value), + "rdf:type"=>array( + array("u"=>$value), + array("u"=>"http://talisaspire.com/schema#Work") + ), + ); + + $expected = new \Tripod\Mongo\MongoGraph(); + $expected->add_resource_triple("http://talisaspire.com/works/4d101f63c10a6-2", $expected->qname_to_uri("dct:subject"),"http://talisaspire.com/disciplines/physics"); + $expected->add_resource_triple("http://talisaspire.com/works/4d101f63c10a6-2", $expected->qname_to_uri("dct:publisher"),$value); + $expected->add_resource_triple("http://talisaspire.com/works/4d101f63c10a6-2", $expected->qname_to_uri("rdf:type"),$value); + $expected->add_resource_triple("http://talisaspire.com/works/4d101f63c10a6-2", $expected->qname_to_uri("rdf:type"),"http://talisaspire.com/schema#Work"); + + $g = new \Tripod\Mongo\MongoGraph(); + $g->add_tripod_array($doc); + + $this->assertEquals($expected, $g); + } + + /** + * @dataProvider addTripodArrayContainingInvalidResourceValues_Provider + */ + public function testAddTripodArrayContainingInvalidResourceValues($value) + { + $doc = array( + "_id"=>array("r"=>"http://talisaspire.com/works/4d101f63c10a6-2", "c"=>"http://talisaspire.com/works/4d101f63c10a6-2"), + "_version"=>0, + "dct:subject"=>array("u"=>"http://talisaspire.com/disciplines/physics"), + "dct:publisher"=>array("u"=>$value), + "rdf:type"=>array( + array("u"=>$value), + array("u"=>"http://talisaspire.com/schema#Work") + ), + ); + + $expected = new \Tripod\Mongo\MongoGraph(); + $expected->add_resource_triple("http://talisaspire.com/works/4d101f63c10a6-2", $expected->qname_to_uri("dct:subject"),"http://talisaspire.com/disciplines/physics"); + $expected->add_resource_triple("http://talisaspire.com/works/4d101f63c10a6-2", $expected->qname_to_uri("rdf:type"),"http://talisaspire.com/schema#Work"); + + $g = new \Tripod\Mongo\MongoGraph(); + $g->add_tripod_array($doc); + + $this->assertEquals($expected, $g); + } + public function addTripodArrayContainingInvalidResourceValues_Provider(){ + return array( + array(1), + array(1.2), + array(true), + array(array()), + array(null), + array(new stdClass()), + array(function(){}) + ); + } + public function testAddTripodArrayWhenAddingViews() { // view contains 4 subgraphs