Aude has uploaded a new change for review. https://gerrit.wikimedia.org/r/227691
Change subject: Update Wikidata - add usage tracking job ...................................................................... Update Wikidata - add usage tracking job Change-Id: I33a392262eb58778a01e0e05d6c9815429ab67f7 --- M composer.lock M extensions/Wikibase/.jscsrc M extensions/Wikibase/client/WikibaseClient.php M extensions/Wikibase/client/includes/Hooks/DataUpdateHookHandlers.php M extensions/Wikibase/client/includes/Usage/EntityUsage.php A extensions/Wikibase/client/includes/store/AddUsagesForPageJob.php M extensions/Wikibase/client/includes/store/ClientStore.php M extensions/Wikibase/client/includes/store/sql/DirectSqlStore.php M extensions/Wikibase/client/tests/phpunit/MockClientStore.php M extensions/Wikibase/client/tests/phpunit/includes/Hooks/DataUpdateHookHandlersTest.php M extensions/Wikibase/client/tests/phpunit/includes/Usage/EntityUsageTest.php A extensions/Wikibase/client/tests/phpunit/includes/store/AddUsagesForPageJobTest.php M extensions/Wikibase/composer.json M extensions/Wikibase/package.json M vendor/composer/autoload_classmap.php M vendor/composer/installed.json 16 files changed, 523 insertions(+), 47 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Wikidata refs/changes/91/227691/1 diff --git a/composer.lock b/composer.lock index 717f103..07826bc 100644 --- a/composer.lock +++ b/composer.lock @@ -1326,12 +1326,12 @@ "source": { "type": "git", "url": "https://github.com/wikimedia/mediawiki-extensions-Wikibase.git", - "reference": "2498f76188fe714f19fdd68ca89a6ac34ecd4e4c" + "reference": "7ed9b2a65f6ff6be7293d796b01e597dd9164c81" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wikimedia/mediawiki-extensions-Wikibase/zipball/2498f76188fe714f19fdd68ca89a6ac34ecd4e4c", - "reference": "2498f76188fe714f19fdd68ca89a6ac34ecd4e4c", + "url": "https://api.github.com/repos/wikimedia/mediawiki-extensions-Wikibase/zipball/7ed9b2a65f6ff6be7293d796b01e597dd9164c81", + "reference": "7ed9b2a65f6ff6be7293d796b01e597dd9164c81", "shasum": "" }, "require": { @@ -1350,8 +1350,8 @@ "php": ">=5.3.2", "wikibase/data-model": "~3.0", "wikibase/data-model-javascript": "^1.0.2", - "wikibase/data-model-serialization": "~1.4", - "wikibase/internal-serialization": "~1.4", + "wikibase/data-model-serialization": "~1.4.0", + "wikibase/internal-serialization": "~1.4.0", "wikibase/javascript-api": "~1.0", "wikibase/serialization-javascript": "~2.0" }, @@ -1399,7 +1399,7 @@ "wikibaserepo", "wikidata" ], - "time": "2015-07-23 01:52:42" + "time": "2015-07-29 02:42:33" }, { "name": "wikibase/wikimedia-badges", diff --git a/extensions/Wikibase/.jscsrc b/extensions/Wikibase/.jscsrc index f66f6bd..312c030 100644 --- a/extensions/Wikibase/.jscsrc +++ b/extensions/Wikibase/.jscsrc @@ -3,12 +3,26 @@ "preset": "wikimedia", // ---- - // Rules from wikimedia preset we don't follow + // Rules from wikimedia preset we don't yet? follow "validateIndentation": null, "requireMultipleVarDecl": null, "disallowDanglingUnderscores": null, - "requireSpacesInsideArrayBrackets": null, + "requireSpacesInsideBrackets": null, + "requireVarDeclFirst": null, + "jsDoc": { + // what we don't yet follow is commented out + //"checkAnnotations": "jsduck5", + //"checkParamNames": true, + "requireParamTypes": true, + "checkRedundantParams": true, + //"checkReturnTypes": true, + "checkRedundantReturns": true, + //"requireReturnTypes": true, + //"checkTypes": "capitalizedNativeCase", + "checkRedundantAccess": true + //"requireNewlineAfterDescription": true + }, // ---- // Own rules @@ -24,5 +38,5 @@ "else" ], - "excludeFiles": [ "node_modules/**", "vendor/**" ] + "excludeFiles": [ "node_modules/**", "vendor/**", "extensions/**" ] } diff --git a/extensions/Wikibase/client/WikibaseClient.php b/extensions/Wikibase/client/WikibaseClient.php index 4c48108..deefc1f 100644 --- a/extensions/Wikibase/client/WikibaseClient.php +++ b/extensions/Wikibase/client/WikibaseClient.php @@ -67,6 +67,7 @@ global $wgExtensionCredits, $wgExtensionMessagesFiles, $wgHooks; global $wgAPIMetaModules, $wgAPIPropModules, $wgSpecialPages, $wgResourceModules; global $wgWBClientSettings, $wgRecentChangesFlags, $wgMessagesDirs; + global $wgJobClasses; $wgExtensionCredits['wikibase'][] = array( 'path' => __DIR__, @@ -123,6 +124,9 @@ // extension hooks $wgHooks['WikibaseDeleteData'][] = '\Wikibase\ClientHooks::onWikibaseDeleteData'; + // job classes + $wgJobClasses['wikibase-addUsagesForPage'] = 'Wikibase\Client\Store\AddUsagesForPageJob'; + // api modules $wgAPIMetaModules['wikibase'] = array( 'class' => 'Wikibase\ApiClientInfo', diff --git a/extensions/Wikibase/client/includes/Hooks/DataUpdateHookHandlers.php b/extensions/Wikibase/client/includes/Hooks/DataUpdateHookHandlers.php index 8b12966..6776f65 100644 --- a/extensions/Wikibase/client/includes/Hooks/DataUpdateHookHandlers.php +++ b/extensions/Wikibase/client/includes/Hooks/DataUpdateHookHandlers.php @@ -3,6 +3,8 @@ namespace Wikibase\Client\Hooks; use Content; +use EnqueueJob; +use JobQueueGroup; use LinksUpdate; use ManualLogEntry; use ParserCache; @@ -10,6 +12,7 @@ use ParserOutput; use Title; use User; +use Wikibase\Client\Store\AddUsagesForPageJob; use Wikibase\Client\Store\UsageUpdater; use Wikibase\Client\Usage\ParserOutputUsageAccumulator; use Wikibase\Client\WikibaseClient; @@ -34,19 +37,15 @@ */ private $usageUpdater; + /** + * @var JobQueueGroup + */ + private $jobScheduler; + public static function newFromGlobalState() { - $wikibaseClient = WikibaseClient::getDefaultInstance(); - $settings = $wikibaseClient->getSettings(); - - $usageUpdater = new UsageUpdater( - $settings->getSetting( 'siteGlobalID' ), - $wikibaseClient->getStore()->getUsageTracker(), - $wikibaseClient->getStore()->getUsageLookup(), - $wikibaseClient->getStore()->getSubscriptionManager() - ); - return new DataUpdateHookHandlers( - $usageUpdater + WikibaseClient::getDefaultInstance()->getStore()->getUsageUpdater(), + JobQueueGroup::singleton() ); } @@ -113,9 +112,11 @@ } public function __construct( - UsageUpdater $usageUpdater + UsageUpdater $usageUpdater, + JobQueueGroup $jobScheduler ) { $this->usageUpdater = $usageUpdater; + $this->jobScheduler = $jobScheduler; } /** @@ -160,7 +161,7 @@ * Implemented to update usage tracking information via UsageUpdater. * * @param ParserOutput $parserOutput - * @param $title $title + * @param Title $title */ public function doParserCacheSaveComplete( ParserOutput $parserOutput, Title $title ) { $usageAcc = new ParserOutputUsageAccumulator( $parserOutput ); @@ -169,13 +170,24 @@ // These timestamps should usually be the same, but asking $title may cause a database query. $touched = $parserOutput->getTimestamp() ?: $title->getTouched(); + if ( count( $usageAcc->getUsages() ) === 0 ) { + // no usages, bail out + return; + } + // Add or touch any usages present in the new rendering. // This allows us to track usages in each user language separately, for multilingual sites. - $this->usageUpdater->addUsagesForPage( - $title->getArticleId(), - $usageAcc->getUsages(), - $touched - ); + + // NOTE: Since parser cache updates may be triggered by page views (in a new language), + // schedule the usage updates in the job queue, to avoid writing to the database + // during a GET request. + + //TODO: Before posting a job, check slave database. If no changes are needed, skip update. + + $addUsagesForPageJob = AddUsagesForPageJob::newSpec( $title, $usageAcc->getUsages(), $touched ); + $enqueueJob = EnqueueJob::newFromLocalJobs( $addUsagesForPageJob ); + + $this->jobScheduler->lazyPush( $enqueueJob ); } /** diff --git a/extensions/Wikibase/client/includes/Usage/EntityUsage.php b/extensions/Wikibase/client/includes/Usage/EntityUsage.php index 5876d7a..864efce 100644 --- a/extensions/Wikibase/client/includes/Usage/EntityUsage.php +++ b/extensions/Wikibase/client/includes/Usage/EntityUsage.php @@ -146,6 +146,17 @@ } /** + * @return array array( 'entityId' => $entityId, 'aspect' => $aspect, 'modifier' => $modifier ) + */ + public function asArray() { + return array( + 'entityId' => $this->entityId->getSerialization(), + 'aspect' => $this->aspect, + 'modifier' => $this->modifier + ); + } + + /** * @param string $aspectKey * * @return string One of the EntityUsage::..._USAGE constants with the modifier split off. diff --git a/extensions/Wikibase/client/includes/store/AddUsagesForPageJob.php b/extensions/Wikibase/client/includes/store/AddUsagesForPageJob.php new file mode 100644 index 0000000..335fd3e --- /dev/null +++ b/extensions/Wikibase/client/includes/store/AddUsagesForPageJob.php @@ -0,0 +1,153 @@ +<?php + +namespace Wikibase\Client\Store; + +use Job; +use JobSpecification; +use Title; +use Wikibase\Client\Usage\EntityUsage; +use Wikibase\Client\WikibaseClient; +use Wikibase\DataModel\Entity\EntityIdParser; +use Wikimedia\Assert\Assert; + +/** + * Job for scheduled invocation of UsageUpdater::addUsagesForPage + * + * @license GPL 2+ + * @author Daniel Kinzler + */ +class AddUsagesForPageJob extends Job { + + /** + * @var integer + */ + private $pageId; + + /** + * @var EntityUsage[] + */ + private $usages; + + /** + * @var string timestamp + */ + private $touched; + + /** + * @var UsageUpdater + */ + private $usageUpdater; + + /** + * @var EntityIdParser $idParser + */ + private $idParser; + + /** + * Spec constructor, for creating JobSpecifications to be pushed to the job queue. + * + * @param Title $title + * @param EntityUsage[] $usages + * @param string $touched + * + * @return JobSpecification + */ + public static function newSpec( Title $title, array $usages, $touched ) { + // NOTE: Map EntityUsage objects to scalar arrays, for JSON serialization in the job queue. + $usages = array_map( function ( EntityUsage $usage ) { + return $usage->asArray(); + }, $usages ); + + $jobParams = array( + 'pageId' => $title->getArticleId(), + 'usages' => $usages, + 'touched' => $touched + ); + + return new JobSpecification( + 'wikibase-addUsagesForPage', + $jobParams, + array( 'removeDuplicates' => true ), + $title + ); + } + + /** + * @param Title $title + * @param array $params + */ + public function __construct( Title $title, array $params ) { + parent::__construct( 'wikibase-addUsagesForPage', $title, $params ); + + Assert::parameter( + isset( $params['pageId'] ) && is_int( $params['pageId'] ) && $params['pageId'] > 0, + '$params["pageId"]', + 'must be a positive integer' ); + + Assert::parameter( + isset( $params['usages'] ) && is_array( $params['usages'] ) && !empty( $params['usages'] ), + '$params["usages"]', + 'must be a non-empty array' ); + + Assert::parameter( + isset( $params['touched'] ) && is_string( $params['touched'] ) && $params['touched'] !== '', + '$params["touched"]', + 'must be a timestamp string' ); + + Assert::parameterElementType( + 'array', + $params['usages'], + '$params["usages"]' ); + + $this->pageId = $params['pageId']; + $this->usages = $params['usages']; + $this->touched = $params['touched']; + + $usageUpdater = WikibaseClient::getDefaultInstance()->getStore()->getUsageUpdater(); + $idParser = WikibaseClient::getDefaultInstance()->getEntityIdParser(); + $this->overrideServices( $usageUpdater, $idParser ); + } + + /** + * Service override for testing + * + * @param UsageUpdater $usageUpdater + * @param EntityIdParser $idParser + */ + public function overrideServices( UsageUpdater $usageUpdater, EntityIdParser $idParser ) { + $this->usageUpdater = $usageUpdater; + $this->idParser = $idParser; + } + + /** + * @return EntityUsage[] + */ + private function getUsages() { + // Turn serialized usage info into EntityUsage objects + $idParser = $this->idParser; + $usages = array_map( function ( array $usageArray ) use ( $idParser ) { + // This is the inverse of EntityUsage::asArray() + return new EntityUsage( + $idParser->parse( $usageArray['entityId'] ), + $usageArray['aspect'], + $usageArray['modifier'] + ); + }, $this->usages ); + + return $usages; + } + + /** + * Call UsageUpdater::addUsagesForPage + * + * @return bool Success + */ + public function run() { + $this->usageUpdater->addUsagesForPage( + $this->pageId, + $this->getUsages(), + $this->touched + ); + } + +} diff --git a/extensions/Wikibase/client/includes/store/ClientStore.php b/extensions/Wikibase/client/includes/store/ClientStore.php index 66b381a..51d052f 100644 --- a/extensions/Wikibase/client/includes/store/ClientStore.php +++ b/extensions/Wikibase/client/includes/store/ClientStore.php @@ -3,6 +3,7 @@ namespace Wikibase; use MWException; +use Wikibase\Client\Store\UsageUpdater; use Wikibase\Client\Usage\SubscriptionManager; use Wikibase\Client\Usage\UsageLookup; use Wikibase\Client\Usage\UsageTracker; @@ -125,4 +126,11 @@ */ public function getEntityPrefetcher(); + /** + * @since 0.5 + * + * @return UsageUpdater + */ + public function getUsageUpdater(); + } diff --git a/extensions/Wikibase/client/includes/store/sql/DirectSqlStore.php b/extensions/Wikibase/client/includes/store/sql/DirectSqlStore.php index 010b1a7..80d42cf 100644 --- a/extensions/Wikibase/client/includes/store/sql/DirectSqlStore.php +++ b/extensions/Wikibase/client/includes/store/sql/DirectSqlStore.php @@ -7,6 +7,7 @@ use Wikibase\Client\Store\Sql\ConsistentReadConnectionManager; use Wikibase\Client\Store\Sql\PagePropsEntityIdLookup; use Wikibase\Client\Store\TitleFactory; +use Wikibase\Client\Store\UsageUpdater; use Wikibase\Client\Usage\NullSubscriptionManager; use Wikibase\Client\Usage\NullUsageTracker; use Wikibase\Client\Usage\SiteLinkUsageLookup; @@ -463,4 +464,17 @@ return $this->entityPrefetcher; } + + /** + * @return UsageUpdater + */ + public function getUsageUpdater() { + return new UsageUpdater( + $this->siteId, + $this->getUsageTracker(), + $this->getUsageLookup(), + $this->getSubscriptionManager() + ); + } + } diff --git a/extensions/Wikibase/client/tests/phpunit/MockClientStore.php b/extensions/Wikibase/client/tests/phpunit/MockClientStore.php index a6aaa73..5a78d04 100644 --- a/extensions/Wikibase/client/tests/phpunit/MockClientStore.php +++ b/extensions/Wikibase/client/tests/phpunit/MockClientStore.php @@ -3,6 +3,7 @@ namespace Wikibase\Test; use Wikibase\ChangesTable; +use Wikibase\Client\Store\UsageUpdater; use Wikibase\Client\Usage\NullSubscriptionManager; use Wikibase\Client\Usage\NullUsageTracker; use Wikibase\Client\Usage\SubscriptionManager; @@ -10,6 +11,7 @@ use Wikibase\Client\Usage\UsageTracker; use Wikibase\ClientStore; use Wikibase\Lib\Store\EntityLookup; +use Wikibase\Lib\Store\EntityPrefetcher; use Wikibase\Lib\Store\EntityRevisionLookup; use Wikibase\Lib\Store\NullEntityPrefetcher; use Wikibase\Lib\Store\SiteLinkLookup; @@ -187,4 +189,18 @@ return new NullEntityPrefetcher(); } + /** + * @since 0.5 + * + * @return UsageUpdater + */ + public function getUsageUpdater() { + return new UsageUpdater( + 'mock', + $this->getUsageTracker(), + $this->getUsageLookup(), + $this->getSubscriptionManager() + ); + } + } diff --git a/extensions/Wikibase/client/tests/phpunit/includes/Hooks/DataUpdateHookHandlersTest.php b/extensions/Wikibase/client/tests/phpunit/includes/Hooks/DataUpdateHookHandlersTest.php index 0e1733e..f2b3976 100644 --- a/extensions/Wikibase/client/tests/phpunit/includes/Hooks/DataUpdateHookHandlersTest.php +++ b/extensions/Wikibase/client/tests/phpunit/includes/Hooks/DataUpdateHookHandlersTest.php @@ -2,6 +2,8 @@ namespace Wikibase\Client\Tests\Hooks; +use JobQueueGroup; +use JobSpecification; use ParserOutput; use Title; use Wikibase\Client\Hooks\DataUpdateHookHandlers; @@ -26,13 +28,20 @@ /** * @param Title $title - * @param EntityUsage[]|null $expectedUsages + * @param array $expectedUsages * @param string|null $touched - * @param bool $prune + * @param bool $prune whether pruneUsagesForPage() should be used + * @param bool $add whether addUsagesForPage() should be used * * @return UsageUpdater */ - private function newUsageUpdater( Title $title, array $expectedUsages = null, $touched = null, $prune = true ) { + private function newUsageUpdater( + Title $title, + array $expectedUsages = null, + $touched = null, + $prune = true, + $add = true + ) { $usageUpdater = $this->getMockBuilder( 'Wikibase\Client\Store\UsageUpdater' ) ->disableOriginalConstructor() ->getMock(); @@ -47,7 +56,7 @@ // greater timestamp. $touchedMatcher = $this->greaterThanOrEqual( $touched ); - if ( $expectedUsages === null ) { + if ( $expectedUsages === null || !$add ) { $usageUpdater->expects( $this->never() ) ->method( 'addUsagesForPage' ); } else { @@ -70,17 +79,87 @@ /** * @param Title $title + * @param array|null $expectedUsages + * @param string|null $touched + * @param bool $useJobQueue whether we expect the job queue to be used + * + * @return JobQueueGroup + */ + private function newJobScheduler( + Title $title, + array $expectedUsages = null, + $touched = null, + $useJobQueue = false + ) { + $jobScheduler = $this->getMockBuilder( 'JobQueueGroup' ) + ->disableOriginalConstructor() + ->getMock(); + + if ( empty( $expectedUsages ) || !$useJobQueue ) { + $jobScheduler->expects( $this->never() ) + ->method( 'lazyPush' ); + } else { + $expectedUsageArray = array_map( function ( EntityUsage $usage ) { + return $usage->asArray(); + }, $expectedUsages ); + + $params = array( + 'jobsByWiki' => array( + wfWikiID() => array( + array( + 'type' => 'wikibase-addUsagesForPage', + 'params' => array( + 'pageId' => $title->getArticleID(), + 'usages' => $expectedUsageArray, + 'touched' => $touched + ), + 'opts' => array( + 'removeDuplicates' => true + ), + 'title' => array( + 'ns' => NS_MAIN, + 'key' => 'Oxygen' + ) + ) + ) + ) + ); + + $jobScheduler->expects( $this->once() ) + ->method( 'lazyPush' ) + ->with( $this->callback( function ( $job ) use ( $params ) { + DataUpdateHookHandlersTest::assertEquals( 'enqueue', $job->getType() ); + DataUpdateHookHandlersTest::assertEquals( $params, $job->getParams() ); + return true; + } ) ); + + } + + return $jobScheduler; + } + + /** + * @param Title $title * @param EntityUsage[]|null $expectedUsages * @param string|null $touched timestamp - * @param bool $prune + * @param bool $prune whether pruneUsagesForPage() should be used + * @param bool $asyncAdd whether addUsagesForPage() should be called via the job queue * * @return DataUpdateHookHandlers */ - private function newDataUpdateHookHandlers( Title $title, array $expectedUsages = null, $touched = null, $prune = true ) { - $usageUpdater = $this->newUsageUpdater( $title, $expectedUsages, $touched, $prune ); + private function newDataUpdateHookHandlers( + Title $title, + array $expectedUsages = null, + $touched = null, + $prune = true, + $asyncAdd = false + ) { + $usageUpdater = $this->newUsageUpdater( $title, $expectedUsages, $touched, $prune, !$asyncAdd ); + $jobScheduler = $this->newJobScheduler( $title, $expectedUsages, $touched, $asyncAdd ); return new DataUpdateHookHandlers( - $usageUpdater + $usageUpdater, + $jobScheduler ); } @@ -164,7 +243,7 @@ $linksUpdate = $this->newLinksUpdate( $title, $usage, $timestamp ); // Assertions are done by the UsageUpdater mock - $handler = $this->newDataUpdateHookHandlers( $title, $usage, $timestamp, true ); + $handler = $this->newDataUpdateHookHandlers( $title, $usage, $timestamp, true, false ); $handler->doLinksUpdateComplete( $linksUpdate ); } @@ -178,7 +257,7 @@ $parserOutput = $this->newParserOutput( $usage, $timestamp ); // Assertions are done by the UsageUpdater mock - $handler = $this->newDataUpdateHookHandlers( $title, $usage, $timestamp, false ); + $handler = $this->newDataUpdateHookHandlers( $title, $usage, $timestamp, false, true ); $handler->doParserCacheSaveComplete( $parserOutput, $title ); } @@ -188,7 +267,7 @@ $timestamp = '20150505000000'; // Assertions are done by the UsageUpdater mock - $handler = $this->newDataUpdateHookHandlers( $title, null, $timestamp, true ); + $handler = $this->newDataUpdateHookHandlers( $title, null, $timestamp, true, false ); $handler->doArticleDeleteComplete( $title->getNamespace(), $title->getArticleID(), $timestamp ); } diff --git a/extensions/Wikibase/client/tests/phpunit/includes/Usage/EntityUsageTest.php b/extensions/Wikibase/client/tests/phpunit/includes/Usage/EntityUsageTest.php index 1a20b61..48f9bc6 100644 --- a/extensions/Wikibase/client/tests/phpunit/includes/Usage/EntityUsageTest.php +++ b/extensions/Wikibase/client/tests/phpunit/includes/Usage/EntityUsageTest.php @@ -55,6 +55,25 @@ $this->assertEquals( "$aspect.$modifier", $usage->getAspectKey() ); } + public function testAsArray() { + $id = new ItemId( 'Q7' ); + $aspect = EntityUsage::LABEL_USAGE; + $modifier = 'ru'; + + $expected = array( + 'entityId' => $id->getSerialization(), + 'aspect' => $aspect, + 'modifier' => null + ); + + $usage = new EntityUsage( $id, $aspect ); + $this->assertEquals( $expected, $usage->asArray() ); + + $expected['modifier'] = $modifier; + $usage = new EntityUsage( $id, $aspect, $modifier ); + $this->assertEquals( $expected, $usage->asArray() ); + } + public function aspectKeyProvider() { return array( array( 'L', array( 'L', null ) ), diff --git a/extensions/Wikibase/client/tests/phpunit/includes/store/AddUsagesForPageJobTest.php b/extensions/Wikibase/client/tests/phpunit/includes/store/AddUsagesForPageJobTest.php new file mode 100644 index 0000000..cb15661 --- /dev/null +++ b/extensions/Wikibase/client/tests/phpunit/includes/store/AddUsagesForPageJobTest.php @@ -0,0 +1,144 @@ +<?php + +namespace Wikibase\Client\Test\Store; + +use Title; +use Wikibase\Client\Store\AddUsagesForPageJob; +use Wikibase\Client\Usage\EntityUsage; +use Wikibase\DataModel\Entity\BasicEntityIdParser; +use Wikibase\DataModel\Entity\ItemId; + +/** + * @covers Wikibase\Client\Store\AddUsagesForPageJob + * + * @group Wikibase + * @group WikibaseClient + * @group WikibaseUsageTracking + * + * @licence GNU GPL v2+ + * @author Daniel Kinzler + */ +class AddUsagesForPageJobTest extends \PHPUnit_Framework_TestCase { + + public function provideConstructor_failure() { + $pageId = 17; + $usageQ5X = new EntityUsage( new ItemId( 'Q5' ), 'X' ); + $usages = array( $usageQ5X->asArray() ); + $touched = '20150101000000'; + + return array( + 'empty' => array( array() ), + + '$pageId is missing' => array( array( + 'usages' => $usages, + 'touched' => $touched, + ) ), + '$pageId is not an int' => array( array( + 'pageId' => 'foo', + 'usages' => $usages, + 'touched' => $touched, + ) ), + '$pageId is zero' => array( array( + 'pageId' => 0, + 'usages' => $usages, + 'touched' => $touched, + ) ), + + '$usages is missing' => array( array( + 'pageId' => $pageId, + 'touched' => $touched, + ) ), + '$usages is not an array' => array( array( + 'pageId' => $pageId, + 'usages' => 'xxx', + 'touched' => $touched, + ) ), + '$usages is empty' => array( array( + 'pageId' => $pageId, + 'usages' => array(), + 'touched' => $touched, + ) ), + '$usages contains crap' => array( array( + 'pageId' => $pageId, + 'usages' => array( 1, 2, 3 ), + 'touched' => $touched, + ) ), + + '$touched is missing' => array( array( + 'pageId' => $pageId, + 'usages' => $usages, + ) ), + '$touched is not a string' => array( array( + 'pageId' => $pageId, + 'usages' => $usages, + 'touched' => 23, + ) ), + '$touched is empty' => array( array( + 'pageId' => $pageId, + 'usages' => $usages, + 'touched' => '', + ) ), + ); + } + + /** + * @dataProvider provideConstructor_failure + * @param array $params + */ + public function testConstructor_failure( $params ) { + $this->setExpectedException( 'InvalidArgumentException' ); + + $title = Title::makeTitle( NS_MAIN, 'Foo' ); + new AddUsagesForPageJob( $title, $params ); + } + + public function testRun() { + $usageQ5X = new EntityUsage( new ItemId( 'Q5' ), 'X' ); + $params = array( + 'pageId' => 17, + 'usages' => array( $usageQ5X->asArray() ), + 'touched' => '20150101000000', + ); + + $usageUpdater = $this->getMockBuilder( 'Wikibase\Client\Store\UsageUpdater' ) + ->disableOriginalConstructor() + ->getMock(); + + $usageUpdater->expects( $this->once() ) + ->method( 'addUsagesForPage' ) + ->with( + $params['pageId'], + array( $usageQ5X ), + $params['touched'] + ); + + $title = Title::makeTitle( NS_MAIN, 'Foo' ); + $job = new AddUsagesForPageJob( $title, $params ); + $job->overrideServices( $usageUpdater, new BasicEntityIdParser() ); + + $job->run(); + } + + public function testNewSpec() { + $usageQ5X = new EntityUsage( new ItemId( 'Q5' ), 'X' ); + + $title = Title::makeTitle( NS_MAIN, 'Foo' ); + $title->resetArticleID( 17 ); + + $touched = '20150101000000'; + $usages = array( $usageQ5X ); + + $spec = AddUsagesForPageJob::newSpec( $title, $usages, $touched ); + + $params = array( + 'pageId' => $title->getArticleID(), + 'usages' => array( $usageQ5X->asArray() ), + 'touched' => '20150101000000', + ); + + $this->assertEquals( 'wikibase-addUsagesForPage', $spec->getType() ); + $this->assertEquals( $title->getFullText(), $spec->getTitle()->getFullText() ); + $this->assertEquals( $params, $spec->getParams() ); + } + +} diff --git a/extensions/Wikibase/composer.json b/extensions/Wikibase/composer.json index 87885c5..943f9fd 100644 --- a/extensions/Wikibase/composer.json +++ b/extensions/Wikibase/composer.json @@ -37,9 +37,9 @@ "wikibase/data-model": "~3.0", "wikibase/data-model-javascript": "^1.0.2", - "wikibase/data-model-serialization": "~1.4", + "wikibase/data-model-serialization": "~1.4.0", "wikibase/javascript-api": "~1.0", - "wikibase/internal-serialization": "~1.4", + "wikibase/internal-serialization": "~1.4.0", "wikibase/serialization-javascript": "~2.0", "diff/diff": "~2.0|~1.0" diff --git a/extensions/Wikibase/package.json b/extensions/Wikibase/package.json index 2e22208..43022cd 100644 --- a/extensions/Wikibase/package.json +++ b/extensions/Wikibase/package.json @@ -11,7 +11,7 @@ "author": "The Wikidata team", "license": "GPL-2.0+", "devDependencies": { - "jscs": "", + "jscs": ">=2.0", "jshint": "" } } diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 9ac65da..d5d4c23 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -369,11 +369,13 @@ 'Wikibase\\Client\\RepoItemLinkGenerator' => $baseDir . '/extensions/Wikibase/client/includes/RepoItemLinkGenerator.php', 'Wikibase\\Client\\RepoLinker' => $baseDir . '/extensions/Wikibase/client/includes/RepoLinker.php', 'Wikibase\\Client\\Specials\\SpecialUnconnectedPages' => $baseDir . '/extensions/Wikibase/client/includes/specials/SpecialUnconnectedPages.php', + 'Wikibase\\Client\\Store\\AddUsagesForPageJob' => $baseDir . '/extensions/Wikibase/client/includes/store/AddUsagesForPageJob.php', 'Wikibase\\Client\\Store\\Sql\\BulkSubscriptionUpdater' => $baseDir . '/extensions/Wikibase/client/includes/store/sql/BulkSubscriptionUpdater.php', 'Wikibase\\Client\\Store\\Sql\\ConsistentReadConnectionManager' => $baseDir . '/extensions/Wikibase/client/includes/store/sql/ConsistentReadConnectionManager.php', 'Wikibase\\Client\\Store\\Sql\\PagePropsEntityIdLookup' => $baseDir . '/extensions/Wikibase/client/includes/store/sql/PagePropsEntityIdLookup.php', 'Wikibase\\Client\\Store\\TitleFactory' => $baseDir . '/extensions/Wikibase/client/includes/store/TitleFactory.php', 'Wikibase\\Client\\Store\\UsageUpdater' => $baseDir . '/extensions/Wikibase/client/includes/store/UsageUpdater.php', + 'Wikibase\\Client\\Test\\Store\\AddUsagesForPageJobTest' => $baseDir . '/extensions/Wikibase/client/tests/phpunit/includes/store/AddUsagesForPageJobTest.php', 'Wikibase\\Client\\Test\\Store\\UsageUpdaterTest' => $baseDir . '/extensions/Wikibase/client/tests/phpunit/includes/UsageUpdaterTest.php', 'Wikibase\\Client\\Test\\Usage\\UsageTrackingIntegrationTest' => $baseDir . '/extensions/Wikibase/client/tests/phpunit/includes/Usage/UsageTrackingIntegrationTest.php', 'Wikibase\\Client\\Tests\\Api\\ApiClientInfoTest' => $baseDir . '/extensions/Wikibase/client/tests/phpunit/includes/api/ApiClientInfoTest.php', diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 2669187..0876d75 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -1420,12 +1420,12 @@ "source": { "type": "git", "url": "https://github.com/wikimedia/mediawiki-extensions-Wikibase.git", - "reference": "2498f76188fe714f19fdd68ca89a6ac34ecd4e4c" + "reference": "7ed9b2a65f6ff6be7293d796b01e597dd9164c81" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wikimedia/mediawiki-extensions-Wikibase/zipball/2498f76188fe714f19fdd68ca89a6ac34ecd4e4c", - "reference": "2498f76188fe714f19fdd68ca89a6ac34ecd4e4c", + "url": "https://api.github.com/repos/wikimedia/mediawiki-extensions-Wikibase/zipball/7ed9b2a65f6ff6be7293d796b01e597dd9164c81", + "reference": "7ed9b2a65f6ff6be7293d796b01e597dd9164c81", "shasum": "" }, "require": { @@ -1444,8 +1444,8 @@ "php": ">=5.3.2", "wikibase/data-model": "~3.0", "wikibase/data-model-javascript": "^1.0.2", - "wikibase/data-model-serialization": "~1.4", - "wikibase/internal-serialization": "~1.4", + "wikibase/data-model-serialization": "~1.4.0", + "wikibase/internal-serialization": "~1.4.0", "wikibase/javascript-api": "~1.0", "wikibase/serialization-javascript": "~2.0" }, @@ -1455,7 +1455,7 @@ "require-dev": { "squizlabs/php_codesniffer": "~2.1" }, - "time": "2015-07-23 01:52:42", + "time": "2015-07-29 02:42:33", "type": "mediawiki-extension", "installation-source": "dist", "autoload": { -- To view, visit https://gerrit.wikimedia.org/r/227691 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I33a392262eb58778a01e0e05d6c9815429ab67f7 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/Wikidata Gerrit-Branch: wmf/1.26wmf13 Gerrit-Owner: Aude <aude.w...@gmail.com> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits