add setNotificationTimestampsForUser to WatchedItemStore

Bug: T134387
Change-Id: Ia6abe7687b51aabe67e8461375075692db28c9a2
This commit is contained in:
addshore 2016-05-04 16:49:27 +01:00 committed by Addshore
parent 33cb9e803a
commit eacec0a223
4 changed files with 152 additions and 9 deletions

View file

@ -145,16 +145,28 @@ class WatchedItemStore implements StatsdAwareInterface {
}
private function uncacheLinkTarget( LinkTarget $target ) {
$this->stats->increment( 'WatchedItemStore.uncacheLinkTarget' );
if ( !isset( $this->cacheIndex[$target->getNamespace()][$target->getDBkey()] ) ) {
return;
}
$this->stats->increment( 'WatchedItemStore.uncacheLinkTarget' );
foreach ( $this->cacheIndex[$target->getNamespace()][$target->getDBkey()] as $key ) {
$this->stats->increment( 'WatchedItemStore.uncacheLinkTarget.items' );
$this->cache->delete( $key );
}
}
private function uncacheUser( User $user ) {
$this->stats->increment( 'WatchedItemStore.uncacheUser' );
foreach ( $this->cacheIndex as $ns => $dbKeyArray ) {
foreach ( $dbKeyArray as $dbKey => $userArray ) {
if ( isset( $userArray[$user->getId()] ) ) {
$this->stats->increment( 'WatchedItemStore.uncacheUser.items' );
$this->cache->delete( $userArray[$user->getId()] );
}
}
}
}
/**
* @param User $user
* @param LinkTarget $target
@ -667,6 +679,41 @@ class WatchedItemStore implements StatsdAwareInterface {
return $success;
}
/**
* @param User $user The user to set the timestamp for
* @param string $timestamp Set the update timestamp to this value
* @param LinkTarget[] $targets List of targets to update. Default to all targets
*
* @return bool success
*/
public function setNotificationTimestampsForUser( User $user, $timestamp, array $targets = [] ) {
// Only loggedin user can have a watchlist
if ( $user->isAnon() ) {
return false;
}
$dbw = $this->getConnection( DB_MASTER );
$conds = [ 'wl_user' => $user->getId() ];
if ( $targets ) {
$batch = new LinkBatch( $targets );
$conds[] = $batch->constructSet( 'wl', $dbw );
}
$success = $dbw->update(
'watchlist',
[ 'wl_notificationtimestamp' => $dbw->timestamp( $timestamp ) ],
$conds,
__METHOD__
);
$this->reuseConnection( $dbw );
$this->uncacheUser( $user );
return $success;
}
/**
* @param User $editor The editor that triggered the update. Their notification
* timestamp will not be updated(they have already seen it)

View file

@ -24,6 +24,7 @@
*
* @file
*/
use MediaWiki\MediaWikiServices;
/**
* API interface for setting the wl_notificationtimestamp field
@ -98,13 +99,14 @@ class ApiSetNotificationTimestamp extends ApiBase {
}
}
$watchedItemStore = MediaWikiServices::getInstance()->getWatchedItemStore();
$apiResult = $this->getResult();
$result = [];
if ( $params['entirewatchlist'] ) {
// Entire watchlist mode: Just update the thing and return a success indicator
$dbw->update( 'watchlist', [ 'wl_notificationtimestamp' => $timestamp ],
[ 'wl_user' => $user->getId() ],
__METHOD__
$watchedItemStore->setNotificationTimestampsForUser(
$user,
$timestamp
);
$result['notificationtimestamp'] = is_null( $timestamp )
@ -133,14 +135,15 @@ class ApiSetNotificationTimestamp extends ApiBase {
if ( $pageSet->getTitles() ) {
// Now process the valid titles
$lb = new LinkBatch( $pageSet->getTitles() );
$dbw->update( 'watchlist', [ 'wl_notificationtimestamp' => $timestamp ],
[ 'wl_user' => $user->getId(), $lb->constructSet( 'wl', $dbw ) ],
__METHOD__
$watchedItemStore->setNotificationTimestampsForUser(
$user,
$timestamp,
$pageSet->getTitles()
);
// Query the results of our update
$timestamps = [];
$lb = new LinkBatch( $pageSet->getTitles() );
$res = $dbw->select(
'watchlist',
[ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp' ],

View file

@ -106,7 +106,7 @@ class WatchedItemStoreIntegrationTest extends MediaWikiTestCase {
);
}
public function testUpdateAndResetNotificationTimestamp() {
public function testUpdateResetAndSetNotificationTimestamp() {
$user = $this->getUser();
$otherUser = ( new TestUser( 'WatchedItemStoreIntegrationTestUser_otherUser' ) )->getUser();
$title = Title::newFromText( 'WatchedItemStoreIntegrationTestPage' );
@ -172,6 +172,24 @@ class WatchedItemStoreIntegrationTest extends MediaWikiTestCase {
[ [ $title, '20150202020202' ] ], $initialVisitingWatchers + 1
)
);
// setNotificationTimestampsForUser specifying a title
$this->assertTrue(
$store->setNotificationTimestampsForUser( $user, '20200202020202', [ $title ] )
);
$this->assertEquals(
'20200202020202',
$store->getWatchedItem( $user, $title )->getNotificationTimestamp()
);
// setNotificationTimestampsForUser not specifying a title
$this->assertTrue(
$store->setNotificationTimestampsForUser( $user, '20210202020202' )
);
$this->assertEquals(
'20210202020202',
$store->getWatchedItem( $user, $title )->getNotificationTimestamp()
);
}
public function testDuplicateAllAssociatedEntries() {

View file

@ -2366,6 +2366,81 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
ScopedCallback::consume( $scopedOverrideRevision );
}
public function testSetNotificationTimestampsForUser_anonUser() {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $this->getMockDb() ),
$this->getMockCache()
);
$this->assertFalse( $store->setNotificationTimestampsForUser( $this->getAnonUser(), '' ) );
}
public function testSetNotificationTimestampsForUser_allRows() {
$user = $this->getMockNonAnonUserWithId( 1 );
$timestamp = '20100101010101';
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'update' )
->with(
'watchlist',
[ 'wl_notificationtimestamp' => 'TS' . $timestamp . 'TS' ],
[ 'wl_user' => 1 ]
)
->will( $this->returnValue( true ) );
$mockDb->expects( $this->exactly( 1 ) )
->method( 'timestamp' )
->will( $this->returnCallback( function( $value ) {
return 'TS' . $value . 'TS';
} ) );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$this->getMockCache()
);
$this->assertTrue(
$store->setNotificationTimestampsForUser( $user, $timestamp )
);
}
public function testSetNotificationTimestampsForUser_specificTargets() {
$user = $this->getMockNonAnonUserWithId( 1 );
$timestamp = '20100101010101';
$targets = [ new TitleValue( 0, 'Foo' ), new TitleValue( 0, 'Bar' ) ];
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'update' )
->with(
'watchlist',
[ 'wl_notificationtimestamp' => 'TS' . $timestamp . 'TS' ],
[ 'wl_user' => 1, 0 => 'makeWhereFrom2d return value' ]
)
->will( $this->returnValue( true ) );
$mockDb->expects( $this->exactly( 1 ) )
->method( 'timestamp' )
->will( $this->returnCallback( function( $value ) {
return 'TS' . $value . 'TS';
} ) );
$mockDb->expects( $this->once() )
->method( 'makeWhereFrom2d' )
->with(
[ [ 'Foo' => 1, 'Bar' => 1 ] ],
$this->isType( 'string' ),
$this->isType( 'string' )
)
->will( $this->returnValue( 'makeWhereFrom2d return value' ) );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$this->getMockCache()
);
$this->assertTrue(
$store->setNotificationTimestampsForUser( $user, $timestamp, $targets )
);
}
public function testUpdateNotificationTimestamp_watchersExist() {
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )