From e7c7b5852c95b94f339e3d9436f83fe197687789 Mon Sep 17 00:00:00 2001 From: Grzegorz Rajchman Date: Fri, 1 Dec 2023 13:54:49 +0000 Subject: [PATCH] PLT-71 Fix PHP type issues (#144) --- src/classes/ExtendedGraph.class.php | 11 ++- src/mongo/Driver.class.php | 15 +++- src/mongo/delegates/SearchDocuments.class.php | 18 ++-- src/mongo/delegates/SearchIndexer.class.php | 10 ++- src/mongo/delegates/Tables.class.php | 10 +-- src/mongo/delegates/Updates.class.php | 3 +- src/mongo/delegates/Views.class.php | 10 ++- test/unit/ExtendedGraphTest.php | 90 ++++++++++++++++++- test/unit/mongo/MongoSearchProviderTest.php | 6 +- .../mongo/MongoTripodComputedFieldsTest.php | 4 +- test/unit/mongo/MongoTripodDriverTest.php | 78 +++++++++++++++- test/unit/mongo/MongoTripodTablesTest.php | 4 +- test/unit/mongo/MongoTripodViewsTest.php | 6 +- test/unit/mongo/data/resources.json | 3 + 14 files changed, 227 insertions(+), 41 deletions(-) diff --git a/src/classes/ExtendedGraph.class.php b/src/classes/ExtendedGraph.class.php index d5c6f113..69f0d172 100644 --- a/src/classes/ExtendedGraph.class.php +++ b/src/classes/ExtendedGraph.class.php @@ -54,7 +54,7 @@ class ExtendedGraph var $_labeller; /** - * @param string|\Tripod\ExtendedGraph $graph + * @param string|array $graph */ public function __construct($graph=null){ $this->_labeller = new Labeller(); @@ -549,7 +549,10 @@ public function from_rdfxml($rdfxml, $base='') { public function from_json($json) { if ($json) { $this->remove_all_triples(); - $this->_index = json_decode($json, true); + $index = json_decode($json, true); + if (is_array($index)) { + $this->_index = $index; + } } } @@ -562,7 +565,9 @@ public function from_json($json) { public function add_json($json) { if ($json) { $json_index = json_decode($json, true); - $this->_index = $this->merge($this->_index, $json_index); + if (is_array($json_index)) { + $this->_index = $this->merge($this->_index, $json_index); + } } } diff --git a/src/mongo/Driver.class.php b/src/mongo/Driver.class.php index 51844151..b40edbed 100755 --- a/src/mongo/Driver.class.php +++ b/src/mongo/Driver.class.php @@ -486,13 +486,13 @@ public function select($query,$fields,$sortBy=null,$limit=null,$offset=0,$contex { $row[$key] = $value; } - else + elseif (is_array($value)) { - if (array_key_exists(VALUE_LITERAL,$value)) + if (isset($value[VALUE_LITERAL])) { $row[$key] = $value[VALUE_LITERAL]; } - else if (array_key_exists(VALUE_URI,$value)) + elseif (isset($value[VALUE_URI])) { $row[$key] = $value[VALUE_URI]; } @@ -502,7 +502,14 @@ public function select($query,$fields,$sortBy=null,$limit=null,$offset=0,$contex // possible array of values foreach ($value as $v) { - $row[$key][] = array_key_exists(VALUE_LITERAL,$v) ? $v[VALUE_LITERAL] : $v[VALUE_URI]; + if (isset($v[VALUE_LITERAL])) + { + $row[$key][] = $v[VALUE_LITERAL]; + } + else if (isset($v[VALUE_URI])) + { + $row[$key][] = $v[VALUE_URI]; + } } } } diff --git a/src/mongo/delegates/SearchDocuments.class.php b/src/mongo/delegates/SearchDocuments.class.php index 617719a1..33a1fd6b 100644 --- a/src/mongo/delegates/SearchDocuments.class.php +++ b/src/mongo/delegates/SearchDocuments.class.php @@ -248,16 +248,16 @@ protected function addFields(Array $source, Array $fieldsOrIndices, Array &$targ $values = array(); if(isset($source[$p])){ - if(isset($source[$p]['u'])){ - $values[] = ($isIndex) ? mb_strtolower(trim($source[$p]['u']), 'UTF-8') : trim($source[$p]['u']); - } else if (isset($source[$p]['l'])){ - $values[] = ($isIndex) ? mb_strtolower(trim($source[$p]['l']), 'UTF-8') : trim($source[$p]['l']); - } else { + if(isset($source[$p][VALUE_URI])){ + $values[] = ($isIndex) ? mb_strtolower(trim($source[$p][VALUE_URI]), 'UTF-8') : trim($source[$p][VALUE_URI]); + } elseif (isset($source[$p][VALUE_LITERAL])){ + $values[] = ($isIndex) ? mb_strtolower(trim($source[$p][VALUE_LITERAL]), 'UTF-8') : trim($source[$p][VALUE_LITERAL]); + } elseif (is_array($source[$p])) { foreach($source[$p] as $v){ - if(isset($v['u'])){ - $values[] = ($isIndex) ? mb_strtolower(trim($v['u']), 'UTF-8') : trim($v['u']); - } else { - $values[] = ($isIndex) ? mb_strtolower(trim($v['l']), 'UTF-8') : trim($v['l']); + if(isset($v[VALUE_URI])){ + $values[] = ($isIndex) ? mb_strtolower(trim($v[VALUE_URI]), 'UTF-8') : trim($v[VALUE_URI]); + } elseif(isset($v[VALUE_LITERAL])) { + $values[] = ($isIndex) ? mb_strtolower(trim($v[VALUE_LITERAL]), 'UTF-8') : trim($v[VALUE_LITERAL]); } } } diff --git a/src/mongo/delegates/SearchIndexer.class.php b/src/mongo/delegates/SearchIndexer.class.php index 19a2c675..fd7148ee 100644 --- a/src/mongo/delegates/SearchIndexer.class.php +++ b/src/mongo/delegates/SearchIndexer.class.php @@ -128,15 +128,17 @@ public function generateAndIndexSearchDocuments($resourceUri, $context, $podName ] ); foreach ($resourceAndType as $rt) { - if (array_key_exists("rdf:type", $rt)) { + if (isset($rt["rdf:type"])) { $rdfTypes = []; - if (array_key_exists('u', $rt["rdf:type"])) { - $rdfTypes[] = $rt["rdf:type"]['u']; + if (isset($rt["rdf:type"][VALUE_URI])) { + $rdfTypes[] = $rt["rdf:type"][VALUE_URI]; } else { // an array of types foreach ($rt["rdf:type"] as $type) { - $rdfTypes[] = $type['u']; + if (isset($type[VALUE_URI])) { + $rdfTypes[] = $type[VALUE_URI]; + } } } diff --git a/src/mongo/delegates/Tables.class.php b/src/mongo/delegates/Tables.class.php index a850da80..8ebc8d75 100644 --- a/src/mongo/delegates/Tables.class.php +++ b/src/mongo/delegates/Tables.class.php @@ -433,12 +433,12 @@ protected function generateTableRowsForResource($resource, $context=null, $specT foreach ($resourceAndType as $rt) { $id = $rt["_id"]; - if (array_key_exists("rdf:type",$rt)) + if (isset($rt["rdf:type"])) { - if (array_key_exists('u',$rt["rdf:type"])) + if (isset($rt["rdf:type"][VALUE_URI])) { // single type, not an array of values - $this->generateTableRowsForType($rt["rdf:type"]['u'],$id[_ID_RESOURCE],$id[_ID_CONTEXT], $specTypes); + $this->generateTableRowsForType($rt["rdf:type"][VALUE_URI],$id[_ID_RESOURCE],$id[_ID_CONTEXT], $specTypes); } else { @@ -446,9 +446,9 @@ protected function generateTableRowsForResource($resource, $context=null, $specT foreach ($rt["rdf:type"] as $type) { // Defensive check in case there is bad data for rdf:type - if(array_key_exists('u', $type)) + if (isset($type[VALUE_URI])) { - $this->generateTableRowsForType($type['u'],$id[_ID_RESOURCE],$id[_ID_CONTEXT], $specTypes); + $this->generateTableRowsForType($type[VALUE_URI],$id[_ID_RESOURCE],$id[_ID_CONTEXT], $specTypes); } } } diff --git a/src/mongo/delegates/Updates.class.php b/src/mongo/delegates/Updates.class.php index 8706e005..7359b186 100644 --- a/src/mongo/delegates/Updates.class.php +++ b/src/mongo/delegates/Updates.class.php @@ -574,7 +574,8 @@ protected function applyChangeSet(\Tripod\ChangeSet $cs, $originalCBDs, $context $valueIndex = array_search($removal, $valueObject); if($valueIndex === false) { - $v = array_pop(array_values($removal)); + $values = array_values($removal); + $v = array_pop($values); $this->errorLog("Removal value {$subjectOfChange} {$predicate} {$v} does not appear in target document to be updated",array("doc"=>$doc)); throw new \Exception("Removal value {$subjectOfChange} {$predicate} {$v} does not appear in target document to be updated"); } diff --git a/src/mongo/delegates/Views.class.php b/src/mongo/delegates/Views.class.php index 4a462b69..9aa3db80 100644 --- a/src/mongo/delegates/Views.class.php +++ b/src/mongo/delegates/Views.class.php @@ -329,19 +329,21 @@ public function generateViews($resources,$context=null) foreach ($resourceAndType as $rt) { $id = $rt["_id"]; - if (array_key_exists("rdf:type",$rt)) + if (isset($rt["rdf:type"])) { - if (array_key_exists('u',$rt["rdf:type"])) + if (isset($rt["rdf:type"][VALUE_URI])) { // single type, not an array of values - $this->generateViewsForResourcesOfType($rt["rdf:type"]['u'],$id[_ID_RESOURCE],$id[_ID_CONTEXT]); + $this->generateViewsForResourcesOfType($rt["rdf:type"][VALUE_URI],$id[_ID_RESOURCE],$id[_ID_CONTEXT]); } else { // an array of types foreach ($rt["rdf:type"] as $type) { - $this->generateViewsForResourcesOfType($type['u'],$id[_ID_RESOURCE],$id[_ID_CONTEXT]); + if (isset($type[VALUE_URI])) { + $this->generateViewsForResourcesOfType($type[VALUE_URI],$id[_ID_RESOURCE],$id[_ID_CONTEXT]); + } } } } diff --git a/test/unit/ExtendedGraphTest.php b/test/unit/ExtendedGraphTest.php index f85986ed..742ee568 100644 --- a/test/unit/ExtendedGraphTest.php +++ b/test/unit/ExtendedGraphTest.php @@ -700,5 +700,93 @@ public function testReplaceLiteralTriples() $this->assertFalse($graph->has_literal_triple("http://test/1", 'http://www.w3.org/2000/01/rdf-schema#label', "value1")); $this->assertFalse($graph->has_literal_triple("http://test/1", 'http://www.w3.org/2000/01/rdf-schema#label', "value2")); } + + public function testFromJson() + { + $graph = new ExtendedGraph(); + $graph->from_json('{ + "http://subject/1": { + "http://predicate": [ + { "type": "uri", "value": "http://value/1" } + ] + }, + "http://subject/2": { + "http://predicate": [ + { "type": "uri", "value": "http://value/2" } + ] + }, + "http://subject/3": { + "http://predicate": [ + { "type": "uri", "value": "http://value/3" } + ] + } + }'); + + $this->assertEquals(3, count($graph->get_subjects())); + $this->assertEquals(['http://subject/1', 'http://subject/2', 'http://subject/3'], $graph->get_subjects()); + $this->assertEquals(['http://value/1', 'http://value/2', 'http://value/3'], $graph->get_resource_properties('http://predicate')); + } + + public function testFromInvalidJson() + { + $graph = new ExtendedGraph(); + $index = $graph->get_index(); + + $graph->from_json('not a valid json'); + + // Should not have changed + $this->assertEquals($index, $graph->get_index()); + } + + public function testAddJson() + { + $graph = new ExtendedGraph(); + $graph->add_json('{ + "http://subject/1": { + "http://predicate": [ + { "type": "uri", "value": "http://value/1" } + ] + } + }'); + + $this->assertEquals(1, count($graph->get_subjects())); + $this->assertEquals(['http://subject/1'], $graph->get_subjects()); + $this->assertEquals(['http://value/1'], $graph->get_resource_properties('http://predicate')); + + $graph->add_json('{ + "http://subject/2": { + "http://predicate": [ + { "type": "uri", "value": "http://value/2" } + ] + } + }'); + + $this->assertEquals(2, count($graph->get_subjects())); + $this->assertEquals(['http://subject/1', 'http://subject/2'], $graph->get_subjects()); + $this->assertEquals(['http://value/1', 'http://value/2'], $graph->get_resource_properties('http://predicate')); + } + + public function testAddInvalidJson() + { + $graph = new ExtendedGraph(); + $graph->add_json('{ + "http://subject/1": { + "http://predicate": [ + { "type": "uri", "value": "http://value/1" } + ] + } + }'); + + $this->assertEquals(1, $graph->get_triple_count()); + $this->assertEquals(1, count($graph->get_subjects())); + $this->assertEquals(['http://subject/1'], $graph->get_subjects()); + $this->assertEquals(['http://value/1'], $graph->get_resource_properties('http://predicate')); + $index = $graph->get_index(); + + $graph->add_json('not a valid json'); + + // Should not have changed + $this->assertEquals($index, $graph->get_index()); + $this->assertEquals(1, $graph->get_triple_count()); + } } -?> \ No newline at end of file diff --git a/test/unit/mongo/MongoSearchProviderTest.php b/test/unit/mongo/MongoSearchProviderTest.php index ad679b1b..e0ecb142 100644 --- a/test/unit/mongo/MongoSearchProviderTest.php +++ b/test/unit/mongo/MongoSearchProviderTest.php @@ -43,9 +43,11 @@ protected function setUp() $t = array(); if(isset($result['rdf:type']['u'])){ $t[] = $result['rdf:type']['u']; - } else { + } elseif (is_array($result['rdf:type'])) { foreach($result['rdf:type'] as $_t){ - $t[] = $_t['u']; + if (isset($_t['u'])) { + $t[] = $_t['u']; + } } } $this->indexer->generateAndIndexSearchDocuments($result['_id']['r'], $result['_id']['c'], $this->tripod->getPodName()); diff --git a/test/unit/mongo/MongoTripodComputedFieldsTest.php b/test/unit/mongo/MongoTripodComputedFieldsTest.php index 0c069e9b..2c5d4aad 100644 --- a/test/unit/mongo/MongoTripodComputedFieldsTest.php +++ b/test/unit/mongo/MongoTripodComputedFieldsTest.php @@ -294,7 +294,7 @@ public function testReplaceComputedField() $this->assertArrayNotHasKey('rdfType', $tableDoc['value']); $tableDoc = $collection->findOne(array('_id.type'=>'t_replace_type', '_id.r'=>'http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA')); - $this->assertEquals('Book Resource', $tableDoc['value']['resourceType']); + $this->assertEquals('Book Resource Testing', $tableDoc['value']['resourceType']); $this->assertArrayNotHasKey('rdfType', $tableDoc['value']); @@ -499,4 +499,4 @@ public function testNestConditionalInArithmeticFunction() \Tripod\Config::getInstance(); $collection->drop(); } -} \ No newline at end of file +} diff --git a/test/unit/mongo/MongoTripodDriverTest.php b/test/unit/mongo/MongoTripodDriverTest.php index b8c2933f..5836a8a0 100755 --- a/test/unit/mongo/MongoTripodDriverTest.php +++ b/test/unit/mongo/MongoTripodDriverTest.php @@ -29,6 +29,7 @@ protected function setUp() $this->tripodTransactionLog->purgeAllTransactions(); // Stub ouf 'addToElastic' search to prevent writes into Elastic Search happening by default. + /** @var \Tripod\Mongo\Driver | PHPUnit_Framework_MockObject_MockObject */ $this->tripod = $this->getMock( '\Tripod\Mongo\Driver', array('validateGraphCardinality'), @@ -126,6 +127,7 @@ public function testGraph() \"0071\" . . . + \"Testing\" . . "); $actualResult = $this->tripod->graph(array("bibo:isbn13.".VALUE_LITERAL=>"9780393929690")); @@ -165,6 +167,7 @@ public function testDescribeResource() \"0071\" . . . + \"Testing\" . . "); $actualResult = $this->tripod->describeResource('http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA'); @@ -204,6 +207,7 @@ public function testDescribeResources() \"0071\" . . . + \"Testing\" . . . \"9780393929691\" . @@ -1680,6 +1684,60 @@ public function testSelectWithOperandWithNamespaceContextQueryDoesNotContainID() $this->assertEquals($expectedResult,$actualResult); } + public function testSelectDocumentWithSpecialFieldTypes() + { + $id = [ + 'r' => 'http://talisaspire.com/resources/' . uniqid(), + 'c' => 'http://talisaspire.com/', + ]; + + /** @var Tripod\Mongo\IConfigInstance */ + $config = \Tripod\Config::getInstance(); + $collection = $config->getCollectionForCBD($this->tripod->getStoreName(), $this->tripod->getPodName()); + $collection->insertOne([ + '_id' => $id, + '_version' => 42, + 'rdf:type' => [ + 'u' => 'dctype:Text', + ], + 'dct:created' => [ + 'l' => '2023-11-30T13:30:00Z', + ], + 'dct:title' => [ + 'l' => 'Test title', + ], + // Timestamps + '_cts' => new \MongoDB\BSON\UTCDateTime(1701351000000), + '_uts' => new \MongoDB\BSON\UTCDateTime(1701351000000), + // Special field types + '_oid' => new \MongoDB\BSON\ObjectID(), + '_bin' => new \MongoDB\BSON\Binary('foo', \MongoDB\BSON\Binary::TYPE_OLD_BINARY), + '_fun' => new \MongoDB\BSON\Javascript('function() { return 42; }'), + '_fun' => new \MongoDB\BSON\Regex('foo', 'i'), + ]); + + $expectedResult = [ + 'head' => [ + 'count' => 1, + 'offset' => 0, + 'limit' => 0, + ], + 'results'=> [ + [ + '_id' => $id, + '_version' => 42, + 'rdf:type' => 'dctype:Text', + 'dct:created' => '2023-11-30T13:30:00Z', + 'dct:title' => 'Test title', + ], + ], + ]; + + $actualResult = $this->tripod->select(['_id' => $id], []); + + $this->assertEquals($expectedResult, $actualResult); + } + /** * Return the distinct values of a table column * @access public @@ -1717,9 +1775,9 @@ public function testGetDistinctTableValues() $results = $this->tripod->getDistinctTableColumnValues($table, "value.type"); $this->assertArrayHasKey('head', $results); $this->assertArrayHasKey('count', $results['head']); - $this->assertEquals(6, $results['head']['count']); + $this->assertEquals(7, $results['head']['count']); $this->assertArrayHasKey('results', $results); - $this->assertEquals(6, count($results['results'])); + $this->assertEquals(7, count($results['results'])); $this->assertContains('acorn:Resource', $results['results']); $this->assertContains('acorn:Work', $results['results']); $this->assertContains('bibo:Book', $results['results']); @@ -2239,3 +2297,19 @@ class TestSaveChangesHookB extends TestSaveChangesHookA // empty } /** END: saveChangesHooks tests */ + +class TripodDriverTestConfig extends \Tripod\Mongo\Config +{ + /** + * Constructor + */ + public function __construct(){} + + /** + * @param array $config + */ + public function loadConfig(array $config) + { + parent::loadConfig($config); + } +} diff --git a/test/unit/mongo/MongoTripodTablesTest.php b/test/unit/mongo/MongoTripodTablesTest.php index 3ecd4e93..fb408f44 100644 --- a/test/unit/mongo/MongoTripodTablesTest.php +++ b/test/unit/mongo/MongoTripodTablesTest.php @@ -860,9 +860,9 @@ public function testDistinct() $results = $this->tripodTables->distinct($table, "value.type"); $this->assertArrayHasKey('head', $results); $this->assertArrayHasKey('count', $results['head']); - $this->assertEquals(6, $results['head']['count']); + $this->assertEquals(7, $results['head']['count']); $this->assertArrayHasKey('results', $results); - $this->assertEquals(6, count($results['results'])); + $this->assertEquals(7, count($results['results'])); $this->assertContains('acorn:Resource', $results['results']); $this->assertContains('acorn:Work', $results['results']); $this->assertContains('bibo:Book', $results['results']); diff --git a/test/unit/mongo/MongoTripodViewsTest.php b/test/unit/mongo/MongoTripodViewsTest.php index 3b987edf..719836be 100644 --- a/test/unit/mongo/MongoTripodViewsTest.php +++ b/test/unit/mongo/MongoTripodViewsTest.php @@ -84,7 +84,8 @@ public function testGenerateView() "_id"=>array("r"=>"http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA","c"=>'http://talisaspire.com/'), "rdf:type"=>array( array(VALUE_URI=>"bibo:Book"), - array(VALUE_URI=>"acorn:Resource") + array(VALUE_URI=>"acorn:Resource"), + array(VALUE_LITERAL=>"Testing") ), "searchterms:topic"=>array( array(VALUE_LITERAL=>"engineering: general"), @@ -501,7 +502,8 @@ public function testGenerateViewWithTTL() "_id"=>array("r"=>"http://talisaspire.com/resources/3SplCtWGPqEyXcDiyhHQpA","c"=>'http://talisaspire.com/'), "rdf:type"=>array( array("u"=>"bibo:Book"), - array("u"=>"acorn:Resource") + array("u"=>"acorn:Resource"), + array("l"=>"Testing") ), "searchterms:topic"=>array( array("l"=>"engineering: general"), diff --git a/test/unit/mongo/data/resources.json b/test/unit/mongo/data/resources.json index a7c6771e..105974a0 100644 --- a/test/unit/mongo/data/resources.json +++ b/test/unit/mongo/data/resources.json @@ -125,6 +125,9 @@ }, { "u":"http://talisaspire.com/schema#Resource" + }, + { + "l":"Testing" } ], "owl:sameAs":