From d89229409cca87158ecd9f08cedad757c62d588f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Arranz?= Date: Mon, 30 May 2016 21:43:15 +0200 Subject: [PATCH] Initial implementation of the sync API endpoint. Fix #182 --- src/wirecloud/live/__init__.py | 0 src/wirecloud/live/consumers.py | 40 +++++++++++++++ src/wirecloud/live/models.py | 91 +++++++++++++++++++++++++++++++++ src/wirecloud/live/routing.py | 27 ++++++++++ src/wirecloud/live/tests.py | 60 ++++++++++++++++++++++ 5 files changed, 218 insertions(+) create mode 100644 src/wirecloud/live/__init__.py create mode 100644 src/wirecloud/live/consumers.py create mode 100644 src/wirecloud/live/models.py create mode 100644 src/wirecloud/live/routing.py create mode 100644 src/wirecloud/live/tests.py diff --git a/src/wirecloud/live/__init__.py b/src/wirecloud/live/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/wirecloud/live/consumers.py b/src/wirecloud/live/consumers.py new file mode 100644 index 0000000000..24fe13ece7 --- /dev/null +++ b/src/wirecloud/live/consumers.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2016 CoNWeT Lab., Universidad Politécnica de Madrid + +# This file is part of Wirecloud. + +# Wirecloud is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# Wirecloud is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with Wirecloud. If not, see . + +from channels import Group +from channels.auth import channel_session_user, channel_session_user_from_http + + +@channel_session_user_from_http +def ws_connect(message): + Group("wc-live-%s" % message.user.username).add(message.reply_channel) + Group("wc-live-*").add(message.reply_channel) + + +@channel_session_user +def ws_message(message): + Group("chat-%s" % message.channel_session['room']).send({ + "text": message['text'], + }) + + +@channel_session_user +def ws_disconnect(message): + Group("wc-live-%s" % message.user.username).discard(message.reply_channel) + Group("wc-live-*").discard(message.reply_channel) diff --git a/src/wirecloud/live/models.py b/src/wirecloud/live/models.py new file mode 100644 index 0000000000..a3f14826b3 --- /dev/null +++ b/src/wirecloud/live/models.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2016 CoNWeT Lab., Universidad Politécnica de Madrid + +# This file is part of Wirecloud. + +# Wirecloud is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# Wirecloud is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with Wirecloud. If not, see . + +from __future__ import unicode_literals + +import json + +from channels import Group +from django.dispatch import receiver +from django.db.models.signals import m2m_changed, post_save +import requests + +from wirecloud.platform.models import CatalogueResource, Workspace + + +def notify(data, affected_users): + for user in affected_users: + Group('wc-live-%s' % user).send({"text": json.dumps(data)}) + + +def get_affected_users(instance): + + if instance.public: + return '*' + else: + affected_users = set(instance.users.values_list("username", flat=True)) + for group in instance.groups.all(): + affected_users.update(group.user_set.values_list("username", flat=True)) + return ",".join(affected_users) + + +@receiver(post_save, sender=Workspace) +def workspace_update(sender, instance, created, raw, **kwargs): + + affected_users = get_affected_users(instance) + + if affected_users != '': + notify( + { + "workspace": instance.id, + "action": "update", + }, + affected_users + ) + + +@receiver(m2m_changed, sender=CatalogueResource.groups.through) +@receiver(m2m_changed, sender=CatalogueResource.users.through) +def update_users_or_groups(sender, instance, action, reverse, model, pk_set, using, **kwargs): + if reverse or action.startswith('pre_') or (pk_set is not None and len(pk_set) == 0): + return + + affected_users = ",".join(model.objects.filter(pk__in=pk_set).values_list("username", flat=True)) + notify( + { + "component": instance.local_uri_part, + "action": "installed" if action == "post_add" else "uninstalled" + }, + affected_users + ) + + +@receiver(post_save, sender=CatalogueResource) +def mac_update(sender, instance, created, raw, **kwargs): + + affected_users = get_affected_users(instance) + + if affected_users != '': + notify( + { + "component": instance.local_uri_part, + "action": "update", + }, + affected_users + ) diff --git a/src/wirecloud/live/routing.py b/src/wirecloud/live/routing.py new file mode 100644 index 0000000000..a3a383a96c --- /dev/null +++ b/src/wirecloud/live/routing.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2016 CoNWeT Lab., Universidad Politécnica de Madrid + +# This file is part of Wirecloud. + +# Wirecloud is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# Wirecloud is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with Wirecloud. If not, see . + +from channels.routing import route +from wirecloud.live.consumers import ws_connect, ws_message, ws_disconnect + +channel_routing = [ + route("websocket.connect", ws_connect, path="^/api/live"), + route("websocket.receive", ws_message), + route("websocket.disconnect", ws_disconnect), +] diff --git a/src/wirecloud/live/tests.py b/src/wirecloud/live/tests.py new file mode 100644 index 0000000000..aa6cc160b1 --- /dev/null +++ b/src/wirecloud/live/tests.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2016 CoNWeT Lab., Universidad Politécnica de Madrid + +# This file is part of Wirecloud. + +# Wirecloud is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# Wirecloud is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with Wirecloud. If not, see . + +from __future__ import unicode_literals + +import datetime + +from django.contrib.auth.models import User +from mock import patch + +from wirecloud.commons.utils.testcases import WirecloudTestCase +from wirecloud.platform.models import CatalogueResource, Workspace + +@patch('wirecloud.live.models.notify') +class LiveNotificationsTestCase(WirecloudTestCase): + + fixtures = ('selenium_test_data',) + tags = ('wirecloud-noselenium', 'wirecloud-live') + + def setUp(self): + self.normuser = User.objects.get(username="normuser") + + def test_new_macs_are_notified(self, notify_mock): + instance = CatalogueResource.objects.create(type=1, creation_date=datetime.datetime.now(), short_name="MyWidget", vendor="Wirecloud", version="1.0") + instance.users.add(self.normuser) + notify_mock.assert_called_once_with( + { + "component": "Wirecloud/MyWidget/1.0", + "action": "installed" + }, + "normuser" + ) + + def test_workspace_updates_are_notified(self, notify_mock): + instance = Workspace.objects.get(pk="2") + instance.description = "New description" + instance.save() + notify_mock.assert_called_once_with( + { + "workspace": 2, + "action": "update" + }, + "user_with_workspaces" + )