commit e5b05b4db05d0a69f19ea4751ad25c8e0b33274d
Author: Serghei Mihai <smihai@entrouvert.com>
Date:   Fri Apr 21 11:43:59 2017 +0200

    provisionning agent (#12910)

diff --git a/corbo/hobo_agent/__init__.py b/corbo/hobo_agent/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/corbo/hobo_agent/management/__init__.py b/corbo/hobo_agent/management/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/corbo/hobo_agent/management/commands/__init__.py b/corbo/hobo_agent/management/commands/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/corbo/hobo_agent/management/commands/hobo_notify.py b/corbo/hobo_agent/management/commands/hobo_notify.py
new file mode 100644
index 0000000..2d2128f
--- /dev/null
+++ b/corbo/hobo_agent/management/commands/hobo_notify.py
@@ -0,0 +1,53 @@
+# corbo - Announces Manager
+# Copyright (C) 2017 Entr'ouvert
+#
+# This program 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.
+#
+# This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+
+from django.db.models import Q
+
+from hobo.agent.common.management.commands import hobo_notify
+
+from corbo.models import Subscription
+
+class Command(hobo_notify.Command):
+
+    def process_notification(self, tenant, notification):
+        super(Command, self).process_notification(tenant, notification)
+        object_type = notification['objects']['@type']
+        if object_type != 'user':
+            return
+        users = notification['objects']['data']
+        action = notification['@type']
+        if notification.get('full'):
+            uuids = [user['uuid'] for user in users]
+            Subscription.objects.exclude(Q(uuid__in=uuids)|Q(uuid__isnull=True)|Q(uuid='')).delete()
+
+        for user in users:
+            for subscription in Subscription.objects.filter(uuid=user['uuid']):
+                if action == 'provision':
+                    if subscription.identifier.startswith('mailto:'):
+                        if user.get('email'):
+                            subscription.identifier = 'mailto:%s' % user['email']
+                        else:
+                            subscription.delete()
+                            continue
+                    elif subscription.identifier.startswith('sms:'):
+                        if user.get('mobile'):
+                            subscription.identifier = 'sms:%s' % user['mobile']
+                        else:
+                            subscription.delete()
+                            continue
+                    subscription.save()
+                elif action == 'deprovision':
+                    subscription.delete()
diff --git a/debian/debian_config.py b/debian/debian_config.py
index 743e9ca..fe2d43d 100644
--- a/debian/debian_config.py
+++ b/debian/debian_config.py
@@ -12,6 +12,8 @@ INSTALLED_APPS += ('mellon',)
 #
 execfile('/usr/lib/hobo/debian_config_common.py')
 
+INSTALLED_APPS = ('corbo.hobo_agent', ) + INSTALLED_APPS
+
 #
 # local settings
 #
diff --git a/tests/conftest.py b/tests/conftest.py
index 9623903..05669b6 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,9 +1,24 @@
 import pytest
 import django_webtest
 import copy
+import tempfile
+import os
+import json
+import shutil
 
 import django.core.mail.backends.locmem
 from django.core.mail.backends.locmem import EmailBackend as DjangoEmailBackend
+from django.utils.text import slugify
+
+from corbo.models import Category, Announce, Broadcast, Subscription
+
+CATEGORIES = ('Alerts', 'News')
+
+SUBSCRIBERS = [{'uuid': 'uuid1', 'email': 'foo@example.net', 'mobile': '0102030405'},
+               {'uuid': 'uuid2', 'email': 'bar@example.net', 'mobile': '0607080900'},
+               {'uuid': '', 'email': 'john@example.net', 'mobile': '0304050607'},
+               {'uuid': None, 'email': 'john2@example.net', 'mobile': '0405060708'}]
+
 
 
 class MockedEmailBackend(object):
@@ -42,3 +57,77 @@ def app(request):
     wtm._patch_settings()
     request.addfinalizer(wtm._unpatch_settings)
     return django_webtest.DjangoTestApp()
+
+
+@pytest.fixture
+def categories():
+    categories = []
+    for category in CATEGORIES:
+        c, created = Category.objects.get_or_create(name=category, slug=slugify(category))
+        categories.append(c)
+    return categories
+
+
+@pytest.fixture
+def subscriptions(categories):
+    subscriptions = []
+    for category in categories:
+        for subscriber in SUBSCRIBERS:
+            kwargs = {'category': category, 'identifier': 'mailto:%(email)s' % subscriber}
+            uuid = subscriber['uuid']
+            if uuid is not None:
+                kwargs['uuid'] = uuid
+            subscriptions.append(Subscription.objects.create(**kwargs))
+            kwargs['identifier'] = 'sms:%(mobile)s' % subscriber
+            subscriptions.append(Subscription.objects.create(**kwargs))
+    return subscriptions
+
+
+@pytest.fixture
+def tenant_base(request, settings):
+    base = tempfile.mkdtemp('corbo-tenant-base')
+    settings.TENANT_BASE = base
+
+    def fin():
+        shutil.rmtree(base)
+    request.addfinalizer(fin)
+    return base
+
+
+@pytest.fixture(scope='function')
+def tenant(transactional_db, request, tenant_base):
+    from hobo.multitenant.models import Tenant
+    base = tenant_base
+
+    @pytest.mark.django_db
+    def make_tenant(name):
+        tenant_dir = os.path.join(base, name)
+        os.mkdir(tenant_dir)
+        with open(os.path.join(tenant_dir, 'hobo.json'), 'w') as fd:
+            json.dump({
+                'variables': {
+                    'hobo_test_variable': True,
+                    'other_variable': 'foo',
+                },
+                'services': [
+                    {'slug': 'test',
+                     'service-id': 'corbo',
+                     'title': 'Test',
+                     'this': True,
+                     'secret_key': '12345',
+                     'base_url': 'http://%s' % name,
+                     'saml-sp-metadata-url': 'http://%s/metadata/' % name,
+                     'variables': {
+                         'other_variable': 'bar',
+                     }
+                    },
+                    {'slug': 'passerelle',
+                     'title': 'Webserivces',
+                     'service-id': 'passerelle',
+                     'secret_key': 'abcdef',
+                     'base_url': 'http://passerelle.example.net',
+                     'saml-sp-metadata-url': 'http://passerelle.example.net/metadata/' },
+                    ]}, fd)
+        return Tenant(domain_url=name,
+                      schema_name=name.replace('-', '_').replace('.', '_'))
+    return make_tenant('corbo.example.net')
diff --git a/tests/settings.py b/tests/settings.py
new file mode 100644
index 0000000..56fc458
--- /dev/null
+++ b/tests/settings.py
@@ -0,0 +1,2 @@
+# Add corbo hobo agent
+INSTALLED_APPS = ('corbo.hobo_agent', ) + INSTALLED_APPS
diff --git a/tests/test_hobo_notify.py b/tests/test_hobo_notify.py
new file mode 100644
index 0000000..6d4b2f3
--- /dev/null
+++ b/tests/test_hobo_notify.py
@@ -0,0 +1,83 @@
+import pytest
+from copy import deepcopy
+import json
+
+from corbo.models import Subscription
+from corbo.hobo_agent.management.commands.hobo_notify import Command
+from django.core.management import call_command
+from django.db.models import Q
+
+NOTIFICATION = {'@type': 'provision',
+                'objects': {'@type': 'user',
+                            'data': [
+                                {'uuid': 'uuid1', 'email': 'foo1@example.net', 'mobile': '0504030201'},
+                                {'uuid': 'uuid2', 'email': 'bar1@example.net', 'mobile': '0009080706'},
+                            ]},
+                'audience': [],
+                'full': False
+}
+
+
+def test_notify_provision(tenant, subscriptions):
+    command = Command()
+    command.process_notification(tenant, NOTIFICATION)
+    assert Subscription.objects.count() == len(subscriptions)
+    for user in NOTIFICATION['objects']['data']:
+        assert Subscription.objects.filter(uuid=user['uuid'], identifier='mailto:%(email)s' % user).exists()
+        assert Subscription.objects.filter(uuid=user['uuid'], identifier='sms:%(mobile)s' % user).exists()
+
+
+def test_notify_other_provision(tenant, subscriptions):
+    role_notification = deepcopy(NOTIFICATION)
+    role_notification['objects']['@type'] = 'role'
+    command = Command()
+    command.process_notification(tenant, role_notification)
+    assert Subscription.objects.count() == len(subscriptions)
+    for subscription in subscriptions:
+        assert Subscription.objects.filter(uuid=subscription.uuid, identifier=subscription.identifier)
+
+
+def test_notify_deprovision(tenant, subscriptions):
+    deprovision_notification = deepcopy(NOTIFICATION)
+    deprovision_notification['@type'] = 'deprovision'
+    command = Command()
+    command.process_notification(tenant, deprovision_notification)
+    for user in deprovision_notification['objects']['data']:
+        assert not Subscription.objects.filter(uuid=user['uuid']).exists()
+
+
+def test_notify_full(tenant, subscriptions):
+    full_notification = deepcopy(NOTIFICATION)
+    full_notification['full'] = True
+    command = Command()
+    command.process_notification(tenant, full_notification)
+    # strings empty and null are the same for CharFields
+    assert Subscription.objects.filter(Q(uuid='')|Q(uuid__isnull=True)).count() == 8
+    assert Subscription.objects.exclude(Q(uuid='')|Q(uuid__isnull=True)).count() == 8
+    full_notification['objects']['data'] = []
+    command.process_notification(tenant, full_notification)
+    # check all remaining subscriptions have empty uuids
+    for subscription in Subscription.objects.all():
+        assert not subscription.uuid
+
+
+def test_notify_emails_only(tenant, subscriptions):
+    email_notification = deepcopy(NOTIFICATION)
+    for user in email_notification['objects']['data']:
+        user['mobile'] = None
+        user['email'] = 'new-%(email)s' % user
+    command = Command()
+    command.process_notification(tenant, email_notification)
+    for user in email_notification['objects']['data']:
+        assert not Subscription.objects.filter(uuid=user['uuid'], identifier__startswith='sms:').exists()
+
+
+def test_notify_mobiles_only(tenant, subscriptions):
+    mobile_notification = deepcopy(NOTIFICATION)
+    for user in mobile_notification['objects']['data']:
+        user['email'] = None
+        user['mobile'] = '0%(mobile)s' % user
+    command = Command()
+    command.process_notification(tenant, mobile_notification)
+    for user in mobile_notification['objects']['data']:
+        assert not Subscription.objects.filter(uuid=user['uuid'], identifier__startswith='mailto:').exists()
diff --git a/tox.ini b/tox.ini
index 1c63b4e..8ef71bf 100644
--- a/tox.ini
+++ b/tox.ini
@@ -6,9 +6,11 @@ usedevelop =
   coverage: True
 setenv =
   DJANGO_SETTINGS_MODULE=corbo.settings
+  CORBO_SETTINGS_FILE=tests/settings.py
   coverage: COVERAGE=--junitxml=test_results.xml --cov-report xml --cov=corbo/ --cov-config .coveragerc
 deps =
   django18: django>=1.8,<1.9
+  http://git.entrouvert.org/hobo.git/snapshot/hobo-master.tar.gz
   pytest-cov
   pytest-django>=3.1.1
   pytest>=3.0.4
