Merge "Hygiene: Use factory functions instead of classes" into mpga

This commit is contained in:
jenkins-bot 2017-02-08 19:38:53 +00:00 committed by Gerrit Code Review
commit d7b5f56665
4 changed files with 174 additions and 168 deletions

View file

@ -17,9 +17,9 @@
*/
function createGateway( config ) {
if ( config.get( 'wgPopupsAPIUseRESTBase' ) ) {
return new mw.popups.RESTBaseGateway( $ );
return mw.popups.createRESTBaseGateway( $.ajax );
}
return new mw.popups.MediaWikiApiGateway( new mw.Api() );
return mw.popups.createMediaWikiApiGateway( new mw.Api() );
}
/**

View file

@ -10,16 +10,13 @@
/**
* Interface for API gateway that fetches page summary
*
* @interface Gateway
* @function
* @param {mw.Api|jQuery}
* @interface ext.popups.Gateway
*/
/**
* Get page summary
*
* Returns a preview model fetched from the api
* @function
* @name Gateway#getPageSummary
* @name ext.popups.Gateway#getPageSummary
* @param {String} title Page title we're querying
* @returns {jQuery.Promise} that resolves with {ext.popups.PreviewModel}
* if the request is successful and the response is not empty; otherwise
@ -27,147 +24,163 @@
*/
/**
* MediaWiki API gateway
* MediaWiki API gateway factory
*
* @class
* @param {mw.Api} api
* @implements {ext.popups.Gateway}
* @returns {ext.popups.Gateway}
*/
function MediaWikiApiGateway( api ) {
this.api = api;
}
function createMediaWikiApiGateway( api ) {
/**
* Fetch page data from the API
*
* @private
* @param {String} title
* @return {jQuery.Promise}
*/
MediaWikiApiGateway.prototype.fetch = function ( title ) {
return this.api.get( {
action: 'query',
prop: 'info|extracts|pageimages|revisions|info',
formatversion: 2,
redirects: true,
exintro: true,
exchars: EXTRACT_LENGTH,
/**
* Fetch page data from the API
*
* @param {String} title
* @return {jQuery.Promise}
*/
function fetch( title ) {
return api.get( {
action: 'query',
prop: 'info|extracts|pageimages|revisions|info',
formatversion: 2,
redirects: true,
exintro: true,
exchars: EXTRACT_LENGTH,
// There is an added geometric limit on .mwe-popups-extract
// so that text does not overflow from the card.
explaintext: true,
// There is an added geometric limit on .mwe-popups-extract
// so that text does not overflow from the card.
explaintext: true,
piprop: 'thumbnail',
pithumbsize: THUMBNAIL_SIZE,
rvprop: 'timestamp',
inprop: 'url',
titles: title,
smaxage: CACHE_LIFETIME,
maxage: CACHE_LIFETIME,
uselang: 'content'
}, {
headers: {
'X-Analytics': 'preview=1'
}
} );
};
/**
* Extract page data from the response
*
* @private
* @param {Object} data API response data
* @throws {Error} Throw an error if page data cannot be extracted,
* i.e. if the response is empty,
* @returns {Object}
*/
MediaWikiApiGateway.prototype.extractPageFromResponse = function( data ) {
if (
data.query &&
data.query.pages &&
data.query.pages.length
) {
return data.query.pages[ 0 ];
piprop: 'thumbnail',
pithumbsize: THUMBNAIL_SIZE,
rvprop: 'timestamp',
inprop: 'url',
titles: title,
smaxage: CACHE_LIFETIME,
maxage: CACHE_LIFETIME,
uselang: 'content'
}, {
headers: {
'X-Analytics': 'preview=1'
}
} );
}
throw new Error( 'API response `query.pages` is empty.' );
};
/**
* Extract page data from the response
*
* @param {Object} data API response data
* @throws {Error} Throw an error if page data cannot be extracted,
* i.e. if the response is empty,
* @returns {Object}
*/
function extractPageFromResponse( data ) {
if (
data.query &&
data.query.pages &&
data.query.pages.length
) {
return data.query.pages[ 0 ];
}
/**
* Transform the API response to a preview model
*
* @private
* @param {Object} page
* @returns {ext.popups.PreviewModel}
*/
MediaWikiApiGateway.prototype.convertPageToModel = function( page ) {
return mw.popups.preview.createModel(
page.title,
page.canonicalurl,
page.pagelanguagehtmlcode,
page.pagelanguagedir,
page.extract,
page.thumbnail
);
};
throw new Error( 'API response `query.pages` is empty.' );
}
MediaWikiApiGateway.prototype.getPageSummary = function( title ) {
return this.fetch( title )
.then( this.extractPageFromResponse )
.then( this.convertPageToModel );
};
/**
* Transform the API response to a preview model
*
* @param {Object} page
* @returns {ext.popups.PreviewModel}
*/
function convertPageToModel( page ) {
return mw.popups.preview.createModel(
page.title,
page.canonicalurl,
page.pagelanguagehtmlcode,
page.pagelanguagedir,
page.extract,
page.thumbnail
);
}
/**
* RESTBase gateway
*
* @class
* @param {jQuery} api
* @implements {ext.popups.Gateway}
*/
function RESTBaseGateway( api ) {
this.api = api;
/**
* Get the page summary from the api and transform the data
*
* @param {String} title
* @returns {jQuery.Promise<ext.popups.PreviewModel>}
*/
function getPageSummary( title ) {
return fetch( title )
.then( extractPageFromResponse )
.then( convertPageToModel );
}
return {
fetch: fetch,
extractPageFromResponse: extractPageFromResponse,
convertPageToModel: convertPageToModel,
getPageSummary: getPageSummary
};
}
/**
* Fetch page data from the API
* RESTBase gateway factory
*
* @private
* @param {String} title
* @return {jQuery.Promise}
* @param {Function} ajax function from jQuery for example
* @returns {ext.popups.Gateway}
*/
RESTBaseGateway.prototype.fetch = function ( title ) {
return this.api.ajax( {
url: RESTBASE_ENDPOINT + encodeURIComponent( title ),
headers: {
Accept: 'application/json; charset=utf-8' +
'profile="' + RESTBASE_PROFILE + '"'
}
} );
};
function createRESTBaseGateway( ajax ) {
/**
* Transform the API response to a preview model
*
* @private
* @param {Object} page
* @returns {ext.popups.PreviewModel}
*/
RESTBaseGateway.prototype.convertPageToModel = function( page ) {
return mw.popups.preview.createModel(
page.title,
new mw.Title( page.title ).getUrl(),
page.lang,
page.dir,
page.extract,
page.thumbnail
);
};
/**
* Fetch page data from the API
*
* @param {String} title
* @return {jQuery.Promise}
*/
function fetch( title ) {
return ajax( {
url: RESTBASE_ENDPOINT + encodeURIComponent( title ),
headers: {
Accept: 'application/json; charset=utf-8' +
'profile="' + RESTBASE_PROFILE + '"'
}
} );
}
RESTBaseGateway.prototype.getPageSummary = function( title ) {
return this.fetch( title )
.then( this.convertPageToModel );
};
/**
* Transform the API response to a preview model
*
* @param {Object} page
* @returns {ext.popups.PreviewModel}
*/
function convertPageToModel( page ) {
return mw.popups.preview.createModel(
page.title,
new mw.Title( page.title ).getUrl(),
page.lang,
page.dir,
page.extract,
page.thumbnail
);
}
mw.popups.MediaWikiApiGateway = MediaWikiApiGateway;
mw.popups.RESTBaseGateway = RESTBaseGateway;
/**
* Get the page summary from the api and transform the data
*
* @param {String} title
* @returns {jQuery.Promise<ext.popups.PreviewModel>}
*/
function getPageSummary( title ) {
return fetch( title )
.then( convertPageToModel );
}
return {
fetch: fetch,
convertPageToModel: convertPageToModel,
getPageSummary: getPageSummary
};
}
mw.popups.createMediaWikiApiGateway = createMediaWikiApiGateway;
mw.popups.createRESTBaseGateway = createRESTBaseGateway;
}( mediaWiki, jQuery ) );

View file

@ -302,7 +302,6 @@
QUnit.test(
'it shouldn\'t delay dispatching the FETCH_END action if the API request is over the target',
function ( assert ) {
var that = this;
this.fetch();
@ -358,8 +357,8 @@
that.waitPromise.then( function () {
assert.ok(
dispatch.calledWith( {
type: 'ABANDON_END',
token: token
type: 'ABANDON_END',
token: token
} ),
'ABANDON_* share the same token.'
);

View file

@ -1,8 +1,8 @@
( function ( mw, $ ) {
var createModel = mw.popups.preview.createModel,
MediaWikiApiGateway = mw.popups.MediaWikiApiGateway,
RESTBaseGateway = mw.popups.RESTBaseGateway,
createMediaWikiApiGateway = mw.popups.createMediaWikiApiGateway,
createRESTBaseGateway = mw.popups.createRESTBaseGateway,
MEDIAWIKI_API_RESPONSE = {
query: {
pages: [
@ -74,11 +74,11 @@
QUnit.module( 'ext.popups/gateway' );
QUnit.test( 'MediaWiki API gateway is called with correct arguments', function ( assert ) {
var getSpy = this.sandbox.spy(),
var spy = this.sandbox.spy(),
api = {
get: getSpy
get: spy
},
gateway = new MediaWikiApiGateway( api ),
gateway = createMediaWikiApiGateway( api ),
expectedOptions = {
action: 'query',
prop: 'info|extracts|pageimages|revisions|info',
@ -104,12 +104,15 @@
gateway.fetch( 'Test Title' );
assert.deepEqual( getSpy.getCall( 0 ).args[ 0 ], expectedOptions, 'options' );
assert.deepEqual( getSpy.getCall( 0 ).args[ 1 ], expectedHeaders, 'headers' );
assert.deepEqual( spy.getCall( 0 ).args[ 0 ], expectedOptions, 'options' );
assert.deepEqual( spy.getCall( 0 ).args[ 1 ], expectedHeaders, 'headers' );
} );
QUnit.test( 'MediaWiki API gateway is correctly extracting the page data from the response ', function ( assert ) {
var gateway = new MediaWikiApiGateway(),
var api = {
get: this.sandbox.stub()
},
gateway = createMediaWikiApiGateway( api ),
errorCases = [
{},
{
@ -151,7 +154,7 @@
} );
QUnit.test( 'MediaWiki API gateway is correctly converting the page data to a model ', function ( assert ) {
var gateway = new MediaWikiApiGateway(),
var gateway = createMediaWikiApiGateway(),
page = gateway.extractPageFromResponse( MEDIAWIKI_API_RESPONSE );
assert.deepEqual(
@ -165,7 +168,7 @@
api = {
get: this.sandbox.stub().returns( deferred.promise() )
},
gateway = new MediaWikiApiGateway( api ),
gateway = createMediaWikiApiGateway( api ),
done = assert.async( 1 );
gateway.getPageSummary( 'Test Title' ).fail( function () {
@ -182,7 +185,7 @@
$.Deferred().resolve( MEDIAWIKI_API_RESPONSE ).promise()
)
},
gateway = new MediaWikiApiGateway( api ),
gateway = createMediaWikiApiGateway( api ),
done = assert.async( 1 );
gateway.getPageSummary( 'Test Title' ).done( function ( result ) {
@ -221,7 +224,7 @@
$.Deferred().resolve( response ).promise()
)
},
gateway = new MediaWikiApiGateway( api ),
gateway = createMediaWikiApiGateway( api ),
done = assert.async( 1 );
gateway.getPageSummary( 'Test Title' ).done( function ( result ) {
@ -233,10 +236,7 @@
QUnit.test( 'RESTBase gateway is called with correct arguments', function ( assert ) {
var getSpy = this.sandbox.spy(),
api = {
ajax: getSpy
},
gateway = new RESTBaseGateway( api ),
gateway = createRESTBaseGateway( getSpy ),
expectedOptions = {
url: '/api/rest_v1/page/summary/' + encodeURIComponent( 'Test Title' ),
headers: {
@ -251,7 +251,7 @@
} );
QUnit.test( 'RESTBase gateway is correctly converting the page data to a model ', function ( assert ) {
var gateway = new RESTBaseGateway();
var gateway = createRESTBaseGateway();
assert.deepEqual(
gateway.convertPageToModel( RESTBASE_RESPONSE ),
@ -261,10 +261,8 @@
QUnit.test( 'RESTBase gateway handles the API failure', function ( assert ) {
var deferred = $.Deferred(),
api = {
ajax: this.sandbox.stub().returns( deferred.promise() )
},
gateway = new RESTBaseGateway( api ),
api = this.sandbox.stub().returns( deferred.promise() ),
gateway = createRESTBaseGateway( api ),
done = assert.async( 1 );
gateway.getPageSummary( 'Test Title' ).fail( function () {
@ -276,12 +274,10 @@
} );
QUnit.test( 'RESTBase gateway returns the correct data ', function ( assert ) {
var api = {
ajax: this.sandbox.stub().returns(
$.Deferred().resolve( RESTBASE_RESPONSE ).promise()
)
},
gateway = new RESTBaseGateway( api ),
var api = this.sandbox.stub().returns(
$.Deferred().resolve( RESTBASE_RESPONSE ).promise()
),
gateway = createRESTBaseGateway( api ),
done = assert.async( 1 );
gateway.getPageSummary( 'Test Title' ).done( function ( result ) {
@ -298,12 +294,10 @@
detail: 'Page or revision not found.',
uri: '/en.wikipedia.org/v1/page/summary/Missing_page'
},
api = {
ajax: this.sandbox.stub().returns(
$.Deferred().rejectWith( response ).promise()
)
},
gateway = new RESTBaseGateway( api ),
api = this.sandbox.stub().returns(
$.Deferred().rejectWith( response ).promise()
),
gateway = createRESTBaseGateway( api ),
done = assert.async( 1 );
gateway.getPageSummary( 'Missing Page' ).fail( function () {