Aude has uploaded a new change for review. https://gerrit.wikimedia.org/r/227694
Change subject: Update Wikidata - add usage tracking job ...................................................................... Update Wikidata - add usage tracking job Change-Id: Ic377cad074a2fb53954f7db320b558b2ebc90098 --- 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/package.json M extensions/Wikibase/view/resources/jquery/wikibase/jquery.wikibase.labelview.js M extensions/Wikibase/view/tests/qunit/jquery/wikibase/jquery.wikibase.entitytermsforlanguageview.tests.js M extensions/Wikibase/view/tests/qunit/jquery/wikibase/jquery.wikibase.labelview.tests.js M vendor/composer/autoload_classmap.php M vendor/composer/installed.json 18 files changed, 618 insertions(+), 151 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Wikidata refs/changes/94/227694/1 diff --git a/composer.lock b/composer.lock index 052b834..b9eb7f6 100644 --- a/composer.lock +++ b/composer.lock @@ -862,7 +862,7 @@ "version": "dev-master", "source": { "type": "git", - "url": "g...@github.com:wmde/Wikidata.org.git", + "url": "https://github.com/wmde/Wikidata.org.git", "reference": "9029d28d8aaeb65cd1338865183740b4be445fa5" }, "dist": { @@ -1276,7 +1276,7 @@ "support": { "issues": "https://phabricator.wikimedia.org/project/profile/989/" }, - "time": "2015-06-30 17:53:04" + "time": "2015-06-30 13:02:17" }, { "name": "wikibase/serialization-javascript", @@ -1325,7 +1325,7 @@ "source": { "type": "git", "url": "https://gerrit.wikimedia.org/r/p/mediawiki/extensions/Wikibase.git", - "reference": "c81228d60966244c1f2db8623191a2ea28cbfe81" + "reference": "b73bc168435a1214956543d94ccfc00e8902a566" }, "require": { "data-values/common": "~0.2.0", @@ -1403,14 +1403,14 @@ "issues": "https://phabricator.wikimedia.org/", "irc": "irc://irc.freenode.net/wikidata" }, - "time": "2015-07-27 19:36:55" + "time": "2015-07-29 02:00:56" }, { "name": "wikibase/wikimedia-badges", "version": "dev-master", "source": { "type": "git", - "url": "g...@github.com:wmde/WikimediaBadges.git", + "url": "https://github.com/wmde/WikimediaBadges.git", "reference": "5cb28488f7791302cf6b835610f132b0b05915cf" }, "dist": { 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 6fb840c..9a9ae33 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__, @@ -121,6 +122,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 dc4f311..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; @@ -464,4 +465,16 @@ 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 1cef882..edf4f11 100644 --- a/extensions/Wikibase/client/tests/phpunit/MockClientStore.php +++ b/extensions/Wikibase/client/tests/phpunit/MockClientStore.php @@ -3,9 +3,13 @@ namespace Wikibase\Test; use Wikibase\ChangesTable; +use Wikibase\Client\Store\UsageUpdater; use Wikibase\Client\Usage\NullSubscriptionManager; use Wikibase\Client\Usage\NullUsageTracker; 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\PropertyInfoStore; use Wikibase\Store\EntityIdLookup; @@ -181,4 +185,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 744489f..ac761a8 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/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/extensions/Wikibase/view/resources/jquery/wikibase/jquery.wikibase.labelview.js b/extensions/Wikibase/view/resources/jquery/wikibase/jquery.wikibase.labelview.js index 3564a36..0715d96 100644 --- a/extensions/Wikibase/view/resources/jquery/wikibase/jquery.wikibase.labelview.js +++ b/extensions/Wikibase/view/resources/jquery/wikibase/jquery.wikibase.labelview.js @@ -38,7 +38,6 @@ $entityId: '.wikibase-labelview-entityid' }, value: null, - inputNodeName: 'TEXTAREA', helpMessage: mw.msg( 'wikibase-label-input-help-message' ), entityId: null, showEntityId: false @@ -55,7 +54,6 @@ !( this.options.value instanceof wb.datamodel.Term ) || !this.options.entityId || !this.options.labelsChanger - || this.options.inputNodeName !== 'INPUT' && this.options.inputNodeName !== 'TEXTAREA' ) { throw new Error( 'Required option not specified properly' ); } @@ -144,7 +142,7 @@ return deferred.resolve().promise(); } - var $input = $( document.createElement( this.options.inputNodeName ) ); + var $input = $( '<input />' ); $input .addClass( this.widgetFullName + '-input' ) @@ -156,11 +154,6 @@ ) .attr( 'lang', languageCode ) .attr( 'dir', $.util.getDirectionality( languageCode ) ) - .on( 'keydown.' + this.widgetName, function( event ) { - if( event.keyCode === $.ui.keyCode.ENTER ) { - event.preventDefault(); - } - } ) .on( 'eachchange.' + this.widgetName, function( event ) { self._trigger( 'change' ); } ); @@ -170,10 +163,7 @@ } if( $.fn.inputautoexpand ) { - $input.inputautoexpand( { - expandHeight: true, - suppressNewLine: true - } ); + $input.inputautoexpand(); } this.$text.empty().append( $input ); @@ -203,7 +193,7 @@ */ _afterStopEditing: function( dropValue ) { if( dropValue && this.options.value.getText() === '' ) { - this.$text.children( '.' + this.widgetFullName + '-input' ).val( '' ); + this.$text.children( 'input' ).val( '' ); } return PARENT.prototype._afterStopEditing.call( this, dropValue ); }, @@ -237,7 +227,7 @@ var response = PARENT.prototype._setOption.call( this, key, value ); if( key === 'disabled' && this.isInEditMode() ) { - this.$text.children( '.' + this.widgetFullName + '-input' ).prop( 'disabled', value ); + this.$text.children( 'input' ).prop( 'disabled', value ); } return response; @@ -261,7 +251,7 @@ return new wb.datamodel.Term( this.options.value.getLanguageCode(), - $.trim( this.$text.children( '.' + this.widgetFullName + '-input' ).val() ) + $.trim( this.$text.children( 'input' ).val() ) ); }, @@ -270,7 +260,7 @@ */ focus: function() { if( this.isInEditMode() ) { - this.$text.children( '.' + this.widgetFullName + '-input' ).focus(); + this.$text.children( 'input' ).focus(); } else { this.element.focus(); } diff --git a/extensions/Wikibase/view/tests/qunit/jquery/wikibase/jquery.wikibase.entitytermsforlanguageview.tests.js b/extensions/Wikibase/view/tests/qunit/jquery/wikibase/jquery.wikibase.entitytermsforlanguageview.tests.js index 860f792..52be41b 100644 --- a/extensions/Wikibase/view/tests/qunit/jquery/wikibase/jquery.wikibase.entitytermsforlanguageview.tests.js +++ b/extensions/Wikibase/view/tests/qunit/jquery/wikibase/jquery.wikibase.entitytermsforlanguageview.tests.js @@ -196,7 +196,7 @@ } ); addToQueue( $queue, function() { - entitytermsforlanguageview.$label.find( 'input, textarea' ).val( '' ); + entitytermsforlanguageview.$label.find( 'input' ).val( '' ); entitytermsforlanguageview.stopEditing(); } ); @@ -224,14 +224,14 @@ 'Verified isInitialValue() returning true.' ); - entitytermsforlanguageview.$label.find( 'input, textarea' ).val( 'changed' ); + entitytermsforlanguageview.$label.find( 'input' ).val( 'changed' ); assert.ok( !entitytermsforlanguageview.isInitialValue(), 'Verified isInitialValue() returning false after changing value.' ); - entitytermsforlanguageview.$label.find( 'input, textarea' ).val( 'test label' ); + entitytermsforlanguageview.$label.find( 'input' ).val( 'test label' ); assert.ok( entitytermsforlanguageview.isInitialValue(), diff --git a/extensions/Wikibase/view/tests/qunit/jquery/wikibase/jquery.wikibase.labelview.tests.js b/extensions/Wikibase/view/tests/qunit/jquery/wikibase/jquery.wikibase.labelview.tests.js index 0b3cf33..9ee4b1a 100644 --- a/extensions/Wikibase/view/tests/qunit/jquery/wikibase/jquery.wikibase.labelview.tests.js +++ b/extensions/Wikibase/view/tests/qunit/jquery/wikibase/jquery.wikibase.labelview.tests.js @@ -98,7 +98,7 @@ labelview.startEditing(); assert.ok( - labelview.$text.find( 'textarea' ).length === 1, + labelview.$text.find( 'input' ).length === 1, 'Generated input element.' ); @@ -108,7 +108,7 @@ labelview.stopEditing(); // should not trigger event labelview.startEditing(); - labelview.$text.find( 'textarea' ).val( '' ); + labelview.$text.find( 'input' ).val( '' ); labelview.stopEditing(); } ); @@ -124,14 +124,14 @@ 'Verified isInitialValue() returning true.' ); - labelview.$text.find( 'textarea' ).val( 'changed' ); + labelview.$text.find( 'input' ).val( 'changed' ); assert.ok( !labelview.isInitialValue(), 'Verified isInitialValue() returning false after changing value.' ); - labelview.$text.find( 'textarea' ).val( 'test label' ); + labelview.$text.find( 'input' ).val( 'test label' ); assert.ok( labelview.isInitialValue(), diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 25ace3b..7cf72f7 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -331,11 +331,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 8691f68..048eaa1 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -1159,100 +1159,12 @@ ] }, { - "name": "wikibase/wikibase", - "version": "dev-wmf/1.26wmf16", - "version_normalized": "dev-wmf/1.26wmf16", - "source": { - "type": "git", - "url": "https://gerrit.wikimedia.org/r/p/mediawiki/extensions/Wikibase.git", - "reference": "c81228d60966244c1f2db8623191a2ea28cbfe81" - }, - "require": { - "data-values/common": "~0.2.0", - "data-values/data-types": "~0.4.0", - "data-values/data-values": "~1.0.0", - "data-values/geo": "~1.0", - "data-values/interfaces": "^0.1.5", - "data-values/javascript": "~0.6.0|~0.7.0", - "data-values/number": "~0.5.0", - "data-values/serialization": "~1.0", - "data-values/time": "~0.8.0", - "data-values/validators": "~0.1.0", - "data-values/value-view": "~0.14.0", - "diff/diff": "~2.0|~1.0", - "php": ">=5.3.2", - "wikibase/data-model": "~3.0", - "wikibase/data-model-javascript": "^1.0.2", - "wikibase/data-model-serialization": "~1.7", - "wikibase/internal-serialization": "~1.4", - "wikibase/javascript-api": "~1.0", - "wikibase/serialization-javascript": "~2.0" - }, - "conflict": { - "mediawiki/mediawiki": "<1.23" - }, - "require-dev": { - "squizlabs/php_codesniffer": "~2.1" - }, - "time": "2015-07-27 19:36:55", - "type": "mediawiki-extension", - "installation-source": "source", - "autoload": { - "classmap": [ - "client/includes/", - "client/WikibaseClient.hooks.php", - "client/tests/phpunit/", - "lib/includes/", - "lib/WikibaseLib.hooks.php", - "lib/tests/phpunit/", - "repo/includes/", - "repo/maintenance/", - "repo/tests/phpunit/", - "repo/Wikibase.hooks.php" - ], - "psr-4": { - "Wikibase\\View\\": "view/src", - "Wikimedia\\Purtle\\": "purtle/src", - "Wikimedia\\Purtle\\Tests\\": "purtle/tests/phpunit" - } - }, - "scripts": { - "test": [ - "composer phpcs" - ], - "phpcs": [ - "vendor/bin/phpcs -sp --standard=phpcs.xml --extensions=php --ignore=extensions/ValueView,vendor ." - ] - }, - "license": [ - "GPL-2.0+" - ], - "authors": [ - { - "name": "The Wikidata team" - } - ], - "description": "Structured data repository for MediaWiki", - "homepage": "http://wikiba.se", - "keywords": [ - "wikibase", - "wikibaseclient", - "wikibaselib", - "wikibaserepo", - "wikidata" - ], - "support": { - "issues": "https://phabricator.wikimedia.org/", - "irc": "irc://irc.freenode.net/wikidata" - } - }, - { "name": "wikibase/wikimedia-badges", "version": "dev-master", "version_normalized": "9999999-dev", "source": { "type": "git", - "url": "g...@github.com:wmde/WikimediaBadges.git", + "url": "https://github.com/wmde/WikimediaBadges.git", "reference": "5cb28488f7791302cf6b835610f132b0b05915cf" }, "dist": { @@ -1302,7 +1214,7 @@ "version_normalized": "9999999-dev", "source": { "type": "git", - "url": "g...@github.com:wmde/Wikidata.org.git", + "url": "https://github.com/wmde/Wikidata.org.git", "reference": "9029d28d8aaeb65cd1338865183740b4be445fa5" }, "dist": { @@ -1497,5 +1409,93 @@ "wikibase", "wikidata" ] + }, + { + "name": "wikibase/wikibase", + "version": "dev-wmf/1.26wmf16", + "version_normalized": "dev-wmf/1.26wmf16", + "source": { + "type": "git", + "url": "https://gerrit.wikimedia.org/r/p/mediawiki/extensions/Wikibase.git", + "reference": "b73bc168435a1214956543d94ccfc00e8902a566" + }, + "require": { + "data-values/common": "~0.2.0", + "data-values/data-types": "~0.4.0", + "data-values/data-values": "~1.0.0", + "data-values/geo": "~1.0", + "data-values/interfaces": "^0.1.5", + "data-values/javascript": "~0.6.0|~0.7.0", + "data-values/number": "~0.5.0", + "data-values/serialization": "~1.0", + "data-values/time": "~0.8.0", + "data-values/validators": "~0.1.0", + "data-values/value-view": "~0.14.0", + "diff/diff": "~2.0|~1.0", + "php": ">=5.3.2", + "wikibase/data-model": "~3.0", + "wikibase/data-model-javascript": "^1.0.2", + "wikibase/data-model-serialization": "~1.7", + "wikibase/internal-serialization": "~1.4", + "wikibase/javascript-api": "~1.0", + "wikibase/serialization-javascript": "~2.0" + }, + "conflict": { + "mediawiki/mediawiki": "<1.23" + }, + "require-dev": { + "squizlabs/php_codesniffer": "~2.1" + }, + "time": "2015-07-28 17:31:42", + "type": "mediawiki-extension", + "installation-source": "source", + "autoload": { + "classmap": [ + "client/includes/", + "client/WikibaseClient.hooks.php", + "client/tests/phpunit/", + "lib/includes/", + "lib/WikibaseLib.hooks.php", + "lib/tests/phpunit/", + "repo/includes/", + "repo/maintenance/", + "repo/tests/phpunit/", + "repo/Wikibase.hooks.php" + ], + "psr-4": { + "Wikibase\\View\\": "view/src", + "Wikimedia\\Purtle\\": "purtle/src", + "Wikimedia\\Purtle\\Tests\\": "purtle/tests/phpunit" + } + }, + "scripts": { + "test": [ + "composer phpcs" + ], + "phpcs": [ + "vendor/bin/phpcs -sp --standard=phpcs.xml --extensions=php --ignore=extensions/ValueView,vendor ." + ] + }, + "license": [ + "GPL-2.0+" + ], + "authors": [ + { + "name": "The Wikidata team" + } + ], + "description": "Structured data repository for MediaWiki", + "homepage": "http://wikiba.se", + "keywords": [ + "wikibase", + "wikibaseclient", + "wikibaselib", + "wikibaserepo", + "wikidata" + ], + "support": { + "issues": "https://phabricator.wikimedia.org/", + "irc": "irc://irc.freenode.net/wikidata" + } } ] -- To view, visit https://gerrit.wikimedia.org/r/227694 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Ic377cad074a2fb53954f7db320b558b2ebc90098 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/Wikidata Gerrit-Branch: wmf/1.26wmf16 Gerrit-Owner: Aude <aude.w...@gmail.com> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits