Henning Snater has submitted this change and it was merged. Change subject: Changes wb.RepoApi.getEntities to promise a collection of wb.Entity ......................................................................
Changes wb.RepoApi.getEntities to promise a collection of wb.Entity Would promise a collection of raw data before. * changed jQuery.wikibase.snakview to make use of the change (rather than break). * simple refactoring and adjustment of some API test Change-Id: If80ca6c9048cd9d6880a9c790240fdb4e81cbcc0 --- M lib/resources/Resources.php M lib/resources/jquery.wikibase/jquery.wikibase.snakview/snakview.js M lib/resources/wikibase.store/wikibase.RepoApi.js M lib/tests/qunit/wikibase.store/wikibase.RepoApi.tests.js 4 files changed, 118 insertions(+), 130 deletions(-) Approvals: Henning Snater: Verified; Looks good to me, approved diff --git a/lib/resources/Resources.php b/lib/resources/Resources.php index 71d75fa..823a42a 100644 --- a/lib/resources/Resources.php +++ b/lib/resources/Resources.php @@ -133,6 +133,7 @@ 'jquery.json', 'user.tokens', 'wikibase.datamodel', + 'wikibase.serialization.entities', 'wikibase.repoAccess', 'wikibase.RepoApiError', 'mediawiki.Title', diff --git a/lib/resources/jquery.wikibase/jquery.wikibase.snakview/snakview.js b/lib/resources/jquery.wikibase/jquery.wikibase.snakview/snakview.js index 0e2eb04..878c348 100644 --- a/lib/resources/jquery.wikibase/jquery.wikibase.snakview/snakview.js +++ b/lib/resources/jquery.wikibase/jquery.wikibase.snakview/snakview.js @@ -202,7 +202,8 @@ .on( 'entityselectorselect', function( e, ui ) { // entity chosen in entity selector but we still need the data type of the entity, so // we have to make a separate API call: - var api = new wb.RepoApi(); + var api = new wb.RepoApi(), + entityId = ui.item.id; // Display spinner as long as the value view is loading. There is no need to display the // spinner when the selected item actually has not changed since the variation will stay @@ -213,24 +214,16 @@ ); } - api.getEntities( ui.item.id, null ).done( function( response ) { - var entityId = ui.item.id, - entity = response.entities[ entityId ], - dataTypeId = entity.datatype, - dataType = dt.getDataType( dataTypeId ), - label; - - if( entity.labels && entity.labels[ language ] ) { - label = entity.labels[ language ].value; - } + api.getEntities( entityId, null, [ language ] ).done( function( entities ) { + var entity = entities[ entityId ]; // update local store with newest information about selected property // TODO: create more sophisticated local store interface rather than accessing // wb.entities directly wb.entities[ entityId ] = { id: entityId, - label: label, - datatype: dataType.getId(), + label: entity.getLabel(), + datatype: entity.getDataType().getId(), url: ui.item.url }; diff --git a/lib/resources/wikibase.store/wikibase.RepoApi.js b/lib/resources/wikibase.store/wikibase.RepoApi.js index 463ff5a..aa9c0d6 100644 --- a/lib/resources/wikibase.store/wikibase.RepoApi.js +++ b/lib/resources/wikibase.store/wikibase.RepoApi.js @@ -75,7 +75,7 @@ }, /** - * Gets one or more entities. + * Gets one or more Entities. * * @param {String[]|String} ids * @param {String[]|String} [props] Key(s) of property/ies to retrieve from the API @@ -86,7 +86,12 @@ * default: null (unsorted) * @param {String} [dir] Sort direction may be 'ascending' or 'descending' * default: null (ascending) - * @return {jQuery.Promise} + * @return {jQuery.Promise} If successful, the first parameter of the done callbacks will be + * an object with keys of the entity's IDs and values of the requested entities + * represented as wb.Entity objects. If a requested Entity does not exist, it will not + * be represented in the result. + * + * @todo Requires tests! */ getEntities: function( ids, props, languages, sort, dir ) { var params = { @@ -98,11 +103,25 @@ dir: dir || undefined }; - return this.get( params ); + return this._getAndPromiseWithAbstraction( params, function( result ) { + var entities = {}, + unserializer = ( new wb.serialization.SerializerFactory() ).newUnserializerFor( wb.Entity ); + + $.each( result.entities, function( id, entityData ) { + if( !entityData.id ) { + return; // missing entity + } + + var entity = unserializer.unserialize( entityData ); + entities[ entity.getId() ] = entity; + } ); + + return [ entities ]; + } ); }, /** - * Gets an entitiy which is linked with a given page. + * Gets an Entity which is linked with a given page. * * @param {String[]|String} [sites] Site(s) the given page is linked on * @param {String[]|String} [titles] Linked page(s) @@ -490,31 +509,6 @@ } }, - /** - * Same as post() but will return a jQuery.Promise which will resolve with some different - * arguments, specified in a callback. - * - * @since 0.4 - * - * @param params - * @param {Function} callbackForAbstraction Called when post request is resolved, will get all - * parameters the original resolved post promise gets. Can return an array whose members - * will then serve as parameters for the public promise. - * @return jQuery.Promise - */ - _postAndPromiseWithAbstraction: function( params, callbackForAbstraction ) { - var deferred = $.Deferred(), - self = this; - - this.post( params ) - .done( function() { - var args = callbackForAbstraction.apply( self, arguments ); - deferred.resolve.apply( deferred, args ); - } ) - .fail( deferred.reject ); - - return deferred.promise(); - }, /** * Performs an API get request. @@ -528,8 +522,48 @@ this._extendRepoCallParams( params, options ); return this._api.get( params, options ); - } + }, + /** + * Same as post() or get() but will return a jQuery.Promise which will resolve with some + * different arguments, specified in a callback. + * + * @since 0.4 + * + * @param {string} method Either 'post' or 'get' + * @param params + * @param {Function} callbackForAbstraction Called when post request is resolved, will get all + * parameters the original resolved post promise gets. Can return an array whose members + * will then serve as parameters for the public promise. + * @return jQuery.Promise + */ + _requestAndPromiseWithAbstraction: function( method, params, callbackForAbstraction ) { + var deferred = $.Deferred(), + self = this; + + this[ method ]( params ) + .done( function() { + var args = callbackForAbstraction.apply( self, arguments ); + deferred.resolve.apply( deferred, args ); + } ) + .fail( deferred.reject ); + + return deferred.promise(); + }, + + /** + * @see _requestAndPromiseWithAbstraction + */ + _postAndPromiseWithAbstraction: function( params, callbackForAbstraction ) { + return this._requestAndPromiseWithAbstraction( 'post', params, callbackForAbstraction ); + }, + + /** + * @see _requestAndPromiseWithAbstraction + */ + _getAndPromiseWithAbstraction: function( params, callbackForAbstraction ) { + return this._requestAndPromiseWithAbstraction( 'get', params, callbackForAbstraction ); + } } ); // TODO: step by step implementation of the store, starting with basic claim stuff diff --git a/lib/tests/qunit/wikibase.store/wikibase.RepoApi.tests.js b/lib/tests/qunit/wikibase.store/wikibase.RepoApi.tests.js index 324d20f..12789af 100644 --- a/lib/tests/qunit/wikibase.store/wikibase.RepoApi.tests.js +++ b/lib/tests/qunit/wikibase.store/wikibase.RepoApi.tests.js @@ -131,27 +131,30 @@ } ); - QUnit.test( 'Set label', function( assert ) { - var data = { - labels: { - en: { - language: 'en', - value: 'en-value' - } - } - }; + /** + * Will test the RepoApi's function to set one of the multilingual basic values, e.g. label or + * description. + * + * @param {Object} assert + * @param {string} type E.g. 'label' or 'description' + * @param {string} apiFnName The name of the wb.RepoApi function to set the value + */ + function testSetBasicMultiLingualValue( assert, type, apiFnName ) { + var typesApiPropName = type + 's', // API uses plural form of the type: 'label' -> 'labels' + language = 'en', + value = language + '-' + type; // could be anything really testrun.queue( qkey, function() { createItem(); } ); testrun.queue( qkey, function() { - api.setLabel( - entity.id, entity.lastrevid, data.labels.en.value, data.labels.en.language + api[ apiFnName ]( + entity.id, entity.lastrevid, value, language ).done( function( response ) { assert.deepEqual( - response.entity.labels.en, - data.labels.en, - 'Set label.' + response.entity[ typesApiPropName ].en, + { language: language, value: value }, + type + ' "' + value + '" has been set for language "' + language + '"' ); testrun.dequeue( qkey ); @@ -159,33 +162,48 @@ } ); testrun.queue( qkey, function() { - api.setLabel( - entity.id, entity.lastrevid, data.labels.en.value, 'doesnotexist' - ).done( function( response ) { + var setTypeApiPromise = api[ apiFnName ]( + entity.id, entity.lastrevid, value, 'non-existent-language' + ); + assert.ok( // promise is a plain object, so check for 'then' function + $.isFunction( setTypeApiPromise.then ), + 'wb.RepoApi.' + apiFnName + ' returns a jQuery Promise' + ); + + + setTypeApiPromise.done( function( response ) { assert.ok( false, - 'It is possible to set te label for an invalid language.' + 'Impossible to set the ' + type + ' for an unknown language' ); - QUnit.start(); - } ).fail( function( code, details ) { + } ); + + setTypeApiPromise.fail( function( code, details ) { assert.equal( code, 'unknown_language', - 'Failed trying to set the label on an invalid language.' + 'Failed trying to set the value on an unknown language' ); testrun.dequeue( qkey ); } ); } ); testrun.queue( qkey, function() { - api.getEntities( entity.id, 'labels' ).done( function( response ) { + api.getEntities( entity.id, typesApiPropName ).done( function( entities ) { + assert.ok( + entities[ entity.id ] instanceof wb.Entity, + 'Entity "' + entity.id + '", the ' + type + ' has been set for, has been' + + 'returned by wb.RepoApi.getEntities.' + ); + + var entityData = entities[ entity.id ].toMap(); assert.deepEqual( - response.entities[entity.id].labels, - data.labels, - 'Verified labels.' + entityData[ type ] && entityData[ type ][ language ], + value, + 'Verified that ' + type + ' "' + value + '" has been set for language "' + language + '"' ); testrun.dequeue( qkey ); @@ -193,72 +211,14 @@ } ); runTest(); + } + QUnit.test( 'Set label', function( assert ) { + testSetBasicMultiLingualValue( assert, 'label', 'setLabel' ); } ); QUnit.test( 'Set description', function( assert ) { - var data = { - descriptions: { - en: { - language: 'en', - value: 'en-value' - } - } - }; - - testrun.queue( qkey, function() { createItem(); } ); - - testrun.queue( qkey, function() { - api.setDescription( - entity.id, entity.lastrevid, data.descriptions.en.value, data.descriptions.en.language - ).done( function( response ) { - - assert.deepEqual( - response.entity.descriptions.en, - data.descriptions.en, - 'Set description.' - ); - - testrun.dequeue( qkey ); - } ).fail( onFail ); - } ); - - testrun.queue( qkey, function() { - api.setDescription( - 'invalidid', entity.lastrevid, data.descriptions.en.value, data.descriptions.en.language - ).done( function( response ) { - - assert.ok( - false, - 'It is possible to pass an invalid id.' - ); - - QUnit.start(); - } ).fail( function( code, details ) { - assert.equal( - code, - 'no-such-entity-id', - 'Failed trying to set a description with an invalid id.' - ); - testrun.dequeue( qkey ); - } ); - } ); - - testrun.queue( qkey, function() { - api.getEntities( entity.id, 'descriptions' ).done( function( response ) { - - assert.deepEqual( - response.entities[entity.id].descriptions, - data.descriptions, - 'Verified descriptions.' - ); - - testrun.dequeue( qkey ); - } ).fail( onFail ); - } ); - - runTest(); - + testSetBasicMultiLingualValue( assert, 'description', 'setDescription' ); } ); // TODO Re-activate test after having solved problem with edit conflict detection added in change -- To view, visit https://gerrit.wikimedia.org/r/51390 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: If80ca6c9048cd9d6880a9c790240fdb4e81cbcc0 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/Wikibase Gerrit-Branch: master Gerrit-Owner: Daniel Werner <daniel.wer...@wikimedia.de> Gerrit-Reviewer: Henning Snater <henning.sna...@wikimedia.de> Gerrit-Reviewer: jenkins-bot _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits