Skip to content

Commit

Permalink
Add ForeignEntityRevisionLookupFactory providing a WikiPageEntityMeta…
Browse files Browse the repository at this point in the history
…DataLookup

for the given foreign repository

Bug: T150330
Change-Id: Iac2de8d73fbc7e49ad2de358d69cc29456da25a5
  • Loading branch information
manicki committed Nov 14, 2016
1 parent 4c058e6 commit a7a10ee
Show file tree
Hide file tree
Showing 3 changed files with 284 additions and 1 deletion.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"wikibase/data-model": "~6.3",
"wikibase/data-model-serialization": "~2.2",
"wikibase/internal-serialization": "~2.3",
"wikibase/data-model-services": "~3.5",
"wikibase/data-model-services": "~3.7",
"wikibase/data-model-javascript": "~3.0.0",
"wikibase/javascript-api": "~2.2.0",
"wikibase/serialization-javascript": "~2.0.6",
Expand Down
145 changes: 145 additions & 0 deletions lib/includes/Store/ForeignEntityRevisionLookupFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<?php

namespace Wikibase\Lib\Store;

use DataValues\Deserializers\DataValueDeserializer;
use Serializers\Serializer;
use Wikibase\DataModel\Assert\RepositoryNameAssert;
use Wikibase\DataModel\Entity\EntityIdParser;
use Wikibase\DataModel\Services\EntityId\PrefixMappingEntityIdParserFactory;
use Wikibase\DataModel\Services\Lookup\UnknownForeignRepositoryException;
use Wikibase\InternalSerialization\DeserializerFactory;
use Wikibase\Lib\Store\Sql\PrefetchingWikiPageEntityMetaDataAccessor;
use Wikibase\Lib\Store\Sql\WikiPageEntityMetaDataLookup;
use Wikimedia\Assert\Assert;
use Wikimedia\Assert\ParameterAssertionException;

/**
* A factory providing the WikiPageEntityMetaDataLookup instance configured for the given foreign repository.
*
* @license GPL-2.0+
*/
class ForeignEntityRevisionLookupFactory {

/**
* @var PrefixMappingEntityIdParserFactory
*/
private $parserFactory;

/**
* @var Serializer
*/
private $entitySerializer;

/**
* @var DataValueDeserializer
*/
private $dataValueDeserializer;

/**
* @var EntityNamespaceLookup
*/
private $entityNamespaceLookup;

/**
* @var int
*/
private $maxBlobSize;

/**
* @var string[]
*/
private $databaseNames;

/**
* @var WikiPageEntityRevisionLookup[]
*/
private $lookups = [];

/**
* @param PrefixMappingEntityIdParserFactory $parserFactory
* @param DataValueDeserializer $dataValueDeserializer
* @param EntityIdParser $idParser Parser used to create an EntityId with a foreign repository prefix stripped
* @param EntityNamespaceLookup $entityNamespaceLookup
* @param int $maxBlobSize The maximum size of a blob allowed in serialization/deserialization,
* @see EntityContentDataCodec
* @param string[] $databaseNames Associative array mapping repository names (prefixes) to database names
*
* @throws ParameterAssertionException
*/
public function __construct(
PrefixMappingEntityIdParserFactory $parserFactory,
Serializer $entitySerializer,
DataValueDeserializer $dataValueDeserializer,
EntityNamespaceLookup $entityNamespaceLookup,
$maxBlobSize,
array $databaseNames
) {
RepositoryNameAssert::assertParameterKeysAreValidRepositoryNames( $databaseNames, '$databaseNames' );
Assert::parameterElementType( 'string', $databaseNames, '$databaseNames' );
Assert::parameter( !array_key_exists( '', $databaseNames ), '$databaseNames', 'must not contain an empty string key' );

$this->parserFactory = $parserFactory;
$this->entitySerializer = $entitySerializer;
$this->dataValueDeserializer = $dataValueDeserializer;
$this->entityNamespaceLookup = $entityNamespaceLookup;
$this->maxBlobSize = $maxBlobSize;
$this->databaseNames = $databaseNames;
}

/**
* @param string $repositoryName
*
* @return WikiPageEntityRevisionLookup
*
* @throws UnknownForeignRepositoryException
*/
public function getLookup( $repositoryName ) {
if ( !isset( $this->lookups[$repositoryName] ) ) {
$this->lookups[$repositoryName] = $this->newLookupForRepository( $repositoryName );

}
return $this->lookups[$repositoryName];
}

/**
* @param string $repositoryName
*
* @return WikiPageEntityRevisionLookup
*
* @throws UnknownForeignRepositoryException
*/
private function newLookupForRepository( $repositoryName ) {
if ( !array_key_exists( $repositoryName, $this->databaseNames ) ) {
throw new UnknownForeignRepositoryException( 'No database configured for repository: ' . $repositoryName );
}

$prefixMappingIdParser = $this->parserFactory->getIdParser( $repositoryName );
$entityDeserializerFactory = new DeserializerFactory(
$this->dataValueDeserializer,
$prefixMappingIdParser,
null
);
$codec = new EntityContentDataCodec(
$prefixMappingIdParser,
$this->entitySerializer,
$entityDeserializerFactory->newEntityDeserializer(),
$this->maxBlobSize
);

$metaDataLookup = new PrefetchingWikiPageEntityMetaDataAccessor(
new WikiPageEntityMetaDataLookup(
$this->entityNamespaceLookup,
$this->databaseNames[$repositoryName],
$repositoryName
)
);

return new WikiPageEntityRevisionLookup(
$codec,
$metaDataLookup,
$this->databaseNames[$repositoryName]
);
}

}
138 changes: 138 additions & 0 deletions lib/tests/phpunit/Store/ForeignEntityRevisionLookupFactoryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<?php

namespace Wikibase\Lib\Tests\Store;

use DataValues\Deserializers\DataValueDeserializer;
use Serializers\Serializer;
use Wikibase\DataModel\Entity\EntityIdParser;
use Wikibase\DataModel\Services\EntityId\PrefixMappingEntityIdParserFactory;
use Wikibase\DataModel\Services\Lookup\UnknownForeignRepositoryException;
use Wikibase\Lib\Store\EntityNamespaceLookup;
use Wikibase\Lib\Store\ForeignEntityRevisionLookupFactory;
use Wikibase\Lib\Store\WikiPageEntityRevisionLookup;
use Wikimedia\Assert\ParameterAssertionException;

/**
* @covers Wikibase\Lib\Store\ForeignEntityRevisionLookupFactory
*
* @group Wikibase
* @group WikibaseLib
*
* @license GPL-2.0+
*/
class ForeignEntityRevisionLookupFactoryTest extends \PHPUnit_Framework_TestCase {

/**
* @return EntityIdParser
*/
private function getEntityIdParser() {
return $this->getMock( EntityIdParser::class );
}

/**
* @return PrefixMappingEntityIdParserFactory
*/
private function getPrefixMappingEntityIdParserFactory() {
$factory = $this->getMockBuilder( PrefixMappingEntityIdParserFactory::class )
->disableOriginalConstructor()
->getMock();
$factory->expects( $this->any() )
->method( 'getIdParser' )
->will( $this->returnValue( $this->getEntityIdParser() ) );
return $factory;
}

/**
* @return Serializer
*/
private function getEntitySerializer() {
return $this->getMock( Serializer::class );
}

/**
* @return DataValueDeserializer
*/
private function getDataValueDeserializer() {
return $this->getMock( DataValueDeserializer::class );
}

/**
* @return EntityNamespaceLookup
*/
private function getEntityNamespaceLookup() {
return new EntityNamespaceLookup( [ 'item' => 100 ] );
}

public function testGivenKnownRepository_getLookupReturnsInstanceOfWikiPageEntityRevisionLookup() {
$factory = new ForeignEntityRevisionLookupFactory(
$this->getPrefixMappingEntityIdParserFactory(),
$this->getEntitySerializer(),
$this->getDataValueDeserializer(),
$this->getEntityNamespaceLookup(),
0,
[ 'foo' => 'foodb' ]
);

$this->assertInstanceOf( WikiPageEntityRevisionLookup::class, $factory->getLookup( 'foo' ) );
}

public function testGivenUnknownRepository_getLookupThrowsException() {
$factory = new ForeignEntityRevisionLookupFactory(
$this->getPrefixMappingEntityIdParserFactory(),
$this->getEntitySerializer(),
$this->getDataValueDeserializer(),
$this->getEntityNamespaceLookup(),
0,
[ 'foo' => 'foodb' ]
);

$this->setExpectedException( UnknownForeignRepositoryException::class );

$this->assertInstanceOf( WikiPageEntityRevisionLookup::class, $factory->getLookup( 'bar' ) );
}

public function testGetLookupReusesTheInstanceOverMultipleCalls() {
$factory = new ForeignEntityRevisionLookupFactory(
$this->getPrefixMappingEntityIdParserFactory(),
$this->getEntitySerializer(),
$this->getDataValueDeserializer(),
$this->getEntityNamespaceLookup(),
0,
[ 'foo' => 'foodb' ]
);

$lookupOne = $factory->getLookup( 'foo' );
$lookupTwo = $factory->getLookup( 'foo' );

$this->assertSame( $lookupOne, $lookupTwo );
}

public function provideInvalidDatabaseNamesValue() {
return [
'repository name containing a colon' => [ [ 'fo:o' => 'foodb' ] ],
'providing database name for local repository' => [ [ '' => 'foodb' ] ],
'non-string key' => [ [ 0 => 'foodb' ] ],
'not a string as a database name (false)' => [ [ 'foo' => false ] ],
'not a string as a database name (true)' => [ [ 'foo' => true ] ],
'not a string as a database name (null)' => [ [ 'foo' => null ] ],
'not a string as a database name (int)' => [ [ 'foo' => 100 ] ],
];
}

/**
* @dataProvider provideInvalidDatabaseNamesValue
*/
public function testGivenInvalidDatabaseNamesValue_exceptionIsThrown( array $databaseNames ) {
$this->setExpectedException( ParameterAssertionException::class );

new ForeignEntityRevisionLookupFactory(
$this->getPrefixMappingEntityIdParserFactory(),
$this->getEntitySerializer(),
$this->getDataValueDeserializer(),
$this->getEntityNamespaceLookup(),
0,
$databaseNames
);
}

}

0 comments on commit a7a10ee

Please sign in to comment.