Mforns has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/176304

Change subject: Add promise support to get method
......................................................................

Add promise support to get method

To get the results and the thrown errors, get method
was accepting the success and error parameters respectively.
However, Dashiki uses jQuery promises returned by the
apis to subscribe callbacks.
To adapt to Dashiki and to implement a common interface,
this fix implements promise support for get method.

Bug: 68448
Change-Id: I719960b677b66f60fbe2c28e8028b1a220b5567e
---
M bower.json
M dist/mediawiki-storage.js
M dist/mediawiki-storage.min.js
M doc/api.md
M package.json
M src/mediawiki-storage.js
M test/mediawiki-storage-spec.js
7 files changed, 195 insertions(+), 70 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/analytics/mediawiki-storage 
refs/changes/04/176304/1

diff --git a/bower.json b/bower.json
index 85aa395..89dc058 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
 {
   "name": "mediawiki-storage",
-  "version": "0.0.0",
+  "version": "0.0.1",
   "authors": [
     "Marcel Ruiz Forns <mfo...@wikimedia.org>"
   ],
diff --git a/dist/mediawiki-storage.js b/dist/mediawiki-storage.js
index 70c2e42..7c78e25 100644
--- a/dist/mediawiki-storage.js
+++ b/dist/mediawiki-storage.js
@@ -13,32 +13,28 @@
      *     - the page id
      *     - the revision id
      *
-     * Note that:
-     * The page contents must be valid json.
-     * The retrieval of the page contents is done via jsonp.
-     * The return value is passed as parameter for the success callback.
-     * Errors occurring before jsonp call (TypeError) will be thrown
-     * synchronously, and errors occurring within or after jsonp call
-     * will be handed as parameter for error callback.
-     *
      * Parameter: options {object}
      *     {
-     *         host: {string}, // i.e. www.wikipedia.org
-     *         // at least one of the following three:
-     *         pageName: {string},
-     *         pageId: {string},
-     *         revId: {string},
-     *         success: {function}, // callback [optional]
-     *                              // parameter: object with page contents
-     *         error: {function} // callback [optional]
-     *                           // parameter: error thrown
+     *         host: {string},
+     *         // specify one of the following 3 params
+     *         pageName: {string} [optional],
+     *         pageId: {string} [optional],
+     *         revId: {string} [optional],
+     *         success: {function} [optional],
+     *         error: {function} [optional]
      *     }
+     *
+     * Returns: {Promise}
+     *
+     * For more information, see the api documentation:
+     * 
https://github.com/wikimedia/analytics-mediawiki-storage/blob/master/doc/api.md
      */
     MediawikiStorage.prototype.get = function (options) {
         if (typeof options !== 'object') {
             throw new TypeError('function must receive an object');
         }
 
+        var deferred = $.Deferred();
         var url  = this._createQueryUrl(options);
         var that = this;
 
@@ -48,23 +44,35 @@
             contentType: 'application/json',
 
             success: function (data) {
-                if (typeof options.success === 'function') {
-                    var pageJson;
-                    try {
-                        pageJson = that._getPageJson(data);
-                    } catch (e) {
-                        return options.error(e);
+                var pageJson;
+
+                // try to parse page contents
+                try {
+                    pageJson = that._getPageJson(data);
+                } catch (e) {
+                    if (typeof options.success === 'function') {
+                        options.error(e);
                     }
+                    deferred.reject(e);
+                    return;
+                }
+
+                // return parsed page contents
+                if (typeof options.success === 'function') {
                     options.success(pageJson);
                 }
+                deferred.resolve(pageJson);
             },
 
-            error: function (jqXHR, textStatus, errorThrown) {
+            error: function (jqXHR, textStatus, e) {
                 if (typeof options.error === 'function') {
-                    options.error(errorThrown);
+                    options.error(e);
                 }
+                deferred.reject(e);
             }
         });
+
+        return deferred.promise();
     };
 
     /**
diff --git a/dist/mediawiki-storage.min.js b/dist/mediawiki-storage.min.js
index bfb3a4e..c76ef81 100644
--- a/dist/mediawiki-storage.min.js
+++ b/dist/mediawiki-storage.min.js
@@ -1 +1 @@
-"use strict";!function(e){var 
t=function(){};t.prototype.get=function(e){if("object"!=typeof e)throw new 
TypeError("function must receive an object");var 
t=this._createQueryUrl(e),r=this;$.ajax({url:t,dataType:"jsonp",contentType:"application/json",success:function(t){if("function"==typeof
 e.success){var o;try{o=r._getPageJson(t)}catch(n){return 
e.error(n)}e.success(o)}},error:function(t,r,o){"function"==typeof 
e.error&&e.error(o)}})},t.prototype._createQueryUrl=function(e){if("string"!=typeof
 e.host)throw new TypeError("host must be a string");var t;if("string"==typeof 
e.pageName)t="&titles="+e.pageName;else if("string"==typeof 
e.pageId)t="&pageids="+e.pageId;else{if("string"!=typeof e.revId)throw new 
TypeError("one of (pageName|pageId|revId) must be a 
string");t="&revids="+e.revId}return["http://",e.host,"/w/api.php","?action=query","&prop=revisions","&format=json","&rvprop=content",t].join("")},t.prototype._getPageJson=function(e){var
 t,r;try{var 
o=e.query.pages,n=Object.keys(o)[0];t=o[n].revisions[0]["*"]}catch(i){throw new 
SyntaxError("unexpected query result")}try{r=JSON.parse(t)}catch(i){throw new 
SyntaxError("page contents are not a valid json")}return r};var r=new 
t;"function"==typeof define&&define.amd?define(function(){return 
r}):"undefined"!=typeof 
module&&module.exports?module.exports=r:e.mediawikiStorage=r}(window);
\ No newline at end of file
+"use strict";!function(e){var 
r=function(){};r.prototype.get=function(e){if("object"!=typeof e)throw new 
TypeError("function must receive an object");var 
r=$.Deferred(),t=this._createQueryUrl(e),o=this;return 
$.ajax({url:t,dataType:"jsonp",contentType:"application/json",success:function(t){var
 n;try{n=o._getPageJson(t)}catch(s){return"function"==typeof 
e.success&&e.error(s),void r.reject(s)}"function"==typeof 
e.success&&e.success(n),r.resolve(n)},error:function(t,o,n){"function"==typeof 
e.error&&e.error(n),r.reject(n)}}),r.promise()},r.prototype._createQueryUrl=function(e){if("string"!=typeof
 e.host)throw new TypeError("host must be a string");var r;if("string"==typeof 
e.pageName)r="&titles="+e.pageName;else if("string"==typeof 
e.pageId)r="&pageids="+e.pageId;else{if("string"!=typeof e.revId)throw new 
TypeError("one of (pageName|pageId|revId) must be a 
string");r="&revids="+e.revId}return["http://",e.host,"/w/api.php","?action=query","&prop=revisions","&format=json","&rvprop=content",r].join("")},r.prototype._getPageJson=function(e){var
 r,t;try{var 
o=e.query.pages,n=Object.keys(o)[0];r=o[n].revisions[0]["*"]}catch(s){throw new 
SyntaxError("unexpected query result")}try{t=JSON.parse(r)}catch(s){throw new 
SyntaxError("page contents are not a valid json")}return t};var t=new 
r;"function"==typeof define&&define.amd?define(function(){return 
t}):"undefined"!=typeof 
module&&module.exports?module.exports=t:e.mediawikiStorage=t}(window);
\ No newline at end of file
diff --git a/doc/api.md b/doc/api.md
index 5f23489..922d6f0 100644
--- a/doc/api.md
+++ b/doc/api.md
@@ -1,11 +1,10 @@
 # API Documentation
-
 mediawiki-storage should run within the following export environments: AMD 
(define), Node.js (require) and browser (global). After you get 
mediawiki-storage object, you can call the following methods on it:
 
-### get
+## get
 __Returns the contents of the specified mediawiki page.__
 
-The mediawiki page is specified using mediawiki's `host` and one of:
+The mediawiki page is specified using mediawiki's `host` and _one_ of:
 
 * `pageName`
 * `pageId`
@@ -13,8 +12,7 @@
 
 _Note that the page contents must be valid json._
 
-##### Parameters:
-
+#### Parameters:
 1. options _{object}_ Dictionary with all options
 
 ```
@@ -28,17 +26,49 @@
 }
 ```
 
-##### Return value:
+#### Retrieving the results:
+There are two options to retrieve the results:
 
-Is passed as a single parameter to `success` callback.
+##### Via _success_ parameter:
+```
+mediawikiStorage.get({
+       // ...
+       success: function (results) {
+               // do something with results
+       }
+});
+```
 
-##### Errors thrown:
+##### Via promise _done_:
+```
+mediawikiStorage.get({
+       // ...
+}).done(function (results) {
+       // do something with results
+});
+```
 
-Errors occurring before jsonp call will be thrown synchronously.
-If an error occures within or after the jsonp call, it will be passed to the 
`error` callback as a single parameter, and execution will be aborted.
+#### Errors thrown:
+Errors occurring before jsonp call will be thrown synchronously. Error 
occurring within or after the jsonp call, will be passed to the corresponding 
callback. There are two options to catch the error:
+##### Via _error_ parameter:
+```
+mediawikiStorage.get({
+       // ...
+       error: function (error) {
+               // do something with error
+       }
+});
+```
+##### Via promise _fail_:
+```
+mediawikiStorage.get({
+       // ...
+}).fail(function (error) {
+       // do something with error
+});
+```
 
-##### Example:
-
+#### Full example:
 ```
 mediawiki-storage.get({
        host: 'meta.wikimedia.org',
diff --git a/package.json b/package.json
index e222011..7dfca35 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "mediawiki-storage",
-  "version": "0.0.0",
+  "version": "0.0.1",
   "devDependencies": {
     "del": "^0.1.3",
     "gulp": "^3.8.10",
diff --git a/src/mediawiki-storage.js b/src/mediawiki-storage.js
index 70c2e42..7c78e25 100644
--- a/src/mediawiki-storage.js
+++ b/src/mediawiki-storage.js
@@ -13,32 +13,28 @@
      *     - the page id
      *     - the revision id
      *
-     * Note that:
-     * The page contents must be valid json.
-     * The retrieval of the page contents is done via jsonp.
-     * The return value is passed as parameter for the success callback.
-     * Errors occurring before jsonp call (TypeError) will be thrown
-     * synchronously, and errors occurring within or after jsonp call
-     * will be handed as parameter for error callback.
-     *
      * Parameter: options {object}
      *     {
-     *         host: {string}, // i.e. www.wikipedia.org
-     *         // at least one of the following three:
-     *         pageName: {string},
-     *         pageId: {string},
-     *         revId: {string},
-     *         success: {function}, // callback [optional]
-     *                              // parameter: object with page contents
-     *         error: {function} // callback [optional]
-     *                           // parameter: error thrown
+     *         host: {string},
+     *         // specify one of the following 3 params
+     *         pageName: {string} [optional],
+     *         pageId: {string} [optional],
+     *         revId: {string} [optional],
+     *         success: {function} [optional],
+     *         error: {function} [optional]
      *     }
+     *
+     * Returns: {Promise}
+     *
+     * For more information, see the api documentation:
+     * 
https://github.com/wikimedia/analytics-mediawiki-storage/blob/master/doc/api.md
      */
     MediawikiStorage.prototype.get = function (options) {
         if (typeof options !== 'object') {
             throw new TypeError('function must receive an object');
         }
 
+        var deferred = $.Deferred();
         var url  = this._createQueryUrl(options);
         var that = this;
 
@@ -48,23 +44,35 @@
             contentType: 'application/json',
 
             success: function (data) {
-                if (typeof options.success === 'function') {
-                    var pageJson;
-                    try {
-                        pageJson = that._getPageJson(data);
-                    } catch (e) {
-                        return options.error(e);
+                var pageJson;
+
+                // try to parse page contents
+                try {
+                    pageJson = that._getPageJson(data);
+                } catch (e) {
+                    if (typeof options.success === 'function') {
+                        options.error(e);
                     }
+                    deferred.reject(e);
+                    return;
+                }
+
+                // return parsed page contents
+                if (typeof options.success === 'function') {
                     options.success(pageJson);
                 }
+                deferred.resolve(pageJson);
             },
 
-            error: function (jqXHR, textStatus, errorThrown) {
+            error: function (jqXHR, textStatus, e) {
                 if (typeof options.error === 'function') {
-                    options.error(errorThrown);
+                    options.error(e);
                 }
+                deferred.reject(e);
             }
         });
+
+        return deferred.promise();
     };
 
     /**
diff --git a/test/mediawiki-storage-spec.js b/test/mediawiki-storage-spec.js
index ef1277b..36f6391 100644
--- a/test/mediawiki-storage-spec.js
+++ b/test/mediawiki-storage-spec.js
@@ -58,7 +58,7 @@
         }
     });
 
-    it('returns a SyntaxError when the query result is not expected', function 
(done) {
+    it('returns SyntaxError when query result is not expected (via error 
parameter)', function (done) {
         sinon.stub($, 'ajax', function (options) {
             options.success('bad query result');
         });
@@ -77,7 +77,26 @@
         });
     });
 
-    it('returns a SyntaxError when the page is not a valid json', function 
(done) {
+    it('returns SyntaxError when query result is not expected (via promise 
fail)', function (done) {
+        sinon.stub($, 'ajax', function (options) {
+            options.success('bad query result');
+        });
+        var promise = mediawikiStorage.get({
+            host     : 'www.wikimedia.org',
+            pageName : 'Schema:EventCapsule'
+        });
+        promise.done(function () {
+            expect(0).toEqual(1);  // should never get here
+        });
+        promise.fail(function (error) {
+            expect(error.name).toEqual('SyntaxError');
+            expect(error.message).toEqual('unexpected query result');
+            $.ajax.restore();
+            done();
+        });
+    });
+
+    it('returns SyntaxError when page is not valid json (via error 
parameter)', function (done) {
         sinon.stub($, 'ajax', function (options) {
             options.success(
                 {query:{pages:{'12345':{revisions:[{'*':'not a valid 
json'}]}}}}
@@ -98,7 +117,28 @@
         });
     });
 
-    it('propagates any jQuery.ajax errors', function (done) {
+    it('returns SyntaxError when page is not valid json (via promise fail)', 
function (done) {
+        sinon.stub($, 'ajax', function (options) {
+            options.success(
+                {query:{pages:{'12345':{revisions:[{'*':'not a valid 
json'}]}}}}
+            );
+        });
+        var promise = mediawikiStorage.get({
+            host     : 'www.wikimedia.org',
+            pageName : 'Schema:EventCapsule'
+        });
+        promise.done(function () {
+            expect(0).toEqual(1);  // should never get here
+        });
+        promise.fail(function (error) {
+            expect(error.name).toEqual('SyntaxError');
+            expect(error.message).toEqual('page contents are not a valid 
json');
+            $.ajax.restore();
+            done();
+        });
+    });
+
+    it('propagates jQuery.ajax errors (via error parameter)', function (done) {
         sinon.stub($, 'ajax', function (options) {
             options.error({}, 'someStatus', new Error('jQuery.ajax error'));
         });
@@ -117,7 +157,26 @@
         });
     });
 
-    it('returns object created from page json', function (done) {
+    it('propagates jQuery.ajax errors (via promise fail)', function (done) {
+        sinon.stub($, 'ajax', function (options) {
+            options.error({}, 'someStatus', new Error('jQuery.ajax error'));
+        });
+        var promise = mediawikiStorage.get({
+            host     : 'www.wikimedia.org',
+            pageName : 'Schema:EventCapsule'
+        });
+        promise.done(function () {
+            expect(0).toEqual(1);  // should never get here
+        });
+        promise.fail(function (error) {
+            expect(error.name).toEqual('Error');
+            expect(error.message).toEqual('jQuery.ajax error');
+            $.ajax.restore();
+            done();
+        });
+    });
+
+    it('returns results (via success parameter)', function (done) {
         sinon.stub($, 'ajax', function (options) {
             options.success(
                 {query:{pages:{'12345':{revisions:[{'*':'{"valid": 
"json"}'}]}}}}
@@ -126,7 +185,7 @@
         mediawikiStorage.get({
             host     : 'www.wikimedia.org',
             pageName : 'Schema:EventCapsule',
-            success  : function (data, textStatus, jqXHR) {
+            success  : function (data) {
                 expect(data).toEqual({"valid": "json"});
                 $.ajax.restore();
                 done();
@@ -137,4 +196,24 @@
         });
     });
 
+    it('returns results (via promise done)', function (done) {
+        sinon.stub($, 'ajax', function (options) {
+            options.success(
+                {query:{pages:{'12345':{revisions:[{'*':'{"valid": 
"json"}'}]}}}}
+            );
+        });
+        var promise = mediawikiStorage.get({
+            host     : 'www.wikimedia.org',
+            pageName : 'Schema:EventCapsule'
+        });
+        promise.done(function (data) {
+            expect(data).toEqual({"valid": "json"});
+            $.ajax.restore();
+            done();
+        });
+        promise.fail(function () {
+            expect(0).toEqual(1);  // should never get here
+        });
+    });
+
 });

-- 
To view, visit https://gerrit.wikimedia.org/r/176304
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I719960b677b66f60fbe2c28e8028b1a220b5567e
Gerrit-PatchSet: 1
Gerrit-Project: analytics/mediawiki-storage
Gerrit-Branch: master
Gerrit-Owner: Mforns <mfo...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to