All files / mobile.startup/watchstar WatchstarGateway.js

81.81% Statements 18/22
80% Branches 8/10
90% Functions 9/10
81.81% Lines 18/22

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 1321x 1x                                                           11x                                                 6x       6x                 8x 4x     4x           4x               8x 5x     3x       3x                                                     9x 9x 14x 14x         1x  
const util = require( '../util' ),
	actionParams = require( '../actionParams' );
 
/**
 * @ignore
 * @typedef {string|number} PageID Page ID. 0 / "0" is a special no-ID value.
 * {@link https://www.mediawiki.org/wiki/Manual:Page_table#page_id Page ID}
 *
 * @ignore
 * @typedef {string} PageTitle Canonical page title.
 * {@link https://www.mediawiki.org/wiki/Manual:Title.php#Canonical_forms Canonical forms}
 *
 * @ignore
 * @typedef {boolean} WatchStatus Page watch status; true if watched, false if
 *                                unwatched.
 * {@link https://www.mediawiki.org/wiki/API:Info API:Info} (see inprop.watched)
 * {@link https://www.mediawiki.org/wiki/API:Watch API:Watch} (see unwatch)
 *
 * @ignore
 * @typedef {Object.<PageTitle, WatchStatus>} WatchStatusMap
 */
 
/**
 * API for retrieving and modifying page watch statuses. This module interacts
 * with two endpoints, API:Info for GETs and API:Watch and for POSTs.
 */
class WatchstarGateway {
	/**
	 * @param {mw.Api} api
	 */
	constructor( api ) {
		this.api = api;
	}
 
	/**
	 * Issues zero to two asynchronous HTTP requests for the watch status of
	 * each page ID and title passed.
	 *
	 * Every watch entry has a title but not necessarily a page ID. Entries
	 * without IDs are missing pages, i.e., pages that do not exist. These
	 * entries are used to observe when a page with a given title is created.
	 * Although it is convenient to use titles because they're always present,
	 * IDs are preferred since they're far less likely to exceed the URL length
	 * limit.
	 *
	 * No request is issued when no IDs and no titles are passed. Given that the
	 * server state does not change between the two requests, overlapping title
	 * and ID members will behave as expected but there is no reason to issue
	 * such a request.
	 *
	 * @param {PageID[]} ids
	 * @param {PageTitle[]} titles
	 * @return {jQuery.Deferred<WatchStatusMap>}
	 */
	getStatuses( ids, titles ) {
		// Issue two requests and coalesce the results.
		return util.Promise.all( [
			this.getStatusesByID( ids ),
			this.getStatusesByTitle( titles )
		] ).then( function () {
			return util.extend.apply( util, arguments );
		} );
	}
 
	/**
	 * @param {PageID[]} ids
	 * @return {jQuery.Deferred<WatchStatusMap>}
	 */
	getStatusesByID( ids ) {
		if ( !ids.length ) {
			return util.Deferred().resolve( {} );
		}
 
		return this.api.get( {
			formatversion: 2,
			action: 'query',
			prop: 'info',
			inprop: 'watched',
			pageids: ids
		} ).then( ( rsp ) => this._unmarshalGetResponse( rsp ) );
	}
 
	/**
	 * @param {PageTitle[]} titles
	 * @return {jQuery.Deferred<WatchStatusMap>}
	 */
	getStatusesByTitle( titles ) {
		if ( !titles.length ) {
			return util.Deferred().resolve( {} );
		}
 
		return this.api.get( actionParams( {
			prop: 'info',
			inprop: 'watched',
			titles
		} ) ).then( ( rsp ) => this._unmarshalGetResponse( rsp ) );
	}
 
	/**
	 * @param {PageTitle[]} titles
	 * @param {WatchStatus} watched
	 * @return {jQuery.Deferred}
	 */
	postStatusesByTitle( titles, watched ) {
		const params = {
			action: 'watch',
			titles
		};
		if ( !watched ) {
			params.unwatch = !watched;
		}
		return this.api.postWithToken( 'watch', params );
	}
 
	/**
	 * @param {Object} rsp The API:Info response.
	 * @return {jQuery.Deferred<WatchStatusMap>}
	 * @see getStatusesByID
	 * @see getStatusesByTitle
	 * @ignore
	 */
	_unmarshalGetResponse( rsp ) {
		const pages = rsp && rsp.query && rsp.query.pages || [];
		return pages.reduce( ( statuses, page ) => {
			statuses[page.title] = page.watched;
			return statuses;
		}, {} );
	}
}
 
module.exports = WatchstarGateway;