From 89b739c655f8cbeba1e05ab0198eee3b60f9e723 Mon Sep 17 00:00:00 2001 From: Stephen Niedzielski Date: Fri, 9 Mar 2018 09:30:52 -0600 Subject: [PATCH] mediawiki.api: Check that query exists in api.getToken() response It's possible that response.query does not exist when requesting a token. This patch removes the assumption and handles the scenario in the same way as a missing token. Additionally, responses with a queryless response are now deleted from the cache. Corresponding console warning: > jQuery.Deferred exception: Cannot read property 'tokens' of undefined > at apiPromise.then.promise.abort (/w/resources/src/mediawiki/api.js) Change-Id: Ia29e0c0d657bf4b3d94f1d463b942451eebd68b4 --- resources/src/mediawiki/api.js | 21 +++++++++-------- .../mediawiki.api/mediawiki.api.test.js | 23 +++++++++++++++++++ 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/resources/src/mediawiki/api.js b/resources/src/mediawiki/api.js index 2e5a92e704..0038ed8ecf 100644 --- a/resources/src/mediawiki/api.js +++ b/resources/src/mediawiki/api.js @@ -363,7 +363,7 @@ * @return {jQuery.Promise} Received token. */ getToken: function ( type, assert ) { - var apiPromise, promiseGroup, d; + var apiPromise, promiseGroup, d, reject; type = mapLegacyToken( type ); promiseGroup = promises[ this.defaults.ajax.url ]; d = promiseGroup && promiseGroup[ type + 'Token' ]; @@ -379,21 +379,24 @@ type: type, assert: assert } ); + reject = function () { + // Clear promise. Do not cache errors. + delete promiseGroup[ type + 'Token' ]; + + // Let caller handle the error code + return $.Deferred().rejectWith( this, arguments ); + }; d = apiPromise .then( function ( res ) { + if ( !res.query ) { + return reject( 'query-missing', res ); + } // If token type is unknown, it is omitted from the response if ( !res.query.tokens[ type + 'token' ] ) { return $.Deferred().reject( 'token-missing', res ); } - return res.query.tokens[ type + 'token' ]; - }, function () { - // Clear promise. Do not cache errors. - delete promiseGroup[ type + 'Token' ]; - - // Let caller handle the error code - return $.Deferred().rejectWith( this, arguments ); - } ) + }, reject ) // Attach abort handler .promise( { abort: apiPromise.abort } ); diff --git a/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js b/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js index 417ad3d81c..7431b294ac 100644 --- a/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js +++ b/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js @@ -213,6 +213,29 @@ } ); } ); + QUnit.test( 'getToken() - no query', function ( assert ) { + var api = new mw.Api(), + // Same-origin warning and missing query in response. + serverRsp = { + warnings: { + tokens: { + '*': 'Tokens may not be obtained when the same-origin policy is not applied.' + } + } + }; + + this.server.respondWith( /type=testnoquery/, [ 200, { 'Content-Type': 'application/json' }, + JSON.stringify( serverRsp ) + ] ); + + return api.getToken( 'testnoquery' ) + .then( function () { assert.fail( 'Expected response missing a query to be rejected' ); } ) + .catch( function ( err, rsp ) { + assert.equal( err, 'query-missing', 'Expected no query error code' ); + assert.deepEqual( rsp, serverRsp ); + } ); + } ); + QUnit.test( 'getToken() - deprecated', function ( assert ) { // Cache API endpoint from default to avoid cachehit in mw.user.tokens var api = new mw.Api( { ajax: { url: '/postWithToken/api.php' } } ), -- 2.20.1