WMDE-leszek has uploaded a new change for review.
https://gerrit.wikimedia.org/r/278283
Change subject: Use WatchedItemStore in ApiQueryInfo::getWatchedInfo
......................................................................
Use WatchedItemStore in ApiQueryInfo::getWatchedInfo
Adds a method for getting WatchedItem objects for a batch of
LinkTargets.
Bug: T129482
Change-Id: I1f84212e7879a84b34bb3b53859069fcea282bba
---
M includes/WatchedItemStore.php
M includes/api/ApiQueryInfo.php
M tests/phpunit/includes/WatchedItemStoreIntegrationTest.php
M tests/phpunit/includes/WatchedItemStoreUnitTest.php
4 files changed, 287 insertions(+), 17 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core
refs/changes/83/278283/1
diff --git a/includes/WatchedItemStore.php b/includes/WatchedItemStore.php
index 4e2dfa5..deeaf79 100644
--- a/includes/WatchedItemStore.php
+++ b/includes/WatchedItemStore.php
@@ -491,6 +491,61 @@
}
/**
+ * Get items for targets
+ *
+ * @param User $user
+ * @param LinkTarget[] $targets
+ *
+ * @return WatchedItem[]
+ */
+ public function getWatchedItems( User $user, array $targets ) {
+ if ( $user->isAnon() ) {
+ return [];
+ }
+
+ $watchedItems = [];
+ $targetsToLoad = [];
+ foreach ( $targets as $target ) {
+ $cachedItem = $this->getCached( $user, $target );
+ if ( $cachedItem ) {
+ $watchedItems[] = $cachedItem;
+ } else {
+ $targetsToLoad[] = $target;
+ }
+ }
+
+ if ( !$targetsToLoad ) {
+ return $watchedItems;
+ }
+
+ $dbr = $this->getConnection( DB_SLAVE );
+
+ $lb = new LinkBatch( $targetsToLoad );
+ $res = $dbr->select(
+ 'watchlist',
+ [ 'wl_namespace', 'wl_title',
'wl_notificationtimestamp' ],
+ [
+ $lb->constructSet( 'wl', $dbr ),
+ 'wl_user' => $user->getId(),
+ ],
+ __METHOD__
+ );
+ $this->reuseConnection( $dbr );
+
+ foreach ( $res as $row ) {
+ $item = new WatchedItem(
+ $user,
+ new TitleValue( (int)$row->wl_namespace,
$row->wl_title ),
+ $row->wl_notificationtimestamp
+ );
+ $this->cache( $item );
+ $watchedItems[] = $item;
+ }
+
+ return $watchedItems;
+ }
+
+ /**
* Must be called separately for Subject & Talk namespaces
*
* @param User $user
diff --git a/includes/api/ApiQueryInfo.php b/includes/api/ApiQueryInfo.php
index 2d382dd..62cdcc9 100644
--- a/includes/api/ApiQueryInfo.php
+++ b/includes/api/ApiQueryInfo.php
@@ -758,28 +758,17 @@
$this->watched = [];
$this->notificationtimestamps = [];
- $db = $this->getDB();
- $lb = new LinkBatch( $this->everything );
+ $items =
WatchedItemStore::getDefaultInstance()->getWatchedItems( $user,
$this->everything );
- $this->resetQueryParams();
- $this->addTables( [ 'watchlist' ] );
- $this->addFields( [ 'wl_title', 'wl_namespace' ] );
- $this->addFieldsIf( 'wl_notificationtimestamp',
$this->fld_notificationtimestamp );
- $this->addWhere( [
- $lb->constructSet( 'wl', $db ),
- 'wl_user' => $user->getId()
- ] );
-
- $res = $this->select( __METHOD__ );
-
- foreach ( $res as $row ) {
+ foreach ( $items as $item ) {
+ $target = $item->getLinkTarget();
if ( $this->fld_watched ) {
-
$this->watched[$row->wl_namespace][$row->wl_title] = true;
+
$this->watched[$target->getNamespace()][$target->getDBkey()] = true;
}
if ( $this->fld_notificationtimestamp ) {
-
$this->notificationtimestamps[$row->wl_namespace][$row->wl_title] =
- $row->wl_notificationtimestamp;
+
$this->notificationtimestamps[$target->getNamespace()][$target->getDBkey()] =
+ $item->getNotificationTimestamp();
}
}
}
diff --git a/tests/phpunit/includes/WatchedItemStoreIntegrationTest.php
b/tests/phpunit/includes/WatchedItemStoreIntegrationTest.php
index 0dea461..3268bf7 100644
--- a/tests/phpunit/includes/WatchedItemStoreIntegrationTest.php
+++ b/tests/phpunit/includes/WatchedItemStoreIntegrationTest.php
@@ -52,6 +52,11 @@
[ 0 => [ 'WatchedItemStoreIntegrationTestPage' => 0 ] ],
$store->countWatchersMultiple( [ $title ], [
'minimumWatchers' => $initialWatchers + 2 ] )
);
+ $watchedItems = $store->getWatchedItems( $user, [ $title ] );
+ $this->assertCount( 1, $watchedItems );
+ $this->assertEquals( $user, $watchedItems[0]->getUser() );
+ $this->assertEquals( $title->getDBkey(),
$watchedItems[0]->getLinkTarget()->getDBkey() );
+ $this->assertEquals( $title->getNamespace(),
$watchedItems[0]->getLinkTarget()->getNamespace() );
$store->removeWatch( $user, $title );
$this->assertFalse(
@@ -64,6 +69,8 @@
$initialWatchers,
$store->countWatchersMultiple( [ $title ]
)[$title->getNamespace()][$title->getDBkey()]
);
+ $watchedItems = $store->getWatchedItems( $user, [ $title ] );
+ $this->assertEmpty( $watchedItems );
}
public function testUpdateAndResetNotificationTimestamp() {
diff --git a/tests/phpunit/includes/WatchedItemStoreUnitTest.php
b/tests/phpunit/includes/WatchedItemStoreUnitTest.php
index 869b0d2..6557869 100644
--- a/tests/phpunit/includes/WatchedItemStoreUnitTest.php
+++ b/tests/phpunit/includes/WatchedItemStoreUnitTest.php
@@ -1380,6 +1380,225 @@
);
}
+ public function testGetWatchedItems() {
+ $targets = [
+ new TitleValue( 0, 'SomeDbKey' ),
+ new TitleValue( 1, 'AnotherDbKey' ),
+ ];
+
+ $mockDb = $this->getMockDb();
+ $dbResult = [
+ $this->getFakeRow( [
+ 'wl_namespace' => 0,
+ 'wl_title' => 'SomeDbKey',
+ 'wl_notificationtimestamp' => '20151212010101'
+ ] ),
+ $this->getFakeRow(
+ [ 'wl_namespace' => 1, 'wl_title' =>
'AnotherDbKey', 'wl_notificationtimestamp' => null ]
+ ),
+ ];
+
+ $mockDb->expects( $this->once() )
+ ->method( 'makeWhereFrom2d' )
+ ->with(
+ [ [ 'SomeDbKey' => 1 ], [ 'AnotherDbKey' => 1 ]
],
+ $this->isType( 'string' ),
+ $this->isType( 'string' )
+ )
+ ->will( $this->returnValue( 'makeWhereFrom2d return
value' ) );
+ $mockDb->expects( $this->once() )
+ ->method( 'select' )
+ ->with(
+ 'watchlist',
+ [ 'wl_namespace', 'wl_title',
'wl_notificationtimestamp' ],
+ [
+ 'makeWhereFrom2d return value',
+ 'wl_user' => 1
+ ],
+ $this->isType( 'string' )
+ )
+ ->will( $this->returnValue( $dbResult ) );
+
+ $mockCache = $this->getMockCache();
+ $mockCache->expects( $this->exactly( 2 ) )
+ ->method( 'get' )
+ ->withConsecutive(
+ [ '0:SomeDbKey:1' ],
+ [ '1:AnotherDbKey:1' ]
+ )
+ ->will( $this->returnValue( null ) );
+ $mockCache->expects( $this->exactly( 2 ) )
+ ->method( 'set' )
+ ->withConsecutive(
+ [ '0:SomeDbKey:1' ],
+ [ '1:AnotherDbKey:1' ]
+ );
+ $mockCache->expects( $this->never() )->method( 'delete' );
+
+ $store = new WatchedItemStore(
+ $this->getMockLoadBalancer( $mockDb ),
+ $mockCache
+ );
+ $user = $this->getMockNonAnonUserWithId( 1 );
+
+ $watchedItems = $store->getWatchedItems(
+ $user,
+ $targets
+ );
+
+ $this->assertInternalType( 'array', $watchedItems );
+ $this->assertCount( 2, $watchedItems );
+
+ $this->assertEquals(
+ new WatchedItem( $user, $targets[0], '20151212010101' ),
+ $watchedItems[0]
+ );
+
+ $this->assertEquals(
+ new WatchedItem( $user, $targets[1], null ),
+ $watchedItems[1]
+ );
+ }
+
+ public function testGetWatchedItems_cachedItem() {
+ $targets = [
+ new TitleValue( 0, 'SomeDbKey' ),
+ new TitleValue( 1, 'AnotherDbKey' ),
+ ];
+
+ $user = $this->getMockNonAnonUserWithId( 1 );
+ $cachedItem = new WatchedItem( $user, $targets[0],
'20151212010101' );
+
+ $mockDb = $this->getMockDb();
+
+ $mockDb->expects( $this->once() )
+ ->method( 'makeWhereFrom2d' )
+ ->with(
+ [ 1 => [ 'AnotherDbKey' => 1 ] ],
+ $this->isType( 'string' ),
+ $this->isType( 'string' )
+ )
+ ->will( $this->returnValue( 'makeWhereFrom2d return
value' ) );
+ $mockDb->expects( $this->once() )
+ ->method( 'select' )
+ ->with(
+ 'watchlist',
+ [ 'wl_namespace', 'wl_title',
'wl_notificationtimestamp' ],
+ [
+ 'makeWhereFrom2d return value',
+ 'wl_user' => 1
+ ],
+ $this->isType( 'string' )
+ )
+ ->will( $this->returnValue( [
+ $this->getFakeRow(
+ [ 'wl_namespace' => 1, 'wl_title' =>
'AnotherDbKey', 'wl_notificationtimestamp' => null ]
+ )
+ ] ) );
+
+ $mockCache = $this->getMockCache();
+ $mockCache->expects( $this->at( 1 ) )
+ ->method( 'get' )
+ ->with( '0:SomeDbKey:1' )
+ ->will( $this->returnValue( $cachedItem ) );
+ $mockCache->expects( $this->at( 3 ) )
+ ->method( 'get' )
+ ->with( '1:AnotherDbKey:1' )
+ ->will( $this->returnValue( null ) );
+ $mockCache->expects( $this->once() )
+ ->method( 'set' )
+ ->with( '1:AnotherDbKey:1' );
+ $mockCache->expects( $this->never() )->method( 'delete' );
+
+ $store = new WatchedItemStore(
+ $this->getMockLoadBalancer( $mockDb ),
+ $mockCache
+ );
+
+ $watchedItems = $store->getWatchedItems(
+ $user,
+ $targets
+ );
+
+ $this->assertInternalType( 'array', $watchedItems );
+ $this->assertCount( 2, $watchedItems );
+
+ $this->assertEquals(
+ $cachedItem,
+ $watchedItems[0]
+ );
+
+ $this->assertEquals(
+ new WatchedItem( $user, $targets[1], null ),
+ $watchedItems[1]
+ );
+ }
+
+ public function testGetWatchedItems_allItemsCached() {
+ $targets = [
+ new TitleValue( 0, 'SomeDbKey' ),
+ new TitleValue( 1, 'AnotherDbKey' ),
+ ];
+
+ $user = $this->getMockNonAnonUserWithId( 1 );
+ $cachedItems = [
+ new WatchedItem( $user, $targets[0], '20151212010101' ),
+ new WatchedItem( $user, $targets[1], null ),
+ ];
+
+ $mockDb = $this->getMockDb();
+ $mockDb->expects( $this->never() )->method( $this->anything() );
+
+ $mockCache = $this->getMockCache();
+ $mockCache->expects( $this->at( 1 ) )
+ ->method( 'get' )
+ ->with( '0:SomeDbKey:1' )
+ ->will( $this->returnValue( $cachedItems[0] ) );
+ $mockCache->expects( $this->at( 3 ) )
+ ->method( 'get' )
+ ->with( '1:AnotherDbKey:1' )
+ ->will( $this->returnValue( $cachedItems[1] ) );
+ $mockCache->expects( $this->never() )->method( 'set' );
+ $mockCache->expects( $this->never() )->method( 'delete' );
+
+ $store = new WatchedItemStore(
+ $this->getMockLoadBalancer( $mockDb ),
+ $mockCache
+ );
+
+ $watchedItems = $store->getWatchedItems(
+ $user,
+ $targets
+ );
+
+ $this->assertEquals( $cachedItems, $watchedItems );
+ }
+
+ public function testGetWatchedItems_anonymousUser() {
+ $mockDb = $this->getMockDb();
+ $mockDb->expects( $this->never() )->method( $this->anything() );
+
+ $mockCache = $this->getMockCache();
+ $mockCache->expects( $this->never() )->method(
$this->anything() );
+
+ $store = new WatchedItemStore(
+ $this->getMockLoadBalancer( $mockDb ),
+ $mockCache
+ );
+
+ $watchedItems = $store->getWatchedItems(
+ $this->getAnonUser(),
+ [
+ new TitleValue( 0, 'SomeDbKey' ),
+ new TitleValue( 0, 'OtherDbKey' ),
+ ]
+ );
+ $this->assertInternalType( 'array', $watchedItems );
+ $this->assertEmpty(
+ $watchedItems
+ );
+ }
+
public function testIsWatchedItem_existingItem() {
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
--
To view, visit https://gerrit.wikimedia.org/r/278283
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I1f84212e7879a84b34bb3b53859069fcea282bba
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: WMDE-leszek <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits