All files / mobile.startup/watchstar WatchstarGateway.js

84% Statements 21/25
80% Branches 8/10
90% Functions 9/10
84% Lines 21/25

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 132 133 134 135 136 137 138 1391x 1x                                                 11x     1x                                                 6x       6x                     8x 8x 4x     4x           4x                   8x 8x 5x     3x       3x                                                           9x 9x 14x 14x         1x  
var util = require( '../util' ),
	actionParams = require( '../actionParams' );
 
/**
 * @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}
 *
 * @typedef {string} PageTitle Canonical page title.
 * {@link https://www.mediawiki.org/wiki/Manual:Title.php#Canonical_forms Canonical forms}
 *
 * @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)
 *
 * @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
 */
function WatchstarGateway( api ) {
	this.api = api;
}
 
WatchstarGateway.prototype = {
	/**
	 * 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.
	 *
	 * @memberof WatchstarGateway
	 * @instance
	 * @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 );
		} );
	},
 
	/**
	 * @memberof WatchstarGateway
	 * @instance
	 * @param {PageID[]} ids
	 * @return {jQuery.Deferred<WatchStatusMap>}
	 */
	getStatusesByID( ids ) {
		var self = this;
		if ( !ids.length ) {
			return util.Deferred().resolve( {} );
		}
 
		return this.api.get( {
			formatversion: 2,
			action: 'query',
			prop: 'info',
			inprop: 'watched',
			pageids: ids
		} ).then( ( rsp ) => self._unmarshalGetResponse( rsp ) );
	},
 
	/**
	 * @memberof WatchstarGateway
	 * @instance
	 * @param {PageTitle[]} titles
	 * @return {jQuery.Deferred<WatchStatusMap>}
	 */
	getStatusesByTitle( titles ) {
		var self = this;
		if ( !titles.length ) {
			return util.Deferred().resolve( {} );
		}
 
		return this.api.get( actionParams( {
			prop: 'info',
			inprop: 'watched',
			titles
		} ) ).then( ( rsp ) => self._unmarshalGetResponse( rsp ) );
	},
 
	/**
	 * @memberof WatchstarGateway
	 * @instance
	 * @param {PageTitle[]} titles
	 * @param {WatchStatus} watched
	 * @return {jQuery.Deferred}
	 */
	postStatusesByTitle( titles, watched ) {
		var params = {
			action: 'watch',
			titles
		};
		if ( !watched ) {
			params.unwatch = !watched;
		}
		return this.api.postWithToken( 'watch', params );
	},
 
	/**
	 * @memberof WatchstarGateway
	 * @instance
	 * @param {Object} rsp The API:Info response.
	 * @return {jQuery.Deferred<WatchStatusMap>}
	 * @see getStatusesByID
	 * @see getStatusesByTitle
	 */
	_unmarshalGetResponse( rsp ) {
		var pages = rsp && rsp.query && rsp.query.pages || [];
		return pages.reduce( ( statuses, page ) => {
			statuses[page.title] = page.watched;
			return statuses;
		}, {} );
	}
};
 
module.exports = WatchstarGateway;