Merge lp:~soren/surveilr/messaging into lp:surveilr

Proposed by Soren Hansen
Status: Merged
Approved by: Soren Hansen
Approved revision: 15
Merged at revision: 11
Proposed branch: lp:~soren/surveilr/messaging
Merge into: lp:surveilr
Diff against target: 466 lines (+336/-16)
11 files modified
surveilr/api/server.py (+22/-1)
surveilr/defaults.cfg (+1/-0)
surveilr/messaging/__init__.py (+34/-0)
surveilr/messaging/fake.py (+32/-0)
surveilr/messaging/sms.py (+52/-0)
surveilr/models.py (+4/-0)
surveilr/tests/__init__.py (+1/-2)
surveilr/tests/test_api_server.py (+36/-13)
surveilr/tests/test_messaging.cfg (+5/-0)
surveilr/tests/test_messaging.py (+114/-0)
surveilr/tests/utils.py (+35/-0)
To merge this branch: bzr merge lp:~soren/surveilr/messaging
Reviewer Review Type Date Requested Status
Soren Hansen Pending
Review via email: mp+83866@code.launchpad.net

Commit message

Add basic messaging subsystem.

To post a comment you must log in.
lp:~soren/surveilr/messaging updated
11. By Soren Hansen

Merge trunk

12. By Soren Hansen

Add support for sender id in SMS messaging driver. Add tests for Clickatell client instantiation.

13. By Soren Hansen

Only define User model once.

14. By Soren Hansen

Include messaging settings in GET /users/{id} response

15. By Soren Hansen

PEP-8 fixes

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'surveilr/api/server.py'
--- surveilr/api/server.py 2011-11-29 20:07:19 +0000
+++ surveilr/api/server.py 2011-11-30 09:36:24 +0000
@@ -35,10 +35,25 @@
35from webob.dec import wsgify35from webob.dec import wsgify
36from webob.exc import HTTPNotFound36from webob.exc import HTTPNotFound
3737
38from surveilr import messaging
38from surveilr import models39from surveilr import models
39from surveilr import utils40from surveilr import utils
4041
4142
43class NotificationController(object):
44 """Routes style controller for notifications"""
45
46 def create(self, req, user_id):
47 """Called for POST requests to /user/{id}/notifications
48
49 Sends a notification to the given user"""
50
51 user = models.User.get(key=user_id)
52 messaging.send(user, json.loads(req.body))
53 response = {}
54 return Response(json.dumps(response))
55
56
42class UserController(object):57class UserController(object):
43 """Routes style controller for actions related to users"""58 """Routes style controller for actions related to users"""
4459
@@ -59,7 +74,10 @@
59 Returns information for the given service"""74 Returns information for the given service"""
60 try:75 try:
61 user = models.User.get(id)76 user = models.User.get(id)
62 return Response({'id': user.key})77 resp_dict = {'id': user.key,
78 'messaging_driver': user.messaging_driver,
79 'messaging_address': user.messaging_address}
80 return Response(json.dumps(resp_dict))
63 except riakalchemy.NoSuchObjectError:81 except riakalchemy.NoSuchObjectError:
64 return HTTPNotFound()82 return HTTPNotFound()
6583
@@ -138,6 +156,9 @@
138 path_prefix='/services/{service_name}')156 path_prefix='/services/{service_name}')
139 map.resource("service", "services", controller='ServiceController')157 map.resource("service", "services", controller='ServiceController')
140 map.resource("user", "users", controller='UserController')158 map.resource("user", "users", controller='UserController')
159 map.resource("notification", "notifications",
160 controller='NotificationController',
161 path_prefix='/users/{user_id}')
141162
142 @wsgify163 @wsgify
143 def __call__(self, req):164 def __call__(self, req):
144165
=== modified file 'surveilr/defaults.cfg'
--- surveilr/defaults.cfg 2011-11-28 22:05:12 +0000
+++ surveilr/defaults.cfg 2011-11-30 09:36:24 +0000
@@ -4,3 +4,4 @@
4username = 4username =
5password = 5password =
6api_id = 6api_id =
7sender =
78
=== added directory 'surveilr/messaging'
=== added file 'surveilr/messaging/__init__.py'
--- surveilr/messaging/__init__.py 1970-01-01 00:00:00 +0000
+++ surveilr/messaging/__init__.py 2011-11-30 09:36:24 +0000
@@ -0,0 +1,34 @@
1"""
2 Surveilr - Log aggregation, analysis and visualisation
3
4 Copyright (C) 2011 Linux2Go
5
6 This program is free software: you can redistribute it and/or
7 modify it under the terms of the GNU Affero General Public License
8 as published by the Free Software Foundation, either version 3 of
9 the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Affero General Public License for more details.
15
16 You should have received a copy of the GNU Affero General Public
17 License along with this program. If not, see
18 <http://www.gnu.org/licenses/>.
19"""
20
21import surveilr.messaging.fake
22import surveilr.messaging.sms
23
24from surveilr import drivers
25
26
27def _get_driver(recipient):
28 return drivers.get_driver('messaging',
29 getattr(recipient, 'messaging_driver', 'fake'))
30
31
32def send(recipient, info):
33 driver = _get_driver(recipient)
34 driver.send(recipient, info)
035
=== added file 'surveilr/messaging/fake.py'
--- surveilr/messaging/fake.py 1970-01-01 00:00:00 +0000
+++ surveilr/messaging/fake.py 2011-11-30 09:36:24 +0000
@@ -0,0 +1,32 @@
1"""
2 Surveilr - Log aggregation, analysis and visualisation
3
4 Copyright (C) 2011 Linux2Go
5
6 This program is free software: you can redistribute it and/or
7 modify it under the terms of the GNU Affero General Public License
8 as published by the Free Software Foundation, either version 3 of
9 the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Affero General Public License for more details.
15
16 You should have received a copy of the GNU Affero General Public
17 License along with this program. If not, see
18 <http://www.gnu.org/licenses/>.
19
20 Fake messaging driver
21"""
22
23import surveilr.drivers
24
25
26class FakeMessaging(object):
27 messages = []
28
29 def send(self, recipient, info):
30 self.messages.append((recipient, info))
31
32surveilr.drivers.register_driver('messaging', 'fake', FakeMessaging())
033
=== added file 'surveilr/messaging/sms.py'
--- surveilr/messaging/sms.py 1970-01-01 00:00:00 +0000
+++ surveilr/messaging/sms.py 2011-11-30 09:36:24 +0000
@@ -0,0 +1,52 @@
1"""
2 Surveilr - Log aggregation, analysis and visualisation
3
4 Copyright (C) 2011 Linux2Go
5
6 This program is free software: you can redistribute it and/or
7 modify it under the terms of the GNU Affero General Public License
8 as published by the Free Software Foundation, either version 3 of
9 the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Affero General Public License for more details.
15
16 You should have received a copy of the GNU Affero General Public
17 License along with this program. If not, see
18 <http://www.gnu.org/licenses/>.
19
20 SMS messaging driver
21"""
22
23from surveilr import config
24from surveilr import drivers
25
26from clickatell.api import Clickatell
27from clickatell import constants as cc
28
29
30class SMSMessaging(object):
31 def __init__(self):
32 username = config.get_str('sms', 'username')
33 password = config.get_str('sms', 'password')
34 api_id = config.get_str('sms', 'api_id')
35 self.client = Clickatell(username, password, api_id,
36 sendmsg_defaults={
37 'callback': cc.YES,
38 'msg_type': cc.SMS_DEFAULT,
39 'deliv_ack': cc.YES,
40 'req_feat': (cc.FEAT_ALPHA +
41 cc.FEAT_NUMER +
42 cc.FEAT_DELIVACK)
43 })
44
45 def send(self, recipient, info):
46 sender = config.get_str('sms', 'sender')
47 self.client.sendmsg(recipients=[recipient.messaging_address],
48 sender=sender,
49 text=str(info))
50
51
52drivers.register_driver('messaging', 'sms', SMSMessaging())
053
=== modified file 'surveilr/models.py'
--- surveilr/models.py 2011-11-29 10:27:40 +0000
+++ surveilr/models.py 2011-11-30 09:36:24 +0000
@@ -35,12 +35,16 @@
3535
36 name = String()36 name = String()
37 most_recent_log_entry = RelatedObjects()37 most_recent_log_entry = RelatedObjects()
38 user = RelatedObjects(backref=True)
3839
3940
40class User(RiakObject):41class User(RiakObject):
41 """A user of the service"""42 """A user of the service"""
42 bucket_name = 'users'43 bucket_name = 'users'
4344
45 messaging_driver = String()
46 messaging_address = String()
47
4448
45class LogEntry(RiakObject):49class LogEntry(RiakObject):
46 """A log entry holding one or more metrics50 """A log entry holding one or more metrics
4751
=== modified file 'surveilr/tests/__init__.py'
--- surveilr/tests/__init__.py 2011-11-29 23:14:20 +0000
+++ surveilr/tests/__init__.py 2011-11-30 09:36:24 +0000
@@ -24,6 +24,7 @@
2424
25from surveilr import config25from surveilr import config
2626
27
27class TestCase(unittest.TestCase):28class TestCase(unittest.TestCase):
28 def config_files(self):29 def config_files(self):
29 module = self.__module__30 module = self.__module__
@@ -49,5 +50,3 @@
4950
50 def tearDown(self):51 def tearDown(self):
51 config.load_default_config()52 config.load_default_config()
52
53
5453
=== modified file 'surveilr/tests/test_api_server.py'
--- surveilr/tests/test_api_server.py 2011-11-29 10:27:40 +0000
+++ surveilr/tests/test_api_server.py 2011-11-30 09:36:24 +0000
@@ -38,23 +38,46 @@
38 """Create, retrieve, delete, attempt to retrieve again"""38 """Create, retrieve, delete, attempt to retrieve again"""
39 req = Request.blank('/users',39 req = Request.blank('/users',
40 method='POST',40 method='POST',
41 POST=json.dumps({'messaging_driver': 'fake',
42 'messaging_address': 'foo'}))
43 resp = application(req)
44 self.assertEquals(resp.status_int, 200)
45
46 service_id = json.loads(resp.body)['id']
47
48 req = Request.blank('/users/%s' % service_id)
49 resp = application(req)
50 self.assertEquals(resp.status_int, 200)
51
52 print resp.body, type(resp.body)
53 user = json.loads(resp.body)
54 self.assertEquals(user['messaging_driver'], 'fake')
55 self.assertEquals(user['messaging_address'], 'foo')
56
57 req = Request.blank('/users/%s' % service_id, method='DELETE')
58 resp = application(req)
59 self.assertEquals(resp.status_int, 200)
60
61 req = Request.blank('/users/%s' % service_id)
62 resp = application(req)
63 self.assertEquals(resp.status_int, 404)
64
65 def test_send_notification(self):
66 req = Request.blank('/users',
67 method='POST',
41 POST=json.dumps({}))68 POST=json.dumps({}))
42 resp = application(req)69 resp = application(req)
43 self.assertEquals(resp.status_int, 200)70 self.assertEquals(resp.status_int, 200)
4471
45 service_id = json.loads(resp.body)['id']72 user_id = json.loads(resp.body)['id']
4673 req = Request.blank('/users/%s/notifications' % user_id,
47 req = Request.blank('/users/%s' % service_id)74 method='POST',
48 resp = application(req)75 POST=json.dumps({
49 self.assertEquals(resp.status_int, 200)76 'timestamp': 13217362355575,
5077 'metrics': {'duration': 85000,
51 req = Request.blank('/users/%s' % service_id, method='DELETE')78 'response_size': 12435}}))
52 resp = application(req)79 resp = application(req)
53 self.assertEquals(resp.status_int, 200)80 self.assertEquals(resp.status_int, 200)
54
55 req = Request.blank('/users/%s' % service_id)
56 resp = application(req)
57 self.assertEquals(resp.status_int, 404)
5881
59 def test_create_retrieve_service(self):82 def test_create_retrieve_service(self):
60 """Create, retrieve, delete, attempt to retrieve again"""83 """Create, retrieve, delete, attempt to retrieve again"""
6184
=== added file 'surveilr/tests/test_messaging.cfg'
--- surveilr/tests/test_messaging.cfg 1970-01-01 00:00:00 +0000
+++ surveilr/tests/test_messaging.cfg 2011-11-30 09:36:24 +0000
@@ -0,0 +1,5 @@
1[sms]
2username = testuser
3password = testpassword
4api_id = testapiid
5sender = testsender
06
=== added file 'surveilr/tests/test_messaging.py'
--- surveilr/tests/test_messaging.py 1970-01-01 00:00:00 +0000
+++ surveilr/tests/test_messaging.py 2011-11-30 09:36:24 +0000
@@ -0,0 +1,114 @@
1"""
2 Surveilr - Log aggregation, analysis and visualisation
3
4 Copyright (C) 2011 Linux2Go
5
6 This program is free software: you can redistribute it and/or
7 modify it under the terms of the GNU Affero General Public License
8 as published by the Free Software Foundation, either version 3 of
9 the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Affero General Public License for more details.
15
16 You should have received a copy of the GNU Affero General Public
17 License along with this program. If not, see
18 <http://www.gnu.org/licenses/>.
19
20 Tests for messaging layer
21"""
22
23import mock
24import unittest
25
26from surveilr import drivers
27from surveilr import messaging
28from surveilr import tests
29from surveilr.messaging import sms
30from surveilr.tests import utils
31
32
33class FakeMessagingDriverTests(unittest.TestCase):
34 def setUp(self):
35 import surveilr.messaging.fake
36 # This does nothing, but pyflakes gets upset if we import it
37 # and never "use" it.
38 surveilr.messaging.fake
39 self.driver = drivers.get_driver('messaging', 'fake')
40
41 def test_send(self):
42 user = utils.get_test_user()
43 info = {'service': 'service_id',
44 'state': 'normal',
45 'previous_state': 'unexpected high'}
46 self.driver.send(user, info)
47
48
49class SMSMessagingDriverTests(tests.TestCase):
50 @mock.patch('surveilr.messaging.sms.Clickatell')
51 def test_instantiate_client(self, clickatell):
52 self.driver = sms.SMSMessaging()
53
54 sendmsg_defaults = {'callback': 1,
55 'req_feat': 8240,
56 'deliv_ack': 1,
57 'msg_type': 'SMS_TEXT'}
58 clickatell.assert_called_with('testuser', 'testpassword', 'testapiid',
59 sendmsg_defaults=sendmsg_defaults)
60
61 def test_send(self):
62 driver = sms.SMSMessaging()
63 driver.client = mock.Mock()
64
65 msisdn = '12345678'
66 user = utils.get_test_user(messaging_driver='sms',
67 messaging_address=msisdn)
68 info = utils.get_test_notification_info()
69
70 driver.send(user, info)
71
72 driver.client.sendmsg.assert_called_with(recipients=[msisdn],
73 sender='testsender',
74 text=str(info))
75
76
77class MessagingAPITests(unittest.TestCase):
78 def test_send(self):
79 user = utils.get_test_user()
80 info = {'service': 'service_id',
81 'state': 'normal',
82 'previous_state': 'unexpected high'}
83
84 with mock.patch('surveilr.messaging._get_driver') as _get_driver:
85 messaging.send(user, info)
86
87 # Check that _get_driver gets called with the user as its argument
88 _get_driver.assert_called_with(user)
89
90 # Check that _get_driver's return value (i.e. the driver)
91 # gets its .send() method called with user and info as its
92 # arguments
93 _get_driver.return_value.send.assert_called_with(user, info)
94
95 def test_get_driver_default(self):
96 user = utils.get_test_user()
97 expected_driver = drivers.get_driver('messaging', 'fake')
98
99 actual_driver = messaging._get_driver(user)
100 self.assertEquals(actual_driver, expected_driver)
101
102 def test_get_driver_sms(self):
103 user = utils.get_test_user(messaging_driver='sms')
104 expected_driver = drivers.get_driver('messaging', 'sms')
105
106 actual_driver = messaging._get_driver(user)
107 self.assertEquals(actual_driver, expected_driver)
108
109 def test_get_driver_fake(self):
110 user = utils.get_test_user(messaging_driver='fake')
111 expected_driver = drivers.get_driver('messaging', 'fake')
112
113 actual_driver = messaging._get_driver(user)
114 self.assertEquals(actual_driver, expected_driver)
0115
=== added file 'surveilr/tests/utils.py'
--- surveilr/tests/utils.py 1970-01-01 00:00:00 +0000
+++ surveilr/tests/utils.py 2011-11-30 09:36:24 +0000
@@ -0,0 +1,35 @@
1"""
2 Surveilr - Log aggregation, analysis and visualisation
3
4 Copyright (C) 2011 Linux2Go
5
6 This program is free software: you can redistribute it and/or
7 modify it under the terms of the GNU Affero General Public License
8 as published by the Free Software Foundation, either version 3 of
9 the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Affero General Public License for more details.
15
16 You should have received a copy of the GNU Affero General Public
17 License along with this program. If not, see
18 <http://www.gnu.org/licenses/>.
19
20 Utilities for unit tests
21"""
22
23from surveilr import models
24
25
26def get_test_user(messaging_driver='fake', messaging_address='somewhere'):
27 return models.User(**{'id': 'user_id',
28 'messaging_driver': messaging_driver,
29 'messaging_address': messaging_address})
30
31
32def get_test_notification_info():
33 return {'service': 'service_id',
34 'state': 'normal',
35 'previous_state': 'unexpected high'}

Subscribers

People subscribed via source and target branches

to all changes: