Mooeypoo has uploaded a new change for review.

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

Change subject: [wip] Add a NetworkHandler as a central API authority to Echo
......................................................................

[wip] Add a NetworkHandler as a central API authority to Echo

This is in preparation for dealing with cross-wiki notifications
where we may need several types of operations to extract bundled
notifications from local and external APIs.

Also, organize all network-related helpers in their own folder.

Change-Id: Ib730c780ea52c93a6026c5d0b22012b6f39bb50d
---
M Resources.php
M modules/ext.echo.init.js
R modules/viewmodel/handlers/mw.echo.dm.APIHandler.js
A modules/viewmodel/handlers/mw.echo.dm.ForeignAPIHandler.js
R modules/viewmodel/handlers/mw.echo.dm.LocalAPIHandler.js
A modules/viewmodel/handlers/mw.echo.dm.NetworkHandler.js
M modules/viewmodel/mw.echo.dm.NotificationsModel.js
M tests/qunit/viewmodel/test_mw.echo.dm.NotificationsModel.js
8 files changed, 154 insertions(+), 54 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Echo 
refs/changes/97/252597/1

diff --git a/Resources.php b/Resources.php
index 9bbe643..7b09f6e 100644
--- a/Resources.php
+++ b/Resources.php
@@ -84,11 +84,12 @@
        'ext.echo.dm' => $echoResourceTemplate + array(
                'scripts' => array(
                        'viewmodel/mw.echo.dm.js',
-                       'viewmodel/mw.echo.dm.NotificationItem.js',
-                       'viewmodel/mw.echo.dm.AbstractAPIHandler.js',
-                       'viewmodel/mw.echo.dm.APIHandler.js',
                        'viewmodel/mw.echo.dm.List.js',
                        'viewmodel/mw.echo.dm.SortedList.js',
+                       'viewmodel/handlers/mw.echo.dm.APIHandler.js',
+                       'viewmodel/handlers/mw.echo.dm.LocalAPIHandler.js',
+                       'viewmodel/handlers/mw.echo.dm.NetworkHandler.js',
+                       'viewmodel/mw.echo.dm.NotificationItem.js',
                        'viewmodel/mw.echo.dm.NotificationList.js',
                        'viewmodel/mw.echo.dm.NotificationsModel.js',
                ),
diff --git a/modules/ext.echo.init.js b/modules/ext.echo.init.js
index 0d84e4f..0376691 100644
--- a/modules/ext.echo.init.js
+++ b/modules/ext.echo.init.js
@@ -34,7 +34,8 @@
 
                // Respond to click on the notification button and load the UI 
on demand
                $( '.mw-echo-notification-badge-nojs' ).click( function ( e ) {
-                       var myType = $( this ).parent().prop( 'id' ) === 
'pt-notifications-alert' ? 'alert' : 'message',
+                       var alertNetworkHandler, msgNetworkHandler,
+                               myType = $( this ).parent().prop( 'id' ) === 
'pt-notifications-alert' ? 'alert' : 'message',
                                time = mw.now();
 
                        if ( e.which !== 1 ) {
@@ -46,10 +47,10 @@
 
                        // Fire the notification API requests
                        apiRequest = new mw.Api( { ajax: { cache: false } } 
).get( $.extend( { notsections: myType }, mw.echo.apiCallParams ) )
-                                       .then( function ( data ) {
-                                               mw.track( 
'timing.MediaWiki.echo.overlay.api', mw.now() - time );
-                                               return data;
-                                       } );
+                               .then( function ( data ) {
+                                       mw.track( 
'timing.MediaWiki.echo.overlay.api', mw.now() - time );
+                                       return data;
+                               } );
 
                        // Load the ui
                        mw.loader.using( 'ext.echo.ui', function () {
@@ -57,13 +58,12 @@
 
                                // Load message button and popup if messages 
exist
                                if ( $existingMessageLink.length ) {
+                                       msgNetworkHandler = new 
mw.echo.dm.NetworkHandler( {
+                                               type: 'message',
+                                               baseParams: 
mw.echo.apiCallParams
+                                       } );
                                        messageNotificationsModel = new 
mw.echo.dm.NotificationsModel(
-                                               new mw.echo.dm.APIHandler( {
-                                                       type: 'message',
-                                                       limit: 25,
-                                                       userLang: 
mw.config.get( 'wgUserLanguage' ),
-                                                       baseParams: 
mw.echo.apiCallParams
-                                               } ),
+                                               msgNetworkHandler,
                                                {
                                                        type: 'message'
                                                }
@@ -88,15 +88,15 @@
                                                        .text( mw.msg( 'mytalk' 
) );
                                        } );
                                }
-
                                // Load alerts popup and button
+
+                               // Create a network handler
+                               alertNetworkHandler = new 
mw.echo.dm.NetworkHandler( {
+                                       type: 'alert',
+                                       baseParams: mw.echo.apiCallParams
+                               } );
                                alertNotificationsModel = new 
mw.echo.dm.NotificationsModel(
-                                       new mw.echo.dm.APIHandler( {
-                                               type: 'alert',
-                                               limit: 25,
-                                               userLang: mw.config.get( 
'wgUserLanguage' ),
-                                               baseParams: 
mw.echo.apiCallParams
-                                       } ),
+                                       alertNetworkHandler,
                                        {
                                                type: 'alert'
                                        }
diff --git a/modules/viewmodel/mw.echo.dm.AbstractAPIHandler.js 
b/modules/viewmodel/handlers/mw.echo.dm.APIHandler.js
similarity index 77%
rename from modules/viewmodel/mw.echo.dm.AbstractAPIHandler.js
rename to modules/viewmodel/handlers/mw.echo.dm.APIHandler.js
index 1ce0a38..97b6320 100644
--- a/modules/viewmodel/mw.echo.dm.AbstractAPIHandler.js
+++ b/modules/viewmodel/handlers/mw.echo.dm.APIHandler.js
@@ -14,7 +14,7 @@
         * @cfg {string} [type='alert'] Notification type
         * @cfg {string} [userLang='en'] User language
         */
-       mw.echo.dm.AbstractAPIHandler = function MwEchoDmAPIHandler( config ) {
+       mw.echo.dm.APIHandler = function MwEchoDmAPIHandler( config ) {
                config = config || {};
 
                // Mixin constructor
@@ -33,8 +33,8 @@
 
        /* Setup */
 
-       OO.initClass( mw.echo.dm.AbstractAPIHandler );
-       OO.mixinClass( mw.echo.dm.AbstractAPIHandler, OO.EventEmitter );
+       OO.initClass( mw.echo.dm.APIHandler );
+       OO.mixinClass( mw.echo.dm.APIHandler, OO.EventEmitter );
 
        /**
         * Fetch notifications from the API.
@@ -46,7 +46,7 @@
         * @return {jQuery.Promise} A promise that resolves with an object 
containing the
         *  notification items
         */
-       mw.echo.dm.AbstractAPIHandler.prototype.fetchNotifications = null;
+       mw.echo.dm.APIHandler.prototype.fetchNotifications = null;
 
        /**
         * Update the seen timestamp
@@ -55,7 +55,7 @@
         *  an array of both.
         * @return {jQuery.Promise} A promise that resolves with the seen 
timestamp
         */
-       mw.echo.dm.AbstractAPIHandler.prototype.updateSeenTime = null;
+       mw.echo.dm.APIHandler.prototype.updateSeenTime = null;
 
        /**
         * Mark all notifications as read
@@ -63,7 +63,7 @@
         * @return {jQuery.Promise} A promise that resolves when all 
notifications
         *  are marked as read.
         */
-       mw.echo.dm.AbstractAPIHandler.prototype.markAllRead = null;
+       mw.echo.dm.APIHandler.prototype.markAllRead = null;
 
        /**
         * Update the read status of a notification item in the API
@@ -72,7 +72,7 @@
         * @return {jQuery.Promise} A promise that resolves when the 
notifications
         *  are marked as read.
         */
-       mw.echo.dm.AbstractAPIHandler.prototype.markItemRead = null;
+       mw.echo.dm.APIHandler.prototype.markItemRead = null;
 
        /**
         * Query the API for unread count of the notifications in this model
@@ -80,14 +80,14 @@
         * @return {jQuery.Promise} jQuery promise that's resolved when the 
unread count is fetched
         *  and the badge label is updated.
         */
-       mw.echo.dm.AbstractAPIHandler.prototype.fetchUnreadCount = null;
+       mw.echo.dm.APIHandler.prototype.fetchUnreadCount = null;
 
        /**
         * Check whether the model is fetching notifications from the API
         *
         * @return {boolean} The model is in the process of fetching from the 
API
         */
-       mw.echo.dm.AbstractAPIHandler.prototype.isFetchingNotifications = 
function () {
+       mw.echo.dm.APIHandler.prototype.isFetchingNotifications = function () {
                return !!this.fetchNotificationsPromise;
        };
 
@@ -96,7 +96,7 @@
         *
         * @return {boolean} The model is in API error state
         */
-       mw.echo.dm.AbstractAPIHandler.prototype.isFetchingErrorState = function 
() {
+       mw.echo.dm.APIHandler.prototype.isFetchingErrorState = function () {
                return !!this.apiErrorState;
        };
 
@@ -105,7 +105,7 @@
         * @return {jQuery.Promise} Promise that is resolved when notifications 
are
         *  fetched from the API.
         */
-       mw.echo.dm.AbstractAPIHandler.prototype.getFetchNotificationPromise = 
function () {
+       mw.echo.dm.APIHandler.prototype.getFetchNotificationPromise = function 
() {
                return this.fetchNotificationsPromise;
        };
 
@@ -114,7 +114,7 @@
         *
         * @return {Object} Base API params
         */
-       mw.echo.dm.AbstractAPIHandler.prototype.getBaseParams = function () {
+       mw.echo.dm.APIHandler.prototype.getBaseParams = function () {
                return this.baseParams;
        };
 } )( mediaWiki );
diff --git a/modules/viewmodel/handlers/mw.echo.dm.ForeignAPIHandler.js 
b/modules/viewmodel/handlers/mw.echo.dm.ForeignAPIHandler.js
new file mode 100644
index 0000000..46890be
--- /dev/null
+++ b/modules/viewmodel/handlers/mw.echo.dm.ForeignAPIHandler.js
@@ -0,0 +1,23 @@
+( function ( mw, $ ) {
+       /**
+        * Foreign notification API handler
+        *
+        * @class
+        * @extends mw.echo.dm.LocalAPIHandler
+        *
+        * @constructor
+        * @param {Object} [config] Configuration object
+        */
+       mw.echo.dm.ForeignAPIHandler = function MwEchoDmForeignAPIHandler( 
apiUrl, config ) {
+               config = config || {};
+
+               // Parent constructor
+               mw.echo.dm.ForeignAPIHandler.parent.call( this, config );
+
+               this.api = new mw.ForeignApi( apiUrl );
+       };
+
+       /* Setup */
+
+       OO.inheritClass( mw.echo.dm.ForeignAPIHandler, 
mw.echo.dm.LocalAPIHandler );
+} )( mediaWiki, jQuery );
diff --git a/modules/viewmodel/mw.echo.dm.APIHandler.js 
b/modules/viewmodel/handlers/mw.echo.dm.LocalAPIHandler.js
similarity index 78%
rename from modules/viewmodel/mw.echo.dm.APIHandler.js
rename to modules/viewmodel/handlers/mw.echo.dm.LocalAPIHandler.js
index 7818daa..b85281b 100644
--- a/modules/viewmodel/mw.echo.dm.APIHandler.js
+++ b/modules/viewmodel/handlers/mw.echo.dm.LocalAPIHandler.js
@@ -3,28 +3,28 @@
         * Notification API handler
         *
         * @class
-        * @extends mw.echo.dm.AbstractAPIHandler
+        * @extends mw.echo.dm.APIHandler
         *
         * @constructor
         * @param {Object} [config] Configuration object
         */
-       mw.echo.dm.APIHandler = function MwEchoDmAPIHandler( config ) {
+       mw.echo.dm.LocalAPIHandler = function MwEchoDmAPIHandler( config ) {
                config = config || {};
 
                // Parent constructor
-               mw.echo.dm.APIHandler.parent.call( this, config );
+               mw.echo.dm.LocalAPIHandler.parent.call( this, config );
 
                this.api = new mw.Api( { ajax: { cache: false } } );
        };
 
        /* Setup */
 
-       OO.inheritClass( mw.echo.dm.APIHandler, mw.echo.dm.AbstractAPIHandler );
+       OO.inheritClass( mw.echo.dm.LocalAPIHandler, mw.echo.dm.APIHandler );
 
        /**
         * @inheritdoc
         */
-       mw.echo.dm.APIHandler.prototype.fetchNotifications = function ( 
apiPromise ) {
+       mw.echo.dm.LocalAPIHandler.prototype.fetchNotifications = function ( 
apiPromise ) {
                var helper = this,
                        params = $.extend( { notsections: this.type }, 
this.getBaseParams() );
 
@@ -46,7 +46,7 @@
        /**
         * @inheritdoc
         */
-       mw.echo.dm.APIHandler.prototype.updateSeenTime = function ( type ) {
+       mw.echo.dm.LocalAPIHandler.prototype.updateSeenTime = function ( type ) 
{
                type = type || this.type;
 
                return this.api.postWithToken( 'edit', {
@@ -63,7 +63,7 @@
        /**
         * @inheritdoc
         */
-       mw.echo.dm.APIHandler.prototype.markAllRead = function () {
+       mw.echo.dm.LocalAPIHandler.prototype.markAllRead = function () {
                var model = this,
                        data = {
                                action: 'echomarkread',
@@ -80,7 +80,7 @@
        /**
         * @inheritdoc
         */
-       mw.echo.dm.APIHandler.prototype.markItemRead = function ( itemId ) {
+       mw.echo.dm.LocalAPIHandler.prototype.markItemRead = function ( itemId ) 
{
                var model = this,
                        data = {
                                action: 'echomarkread',
@@ -97,7 +97,7 @@
        /**
         * @inheritdoc
         */
-       mw.echo.dm.APIHandler.prototype.fetchUnreadCount = function () {
+       mw.echo.dm.LocalAPIHandler.prototype.fetchUnreadCount = function () {
                var apiData = {
                                action: 'query',
                                meta: 'notifications',
diff --git a/modules/viewmodel/handlers/mw.echo.dm.NetworkHandler.js 
b/modules/viewmodel/handlers/mw.echo.dm.NetworkHandler.js
new file mode 100644
index 0000000..9636296
--- /dev/null
+++ b/modules/viewmodel/handlers/mw.echo.dm.NetworkHandler.js
@@ -0,0 +1,68 @@
+( function ( mw, $ ) {
+       /**
+        * Network handler for echo notifications. Manages multiple APIHandlers
+        * according to their sources.
+        *
+        * @class
+        * @mixins OO.EventEmitter
+        *
+        * @constructor
+        * @param {Object} [config] Configuration object
+        */
+       mw.echo.dm.NetworkHandler = function MwEchoDmNetworkHandler( config ) {
+               config = config || {};
+
+               // Mixin constructor
+               OO.EventEmitter.call( this );
+
+               this.type = config.type || 'alert';
+               this.baseParams = config.baseParams || {};
+               this.handlers = {};
+
+               // Add initial local handler
+               this.addApiHandler( 'local', {} );
+       };
+
+       /* Setup */
+
+       OO.initClass( mw.echo.dm.NetworkHandler );
+       OO.mixinClass( mw.echo.dm.NetworkHandler, OO.EventEmitter );
+
+       /* Methods */
+
+       /**
+        * Get the API handler that matches the symbolic name
+        *
+        * @param {string} name Symbolic name of the API handler
+        * @return {mw.echo.dm.APIHandler|undefined} API handler, if exists
+        */
+       mw.echo.dm.NetworkHandler.prototype.getApiHandler = function ( name ) {
+               return this.handlers[ name ];
+       };
+
+       /**
+        * Add an API handler
+        *
+        * @param {string} name Symbolic name
+        * @param {Object} details Configuration details
+        * @param {boolean} isExternal Is an external API
+        * @throws {Error} If no URL was given for a foreign API
+        */
+       mw.echo.dm.NetworkHandler.prototype.addApiHandler = function ( name, 
config, isExternal ) {
+               if ( !this.handlers[ name ] ) {
+                       config.baseParams = $.extend( {}, config.baseParams, 
this.baseParams );
+
+                       if ( isExternal ) {
+                               if ( !config.url ) {
+                                       throw new Error( 'External APIs must 
have a valid url.' );
+                               } else {
+                                       this.handlers[ name ] = new 
mw.echo.dm.ForeignAPIHandler( config.url, config );
+                               }
+                       } else {
+                               this.handlers[ name ] = new 
mw.echo.dm.LocalAPIHandler( config );
+                       }
+               }
+
+       };
+
+} )( mediaWiki, jQuery );
diff --git a/modules/viewmodel/mw.echo.dm.NotificationsModel.js 
b/modules/viewmodel/mw.echo.dm.NotificationsModel.js
index 4904297..f6a3656 100644
--- a/modules/viewmodel/mw.echo.dm.NotificationsModel.js
+++ b/modules/viewmodel/mw.echo.dm.NotificationsModel.js
@@ -6,14 +6,16 @@
         * @mixins OO.EventEmitter
         *
         * @constructor
-        * @param {mw.echo.dm.AbstractAPIHandler} apiHandler API handler
+        * @param {mw.echo.dm.NetworkHandler} networkHandler Network handler
         * @param {Object} [config] Configuration object
         * @cfg {string|string[]} [type='alert'] Notification type 'alert', 
'message'
         *  or an array [ 'alert', 'message' ]
+        * @cfg {string} [source='local'] Model source, 'local' or some 
symbolic name identifying
+        *  the source of the notification items for the network handler.
         * @cfg {number} [limit=25] Notification limit
         * @cfg {string} [userLang] User language
         */
-       mw.echo.dm.NotificationsModel = function MwEchoDmNotificationsModel( 
apiHandler, config ) {
+       mw.echo.dm.NotificationsModel = function MwEchoDmNotificationsModel( 
networkHandler, config ) {
                config = config || {};
 
                // Mixin constructor
@@ -23,8 +25,9 @@
                mw.echo.dm.SortedList.call( this );
 
                this.type = config.type || 'alert';
+               this.source = config.source || 'local';
 
-               this.apiHandler = apiHandler;
+               this.networkHandler = networkHandler;
 
                this.seenTime = mw.config.get( 'wgEchoSeenTime' ) || {};
 
@@ -244,7 +247,7 @@
                }
                this.emit( 'updateSeenTime' );
 
-               return this.apiHandler.updateSeenTime( type )
+               return this.getApi().updateSeenTime( type )
                        .then( this.setSeenTime.bind( this ) );
        };
 
@@ -261,7 +264,7 @@
                        return $.Deferred().resolve( 0 ).promise();
                }
 
-               return this.apiHandler.markAllRead()
+               return this.getApi().markAllRead()
                        .then( function () {
                                var i, len,
                                        items = 
model.unreadNotifications.getItems();
@@ -286,7 +289,7 @@
                        return $.Deferred().resolve( 0 ).promise();
                }
 
-               return this.apiHandler.markItemRead( itemId );
+               return this.getApi().markItemRead( itemId );
        };
 
        /**
@@ -304,7 +307,7 @@
 
                // Rebuild the notifications promise either when it is null or 
when
                // it exists in a failed state
-               return this.apiHandler.fetchNotifications( apiPromise )
+               return this.getApi().fetchNotifications( apiPromise )
                        .then( function ( result ) {
                                var notifData, i, len, t, tlen, $content,
                                        notificationModel, types,
@@ -410,7 +413,7 @@
         *  and the badge label is updated.
         */
        mw.echo.dm.NotificationsModel.prototype.fetchUnreadCountFromApi = 
function () {
-               return this.apiHandler.fetchUnreadCount();
+               return this.getApi().fetchUnreadCount();
        };
 
        /**
@@ -419,7 +422,7 @@
         * @return {boolean} The model is in the process of fetching from the 
API
         */
        mw.echo.dm.NotificationsModel.prototype.isFetchingNotifications = 
function () {
-               return this.apiHandler.isFetchingNotifications();
+               return this.getApi().isFetchingNotifications();
        };
 
        /**
@@ -428,7 +431,7 @@
         * @return {boolean} The model is in api error state
         */
        mw.echo.dm.NotificationsModel.prototype.isFetchingErrorState = function 
() {
-               return this.apiHandler.isFetchingErrorState();
+               return this.getApi().isFetchingErrorState();
        };
 
        /**
@@ -437,6 +440,11 @@
         *  fetched from the API.
         */
        mw.echo.dm.NotificationsModel.prototype.getFetchNotificationPromise = 
function () {
-               return this.apiHandler.getFetchNotificationPromise();
+               return this.getApi().getFetchNotificationPromise();
        };
+
+       mw.echo.dm.NotificationsModel.prototype.getApi = function () {
+               return this.networkHandler.getApiHandler( this.source );
+       };
+
 } )( mediaWiki, jQuery );
diff --git a/tests/qunit/viewmodel/test_mw.echo.dm.NotificationsModel.js 
b/tests/qunit/viewmodel/test_mw.echo.dm.NotificationsModel.js
index 7a6e53d..b1ba4f5 100644
--- a/tests/qunit/viewmodel/test_mw.echo.dm.NotificationsModel.js
+++ b/tests/qunit/viewmodel/test_mw.echo.dm.NotificationsModel.js
@@ -22,7 +22,7 @@
                TestApiHandler.parent.call( this );
        }
        /* Setup */
-       OO.inheritClass( TestApiHandler, mw.echo.dm.AbstractAPIHandler );
+       OO.inheritClass( TestApiHandler, mw.echo.dm.APIHandler );
        // Override api call
        TestApiHandler.prototype.markItemRead = function () {
                return $.Deferred().resolve( 0 );

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ib730c780ea52c93a6026c5d0b22012b6f39bb50d
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Echo
Gerrit-Branch: master
Gerrit-Owner: Mooeypoo <mor...@gmail.com>

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

Reply via email to