0006-notifications-enforce-namespacing-on-external-ids-to.patch
combo/apps/notifications/api_views.py | ||
---|---|---|
23 | 23 | |
24 | 24 |
class NotificationSerializer(serializers.Serializer): |
25 | 25 |
summary = serializers.CharField(required=True, allow_blank=False, max_length=140) |
26 |
id = serializers.SlugField(required=False, allow_null=True)
|
|
26 |
id = serializers.CharField(required=False, allow_null=True)
|
|
27 | 27 |
body = serializers.CharField(allow_blank=False, default='') |
28 | 28 |
url = serializers.URLField(allow_blank=True, default='') |
29 | 29 |
origin = serializers.CharField(allow_blank=True, default='') |
... | ... | |
42 | 42 |
response = {'err': 1, 'err_desc': serializer.errors} |
43 | 43 |
return Response(response, status.HTTP_400_BAD_REQUEST) |
44 | 44 |
data = serializer.validated_data |
45 | ||
46 |
notification, created = Notification.notify( |
|
47 |
user=request.user, |
|
48 |
summary=data['summary'], |
|
49 |
id=data.get('id'), |
|
50 |
body=data.get('body'), |
|
51 |
url=data.get('url'), |
|
52 |
origin=data.get('origin'), |
|
53 |
start_timestamp=data.get('start_timestamp'), |
|
54 |
end_timestamp=data.get('end_timestamp'), |
|
55 |
duration=data.get('duration') |
|
56 |
) |
|
57 |
response = {'err': 0, 'data': {'id': notification.public_id, 'created': created}} |
|
58 |
return Response(response) |
|
45 |
try: |
|
46 |
notification, created = Notification.notify( |
|
47 |
user=request.user, |
|
48 |
summary=data['summary'], |
|
49 |
id=data.get('id'), |
|
50 |
body=data.get('body'), |
|
51 |
url=data.get('url'), |
|
52 |
origin=data.get('origin'), |
|
53 |
start_timestamp=data.get('start_timestamp'), |
|
54 |
end_timestamp=data.get('end_timestamp'), |
|
55 |
duration=data.get('duration') |
|
56 |
) |
|
57 |
except ValueError as e: |
|
58 |
response = {'err': 1, 'err_desc': {'id': [unicode(e)]}} |
|
59 |
return Response(response, status.HTTP_400_BAD_REQUEST) |
|
60 |
else: |
|
61 |
response = {'err': 0, 'data': {'id': notification.public_id, 'created': created}} |
|
62 |
return Response(response) |
|
59 | 63 | |
60 | 64 |
add = Add.as_view() |
61 | 65 |
combo/apps/notifications/models.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 re |
|
18 | ||
17 | 19 |
from django.conf import settings |
18 | 20 |
from django.db import models |
19 | 21 |
from django.utils.translation import ugettext_lazy as _ |
... | ... | |
56 | 58 | |
57 | 59 | |
58 | 60 |
class Notification(models.Model): |
61 |
ID_RE = r'^[\w-]+:[\w]+$' |
|
62 | ||
59 | 63 |
objects = NotificationQuerySet.as_manager() |
60 | 64 | |
61 | 65 |
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) |
... | ... | |
68 | 72 |
acked = models.BooleanField(_('Acked'), default=False) |
69 | 73 |
external_id = models.SlugField(_('External identifier'), null=True) |
70 | 74 | |
75 | ||
71 | 76 |
class Meta: |
72 | 77 |
verbose_name = _('Notification') |
73 | 78 |
unique_together = ( |
... | ... | |
125 | 130 |
id = unicode(id) |
126 | 131 |
except Exception as e: |
127 | 132 |
raise ValueError('id must be convertible to unicode', e) |
133 |
if not re.match(cls.ID_RE, id): |
|
134 |
raise ValueError('id must match regular expression %s' % cls.ID_RE) |
|
128 | 135 |
notification, created = Notification.objects.update_or_create( |
129 | 136 |
user=user, external_id=id, |
130 | 137 |
defaults=defaults) |
tests/test_notification.py | ||
---|---|---|
66 | 66 |
noti = Notification.objects.get() |
67 | 67 |
assert noti.end_timestamp - noti.start_timestamp == timedelta(seconds=3600) |
68 | 68 | |
69 |
notification, created = Notification.notify(user, 'notibar', id='notibar') |
|
69 |
notification, created = Notification.notify(user, 'notibar', id='ns:notibar')
|
|
70 | 70 |
assert Notification.objects.count() == 2 |
71 |
notification, created = Notification.notify(user, 'notirebar', id='notibar') |
|
71 |
notification, created = Notification.notify(user, 'notirebar', id='ns:notibar')
|
|
72 | 72 |
assert not created |
73 | 73 |
assert Notification.objects.count() == 2 |
74 | 74 | |
... | ... | |
153 | 153 |
login() |
154 | 154 |
notify({'summary': 'foo'}, '1', 1) |
155 | 155 |
notify({'summary': 'bar'}, '2', 2) |
156 |
notify({'summary': 'bar', 'id': 'noti3'}, 'noti3', 3)
|
|
156 |
notify({'summary': 'bar', 'id': 'ns:noti3'}, 'ns:noti3', 3)
|
|
157 | 157 |
notif = { |
158 | 158 |
'summary': 'bar', |
159 | 159 |
'url': 'http://www.example.net', |
... | ... | |
233 | 233 |
kwargs={'notification_id': 'noti1'}) == '/api/notification/ack/noti1/' |
234 | 234 |
assert reverse('api-notification-forget', |
235 | 235 |
kwargs={'notification_id': 'noti1'}) == '/api/notification/forget/noti1/' |
236 | ||
237 | ||
238 |
def test_notification_id_and_origin(user): |
|
239 |
login() |
|
240 | ||
241 |
def notify(data): |
|
242 |
resp = client.post(reverse('api-notification-add'), json.dumps(data), |
|
243 |
content_type='application/json') |
|
244 | ||
245 |
return json.loads(resp.content) |
|
246 | ||
247 |
result = notify({'summary': 'foo', 'id': '1'}) |
|
248 |
assert result['err'] == 1 |
|
249 | ||
250 |
notification, created = Notification.notify(user, 'foo') |
|
251 | ||
252 |
result = notify({'summary': 'foo', 'id': str(notification.id)}) |
|
253 |
assert result['err'] == 0 |
|
254 | ||
255 |
result = notify({'summary': 'foo', 'id': 'foo'}) |
|
256 |
assert result['err'] == 1 |
|
257 | ||
258 |
result = notify({'summary': 'foo', 'id': 'foo:foo', 'origin': 'bar'}) |
|
259 |
assert result['err'] == 0 |
|
236 |
- |