On 03/10/2017 09:32 AM, Aline Manera wrote:
On 03/10/2017 09:30 AM, Daniel Henrique Barboza wrote:
>
>
> On 03/10/2017 08:40 AM, Aline Manera wrote:
>> Hi Daniel,
>>
>> On 03/07/2017 03:52 PM, dhbarboza82(a)gmail.com wrote:
>>> From: Daniel Henrique Barboza <danielhb(a)linux.vnet.ibm.com>
>>>
>>> This patch makes backend and UI changes to implement the asynchronous
>>> UI notification in WoK.
>>>
>>> - Backend:
>>>
>>> A push server was implemented from scratch to manage the opened
>>> websocket
>>> connections. The push server connects to the
>>> /run/user/<user_id>/woknotifications UNIX socket and broadcasts all
>>> messages
>>> to all connections.
>>>
>>> The websocket module is the same module that exists in the Kimchi
>>> plug-in. The idea is to remove the module from Kimchi and make it
>>> use the module from WoK. ws_proxy initialization was also added
>>> in src/wok/server.py.
>>>
>>> A change were made in Wok base control classes to allow every
>>> call of log_request to also send a websocket notification in
>>> the following format:
>>>
>>> <METHOD>:/<plugin>/<entity>/<action>
>>>
>>> For example, creating a new user in Ginger would trigger the
>>> following websocket notification:
>>>
>>> 'POST:/ginger/users'
>>>
>>> - Frontend:
>>>
>>> In ui/js/wok.main.js two new functions were added to help the
>>> usage of asynchronous notifications in the frontend. The idea:
>>> a single websocket is opened per session. This opened websocket
>>> will broadcast all incoming messages to all listeners registered.
>>> Listeners can be added by the new wok.addNotificationListener()
>>> method. This method will clean up any registered listener by
>>> itself when the user changes tabs/URL.
>>>
>>> The single websocket sends heartbeats to the backend side each
>>> 30 seconds. No reply from the backend is issued or expected. This
>>> heartbeat is just a way to ensure that the browser does not
>>> close the connection due to inactivity. This behavior varies from
>>> browser to browser but this 30 second heartbeat is more than enough
>>> to ensure that the websocket is kept alive.
>>>
>>> - Working example in User Log:
>>>
>>> A simple usage is provided in this patch. Changes were made in the
>>> UI of the User Log feature to refresh the listing each time a new
>>> log entry websocket notification is received. The idea is to allow
>>> this code to be a working example of how other tabs can consume
>>> the asynchronous notifications.
>>>
>>> Signed-off-by: Daniel Henrique Barboza <danielhb(a)linux.vnet.ibm.com>
>>> ---
>>> contrib/DEBIAN/control.in | 1 +
>>> contrib/wok.spec.fedora.in | 1 +
>>> contrib/wok.spec.suse.in | 1 +
>>> docs/fedora-deps.md | 2 +-
>>> docs/opensuse-deps.md | 3 +-
>>> docs/ubuntu-deps.md | 3 +-
>>> src/wok/config.py.in | 8 ++
>>> src/wok/control/base.py | 16 ++--
>>> src/wok/model/notifications.py | 2 +-
>>> src/wok/pushserver.py | 163
>>> +++++++++++++++++++++++++++++++++++++++++
>>> src/wok/reqlogger.py | 11 ++-
>>> src/wok/root.py | 3 +
>>> src/wok/server.py | 13 +++-
>>> src/wok/websocket.py | 123 +++++++++++++++++++++++++++++++
>>> ui/js/src/wok.main.js | 43 +++++++++++
>>> ui/js/wok.user-log.js | 12 +++
>>> 16 files changed, 391 insertions(+), 14 deletions(-)
>>> create mode 100644 src/wok/pushserver.py
>>> create mode 100644 src/wok/websocket.py
>>
>>> diff --git a/ui/js/src/wok.main.js b/ui/js/src/wok.main.js
>>> index c8c3889..b58706d 100644
>>> --- a/ui/js/src/wok.main.js
>>> +++ b/ui/js/src/wok.main.js
>>> @@ -29,6 +29,46 @@ wok.getConfig(function(result) {
>>> wok.config = {};
>>> });
>>
>>> +wok.notificationListeners = [];
>>> +wok.addNotificationListener = function(func) {
>>> + wok.notificationListeners.push(func);
>>> + $(window).one("hashchange", function() {
>>> + var del_index = wok.notificationListeners.indexOf(func);
>>> + wok.notificationListeners.splice(del_index, 1);
>>> + });
>>> +};
>>> +
>>> +wok.notificationsWebSocket = undefined;
>>> +wok.startNotificationWebSocket = function () {
>>> + var addr = window.location.hostname + ':' +
window.location.port;
>>> + var token =
>>> wok.urlSafeB64Encode('woknotifications').replace(/=*$/g,
"");
>>> + var url = 'wss://' + addr + '/websockify?token=' +
token;
>>> + wok.notificationsWebSocket = new WebSocket(url, ['base64']);
>>> +
>>> + wok.notificationsWebSocket.onmessage = function(event) {
>>> + var buffer_rcv = window.atob(event.data);
>>> + var messages = buffer_rcv.split("//EOM//");
>>> + for (var i = 0; i < messages.length; i++) {
>>> + if (messages[i] === "") {
>>> + continue;
>>> + }
>>> + for (var j = 0; j < wok.notificationListeners.length;
>>> j++) {
>>> + wok.notificationListeners[j](messages[i]);
>>> + }
>>> + }
>>> + };
>>> +
>>
>> I am not sure I understood this code correctly.
>> From what I had in mind wok.notificationListeners should be an dict
>> instead of an array. In which the key is the notification message
>> and the value the functions to be executed on message received.
>>
>> wok.notificationListeners = {}
>>
>> When adding a new listener, we would do:
>>
>> wok.notificationListeners[<msg>] = <func-to-handle-msg>
>>
>> That way, we avoid calling each wok.notificationListeners sending
>> the same message to listeners not related to that message.
>>
>> To call the listener, we do:
>>
>> func = wok.notificationListeners.get(<message>)
>> if func != undefined:
>> #call func
>> func()
>
> The idea was to make it generic. Every message would be forwarded to
> all functions
> that is currently listening to websocket events. The job of filtering
> what you want
> would be done inside each listener itself.
>
> This allows for multiple listeners being able to receive the same
> message. Otherwise,
> suppose that in the same tab we have multiple independent features
> (like Ginger with
> sidebar), common interest messages like "WoK is going to be reloaded"
> or "A task of
> id=N has been finished" would be captured by only one of them.
>
Also thinking about the Ginger scenario, the $(window).one("hashchange")
will not work
So it is better to check the function is defined or not.
func_list = wok.listenerNotification[<msg>]
if func_list:
for func in func_list:
if func != undefined:
#call func
So do a list of functions to be called.
wok.listenerNotifications[<msg>] = [<list of listener>]
Doesn't make sense forward all the messgaes to all the listener. The
listener should be trciked by a message.
>
>>
>>> + sessionStorage.setItem('wokNotificationWebSocket',
'true');
>>> + var heartbeat = setInterval(function() {
>>> + wok.notificationsWebSocket.send(window.btoa('heartbeat'));
>>> + }, 30000);
>>> +
>>> + wok.notificationsWebSocket.onclose = function() {
>>> + clearInterval(heartbeat);
>>> + };
>>> +};
>>> +
>>> +
>>> wok.main = function() {
>>> wok.isLoggingOut = false;
>>> wok.popable();
>>> @@ -395,6 +435,9 @@ wok.main = function() {
>>>
>>> // Set handler for help button
>>> $('#btn-help').on('click', wok.openHelp);
>>> +
>>> + // start WebSocket
>>> + wok.startNotificationWebSocket();
>>> };
>>>
>>> var initUI = function() {
>>> diff --git a/ui/js/wok.user-log.js b/ui/js/wok.user-log.js
>>> index 0e8fb09..765b75a 100644
>>> --- a/ui/js/wok.user-log.js
>>> +++ b/ui/js/wok.user-log.js
>>> @@ -153,6 +153,18 @@ wok.initUserLogContent = function() {
>>> $("#user-log-grid").bootgrid("search");
>>> wok.initUserLogConfigGridData();
>>> });
>>> +
>>> + wok.addNotificationListener(function(message) {
>>
>>> + var msg_attr = message.split(":");
>>> + if (msg_attr.length != 2) {
>>> + return;
>>> + }
>>> + var method = msg_attr[0];
>>> + var target = msg_attr[1];
>>> + if (method === "POST" && target ===
'/wok/logs') {
>>
>> All the above block is not needed when you follow what I described
>> above, as this function would only be called when POST:/wok/logs
>
> Yes, if we restrict the messages to just be received by one listener
> this filtering is
> unnecessary.
>>
>>> + $("#refresh-button").click();
>>> + }
>>> + });
>>> };
>>>
>>> wok.initUserLogWindow = function() {
>>
>
_______________________________________________
Kimchi-devel mailing list
Kimchi-devel(a)ovirt.org
http://lists.ovirt.org/mailman/listinfo/kimchi-devel