Projet

Général

Profil

0002-api-subscriptions-management-endpoint-10794.patch

Voir les différences:

Subject: [PATCH 2/3] api: subscriptions management endpoint (#10794)

 corbo/api_urls.py                           |  3 +-
 corbo/api_views.py                          | 82 +++++++++++++++++++++++++++++
 corbo/migrations/0004_auto_20160504_1701.py | 53 +++++++++++++++++++
 corbo/models.py                             | 15 ++----
 tests/test_api.py                           | 54 ++++++++++++++++++-
 5 files changed, 195 insertions(+), 12 deletions(-)
 create mode 100644 corbo/migrations/0004_auto_20160504_1701.py
corbo/api_urls.py
16 16

  
17 17
from django.conf.urls import patterns, include, url
18 18

  
19
from .api_views import NewslettersView
19
from .api_views import NewslettersView, SubscriptionsView
20 20

  
21 21
urlpatterns = patterns('',
22 22
            url(r'^newsletters/', NewslettersView.as_view(), name='newsletters'),
23
            url(r'^subscriptions/', SubscriptionsView.as_view(), name='subscriptions'),
23 24
)
corbo/api_views.py
14 14
# You should have received a copy of the GNU Affero General Public License
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17
import json
18
from collections import defaultdict
19

  
20
from django.core.exceptions import PermissionDenied
21
from django.db.models import Q
22
from django.db import transaction
23

  
17 24
from rest_framework.views import APIView
18 25
from rest_framework.response import Response
19 26

  
......
30 37
                          'transports': transports}
31 38
            newsletters.append(newsletter)
32 39
        return Response({'data': newsletters})
40

  
41

  
42
class SubscriptionsView(APIView):
43

  
44
    def get_subscriptions(self, email, uuid=None):
45
        subscriptions = defaultdict(dict)
46
        identifier = 'mailto:'+email
47
        for s in Subscription.objects.filter(Q(identifier=identifier)|Q(uuid=uuid)):
48
            cat_id = s.category.pk
49
            subscriptions[cat_id]['id'] = str(cat_id)
50
            subscriptions[cat_id]['text'] = s.category.name
51
            transport_id, transport_name = identifier.split(':')
52
            transport = {'id': transport_id,
53
                         'text': transport_id}
54
            if 'transports' in subscriptions[cat_id]:
55
                subscriptions[cat_id]['transports'].append(transport)
56
            else:
57
                subscriptions[cat_id]['transports'] = [transport]
58

  
59
        return subscriptions.values()
60

  
61
    @transaction.atomic
62
    def update_subscriptions(self, category_id, transports, email, uuid=None):
63
        uuid = uuid or u''
64
        identifier = u'mailto:'
65
        if email:
66
            identifier += email
67
        try:
68
            cat = Category.objects.get(pk=category_id)
69
        except Category.DoesNotExist:
70
            return
71

  
72
        subcriptions = cat.subscription_set
73

  
74
        if email:
75
            if 'mailto' in transports:
76
                subcriptions.get_or_create(identifier=identifier, uuid=uuid)
77
                if uuid:
78
                    subcriptions.exclude(uuid=uuid).filter(identifier=identifier).delete()
79
            else:
80
                subcriptions.filter(identifier=identifier).delete()
81

  
82
        if uuid:
83
            if 'homepage' in transports:
84
                subcriptions.get_or_create(uuid=uuid)
85
            else:
86
                subcriptions.filter(identifier='', uuid=uuid).delete()
87

  
88
    def get(self, request):
89
        email = request.GET.get('email')
90
        uuid = request.GET.get('uuid')
91
        if not email:
92
            raise PermissionDenied('Email parameter required')
93
        return Response({'data': self.get_subscriptions(email, uuid)})
94

  
95
    def post(self, request):
96
        email = request.GET.get('email')
97
        uuid = request.GET.get('uuid')
98
        if not email:
99
            raise PermissionDenied('Email parameter required')
100
        data = json.loads(request.body)
101
        for subscription in data:
102
            self.update_subscriptions(subscription['id'], subscription['transports'],
103
                                      email, uuid)
104
        return Response({'data': True})
105

  
106
    def delete(self, request):
107
        email = request.GET.get('email')
108
        uuid = request.GET.get('uuid')
109
        if not email:
110
            raise PermissionDenied('Email parameter required')
111

  
112
        for subscription in self.get_subscriptions(email):
113
            self.update_subscriptions(subscription['id'], [], email, uuid)
114
        return Response({'data': True})
corbo/migrations/0004_auto_20160504_1701.py
1
# -*- coding: utf-8 -*-
2
from __future__ import unicode_literals
3

  
4
from django.db import models, migrations
5

  
6

  
7
class Migration(migrations.Migration):
8

  
9
    dependencies = [
10
        ('corbo', '0003_auto_20160427_1342'),
11
    ]
12

  
13
    operations = [
14
        migrations.RemoveField(
15
            model_name='subscriptiontype',
16
            name='subscription',
17
        ),
18
        migrations.DeleteModel(
19
            name='SubscriptionType',
20
        ),
21
        migrations.AddField(
22
            model_name='subscription',
23
            name='identifier',
24
            field=models.CharField(help_text='ex.: mailto, homepage, ...', max_length=128, verbose_name='identifier', blank=True),
25
            preserve_default=True,
26
        ),
27
        migrations.AddField(
28
            model_name='subscription',
29
            name='uuid',
30
            field=models.CharField(max_length=128, verbose_name='User identifier', blank=True),
31
            preserve_default=True,
32
        ),
33
        migrations.AlterField(
34
            model_name='broadcast',
35
            name='channel',
36
            field=models.CharField(max_length=32, verbose_name='channel', choices=[(b'mailto', 'Email'), (b'homepage', 'Homepage')]),
37
            preserve_default=True,
38
        ),
39
        migrations.AlterField(
40
            model_name='subscription',
41
            name='category',
42
            field=models.ForeignKey(verbose_name='Category', to='corbo.Category'),
43
            preserve_default=True,
44
        ),
45
        migrations.AlterUniqueTogether(
46
            name='subscription',
47
            unique_together=set([('category', 'identifier', 'uuid')]),
48
        ),
49
        migrations.RemoveField(
50
            model_name='subscription',
51
            name='user',
52
        ),
53
    ]
corbo/models.py
68 68
        unique_together = ('announce', 'channel')
69 69

  
70 70

  
71
class SubscriptionType(models.Model):
72
    subscription = models.ForeignKey('Subscription')
73
    identifier = models.CharField(_('identifier'), max_length=128, blank=True,
74
            help_text=_('ex.: email, mobile phone number, jabber id'))
75

  
76

  
77 71
class Subscription(models.Model):
78
    user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('user'),
79
                             blank=True, null=True)
80
    category = models.ForeignKey('Category', verbose_name=_('category'))
72
    category = models.ForeignKey('Category', verbose_name=_('Category'))
73
    uuid = models.CharField(_('User identifier'), max_length=128, blank=True)
74
    identifier = models.CharField(_('identifier'), max_length=128, blank=True,
75
            help_text=_('ex.: mailto, homepage, ...'))
81 76
    class Meta:
82
        unique_together = ('user', 'category')
77
        unique_together = ('category', 'identifier', 'uuid')
tests/test_api.py
1 1
import pytest
2 2
import json
3
from uuid import uuid4
3 4

  
4 5

  
5 6
from django.core.urlresolvers import reverse
7
from django.utils.http import urlencode
6 8

  
7
from corbo.models import Category, Announce, Broadcast
9
from corbo.models import Category, Announce, Broadcast, Subscription
10
from corbo.models import channel_choices
8 11

  
9 12
pytestmark = pytest.mark.django_db
10 13

  
......
44 47
        assert category['transports'] == [{'id': 'mailto', 'text': 'Email'},
45 48
                                          {'id': 'homepage', 'text': 'Homepage'}
46 49
                                          ]
50

  
51

  
52
def test_get_subscriptions_by_email(app, categories, announces):
53
    resp = app.get(reverse('subscriptions'), status=403)
54
    foo = 'foo@example.com'
55
    for identifier, name in channel_choices[:1]:
56
        for category in categories:
57
            uri = '%s:%s' % (identifier, foo)
58
            subscription = Subscription.objects.create(identifier=uri,
59
                                category=category)
60
            resp = app.get(reverse('subscriptions'), {'email': foo}, status=200)
61
            assert 'data' in resp.json
62
            data = resp.json['data']
63
            for d in data:
64
                assert d['id'] in [str(category.id) for category in categories]
65
                assert d['text'] in [category.name for category in categories]
66
                for t in d['transports']:
67
                    assert t['id'] == identifier
68

  
69

  
70
def test_update_subscriptions(app, categories, announces):
71
    params = urlencode({'email': 'foo@example.com',
72
                        'uuid': str(uuid4())})
73
    subscriptions_url = reverse('subscriptions') + '?' + params
74
    for category in categories:
75
        transports = []
76
        for identifier, name in channel_choices[:1]:
77
            transports.append(identifier)
78
            category_id = str(category.id)
79
            subscriptions = [{'id': category_id,
80
                              'text': category.name,
81
                              'transports': transports}]
82
            resp = app.post_json(subscriptions_url , subscriptions)
83
            if resp.json['data']:
84
                resp = app.get(subscriptions_url, status=200)
85
                print resp.json['data']
86
                for cat in resp.json['data']:
87
                    if cat['id'] == category_id:
88
                        sub_transports = [c['id'] for c in cat['transports']]
89
                        assert sub_transports == transports
90

  
91

  
92
def test_delete_subscriptions(app, categories, announces):
93
    params = urlencode({'email': 'foo@example.com', 'uuid': str(uuid4())})
94
    subscriptions_url = reverse('subscriptions') + '?' + params
95
    resp = app.delete(subscriptions_url)
96
    if resp.json['data']:
97
        resp = app.get(subscriptions_url, status=200)
98
        assert resp.json['data'] == []
47
-