mediawiki.api: add postWithToken and getToken
authorNiklas Laxström <niklas.laxstrom@gmail.com>
Mon, 20 May 2013 15:36:48 +0000 (15:36 +0000)
committersuchetag <sghoshal@wikimedia.org>
Thu, 17 Oct 2013 20:30:03 +0000 (02:00 +0530)
Similar to postWithEditToken in mw.api.edit, but more generic.

Change-Id: Ic2af66b91a996bc9b46dc3e999b4099541573b08

resources/mediawiki.api/mediawiki.api.edit.js
resources/mediawiki.api/mediawiki.api.js

index 381e172..cc83a4b 100644 (file)
@@ -3,9 +3,6 @@
  */
 ( function ( mw, $ ) {
 
-       // Cache token so we don't have to keep fetching new ones for every single request.
-       var cachedToken = null;
-
        $.extend( mw.Api.prototype, {
 
                /**
                 * @return {jQuery.Promise} See #post
                 */
                postWithEditToken: function ( params, ok, err ) {
-                       var useTokenToPost, getTokenIfBad,
-                               api = this;
-                       if ( cachedToken === null ) {
-                               // We don't have a valid cached token, so get a fresh one and try posting.
-                               // We do not trap any 'badtoken' or 'notoken' errors, because we don't want
-                               // an infinite loop. If this fresh token is bad, something else is very wrong.
-                               useTokenToPost = function ( token ) {
-                                       params.token = token;
-                                       api.post( params, { ok: ok, err: err } );
-                               };
-                               return api.getEditToken( useTokenToPost, err );
-                       } else {
-                               // We do have a token, but it might be expired. So if it is 'bad' then
-                               // start over with a new token.
-                               params.token = cachedToken;
-                               getTokenIfBad = function ( code, result ) {
-                                       if ( code === 'badtoken' ) {
-                                               // force a new token, clear any old one
-                                               cachedToken = null;
-                                               api.postWithEditToken( params, ok, err );
-                                       } else {
-                                               err( code, result );
-                                       }
-                               };
-                               return api.post( params, { ok: ok, err: getTokenIfBad } );
-                       }
+                       return this.postWithToken( 'edit', params ).done( ok ).fail( err );
                },
 
                /**
                 * @return {string} return.done.token Received token.
                 */
                getEditToken: function ( ok, err ) {
-                       var d = $.Deferred(),
-                               apiPromise;
-
-                       // Backwards compatibility (< MW 1.20)
-                       d.done( ok ).fail( err );
-
-                       apiPromise = this.get( {
-                                       action: 'tokens',
-                                       type: 'edit'
-                               }, {
-                                       // Due to the API assuming we're logged out if we pass the callback-parameter,
-                                       // we have to disable jQuery's callback system, and instead parse JSON string,
-                                       // by setting 'jsonp' to false.
-                                       // TODO: This concern seems genuine but no other module has it. Is it still
-                                       // needed and/or should we pass this by default?
-                                       jsonp: false
-                               } )
-                               .done( function ( data ) {
-                                       var token;
-                                       // If token type is not available for this user,
-                                       // key 'edittoken' is missing or can contain Boolean false
-                                       if ( data.tokens && data.tokens.edittoken ) {
-                                               token = data.tokens.edittoken;
-                                               cachedToken = token;
-                                               d.resolve( token );
-                                       } else {
-                                               d.reject( 'token-missing', data );
-                                       }
-                               } )
-                               .fail( d.reject );
-
-                       return d.promise( { abort: apiPromise.abort } );
+                       return this.getToken( 'edit' ).done( ok ).fail( err );
                },
 
                /**
                                text: message
                        }, ok, err );
                }
-
-        } );
+       } );
 
        /**
         * @class mw.Api
index 142c454..f9a14b0 100644 (file)
@@ -20,7 +20,8 @@
 
                                dataType: 'json'
                        }
-               };
+               },
+               tokenCache = {};
 
        /**
         * Constructor to create an object to interact with the API of a particular MediaWiki server.
                        return apiDeferred.promise( { abort: xhr.abort } ).fail( function ( code, details ) {
                                mw.log( 'mw.Api error: ', code, details );
                        } );
-               }
+               },
+
+               /**
+                * Post to API with specified type of token. If we have no token, get one and try to post.
+                * If we have a cached token try using that, and if it fails, blank out the
+                * cached token and start over. For example to change an user option you could do:
+                *
+                *     new mw.Api().postWithToken( 'options', {
+                *         action: 'options',
+                *         optionname: 'gender',
+                *         optionvalue: 'female'
+                *     } );
+                *
+                * @param {string} tokenType The name of the token, like options or edit.
+                * @param {Object} params API parameters
+                * @return {jQuery.Promise} See #post
+                */
+               postWithToken: function ( tokenType, params ) {
+                       var api = this, hasOwn = tokenCache.hasOwnProperty;
+                       if ( hasOwn.call( tokenCache, tokenType ) && tokenCache[tokenType] !== undefined ) {
+                               params.token = tokenCache[tokenType];
+                               return api.post( params ).then(
+                                       null,
+                                       function ( code ) {
+                                               if ( code === 'badtoken' ) {
+                                                       // force a new token, clear any old one
+                                                       tokenCache[tokenType] = params.token = undefined;
+                                                       return api.post( params );
+                                               }
+                                       }
+                               );
+                       } else {
+                               return api.getToken( tokenType ).then( function ( token ) {
+                                       tokenCache[tokenType] = params.token = token;
+                                       return api.post( params );
+                               } );
+                       }
+               },
 
+               /**
+                * Api helper to grab any token.
+                *
+                * @param {string} type Token type.
+                * @return {jQuery.Promise}
+                * @return {Function} return.done
+                * @return {string} return.done.token Received token.
+                */
+               getToken: function ( type ) {
+                       var apiPromise,
+                               d = $.Deferred();
+
+                       apiPromise = this.get( {
+                                       action: 'tokens',
+                                       type: type
+                               }, {
+                                       // Due to the API assuming we're logged out if we pass the callback-parameter,
+                                       // we have to disable jQuery's callback system, and instead parse JSON string,
+                                       // by setting 'jsonp' to false.
+                                       // TODO: This concern seems genuine but no other module has it. Is it still
+                                       // needed and/or should we pass this by default?
+                               } )
+                               .done( function ( data ) {
+                                       // If token type is not available for this user,
+                                       // key '...token' is missing or can contain Boolean false
+                                       if ( data.tokens && data.tokens[type + 'token'] ) {
+                                               d.resolve( data.tokens[type + 'token'] );
+                                       } else {
+                                               d.reject( 'token-missing', data );
+                                       }
+                               } )
+                               .fail( d.reject );
+
+                       return d.promise( { abort: apiPromise.abort } );
+               }
        };
 
        /**